From a78f2541b9bccc8d07e15513b19c364e7412552b Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Fri, 23 Sep 2016 04:19:11 -0300 Subject: [PATCH 001/163] Created function to get the catalog visibility options --- includes/wc-product-functions.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index da0d980127b..5df23deaa90 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -733,3 +733,18 @@ function wc_get_product_attachment_props( $attachment_id, $product = false ) { } return $props; } + +/** + * Get product visibility options. + * + * @since 2.7.0 + * @return array + */ +function wc_get_product_visibility_options() { + return apply_filters( 'woocommerce_product_visibility_options', array( + 'visible' => __( 'Catalog/search', 'woocommerce' ), + 'catalog' => __( 'Catalog', 'woocommerce' ), + 'search' => __( 'Search', 'woocommerce' ), + 'hidden' => __( 'Hidden', 'woocommerce' ), + ) ); +} From 6b52cacc3f216b20d89ccd44f270754e78bb777d Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Fri, 23 Sep 2016 04:19:35 -0300 Subject: [PATCH 002/163] First methods for WP_Product crud --- .../abstracts/abstract-wc-legacy-product.php | 137 ++ includes/abstracts/abstract-wc-product.php | 1464 +++++++++++++---- 2 files changed, 1243 insertions(+), 358 deletions(-) create mode 100644 includes/abstracts/abstract-wc-legacy-product.php diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php new file mode 100644 index 00000000000..2c35bbbbb3a --- /dev/null +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -0,0 +1,137 @@ +id, '_' . $key ); + } + + /** + * Magic __get method for backwards compatibility.Maps legacy vars to new getters. + * + * @param string $key Key name. + * @return mixed + */ + public function __get( $key ) { + _doing_it_wrong( $key, __( 'Product properties should not be accessed directly.', 'woocommerce' ), '2.7' ); + + switch ( $key ) { + case 'id' : + $value = $this->get_id(); + break; + case 'product_attributes' : + $value = isset( $this->data['attributes'] ) ? $this->data['attributes'] : ''; + break; + case 'visibility' : + $value = $this->get_catalog_visibility(); + break; + case 'sale_price_dates_from' : + $value = $this->get_date_on_sale_from(); + break; + case 'sale_price_dates_to' : + $value = $this->get_date_on_sale_to(); + break; + case 'post' : + $value = get_post( $this->get_id() ); + break; + default : + $value = get_post_meta( $this->id, '_' . $key, true ); + + // Get values or default if not set. + if ( in_array( $key, array( 'downloadable', 'virtual', 'backorders', 'manage_stock', 'featured', 'sold_individually' ) ) ) { + $value = $value ? $value : 'no'; + } elseif ( in_array( $key, array( 'product_attributes', 'crosssell_ids', 'upsell_ids' ) ) ) { + $value = $value ? $value : array(); + } elseif ( 'stock' === $key ) { + $value = $value ? $value : 0; + } elseif ( 'stock_status' === $key ) { + $value = $value ? $value : 'instock'; + } elseif ( 'tax_status' === $key ) { + $value = $value ? $value : 'taxable'; + } + break; + } + + return $value; + } + + /** + * Get the product's post data. + * + * @deprecated 2.7.0 + * @return WP_Post + */ + public function get_post_data() { + return $this->post; + } + + /** + * Get the title of the post. + * + * @deprecated 2.7.0 + * @return string + */ + public function get_title() { + return apply_filters( 'woocommerce_product_title', $this->post ? $this->post->post_title : '', $this ); + } + + /** + * Get the parent of the post. + * + * @deprecated 2.7.0 + * @return int + */ + public function get_parent() { + return apply_filters( 'woocommerce_product_parent', absint( $this->post->post_parent ), $this ); + } +} diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 6e908d47233..1dab80acb8f 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -4,155 +4,1100 @@ * * The WooCommerce product class handles individual product data. * - * @class WC_Product - * @var WP_Post - * @version 2.1.0 - * @package WooCommerce/Abstracts - * @category Abstract Class - * @author WooThemes - * - * @property string $width Product width - * @property string $length Product length - * @property string $height Product height - * @property string $weight Product weight - * @property string $price Product price - * @property string $regular_price Product regular price - * @property string $sale_price Product sale price - * @property string $product_image_gallery String of image IDs in the gallery - * @property string $sku Product SKU - * @property string $stock Stock amount - * @property string $downloadable Shows/define if the product is downloadable - * @property string $virtual Shows/define if the product is virtual - * @property string $sold_individually Allow one item to be bought in a single order - * @property string $tax_status Tax status - * @property string $tax_class Tax class - * @property string $manage_stock Shows/define if can manage the product stock - * @property string $stock_status Stock status - * @property string $backorders Whether or not backorders are allowed - * @property string $featured Featured product - * @property string $visibility Product visibility - * @property string $variation_id Variation ID when dealing with variations + * @version 2.7.0 + * @package WooCommerce/Abstracts + * @category Abstract Class + * @author WooThemes */ -class WC_Product { + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +include_once( 'abstract-wc-legacy-product.php' ); + +/** + * Product Class. + */ +class WC_Product extends WC_Abstract_Legacy_Product { /** - * The product (post) ID. + * Stores customer data. * - * @var int + * @var array */ - public $id = 0; + protected $data = array( + 'name' => '', + 'slug' => '', + 'permalink' => '', + 'date_created' => '', + 'date_modified' => '', + 'type' => '', + 'status' => '', + 'featured' => 'no', + 'catalog_visibility' => 'hidden', + 'description' => '', + 'short_description' => '', + 'sku' => '', + 'regular_price' => '', + 'sale_price' => '', + 'date_on_sale_from' => '', + 'date_on_sale_to' => '', + 'total_sales' => '', + 'tax_status' => 'taxable', + 'tax_class' => '', + 'manage_stock' => 'no', + 'stock_quantity' => null, + 'stock_status' => '', + 'backorders' => 'no', + 'sold_individually' => 'no', + 'weight' => '', + 'length' => '', + 'width' => '', + 'height' => '', + 'upsell_ids' => '', + 'cross_sell_ids' => '', + 'parent_id' => 0, + 'reviews_allowed' => true, + 'purchase_note' => '', + 'attributes' => '', + 'default_attributes' => '', + 'menu_order' => 0, + ); /** - * $post Stores post data. + * Data stored in meta keys, but not considered "meta". * - * @var $post WP_Post + * @since 2.7.0 + * @var array */ - public $post = null; - - /** - * The product's type (simple, variable etc). - * - * @var string - */ - public $product_type = null; - - /** - * Product shipping class. - * - * @var string - */ - protected $shipping_class = ''; - - /** - * ID of the shipping class this product has. - * - * @var int - */ - protected $shipping_class_id = 0; - - /** @public string The product's total stock, including that of its children. */ - public $total_stock; + protected $internal_meta_keys = array(); /** * Supported features such as 'ajax_add_to_cart'. + * * @var array */ protected $supports = array(); /** - * Constructor gets the post object and sets the ID for the loaded product. + * Get the product if ID is passed, otherwise the product is new and empty. + * This class should NOT be instantiated, but the wc_get_product() function + * should be used. It is possible, but the wc_get_product() is preferred. * - * @param int|WC_Product|object $product Product ID, post object, or product object + * @param int|WC_Product|object $product Product to init. */ - public function __construct( $product ) { - if ( is_numeric( $product ) ) { - $this->id = absint( $product ); - $this->post = get_post( $this->id ); - } elseif ( $product instanceof WC_Product ) { - $this->id = absint( $product->id ); - $this->post = $product->post; - } elseif ( isset( $product->ID ) ) { - $this->id = absint( $product->ID ); - $this->post = $product; + public function __construct( $product = 0 ) { + if ( is_numeric( $product ) && $product > 0 ) { + $this->read( $product ); + } elseif ( $product instanceof self ) { + $this->read( absint( $product->get_id() ) ); + } elseif ( ! empty( $product->ID ) ) { + $this->read( absint( $product->ID ) ); } } + /* + |-------------------------------------------------------------------------- + | Getters + |-------------------------------------------------------------------------- + | + | Methods for getting data from the product object. + */ + /** - * __isset function. + * Get product name. * - * @param mixed $key + * @since 2.7.0 + * @return string + */ + public function get_name() { + return apply_filters( 'woocommerce_product_get_name', $this->data['name'], $this ); + } + + /** + * Get product slug. + * + * @since 2.7.0 + * @return string + */ + public function get_slug() { + return $this->data['slug']; + } + + /** + * Product permalink. + * + * @return string + */ + public function get_permalink() { + return $this->data['permalink']; + } + + /** + * Get product created date. + * + * @since 2.7.0 + * @return string Timestamp. + */ + public function get_date_created() { + return $this->data['date_created']; + } + + /** + * Get product modified date. + * + * @since 2.7.0 + * @return string Timestamp. + */ + public function get_date_modified() { + return $this->data['date_modified']; + } + + /** + * Return the product type. + * + * @return string + */ + public function get_type() { + return $this->data['type']; + } + + /** + * Get product status. + * + * @since 2.7.0 + * @return string + */ + public function get_status() { + return $this->data['status']; + } + + /** + * If the product is featured. + * + * @since 2.7.0 + * @return string + */ + public function get_featured() { + return $this->data['featured']; + } + + /** + * Get catalog visibility. + * + * @since 2.7.0 + * @return string + */ + public function get_catalog_visibility() { + return $this->data['catalog_visibility']; + } + + /** + * Get product description. + * + * @since 2.7.0 + * @return string + */ + public function get_description() { + return $this->data['description']; + } + + /** + * Get product short description. + * + * @since 2.7.0 + * @return string + */ + public function get_short_description() { + return $this->data['short_description']; + } + + /** + * Get SKU (Stock-keeping unit) - product unique ID. + * + * @return string + */ + public function get_sku() { + return apply_filters( 'woocommerce_get_sku', $this->data['sku'], $this ); + } + + /** + * Returns the product's regular price. + * + * @return string price + */ + public function get_regular_price() { + return apply_filters( 'woocommerce_get_regular_price', $this->data['regular_price'], $this ); + } + + /** + * Returns the product's sale price. + * + * @return string price + */ + public function get_sale_price() { + return apply_filters( 'woocommerce_get_sale_price', $this->data['sale_price'], $this ); + } + + /** + * Get date on sale from. + * + * @since 2.7.0 + * @return string + */ + public function get_date_on_sale_from() { + return $this->data['date_on_sale_from']; + } + + /** + * Get date on sale to. + * + * @since 2.7.0 + * @return string + */ + public function get_date_on_sale_to() { + return $this->data['date_on_sale_to']; + } + + /** + * Get number total of sales. + * + * @since 2.7.0 + * @return int + */ + public function get_total_sales() { + return $this->data['total_sales']; + } + + /** + * Returns the tax status. + * + * @return string + */ + public function get_tax_status() { + return $this->data['tax_status']; + } + + /** + * Returns the tax class. + * + * @return string + */ + public function get_tax_class() { + return apply_filters( 'woocommerce_product_tax_class', $this->data['tax_class'], $this ); + } + + /** + * Return if product manage stock. + * + * @since 2.7.0 + * @return string + */ + public function get_manage_stock() { + return $this->data['manage_stock']; + } + + /** + * Returns number of items available for sale. + * + * @return int + */ + public function get_stock_quantity() { + return apply_filters( 'woocommerce_get_stock_quantity', $this->get_manage_stock() ? wc_stock_amount( $this->data['stock_quantity'] ) : null, $this ); + } + + /** + * Return the stock status. + * + * @since 2.7.0 + * @return string + */ + public function get_stock_status() { + return $this->data['stock_status']; + } + + /** + * Get backorders. + * + * @since 2.7.0 + * @return string + */ + public function get_backorders() { + return $this->data['backorders']; + } + + /** + * Return if should be sold individually. + * + * @since 2.7.0 + * @return return + */ + public function get_sold_individually() { + return $this->data['sold_individually']; + } + + /** + * Returns the product's weight. + * + * @return string + */ + public function get_weight() { + // Legacy filter. + $weight = apply_filters( 'woocommerce_product_weight', $this->data['weight'], $this ); + + // New filter. + return apply_filters( 'woocommerce_product_get_weight', $weight, $this ); + } + + /** + * Returns the product length. + * + * @return string + */ + public function get_length() { + // Legacy filter. + $length = apply_filters( 'woocommerce_product_length', $this->data['length'], $this ); + + // New filter since 2.7. + return apply_filters( 'woocommerce_product_get_length', $this->data['length'], $this ); + } + + /** + * Returns the product width. + * + * @return string + */ + public function get_width() { + // Legacy filter. + $length = apply_filters( 'woocommerce_product_width', $this->data['width'], $this ); + + // New filter since 2.7. + return apply_filters( 'woocommerce_product_get_width', $this->data['width'], $this ); + } + + /** + * Returns the product height. + * + * @return string + */ + public function get_height() { + // Legacy filter. + $length = apply_filters( 'woocommerce_product_height', $this->data['height'], $this ); + + // New filter since 2.7. + return apply_filters( 'woocommerce_product_get_height', $this->data['height'], $this ); + } + + /** + * Get Upseels IDs. + * + * @since 2.7.0 + * @return string + */ + public function get_upsell_ids() { + return $this->data['backorders']; + } + + /** + * Get Upseels IDs. + * + * @since 2.7.0 + * @return string + */ + public function get_cross_sell_ids() { + return $this->data['cross_sell_ids']; + } + + /** + * Get parent ID. + * + * @since 2.7.0 + * @return string + */ + public function get_parent_id() { + return $this->data['parent_id']; + } + + /** + * Return if reviews is allowed. + * + * @since 2.7.0 * @return bool */ - public function __isset( $key ) { - return metadata_exists( 'post', $this->id, '_' . $key ); + public function get_reviews_allowed() { + return $this->data['reviews_allowed']; } /** - * __get function. + * Get purchase note. * - * @param string $key - * @return mixed + * @since 2.7.0 + * @return string */ - public function __get( $key ) { - $value = get_post_meta( $this->id, '_' . $key, true ); - - // Get values or default if not set - if ( in_array( $key, array( 'downloadable', 'virtual', 'backorders', 'manage_stock', 'featured', 'sold_individually' ) ) ) { - $value = $value ? $value : 'no'; - - } elseif ( in_array( $key, array( 'product_attributes', 'crosssell_ids', 'upsell_ids' ) ) ) { - $value = $value ? $value : array(); - - } elseif ( 'visibility' === $key ) { - $value = $value ? $value : 'hidden'; - - } elseif ( 'stock' === $key ) { - $value = $value ? $value : 0; - - } elseif ( 'stock_status' === $key ) { - $value = $value ? $value : 'instock'; - - } elseif ( 'tax_status' === $key ) { - $value = $value ? $value : 'taxable'; - - } - - if ( false !== $value ) { - $this->$key = $value; - } - - return $value; + public function get_purchase_note() { + return $this->data['purchase_note']; } /** - * Get the product's post data. + * Returns the product categories. * - * @return object + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return string */ - public function get_post_data() { - return $this->post; + public function get_categories( $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $this->get_id(), 'product_cat', $before, $sep, $after ); } + /** + * Returns the product tags. + * + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return array + */ + public function get_tags( $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $this->get_id(), 'product_tag', $before, $sep, $after ); + } + + /** + * Returns product attributes. + * + * @return array + */ + public function get_attributes() { + $attributes = array_filter( (array) maybe_unserialize( $this->data['product_attributes'] ) ); + $taxonomies = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_name' ); + + // Check for any attributes which have been removed globally + foreach ( $attributes as $key => $attribute ) { + if ( $attribute['is_taxonomy'] ) { + if ( ! in_array( substr( $attribute['name'], 3 ), $taxonomies ) ) { + unset( $attributes[ $key ] ); + } + } + } + + return apply_filters( 'woocommerce_get_product_attributes', $attributes ); + } + + /** + * Get default attributes. + * + * @since 2.7.0 + * @return string + */ + public function get_default_attributes() { + return $this->data['default_attributes']; + } + + /** + * Get menu order. + * + * @since 2.7.0 + * @return int + */ + public function get_menu_order() { + return $this->data['menu_order']; + } + + /* + |-------------------------------------------------------------------------- + | Setters + |-------------------------------------------------------------------------- + | + | Functions for setting product data. These should not update anything in the + | database itself and should only change what is stored in the class + | object. + */ + + /** + * Set product name. + * + * @since 2.7.0 + * @param string $name Product name. + */ + public function set_name( $name ) { + $this->data['name'] = $name; + } + + /** + * Set product slug. + * + * @since 2.7.0 + * @param string $slug Product slug. + */ + public function set_slug( $slug ) { + $this->data['slug'] = $slug; + } + + /** + * Set product permalink. + * + * @since 2.7.0 + * @param string $permalink Product permalink. + */ + public function set_permalink( $permalink ) { + $this->data['permalink'] = $permalink; + } + + /** + * Set product created date. + * + * @since 2.7.0 + * @param string $timestamp Timestamp. + */ + public function set_date_created( $timestamp ) { + $this->data['date_created'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ); + } + + /** + * Get product modified date. + * + * @since 2.7.0 + * @param string $timestamp Timestamp. + */ + public function set_date_modified( $timestamp ) { + $this->data['date_modified'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ); + } + + /** + * Return the product type. + * + * @return string + */ + public function set_type( $type ) { + $this->data['type'] = $type; + } + + /** + * Get product status. + * + * @since 2.7.0 + * @param string $status Product status. + */ + public function set_status( $status ) { + $this->data['status'] = $status; + } + + /** + * Set if the product is featured. + * + * @since 2.7.0 + * @param string $featured Options: 'yes' or 'no'. + */ + public function set_featured( $featured ) { + $this->data['featured'] = $featured; + } + + /** + * Set catalog visibility. + * + * @since 2.7.0 + * @throws WC_Data_Exception + * @param string $visibility Options: 'hidden', 'visible', 'search' and 'catalog'. + */ + public function set_catalog_visibility( $visibility ) { + $options = array_keys( wc_get_product_visibility_options() ); + if ( ! in_array( $visibility, $options, true ) ) { + $this->error( 'product_invalid_catalog_visibility', __( 'Invalid catalog visibility option.', 'woocommerce' ) ); + } + + $this->data['catalog_visibility'] = $visibility; + } + + /** + * Set product description. + * + * @since 2.7.0 + * @param string $description Product description. + */ + public function set_description( $description ) { + $this->data['description'] = $description; + } + + /** + * Set product short description. + * + * @since 2.7.0 + * @param string $short_description Product short description. + */ + public function set_short_description( $short_description ) { + $this->data['short_description'] = $short_description; + } + + /** + * Set SKU. + * + * @since 2.7.0 + * @throws WC_Data_Exception + * @param string $sku Product SKU. + */ + public function set_sku( $sku ) { + if ( ! wc_product_has_unique_sku( $this->get_id(), $sku ) ) { + $this->error( 'product_invalid_sku', __( 'Invalid or duplicated SKU.', 'woocommerce' ) ); + } + + $this->data['sku'] = $sku; + } + + /** + * Set the product's regular price. + * + * @since 2.7.0 + * @param string $price Regular price. + */ + public function set_regular_price( $price ) { + $this->data['regular_price'] = $price; + } + + /** + * Set the product's sale price. + * + * @since 2.7.0 + * @param string $price sale price. + */ + public function set_sale_price( $price ) { + $this->data['sale_price'] = $price; + } + + /** + * Set date on sale from. + * + * @since 2.7.0 + * @param string $data Sale from date. + */ + public function set_date_on_sale_from( $date ) { + $this->data['date_on_sale_from'] = $date; + } + + /** + * Set date on sale to. + * + * @since 2.7.0 + * @param string $data Sale to date. + */ + public function set_date_on_sale_to( $date ) { + return $this->data['date_on_sale_to'] = $date; + } + + /** + * Set number total of sales. + * + * @since 2.7.0 + * @param int $total Total of sales. + */ + public function set_total_sales( $total ) { + $this->data['total_sales'] = absint( $total ); + } + + /** + * Set the tax status. + * + * @since 2.7.0 + * @throws WC_Data_Exception + * @param string $status Tax status. + */ + public function set_tax_status( $status ) { + $options = array( + 'taxable', + 'shipping', + 'none', + ); + + if ( ! in_array( $status, $options, true ) ) { + $this->error( 'product_invalid_tax_status', __( 'Invalid product tax status.', 'woocommerce' ) ); + } + + $this->data['tax_status'] = $status; + } + + /** + * Set the tax class. + * + * @since 2.7.0 + * @param string $class Tax class. + */ + public function set_tax_class( $class ) { + $this->data['tax_class'] = wc_clean( $class ); + } + + // @TODO: + + /** + * Return if product manage stock. + * + * @since 2.7.0 + * @return string + */ + public function set_manage_stock() { + return $this->data['manage_stock']; + } + + /** + * Returns number of items available for sale. + * + * @return int + */ + public function set_stock_quantity() { + return apply_filters( 'woocommerce_get_stock_quantity', $this->get_manage_stock() ? wc_stock_amount( $this->data['stock_quantity'] ) : null, $this ); + } + + /** + * Set stock status. + * + * @param string $status New status. + */ + public function set_stock_status( $status ) { + $status = 'outofstock' === $status ? 'outofstock' : 'instock'; + + // Sanity check. + if ( $this->managing_stock() ) { + if ( ! $this->backorders_allowed() && $this->get_stock_quantity() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) { + $status = 'outofstock'; + } + } + + if ( update_post_meta( $this->get_id(), '_stock_status', $status ) ) { + $this->data['stock_status'] = $status; + do_action( 'woocommerce_product_set_stock_status', $this->get_id(), $status ); + } + } + + /** + * Get backorders. + * + * @since 2.7.0 + * @return string + */ + public function set_backorders() { + return $this->data['backorders']; + } + + /** + * Return if should be sold individually. + * + * @since 2.7.0 + * @return return + */ + public function set_sold_individually() { + return $this->data['sold_individually']; + } + + /** + * Returns the product's weight. + * + * @return string + */ + public function set_weight() { + // Legacy filter. + $weight = apply_filters( 'woocommerce_product_weight', $this->data['weight'], $this ); + + // New filter. + return apply_filters( 'woocommerce_product_get_weight', $weight, $this ); + } + + /** + * Returns the product length. + * + * @return string + */ + public function set_length() { + // Legacy filter. + $length = apply_filters( 'woocommerce_product_length', $this->data['length'], $this ); + + // New filter since 2.7. + return apply_filters( 'woocommerce_product_get_length', $this->data['length'], $this ); + } + + /** + * Returns the product width. + * + * @return string + */ + public function set_width() { + // Legacy filter. + $length = apply_filters( 'woocommerce_product_width', $this->data['width'], $this ); + + // New filter since 2.7. + return apply_filters( 'woocommerce_product_get_width', $this->data['width'], $this ); + } + + /** + * Returns the product height. + * + * @return string + */ + public function set_height() { + // Legacy filter. + $length = apply_filters( 'woocommerce_product_height', $this->data['height'], $this ); + + // New filter since 2.7. + return apply_filters( 'woocommerce_product_get_height', $this->data['height'], $this ); + } + + /** + * Get Upseels IDs. + * + * @since 2.7.0 + * @return string + */ + public function set_upsell_ids() { + return $this->data['backorders']; + } + + /** + * Get Upseels IDs. + * + * @since 2.7.0 + * @return string + */ + public function set_cross_sell_ids() { + return $this->data['cross_sell_ids']; + } + + /** + * Get parent ID. + * + * @since 2.7.0 + * @return string + */ + public function set_parent_id() { + return $this->data['parent_id']; + } + + /** + * Return if reviews is allowed. + * + * @since 2.7.0 + * @return bool + */ + public function set_reviews_allowed() { + return $this->data['reviews_allowed']; + } + + /** + * Get purchase note. + * + * @since 2.7.0 + * @return string + */ + public function set_purchase_note() { + return $this->data['purchase_note']; + } + + /** + * Returns the product categories. + * + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return string + */ + public function set_categories( $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $this->get_id(), 'product_cat', $before, $sep, $after ); + } + + /** + * Returns the product tags. + * + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return array + */ + public function set_tags( $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $this->get_id(), 'product_tag', $before, $sep, $after ); + } + + /** + * Returns product attributes. + * + * @return array + */ + public function set_attributes() { + $attributes = array_filter( (array) maybe_unserialize( $this->data['product_attributes'] ) ); + $taxonomies = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_name' ); + + // Check for any attributes which have been removed globally + foreach ( $attributes as $key => $attribute ) { + if ( $attribute['is_taxonomy'] ) { + if ( ! in_array( substr( $attribute['name'], 3 ), $taxonomies ) ) { + unset( $attributes[ $key ] ); + } + } + } + + return apply_filters( 'woocommerce_get_product_attributes', $attributes ); + } + + /** + * Get default attributes. + * + * @since 2.7.0 + * @return string + */ + public function set_default_attributes() { + return $this->data['default_attributes']; + } + + /** + * Get menu order. + * + * @since 2.7.0 + * @return int + */ + public function set_menu_order() { + return $this->data['menu_order']; + } + + /* + |-------------------------------------------------------------------------- + | CRUD methods + |-------------------------------------------------------------------------- + | + | Methods which create, read, update and delete products from the database. + | + | A save method is included for convenience (chooses update or create based + | on if the order exists yet). + */ + + /** + * Reads a product from the database and sets its data to the class. + * + * @since 2.7.0 + * @param int $id + */ + public function read( $id ) { + $this->set_defaults(); + + if ( ! $id || ! ( $post_object = get_post( $id ) ) ) { + return; + } + + $this->set_id( $id ); + $this->set_props( array( + 'name' => get_the_title( $post_object ), + 'slug' => $post_object->post_name, + 'permalink' => get_permalink( $post_object ), + 'date_created' => $post_object->post_date, + 'date_modified' => $post_object->post_modified, + 'type' => '', + 'status' => $post_object->post_status, + 'featured' => get_post_meta( $id, '_featured', true ), + 'catalog_visibility' => 'hidden', + 'description' => $post_object->post_content, + 'short_description' => $post_object->post_excerpt, + 'sku' => get_post_meta( $id, '_sku', true ), + 'regular_price' => get_post_meta( $id, '_regular_price', true ), + 'sale_price' => get_post_meta( $id, '_sale_price', true ), + 'date_on_sale_from' => get_post_meta( $id, '_sale_price_dates_from', true ), + 'date_on_sale_to' => get_post_meta( $id, '_sale_price_dates_to', true ), + 'total_sales' => get_post_meta( $id, 'total_sales', true ), + 'tax_status' => get_post_meta( $id, '_tax_status', true ), + 'tax_class' => get_post_meta( $id, '_tax_class', true ), + 'manage_stock' => 'no', + 'stock_quantity' => get_post_meta( $id, '_stock', true ), + 'stock_status' => get_post_meta( $id, '_stock_status', true ), + 'backorders' => get_post_meta( $id, '_backorders', true ), + 'sold_individually' => get_post_meta( $id, '_sold_individually', true ), + 'weight' => get_post_meta( $id, '_weight', true ), + 'length' => get_post_meta( $id, '_length', true ), + 'width' => get_post_meta( $id, '_width', true ), + 'height' => get_post_meta( $id, '_height', true ), + 'upsell_ids' => get_post_meta( $id, '_upsell_ids', true ), + 'cross_sell_ids' => get_post_meta( $id, '_crosssell_ids', true ), + 'parent_id' => $post_object->post_parent, + 'reviews_allowed' => $post_object->comment_status, + 'purchase_note' => get_post_meta( $id, '_purchase_note', true ), + 'attributes' => get_post_meta( $id, '_attributes', true ), + 'default_attributes' => get_post_meta( $id, '_default_attributes', true ), + 'menu_order' => $post_object->menu_order, + ) ); + $this->read_meta_data(); + + do_action( 'woocommerce_product_loaded', $this ); + } + + /** + * Create a new product. + * + * @since 2.7.0 + */ + public function create() { + $this->set_date_created( current_time( 'timestamp' ) ); + + $id = wp_insert_post( apply_filters( 'woocommerce_new_product_data', array( + 'post_type' => 'product', + 'post_status' => 'publish', + 'post_author' => get_current_user_id(), + 'post_title' => $this->get_code(), + 'post_content' => '', + 'post_excerpt' => $this->get_description(), + 'post_date' => date( 'Y-m-d H:i:s', $this->get_date_created() ), + 'post_date_gmt' => get_gmt_from_date( date( 'Y-m-d H:i:s', $this->get_date_created() ) ), + ) ), true ); + + if ( $id ) { + $this->set_id( $id ); + $this->update_post_meta( $id ); + $this->save_meta_data(); + do_action( 'woocommerce_new_product', $id ); + } + } + + /** + * Updates an existing product. + * + * @since 2.7.0 + */ + public function update() { + + } + + /** + * Save data (either create or update depending on if we are working on an existing product). + * + * @since 2.7.0 + */ + public function save() { + if ( $this->get_id() ) { + $this->update(); + } else { + $this->create(); + } + } + + /** + * Delete product from the database. + * + * @since 2.7.0 + */ + public function delete() { + wp_delete_post( $this->get_id() ); + do_action( 'woocommerce_delete_product', $this->get_id() ); + $this->set_id( 0 ); + } + + /** + * Helper method that updates all the post meta for a product based on it's settings in the WC_Product class. + * + * @since 2.7.0 + * @param int $id Object ID. + */ + private function update_post_meta( $id ) { + // update_post_meta( $id, 'discount_type', $this->get_discount_type() ); + } + + /* + |-------------------------------------------------------------------------- + | Other Actions + |-------------------------------------------------------------------------- + */ + /** * Check if a product supports a given feature. * @@ -166,17 +1111,6 @@ class WC_Product { return apply_filters( 'woocommerce_product_supports', in_array( $feature, $this->supports ) ? true : false, $feature, $this ); } - /** - * Return the product ID - * - * @since 2.5.0 - * @return int product (post) ID - */ - public function get_id() { - - return $this->id; - } - /** * Returns the gallery attachment ids. * @@ -186,33 +1120,6 @@ class WC_Product { return apply_filters( 'woocommerce_product_gallery_attachment_ids', array_filter( array_filter( (array) explode( ',', $this->product_image_gallery ) ), 'wp_attachment_is_image' ), $this ); } - /** - * Wrapper for get_permalink. - * - * @return string - */ - public function get_permalink() { - return get_permalink( $this->id ); - } - - /** - * Get SKU (Stock-keeping unit) - product unique ID. - * - * @return string - */ - public function get_sku() { - return apply_filters( 'woocommerce_get_sku', $this->sku, $this ); - } - - /** - * Returns number of items available for sale. - * - * @return int - */ - public function get_stock_quantity() { - return apply_filters( 'woocommerce_get_stock_quantity', $this->managing_stock() ? wc_stock_amount( $this->stock ) : null, $this ); - } - /** * Get total stock - This is the stock of parent and children combined. * @@ -267,23 +1174,23 @@ class WC_Product { if ( ! is_null( $amount ) && $this->managing_stock() ) { // Ensure key exists - add_post_meta( $this->id, '_stock', 0, true ); + add_post_meta( $this->get_id(), '_stock', 0, true ); // Update stock in DB directly switch ( $mode ) { case 'add' : - $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value + %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->id ) ); + $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value + %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->get_id() ) ); break; case 'subtract' : - $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value - %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->id ) ); + $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value - %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->get_id() ) ); break; default : - $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->id ) ); + $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->get_id() ) ); break; } // Clear caches - wp_cache_delete( $this->id, 'post_meta' ); + wp_cache_delete( $this->get_id(), 'post_meta' ); delete_transient( 'wc_low_stock_count' ); delete_transient( 'wc_outofstock_count' ); unset( $this->stock ); @@ -318,37 +1225,6 @@ class WC_Product { return $this->set_stock( $amount, 'add' ); } - /** - * Set stock status of the product. - * - * @param string $status - */ - public function set_stock_status( $status ) { - - $status = ( 'outofstock' === $status ) ? 'outofstock' : 'instock'; - - // Sanity check - if ( $this->managing_stock() ) { - if ( ! $this->backorders_allowed() && $this->get_stock_quantity() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) { - $status = 'outofstock'; - } - } - - if ( update_post_meta( $this->id, '_stock_status', $status ) ) { - $this->stock_status = $status; - do_action( 'woocommerce_product_set_stock_status', $this->id, $status ); - } - } - - /** - * Return the product type. - * - * @return string - */ - public function get_type() { - return is_null( $this->product_type ) ? '' : $this->product_type; - } - /** * Checks the product type. * @@ -548,31 +1424,13 @@ class WC_Product { return $this->get_tax_status() === 'taxable' || $this->get_tax_status() === 'shipping' ? true : false; } - /** - * Get the title of the post. - * - * @return string - */ - public function get_title() { - return apply_filters( 'woocommerce_product_title', $this->post ? $this->post->post_title : '', $this ); - } - - /** - * Get the parent of the post. - * - * @return int - */ - public function get_parent() { - return apply_filters( 'woocommerce_product_parent', absint( $this->post->post_parent ), $this ); - } - /** * Get the add to url used mainly in loops. * * @return string */ public function add_to_cart_url() { - return apply_filters( 'woocommerce_product_add_to_cart_url', get_permalink( $this->id ), $this ); + return apply_filters( 'woocommerce_product_add_to_cart_url', get_permalink( $this->get_id() ), $this ); } /** @@ -599,7 +1457,9 @@ class WC_Product { * @return bool */ public function managing_stock() { - return ( ! isset( $this->manage_stock ) || 'no' === $this->manage_stock || 'yes' !== get_option( 'woocommerce_manage_stock' ) ) ? false : true; + $managing_stock = 'no' === $this->get_manage_stock() || 'yes' !== get_option( 'woocommerce_manage_stock' ); + + return ! $managing_stock; } /** @@ -617,7 +1477,7 @@ class WC_Product { * @return bool */ public function backorders_allowed() { - return apply_filters( 'woocommerce_product_backorders_allowed', ( 'yes' === $this->backorders || 'notify' === $this->backorders ), $this->id, $this ); + return apply_filters( 'woocommerce_product_backorders_allowed', ( 'yes' === $this->get_backorders() || 'notify' === $this->get_backorders() ), $this->get_id(), $this ); } /** @@ -722,6 +1582,7 @@ class WC_Product { } else { $class = 'in-stock'; } + return apply_filters( 'woocommerce_get_availability_class', $class, $this ); } @@ -731,7 +1592,7 @@ class WC_Product { * @return bool */ public function is_featured() { - return ( 'yes' === $this->featured ) ? true : false; + return 'yes' === $this->get_featured(); } /** @@ -743,28 +1604,28 @@ class WC_Product { if ( ! $this->post ) { $visible = false; - // Published/private - } elseif ( 'publish' !== $this->post->post_status && ! current_user_can( 'edit_post', $this->id ) ) { + // Published/private. + } elseif ( 'publish' !== $this->post->post_status && ! current_user_can( 'edit_post', $this->get_id() ) ) { $visible = false; - // Out of stock visibility + // Out of stock visibility. } elseif ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ! $this->is_in_stock() ) { $visible = false; - // visibility setting + // visibility setting. } elseif ( 'hidden' === $this->visibility ) { $visible = false; } elseif ( 'visible' === $this->visibility ) { $visible = true; - // Visibility in loop + // Visibility in loop. } elseif ( is_search() ) { $visible = 'search' === $this->visibility; } else { $visible = 'catalog' === $this->visibility; } - return apply_filters( 'woocommerce_product_is_visible', $visible, $this->id ); + return apply_filters( 'woocommerce_product_is_visible', $visible, $this->get_id() ); } /** @@ -785,16 +1646,16 @@ class WC_Product { $purchasable = true; - // Products must exist of course + // Products must exist of course. if ( ! $this->exists() ) { $purchasable = false; - // Other products types need a price to be set + // Other products types need a price to be set. } elseif ( $this->get_price() === '' ) { $purchasable = false; - // Check the product is published - } elseif ( 'publish' !== $this->post->post_status && ! current_user_can( 'edit_post', $this->id ) ) { + // Check the product is published. + } elseif ( 'publish' !== $this->post->post_status && ! current_user_can( 'edit_post', $this->get_id() ) ) { $purchasable = false; } @@ -819,24 +1680,6 @@ class WC_Product { $this->price = $this->price + $price; } - /** - * Returns the product's sale price. - * - * @return string price - */ - public function get_sale_price() { - return apply_filters( 'woocommerce_get_sale_price', $this->sale_price, $this ); - } - - /** - * Returns the product's regular price. - * - * @return string price - */ - public function get_regular_price() { - return apply_filters( 'woocommerce_get_regular_price', $this->regular_price, $this ); - } - /** * Returns the product's active price. * @@ -1059,35 +1902,17 @@ class WC_Product { return apply_filters( 'woocommerce_get_price_html_from_to', $price, $from, $to, $this ); } - /** - * Returns the tax class. - * - * @return string - */ - public function get_tax_class() { - return apply_filters( 'woocommerce_product_tax_class', $this->tax_class, $this ); - } - - /** - * Returns the tax status. - * - * @return string - */ - public function get_tax_status() { - return $this->tax_status; - } - /** * Get the average rating of product. This is calculated once and stored in postmeta. * @return string */ public function get_average_rating() { // No meta data? Do the calculation - if ( ! metadata_exists( 'post', $this->id, '_wc_average_rating' ) ) { - $this->sync_average_rating( $this->id ); + if ( ! metadata_exists( 'post', $this->get_id(), '_wc_average_rating' ) ) { + $this->sync_average_rating( $this->get_id() ); } - return (string) floatval( get_post_meta( $this->id, '_wc_average_rating', true ) ); + return (string) floatval( get_post_meta( $this->get_id(), '_wc_average_rating', true ) ); } /** @@ -1097,11 +1922,11 @@ class WC_Product { */ public function get_rating_count( $value = null ) { // No meta data? Do the calculation - if ( ! metadata_exists( 'post', $this->id, '_wc_rating_count' ) ) { - $this->sync_rating_count( $this->id ); + if ( ! metadata_exists( 'post', $this->get_id(), '_wc_rating_count' ) ) { + $this->sync_rating_count( $this->get_id() ); } - $counts = get_post_meta( $this->id, '_wc_rating_count', true ); + $counts = get_post_meta( $this->get_id(), '_wc_rating_count', true ); if ( is_null( $value ) ) { return array_sum( $counts ); @@ -1200,17 +2025,17 @@ class WC_Product { global $wpdb; // No meta date? Do the calculation - if ( ! metadata_exists( 'post', $this->id, '_wc_review_count' ) ) { + if ( ! metadata_exists( 'post', $this->get_id(), '_wc_review_count' ) ) { $count = $wpdb->get_var( $wpdb->prepare(" SELECT COUNT(*) FROM $wpdb->comments WHERE comment_parent = 0 AND comment_post_ID = %d AND comment_approved = '1' - ", $this->id ) ); + ", $this->get_id() ) ); - update_post_meta( $this->id, '_wc_review_count', $count ); + update_post_meta( $this->get_id(), '_wc_review_count', $count ); } else { - $count = get_post_meta( $this->id, '_wc_review_count', true ); + $count = get_post_meta( $this->get_id(), '_wc_review_count', true ); } return apply_filters( 'woocommerce_product_review_count', $count, $this ); @@ -1234,30 +2059,6 @@ class WC_Product { return apply_filters( 'woocommerce_product_crosssell_ids', (array) maybe_unserialize( $this->crosssell_ids ), $this ); } - /** - * Returns the product categories. - * - * @param string $sep (default: ', ') - * @param string $before (default: '') - * @param string $after (default: '') - * @return string - */ - public function get_categories( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->id, 'product_cat', $before, $sep, $after ); - } - - /** - * Returns the product tags. - * - * @param string $sep (default: ', ') - * @param string $before (default: '') - * @param string $after (default: '') - * @return array - */ - public function get_tags( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->id, 'product_tag', $before, $sep, $after ); - } - /** * Returns the product shipping class. * @@ -1267,7 +2068,7 @@ class WC_Product { if ( ! $this->shipping_class ) { - $classes = get_the_terms( $this->id, 'product_shipping_class' ); + $classes = get_the_terms( $this->get_id(), 'product_shipping_class' ); if ( $classes && ! is_wp_error( $classes ) ) { $this->shipping_class = current( $classes )->slug; @@ -1288,7 +2089,7 @@ class WC_Product { if ( ! $this->shipping_class_id ) { - $classes = get_the_terms( $this->id, 'product_shipping_class' ); + $classes = get_the_terms( $this->get_id(), 'product_shipping_class' ); if ( $classes && ! is_wp_error( $classes ) ) { $this->shipping_class_id = current( $classes )->term_id; @@ -1317,7 +2118,7 @@ class WC_Product { public function get_related( $limit = 5 ) { global $wpdb; - $transient_name = 'wc_related_' . $this->id; + $transient_name = 'wc_related_' . $this->get_id(); $related_posts = get_transient( $transient_name ); $limit = $limit > 0 ? $limit : 5; @@ -1332,7 +2133,7 @@ class WC_Product { $related_posts = array(); } else { // Sanitize - $exclude_ids = array_map( 'absint', array_merge( array( 0, $this->id ), $this->get_upsells() ) ); + $exclude_ids = array_map( 'absint', array_merge( array( 0, $this->get_id() ), $this->get_upsells() ) ); // Generate query - but query an extra 10 results to give the appearance of random results $query = $this->build_related_query( $cats_array, $tags_array, $exclude_ids, $limit + 10 ); @@ -1369,7 +2170,7 @@ class WC_Product { if ( isset( $attribute['is_taxonomy'] ) && $attribute['is_taxonomy'] ) { - return implode( ', ', wc_get_product_terms( $this->id, $attribute['name'], array( 'fields' => 'names' ) ) ); + return implode( ', ', wc_get_product_terms( $this->get_id(), $attribute['name'], array( 'fields' => 'names' ) ) ); } else { @@ -1380,27 +2181,6 @@ class WC_Product { return ''; } - /** - * Returns product attributes. - * - * @return array - */ - public function get_attributes() { - $attributes = array_filter( (array) maybe_unserialize( $this->product_attributes ) ); - $taxonomies = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_name' ); - - // Check for any attributes which have been removed globally - foreach ( $attributes as $key => $attribute ) { - if ( $attribute['is_taxonomy'] ) { - if ( ! in_array( substr( $attribute['name'], 3 ), $taxonomies ) ) { - unset( $attributes[ $key ] ); - } - } - } - - return apply_filters( 'woocommerce_get_product_attributes', $attributes ); - } - /** * Returns whether or not the product has any attributes set. * @@ -1441,46 +2221,14 @@ class WC_Product { /** * Does a child have dimensions set? + * * @since 2.7.0 - * @return boolean + * @return bool */ public function child_has_dimensions() { return false; } - /** - * Returns the product length. - * @return string - */ - public function get_length() { - return apply_filters( 'woocommerce_product_length', $this->length ? $this->length : '', $this ); - } - - /** - * Returns the product width. - * @return string - */ - public function get_width() { - return apply_filters( 'woocommerce_product_width', $this->width ? $this->width : '', $this ); - } - - /** - * Returns the product height. - * @return string - */ - public function get_height() { - return apply_filters( 'woocommerce_product_height', $this->height ? $this->height : '', $this ); - } - - /** - * Returns the product's weight. - * @todo refactor filters in this class to naming woocommerce_product_METHOD - * @return string - */ - public function get_weight() { - return apply_filters( 'woocommerce_product_weight', apply_filters( 'woocommerce_product_get_weight', $this->weight ? $this->weight : '' ), $this ); - } - /** * Returns whether or not the product has weight set. * @@ -1533,9 +2281,9 @@ class WC_Product { */ public function get_image_id() { - if ( has_post_thumbnail( $this->id ) ) { - $image_id = get_post_thumbnail_id( $this->id ); - } elseif ( ( $parent_id = wp_get_post_parent_id( $this->id ) ) && has_post_thumbnail( $parent_id ) ) { + if ( has_post_thumbnail( $this->get_id() ) ) { + $image_id = get_post_thumbnail_id( $this->get_id() ); + } elseif ( ( $parent_id = wp_get_post_parent_id( $this->get_id() ) ) && has_post_thumbnail( $parent_id ) ) { $image_id = get_post_thumbnail_id( $parent_id ); } else { $image_id = 0; @@ -1553,9 +2301,9 @@ class WC_Product { * @return string */ public function get_image( $size = 'shop_thumbnail', $attr = array(), $placeholder = true ) { - if ( has_post_thumbnail( $this->id ) ) { - $image = get_the_post_thumbnail( $this->id, $size, $attr ); - } elseif ( ( $parent_id = wp_get_post_parent_id( $this->id ) ) && has_post_thumbnail( $parent_id ) ) { + if ( has_post_thumbnail( $this->get_id() ) ) { + $image = get_the_post_thumbnail( $this->get_id(), $size, $attr ); + } elseif ( ( $parent_id = wp_get_post_parent_id( $this->get_id() ) ) && has_post_thumbnail( $parent_id ) ) { $image = get_the_post_thumbnail( $parent_id, $size, $attr ); } elseif ( $placeholder ) { $image = wc_placeholder_img( $size ); @@ -1574,7 +2322,7 @@ class WC_Product { if ( $this->get_sku() ) { $identifier = $this->get_sku(); } else { - $identifier = '#' . $this->id; + $identifier = '#' . $this->get_id(); } return sprintf( '%s – %s', $identifier, $this->get_title() ); @@ -1589,7 +2337,7 @@ class WC_Product { protected function get_related_terms( $term ) { $terms_array = array( 0 ); - $terms = apply_filters( 'woocommerce_get_related_' . $term . '_terms', wp_get_post_terms( $this->id, $term ), $this->id ); + $terms = apply_filters( 'woocommerce_get_related_' . $term . '_terms', wp_get_post_terms( $this->get_id(), $term ), $this->get_id() ); foreach ( $terms as $term ) { $terms_array[] = $term->term_id; } @@ -1632,8 +2380,8 @@ class WC_Product { $query['where'] .= " AND pm2.meta_value = 'instock'"; } - $relate_by_category = apply_filters( 'woocommerce_product_related_posts_relate_by_category', true, $this->id ); - $relate_by_tag = apply_filters( 'woocommerce_product_related_posts_relate_by_tag', true, $this->id ); + $relate_by_category = apply_filters( 'woocommerce_product_related_posts_relate_by_category', true, $this->get_id() ); + $relate_by_tag = apply_filters( 'woocommerce_product_related_posts_relate_by_tag', true, $this->get_id() ); if ( $relate_by_category || $relate_by_tag ) { $query['where'] .= ' AND ('; @@ -1653,7 +2401,7 @@ class WC_Product { } $query['limits'] = " LIMIT {$limit} "; - $query = apply_filters( 'woocommerce_product_related_posts_query', $query, $this->id ); + $query = apply_filters( 'woocommerce_product_related_posts_query', $query, $this->get_id() ); return $query; } From 78b224877c9d44b3779481a92e2a31ef57eac8a8 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Thu, 29 Sep 2016 20:02:50 -0300 Subject: [PATCH 003/163] Product set methods --- includes/abstracts/abstract-wc-product.php | 268 ++++++++++----------- 1 file changed, 127 insertions(+), 141 deletions(-) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 1dab80acb8f..92940c6b5f7 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -60,8 +60,8 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'parent_id' => 0, 'reviews_allowed' => true, 'purchase_note' => '', - 'attributes' => '', - 'default_attributes' => '', + 'attributes' => array(), + 'default_attributes' => array(), 'menu_order' => 0, ); @@ -331,7 +331,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * Return if should be sold individually. * * @since 2.7.0 - * @return return + * @return string */ public function get_sold_individually() { return $this->data['sold_individually']; @@ -360,7 +360,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { $length = apply_filters( 'woocommerce_product_length', $this->data['length'], $this ); // New filter since 2.7. - return apply_filters( 'woocommerce_product_get_length', $this->data['length'], $this ); + return apply_filters( 'woocommerce_product_get_length', $length, $this ); } /** @@ -370,10 +370,10 @@ class WC_Product extends WC_Abstract_Legacy_Product { */ public function get_width() { // Legacy filter. - $length = apply_filters( 'woocommerce_product_width', $this->data['width'], $this ); + $width = apply_filters( 'woocommerce_product_width', $this->data['width'], $this ); // New filter since 2.7. - return apply_filters( 'woocommerce_product_get_width', $this->data['width'], $this ); + return apply_filters( 'woocommerce_product_get_width', $width, $this ); } /** @@ -383,10 +383,10 @@ class WC_Product extends WC_Abstract_Legacy_Product { */ public function get_height() { // Legacy filter. - $length = apply_filters( 'woocommerce_product_height', $this->data['height'], $this ); + $height = apply_filters( 'woocommerce_product_height', $this->data['height'], $this ); // New filter since 2.7. - return apply_filters( 'woocommerce_product_get_height', $this->data['height'], $this ); + return apply_filters( 'woocommerce_product_get_height', $height, $this ); } /** @@ -396,7 +396,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return string */ public function get_upsell_ids() { - return $this->data['backorders']; + return $this->data['upsell_ids']; } /** @@ -413,7 +413,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * Get parent ID. * * @since 2.7.0 - * @return string + * @return int */ public function get_parent_id() { return $this->data['parent_id']; @@ -488,7 +488,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * Get default attributes. * * @since 2.7.0 - * @return string + * @return array */ public function get_default_attributes() { return $this->data['default_attributes']; @@ -555,7 +555,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { } /** - * Get product modified date. + * Set product modified date. * * @since 2.7.0 * @param string $timestamp Timestamp. @@ -565,7 +565,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { } /** - * Return the product type. + * Set the product type. * * @return string */ @@ -574,7 +574,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { } /** - * Get product status. + * Set product status. * * @since 2.7.0 * @param string $status Product status. @@ -725,25 +725,24 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->data['tax_class'] = wc_clean( $class ); } - // @TODO: - /** - * Return if product manage stock. + * Set if product manage stock. * * @since 2.7.0 - * @return string + * @param string $manage_stock Options: 'yes' or 'no'. */ - public function set_manage_stock() { - return $this->data['manage_stock']; + public function set_manage_stock( $manage_stock ) { + $this->data['manage_stock'] = $manage_stock; } /** - * Returns number of items available for sale. + * Set number of items available for sale. * - * @return int + * @since 2.7.0 + * @param float|null $quantity Stock quantity. */ - public function set_stock_quantity() { - return apply_filters( 'woocommerce_get_stock_quantity', $this->get_manage_stock() ? wc_stock_amount( $this->data['stock_quantity'] ) : null, $this ); + public function set_stock_quantity( $quantity ) { + $this->data['stock_quantity'] = $quantity; } /** @@ -768,190 +767,163 @@ class WC_Product extends WC_Abstract_Legacy_Product { } /** - * Get backorders. + * Set backorders. * * @since 2.7.0 - * @return string + * @param string $backorders Options: 'yes', 'no' or 'notify'. */ - public function set_backorders() { - return $this->data['backorders']; + public function set_backorders( $backorders ) { + $this->data['backorders'] = $backorders; } /** - * Return if should be sold individually. + * Set if should be sold individually. * * @since 2.7.0 - * @return return + * @param string $sold_individually Options: 'yes' or 'no'. */ - public function set_sold_individually() { - return $this->data['sold_individually']; + public function set_sold_individually( $sold_individually ) { + $this->data['sold_individually'] = $sold_individually; } /** - * Returns the product's weight. - * - * @return string - */ - public function set_weight() { - // Legacy filter. - $weight = apply_filters( 'woocommerce_product_weight', $this->data['weight'], $this ); - - // New filter. - return apply_filters( 'woocommerce_product_get_weight', $weight, $this ); - } - - /** - * Returns the product length. - * - * @return string - */ - public function set_length() { - // Legacy filter. - $length = apply_filters( 'woocommerce_product_length', $this->data['length'], $this ); - - // New filter since 2.7. - return apply_filters( 'woocommerce_product_get_length', $this->data['length'], $this ); - } - - /** - * Returns the product width. - * - * @return string - */ - public function set_width() { - // Legacy filter. - $length = apply_filters( 'woocommerce_product_width', $this->data['width'], $this ); - - // New filter since 2.7. - return apply_filters( 'woocommerce_product_get_width', $this->data['width'], $this ); - } - - /** - * Returns the product height. - * - * @return string - */ - public function set_height() { - // Legacy filter. - $length = apply_filters( 'woocommerce_product_height', $this->data['height'], $this ); - - // New filter since 2.7. - return apply_filters( 'woocommerce_product_get_height', $this->data['height'], $this ); - } - - /** - * Get Upseels IDs. + * Set the product's weight. * * @since 2.7.0 - * @return string + * @param float $weigth Total weigth. */ - public function set_upsell_ids() { - return $this->data['backorders']; + public function set_weight( $weight ) { + $this->data['weight'] = $weight; } /** - * Get Upseels IDs. + * Set the product length. * * @since 2.7.0 - * @return string + * @param float $weigth Total weigth. */ - public function set_cross_sell_ids() { - return $this->data['cross_sell_ids']; + public function set_length( $length ) { + $this->data['length'] = $length; } /** - * Get parent ID. + * Set the product width. * * @since 2.7.0 - * @return string + * @param float $width Total width. */ - public function set_parent_id() { - return $this->data['parent_id']; + public function set_width( $width ) { + $this->data['width'] = $width; } /** - * Return if reviews is allowed. + * Set the product height. * * @since 2.7.0 - * @return bool + * @param float $height Total height. */ - public function set_reviews_allowed() { - return $this->data['reviews_allowed']; + public function set_height( $height ) { + $this->data['height'] = $height; } /** - * Get purchase note. + * Set Upseels IDs. * * @since 2.7.0 - * @return string + * @param string $upsell_ids IDs from the up-sell products. + */ + public function set_upsell_ids( $upsell_ids ) { + $this->data['upsell_ids'] = $upsell_ids; + } + + /** + * Set Upseels IDs. + * + * @since 2.7.0 + * @param string $cross_sell_ids IDs from the cross-sell products. + */ + public function set_cross_sell_ids( $cross_sell_ids ) { + $this->data['cross_sell_ids'] = $cross_sell_ids; + } + + /** + * Set parent ID. + * + * @since 2.7.0 + * @param int $parent_id Product parent ID. + */ + public function set_parent_id( $parent_id ) { + $this->data['parent_id'] = absint( $parent_id ); + } + + /** + * Set if reviews is allowed. + * + * @since 2.7.0 + * @param bool $reviews_allowed Reviews allowed or not. + */ + public function set_reviews_allowed( $reviews_allowed ) { + $this->data['reviews_allowed'] = (bool) $reviews_allowed; + } + + /** + * Set purchase note. + * + * @since 2.7.0 + * @param string $purchase_note Purchase note. */ public function set_purchase_note() { - return $this->data['purchase_note']; + $this->data['purchase_note'] = $purchase_note; } /** - * Returns the product categories. + * Set the product categories. * - * @param string $sep (default: ', '). - * @param string $before (default: ''). - * @param string $after (default: ''). - * @return string + * @since 2.7.0 + * @param array $terms_id List of terms IDs. */ - public function set_categories( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->get_id(), 'product_cat', $before, $sep, $after ); + public function set_categories( $terms_id ) { + $this->save_taxonomy_terms( $terms_id, 'cat' ); } /** - * Returns the product tags. + * Set the product tags. * - * @param string $sep (default: ', '). - * @param string $before (default: ''). - * @param string $after (default: ''). - * @return array + * @since 2.7.0 + * @param array $terms_id List of terms IDs. */ - public function set_tags( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->get_id(), 'product_tag', $before, $sep, $after ); + public function set_tags( $terms_id ) { + $this->save_taxonomy_terms( $terms_id, 'tag' ); } /** * Returns product attributes. * - * @return array + * @since 2.7.0 + * @param array $attributes List of product attributes. */ - public function set_attributes() { - $attributes = array_filter( (array) maybe_unserialize( $this->data['product_attributes'] ) ); - $taxonomies = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_name' ); - - // Check for any attributes which have been removed globally - foreach ( $attributes as $key => $attribute ) { - if ( $attribute['is_taxonomy'] ) { - if ( ! in_array( substr( $attribute['name'], 3 ), $taxonomies ) ) { - unset( $attributes[ $key ] ); - } - } - } - - return apply_filters( 'woocommerce_get_product_attributes', $attributes ); + public function set_attributes( $attributes ) { + $this->data['product_attributes'] = $attributes; } /** - * Get default attributes. + * Set default attributes. * * @since 2.7.0 - * @return string + * @param array $default_attributes List of default attributes. */ - public function set_default_attributes() { - return $this->data['default_attributes']; + public function set_default_attributes( $default_attributes ) { + $this->data['default_attributes'] = $default_attributes; } /** - * Get menu order. + * Set menu order. * * @since 2.7.0 - * @return int + * @param int $menu_order Menu order. */ - public function set_menu_order() { - return $this->data['menu_order']; + public function set_menu_order( $menu_order ) { + $this->data['menu_order'] = intval( $menu_order ); } /* @@ -969,7 +941,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * Reads a product from the database and sets its data to the class. * * @since 2.7.0 - * @param int $id + * @param int $id Product ID. */ public function read( $id ) { $this->set_defaults(); @@ -2405,4 +2377,18 @@ class WC_Product extends WC_Abstract_Legacy_Product { return $query; } + + /** + * Save taxonomy terms. + * + * @since 2.7.0 + * @param array $terms_id Terms ID. + * @param string $taxonomy Taxonomy. + * @return array|WP_Error + */ + protected function save_taxonomy_terms( $terms_id, $taxonomy = 'cat' ) { + $terms_id = array_unique( array_map( 'intval', $terms_id ) ); + + return wp_set_object_terms( $this->get_id(), $terms_id, 'product_' . $taxonomy ); + } } From e60b001a90c66c398a3cdb4392f8ba6af89e26d7 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Thu, 29 Sep 2016 20:16:42 -0300 Subject: [PATCH 004/163] Fixed several erros while setting data --- includes/abstracts/abstract-wc-product.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 92940c6b5f7..d09b0354a89 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -637,7 +637,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $sku Product SKU. */ public function set_sku( $sku ) { - if ( ! wc_product_has_unique_sku( $this->get_id(), $sku ) ) { + if ( ! empty( $sku ) && ! wc_product_has_unique_sku( $this->get_id(), $sku ) ) { $this->error( 'product_invalid_sku', __( 'Invalid or duplicated SKU.', 'woocommerce' ) ); } @@ -708,6 +708,11 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'none', ); + // Set default if empty. + if ( empty( $status ) ) { + $status = 'taxable'; + } + if ( ! in_array( $status, $options, true ) ) { $this->error( 'product_invalid_tax_status', __( 'Invalid product tax status.', 'woocommerce' ) ); } @@ -761,9 +766,10 @@ class WC_Product extends WC_Abstract_Legacy_Product { } if ( update_post_meta( $this->get_id(), '_stock_status', $status ) ) { - $this->data['stock_status'] = $status; do_action( 'woocommerce_product_set_stock_status', $this->get_id(), $status ); } + + $this->data['stock_status'] = $status; } /** @@ -872,7 +878,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @since 2.7.0 * @param string $purchase_note Purchase note. */ - public function set_purchase_note() { + public function set_purchase_note( $purchase_note ) { $this->data['purchase_note'] = $purchase_note; } From 43fd79cfcda3b246ee3a1f75e67bb147a9cf3428 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Fri, 23 Sep 2016 04:19:35 -0300 Subject: [PATCH 005/163] First methods for WP_Product crud --- .../abstracts/abstract-wc-legacy-product.php | 137 ++ includes/abstracts/abstract-wc-product.php | 1464 +++++++++++++---- 2 files changed, 1243 insertions(+), 358 deletions(-) create mode 100644 includes/abstracts/abstract-wc-legacy-product.php diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php new file mode 100644 index 00000000000..2c35bbbbb3a --- /dev/null +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -0,0 +1,137 @@ +id, '_' . $key ); + } + + /** + * Magic __get method for backwards compatibility.Maps legacy vars to new getters. + * + * @param string $key Key name. + * @return mixed + */ + public function __get( $key ) { + _doing_it_wrong( $key, __( 'Product properties should not be accessed directly.', 'woocommerce' ), '2.7' ); + + switch ( $key ) { + case 'id' : + $value = $this->get_id(); + break; + case 'product_attributes' : + $value = isset( $this->data['attributes'] ) ? $this->data['attributes'] : ''; + break; + case 'visibility' : + $value = $this->get_catalog_visibility(); + break; + case 'sale_price_dates_from' : + $value = $this->get_date_on_sale_from(); + break; + case 'sale_price_dates_to' : + $value = $this->get_date_on_sale_to(); + break; + case 'post' : + $value = get_post( $this->get_id() ); + break; + default : + $value = get_post_meta( $this->id, '_' . $key, true ); + + // Get values or default if not set. + if ( in_array( $key, array( 'downloadable', 'virtual', 'backorders', 'manage_stock', 'featured', 'sold_individually' ) ) ) { + $value = $value ? $value : 'no'; + } elseif ( in_array( $key, array( 'product_attributes', 'crosssell_ids', 'upsell_ids' ) ) ) { + $value = $value ? $value : array(); + } elseif ( 'stock' === $key ) { + $value = $value ? $value : 0; + } elseif ( 'stock_status' === $key ) { + $value = $value ? $value : 'instock'; + } elseif ( 'tax_status' === $key ) { + $value = $value ? $value : 'taxable'; + } + break; + } + + return $value; + } + + /** + * Get the product's post data. + * + * @deprecated 2.7.0 + * @return WP_Post + */ + public function get_post_data() { + return $this->post; + } + + /** + * Get the title of the post. + * + * @deprecated 2.7.0 + * @return string + */ + public function get_title() { + return apply_filters( 'woocommerce_product_title', $this->post ? $this->post->post_title : '', $this ); + } + + /** + * Get the parent of the post. + * + * @deprecated 2.7.0 + * @return int + */ + public function get_parent() { + return apply_filters( 'woocommerce_product_parent', absint( $this->post->post_parent ), $this ); + } +} diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 6e908d47233..1dab80acb8f 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -4,155 +4,1100 @@ * * The WooCommerce product class handles individual product data. * - * @class WC_Product - * @var WP_Post - * @version 2.1.0 - * @package WooCommerce/Abstracts - * @category Abstract Class - * @author WooThemes - * - * @property string $width Product width - * @property string $length Product length - * @property string $height Product height - * @property string $weight Product weight - * @property string $price Product price - * @property string $regular_price Product regular price - * @property string $sale_price Product sale price - * @property string $product_image_gallery String of image IDs in the gallery - * @property string $sku Product SKU - * @property string $stock Stock amount - * @property string $downloadable Shows/define if the product is downloadable - * @property string $virtual Shows/define if the product is virtual - * @property string $sold_individually Allow one item to be bought in a single order - * @property string $tax_status Tax status - * @property string $tax_class Tax class - * @property string $manage_stock Shows/define if can manage the product stock - * @property string $stock_status Stock status - * @property string $backorders Whether or not backorders are allowed - * @property string $featured Featured product - * @property string $visibility Product visibility - * @property string $variation_id Variation ID when dealing with variations + * @version 2.7.0 + * @package WooCommerce/Abstracts + * @category Abstract Class + * @author WooThemes */ -class WC_Product { + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +include_once( 'abstract-wc-legacy-product.php' ); + +/** + * Product Class. + */ +class WC_Product extends WC_Abstract_Legacy_Product { /** - * The product (post) ID. + * Stores customer data. * - * @var int + * @var array */ - public $id = 0; + protected $data = array( + 'name' => '', + 'slug' => '', + 'permalink' => '', + 'date_created' => '', + 'date_modified' => '', + 'type' => '', + 'status' => '', + 'featured' => 'no', + 'catalog_visibility' => 'hidden', + 'description' => '', + 'short_description' => '', + 'sku' => '', + 'regular_price' => '', + 'sale_price' => '', + 'date_on_sale_from' => '', + 'date_on_sale_to' => '', + 'total_sales' => '', + 'tax_status' => 'taxable', + 'tax_class' => '', + 'manage_stock' => 'no', + 'stock_quantity' => null, + 'stock_status' => '', + 'backorders' => 'no', + 'sold_individually' => 'no', + 'weight' => '', + 'length' => '', + 'width' => '', + 'height' => '', + 'upsell_ids' => '', + 'cross_sell_ids' => '', + 'parent_id' => 0, + 'reviews_allowed' => true, + 'purchase_note' => '', + 'attributes' => '', + 'default_attributes' => '', + 'menu_order' => 0, + ); /** - * $post Stores post data. + * Data stored in meta keys, but not considered "meta". * - * @var $post WP_Post + * @since 2.7.0 + * @var array */ - public $post = null; - - /** - * The product's type (simple, variable etc). - * - * @var string - */ - public $product_type = null; - - /** - * Product shipping class. - * - * @var string - */ - protected $shipping_class = ''; - - /** - * ID of the shipping class this product has. - * - * @var int - */ - protected $shipping_class_id = 0; - - /** @public string The product's total stock, including that of its children. */ - public $total_stock; + protected $internal_meta_keys = array(); /** * Supported features such as 'ajax_add_to_cart'. + * * @var array */ protected $supports = array(); /** - * Constructor gets the post object and sets the ID for the loaded product. + * Get the product if ID is passed, otherwise the product is new and empty. + * This class should NOT be instantiated, but the wc_get_product() function + * should be used. It is possible, but the wc_get_product() is preferred. * - * @param int|WC_Product|object $product Product ID, post object, or product object + * @param int|WC_Product|object $product Product to init. */ - public function __construct( $product ) { - if ( is_numeric( $product ) ) { - $this->id = absint( $product ); - $this->post = get_post( $this->id ); - } elseif ( $product instanceof WC_Product ) { - $this->id = absint( $product->id ); - $this->post = $product->post; - } elseif ( isset( $product->ID ) ) { - $this->id = absint( $product->ID ); - $this->post = $product; + public function __construct( $product = 0 ) { + if ( is_numeric( $product ) && $product > 0 ) { + $this->read( $product ); + } elseif ( $product instanceof self ) { + $this->read( absint( $product->get_id() ) ); + } elseif ( ! empty( $product->ID ) ) { + $this->read( absint( $product->ID ) ); } } + /* + |-------------------------------------------------------------------------- + | Getters + |-------------------------------------------------------------------------- + | + | Methods for getting data from the product object. + */ + /** - * __isset function. + * Get product name. * - * @param mixed $key + * @since 2.7.0 + * @return string + */ + public function get_name() { + return apply_filters( 'woocommerce_product_get_name', $this->data['name'], $this ); + } + + /** + * Get product slug. + * + * @since 2.7.0 + * @return string + */ + public function get_slug() { + return $this->data['slug']; + } + + /** + * Product permalink. + * + * @return string + */ + public function get_permalink() { + return $this->data['permalink']; + } + + /** + * Get product created date. + * + * @since 2.7.0 + * @return string Timestamp. + */ + public function get_date_created() { + return $this->data['date_created']; + } + + /** + * Get product modified date. + * + * @since 2.7.0 + * @return string Timestamp. + */ + public function get_date_modified() { + return $this->data['date_modified']; + } + + /** + * Return the product type. + * + * @return string + */ + public function get_type() { + return $this->data['type']; + } + + /** + * Get product status. + * + * @since 2.7.0 + * @return string + */ + public function get_status() { + return $this->data['status']; + } + + /** + * If the product is featured. + * + * @since 2.7.0 + * @return string + */ + public function get_featured() { + return $this->data['featured']; + } + + /** + * Get catalog visibility. + * + * @since 2.7.0 + * @return string + */ + public function get_catalog_visibility() { + return $this->data['catalog_visibility']; + } + + /** + * Get product description. + * + * @since 2.7.0 + * @return string + */ + public function get_description() { + return $this->data['description']; + } + + /** + * Get product short description. + * + * @since 2.7.0 + * @return string + */ + public function get_short_description() { + return $this->data['short_description']; + } + + /** + * Get SKU (Stock-keeping unit) - product unique ID. + * + * @return string + */ + public function get_sku() { + return apply_filters( 'woocommerce_get_sku', $this->data['sku'], $this ); + } + + /** + * Returns the product's regular price. + * + * @return string price + */ + public function get_regular_price() { + return apply_filters( 'woocommerce_get_regular_price', $this->data['regular_price'], $this ); + } + + /** + * Returns the product's sale price. + * + * @return string price + */ + public function get_sale_price() { + return apply_filters( 'woocommerce_get_sale_price', $this->data['sale_price'], $this ); + } + + /** + * Get date on sale from. + * + * @since 2.7.0 + * @return string + */ + public function get_date_on_sale_from() { + return $this->data['date_on_sale_from']; + } + + /** + * Get date on sale to. + * + * @since 2.7.0 + * @return string + */ + public function get_date_on_sale_to() { + return $this->data['date_on_sale_to']; + } + + /** + * Get number total of sales. + * + * @since 2.7.0 + * @return int + */ + public function get_total_sales() { + return $this->data['total_sales']; + } + + /** + * Returns the tax status. + * + * @return string + */ + public function get_tax_status() { + return $this->data['tax_status']; + } + + /** + * Returns the tax class. + * + * @return string + */ + public function get_tax_class() { + return apply_filters( 'woocommerce_product_tax_class', $this->data['tax_class'], $this ); + } + + /** + * Return if product manage stock. + * + * @since 2.7.0 + * @return string + */ + public function get_manage_stock() { + return $this->data['manage_stock']; + } + + /** + * Returns number of items available for sale. + * + * @return int + */ + public function get_stock_quantity() { + return apply_filters( 'woocommerce_get_stock_quantity', $this->get_manage_stock() ? wc_stock_amount( $this->data['stock_quantity'] ) : null, $this ); + } + + /** + * Return the stock status. + * + * @since 2.7.0 + * @return string + */ + public function get_stock_status() { + return $this->data['stock_status']; + } + + /** + * Get backorders. + * + * @since 2.7.0 + * @return string + */ + public function get_backorders() { + return $this->data['backorders']; + } + + /** + * Return if should be sold individually. + * + * @since 2.7.0 + * @return return + */ + public function get_sold_individually() { + return $this->data['sold_individually']; + } + + /** + * Returns the product's weight. + * + * @return string + */ + public function get_weight() { + // Legacy filter. + $weight = apply_filters( 'woocommerce_product_weight', $this->data['weight'], $this ); + + // New filter. + return apply_filters( 'woocommerce_product_get_weight', $weight, $this ); + } + + /** + * Returns the product length. + * + * @return string + */ + public function get_length() { + // Legacy filter. + $length = apply_filters( 'woocommerce_product_length', $this->data['length'], $this ); + + // New filter since 2.7. + return apply_filters( 'woocommerce_product_get_length', $this->data['length'], $this ); + } + + /** + * Returns the product width. + * + * @return string + */ + public function get_width() { + // Legacy filter. + $length = apply_filters( 'woocommerce_product_width', $this->data['width'], $this ); + + // New filter since 2.7. + return apply_filters( 'woocommerce_product_get_width', $this->data['width'], $this ); + } + + /** + * Returns the product height. + * + * @return string + */ + public function get_height() { + // Legacy filter. + $length = apply_filters( 'woocommerce_product_height', $this->data['height'], $this ); + + // New filter since 2.7. + return apply_filters( 'woocommerce_product_get_height', $this->data['height'], $this ); + } + + /** + * Get Upseels IDs. + * + * @since 2.7.0 + * @return string + */ + public function get_upsell_ids() { + return $this->data['backorders']; + } + + /** + * Get Upseels IDs. + * + * @since 2.7.0 + * @return string + */ + public function get_cross_sell_ids() { + return $this->data['cross_sell_ids']; + } + + /** + * Get parent ID. + * + * @since 2.7.0 + * @return string + */ + public function get_parent_id() { + return $this->data['parent_id']; + } + + /** + * Return if reviews is allowed. + * + * @since 2.7.0 * @return bool */ - public function __isset( $key ) { - return metadata_exists( 'post', $this->id, '_' . $key ); + public function get_reviews_allowed() { + return $this->data['reviews_allowed']; } /** - * __get function. + * Get purchase note. * - * @param string $key - * @return mixed + * @since 2.7.0 + * @return string */ - public function __get( $key ) { - $value = get_post_meta( $this->id, '_' . $key, true ); - - // Get values or default if not set - if ( in_array( $key, array( 'downloadable', 'virtual', 'backorders', 'manage_stock', 'featured', 'sold_individually' ) ) ) { - $value = $value ? $value : 'no'; - - } elseif ( in_array( $key, array( 'product_attributes', 'crosssell_ids', 'upsell_ids' ) ) ) { - $value = $value ? $value : array(); - - } elseif ( 'visibility' === $key ) { - $value = $value ? $value : 'hidden'; - - } elseif ( 'stock' === $key ) { - $value = $value ? $value : 0; - - } elseif ( 'stock_status' === $key ) { - $value = $value ? $value : 'instock'; - - } elseif ( 'tax_status' === $key ) { - $value = $value ? $value : 'taxable'; - - } - - if ( false !== $value ) { - $this->$key = $value; - } - - return $value; + public function get_purchase_note() { + return $this->data['purchase_note']; } /** - * Get the product's post data. + * Returns the product categories. * - * @return object + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return string */ - public function get_post_data() { - return $this->post; + public function get_categories( $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $this->get_id(), 'product_cat', $before, $sep, $after ); } + /** + * Returns the product tags. + * + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return array + */ + public function get_tags( $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $this->get_id(), 'product_tag', $before, $sep, $after ); + } + + /** + * Returns product attributes. + * + * @return array + */ + public function get_attributes() { + $attributes = array_filter( (array) maybe_unserialize( $this->data['product_attributes'] ) ); + $taxonomies = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_name' ); + + // Check for any attributes which have been removed globally + foreach ( $attributes as $key => $attribute ) { + if ( $attribute['is_taxonomy'] ) { + if ( ! in_array( substr( $attribute['name'], 3 ), $taxonomies ) ) { + unset( $attributes[ $key ] ); + } + } + } + + return apply_filters( 'woocommerce_get_product_attributes', $attributes ); + } + + /** + * Get default attributes. + * + * @since 2.7.0 + * @return string + */ + public function get_default_attributes() { + return $this->data['default_attributes']; + } + + /** + * Get menu order. + * + * @since 2.7.0 + * @return int + */ + public function get_menu_order() { + return $this->data['menu_order']; + } + + /* + |-------------------------------------------------------------------------- + | Setters + |-------------------------------------------------------------------------- + | + | Functions for setting product data. These should not update anything in the + | database itself and should only change what is stored in the class + | object. + */ + + /** + * Set product name. + * + * @since 2.7.0 + * @param string $name Product name. + */ + public function set_name( $name ) { + $this->data['name'] = $name; + } + + /** + * Set product slug. + * + * @since 2.7.0 + * @param string $slug Product slug. + */ + public function set_slug( $slug ) { + $this->data['slug'] = $slug; + } + + /** + * Set product permalink. + * + * @since 2.7.0 + * @param string $permalink Product permalink. + */ + public function set_permalink( $permalink ) { + $this->data['permalink'] = $permalink; + } + + /** + * Set product created date. + * + * @since 2.7.0 + * @param string $timestamp Timestamp. + */ + public function set_date_created( $timestamp ) { + $this->data['date_created'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ); + } + + /** + * Get product modified date. + * + * @since 2.7.0 + * @param string $timestamp Timestamp. + */ + public function set_date_modified( $timestamp ) { + $this->data['date_modified'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ); + } + + /** + * Return the product type. + * + * @return string + */ + public function set_type( $type ) { + $this->data['type'] = $type; + } + + /** + * Get product status. + * + * @since 2.7.0 + * @param string $status Product status. + */ + public function set_status( $status ) { + $this->data['status'] = $status; + } + + /** + * Set if the product is featured. + * + * @since 2.7.0 + * @param string $featured Options: 'yes' or 'no'. + */ + public function set_featured( $featured ) { + $this->data['featured'] = $featured; + } + + /** + * Set catalog visibility. + * + * @since 2.7.0 + * @throws WC_Data_Exception + * @param string $visibility Options: 'hidden', 'visible', 'search' and 'catalog'. + */ + public function set_catalog_visibility( $visibility ) { + $options = array_keys( wc_get_product_visibility_options() ); + if ( ! in_array( $visibility, $options, true ) ) { + $this->error( 'product_invalid_catalog_visibility', __( 'Invalid catalog visibility option.', 'woocommerce' ) ); + } + + $this->data['catalog_visibility'] = $visibility; + } + + /** + * Set product description. + * + * @since 2.7.0 + * @param string $description Product description. + */ + public function set_description( $description ) { + $this->data['description'] = $description; + } + + /** + * Set product short description. + * + * @since 2.7.0 + * @param string $short_description Product short description. + */ + public function set_short_description( $short_description ) { + $this->data['short_description'] = $short_description; + } + + /** + * Set SKU. + * + * @since 2.7.0 + * @throws WC_Data_Exception + * @param string $sku Product SKU. + */ + public function set_sku( $sku ) { + if ( ! wc_product_has_unique_sku( $this->get_id(), $sku ) ) { + $this->error( 'product_invalid_sku', __( 'Invalid or duplicated SKU.', 'woocommerce' ) ); + } + + $this->data['sku'] = $sku; + } + + /** + * Set the product's regular price. + * + * @since 2.7.0 + * @param string $price Regular price. + */ + public function set_regular_price( $price ) { + $this->data['regular_price'] = $price; + } + + /** + * Set the product's sale price. + * + * @since 2.7.0 + * @param string $price sale price. + */ + public function set_sale_price( $price ) { + $this->data['sale_price'] = $price; + } + + /** + * Set date on sale from. + * + * @since 2.7.0 + * @param string $data Sale from date. + */ + public function set_date_on_sale_from( $date ) { + $this->data['date_on_sale_from'] = $date; + } + + /** + * Set date on sale to. + * + * @since 2.7.0 + * @param string $data Sale to date. + */ + public function set_date_on_sale_to( $date ) { + return $this->data['date_on_sale_to'] = $date; + } + + /** + * Set number total of sales. + * + * @since 2.7.0 + * @param int $total Total of sales. + */ + public function set_total_sales( $total ) { + $this->data['total_sales'] = absint( $total ); + } + + /** + * Set the tax status. + * + * @since 2.7.0 + * @throws WC_Data_Exception + * @param string $status Tax status. + */ + public function set_tax_status( $status ) { + $options = array( + 'taxable', + 'shipping', + 'none', + ); + + if ( ! in_array( $status, $options, true ) ) { + $this->error( 'product_invalid_tax_status', __( 'Invalid product tax status.', 'woocommerce' ) ); + } + + $this->data['tax_status'] = $status; + } + + /** + * Set the tax class. + * + * @since 2.7.0 + * @param string $class Tax class. + */ + public function set_tax_class( $class ) { + $this->data['tax_class'] = wc_clean( $class ); + } + + // @TODO: + + /** + * Return if product manage stock. + * + * @since 2.7.0 + * @return string + */ + public function set_manage_stock() { + return $this->data['manage_stock']; + } + + /** + * Returns number of items available for sale. + * + * @return int + */ + public function set_stock_quantity() { + return apply_filters( 'woocommerce_get_stock_quantity', $this->get_manage_stock() ? wc_stock_amount( $this->data['stock_quantity'] ) : null, $this ); + } + + /** + * Set stock status. + * + * @param string $status New status. + */ + public function set_stock_status( $status ) { + $status = 'outofstock' === $status ? 'outofstock' : 'instock'; + + // Sanity check. + if ( $this->managing_stock() ) { + if ( ! $this->backorders_allowed() && $this->get_stock_quantity() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) { + $status = 'outofstock'; + } + } + + if ( update_post_meta( $this->get_id(), '_stock_status', $status ) ) { + $this->data['stock_status'] = $status; + do_action( 'woocommerce_product_set_stock_status', $this->get_id(), $status ); + } + } + + /** + * Get backorders. + * + * @since 2.7.0 + * @return string + */ + public function set_backorders() { + return $this->data['backorders']; + } + + /** + * Return if should be sold individually. + * + * @since 2.7.0 + * @return return + */ + public function set_sold_individually() { + return $this->data['sold_individually']; + } + + /** + * Returns the product's weight. + * + * @return string + */ + public function set_weight() { + // Legacy filter. + $weight = apply_filters( 'woocommerce_product_weight', $this->data['weight'], $this ); + + // New filter. + return apply_filters( 'woocommerce_product_get_weight', $weight, $this ); + } + + /** + * Returns the product length. + * + * @return string + */ + public function set_length() { + // Legacy filter. + $length = apply_filters( 'woocommerce_product_length', $this->data['length'], $this ); + + // New filter since 2.7. + return apply_filters( 'woocommerce_product_get_length', $this->data['length'], $this ); + } + + /** + * Returns the product width. + * + * @return string + */ + public function set_width() { + // Legacy filter. + $length = apply_filters( 'woocommerce_product_width', $this->data['width'], $this ); + + // New filter since 2.7. + return apply_filters( 'woocommerce_product_get_width', $this->data['width'], $this ); + } + + /** + * Returns the product height. + * + * @return string + */ + public function set_height() { + // Legacy filter. + $length = apply_filters( 'woocommerce_product_height', $this->data['height'], $this ); + + // New filter since 2.7. + return apply_filters( 'woocommerce_product_get_height', $this->data['height'], $this ); + } + + /** + * Get Upseels IDs. + * + * @since 2.7.0 + * @return string + */ + public function set_upsell_ids() { + return $this->data['backorders']; + } + + /** + * Get Upseels IDs. + * + * @since 2.7.0 + * @return string + */ + public function set_cross_sell_ids() { + return $this->data['cross_sell_ids']; + } + + /** + * Get parent ID. + * + * @since 2.7.0 + * @return string + */ + public function set_parent_id() { + return $this->data['parent_id']; + } + + /** + * Return if reviews is allowed. + * + * @since 2.7.0 + * @return bool + */ + public function set_reviews_allowed() { + return $this->data['reviews_allowed']; + } + + /** + * Get purchase note. + * + * @since 2.7.0 + * @return string + */ + public function set_purchase_note() { + return $this->data['purchase_note']; + } + + /** + * Returns the product categories. + * + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return string + */ + public function set_categories( $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $this->get_id(), 'product_cat', $before, $sep, $after ); + } + + /** + * Returns the product tags. + * + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return array + */ + public function set_tags( $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $this->get_id(), 'product_tag', $before, $sep, $after ); + } + + /** + * Returns product attributes. + * + * @return array + */ + public function set_attributes() { + $attributes = array_filter( (array) maybe_unserialize( $this->data['product_attributes'] ) ); + $taxonomies = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_name' ); + + // Check for any attributes which have been removed globally + foreach ( $attributes as $key => $attribute ) { + if ( $attribute['is_taxonomy'] ) { + if ( ! in_array( substr( $attribute['name'], 3 ), $taxonomies ) ) { + unset( $attributes[ $key ] ); + } + } + } + + return apply_filters( 'woocommerce_get_product_attributes', $attributes ); + } + + /** + * Get default attributes. + * + * @since 2.7.0 + * @return string + */ + public function set_default_attributes() { + return $this->data['default_attributes']; + } + + /** + * Get menu order. + * + * @since 2.7.0 + * @return int + */ + public function set_menu_order() { + return $this->data['menu_order']; + } + + /* + |-------------------------------------------------------------------------- + | CRUD methods + |-------------------------------------------------------------------------- + | + | Methods which create, read, update and delete products from the database. + | + | A save method is included for convenience (chooses update or create based + | on if the order exists yet). + */ + + /** + * Reads a product from the database and sets its data to the class. + * + * @since 2.7.0 + * @param int $id + */ + public function read( $id ) { + $this->set_defaults(); + + if ( ! $id || ! ( $post_object = get_post( $id ) ) ) { + return; + } + + $this->set_id( $id ); + $this->set_props( array( + 'name' => get_the_title( $post_object ), + 'slug' => $post_object->post_name, + 'permalink' => get_permalink( $post_object ), + 'date_created' => $post_object->post_date, + 'date_modified' => $post_object->post_modified, + 'type' => '', + 'status' => $post_object->post_status, + 'featured' => get_post_meta( $id, '_featured', true ), + 'catalog_visibility' => 'hidden', + 'description' => $post_object->post_content, + 'short_description' => $post_object->post_excerpt, + 'sku' => get_post_meta( $id, '_sku', true ), + 'regular_price' => get_post_meta( $id, '_regular_price', true ), + 'sale_price' => get_post_meta( $id, '_sale_price', true ), + 'date_on_sale_from' => get_post_meta( $id, '_sale_price_dates_from', true ), + 'date_on_sale_to' => get_post_meta( $id, '_sale_price_dates_to', true ), + 'total_sales' => get_post_meta( $id, 'total_sales', true ), + 'tax_status' => get_post_meta( $id, '_tax_status', true ), + 'tax_class' => get_post_meta( $id, '_tax_class', true ), + 'manage_stock' => 'no', + 'stock_quantity' => get_post_meta( $id, '_stock', true ), + 'stock_status' => get_post_meta( $id, '_stock_status', true ), + 'backorders' => get_post_meta( $id, '_backorders', true ), + 'sold_individually' => get_post_meta( $id, '_sold_individually', true ), + 'weight' => get_post_meta( $id, '_weight', true ), + 'length' => get_post_meta( $id, '_length', true ), + 'width' => get_post_meta( $id, '_width', true ), + 'height' => get_post_meta( $id, '_height', true ), + 'upsell_ids' => get_post_meta( $id, '_upsell_ids', true ), + 'cross_sell_ids' => get_post_meta( $id, '_crosssell_ids', true ), + 'parent_id' => $post_object->post_parent, + 'reviews_allowed' => $post_object->comment_status, + 'purchase_note' => get_post_meta( $id, '_purchase_note', true ), + 'attributes' => get_post_meta( $id, '_attributes', true ), + 'default_attributes' => get_post_meta( $id, '_default_attributes', true ), + 'menu_order' => $post_object->menu_order, + ) ); + $this->read_meta_data(); + + do_action( 'woocommerce_product_loaded', $this ); + } + + /** + * Create a new product. + * + * @since 2.7.0 + */ + public function create() { + $this->set_date_created( current_time( 'timestamp' ) ); + + $id = wp_insert_post( apply_filters( 'woocommerce_new_product_data', array( + 'post_type' => 'product', + 'post_status' => 'publish', + 'post_author' => get_current_user_id(), + 'post_title' => $this->get_code(), + 'post_content' => '', + 'post_excerpt' => $this->get_description(), + 'post_date' => date( 'Y-m-d H:i:s', $this->get_date_created() ), + 'post_date_gmt' => get_gmt_from_date( date( 'Y-m-d H:i:s', $this->get_date_created() ) ), + ) ), true ); + + if ( $id ) { + $this->set_id( $id ); + $this->update_post_meta( $id ); + $this->save_meta_data(); + do_action( 'woocommerce_new_product', $id ); + } + } + + /** + * Updates an existing product. + * + * @since 2.7.0 + */ + public function update() { + + } + + /** + * Save data (either create or update depending on if we are working on an existing product). + * + * @since 2.7.0 + */ + public function save() { + if ( $this->get_id() ) { + $this->update(); + } else { + $this->create(); + } + } + + /** + * Delete product from the database. + * + * @since 2.7.0 + */ + public function delete() { + wp_delete_post( $this->get_id() ); + do_action( 'woocommerce_delete_product', $this->get_id() ); + $this->set_id( 0 ); + } + + /** + * Helper method that updates all the post meta for a product based on it's settings in the WC_Product class. + * + * @since 2.7.0 + * @param int $id Object ID. + */ + private function update_post_meta( $id ) { + // update_post_meta( $id, 'discount_type', $this->get_discount_type() ); + } + + /* + |-------------------------------------------------------------------------- + | Other Actions + |-------------------------------------------------------------------------- + */ + /** * Check if a product supports a given feature. * @@ -166,17 +1111,6 @@ class WC_Product { return apply_filters( 'woocommerce_product_supports', in_array( $feature, $this->supports ) ? true : false, $feature, $this ); } - /** - * Return the product ID - * - * @since 2.5.0 - * @return int product (post) ID - */ - public function get_id() { - - return $this->id; - } - /** * Returns the gallery attachment ids. * @@ -186,33 +1120,6 @@ class WC_Product { return apply_filters( 'woocommerce_product_gallery_attachment_ids', array_filter( array_filter( (array) explode( ',', $this->product_image_gallery ) ), 'wp_attachment_is_image' ), $this ); } - /** - * Wrapper for get_permalink. - * - * @return string - */ - public function get_permalink() { - return get_permalink( $this->id ); - } - - /** - * Get SKU (Stock-keeping unit) - product unique ID. - * - * @return string - */ - public function get_sku() { - return apply_filters( 'woocommerce_get_sku', $this->sku, $this ); - } - - /** - * Returns number of items available for sale. - * - * @return int - */ - public function get_stock_quantity() { - return apply_filters( 'woocommerce_get_stock_quantity', $this->managing_stock() ? wc_stock_amount( $this->stock ) : null, $this ); - } - /** * Get total stock - This is the stock of parent and children combined. * @@ -267,23 +1174,23 @@ class WC_Product { if ( ! is_null( $amount ) && $this->managing_stock() ) { // Ensure key exists - add_post_meta( $this->id, '_stock', 0, true ); + add_post_meta( $this->get_id(), '_stock', 0, true ); // Update stock in DB directly switch ( $mode ) { case 'add' : - $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value + %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->id ) ); + $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value + %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->get_id() ) ); break; case 'subtract' : - $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value - %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->id ) ); + $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value - %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->get_id() ) ); break; default : - $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->id ) ); + $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->get_id() ) ); break; } // Clear caches - wp_cache_delete( $this->id, 'post_meta' ); + wp_cache_delete( $this->get_id(), 'post_meta' ); delete_transient( 'wc_low_stock_count' ); delete_transient( 'wc_outofstock_count' ); unset( $this->stock ); @@ -318,37 +1225,6 @@ class WC_Product { return $this->set_stock( $amount, 'add' ); } - /** - * Set stock status of the product. - * - * @param string $status - */ - public function set_stock_status( $status ) { - - $status = ( 'outofstock' === $status ) ? 'outofstock' : 'instock'; - - // Sanity check - if ( $this->managing_stock() ) { - if ( ! $this->backorders_allowed() && $this->get_stock_quantity() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) { - $status = 'outofstock'; - } - } - - if ( update_post_meta( $this->id, '_stock_status', $status ) ) { - $this->stock_status = $status; - do_action( 'woocommerce_product_set_stock_status', $this->id, $status ); - } - } - - /** - * Return the product type. - * - * @return string - */ - public function get_type() { - return is_null( $this->product_type ) ? '' : $this->product_type; - } - /** * Checks the product type. * @@ -548,31 +1424,13 @@ class WC_Product { return $this->get_tax_status() === 'taxable' || $this->get_tax_status() === 'shipping' ? true : false; } - /** - * Get the title of the post. - * - * @return string - */ - public function get_title() { - return apply_filters( 'woocommerce_product_title', $this->post ? $this->post->post_title : '', $this ); - } - - /** - * Get the parent of the post. - * - * @return int - */ - public function get_parent() { - return apply_filters( 'woocommerce_product_parent', absint( $this->post->post_parent ), $this ); - } - /** * Get the add to url used mainly in loops. * * @return string */ public function add_to_cart_url() { - return apply_filters( 'woocommerce_product_add_to_cart_url', get_permalink( $this->id ), $this ); + return apply_filters( 'woocommerce_product_add_to_cart_url', get_permalink( $this->get_id() ), $this ); } /** @@ -599,7 +1457,9 @@ class WC_Product { * @return bool */ public function managing_stock() { - return ( ! isset( $this->manage_stock ) || 'no' === $this->manage_stock || 'yes' !== get_option( 'woocommerce_manage_stock' ) ) ? false : true; + $managing_stock = 'no' === $this->get_manage_stock() || 'yes' !== get_option( 'woocommerce_manage_stock' ); + + return ! $managing_stock; } /** @@ -617,7 +1477,7 @@ class WC_Product { * @return bool */ public function backorders_allowed() { - return apply_filters( 'woocommerce_product_backorders_allowed', ( 'yes' === $this->backorders || 'notify' === $this->backorders ), $this->id, $this ); + return apply_filters( 'woocommerce_product_backorders_allowed', ( 'yes' === $this->get_backorders() || 'notify' === $this->get_backorders() ), $this->get_id(), $this ); } /** @@ -722,6 +1582,7 @@ class WC_Product { } else { $class = 'in-stock'; } + return apply_filters( 'woocommerce_get_availability_class', $class, $this ); } @@ -731,7 +1592,7 @@ class WC_Product { * @return bool */ public function is_featured() { - return ( 'yes' === $this->featured ) ? true : false; + return 'yes' === $this->get_featured(); } /** @@ -743,28 +1604,28 @@ class WC_Product { if ( ! $this->post ) { $visible = false; - // Published/private - } elseif ( 'publish' !== $this->post->post_status && ! current_user_can( 'edit_post', $this->id ) ) { + // Published/private. + } elseif ( 'publish' !== $this->post->post_status && ! current_user_can( 'edit_post', $this->get_id() ) ) { $visible = false; - // Out of stock visibility + // Out of stock visibility. } elseif ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ! $this->is_in_stock() ) { $visible = false; - // visibility setting + // visibility setting. } elseif ( 'hidden' === $this->visibility ) { $visible = false; } elseif ( 'visible' === $this->visibility ) { $visible = true; - // Visibility in loop + // Visibility in loop. } elseif ( is_search() ) { $visible = 'search' === $this->visibility; } else { $visible = 'catalog' === $this->visibility; } - return apply_filters( 'woocommerce_product_is_visible', $visible, $this->id ); + return apply_filters( 'woocommerce_product_is_visible', $visible, $this->get_id() ); } /** @@ -785,16 +1646,16 @@ class WC_Product { $purchasable = true; - // Products must exist of course + // Products must exist of course. if ( ! $this->exists() ) { $purchasable = false; - // Other products types need a price to be set + // Other products types need a price to be set. } elseif ( $this->get_price() === '' ) { $purchasable = false; - // Check the product is published - } elseif ( 'publish' !== $this->post->post_status && ! current_user_can( 'edit_post', $this->id ) ) { + // Check the product is published. + } elseif ( 'publish' !== $this->post->post_status && ! current_user_can( 'edit_post', $this->get_id() ) ) { $purchasable = false; } @@ -819,24 +1680,6 @@ class WC_Product { $this->price = $this->price + $price; } - /** - * Returns the product's sale price. - * - * @return string price - */ - public function get_sale_price() { - return apply_filters( 'woocommerce_get_sale_price', $this->sale_price, $this ); - } - - /** - * Returns the product's regular price. - * - * @return string price - */ - public function get_regular_price() { - return apply_filters( 'woocommerce_get_regular_price', $this->regular_price, $this ); - } - /** * Returns the product's active price. * @@ -1059,35 +1902,17 @@ class WC_Product { return apply_filters( 'woocommerce_get_price_html_from_to', $price, $from, $to, $this ); } - /** - * Returns the tax class. - * - * @return string - */ - public function get_tax_class() { - return apply_filters( 'woocommerce_product_tax_class', $this->tax_class, $this ); - } - - /** - * Returns the tax status. - * - * @return string - */ - public function get_tax_status() { - return $this->tax_status; - } - /** * Get the average rating of product. This is calculated once and stored in postmeta. * @return string */ public function get_average_rating() { // No meta data? Do the calculation - if ( ! metadata_exists( 'post', $this->id, '_wc_average_rating' ) ) { - $this->sync_average_rating( $this->id ); + if ( ! metadata_exists( 'post', $this->get_id(), '_wc_average_rating' ) ) { + $this->sync_average_rating( $this->get_id() ); } - return (string) floatval( get_post_meta( $this->id, '_wc_average_rating', true ) ); + return (string) floatval( get_post_meta( $this->get_id(), '_wc_average_rating', true ) ); } /** @@ -1097,11 +1922,11 @@ class WC_Product { */ public function get_rating_count( $value = null ) { // No meta data? Do the calculation - if ( ! metadata_exists( 'post', $this->id, '_wc_rating_count' ) ) { - $this->sync_rating_count( $this->id ); + if ( ! metadata_exists( 'post', $this->get_id(), '_wc_rating_count' ) ) { + $this->sync_rating_count( $this->get_id() ); } - $counts = get_post_meta( $this->id, '_wc_rating_count', true ); + $counts = get_post_meta( $this->get_id(), '_wc_rating_count', true ); if ( is_null( $value ) ) { return array_sum( $counts ); @@ -1200,17 +2025,17 @@ class WC_Product { global $wpdb; // No meta date? Do the calculation - if ( ! metadata_exists( 'post', $this->id, '_wc_review_count' ) ) { + if ( ! metadata_exists( 'post', $this->get_id(), '_wc_review_count' ) ) { $count = $wpdb->get_var( $wpdb->prepare(" SELECT COUNT(*) FROM $wpdb->comments WHERE comment_parent = 0 AND comment_post_ID = %d AND comment_approved = '1' - ", $this->id ) ); + ", $this->get_id() ) ); - update_post_meta( $this->id, '_wc_review_count', $count ); + update_post_meta( $this->get_id(), '_wc_review_count', $count ); } else { - $count = get_post_meta( $this->id, '_wc_review_count', true ); + $count = get_post_meta( $this->get_id(), '_wc_review_count', true ); } return apply_filters( 'woocommerce_product_review_count', $count, $this ); @@ -1234,30 +2059,6 @@ class WC_Product { return apply_filters( 'woocommerce_product_crosssell_ids', (array) maybe_unserialize( $this->crosssell_ids ), $this ); } - /** - * Returns the product categories. - * - * @param string $sep (default: ', ') - * @param string $before (default: '') - * @param string $after (default: '') - * @return string - */ - public function get_categories( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->id, 'product_cat', $before, $sep, $after ); - } - - /** - * Returns the product tags. - * - * @param string $sep (default: ', ') - * @param string $before (default: '') - * @param string $after (default: '') - * @return array - */ - public function get_tags( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->id, 'product_tag', $before, $sep, $after ); - } - /** * Returns the product shipping class. * @@ -1267,7 +2068,7 @@ class WC_Product { if ( ! $this->shipping_class ) { - $classes = get_the_terms( $this->id, 'product_shipping_class' ); + $classes = get_the_terms( $this->get_id(), 'product_shipping_class' ); if ( $classes && ! is_wp_error( $classes ) ) { $this->shipping_class = current( $classes )->slug; @@ -1288,7 +2089,7 @@ class WC_Product { if ( ! $this->shipping_class_id ) { - $classes = get_the_terms( $this->id, 'product_shipping_class' ); + $classes = get_the_terms( $this->get_id(), 'product_shipping_class' ); if ( $classes && ! is_wp_error( $classes ) ) { $this->shipping_class_id = current( $classes )->term_id; @@ -1317,7 +2118,7 @@ class WC_Product { public function get_related( $limit = 5 ) { global $wpdb; - $transient_name = 'wc_related_' . $this->id; + $transient_name = 'wc_related_' . $this->get_id(); $related_posts = get_transient( $transient_name ); $limit = $limit > 0 ? $limit : 5; @@ -1332,7 +2133,7 @@ class WC_Product { $related_posts = array(); } else { // Sanitize - $exclude_ids = array_map( 'absint', array_merge( array( 0, $this->id ), $this->get_upsells() ) ); + $exclude_ids = array_map( 'absint', array_merge( array( 0, $this->get_id() ), $this->get_upsells() ) ); // Generate query - but query an extra 10 results to give the appearance of random results $query = $this->build_related_query( $cats_array, $tags_array, $exclude_ids, $limit + 10 ); @@ -1369,7 +2170,7 @@ class WC_Product { if ( isset( $attribute['is_taxonomy'] ) && $attribute['is_taxonomy'] ) { - return implode( ', ', wc_get_product_terms( $this->id, $attribute['name'], array( 'fields' => 'names' ) ) ); + return implode( ', ', wc_get_product_terms( $this->get_id(), $attribute['name'], array( 'fields' => 'names' ) ) ); } else { @@ -1380,27 +2181,6 @@ class WC_Product { return ''; } - /** - * Returns product attributes. - * - * @return array - */ - public function get_attributes() { - $attributes = array_filter( (array) maybe_unserialize( $this->product_attributes ) ); - $taxonomies = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_name' ); - - // Check for any attributes which have been removed globally - foreach ( $attributes as $key => $attribute ) { - if ( $attribute['is_taxonomy'] ) { - if ( ! in_array( substr( $attribute['name'], 3 ), $taxonomies ) ) { - unset( $attributes[ $key ] ); - } - } - } - - return apply_filters( 'woocommerce_get_product_attributes', $attributes ); - } - /** * Returns whether or not the product has any attributes set. * @@ -1441,46 +2221,14 @@ class WC_Product { /** * Does a child have dimensions set? + * * @since 2.7.0 - * @return boolean + * @return bool */ public function child_has_dimensions() { return false; } - /** - * Returns the product length. - * @return string - */ - public function get_length() { - return apply_filters( 'woocommerce_product_length', $this->length ? $this->length : '', $this ); - } - - /** - * Returns the product width. - * @return string - */ - public function get_width() { - return apply_filters( 'woocommerce_product_width', $this->width ? $this->width : '', $this ); - } - - /** - * Returns the product height. - * @return string - */ - public function get_height() { - return apply_filters( 'woocommerce_product_height', $this->height ? $this->height : '', $this ); - } - - /** - * Returns the product's weight. - * @todo refactor filters in this class to naming woocommerce_product_METHOD - * @return string - */ - public function get_weight() { - return apply_filters( 'woocommerce_product_weight', apply_filters( 'woocommerce_product_get_weight', $this->weight ? $this->weight : '' ), $this ); - } - /** * Returns whether or not the product has weight set. * @@ -1533,9 +2281,9 @@ class WC_Product { */ public function get_image_id() { - if ( has_post_thumbnail( $this->id ) ) { - $image_id = get_post_thumbnail_id( $this->id ); - } elseif ( ( $parent_id = wp_get_post_parent_id( $this->id ) ) && has_post_thumbnail( $parent_id ) ) { + if ( has_post_thumbnail( $this->get_id() ) ) { + $image_id = get_post_thumbnail_id( $this->get_id() ); + } elseif ( ( $parent_id = wp_get_post_parent_id( $this->get_id() ) ) && has_post_thumbnail( $parent_id ) ) { $image_id = get_post_thumbnail_id( $parent_id ); } else { $image_id = 0; @@ -1553,9 +2301,9 @@ class WC_Product { * @return string */ public function get_image( $size = 'shop_thumbnail', $attr = array(), $placeholder = true ) { - if ( has_post_thumbnail( $this->id ) ) { - $image = get_the_post_thumbnail( $this->id, $size, $attr ); - } elseif ( ( $parent_id = wp_get_post_parent_id( $this->id ) ) && has_post_thumbnail( $parent_id ) ) { + if ( has_post_thumbnail( $this->get_id() ) ) { + $image = get_the_post_thumbnail( $this->get_id(), $size, $attr ); + } elseif ( ( $parent_id = wp_get_post_parent_id( $this->get_id() ) ) && has_post_thumbnail( $parent_id ) ) { $image = get_the_post_thumbnail( $parent_id, $size, $attr ); } elseif ( $placeholder ) { $image = wc_placeholder_img( $size ); @@ -1574,7 +2322,7 @@ class WC_Product { if ( $this->get_sku() ) { $identifier = $this->get_sku(); } else { - $identifier = '#' . $this->id; + $identifier = '#' . $this->get_id(); } return sprintf( '%s – %s', $identifier, $this->get_title() ); @@ -1589,7 +2337,7 @@ class WC_Product { protected function get_related_terms( $term ) { $terms_array = array( 0 ); - $terms = apply_filters( 'woocommerce_get_related_' . $term . '_terms', wp_get_post_terms( $this->id, $term ), $this->id ); + $terms = apply_filters( 'woocommerce_get_related_' . $term . '_terms', wp_get_post_terms( $this->get_id(), $term ), $this->get_id() ); foreach ( $terms as $term ) { $terms_array[] = $term->term_id; } @@ -1632,8 +2380,8 @@ class WC_Product { $query['where'] .= " AND pm2.meta_value = 'instock'"; } - $relate_by_category = apply_filters( 'woocommerce_product_related_posts_relate_by_category', true, $this->id ); - $relate_by_tag = apply_filters( 'woocommerce_product_related_posts_relate_by_tag', true, $this->id ); + $relate_by_category = apply_filters( 'woocommerce_product_related_posts_relate_by_category', true, $this->get_id() ); + $relate_by_tag = apply_filters( 'woocommerce_product_related_posts_relate_by_tag', true, $this->get_id() ); if ( $relate_by_category || $relate_by_tag ) { $query['where'] .= ' AND ('; @@ -1653,7 +2401,7 @@ class WC_Product { } $query['limits'] = " LIMIT {$limit} "; - $query = apply_filters( 'woocommerce_product_related_posts_query', $query, $this->id ); + $query = apply_filters( 'woocommerce_product_related_posts_query', $query, $this->get_id() ); return $query; } From 42b96346b508d89a405d56fded15f8a3f220f179 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Thu, 29 Sep 2016 20:02:50 -0300 Subject: [PATCH 006/163] Product set methods --- includes/abstracts/abstract-wc-product.php | 268 ++++++++++----------- 1 file changed, 127 insertions(+), 141 deletions(-) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 1dab80acb8f..92940c6b5f7 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -60,8 +60,8 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'parent_id' => 0, 'reviews_allowed' => true, 'purchase_note' => '', - 'attributes' => '', - 'default_attributes' => '', + 'attributes' => array(), + 'default_attributes' => array(), 'menu_order' => 0, ); @@ -331,7 +331,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * Return if should be sold individually. * * @since 2.7.0 - * @return return + * @return string */ public function get_sold_individually() { return $this->data['sold_individually']; @@ -360,7 +360,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { $length = apply_filters( 'woocommerce_product_length', $this->data['length'], $this ); // New filter since 2.7. - return apply_filters( 'woocommerce_product_get_length', $this->data['length'], $this ); + return apply_filters( 'woocommerce_product_get_length', $length, $this ); } /** @@ -370,10 +370,10 @@ class WC_Product extends WC_Abstract_Legacy_Product { */ public function get_width() { // Legacy filter. - $length = apply_filters( 'woocommerce_product_width', $this->data['width'], $this ); + $width = apply_filters( 'woocommerce_product_width', $this->data['width'], $this ); // New filter since 2.7. - return apply_filters( 'woocommerce_product_get_width', $this->data['width'], $this ); + return apply_filters( 'woocommerce_product_get_width', $width, $this ); } /** @@ -383,10 +383,10 @@ class WC_Product extends WC_Abstract_Legacy_Product { */ public function get_height() { // Legacy filter. - $length = apply_filters( 'woocommerce_product_height', $this->data['height'], $this ); + $height = apply_filters( 'woocommerce_product_height', $this->data['height'], $this ); // New filter since 2.7. - return apply_filters( 'woocommerce_product_get_height', $this->data['height'], $this ); + return apply_filters( 'woocommerce_product_get_height', $height, $this ); } /** @@ -396,7 +396,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return string */ public function get_upsell_ids() { - return $this->data['backorders']; + return $this->data['upsell_ids']; } /** @@ -413,7 +413,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * Get parent ID. * * @since 2.7.0 - * @return string + * @return int */ public function get_parent_id() { return $this->data['parent_id']; @@ -488,7 +488,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * Get default attributes. * * @since 2.7.0 - * @return string + * @return array */ public function get_default_attributes() { return $this->data['default_attributes']; @@ -555,7 +555,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { } /** - * Get product modified date. + * Set product modified date. * * @since 2.7.0 * @param string $timestamp Timestamp. @@ -565,7 +565,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { } /** - * Return the product type. + * Set the product type. * * @return string */ @@ -574,7 +574,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { } /** - * Get product status. + * Set product status. * * @since 2.7.0 * @param string $status Product status. @@ -725,25 +725,24 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->data['tax_class'] = wc_clean( $class ); } - // @TODO: - /** - * Return if product manage stock. + * Set if product manage stock. * * @since 2.7.0 - * @return string + * @param string $manage_stock Options: 'yes' or 'no'. */ - public function set_manage_stock() { - return $this->data['manage_stock']; + public function set_manage_stock( $manage_stock ) { + $this->data['manage_stock'] = $manage_stock; } /** - * Returns number of items available for sale. + * Set number of items available for sale. * - * @return int + * @since 2.7.0 + * @param float|null $quantity Stock quantity. */ - public function set_stock_quantity() { - return apply_filters( 'woocommerce_get_stock_quantity', $this->get_manage_stock() ? wc_stock_amount( $this->data['stock_quantity'] ) : null, $this ); + public function set_stock_quantity( $quantity ) { + $this->data['stock_quantity'] = $quantity; } /** @@ -768,190 +767,163 @@ class WC_Product extends WC_Abstract_Legacy_Product { } /** - * Get backorders. + * Set backorders. * * @since 2.7.0 - * @return string + * @param string $backorders Options: 'yes', 'no' or 'notify'. */ - public function set_backorders() { - return $this->data['backorders']; + public function set_backorders( $backorders ) { + $this->data['backorders'] = $backorders; } /** - * Return if should be sold individually. + * Set if should be sold individually. * * @since 2.7.0 - * @return return + * @param string $sold_individually Options: 'yes' or 'no'. */ - public function set_sold_individually() { - return $this->data['sold_individually']; + public function set_sold_individually( $sold_individually ) { + $this->data['sold_individually'] = $sold_individually; } /** - * Returns the product's weight. - * - * @return string - */ - public function set_weight() { - // Legacy filter. - $weight = apply_filters( 'woocommerce_product_weight', $this->data['weight'], $this ); - - // New filter. - return apply_filters( 'woocommerce_product_get_weight', $weight, $this ); - } - - /** - * Returns the product length. - * - * @return string - */ - public function set_length() { - // Legacy filter. - $length = apply_filters( 'woocommerce_product_length', $this->data['length'], $this ); - - // New filter since 2.7. - return apply_filters( 'woocommerce_product_get_length', $this->data['length'], $this ); - } - - /** - * Returns the product width. - * - * @return string - */ - public function set_width() { - // Legacy filter. - $length = apply_filters( 'woocommerce_product_width', $this->data['width'], $this ); - - // New filter since 2.7. - return apply_filters( 'woocommerce_product_get_width', $this->data['width'], $this ); - } - - /** - * Returns the product height. - * - * @return string - */ - public function set_height() { - // Legacy filter. - $length = apply_filters( 'woocommerce_product_height', $this->data['height'], $this ); - - // New filter since 2.7. - return apply_filters( 'woocommerce_product_get_height', $this->data['height'], $this ); - } - - /** - * Get Upseels IDs. + * Set the product's weight. * * @since 2.7.0 - * @return string + * @param float $weigth Total weigth. */ - public function set_upsell_ids() { - return $this->data['backorders']; + public function set_weight( $weight ) { + $this->data['weight'] = $weight; } /** - * Get Upseels IDs. + * Set the product length. * * @since 2.7.0 - * @return string + * @param float $weigth Total weigth. */ - public function set_cross_sell_ids() { - return $this->data['cross_sell_ids']; + public function set_length( $length ) { + $this->data['length'] = $length; } /** - * Get parent ID. + * Set the product width. * * @since 2.7.0 - * @return string + * @param float $width Total width. */ - public function set_parent_id() { - return $this->data['parent_id']; + public function set_width( $width ) { + $this->data['width'] = $width; } /** - * Return if reviews is allowed. + * Set the product height. * * @since 2.7.0 - * @return bool + * @param float $height Total height. */ - public function set_reviews_allowed() { - return $this->data['reviews_allowed']; + public function set_height( $height ) { + $this->data['height'] = $height; } /** - * Get purchase note. + * Set Upseels IDs. * * @since 2.7.0 - * @return string + * @param string $upsell_ids IDs from the up-sell products. + */ + public function set_upsell_ids( $upsell_ids ) { + $this->data['upsell_ids'] = $upsell_ids; + } + + /** + * Set Upseels IDs. + * + * @since 2.7.0 + * @param string $cross_sell_ids IDs from the cross-sell products. + */ + public function set_cross_sell_ids( $cross_sell_ids ) { + $this->data['cross_sell_ids'] = $cross_sell_ids; + } + + /** + * Set parent ID. + * + * @since 2.7.0 + * @param int $parent_id Product parent ID. + */ + public function set_parent_id( $parent_id ) { + $this->data['parent_id'] = absint( $parent_id ); + } + + /** + * Set if reviews is allowed. + * + * @since 2.7.0 + * @param bool $reviews_allowed Reviews allowed or not. + */ + public function set_reviews_allowed( $reviews_allowed ) { + $this->data['reviews_allowed'] = (bool) $reviews_allowed; + } + + /** + * Set purchase note. + * + * @since 2.7.0 + * @param string $purchase_note Purchase note. */ public function set_purchase_note() { - return $this->data['purchase_note']; + $this->data['purchase_note'] = $purchase_note; } /** - * Returns the product categories. + * Set the product categories. * - * @param string $sep (default: ', '). - * @param string $before (default: ''). - * @param string $after (default: ''). - * @return string + * @since 2.7.0 + * @param array $terms_id List of terms IDs. */ - public function set_categories( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->get_id(), 'product_cat', $before, $sep, $after ); + public function set_categories( $terms_id ) { + $this->save_taxonomy_terms( $terms_id, 'cat' ); } /** - * Returns the product tags. + * Set the product tags. * - * @param string $sep (default: ', '). - * @param string $before (default: ''). - * @param string $after (default: ''). - * @return array + * @since 2.7.0 + * @param array $terms_id List of terms IDs. */ - public function set_tags( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->get_id(), 'product_tag', $before, $sep, $after ); + public function set_tags( $terms_id ) { + $this->save_taxonomy_terms( $terms_id, 'tag' ); } /** * Returns product attributes. * - * @return array + * @since 2.7.0 + * @param array $attributes List of product attributes. */ - public function set_attributes() { - $attributes = array_filter( (array) maybe_unserialize( $this->data['product_attributes'] ) ); - $taxonomies = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_name' ); - - // Check for any attributes which have been removed globally - foreach ( $attributes as $key => $attribute ) { - if ( $attribute['is_taxonomy'] ) { - if ( ! in_array( substr( $attribute['name'], 3 ), $taxonomies ) ) { - unset( $attributes[ $key ] ); - } - } - } - - return apply_filters( 'woocommerce_get_product_attributes', $attributes ); + public function set_attributes( $attributes ) { + $this->data['product_attributes'] = $attributes; } /** - * Get default attributes. + * Set default attributes. * * @since 2.7.0 - * @return string + * @param array $default_attributes List of default attributes. */ - public function set_default_attributes() { - return $this->data['default_attributes']; + public function set_default_attributes( $default_attributes ) { + $this->data['default_attributes'] = $default_attributes; } /** - * Get menu order. + * Set menu order. * * @since 2.7.0 - * @return int + * @param int $menu_order Menu order. */ - public function set_menu_order() { - return $this->data['menu_order']; + public function set_menu_order( $menu_order ) { + $this->data['menu_order'] = intval( $menu_order ); } /* @@ -969,7 +941,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * Reads a product from the database and sets its data to the class. * * @since 2.7.0 - * @param int $id + * @param int $id Product ID. */ public function read( $id ) { $this->set_defaults(); @@ -2405,4 +2377,18 @@ class WC_Product extends WC_Abstract_Legacy_Product { return $query; } + + /** + * Save taxonomy terms. + * + * @since 2.7.0 + * @param array $terms_id Terms ID. + * @param string $taxonomy Taxonomy. + * @return array|WP_Error + */ + protected function save_taxonomy_terms( $terms_id, $taxonomy = 'cat' ) { + $terms_id = array_unique( array_map( 'intval', $terms_id ) ); + + return wp_set_object_terms( $this->get_id(), $terms_id, 'product_' . $taxonomy ); + } } From 60b136130509bc04567042781d49eae0f400bd18 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Thu, 29 Sep 2016 20:16:42 -0300 Subject: [PATCH 007/163] Fixed several erros while setting data --- includes/abstracts/abstract-wc-product.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 92940c6b5f7..d09b0354a89 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -637,7 +637,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $sku Product SKU. */ public function set_sku( $sku ) { - if ( ! wc_product_has_unique_sku( $this->get_id(), $sku ) ) { + if ( ! empty( $sku ) && ! wc_product_has_unique_sku( $this->get_id(), $sku ) ) { $this->error( 'product_invalid_sku', __( 'Invalid or duplicated SKU.', 'woocommerce' ) ); } @@ -708,6 +708,11 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'none', ); + // Set default if empty. + if ( empty( $status ) ) { + $status = 'taxable'; + } + if ( ! in_array( $status, $options, true ) ) { $this->error( 'product_invalid_tax_status', __( 'Invalid product tax status.', 'woocommerce' ) ); } @@ -761,9 +766,10 @@ class WC_Product extends WC_Abstract_Legacy_Product { } if ( update_post_meta( $this->get_id(), '_stock_status', $status ) ) { - $this->data['stock_status'] = $status; do_action( 'woocommerce_product_set_stock_status', $this->get_id(), $status ); } + + $this->data['stock_status'] = $status; } /** @@ -872,7 +878,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @since 2.7.0 * @param string $purchase_note Purchase note. */ - public function set_purchase_note() { + public function set_purchase_note( $purchase_note ) { $this->data['purchase_note'] = $purchase_note; } From 30a164ea670ccb7fb95dc5d2f455ffc6f195a016 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 17 Oct 2016 12:22:23 +0100 Subject: [PATCH 008/163] Hardcode the get_type per product class --- .../abstracts/abstract-wc-legacy-product.php | 7 ++++ includes/abstracts/abstract-wc-product.php | 39 +++++++++---------- includes/class-wc-product-external.php | 11 ++---- includes/class-wc-product-grouped.php | 11 ++---- includes/class-wc-product-simple.php | 14 ++++++- includes/class-wc-product-variable.php | 10 ++--- includes/class-wc-product-variation.php | 9 ++++- 7 files changed, 58 insertions(+), 43 deletions(-) diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index 2c35bbbbb3a..3399a05320e 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -84,6 +84,13 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { case 'post' : $value = get_post( $this->get_id() ); break; + + case 'product_type' : // @todo What do we do with 3rd party use of product_type now it's hardcoded? + $value = $this->get_type(); + break; + + + default : $value = get_post_meta( $this->id, '_' . $key, true ); diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index d09b0354a89..17747c7306d 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -32,7 +32,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'permalink' => '', 'date_created' => '', 'date_modified' => '', - 'type' => '', 'status' => '', 'featured' => 'no', 'catalog_visibility' => 'hidden', @@ -115,6 +114,24 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_product_get_name', $this->data['name'], $this ); } + /** + * Get internal type. + * @since 2.7.0 + * @return string + */ + public function get_type() { + return 'simple'; + } + + // @todo below. + + + + + + + + /** * Get product slug. * @@ -154,15 +171,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { return $this->data['date_modified']; } - /** - * Return the product type. - * - * @return string - */ - public function get_type() { - return $this->data['type']; - } - /** * Get product status. * @@ -564,15 +572,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->data['date_modified'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ); } - /** - * Set the product type. - * - * @return string - */ - public function set_type( $type ) { - $this->data['type'] = $type; - } - /** * Set product status. * @@ -1212,7 +1211,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return bool */ public function is_type( $type ) { - return ( $this->product_type == $type || ( is_array( $type ) && in_array( $this->product_type, $type ) ) ) ? true : false; + return ( $this->get_type() === $type || ( is_array( $type ) && in_array( $this->get_type(), $type ) ) ); } /** diff --git a/includes/class-wc-product-external.php b/includes/class-wc-product-external.php index c2ea1351e8b..94a66583d07 100644 --- a/includes/class-wc-product-external.php +++ b/includes/class-wc-product-external.php @@ -18,14 +18,11 @@ if ( ! defined( 'ABSPATH' ) ) { class WC_Product_External extends WC_Product { /** - * Constructor. - * - * @access public - * @param mixed $product + * Get internal type. + * @return string */ - public function __construct( $product ) { - $this->product_type = 'external'; - parent::__construct( $product ); + public function get_type() { + return 'external'; } /** diff --git a/includes/class-wc-product-grouped.php b/includes/class-wc-product-grouped.php index 2814bfe0e41..e5a3b8f4c14 100644 --- a/includes/class-wc-product-grouped.php +++ b/includes/class-wc-product-grouped.php @@ -21,14 +21,11 @@ class WC_Product_Grouped extends WC_Product { public $children; /** - * Constructor. - * - * @access public - * @param mixed $product + * Get internal type. + * @return string */ - public function __construct( $product ) { - $this->product_type = 'grouped'; - parent::__construct( $product ); + public function get_type() { + return 'grouped'; } /** diff --git a/includes/class-wc-product-simple.php b/includes/class-wc-product-simple.php index edb8fe6673a..1852f7aa6c2 100644 --- a/includes/class-wc-product-simple.php +++ b/includes/class-wc-product-simple.php @@ -10,7 +10,6 @@ if ( ! defined( 'ABSPATH' ) ) { * The default product type kinda product. * * @class WC_Product_Simple - * @version 2.0.0 * @package WooCommerce/Classes/Products * @category Class * @author WooThemes @@ -23,11 +22,22 @@ class WC_Product_Simple extends WC_Product { * @param mixed $product */ public function __construct( $product ) { - $this->product_type = 'simple'; $this->supports[] = 'ajax_add_to_cart'; parent::__construct( $product ); } + /** + * Get internal type. + * @return string + */ + public function get_type() { + return 'simple'; + } + + + + + /** * Get the add to url used mainly in loops. * diff --git a/includes/class-wc-product-variable.php b/includes/class-wc-product-variable.php index 80ef48fdd72..9d58aedd266 100644 --- a/includes/class-wc-product-variable.php +++ b/includes/class-wc-product-variable.php @@ -24,13 +24,11 @@ class WC_Product_Variable extends WC_Product { private $prices_array = array(); /** - * Constructor. - * - * @param mixed $product + * Get internal type. + * @return string */ - public function __construct( $product ) { - $this->product_type = 'variable'; - parent::__construct( $product ); + public function get_type() { + return 'variable'; } /** diff --git a/includes/class-wc-product-variation.php b/includes/class-wc-product-variation.php index c9e1152a055..ba9dfd45e93 100644 --- a/includes/class-wc-product-variation.php +++ b/includes/class-wc-product-variation.php @@ -85,7 +85,6 @@ class WC_Product_Variation extends WC_Product { throw new Exception( sprintf( 'No parent product set for variation #%d', $this->variation_id ), 422 ); } - $this->product_type = 'variation'; $this->parent = ! empty( $args['parent'] ) ? $args['parent'] : wc_get_product( $this->id ); $this->post = ! empty( $this->parent->post ) ? $this->parent->post : array(); @@ -95,6 +94,14 @@ class WC_Product_Variation extends WC_Product { } } + /** + * Get internal type. + * @return string + */ + public function get_type() { + return 'variation'; + } + /** * __isset function. * From a74de70dc3e66fa6b64cdf390d04941c0be6e8cf Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 17 Oct 2016 14:46:46 +0100 Subject: [PATCH 009/163] Initial look through getters and setters and abstract data --- includes/abstracts/abstract-wc-product.php | 236 ++++++++++----------- 1 file changed, 116 insertions(+), 120 deletions(-) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 17747c7306d..104e40ce1d1 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -29,11 +29,11 @@ class WC_Product extends WC_Abstract_Legacy_Product { protected $data = array( 'name' => '', 'slug' => '', - 'permalink' => '', + //'permalink' => '', 'date_created' => '', 'date_modified' => '', 'status' => '', - 'featured' => 'no', + 'featured' => false, 'catalog_visibility' => 'hidden', 'description' => '', 'short_description' => '', @@ -45,17 +45,17 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'total_sales' => '', 'tax_status' => 'taxable', 'tax_class' => '', - 'manage_stock' => 'no', + 'manage_stock' => false, 'stock_quantity' => null, 'stock_status' => '', 'backorders' => 'no', - 'sold_individually' => 'no', + 'sold_individually' => false, 'weight' => '', 'length' => '', 'width' => '', 'height' => '', - 'upsell_ids' => '', - 'cross_sell_ids' => '', + 'upsell_ids' => array(), + 'cross_sell_ids' => array(), 'parent_id' => 0, 'reviews_allowed' => true, 'purchase_note' => '', @@ -96,6 +96,23 @@ class WC_Product extends WC_Abstract_Legacy_Product { } } + /** + * Get internal type. + * @since 2.7.0 + * @return string + */ + public function get_type() { + return 'simple'; + } + + /** + * Product permalink. + * @return string + */ + public function get_permalink() { + return get_permalink( $this->get_id() ); + } + /* |-------------------------------------------------------------------------- | Getters @@ -114,27 +131,8 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_product_get_name', $this->data['name'], $this ); } - /** - * Get internal type. - * @since 2.7.0 - * @return string - */ - public function get_type() { - return 'simple'; - } - - // @todo below. - - - - - - - - /** * Get product slug. - * * @since 2.7.0 * @return string */ @@ -142,15 +140,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { return $this->data['slug']; } - /** - * Product permalink. - * - * @return string - */ - public function get_permalink() { - return $this->data['permalink']; - } - /** * Get product created date. * @@ -185,7 +174,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * If the product is featured. * * @since 2.7.0 - * @return string + * @return boolean */ public function get_featured() { return $this->data['featured']; @@ -300,7 +289,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * Return if product manage stock. * * @since 2.7.0 - * @return string + * @return boolean */ public function get_manage_stock() { return $this->data['manage_stock']; @@ -309,7 +298,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { /** * Returns number of items available for sale. * - * @return int + * @return int|null */ public function get_stock_quantity() { return apply_filters( 'woocommerce_get_stock_quantity', $this->get_manage_stock() ? wc_stock_amount( $this->data['stock_quantity'] ) : null, $this ); @@ -329,7 +318,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * Get backorders. * * @since 2.7.0 - * @return string + * @return string yes no or notify */ public function get_backorders() { return $this->data['backorders']; @@ -339,7 +328,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * Return if should be sold individually. * * @since 2.7.0 - * @return string + * @return boolean */ public function get_sold_individually() { return $this->data['sold_individually']; @@ -352,7 +341,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { */ public function get_weight() { // Legacy filter. - $weight = apply_filters( 'woocommerce_product_weight', $this->data['weight'], $this ); + $weight = apply_filters( 'woocommerce_product_weight', $this->data['weight'], $this ); // @todo standardize these filter names and move BW compat to deprecated class file. // New filter. return apply_filters( 'woocommerce_product_get_weight', $weight, $this ); @@ -398,20 +387,20 @@ class WC_Product extends WC_Abstract_Legacy_Product { } /** - * Get Upseels IDs. + * Get upsel IDs. * * @since 2.7.0 - * @return string + * @return array */ public function get_upsell_ids() { return $this->data['upsell_ids']; } /** - * Get Upseels IDs. + * Get cross sell IDs. * * @since 2.7.0 - * @return string + * @return array */ public function get_cross_sell_ids() { return $this->data['cross_sell_ids']; @@ -447,37 +436,13 @@ class WC_Product extends WC_Abstract_Legacy_Product { return $this->data['purchase_note']; } - /** - * Returns the product categories. - * - * @param string $sep (default: ', '). - * @param string $before (default: ''). - * @param string $after (default: ''). - * @return string - */ - public function get_categories( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->get_id(), 'product_cat', $before, $sep, $after ); - } - - /** - * Returns the product tags. - * - * @param string $sep (default: ', '). - * @param string $before (default: ''). - * @param string $after (default: ''). - * @return array - */ - public function get_tags( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->get_id(), 'product_tag', $before, $sep, $after ); - } - /** * Returns product attributes. * * @return array */ public function get_attributes() { - $attributes = array_filter( (array) maybe_unserialize( $this->data['product_attributes'] ) ); + $attributes = $this->data['product_attributes']; $taxonomies = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_name' ); // Check for any attributes which have been removed globally @@ -542,16 +507,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->data['slug'] = $slug; } - /** - * Set product permalink. - * - * @since 2.7.0 - * @param string $permalink Product permalink. - */ - public function set_permalink( $permalink ) { - $this->data['permalink'] = $permalink; - } - /** * Set product created date. * @@ -586,10 +541,10 @@ class WC_Product extends WC_Abstract_Legacy_Product { * Set if the product is featured. * * @since 2.7.0 - * @param string $featured Options: 'yes' or 'no'. + * @param bool|string */ public function set_featured( $featured ) { - $this->data['featured'] = $featured; + $this->data['featured'] = wc_string_to_bool( $featured ); } /** @@ -604,7 +559,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { if ( ! in_array( $visibility, $options, true ) ) { $this->error( 'product_invalid_catalog_visibility', __( 'Invalid catalog visibility option.', 'woocommerce' ) ); } - $this->data['catalog_visibility'] = $visibility; } @@ -639,7 +593,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { if ( ! empty( $sku ) && ! wc_product_has_unique_sku( $this->get_id(), $sku ) ) { $this->error( 'product_invalid_sku', __( 'Invalid or duplicated SKU.', 'woocommerce' ) ); } - $this->data['sku'] = $sku; } @@ -650,7 +603,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $price Regular price. */ public function set_regular_price( $price ) { - $this->data['regular_price'] = $price; + $this->data['regular_price'] = wc_format_decimal( $price ); } /** @@ -660,27 +613,27 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $price sale price. */ public function set_sale_price( $price ) { - $this->data['sale_price'] = $price; + $this->data['sale_price'] = wc_format_decimal( $price ); } /** * Set date on sale from. * * @since 2.7.0 - * @param string $data Sale from date. + * @param string $timestamp Sale from date. */ - public function set_date_on_sale_from( $date ) { - $this->data['date_on_sale_from'] = $date; + public function set_date_on_sale_from( $timestamp ) { + $this->data['date_on_sale_from'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ); } /** * Set date on sale to. * * @since 2.7.0 - * @param string $data Sale to date. + * @param string $timestamp Sale to date. */ public function set_date_on_sale_to( $date ) { - return $this->data['date_on_sale_to'] = $date; + return $this->data['date_on_sale_to'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ); } /** @@ -733,10 +686,10 @@ class WC_Product extends WC_Abstract_Legacy_Product { * Set if product manage stock. * * @since 2.7.0 - * @param string $manage_stock Options: 'yes' or 'no'. + * @param bool */ public function set_manage_stock( $manage_stock ) { - $this->data['manage_stock'] = $manage_stock; + $this->data['manage_stock'] = wc_string_to_bool( $manage_stock ); } /** @@ -785,10 +738,10 @@ class WC_Product extends WC_Abstract_Legacy_Product { * Set if should be sold individually. * * @since 2.7.0 - * @param string $sold_individually Options: 'yes' or 'no'. + * @param bool */ public function set_sold_individually( $sold_individually ) { - $this->data['sold_individually'] = $sold_individually; + $this->data['sold_individually'] = wc_string_to_bool( $sold_individually ); } /** @@ -832,23 +785,23 @@ class WC_Product extends WC_Abstract_Legacy_Product { } /** - * Set Upseels IDs. + * Set upsell IDs. * * @since 2.7.0 * @param string $upsell_ids IDs from the up-sell products. */ public function set_upsell_ids( $upsell_ids ) { - $this->data['upsell_ids'] = $upsell_ids; + $this->data['upsell_ids'] = array_filter( (array) $upsell_ids ); } /** - * Set Upseels IDs. + * Set crosssell IDs. * * @since 2.7.0 * @param string $cross_sell_ids IDs from the cross-sell products. */ public function set_cross_sell_ids( $cross_sell_ids ) { - $this->data['cross_sell_ids'] = $cross_sell_ids; + $this->data['cross_sell_ids'] = array_filter( (array) $cross_sell_ids ); } /** @@ -868,7 +821,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param bool $reviews_allowed Reviews allowed or not. */ public function set_reviews_allowed( $reviews_allowed ) { - $this->data['reviews_allowed'] = (bool) $reviews_allowed; + $this->data['reviews_allowed'] = wc_string_to_bool( $reviews_allowed ); } /** @@ -881,26 +834,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->data['purchase_note'] = $purchase_note; } - /** - * Set the product categories. - * - * @since 2.7.0 - * @param array $terms_id List of terms IDs. - */ - public function set_categories( $terms_id ) { - $this->save_taxonomy_terms( $terms_id, 'cat' ); - } - - /** - * Set the product tags. - * - * @since 2.7.0 - * @param array $terms_id List of terms IDs. - */ - public function set_tags( $terms_id ) { - $this->save_taxonomy_terms( $terms_id, 'tag' ); - } - /** * Returns product attributes. * @@ -908,7 +841,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param array $attributes List of product attributes. */ public function set_attributes( $attributes ) { - $this->data['product_attributes'] = $attributes; + $this->data['product_attributes'] = $attributes; // @todo ensure unserialised, array, and filtered out empty values } /** @@ -931,6 +864,69 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->data['menu_order'] = intval( $menu_order ); } + + + + + + + + + + + + + + + + + + /** + * Returns the product categories. @todo store in class and save? + * + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return string + */ + public function get_categories( $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $this->get_id(), 'product_cat', $before, $sep, $after ); + } + + /** + * Returns the product tags. @todo store in class and save? + * + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return array + */ + public function get_tags( $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $this->get_id(), 'product_tag', $before, $sep, $after ); + } + + /** + * Set the product categories. @todo store in class and save? + * + * @since 2.7.0 + * @param array $terms_id List of terms IDs. + */ + public function set_categories( $terms_id ) { + $this->save_taxonomy_terms( $terms_id, 'cat' ); + } + + /** + * Set the product tags. @todo store in class and save? + * + * @since 2.7.0 + * @param array $terms_id List of terms IDs. + */ + public function set_tags( $terms_id ) { + $this->save_taxonomy_terms( $terms_id, 'tag' ); + } + + + /* |-------------------------------------------------------------------------- | CRUD methods From 627beecd4270b55a00b34f704d623dcb075888ec Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 17 Oct 2016 15:56:16 +0100 Subject: [PATCH 010/163] Missing var --- includes/abstracts/abstract-wc-product.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 104e40ce1d1..d4f51b08e95 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -632,7 +632,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @since 2.7.0 * @param string $timestamp Sale to date. */ - public function set_date_on_sale_to( $date ) { + public function set_date_on_sale_to( $timestamp ) { return $this->data['date_on_sale_to'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ); } From 803709cb94a72270c995793333e9d16bb1b4850f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 17 Oct 2016 15:57:09 +0100 Subject: [PATCH 011/163] Add related product functions and deprecate those in class. --- .../abstracts/abstract-wc-legacy-product.php | 30 ++++ includes/abstracts/abstract-wc-product.php | 129 ------------------ includes/wc-product-functions.php | 120 ++++++++++++++++ templates/single-product/related.php | 2 +- 4 files changed, 151 insertions(+), 130 deletions(-) diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index 3399a05320e..a14b38f4e6d 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -21,6 +21,36 @@ if ( ! defined( 'ABSPATH' ) ) { */ abstract class WC_Abstract_Legacy_Product extends WC_Data { + /** + * Get and return related products. + * @deprecated 2.7.0 Use wc_get_related_products instead. + */ + public function get_related( $limit = 5 ) { + _deprecated_function( 'WC_Product::get_related', '2.7', 'wc_get_related_products' ); + return wc_get_related_products( $this->get_id(), $limit ); + } + + /** + * Retrieves related product terms. + * @deprecated 2.7.0 Use wc_get_related_terms instead. + */ + protected function get_related_terms( $term ) { + _deprecated_function( 'WC_Product::get_related_terms', '2.7', 'wc_get_related_terms' ); + return array_merge( array( 0 ), wc_get_related_terms( $this->get_id(), $term ) ); + } + + /** + * Builds the related posts query. + * @deprecated 2.7.0 Use wc_get_related_products_query instead. + */ + protected function build_related_query( $cats_array, $tags_array, $exclude_ids, $limit ) { + _deprecated_function( 'WC_Product::build_related_query', '2.7', 'wc_get_related_products_query' ); + return wc_get_related_products_query( $cats_array, $tags_array, $exclude_ids, $limit ); + } + + + + /** * The product's type (simple, variable etc). * diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 104e40ce1d1..171ccad9f7a 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -2074,57 +2074,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { return absint( $this->shipping_class_id ); } - /** - * Get and return related products. - * - * Notes: - * - Results are cached in a transient for faster queries. - * - To make results appear random, we query and extra 10 products and shuffle them. - * - To ensure we always have enough results, it will check $limit before returning the cached result, if not recalc. - * - This used to rely on transient version to invalidate cache, but to avoid multiple transients we now just expire daily. - * This means if a related product is edited and no longer related, it won't be removed for 24 hours. Acceptable trade-off for performance. - * - Saving a product will flush caches for that product. - * - * @param int $limit (default: 5) Should be an integer greater than 0. - * @return array Array of post IDs - */ - public function get_related( $limit = 5 ) { - global $wpdb; - - $transient_name = 'wc_related_' . $this->get_id(); - $related_posts = get_transient( $transient_name ); - $limit = $limit > 0 ? $limit : 5; - - // We want to query related posts if they are not cached, or we don't have enough - if ( false === $related_posts || sizeof( $related_posts ) < $limit ) { - // Related products are found from category and tag - $tags_array = $this->get_related_terms( 'product_tag' ); - $cats_array = $this->get_related_terms( 'product_cat' ); - - // Don't bother if none are set - if ( 1 === sizeof( $cats_array ) && 1 === sizeof( $tags_array ) ) { - $related_posts = array(); - } else { - // Sanitize - $exclude_ids = array_map( 'absint', array_merge( array( 0, $this->get_id() ), $this->get_upsells() ) ); - - // Generate query - but query an extra 10 results to give the appearance of random results - $query = $this->build_related_query( $cats_array, $tags_array, $exclude_ids, $limit + 10 ); - - // Get the posts - $related_posts = $wpdb->get_col( implode( ' ', $query ) ); - } - - set_transient( $transient_name, $related_posts, DAY_IN_SECONDS ); - } - - // Randomise the results - shuffle( $related_posts ); - - // Limit the returned results - return array_slice( $related_posts, 0, $limit ); - } - /** * Returns a single product attribute. * @@ -2301,84 +2250,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { return sprintf( '%s – %s', $identifier, $this->get_title() ); } - /** - * Retrieves related product terms. - * - * @param string $term - * @return array - */ - protected function get_related_terms( $term ) { - $terms_array = array( 0 ); - - $terms = apply_filters( 'woocommerce_get_related_' . $term . '_terms', wp_get_post_terms( $this->get_id(), $term ), $this->get_id() ); - foreach ( $terms as $term ) { - $terms_array[] = $term->term_id; - } - - return array_map( 'absint', $terms_array ); - } - - /** - * Builds the related posts query. - * - * @param array $cats_array - * @param array $tags_array - * @param array $exclude_ids - * @param int $limit - * @return string - */ - protected function build_related_query( $cats_array, $tags_array, $exclude_ids, $limit ) { - global $wpdb; - - $limit = absint( $limit ); - - $query = array(); - $query['fields'] = "SELECT DISTINCT ID FROM {$wpdb->posts} p"; - $query['join'] = " INNER JOIN {$wpdb->postmeta} pm ON ( pm.post_id = p.ID AND pm.meta_key='_visibility' )"; - $query['join'] .= " INNER JOIN {$wpdb->term_relationships} tr ON (p.ID = tr.object_id)"; - $query['join'] .= " INNER JOIN {$wpdb->term_taxonomy} tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id)"; - $query['join'] .= " INNER JOIN {$wpdb->terms} t ON (t.term_id = tt.term_id)"; - - if ( get_option( 'woocommerce_hide_out_of_stock_items' ) === 'yes' ) { - $query['join'] .= " INNER JOIN {$wpdb->postmeta} pm2 ON ( pm2.post_id = p.ID AND pm2.meta_key='_stock_status' )"; - } - - $query['where'] = " WHERE 1=1"; - $query['where'] .= " AND p.post_status = 'publish'"; - $query['where'] .= " AND p.post_type = 'product'"; - $query['where'] .= " AND p.ID NOT IN ( " . implode( ',', $exclude_ids ) . " )"; - $query['where'] .= " AND pm.meta_value IN ( 'visible', 'catalog' )"; - - if ( get_option( 'woocommerce_hide_out_of_stock_items' ) === 'yes' ) { - $query['where'] .= " AND pm2.meta_value = 'instock'"; - } - - $relate_by_category = apply_filters( 'woocommerce_product_related_posts_relate_by_category', true, $this->get_id() ); - $relate_by_tag = apply_filters( 'woocommerce_product_related_posts_relate_by_tag', true, $this->get_id() ); - - if ( $relate_by_category || $relate_by_tag ) { - $query['where'] .= ' AND ('; - - if ( $relate_by_category ) { - $query['where'] .= " ( tt.taxonomy = 'product_cat' AND t.term_id IN ( " . implode( ',', $cats_array ) . " ) ) "; - if ( $relate_by_tag ) { - $query['where'] .= ' OR '; - } - } - - if ( $relate_by_tag ) { - $query['where'] .= " ( tt.taxonomy = 'product_tag' AND t.term_id IN ( " . implode( ',', $tags_array ) . " ) ) "; - } - - $query['where'] .= ')'; - } - - $query['limits'] = " LIMIT {$limit} "; - $query = apply_filters( 'woocommerce_product_related_posts_query', $query, $this->get_id() ); - - return $query; - } - /** * Save taxonomy terms. * diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 0c89e29bb48..132ca39198d 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -751,3 +751,123 @@ function wc_get_product_visibility_options() { 'hidden' => __( 'Hidden', 'woocommerce' ), ) ); } + +/** + * Get relateed products. + * @param integer $product_id + * @param integer $limit + * @param array $exclude_ids + * @return array + */ +function wc_get_related_products( $product_id, $limit = 5, $exclude_ids = array() ) { + global $wpdb; + + $product_id = absint( $product_id ); + $exclude_ids = array_map( 'absint', array_merge( array( 0, $product_id ), $exclude_ids ) ); + $transient_name = 'wc_related_' . $product_id; + $related_posts = get_transient( $transient_name ); + $limit = $limit > 0 ? $limit : 5; + + // We want to query related posts if they are not cached, or we don't have enough + if ( false === $related_posts || sizeof( $related_posts ) < $limit ) { + // Related products are found from category and tags + if ( apply_filters( 'woocommerce_product_related_posts_relate_by_category', true, $product_id ) ) { + $cats_array = wc_get_related_terms( $product_id, 'product_cat' ); + } else { + $cats_array = array(); + } + + if ( apply_filters( 'woocommerce_product_related_posts_relate_by_tag', true, $product_id ) ) { + $tags_array = wc_get_related_terms( $product_id, 'product_tag' ); + } else { + $tags_array = array(); + } + + // Don't bother if none are set, unless woocommerce_product_related_posts_force_display is set to true in which case all products are related. + if ( empty( $cats_array ) && empty( $tags_array ) && ! apply_filters( 'woocommerce_product_related_posts_force_display', false, $product_id ) ) { + $related_posts = array(); + } else { + // Generate query - but query an extra 10 results to give the appearance of random results + $query = apply_filters( 'woocommerce_product_related_posts_query', wc_get_related_products_query( $cats_array, $tags_array, $exclude_ids, $limit + 10 ), $product_id ); + + // Get the posts + $related_posts = $wpdb->get_col( implode( ' ', $query ) ); + } + + set_transient( $transient_name, $related_posts, DAY_IN_SECONDS ); + } + + // Randomise the results + shuffle( $related_posts ); + + // Limit the returned results + return array_slice( $related_posts, 0, $limit ); +} + +/** + * Retrieves related product terms. + * @since 2.7.0 + * @param integer $product_id + * @param string $taxonomy + * @return array + */ +function wc_get_related_terms( $product_id, $taxonomy ) { + $terms = apply_filters( 'woocommerce_get_related_' . $taxonomy . '_terms', wp_get_post_terms( $product_id, $term ), $product_id ); + return array_map( 'absint', wp_list_pluck( $terms, 'term_id' ) ); +} + +/** + * Builds the related posts query. + * + * @param array $cats_array + * @param array $tags_array + * @param array $exclude_ids + * @param int $limit + * @return string + */ +function wc_get_related_products_query( $cats_array, $tags_array, $exclude_ids, $limit ) { + global $wpdb; + + $limit = absint( $limit ); + $query = array(); + $query['fields'] = "SELECT DISTINCT ID FROM {$wpdb->posts} p"; + $query['join'] = " INNER JOIN {$wpdb->postmeta} pm ON ( pm.post_id = p.ID AND pm.meta_key='_visibility' )"; + $query['join'] .= " INNER JOIN {$wpdb->term_relationships} tr ON (p.ID = tr.object_id)"; + $query['join'] .= " INNER JOIN {$wpdb->term_taxonomy} tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id)"; + $query['join'] .= " INNER JOIN {$wpdb->terms} t ON (t.term_id = tt.term_id)"; + + if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) { + $query['join'] .= " INNER JOIN {$wpdb->postmeta} pm2 ON ( pm2.post_id = p.ID AND pm2.meta_key='_stock_status' )"; + } + + $query['where'] = " WHERE 1=1"; + $query['where'] .= " AND p.post_status = 'publish'"; + $query['where'] .= " AND p.post_type = 'product'"; + $query['where'] .= " AND p.ID NOT IN ( " . implode( ',', $exclude_ids ) . " )"; + $query['where'] .= " AND pm.meta_value IN ( 'visible', 'catalog' )"; + + if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) { + $query['where'] .= " AND pm2.meta_value = 'instock'"; + } + + if ( $cats_array || $tags_array ) { + $query['where'] .= ' AND ('; + + if ( $cats_array ) { + $query['where'] .= " ( tt.taxonomy = 'product_cat' AND t.term_id IN ( " . implode( ',', $cats_array ) . " ) ) "; + if ( $relate_by_tag ) { + $query['where'] .= ' OR '; + } + } + + if ( $tags_array ) { + $query['where'] .= " ( tt.taxonomy = 'product_tag' AND t.term_id IN ( " . implode( ',', $tags_array ) . " ) ) "; + } + + $query['where'] .= ')'; + } + + $query['limits'] = " LIMIT {$limit} "; + + return $query; +} diff --git a/templates/single-product/related.php b/templates/single-product/related.php index 330f6356030..5d5b159fbd9 100644 --- a/templates/single-product/related.php +++ b/templates/single-product/related.php @@ -26,7 +26,7 @@ if ( empty( $product ) || ! $product->exists() ) { return; } -if ( ! $related = $product->get_related( $posts_per_page ) ) { +if ( ! $related = wc_get_related_products( $product->get_id(), $posts_per_page, $product->get_upsells() ) ) { return; } From 38c18664c9beaa90cb310017c41e3feb2619080b Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 17 Oct 2016 16:00:43 +0100 Subject: [PATCH 012/163] No need to exclude ID --- templates/single-product/related.php | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/single-product/related.php b/templates/single-product/related.php index 5d5b159fbd9..b63eed0778d 100644 --- a/templates/single-product/related.php +++ b/templates/single-product/related.php @@ -37,7 +37,6 @@ $args = apply_filters( 'woocommerce_related_products_args', array( 'posts_per_page' => $posts_per_page, 'orderby' => $orderby, 'post__in' => $related, - 'post__not_in' => array( $product->id ), ) ); $products = new WP_Query( $args ); From 92484d140c40261b36e89c18ad466eed2315f62a Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 17 Oct 2016 13:23:49 -0200 Subject: [PATCH 013/163] Fixed coding standards and improved the docblocks --- includes/wc-product-functions.php | 50 ++++++++++++++++++------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 132ca39198d..4c66f464603 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -754,9 +754,11 @@ function wc_get_product_visibility_options() { /** * Get relateed products. - * @param integer $product_id - * @param integer $limit - * @param array $exclude_ids + * + * @since 2.7.0 + * @param int $product_id Product ID + * @param int $limit Limit of results. + * @param array $exclude_ids Exclude IDs from the results. * @return array */ function wc_get_related_products( $product_id, $limit = 5, $exclude_ids = array() ) { @@ -768,9 +770,9 @@ function wc_get_related_products( $product_id, $limit = 5, $exclude_ids = array( $related_posts = get_transient( $transient_name ); $limit = $limit > 0 ? $limit : 5; - // We want to query related posts if they are not cached, or we don't have enough + // We want to query related posts if they are not cached, or we don't have enough. if ( false === $related_posts || sizeof( $related_posts ) < $limit ) { - // Related products are found from category and tags + // Related products are found from category and tags. if ( apply_filters( 'woocommerce_product_related_posts_relate_by_category', true, $product_id ) ) { $cats_array = wc_get_related_terms( $product_id, 'product_cat' ); } else { @@ -787,47 +789,55 @@ function wc_get_related_products( $product_id, $limit = 5, $exclude_ids = array( if ( empty( $cats_array ) && empty( $tags_array ) && ! apply_filters( 'woocommerce_product_related_posts_force_display', false, $product_id ) ) { $related_posts = array(); } else { - // Generate query - but query an extra 10 results to give the appearance of random results + // Generate query - but query an extra 10 results to give the appearance of random results. $query = apply_filters( 'woocommerce_product_related_posts_query', wc_get_related_products_query( $cats_array, $tags_array, $exclude_ids, $limit + 10 ), $product_id ); - // Get the posts + // Get the posts. $related_posts = $wpdb->get_col( implode( ' ', $query ) ); } set_transient( $transient_name, $related_posts, DAY_IN_SECONDS ); } - // Randomise the results + // Randomise the results. shuffle( $related_posts ); - // Limit the returned results + // Limit the returned results. return array_slice( $related_posts, 0, $limit ); } /** * Retrieves related product terms. - * @since 2.7.0 - * @param integer $product_id - * @param string $taxonomy + * + * @since 2.7.0 + * @param int $product_id Product ID. + * @param string $taxonomy Taxonomy slug. * @return array */ function wc_get_related_terms( $product_id, $taxonomy ) { $terms = apply_filters( 'woocommerce_get_related_' . $taxonomy . '_terms', wp_get_post_terms( $product_id, $term ), $product_id ); + return array_map( 'absint', wp_list_pluck( $terms, 'term_id' ) ); } /** * Builds the related posts query. * - * @param array $cats_array - * @param array $tags_array - * @param array $exclude_ids - * @param int $limit + * @since 2.7.0 + * @param array $cats_array List of categories IDs. + * @param array $tags_array List of tags IDs. + * @param array $exclude_ids Excluded IDs. + * @param int $limit Limit of results. * @return string */ function wc_get_related_products_query( $cats_array, $tags_array, $exclude_ids, $limit ) { global $wpdb; + // Arrays to string. + $exclude_ids = implode( ',', $exclude_ids ); + $cats_array = implode( ',', $cats_array ); + $tags_array = implode( ',', $tags_array ); + $limit = absint( $limit ); $query = array(); $query['fields'] = "SELECT DISTINCT ID FROM {$wpdb->posts} p"; @@ -840,10 +850,10 @@ function wc_get_related_products_query( $cats_array, $tags_array, $exclude_ids, $query['join'] .= " INNER JOIN {$wpdb->postmeta} pm2 ON ( pm2.post_id = p.ID AND pm2.meta_key='_stock_status' )"; } - $query['where'] = " WHERE 1=1"; + $query['where'] = ' WHERE 1=1'; $query['where'] .= " AND p.post_status = 'publish'"; $query['where'] .= " AND p.post_type = 'product'"; - $query['where'] .= " AND p.ID NOT IN ( " . implode( ',', $exclude_ids ) . " )"; + $query['where'] .= " AND p.ID NOT IN ( {$exclude_ids} )"; $query['where'] .= " AND pm.meta_value IN ( 'visible', 'catalog' )"; if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) { @@ -854,14 +864,14 @@ function wc_get_related_products_query( $cats_array, $tags_array, $exclude_ids, $query['where'] .= ' AND ('; if ( $cats_array ) { - $query['where'] .= " ( tt.taxonomy = 'product_cat' AND t.term_id IN ( " . implode( ',', $cats_array ) . " ) ) "; + $query['where'] .= " ( tt.taxonomy = 'product_cat' AND t.term_id IN ( {$cats_array} ) ) "; if ( $relate_by_tag ) { $query['where'] .= ' OR '; } } if ( $tags_array ) { - $query['where'] .= " ( tt.taxonomy = 'product_tag' AND t.term_id IN ( " . implode( ',', $tags_array ) . " ) ) "; + $query['where'] .= " ( tt.taxonomy = 'product_tag' AND t.term_id IN ( {$tags_array} ) ) "; } $query['where'] .= ')'; From c3b61359375956a7ebb7d9dafad59dc994a28e5e Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 17 Oct 2016 13:28:52 -0200 Subject: [PATCH 014/163] Get cached terms from wc_get_related_terms() --- includes/wc-product-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 4c66f464603..9b787fceeb7 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -815,7 +815,7 @@ function wc_get_related_products( $product_id, $limit = 5, $exclude_ids = array( * @return array */ function wc_get_related_terms( $product_id, $taxonomy ) { - $terms = apply_filters( 'woocommerce_get_related_' . $taxonomy . '_terms', wp_get_post_terms( $product_id, $term ), $product_id ); + $terms = apply_filters( 'woocommerce_get_related_' . $taxonomy . '_terms', get_the_terms( $product_id, $term ), $product_id ); return array_map( 'absint', wp_list_pluck( $terms, 'term_id' ) ); } From aad06f6642bafcd1d9f4d4c33162de27c9278d45 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 17 Oct 2016 13:29:12 -0200 Subject: [PATCH 015/163] Fixed wrong variable in wc_get_related_terms --- includes/wc-product-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 9b787fceeb7..d9a868a47ed 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -815,7 +815,7 @@ function wc_get_related_products( $product_id, $limit = 5, $exclude_ids = array( * @return array */ function wc_get_related_terms( $product_id, $taxonomy ) { - $terms = apply_filters( 'woocommerce_get_related_' . $taxonomy . '_terms', get_the_terms( $product_id, $term ), $product_id ); + $terms = apply_filters( 'woocommerce_get_related_' . $taxonomy . '_terms', get_the_terms( $product_id, $taxonomy ), $product_id ); return array_map( 'absint', wp_list_pluck( $terms, 'term_id' ) ); } From f42d85345308d21c81046ed8209fd1634fe13c92 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 17 Oct 2016 13:31:49 -0200 Subject: [PATCH 016/163] Use count() instead of sizeof() --- includes/wc-product-functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index d9a868a47ed..70d16c43294 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -756,7 +756,7 @@ function wc_get_product_visibility_options() { * Get relateed products. * * @since 2.7.0 - * @param int $product_id Product ID + * @param int $product_id Product ID. * @param int $limit Limit of results. * @param array $exclude_ids Exclude IDs from the results. * @return array @@ -771,7 +771,7 @@ function wc_get_related_products( $product_id, $limit = 5, $exclude_ids = array( $limit = $limit > 0 ? $limit : 5; // We want to query related posts if they are not cached, or we don't have enough. - if ( false === $related_posts || sizeof( $related_posts ) < $limit ) { + if ( false === $related_posts || count( $related_posts ) < $limit ) { // Related products are found from category and tags. if ( apply_filters( 'woocommerce_product_related_posts_relate_by_category', true, $product_id ) ) { $cats_array = wc_get_related_terms( $product_id, 'product_cat' ); From 6bf077a367dc7d3d326e6e3acee9966e8de085e0 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 17 Oct 2016 17:04:24 +0100 Subject: [PATCH 017/163] Sanitize ids later --- includes/wc-product-functions.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 70d16c43294..bd115204ff2 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -765,7 +765,7 @@ function wc_get_related_products( $product_id, $limit = 5, $exclude_ids = array( global $wpdb; $product_id = absint( $product_id ); - $exclude_ids = array_map( 'absint', array_merge( array( 0, $product_id ), $exclude_ids ) ); + $exclude_ids = array_merge( array( 0, $product_id ), $exclude_ids ); $transient_name = 'wc_related_' . $product_id; $related_posts = get_transient( $transient_name ); $limit = $limit > 0 ? $limit : 5; @@ -834,9 +834,9 @@ function wc_get_related_products_query( $cats_array, $tags_array, $exclude_ids, global $wpdb; // Arrays to string. - $exclude_ids = implode( ',', $exclude_ids ); - $cats_array = implode( ',', $cats_array ); - $tags_array = implode( ',', $tags_array ); + $exclude_ids = implode( ',', array_map( 'absint', $exclude_ids ) ); + $cats_array = implode( ',', array_map( 'absint', $cats_array ) ); + $tags_array = implode( ',', array_map( 'absint', $tags_array ) ); $limit = absint( $limit ); $query = array(); From e6e600ba8a4b6787cd1fd96e80b2d7159db7fe0e Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 17 Oct 2016 17:07:19 +0100 Subject: [PATCH 018/163] Remove unneeded comments --- includes/wc-product-functions.php | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index bd115204ff2..3e6e477801d 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -753,7 +753,7 @@ function wc_get_product_visibility_options() { } /** - * Get relateed products. + * Get related products based on product category and tags. * * @since 2.7.0 * @param int $product_id Product ID. @@ -772,37 +772,22 @@ function wc_get_related_products( $product_id, $limit = 5, $exclude_ids = array( // We want to query related posts if they are not cached, or we don't have enough. if ( false === $related_posts || count( $related_posts ) < $limit ) { - // Related products are found from category and tags. - if ( apply_filters( 'woocommerce_product_related_posts_relate_by_category', true, $product_id ) ) { - $cats_array = wc_get_related_terms( $product_id, 'product_cat' ); - } else { - $cats_array = array(); - } - - if ( apply_filters( 'woocommerce_product_related_posts_relate_by_tag', true, $product_id ) ) { - $tags_array = wc_get_related_terms( $product_id, 'product_tag' ); - } else { - $tags_array = array(); - } + $cats_array = apply_filters( 'woocommerce_product_related_posts_relate_by_category', true, $product_id ) ? wc_get_related_terms( $product_id, 'product_cat' ) : array(); + $tags_array = apply_filters( 'woocommerce_product_related_posts_relate_by_tag', true, $product_id ) ? wc_get_related_terms( $product_id, 'product_tag' ) : array(); // Don't bother if none are set, unless woocommerce_product_related_posts_force_display is set to true in which case all products are related. if ( empty( $cats_array ) && empty( $tags_array ) && ! apply_filters( 'woocommerce_product_related_posts_force_display', false, $product_id ) ) { $related_posts = array(); } else { - // Generate query - but query an extra 10 results to give the appearance of random results. - $query = apply_filters( 'woocommerce_product_related_posts_query', wc_get_related_products_query( $cats_array, $tags_array, $exclude_ids, $limit + 10 ), $product_id ); - - // Get the posts. - $related_posts = $wpdb->get_col( implode( ' ', $query ) ); + // Generate query - but query an extra 10 results to give the appearance of random results when later shuffled. + $related_posts = $wpdb->get_col( implode( ' ', apply_filters( 'woocommerce_product_related_posts_query', wc_get_related_products_query( $cats_array, $tags_array, $exclude_ids, $limit + 10 ), $product_id ) ) ); } set_transient( $transient_name, $related_posts, DAY_IN_SECONDS ); } - // Randomise the results. shuffle( $related_posts ); - // Limit the returned results. return array_slice( $related_posts, 0, $limit ); } From 50f21276fcdfd7ab573e70fd38e52ad347aa35bb Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 17 Oct 2016 17:18:57 +0100 Subject: [PATCH 019/163] wc_get_product_term_ids instead of related wording and use in other places. get_the_terms is used here and also handles caching, something wp_get_post_terms does not. --- includes/abstracts/abstract-wc-legacy-product.php | 6 +++--- includes/wc-product-functions.php | 14 ++++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index a14b38f4e6d..b04e2ab2a26 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -32,11 +32,11 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { /** * Retrieves related product terms. - * @deprecated 2.7.0 Use wc_get_related_terms instead. + * @deprecated 2.7.0 Use wc_get_product_term_ids instead. */ protected function get_related_terms( $term ) { - _deprecated_function( 'WC_Product::get_related_terms', '2.7', 'wc_get_related_terms' ); - return array_merge( array( 0 ), wc_get_related_terms( $this->get_id(), $term ) ); + _deprecated_function( 'WC_Product::get_related_terms', '2.7', 'wc_get_product_term_ids' ); + return array_merge( array( 0 ), wc_get_product_term_ids( $this->get_id(), $term ) ); } /** diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 3e6e477801d..8d4f7cde9dc 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -700,7 +700,7 @@ function wc_get_product_variation_attributes( $variation_id ) { * @return array */ function wc_get_product_cat_ids( $product_id ) { - $product_cats = wp_get_post_terms( $product_id, 'product_cat', array( "fields" => "ids" ) ); + $product_cats = wc_get_product_term_ids( $product_id, 'product_cat' ); foreach ( $product_cats as $product_cat ) { $product_cats = array_merge( $product_cats, get_ancestors( $product_cat, 'product_cat' ) ); @@ -772,8 +772,8 @@ function wc_get_related_products( $product_id, $limit = 5, $exclude_ids = array( // We want to query related posts if they are not cached, or we don't have enough. if ( false === $related_posts || count( $related_posts ) < $limit ) { - $cats_array = apply_filters( 'woocommerce_product_related_posts_relate_by_category', true, $product_id ) ? wc_get_related_terms( $product_id, 'product_cat' ) : array(); - $tags_array = apply_filters( 'woocommerce_product_related_posts_relate_by_tag', true, $product_id ) ? wc_get_related_terms( $product_id, 'product_tag' ) : array(); + $cats_array = apply_filters( 'woocommerce_product_related_posts_relate_by_category', true, $product_id ) ? apply_filters( 'woocommerce_get_related_product_cat_terms', wc_get_product_term_ids( $product_id, 'product_cat' ), $product_id ) : array(); + $tags_array = apply_filters( 'woocommerce_product_related_posts_relate_by_tag', true, $product_id ) ? apply_filters( 'woocommerce_get_related_product_tag_terms', wc_get_product_term_ids( $product_id, 'product_tag' ), $product_id ) : array(); // Don't bother if none are set, unless woocommerce_product_related_posts_force_display is set to true in which case all products are related. if ( empty( $cats_array ) && empty( $tags_array ) && ! apply_filters( 'woocommerce_product_related_posts_force_display', false, $product_id ) ) { @@ -792,17 +792,15 @@ function wc_get_related_products( $product_id, $limit = 5, $exclude_ids = array( } /** - * Retrieves related product terms. + * Retrieves product term ids for a taxonomy. * * @since 2.7.0 * @param int $product_id Product ID. * @param string $taxonomy Taxonomy slug. * @return array */ -function wc_get_related_terms( $product_id, $taxonomy ) { - $terms = apply_filters( 'woocommerce_get_related_' . $taxonomy . '_terms', get_the_terms( $product_id, $taxonomy ), $product_id ); - - return array_map( 'absint', wp_list_pluck( $terms, 'term_id' ) ); +function wc_get_product_term_ids( $product_id, $taxonomy ) { + return wp_list_pluck( get_the_terms( $product_id, $taxonomy ), 'term_id' ); } /** From fde97df50f3cb9f0dbd92179f20f6506742f5ffd Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Mon, 17 Oct 2016 13:22:38 -0700 Subject: [PATCH 020/163] Clean up the abstract product class a bit, deprecate two functions we have renamed, make update & create work properly, and add some tests for it. --- .../abstracts/abstract-wc-legacy-product.php | 22 +++ includes/abstracts/abstract-wc-product.php | 184 +++++++++--------- tests/unit-tests/product/crud.php | 117 +++++++++++ 3 files changed, 233 insertions(+), 90 deletions(-) create mode 100644 tests/unit-tests/product/crud.php diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index 3399a05320e..ddd687d011f 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -129,6 +129,7 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { * @return string */ public function get_title() { + _deprecated_function( 'WC_Product::get_title', '2.7', 'WC_Product::get_name' ); return apply_filters( 'woocommerce_product_title', $this->post ? $this->post->post_title : '', $this ); } @@ -139,6 +140,27 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { * @return int */ public function get_parent() { + _deprecated_function( 'WC_Product::get_parent', '2.7', 'WC_Product::get_parent_id' ); return apply_filters( 'woocommerce_product_parent', absint( $this->post->post_parent ), $this ); } + + /** + * Returns the upsell product ids. + * + * @return array + */ + public function get_upsells() { + _deprecated_function( 'WC_Product::get_upsells', '2.7', 'WC_Product::get_upsell_ids' ); + return apply_filters( 'woocommerce_product_upsell_ids', (array) maybe_unserialize( $this->upsell_ids ), $this ); + } + + /** + * Returns the cross sell product ids. + * + * @return array + */ + public function get_cross_sells() { + _deprecated_function( 'WC_Product::get_cross_sells', '2.7', 'WC_Product::get_cross_sell_ids' ); + return apply_filters( 'woocommerce_product_crosssell_ids', (array) maybe_unserialize( $this->crosssell_ids ), $this ); + } } diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index d4f51b08e95..377f12f1933 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -22,14 +22,13 @@ include_once( 'abstract-wc-legacy-product.php' ); class WC_Product extends WC_Abstract_Legacy_Product { /** - * Stores customer data. + * Stores product data. * * @var array */ protected $data = array( 'name' => '', 'slug' => '', - //'permalink' => '', 'date_created' => '', 'date_modified' => '', 'status' => '', @@ -87,6 +86,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param int|WC_Product|object $product Product to init. */ public function __construct( $product = 0 ) { + parent::__construct( $product ); if ( is_numeric( $product ) && $product > 0 ) { $this->read( $product ); } elseif ( $product instanceof self ) { @@ -96,6 +96,14 @@ class WC_Product extends WC_Abstract_Legacy_Product { } } + /* + |-------------------------------------------------------------------------- + | Getters + |-------------------------------------------------------------------------- + | + | Methods for getting data from the product object. + */ + /** * Get internal type. * @since 2.7.0 @@ -113,14 +121,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { return get_permalink( $this->get_id() ); } - /* - |-------------------------------------------------------------------------- - | Getters - |-------------------------------------------------------------------------- - | - | Methods for getting data from the product object. - */ - /** * Get product name. * @@ -442,14 +442,16 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return array */ public function get_attributes() { - $attributes = $this->data['product_attributes']; + $attributes = $this->data['attributes']; $taxonomies = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_name' ); // Check for any attributes which have been removed globally - foreach ( $attributes as $key => $attribute ) { - if ( $attribute['is_taxonomy'] ) { - if ( ! in_array( substr( $attribute['name'], 3 ), $taxonomies ) ) { - unset( $attributes[ $key ] ); + if ( is_array( $attributes ) && count( $attributes ) > 0 ) { + foreach ( $attributes as $key => $attribute ) { + if ( $attribute['is_taxonomy'] ) { + if ( ! in_array( substr( $attribute['name'], 3 ), $taxonomies ) ) { + unset( $attributes[ $key ] ); + } } } } @@ -477,6 +479,30 @@ class WC_Product extends WC_Abstract_Legacy_Product { return $this->data['menu_order']; } + /** + * Returns the product categories. @todo store in class and save? + * + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return string + */ + public function get_categories( $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $this->get_id(), 'product_cat', $before, $sep, $after ); + } + + /** + * Returns the product tags. @todo store in class and save? + * + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return array + */ + public function get_tags( $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $this->get_id(), 'product_tag', $before, $sep, $after ); + } + /* |-------------------------------------------------------------------------- | Setters @@ -835,13 +861,13 @@ class WC_Product extends WC_Abstract_Legacy_Product { } /** - * Returns product attributes. + * Set product attributes. * * @since 2.7.0 * @param array $attributes List of product attributes. */ public function set_attributes( $attributes ) { - $this->data['product_attributes'] = $attributes; // @todo ensure unserialised, array, and filtered out empty values + $this->data['attributes'] = $attributes; // @todo ensure unserialised, array, and filtered out empty values } /** @@ -864,47 +890,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->data['menu_order'] = intval( $menu_order ); } - - - - - - - - - - - - - - - - - - /** - * Returns the product categories. @todo store in class and save? - * - * @param string $sep (default: ', '). - * @param string $before (default: ''). - * @param string $after (default: ''). - * @return string - */ - public function get_categories( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->get_id(), 'product_cat', $before, $sep, $after ); - } - - /** - * Returns the product tags. @todo store in class and save? - * - * @param string $sep (default: ', '). - * @param string $before (default: ''). - * @param string $after (default: ''). - * @return array - */ - public function get_tags( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->get_id(), 'product_tag', $before, $sep, $after ); - } - /** * Set the product categories. @todo store in class and save? * @@ -925,8 +910,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->save_taxonomy_terms( $terms_id, 'tag' ); } - - /* |-------------------------------------------------------------------------- | CRUD methods @@ -961,7 +944,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'type' => '', 'status' => $post_object->post_status, 'featured' => get_post_meta( $id, '_featured', true ), - 'catalog_visibility' => 'hidden', + 'catalog_visibility' => get_post_meta( $id, '_visibility', true ), 'description' => $post_object->post_content, 'short_description' => $post_object->post_excerpt, 'sku' => get_post_meta( $id, '_sku', true ), @@ -972,7 +955,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'total_sales' => get_post_meta( $id, 'total_sales', true ), 'tax_status' => get_post_meta( $id, '_tax_status', true ), 'tax_class' => get_post_meta( $id, '_tax_class', true ), - 'manage_stock' => 'no', + 'manage_stock' => get_post_meta( $id, '_manage_stock', true ), 'stock_quantity' => get_post_meta( $id, '_stock', true ), 'stock_status' => get_post_meta( $id, '_stock_status', true ), 'backorders' => get_post_meta( $id, '_backorders', true ), @@ -1004,14 +987,17 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->set_date_created( current_time( 'timestamp' ) ); $id = wp_insert_post( apply_filters( 'woocommerce_new_product_data', array( - 'post_type' => 'product', - 'post_status' => 'publish', - 'post_author' => get_current_user_id(), - 'post_title' => $this->get_code(), - 'post_content' => '', - 'post_excerpt' => $this->get_description(), - 'post_date' => date( 'Y-m-d H:i:s', $this->get_date_created() ), - 'post_date_gmt' => get_gmt_from_date( date( 'Y-m-d H:i:s', $this->get_date_created() ) ), + 'post_type' => 'product', + 'post_status' => 'publish', + 'post_author' => get_current_user_id(), + 'post_title' => $this->get_name(), + 'post_content' => $this->get_description(), + 'post_excerpt' => $this->get_short_description(), + 'post_parent' => $this->get_parent_id(), + 'comment_status' => $this->get_reviews_allowed(), + 'menu_order' => $this->get_menu_order(), + 'post_date' => date( 'Y-m-d H:i:s', $this->get_date_created() ), + 'post_date_gmt' => get_gmt_from_date( date( 'Y-m-d H:i:s', $this->get_date_created() ) ), ) ), true ); if ( $id ) { @@ -1028,7 +1014,19 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @since 2.7.0 */ public function update() { - + $post_data = array( + 'ID' => $this->get_id(), + 'post_content' => $this->get_description(), + 'post_excerpt' => $this->get_short_description(), + 'post_title' => $this->get_name(), + 'post_parent' => $this->get_parent_id(), + 'comment_status' => $this->get_reviews_allowed(), + 'menu_order' => $this->get_menu_order(), + ); + wp_update_post( $post_data ); + $this->update_post_meta( $this->get_id() ); + $this->save_meta_data(); + do_action( 'woocommerce_update_product', $this->get_id() ); } /** @@ -1062,7 +1060,31 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param int $id Object ID. */ private function update_post_meta( $id ) { - // update_post_meta( $id, 'discount_type', $this->get_discount_type() ); + update_post_meta( $id, '_featured', $this->get_featured() ); + update_post_meta( $id, '_visibility', $this->get_catalog_visibility() ); + update_post_meta( $id, '_sku', $this->get_sku() ); + update_post_meta( $id, '_regular_price', $this->get_regular_price() ); + update_post_meta( $id, '_sale_price', $this->get_sale_price() ); + update_post_meta( $id, '_regular_price', $this->get_regular_price() ); + update_post_meta( $id, '_sale_price_dates_from', $this->get_date_on_sale_from() ); + update_post_meta( $id, '_sale_price_dates_to', $this->get_date_on_sale_to() ); + update_post_meta( $id, 'total_sales', $this->get_total_sales() ); + update_post_meta( $id, '_tax_status', $this->get_tax_status() ); + update_post_meta( $id, '_tax_class', $this->get_tax_class() ); + update_post_meta( $id, '_manage_stock', $this->get_manage_stock() ); + update_post_meta( $id, '_stock', $this->get_stock_quantity() ); + update_post_meta( $id, '_stock_status', $this->get_stock_status() ); + update_post_meta( $id, '_backorders', $this->get_backorders() ); + update_post_meta( $id, '_sold_individually', $this->get_sold_individually() ); + update_post_meta( $id, '_weight', $this->get_weight() ); + update_post_meta( $id, '_length', $this->get_length() ); + update_post_meta( $id, '_width', $this->get_width() ); + update_post_meta( $id, '_height', $this->get_height() ); + update_post_meta( $id, '_upsell_ids', $this->get_upsell_ids() ); + update_post_meta( $id, '_crosssell_ids', $this->get_cross_sell_ids() ); + update_post_meta( $id, '_purchase_note', $this->get_purchase_note() ); + update_post_meta( $id, '_attributes', $this->get_attributes() ); + update_post_meta( $id, '_default_attributes', $this->get_default_attributes() ); } /* @@ -2014,24 +2036,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_product_review_count', $count, $this ); } - /** - * Returns the upsell product ids. - * - * @return array - */ - public function get_upsells() { - return apply_filters( 'woocommerce_product_upsell_ids', (array) maybe_unserialize( $this->upsell_ids ), $this ); - } - - /** - * Returns the cross sell product ids. - * - * @return array - */ - public function get_cross_sells() { - return apply_filters( 'woocommerce_product_crosssell_ids', (array) maybe_unserialize( $this->crosssell_ids ), $this ); - } - /** * Returns the product shipping class. * diff --git a/tests/unit-tests/product/crud.php b/tests/unit-tests/product/crud.php new file mode 100644 index 00000000000..e8285f8316b --- /dev/null +++ b/tests/unit-tests/product/crud.php @@ -0,0 +1,117 @@ +set_regular_price( 42 ); + $product->set_name( 'My Product' ); + $product->create(); + + $read_product = new WC_Product( $product->get_id() ); + + $this->assertEquals( '42', $read_product->get_regular_price() ); + $this->assertEquals( 'My Product', $read_product->get_name() ); + } + + /** + * Test reading a product. + * + * @since 2.7.0 + */ + function test_product_read() { + $product = WC_Helper_Product::create_simple_product(); + $product = new WC_Product( $product->get_id() ); + + $this->assertEquals( '10', $product->get_regular_price() ); + } + + /** + * Test updating a product. + * + * @since 2.7.0 + */ + function test_product_update() { + $product = WC_Helper_Product::create_simple_product(); + + $this->assertEquals( '10', $product->get_regular_price() ); + + $product->set_regular_price( 15 ); + $product->save(); + + // Reread from database + $product = new WC_Product( $product->get_id() ); + + $this->assertEquals( '15', $product->get_regular_price() ); + } + + /** + * Test deleting a product. + * + * @since 2.7.0 + */ + function test_product_delete() { + $product = WC_Helper_Product::create_simple_product(); + $product->delete(); + $this->assertEquals( 0, $product->get_id() ); + } + + /** + * Test product setters and getters + * @todo needs tests for tags, categories, and attributes + * @since 2.7.0 + */ + public function test_product_getters_and_setters() { + $getters_and_setters = array( + 'name' => 'Test', + 'slug' => 'test', + 'status' => 'publish', + 'catalog_visibility' => 'search', + 'featured' => false, + 'description' => 'Hello world', + 'short_description' => 'hello', + 'sku' => 'TEST SKU', + 'regular_price' => 15.00, + 'sale_price' => 10.00, + 'date_on_sale_from' => '1475798400', + 'date_on_sale_to' => '1477267200', + 'total_sales' => 20, + 'tax_status' => 'none', + 'tax_class' => '', + 'manage_stock' => true, + 'stock_quantity' => 10, + 'stock_status' => 'instock', + 'backorders' => 'notify', + 'sold_individually' => false, + 'weight' => 100, + 'length' => 10, + 'width' => 10, + 'height' => 10, + 'upsell_ids' => array( 2, 3 ), + 'cross_sell_ids' => array( 4, 5 ), + 'parent_id' => 0, + 'reviews_allowed' => true, + 'default_attributes' => array(), + 'purchase_note' => 'A note', + 'menu_order' => 2, + ); + $product = new WC_Product; + foreach ( $getters_and_setters as $function => $value ) { + $product->{"set_{$function}"}( $value ); + } + $product->create(); + $product = new WC_Product_Simple( $product->get_id() ); + foreach ( $getters_and_setters as $function => $value ) { + $this->assertEquals( $value, $product->{"get_{$function}"}(), $function ); + } + } +} From 560513cde74fb53005037486a92e4620af72ca3b Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 18 Oct 2016 11:19:35 +0100 Subject: [PATCH 021/163] Bump template version --- templates/single-product/related.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/single-product/related.php b/templates/single-product/related.php index b63eed0778d..a6fa13f48ff 100644 --- a/templates/single-product/related.php +++ b/templates/single-product/related.php @@ -13,7 +13,7 @@ * @see https://docs.woocommerce.com/document/template-structure/ * @author WooThemes * @package WooCommerce/Templates - * @version 1.6.4 + * @version 2.7.0 */ if ( ! defined( 'ABSPATH' ) ) { From 6e69b0d924aa26a0bbe7dcdb7273253f5f9a951b Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Tue, 18 Oct 2016 07:47:28 -0700 Subject: [PATCH 022/163] Handle PR feedback: Remove duplicate regular_price update, allow changing of post status for products, remove deprecation for get_title since we might still offer it as a function --- includes/abstracts/abstract-wc-legacy-product.php | 1 - includes/abstracts/abstract-wc-product.php | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index ddd687d011f..965346d7b08 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -129,7 +129,6 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { * @return string */ public function get_title() { - _deprecated_function( 'WC_Product::get_title', '2.7', 'WC_Product::get_name' ); return apply_filters( 'woocommerce_product_title', $this->post ? $this->post->post_title : '', $this ); } diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 377f12f1933..cd13baca14f 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -31,7 +31,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'slug' => '', 'date_created' => '', 'date_modified' => '', - 'status' => '', + 'status' => 'publish', 'featured' => false, 'catalog_visibility' => 'hidden', 'description' => '', @@ -988,7 +988,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { $id = wp_insert_post( apply_filters( 'woocommerce_new_product_data', array( 'post_type' => 'product', - 'post_status' => 'publish', + 'post_status' => $this->get_status(), 'post_author' => get_current_user_id(), 'post_title' => $this->get_name(), 'post_content' => $this->get_description(), @@ -1021,6 +1021,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'post_title' => $this->get_name(), 'post_parent' => $this->get_parent_id(), 'comment_status' => $this->get_reviews_allowed(), + 'post_status' => $this->get_status(), 'menu_order' => $this->get_menu_order(), ); wp_update_post( $post_data ); @@ -1065,7 +1066,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { update_post_meta( $id, '_sku', $this->get_sku() ); update_post_meta( $id, '_regular_price', $this->get_regular_price() ); update_post_meta( $id, '_sale_price', $this->get_sale_price() ); - update_post_meta( $id, '_regular_price', $this->get_regular_price() ); update_post_meta( $id, '_sale_price_dates_from', $this->get_date_on_sale_from() ); update_post_meta( $id, '_sale_price_dates_to', $this->get_date_on_sale_to() ); update_post_meta( $id, 'total_sales', $this->get_total_sales() ); From c2a67803fc7c4affda756482f7813598fd8be313 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 18 Oct 2016 17:13:12 +0100 Subject: [PATCH 023/163] Made abstract function useful --- includes/abstracts/abstract-wc-product.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index cd13baca14f..c33bdcb8a10 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -1388,7 +1388,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return bool */ public function has_child() { - return false; + return 0 < count( $this->get_children() ); } /** From b311caa1bc0c1909b2d3059675e49e0c0c80c1bd Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Tue, 18 Oct 2016 09:59:02 -0700 Subject: [PATCH 024/163] External Product CRUD --- includes/abstracts/abstract-wc-product.php | 6 +- includes/class-wc-product-external.php | 116 ++++++++++++++++-- .../helpers/class-wc-helper-product.php | 31 +++++ tests/unit-tests/product/crud.php | 76 ++++++++++++ 4 files changed, 214 insertions(+), 15 deletions(-) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index cd13baca14f..97ce0e021a0 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -974,8 +974,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'menu_order' => $post_object->menu_order, ) ); $this->read_meta_data(); - - do_action( 'woocommerce_product_loaded', $this ); } /** @@ -1058,9 +1056,9 @@ class WC_Product extends WC_Abstract_Legacy_Product { * Helper method that updates all the post meta for a product based on it's settings in the WC_Product class. * * @since 2.7.0 - * @param int $id Object ID. */ - private function update_post_meta( $id ) { + protected function update_post_meta() { + $id = $this->get_id(); update_post_meta( $id, '_featured', $this->get_featured() ); update_post_meta( $id, '_visibility', $this->get_catalog_visibility() ); update_post_meta( $id, '_sku', $this->get_sku() ); diff --git a/includes/class-wc-product-external.php b/includes/class-wc-product-external.php index 94a66583d07..8c058a516f1 100644 --- a/includes/class-wc-product-external.php +++ b/includes/class-wc-product-external.php @@ -10,13 +10,40 @@ if ( ! defined( 'ABSPATH' ) ) { * External products cannot be bought; they link offsite. Extends simple products. * * @class WC_Product_External - * @version 2.0.0 + * @version 2.7.0 * @package WooCommerce/Classes/Products * @category Class * @author WooThemes */ class WC_Product_External extends WC_Product { + /** + * Stores product data. + * + * @var array + */ + protected $extra_data = array( + 'product_url' => '', + 'button_text' => '', + ); + + /** + * Merges external product data into the parent object. + * @param int|WC_Product|object $product Product to init. + */ + public function __construct( $product = 0 ) { + $this->data = array_merge( $this->data, $this->extra_data ); + parent::__construct( $product ); + } + + /* + |-------------------------------------------------------------------------- + | Getters + |-------------------------------------------------------------------------- + | + | Methods for getting data from the product object. + */ + /** * Get internal type. * @return string @@ -25,6 +52,60 @@ class WC_Product_External extends WC_Product { return 'external'; } + /** + * Get product url. + * + * @return string + */ + public function get_product_url() { + return esc_url( $this->data['product_url'] ); + } + + /** + * Get button text. + * + * @return string + */ + public function get_button_text() { + return $this->data['button_text'] ? $this->data['button_text'] : __( 'Buy product', 'woocommerce' ); + } + + /* + |-------------------------------------------------------------------------- + | Setters + |-------------------------------------------------------------------------- + | + | Functions for setting product data. These should not update anything in the + | database itself and should only change what is stored in the class + | object. + */ + + /** + * Set product URL. + * + * @since 2.7.0 + * @param string $product_url Product URL. + */ + public function set_product_url( $product_url ) { + $this->data['product_url'] = $product_url; + } + + /** + * Set button text. + * + * @since 2.7.0 + * @param string $button_text Button text. + */ + public function set_button_text( $button_text ) { + $this->data['button_text'] = $button_text; + } + + /* + |-------------------------------------------------------------------------- + | Other Actions + |-------------------------------------------------------------------------- + */ + /** * Returns false if the product cannot be bought. * @@ -65,23 +146,36 @@ class WC_Product_External extends WC_Product { return apply_filters( 'woocommerce_product_single_add_to_cart_text', $this->get_button_text(), $this ); } + /* + |-------------------------------------------------------------------------- + | CRUD methods + |-------------------------------------------------------------------------- + */ + /** - * Get product url. + * Reads a product from the database and sets its data to the class. * - * @access public - * @return string + * @since 2.7.0 + * @param int $id Product ID. */ - public function get_product_url() { - return esc_url( $this->product_url ); + public function read( $id ) { + parent::read( $id ); + $this->set_props( array( + 'product_url' => get_post_meta( $id, '_product_url', true ), + 'button_text' => get_post_meta( $id, '_button_text', true ), + ) ); + do_action( 'woocommerce_product_loaded', $this ); + do_action( 'woocommerce_product_' . $this->get_type() . '_loaded', $this ); } /** - * Get button text. + * Helper method that updates all the post meta for an external product. * - * @access public - * @return string + * @since 2.7.0 */ - public function get_button_text() { - return $this->button_text ? $this->button_text : __( 'Buy product', 'woocommerce' ); + protected function update_post_meta() { + parent::update_post_meta(); + update_post_meta( $this->get_id(), '_product_url', $this->get_product_url() ); + update_post_meta( $this->get_id(), '_button_text', $this->get_button_text() ); } } diff --git a/tests/framework/helpers/class-wc-helper-product.php b/tests/framework/helpers/class-wc-helper-product.php index ea7d5989354..895d221f246 100644 --- a/tests/framework/helpers/class-wc-helper-product.php +++ b/tests/framework/helpers/class-wc-helper-product.php @@ -54,6 +54,37 @@ class WC_Helper_Product { return new WC_Product_Simple( $product ); } + /** + * Create external product. + * + * @since 2.7.0 + * + * @return WC_Product_External + */ + public static function create_external_product() { + // Create the product + $product = wp_insert_post( array( + 'post_title' => 'Dummy External Product', + 'post_type' => 'product', + 'post_status' => 'publish', + ) ); + update_post_meta( $product, '_price', '10' ); + update_post_meta( $product, '_regular_price', '10' ); + update_post_meta( $product, '_sale_price', '' ); + update_post_meta( $product, '_sku', 'DUMMY EXTERNAL SKU' ); + update_post_meta( $product, '_manage_stock', 'no' ); + update_post_meta( $product, '_tax_status', 'taxable' ); + update_post_meta( $product, '_downloadable', 'no' ); + update_post_meta( $product, '_virtual', 'taxable' ); + update_post_meta( $product, '_visibility', 'visible' ); + update_post_meta( $product, '_stock_status', 'instock' ); + + update_post_meta( $product, '_product_url', 'http://woocommerce.com' ); + update_post_meta( $product, '_button_text', 'Buy external product' ); + + return new WC_Product_External( $product ); + } + /** * Create a dummy variation product. * diff --git a/tests/unit-tests/product/crud.php b/tests/unit-tests/product/crud.php index e8285f8316b..a5755a60f9e 100644 --- a/tests/unit-tests/product/crud.php +++ b/tests/unit-tests/product/crud.php @@ -114,4 +114,80 @@ class WC_Tests_Product_CRUD extends WC_Unit_Test_Case { $this->assertEquals( $value, $product->{"get_{$function}"}(), $function ); } } + + /** + * Test creating a new external product. + * + * @since 2.7.0 + */ + function test_external_product_create() { + $product = new WC_Product_External; + $product->set_regular_price( 42 ); + $product->set_button_text( 'Test CRUD' ); + $product->set_product_url( 'http://automattic.com' ); + $product->set_name( 'My External Product' ); + $product->create(); + + $read_product = new WC_Product_External( $product->get_id() ); + + $this->assertEquals( '42', $read_product->get_regular_price() ); + $this->assertEquals( 'Test CRUD', $read_product->get_button_text() ); + $this->assertEquals( 'http://automattic.com', $read_product->get_product_url() ); + $this->assertEquals( 'My External Product', $read_product->get_name() ); + } + + /** + * Test getting / reading an external product. Make sure both our external + * product data and the main product data are present. + * + * @since 2.7.0 + */ + function test_external_product_read() { + $product = WC_Helper_Product::create_external_product(); + $product = new WC_Product_External( $product->get_id() ); + + $this->assertEquals( 'Buy external product', $product->get_button_text() ); + $this->assertEquals( '10', $product->get_regular_price() ); + } + + /** + * Test updating an external product. Make sure both our external + * product data and the main product data are written to and present. + * + * @since 2.7.0 + */ + function test_external_product_update() { + $product = WC_Helper_Product::create_external_product(); + + $this->assertEquals( 'Buy external product', $product->get_button_text() ); + $this->assertEquals( '10', $product->get_regular_price() ); + + $product->set_button_text( 'Buy my external product' ); + $product->set_regular_price( 15 ); + $product->save(); + + // Reread from database + $product = new WC_Product_External( $product->get_id() ); + + $this->assertEquals( 'Buy my external product', $product->get_button_text() ); + $this->assertEquals( '15', $product->get_regular_price() ); + } + + /** + * Test external product setters and getters + * + * @since 2.7.0 + */ + public function test_external_product_getters_and_setters() { + $time = time(); + $getters_and_setters = array( + 'button_text' => 'Test Button Text', + 'product_url' => 'http://wordpress.org', + ); + $product = new WC_Product_External; + foreach ( $getters_and_setters as $function => $value ) { + $product->{"set_{$function}"}( $value ); + $this->assertEquals( $value, $product->{"get_{$function}"}(), $function ); + } + } } From c4fb1db46b8c23f7f472c16f01f88fb5c34e9bdf Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Tue, 18 Oct 2016 10:23:20 -0700 Subject: [PATCH 025/163] _virtual meta should be 'no', not taxable, in product unit test helper --- tests/framework/helpers/class-wc-helper-product.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/framework/helpers/class-wc-helper-product.php b/tests/framework/helpers/class-wc-helper-product.php index 895d221f246..04c9bdc016c 100644 --- a/tests/framework/helpers/class-wc-helper-product.php +++ b/tests/framework/helpers/class-wc-helper-product.php @@ -47,7 +47,7 @@ class WC_Helper_Product { update_post_meta( $product, '_manage_stock', 'no' ); update_post_meta( $product, '_tax_status', 'taxable' ); update_post_meta( $product, '_downloadable', 'no' ); - update_post_meta( $product, '_virtual', 'taxable' ); + update_post_meta( $product, '_virtual', 'no' ); update_post_meta( $product, '_visibility', 'visible' ); update_post_meta( $product, '_stock_status', 'instock' ); @@ -75,7 +75,7 @@ class WC_Helper_Product { update_post_meta( $product, '_manage_stock', 'no' ); update_post_meta( $product, '_tax_status', 'taxable' ); update_post_meta( $product, '_downloadable', 'no' ); - update_post_meta( $product, '_virtual', 'taxable' ); + update_post_meta( $product, '_virtual', 'no' ); update_post_meta( $product, '_visibility', 'visible' ); update_post_meta( $product, '_stock_status', 'instock' ); @@ -120,7 +120,7 @@ class WC_Helper_Product { update_post_meta( $product_id, '_manage_stock', 'no' ); update_post_meta( $product_id, '_tax_status', 'taxable' ); update_post_meta( $product_id, '_downloadable', 'no' ); - update_post_meta( $product_id, '_virtual', 'taxable' ); + update_post_meta( $product_id, '_virtual', 'no' ); update_post_meta( $product_id, '_visibility', 'visible' ); update_post_meta( $product_id, '_stock_status', 'instock' ); @@ -161,7 +161,7 @@ class WC_Helper_Product { update_post_meta( $variation_id, '_sku', 'DUMMY SKU VARIABLE SMALL' ); update_post_meta( $variation_id, '_manage_stock', 'no' ); update_post_meta( $variation_id, '_downloadable', 'no' ); - update_post_meta( $variation_id, '_virtual', 'taxable' ); + update_post_meta( $variation_id, '_virtual', 'no' ); update_post_meta( $variation_id, '_stock_status', 'instock' ); // Attribute meta @@ -183,7 +183,7 @@ class WC_Helper_Product { update_post_meta( $variation_id, '_sku', 'DUMMY SKU VARIABLE SMALL' ); update_post_meta( $variation_id, '_manage_stock', 'no' ); update_post_meta( $variation_id, '_downloadable', 'no' ); - update_post_meta( $variation_id, '_virtual', 'taxable' ); + update_post_meta( $variation_id, '_virtual', 'no' ); update_post_meta( $variation_id, '_stock_status', 'instock' ); // Attribute meta From a8b7ee692992affdaf7ae68f0ba695ede5217778 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 18 Oct 2016 18:38:42 +0100 Subject: [PATCH 026/163] Grouped product class --- includes/class-wc-product-grouped.php | 165 +++++++++++++++----------- 1 file changed, 96 insertions(+), 69 deletions(-) diff --git a/includes/class-wc-product-grouped.php b/includes/class-wc-product-grouped.php index e5a3b8f4c14..8d73d6501df 100644 --- a/includes/class-wc-product-grouped.php +++ b/includes/class-wc-product-grouped.php @@ -1,7 +1,6 @@ array(), + ); + + /** + * Merges grouped product data into the parent object. + * @param int|WC_Product|object $product Product to init. + */ + public function __construct( $product = 0 ) { + $this->data = array_merge( $this->data, $this->extra_data ); + parent::__construct( $product ); + } + + /* + |-------------------------------------------------------------------------- + | Getters + |-------------------------------------------------------------------------- + | + | Methods for getting data from the product object. + */ /** * Get internal type. @@ -28,6 +50,15 @@ class WC_Product_Grouped extends WC_Product { return 'grouped'; } + /** + * Return the children of this product. + * + * @return array + */ + public function get_children() { + return $this->data['children']; + } + /** * Get the add to cart button text. * @@ -38,79 +69,20 @@ class WC_Product_Grouped extends WC_Product { return apply_filters( 'woocommerce_product_add_to_cart_text', __( 'View products', 'woocommerce' ), $this ); } - /** - * Return the products children posts. - * - * @access public - * @return array - */ - public function get_children() { - if ( ! is_array( $this->children ) || empty( $this->children ) ) { - $transient_name = 'wc_product_children_' . $this->id; - $this->children = array_filter( array_map( 'absint', (array) get_transient( $transient_name ) ) ); - - if ( empty( $this->children ) ) { - - $args = apply_filters( 'woocommerce_grouped_children_args', array( - 'post_parent' => $this->id, - 'post_type' => 'product', - 'orderby' => 'menu_order', - 'order' => 'ASC', - 'fields' => 'ids', - 'post_status' => 'publish', - 'numberposts' => -1, - ) ); - - $this->children = get_posts( $args ); - - set_transient( $transient_name, $this->children, DAY_IN_SECONDS * 30 ); - } - } - return (array) $this->children; - } - - /** - * Returns whether or not the product has any child product. - * - * @access public - * @return bool - */ - public function has_child() { - return sizeof( $this->get_children() ) ? true : false; - } - /** * Returns whether or not the product is on sale. * - * @access public * @return bool */ public function is_on_sale() { - $is_on_sale = false; - - if ( $this->has_child() ) { - - foreach ( $this->get_children() as $child_id ) { - $sale_price = get_post_meta( $child_id, '_sale_price', true ); - if ( '' !== $sale_price && $sale_price >= 0 ) { - $is_on_sale = true; - } - } - } else { - - if ( $this->sale_price && $this->sale_price == $this->price ) { - $is_on_sale = true; - } - } - - return apply_filters( 'woocommerce_product_is_on_sale', $is_on_sale, $this ); + global $wpdb; + $on_sale = 1 === $wpdb->get_var( "SELECT 1 FROM $wpdb->postmeta WHERE meta_key='_sale_price' AND meta_value>0 AND post_id IN (" . implode( ',', array_map( 'esc_sql', $this->get_children() ) ) . ");" ); + return apply_filters( 'woocommerce_product_is_on_sale', $on_sale, $this ); } - /** * Returns false if the product cannot be bought. * - * @access public * @return bool */ public function is_purchasable() { @@ -118,7 +90,7 @@ class WC_Product_Grouped extends WC_Product { } /** - * Returns the price in html format. + * Returns the price in html format. @todo consider moving to template function * * @access public * @param string $price (default: '') @@ -129,7 +101,7 @@ class WC_Product_Grouped extends WC_Product { $child_prices = array(); foreach ( $this->get_children() as $child_id ) { - $child = wc_get_product( $child_id ); + $child = wc_get_product( $child_id ); if ( '' !== $child->get_price() ) { $child_prices[] = 'incl' === $tax_display_mode ? $child->get_price_including_tax() : $child->get_price_excluding_tax(); } @@ -158,4 +130,59 @@ class WC_Product_Grouped extends WC_Product { return apply_filters( 'woocommerce_get_price_html', $price, $this ); } + + /* + |-------------------------------------------------------------------------- + | Setters + |-------------------------------------------------------------------------- + | + | Methods for getting data from the product object. + */ + + /** + * Return the children of this product. + * + * @param array $children + */ + public function set_children( $children ) { + $this->data['children'] = array_filter( wp_parse_id_list( (array) $children ) ); + } + + /* + |-------------------------------------------------------------------------- + | CRUD methods + |-------------------------------------------------------------------------- + */ + + /** + * Reads a product from the database and sets its data to the class. + * + * @since 2.7.0 + * @param int $id Product ID. + */ + public function read( $id ) { + parent::read( $id ); + + $transient_name = 'wc_product_children_' . $this->get_id(); + $grouped_products = array_filter( wp_parse_id_list( (array) get_transient( $transient_name ) ) ); + + if ( empty( $grouped_products ) ) { + $grouped_products = get_posts( apply_filters( 'woocommerce_grouped_children_args', array( + 'post_parent' => $this->get_id(), + 'post_type' => 'product', + 'orderby' => 'menu_order', + 'order' => 'ASC', + 'fields' => 'ids', + 'post_status' => 'publish', + 'numberposts' => -1, + ) ) ); + set_transient( $transient_name, $grouped_products, DAY_IN_SECONDS * 30 ); + } + + $this->set_props( array( + 'children' => $grouped_products, + ) ); + do_action( 'woocommerce_product_loaded', $this ); + do_action( 'woocommerce_product_' . $this->get_type() . '_loaded', $this ); + } } From 2819fadd7f4f8d3436d250b94d506935b975d56f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 18 Oct 2016 18:38:48 +0100 Subject: [PATCH 027/163] Tests --- .../helpers/class-wc-helper-product.php | 31 +++++++- tests/unit-tests/product/crud.php | 77 +++++++++++++++++-- 2 files changed, 99 insertions(+), 9 deletions(-) diff --git a/tests/framework/helpers/class-wc-helper-product.php b/tests/framework/helpers/class-wc-helper-product.php index ea7d5989354..202a48b26d8 100644 --- a/tests/framework/helpers/class-wc-helper-product.php +++ b/tests/framework/helpers/class-wc-helper-product.php @@ -32,13 +32,14 @@ class WC_Helper_Product { * * @return WC_Product_Simple */ - public static function create_simple_product() { + public static function create_simple_product( $parent = 0 ) { // Create the product $product = wp_insert_post( array( 'post_title' => 'Dummy Product', 'post_type' => 'product', 'post_status' => 'publish', + 'post_parent' => $parent, ) ); update_post_meta( $product, '_price', '10' ); update_post_meta( $product, '_regular_price', '10' ); @@ -47,13 +48,39 @@ class WC_Helper_Product { update_post_meta( $product, '_manage_stock', 'no' ); update_post_meta( $product, '_tax_status', 'taxable' ); update_post_meta( $product, '_downloadable', 'no' ); - update_post_meta( $product, '_virtual', 'taxable' ); + update_post_meta( $product, '_virtual', 'no' ); update_post_meta( $product, '_visibility', 'visible' ); update_post_meta( $product, '_stock_status', 'instock' ); return new WC_Product_Simple( $product ); } + /** + * Create grouped product. + * + * @since 2.7.0 + * + * @return WC_Product_Grouped + */ + public static function create_grouped_product() { + // Create the product + $product = wp_insert_post( array( + 'post_title' => 'Dummy Grouped Product', + 'post_type' => 'product', + 'post_status' => 'publish', + ) ); + $simple_product_1 = self::create_simple_product( $product->ID ); + $simple_product_2 = self::create_simple_product( $product->ID ); + update_post_meta( $product, '_sku', 'DUMMY GROUPED SKU' ); + update_post_meta( $product, '_manage_stock', 'no' ); + update_post_meta( $product, '_tax_status', 'taxable' ); + update_post_meta( $product, '_downloadable', 'no' ); + update_post_meta( $product, '_virtual', 'no' ); + update_post_meta( $product, '_visibility', 'visible' ); + update_post_meta( $product, '_stock_status', 'instock' ); + return new WC_Product_Grouped( $product ); + } + /** * Create a dummy variation product. * diff --git a/tests/unit-tests/product/crud.php b/tests/unit-tests/product/crud.php index e8285f8316b..010c123f9a6 100644 --- a/tests/unit-tests/product/crud.php +++ b/tests/unit-tests/product/crud.php @@ -104,13 +104,76 @@ class WC_Tests_Product_CRUD extends WC_Unit_Test_Case { 'purchase_note' => 'A note', 'menu_order' => 2, ); - $product = new WC_Product; - foreach ( $getters_and_setters as $function => $value ) { - $product->{"set_{$function}"}( $value ); - } - $product->create(); - $product = new WC_Product_Simple( $product->get_id() ); - foreach ( $getters_and_setters as $function => $value ) { + $product = new WC_Product; + foreach ( $getters_and_setters as $function => $value ) { + $product->{"set_{$function}"}( $value ); + } + $product->create(); + $product = new WC_Product_Simple( $product->get_id() ); + foreach ( $getters_and_setters as $function => $value ) { + $this->assertEquals( $value, $product->{"get_{$function}"}(), $function ); + } + } + + /** + * Test creating a new grouped product. + * + * @since 2.7.0 + */ + function test_grouped_product_create() { + $simple_product = WC_Helper_Product::create_simple_product(); + $product = new WC_Product_Grouped; + $product->set_children( array( $simple_product->get_id() ) ); + $product->set_name( 'My Grouped Product' ); + $product->create(); + $read_product = new WC_Product_Grouped( $product->get_id() ); + $this->assertEquals( 'My Grouped Product', $read_product->get_name() ); + $this->assertEquals( array( $simple_product->get_id() ), $read_product->get_children() ); + } + + /** + * Test getting / reading an grouped product. + * + * @since 2.7.0 + */ + function test_grouped_product_read() { + $product = WC_Helper_Product::create_grouped_product(); + $product = new WC_Product_Grouped( $product->get_id() ); + $this->assertEquals( 'Dummy Grouped Product', $read_product->get_name() ); + $this->assertEquals( 2, count( $product->get_children() ) ); + } + /** + * Test updating an grouped product. + * + * @since 2.7.0 + */ + function test_grouped_product_update() { + $product = WC_Helper_Product::create_grouped_product(); + $simple_product = WC_Helper_Product::create_simple_product(); + $this->assertEquals( 'My Grouped Product', $product->get_name() ); + $this->assertEquals( array( $simple_product->get_id() ), $product->get_children() ); + $children = $product->get_children(); + $children[] = $simple_product->get_id(); + $product->set_children( $children ); + $product->set_name( 'My Grouped Product 2' ); + $product->save(); + // Reread from database + $product = new WC_Product_Grouped( $product->get_id() ); + $this->assertEquals( 3, count( $product->get_children() ) ); + $this->assertEquals( 'My Grouped Product 2', $read_product->get_name() ); + } + /** + * Test grouped product setters and getters + * + * @since 2.7.0 + */ + public function test_grouped_product_getters_and_setters() { + $getters_and_setters = array( + 'children' => array( 1, 2, 3 ), + ); + $product = new WC_Product_Grouped; + foreach ( $getters_and_setters as $function => $value ) { + $product->{"set_{$function}"}( $value ); $this->assertEquals( $value, $product->{"get_{$function}"}(), $function ); } } From d705f4cefff0e354d7f02d080abb464dc7e788ec Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 18 Oct 2016 19:05:33 +0100 Subject: [PATCH 028/163] Move children to meta and update test --- .../helpers/class-wc-helper-product.php | 8 ++++---- tests/unit-tests/product/crud.php | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/framework/helpers/class-wc-helper-product.php b/tests/framework/helpers/class-wc-helper-product.php index c0f3405a6e7..0806c799539 100644 --- a/tests/framework/helpers/class-wc-helper-product.php +++ b/tests/framework/helpers/class-wc-helper-product.php @@ -32,14 +32,13 @@ class WC_Helper_Product { * * @return WC_Product_Simple */ - public static function create_simple_product( $parent = 0 ) { + public static function create_simple_product() { // Create the product $product = wp_insert_post( array( 'post_title' => 'Dummy Product', 'post_type' => 'product', 'post_status' => 'publish', - 'post_parent' => $parent, ) ); update_post_meta( $product, '_price', '10' ); update_post_meta( $product, '_regular_price', '10' ); @@ -69,8 +68,9 @@ class WC_Helper_Product { 'post_type' => 'product', 'post_status' => 'publish', ) ); - $simple_product_1 = self::create_simple_product( $product->ID ); - $simple_product_2 = self::create_simple_product( $product->ID ); + $simple_product_1 = self::create_simple_product( $product ); + $simple_product_2 = self::create_simple_product( $product ); + update_post_meta( $product, '_children', array( $simple_product_1->id, $simple_product_2->id ) ); update_post_meta( $product, '_sku', 'DUMMY GROUPED SKU' ); update_post_meta( $product, '_manage_stock', 'no' ); update_post_meta( $product, '_tax_status', 'taxable' ); diff --git a/tests/unit-tests/product/crud.php b/tests/unit-tests/product/crud.php index ca855d0b1ab..45d1b392dc7 100644 --- a/tests/unit-tests/product/crud.php +++ b/tests/unit-tests/product/crud.php @@ -137,10 +137,10 @@ class WC_Tests_Product_CRUD extends WC_Unit_Test_Case { * @since 2.7.0 */ function test_grouped_product_read() { - $product = WC_Helper_Product::create_grouped_product(); - $product = new WC_Product_Grouped( $product->get_id() ); + $product = WC_Helper_Product::create_grouped_product(); + $read_product = new WC_Product_Grouped( $product->get_id() ); $this->assertEquals( 'Dummy Grouped Product', $read_product->get_name() ); - $this->assertEquals( 2, count( $product->get_children() ) ); + $this->assertEquals( 2, count( $read_product->get_children() ) ); } /** * Test updating an grouped product. @@ -150,17 +150,17 @@ class WC_Tests_Product_CRUD extends WC_Unit_Test_Case { function test_grouped_product_update() { $product = WC_Helper_Product::create_grouped_product(); $simple_product = WC_Helper_Product::create_simple_product(); - $this->assertEquals( 'My Grouped Product', $product->get_name() ); - $this->assertEquals( array( $simple_product->get_id() ), $product->get_children() ); + $this->assertEquals( 'Dummy Grouped Product', $product->get_name() ); + $this->assertEquals( 2, count( $product->get_children() ) ); $children = $product->get_children(); $children[] = $simple_product->get_id(); $product->set_children( $children ); - $product->set_name( 'My Grouped Product 2' ); + $product->set_name( 'Dummy Grouped Product 2' ); $product->save(); // Reread from database $product = new WC_Product_Grouped( $product->get_id() ); $this->assertEquals( 3, count( $product->get_children() ) ); - $this->assertEquals( 'My Grouped Product 2', $read_product->get_name() ); + $this->assertEquals( 'Dummy Grouped Product 2', $product->get_name() ); } /** * Test grouped product setters and getters @@ -178,12 +178,12 @@ class WC_Tests_Product_CRUD extends WC_Unit_Test_Case { } } - /** + /** * Test creating a new external product. * * @since 2.7.0 */ - function test_external_product_create() { + function test_external_product_create() { $product = new WC_Product_External; $product->set_regular_price( 42 ); $product->set_button_text( 'Test CRUD' ); From 95de56d402a624af9c20916042e05dcf2455fe71 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 18 Oct 2016 19:08:40 +0100 Subject: [PATCH 029/163] Use get_upsell_ids --- templates/single-product/related.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/single-product/related.php b/templates/single-product/related.php index a6fa13f48ff..d3570b888e4 100644 --- a/templates/single-product/related.php +++ b/templates/single-product/related.php @@ -26,7 +26,7 @@ if ( empty( $product ) || ! $product->exists() ) { return; } -if ( ! $related = wc_get_related_products( $product->get_id(), $posts_per_page, $product->get_upsells() ) ) { +if ( ! $related = wc_get_related_products( $product->get_id(), $posts_per_page, $product->get_upsell_ids() ) ) { return; } From 2a5a9faa61c57f92dffeee49e97c5f2cba626634 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 19 Oct 2016 11:13:08 +0100 Subject: [PATCH 030/163] Spacing in query --- includes/class-wc-product-grouped.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-product-grouped.php b/includes/class-wc-product-grouped.php index 2e30622abfe..2739ec46f43 100644 --- a/includes/class-wc-product-grouped.php +++ b/includes/class-wc-product-grouped.php @@ -76,7 +76,7 @@ class WC_Product_Grouped extends WC_Product { */ public function is_on_sale() { global $wpdb; - $on_sale = 1 === $wpdb->get_var( "SELECT 1 FROM $wpdb->postmeta WHERE meta_key='_sale_price' AND meta_value>0 AND post_id IN (" . implode( ',', array_map( 'esc_sql', $this->get_children() ) ) . ");" ); + $on_sale = 1 === $wpdb->get_var( "SELECT 1 FROM $wpdb->postmeta WHERE meta_key = '_sale_price' AND meta_value > 0 AND post_id IN (" . implode( ',', array_map( 'esc_sql', $this->get_children() ) ) . ");" ); return apply_filters( 'woocommerce_product_is_on_sale', $on_sale, $this ); } From 0520f73a6b56370be05943e77934f20d65960fb1 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 20 Oct 2016 12:42:24 +0100 Subject: [PATCH 031/163] Moving and refactoring methods --- .../abstracts/abstract-wc-legacy-product.php | 11 +- includes/abstracts/abstract-wc-product.php | 449 +++++++----------- 2 files changed, 173 insertions(+), 287 deletions(-) diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index 731d1265a6c..98d6d4ad581 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -48,7 +48,16 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { return wc_get_related_products_query( $cats_array, $tags_array, $exclude_ids, $limit ); } - + /** + * Returns the child product. + * @deprecated 2.7.0 Use wc_get_product instead. + * @param mixed $child_id + * @return WC_Product|WC_Product|WC_Product_variation + */ + public function get_child( $child_id ) { + _deprecated_function( 'WC_Product::get_child', '2.7', 'wc_get_product' ); + return wc_get_product( $child_id ); + } /** diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 6823645cacc..05e17895b21 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -31,7 +31,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'slug' => '', 'date_created' => '', 'date_modified' => '', - 'status' => 'publish', + 'status' => false, 'featured' => false, 'catalog_visibility' => 'hidden', 'description' => '', @@ -61,6 +61,8 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'attributes' => array(), 'default_attributes' => array(), 'menu_order' => 0, + 'virtual' => false, // @todo + 'downloadable' => false, // @todo ); /** @@ -96,6 +98,128 @@ class WC_Product extends WC_Abstract_Legacy_Product { } } + /* + |-------------------------------------------------------------------------- + | Conditionals + |-------------------------------------------------------------------------- + */ + + /** + * Check if a product supports a given feature. + * + * Product classes should override this to declare support (or lack of support) for a feature. + * + * @param string $feature string The name of a feature to test support for. + * @return bool True if the product supports the feature, false otherwise. + * @since 2.5.0 + */ + public function supports( $feature ) { + return apply_filters( 'woocommerce_product_supports', in_array( $feature, $this->supports ) ? true : false, $feature, $this ); + } + + /** + * Returns whether or not the product post exists. + * + * @return bool + */ + public function exists() { + return false !== $this->get_status(); + } + + /** + * Checks the product type. + * + * Backwards compat with downloadable/virtual. + * + * @param string $type Array or string of types + * @return bool + */ + public function is_type( $type ) { + return ( $this->get_type() === $type || ( is_array( $type ) && in_array( $this->get_type(), $type ) ) ); + } + + /** + * Checks if a product is downloadable. + * + * @return bool + */ + public function is_downloadable() { + return apply_filters( 'woocommerce_is_downloadable', true === $this->data['downloadable'] , $this ); + } + + /** + * Checks if a product is virtual (has no shipping). + * + * @return bool + */ + public function is_virtual() { + return apply_filters( 'woocommerce_is_virtual', true === $this->data['virtual'], $this ); + } + + /** + * Returns whether or not the product is featured. + * + * @return bool + */ + public function is_featured() { + return true === $this->get_featured(); + } + + /** + * Check if a product is sold individually (no quantities). + * + * @return bool + */ + public function is_sold_individually() { + return apply_filters( 'woocommerce_is_sold_individually', true === $this->get_sold_individually(), $this ); + } + + /** + * Returns whether or not the product is visible in the catalog. + * + * @return bool + */ + public function is_visible() { + $visible = 'visible' === $this->get_catalog_visibility() || ( is_search() && 'search' === $this->get_catalog_visibility() ) || ( ! is_search() && 'catalog' === $this->get_catalog_visibility() ); + + if ( 'publish' !== $this->get_status() && ! current_user_can( 'edit_post', $this->get_id() ) ) { + $visible = false; + } + + if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ! $this->is_in_stock() ) { + $visible = false; + } + + return apply_filters( 'woocommerce_product_is_visible', $visible, $this->get_id() ); + } + + /** + * Returns false if the product cannot be bought. + * + * @return bool + */ + public function is_purchasable() { + return apply_filters( 'woocommerce_is_purchasable', $this->exists() && ( 'publish' === $this->get_status() || current_user_can( 'edit_post', $this->get_id() ) ) && '' !== $this->get_price(), $this ); + } + + /** + * Returns whether or not the product has dimensions set. + * + * @return bool + */ + public function has_dimensions() { + return $this->get_length() || $this->get_height() || $this->get_width(); + } + + /** + * Returns whether or not the product has weight set. + * + * @return bool + */ + public function has_weight() { + return $this->get_weight() ? true : false; + } + /* |-------------------------------------------------------------------------- | Getters @@ -1085,24 +1209,45 @@ class WC_Product extends WC_Abstract_Legacy_Product { update_post_meta( $id, '_default_attributes', $this->get_default_attributes() ); } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /* |-------------------------------------------------------------------------- | Other Actions |-------------------------------------------------------------------------- */ - /** - * Check if a product supports a given feature. - * - * Product classes should override this to declare support (or lack of support) for a feature. - * - * @param string $feature string The name of a feature to test support for. - * @return bool True if the product supports the feature, false otherwise. - * @since 2.5.0 - */ - public function supports( $feature ) { - return apply_filters( 'woocommerce_product_supports', in_array( $feature, $this->supports ) ? true : false, $feature, $this ); - } + /** * Returns the gallery attachment ids. @@ -1218,26 +1363,9 @@ class WC_Product extends WC_Abstract_Legacy_Product { return $this->set_stock( $amount, 'add' ); } - /** - * Checks the product type. - * - * Backwards compat with downloadable/virtual. - * - * @param string $type Array or string of types - * @return bool - */ - public function is_type( $type ) { - return ( $this->get_type() === $type || ( is_array( $type ) && in_array( $this->get_type(), $type ) ) ); - } - /** - * Checks if a product is downloadable. - * - * @return bool - */ - public function is_downloadable() { - return ( 'yes' === $this->downloadable ); - } + + /** * Check if downloadable product has a file attached. @@ -1327,14 +1455,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_product_file_download_path', $file_path, $this, $download_id ); } - /** - * Checks if a product is virtual (has no shipping). - * - * @return bool - */ - public function is_virtual() { - return apply_filters( 'woocommerce_is_virtual', ( 'yes' === $this->virtual ), $this ); - } + /** * Checks if a product needs shipping. @@ -1345,31 +1466,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_product_needs_shipping', $this->is_virtual() ? false : true, $this ); } - /** - * Check if a product is sold individually (no quantities). - * - * @return bool - */ - public function is_sold_individually() { - $return = false; - - if ( 'yes' == $this->sold_individually ) { - $return = true; - } - - return apply_filters( 'woocommerce_is_sold_individually', $return, $this ); - } - - /** - * Returns the child product. - * - * @param mixed $child_id - * @return WC_Product|WC_Product|WC_Product_variation - */ - public function get_child( $child_id ) { - return wc_get_product( $child_id ); - } /** * Returns the children. @@ -1389,14 +1486,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { return 0 < count( $this->get_children() ); } - /** - * Returns whether or not the product post exists. - * - * @return bool - */ - public function exists() { - return empty( $this->post ) ? false : true; - } + /** * Returns whether or not the product is taxable. @@ -1579,47 +1669,8 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_get_availability_class', $class, $this ); } - /** - * Returns whether or not the product is featured. - * - * @return bool - */ - public function is_featured() { - return 'yes' === $this->get_featured(); - } - /** - * Returns whether or not the product is visible in the catalog. - * - * @return bool - */ - public function is_visible() { - if ( ! $this->post ) { - $visible = false; - // Published/private. - } elseif ( 'publish' !== $this->post->post_status && ! current_user_can( 'edit_post', $this->get_id() ) ) { - $visible = false; - - // Out of stock visibility. - } elseif ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ! $this->is_in_stock() ) { - $visible = false; - - // visibility setting. - } elseif ( 'hidden' === $this->visibility ) { - $visible = false; - } elseif ( 'visible' === $this->visibility ) { - $visible = true; - - // Visibility in loop. - } elseif ( is_search() ) { - $visible = 'search' === $this->visibility; - } else { - $visible = 'catalog' === $this->visibility; - } - - return apply_filters( 'woocommerce_product_is_visible', $visible, $this->get_id() ); - } /** * Returns whether or not the product is on sale. @@ -1630,30 +1681,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_product_is_on_sale', ( $this->get_sale_price() !== $this->get_regular_price() && $this->get_sale_price() === $this->get_price() ), $this ); } - /** - * Returns false if the product cannot be bought. - * - * @return bool - */ - public function is_purchasable() { - $purchasable = true; - - // Products must exist of course. - if ( ! $this->exists() ) { - $purchasable = false; - - // Other products types need a price to be set. - } elseif ( $this->get_price() === '' ) { - $purchasable = false; - - // Check the product is published. - } elseif ( 'publish' !== $this->post->post_status && ! current_user_can( 'edit_post', $this->get_id() ) ) { - $purchasable = false; - } - - return apply_filters( 'woocommerce_is_purchasable', $purchasable, $this ); - } /** * Set a products price dynamically. @@ -1895,144 +1923,9 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_get_price_html_from_to', $price, $from, $to, $this ); } - /** - * Get the average rating of product. This is calculated once and stored in postmeta. - * @return string - */ - public function get_average_rating() { - // No meta data? Do the calculation - if ( ! metadata_exists( 'post', $this->get_id(), '_wc_average_rating' ) ) { - $this->sync_average_rating( $this->get_id() ); - } - return (string) floatval( get_post_meta( $this->get_id(), '_wc_average_rating', true ) ); - } - /** - * Get the total amount (COUNT) of ratings. - * @param int $value Optional. Rating value to get the count for. By default returns the count of all rating values. - * @return int - */ - public function get_rating_count( $value = null ) { - // No meta data? Do the calculation - if ( ! metadata_exists( 'post', $this->get_id(), '_wc_rating_count' ) ) { - $this->sync_rating_count( $this->get_id() ); - } - $counts = get_post_meta( $this->get_id(), '_wc_rating_count', true ); - - if ( is_null( $value ) ) { - return array_sum( $counts ); - } else { - return isset( $counts[ $value ] ) ? $counts[ $value ] : 0; - } - } - - /** - * Sync product rating. Can be called statically. - * @param int $post_id - */ - public static function sync_average_rating( $post_id ) { - if ( ! metadata_exists( 'post', $post_id, '_wc_rating_count' ) ) { - self::sync_rating_count( $post_id ); - } - - $count = array_sum( (array) get_post_meta( $post_id, '_wc_rating_count', true ) ); - - if ( $count ) { - global $wpdb; - - $ratings = $wpdb->get_var( $wpdb->prepare(" - SELECT SUM(meta_value) FROM $wpdb->commentmeta - LEFT JOIN $wpdb->comments ON $wpdb->commentmeta.comment_id = $wpdb->comments.comment_ID - WHERE meta_key = 'rating' - AND comment_post_ID = %d - AND comment_approved = '1' - AND meta_value > 0 - ", $post_id ) ); - $average = number_format( $ratings / $count, 2, '.', '' ); - } else { - $average = 0; - } - update_post_meta( $post_id, '_wc_average_rating', $average ); - } - - /** - * Sync product rating count. Can be called statically. - * @param int $post_id - */ - public static function sync_rating_count( $post_id ) { - global $wpdb; - - $counts = array(); - $raw_counts = $wpdb->get_results( $wpdb->prepare( " - SELECT meta_value, COUNT( * ) as meta_value_count FROM $wpdb->commentmeta - LEFT JOIN $wpdb->comments ON $wpdb->commentmeta.comment_id = $wpdb->comments.comment_ID - WHERE meta_key = 'rating' - AND comment_post_ID = %d - AND comment_approved = '1' - AND meta_value > 0 - GROUP BY meta_value - ", $post_id ) ); - - foreach ( $raw_counts as $count ) { - $counts[ $count->meta_value ] = $count->meta_value_count; - } - - update_post_meta( $post_id, '_wc_rating_count', $counts ); - } - - /** - * Returns the product rating in html format. - * - * @param string $rating (default: '') - * - * @return string - */ - public function get_rating_html( $rating = null ) { - $rating_html = ''; - - if ( ! is_numeric( $rating ) ) { - $rating = $this->get_average_rating(); - } - - if ( $rating > 0 ) { - - $rating_html = '
'; - - $rating_html .= '' . $rating . ' ' . __( 'out of 5', 'woocommerce' ) . ''; - - $rating_html .= '
'; - } - - return apply_filters( 'woocommerce_product_get_rating_html', $rating_html, $rating ); - } - - /** - * Get the total amount (COUNT) of reviews. - * - * @since 2.3.2 - * @return int The total numver of product reviews - */ - public function get_review_count() { - global $wpdb; - - // No meta date? Do the calculation - if ( ! metadata_exists( 'post', $this->get_id(), '_wc_review_count' ) ) { - $count = $wpdb->get_var( $wpdb->prepare(" - SELECT COUNT(*) FROM $wpdb->comments - WHERE comment_parent = 0 - AND comment_post_ID = %d - AND comment_approved = '1' - ", $this->get_id() ) ); - - update_post_meta( $this->get_id(), '_wc_review_count', $count ); - } else { - $count = get_post_meta( $this->get_id(), '_wc_review_count', true ); - } - - return apply_filters( 'woocommerce_product_review_count', $count, $this ); - } /** * Returns the product shipping class. @@ -2134,14 +2027,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'wc_product_enable_dimensions_display', true ) && ( $this->has_dimensions() || $this->has_weight() ); } - /** - * Returns whether or not the product has dimensions set. - * - * @return bool - */ - public function has_dimensions() { - return $this->get_dimensions() ? true : false; - } + /** * Does a child have dimensions set? @@ -2153,15 +2039,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { return false; } - /** - * Returns whether or not the product has weight set. - * - * @return bool - */ - public function has_weight() { - return $this->get_weight() ? true : false; - } - /** * Does a child have a weight set? * @since 2.7.0 From 61a866f8f066b82707ec5284df0c19e02ad503e9 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 20 Oct 2016 15:02:25 +0100 Subject: [PATCH 032/163] Availability html --- .../abstracts/abstract-wc-legacy-product.php | 123 +++ includes/abstracts/abstract-wc-product.php | 757 +++++++----------- includes/class-wc-product-variable.php | 6 +- includes/wc-formatting-functions.php | 41 + includes/wc-template-functions.php | 58 ++ .../single-product/add-to-cart/grouped.php | 6 +- .../single-product/add-to-cart/simple.php | 12 +- templates/single-product/stock.php | 35 + .../tabs/additional-information.php | 4 +- 9 files changed, 541 insertions(+), 501 deletions(-) create mode 100644 templates/single-product/stock.php diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index 98d6d4ad581..828bd44ab51 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -59,6 +59,129 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { return wc_get_product( $child_id ); } + /** + * Functions for getting parts of a price, in html, used by get_price_html. + * + * @deprecated 2.7.0 + * @return string + */ + public function get_price_html_from_text() { + _deprecated_function( 'WC_Product::get_price_html_from_text', '2.7' ); + $from = '' . _x( 'From:', 'min_price', 'woocommerce' ) . ' '; + return apply_filters( 'woocommerce_get_price_html_from_text', $from, $this ); + } + + /** + * Functions for getting parts of a price, in html, used by get_price_html. + * + * @deprecated 2.7.0 Use wc_format_price_range instead. + * @param string $from String or float to wrap with 'from' text + * @param mixed $to String or float to wrap with 'to' text + * @return string + */ + public function get_price_html_from_to( $from, $to ) { + _deprecated_function( 'WC_Product::get_price_html_from_to', '2.7', 'wc_format_price_range' ); + return apply_filters( 'woocommerce_get_price_html_from_to', wc_format_price_range( $from, $to ), $from, $to, $this ); + } + + /** + * Get the suffix to display after prices > 0. + * + * @deprecated 2.7.0 Use wc_get_price_suffix instead. + * @param string $price to calculate, left blank to just use get_price() + * @param integer $qty passed on to get_price_including_tax() or get_price_excluding_tax() + * @return string + */ + public function get_price_suffix( $price = '', $qty = 1 ) { + _deprecated_function( 'WC_Product::get_price_suffix', '2.7', 'wc_get_price_suffix' ); + return wc_get_price_suffix( $this, $price, $qty ); + } + + /** + * Lists a table of attributes for the product page. + * @deprecated 2.7.0 Use wc_display_product_attributes instead. + */ + public function list_attributes() { + _deprecated_function( 'WC_Product::list_attributes', '2.7', 'wc_display_product_attributes' ); + wc_display_product_attributes( $this ); + } + + /** + * Returns the availability of the product. + * + * If stock management is enabled at global and product level, a stock message + * will be shown. e.g. In stock, In stock x10, Out of stock. + * + * If stock management is disabled at global or product level, out of stock + * will be shown when needed, but in stock will be hidden from view. + * + * This can all be changed through use of the woocommerce_get_availability filter. + * + * @return string + */ + public function get_availability() { + return apply_filters( 'woocommerce_get_availability', array( + 'availability' => $this->get_availability_text(), + 'class' => $this->get_availability_class(), + ), $this ); + } + + /** + * Get availability text based on stock status. + * + * @return string + */ + protected function get_availability_text() { + if ( ! $this->is_in_stock() ) { + $availability = __( 'Out of stock', 'woocommerce' ); + } elseif ( $this->managing_stock() && $this->is_on_backorder( 1 ) ) { + $availability = $this->backorders_require_notification() ? __( 'Available on backorder', 'woocommerce' ) : __( 'In stock', 'woocommerce' ); + } elseif ( $this->managing_stock() ) { + switch ( get_option( 'woocommerce_stock_format' ) ) { + case 'no_amount' : + $availability = __( 'In stock', 'woocommerce' ); + break; + case 'low_amount' : + if ( $this->get_total_stock() <= get_option( 'woocommerce_notify_low_stock_amount' ) ) { + $availability = sprintf( __( 'Only %s left in stock', 'woocommerce' ), $this->get_total_stock() ); + + if ( $this->backorders_allowed() && $this->backorders_require_notification() ) { + $availability .= ' ' . __( '(also available on backorder)', 'woocommerce' ); + } + } else { + $availability = __( 'In stock', 'woocommerce' ); + } + break; + default : + $availability = sprintf( __( '%s in stock', 'woocommerce' ), $this->get_total_stock() ); + + if ( $this->backorders_allowed() && $this->backorders_require_notification() ) { + $availability .= ' ' . __( '(also available on backorder)', 'woocommerce' ); + } + break; + } + } else { + $availability = ''; + } + return apply_filters( 'woocommerce_get_availability_text', $availability, $this ); + } + + /** + * Get availability classname based on stock status. + * + * @return string + */ + protected function get_availability_class() { + if ( ! $this->is_in_stock() ) { + $class = 'out-of-stock'; + } elseif ( $this->managing_stock() && $this->is_on_backorder( 1 ) && $this->backorders_require_notification() ) { + $class = 'available-on-backorder'; + } else { + $class = 'in-stock'; + } + + return apply_filters( 'woocommerce_get_availability_class', $class, $this ); + } /** * The product's type (simple, variable etc). diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 05e17895b21..d724decd0a5 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -98,128 +98,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { } } - /* - |-------------------------------------------------------------------------- - | Conditionals - |-------------------------------------------------------------------------- - */ - - /** - * Check if a product supports a given feature. - * - * Product classes should override this to declare support (or lack of support) for a feature. - * - * @param string $feature string The name of a feature to test support for. - * @return bool True if the product supports the feature, false otherwise. - * @since 2.5.0 - */ - public function supports( $feature ) { - return apply_filters( 'woocommerce_product_supports', in_array( $feature, $this->supports ) ? true : false, $feature, $this ); - } - - /** - * Returns whether or not the product post exists. - * - * @return bool - */ - public function exists() { - return false !== $this->get_status(); - } - - /** - * Checks the product type. - * - * Backwards compat with downloadable/virtual. - * - * @param string $type Array or string of types - * @return bool - */ - public function is_type( $type ) { - return ( $this->get_type() === $type || ( is_array( $type ) && in_array( $this->get_type(), $type ) ) ); - } - - /** - * Checks if a product is downloadable. - * - * @return bool - */ - public function is_downloadable() { - return apply_filters( 'woocommerce_is_downloadable', true === $this->data['downloadable'] , $this ); - } - - /** - * Checks if a product is virtual (has no shipping). - * - * @return bool - */ - public function is_virtual() { - return apply_filters( 'woocommerce_is_virtual', true === $this->data['virtual'], $this ); - } - - /** - * Returns whether or not the product is featured. - * - * @return bool - */ - public function is_featured() { - return true === $this->get_featured(); - } - - /** - * Check if a product is sold individually (no quantities). - * - * @return bool - */ - public function is_sold_individually() { - return apply_filters( 'woocommerce_is_sold_individually', true === $this->get_sold_individually(), $this ); - } - - /** - * Returns whether or not the product is visible in the catalog. - * - * @return bool - */ - public function is_visible() { - $visible = 'visible' === $this->get_catalog_visibility() || ( is_search() && 'search' === $this->get_catalog_visibility() ) || ( ! is_search() && 'catalog' === $this->get_catalog_visibility() ); - - if ( 'publish' !== $this->get_status() && ! current_user_can( 'edit_post', $this->get_id() ) ) { - $visible = false; - } - - if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ! $this->is_in_stock() ) { - $visible = false; - } - - return apply_filters( 'woocommerce_product_is_visible', $visible, $this->get_id() ); - } - - /** - * Returns false if the product cannot be bought. - * - * @return bool - */ - public function is_purchasable() { - return apply_filters( 'woocommerce_is_purchasable', $this->exists() && ( 'publish' === $this->get_status() || current_user_can( 'edit_post', $this->get_id() ) ) && '' !== $this->get_price(), $this ); - } - - /** - * Returns whether or not the product has dimensions set. - * - * @return bool - */ - public function has_dimensions() { - return $this->get_length() || $this->get_height() || $this->get_width(); - } - - /** - * Returns whether or not the product has weight set. - * - * @return bool - */ - public function has_weight() { - return $this->get_weight() ? true : false; - } - /* |-------------------------------------------------------------------------- | Getters @@ -1021,7 +899,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param array $terms_id List of terms IDs. */ public function set_categories( $terms_id ) { - $this->save_taxonomy_terms( $terms_id, 'cat' ); + //$this->save_taxonomy_terms( $terms_id, 'cat' ); } /** @@ -1031,7 +909,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param array $terms_id List of terms IDs. */ public function set_tags( $terms_id ) { - $this->save_taxonomy_terms( $terms_id, 'tag' ); + //$this->save_taxonomy_terms( $terms_id, 'tag' ); } /* @@ -1209,22 +1087,289 @@ class WC_Product extends WC_Abstract_Legacy_Product { update_post_meta( $id, '_default_attributes', $this->get_default_attributes() ); } + /* + |-------------------------------------------------------------------------- + | Conditionals + |-------------------------------------------------------------------------- + */ + /** + * Check if a product supports a given feature. + * + * Product classes should override this to declare support (or lack of support) for a feature. + * + * @param string $feature string The name of a feature to test support for. + * @return bool True if the product supports the feature, false otherwise. + * @since 2.5.0 + */ + public function supports( $feature ) { + return apply_filters( 'woocommerce_product_supports', in_array( $feature, $this->supports ) ? true : false, $feature, $this ); + } + /** + * Returns whether or not the product post exists. + * + * @return bool + */ + public function exists() { + return false !== $this->get_status(); + } + /** + * Checks the product type. + * + * Backwards compat with downloadable/virtual. + * + * @param string $type Array or string of types + * @return bool + */ + public function is_type( $type ) { + return ( $this->get_type() === $type || ( is_array( $type ) && in_array( $this->get_type(), $type ) ) ); + } + /** + * Checks if a product is downloadable. + * + * @return bool + */ + public function is_downloadable() { + return apply_filters( 'woocommerce_is_downloadable', true === $this->data['downloadable'] , $this ); + } + /** + * Checks if a product is virtual (has no shipping). + * + * @return bool + */ + public function is_virtual() { + return apply_filters( 'woocommerce_is_virtual', true === $this->data['virtual'], $this ); + } + /** + * Returns whether or not the product is featured. + * + * @return bool + */ + public function is_featured() { + return true === $this->get_featured(); + } + /** + * Check if a product is sold individually (no quantities). + * + * @return bool + */ + public function is_sold_individually() { + return apply_filters( 'woocommerce_is_sold_individually', true === $this->get_sold_individually(), $this ); + } + /** + * Returns whether or not the product is visible in the catalog. + * + * @return bool + */ + public function is_visible() { + $visible = 'visible' === $this->get_catalog_visibility() || ( is_search() && 'search' === $this->get_catalog_visibility() ) || ( ! is_search() && 'catalog' === $this->get_catalog_visibility() ); + if ( 'publish' !== $this->get_status() && ! current_user_can( 'edit_post', $this->get_id() ) ) { + $visible = false; + } + if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ! $this->is_in_stock() ) { + $visible = false; + } + return apply_filters( 'woocommerce_product_is_visible', $visible, $this->get_id() ); + } + /** + * Returns false if the product cannot be bought. + * + * @return bool + */ + public function is_purchasable() { + return apply_filters( 'woocommerce_is_purchasable', $this->exists() && ( 'publish' === $this->get_status() || current_user_can( 'edit_post', $this->get_id() ) ) && '' !== $this->get_price(), $this ); + } + /** + * Returns whether or not the product is on sale. + * + * @return bool + */ + public function is_on_sale() { + return apply_filters( 'woocommerce_product_is_on_sale', ( $this->get_sale_price() !== $this->get_regular_price() && $this->get_sale_price() === $this->get_price() ), $this ); + } + /** + * Returns whether or not the product has dimensions set. + * + * @return bool + */ + public function has_dimensions() { + return $this->get_length() || $this->get_height() || $this->get_width(); + } + /** + * Returns whether or not the product has weight set. + * + * @return bool + */ + public function has_weight() { + return $this->get_weight() ? true : false; + } + /** + * Returns whether or not the product is in stock. + * + * @return bool + */ + public function is_in_stock() { + return apply_filters( 'woocommerce_product_is_in_stock', 'instock' === $this->get_stock_status(), $this ); + } + + /** + * Checks if a product needs shipping. + * + * @return bool + */ + public function needs_shipping() { + return apply_filters( 'woocommerce_product_needs_shipping', ! $this->is_virtual(), $this ); + } + + /** + * Returns whether or not the product is taxable. + * + * @return bool + */ + public function is_taxable() { + return apply_filters( 'woocommerce_product_is_taxable', $this->get_tax_status() === 'taxable' && wc_tax_enabled(), $this ); + } + + /** + * Returns whether or not the product shipping is taxable. + * + * @return bool + */ + public function is_shipping_taxable() { + return $this->get_tax_status() === 'taxable' || $this->get_tax_status() === 'shipping'; + } + + /** + * Returns whether or not the product is stock managed. + * + * @return bool + */ + public function managing_stock() { + return $this->get_manage_stock() && 'yes' === get_option( 'woocommerce_manage_stock' ); + } + + /** + * Returns whether or not the product can be backordered. + * + * @return bool + */ + public function backorders_allowed() { + return apply_filters( 'woocommerce_product_backorders_allowed', ( 'yes' === $this->get_backorders() || 'notify' === $this->get_backorders() ), $this->get_id(), $this ); + } + + /** + * Returns whether or not the product needs to notify the customer on backorder. + * + * @return bool + */ + public function backorders_require_notification() { + return apply_filters( 'woocommerce_product_backorders_require_notification', ( $this->managing_stock() && 'notify' === $this->get_backorders() ), $this ); + } + + /** + * Check if a product is on backorder. + * + * @param int $qty_in_cart (default: 0) + * @return bool + */ + public function is_on_backorder( $qty_in_cart = 0 ) { + return $this->managing_stock() && $this->backorders_allowed() && ( $this->get_total_stock() - $qty_in_cart ) < 0 ? true : false; + } + + /** + * Returns whether or not the product has enough stock for the order. + * + * @param mixed $quantity + * @return bool + */ + public function has_enough_stock( $quantity ) { + return ! $this->managing_stock() || $this->backorders_allowed() || $this->get_stock_quantity() >= $quantity ? true : false; + } + + /* + |-------------------------------------------------------------------------- + | Non-CRUD Getters + |-------------------------------------------------------------------------- + */ + + /** + * Get the add to url used mainly in loops. + * + * @return string + */ + public function add_to_cart_url() { + return apply_filters( 'woocommerce_product_add_to_cart_url', get_permalink( $this->get_id() ), $this ); + } + + /** + * Get the add to cart button text for the single page. + * + * @return string + */ + public function single_add_to_cart_text() { + return apply_filters( 'woocommerce_product_single_add_to_cart_text', __( 'Add to cart', 'woocommerce' ), $this ); + } + + /** + * Get the add to cart button text. + * + * @return string + */ + public function add_to_cart_text() { + return apply_filters( 'woocommerce_product_add_to_cart_text', __( 'Read more', 'woocommerce' ), $this ); + } + + /** + * Gets the main product image ID. + * + * @return int + */ + public function get_image_id() { + if ( has_post_thumbnail( $this->get_id() ) ) { + $image_id = get_post_thumbnail_id( $this->get_id() ); + } elseif ( ( $parent_id = wp_get_post_parent_id( $this->get_id() ) ) && has_post_thumbnail( $parent_id ) ) { + $image_id = get_post_thumbnail_id( $parent_id ); + } else { + $image_id = 0; + } + return $image_id; + } + + /** + * Returns the main product image. + * + * @param string $size (default: 'shop_thumbnail') + * @param array $attr + * @param bool True to return $placeholder if no image is found, or false to return an empty string. + * @return string + */ + public function get_image( $size = 'shop_thumbnail', $attr = array(), $placeholder = true ) { + if ( has_post_thumbnail( $this->get_id() ) ) { + $image = get_the_post_thumbnail( $this->get_id(), $size, $attr ); + } elseif ( ( $parent_id = wp_get_post_parent_id( $this->get_id() ) ) && has_post_thumbnail( $parent_id ) ) { + $image = get_the_post_thumbnail( $parent_id, $size, $attr ); + } elseif ( $placeholder ) { + $image = wc_placeholder_img( $size ); + } else { + $image = ''; + } + return str_replace( array( 'https://', 'http://' ), '//', $image ); + } @@ -1457,14 +1602,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { - /** - * Checks if a product needs shipping. - * - * @return bool - */ - public function needs_shipping() { - return apply_filters( 'woocommerce_product_needs_shipping', $this->is_virtual() ? false : true, $this ); - } + @@ -1488,198 +1626,10 @@ class WC_Product extends WC_Abstract_Legacy_Product { - /** - * Returns whether or not the product is taxable. - * - * @return bool - */ - public function is_taxable() { - $taxable = $this->get_tax_status() === 'taxable' && wc_tax_enabled() ? true : false; - return apply_filters( 'woocommerce_product_is_taxable', $taxable, $this ); - } - - /** - * Returns whether or not the product shipping is taxable. - * - * @return bool - */ - public function is_shipping_taxable() { - return $this->get_tax_status() === 'taxable' || $this->get_tax_status() === 'shipping' ? true : false; - } - - /** - * Get the add to url used mainly in loops. - * - * @return string - */ - public function add_to_cart_url() { - return apply_filters( 'woocommerce_product_add_to_cart_url', get_permalink( $this->get_id() ), $this ); - } - - /** - * Get the add to cart button text for the single page. - * - * @return string - */ - public function single_add_to_cart_text() { - return apply_filters( 'woocommerce_product_single_add_to_cart_text', __( 'Add to cart', 'woocommerce' ), $this ); - } - - /** - * Get the add to cart button text. - * - * @return string - */ - public function add_to_cart_text() { - return apply_filters( 'woocommerce_product_add_to_cart_text', __( 'Read more', 'woocommerce' ), $this ); - } - - /** - * Returns whether or not the product is stock managed. - * - * @return bool - */ - public function managing_stock() { - $managing_stock = 'no' === $this->get_manage_stock() || 'yes' !== get_option( 'woocommerce_manage_stock' ); - - return ! $managing_stock; - } - - /** - * Returns whether or not the product is in stock. - * - * @return bool - */ - public function is_in_stock() { - return apply_filters( 'woocommerce_product_is_in_stock', ( 'instock' === $this->stock_status ), $this ); - } - - /** - * Returns whether or not the product can be backordered. - * - * @return bool - */ - public function backorders_allowed() { - return apply_filters( 'woocommerce_product_backorders_allowed', ( 'yes' === $this->get_backorders() || 'notify' === $this->get_backorders() ), $this->get_id(), $this ); - } - - /** - * Returns whether or not the product needs to notify the customer on backorder. - * - * @return bool - */ - public function backorders_require_notification() { - return apply_filters( 'woocommerce_product_backorders_require_notification', ( $this->managing_stock() && 'notify' === $this->backorders ), $this ); - } - - /** - * Check if a product is on backorder. - * - * @param int $qty_in_cart (default: 0) - * @return bool - */ - public function is_on_backorder( $qty_in_cart = 0 ) { - return $this->managing_stock() && $this->backorders_allowed() && ( $this->get_total_stock() - $qty_in_cart ) < 0 ? true : false; - } - - /** - * Returns whether or not the product has enough stock for the order. - * - * @param mixed $quantity - * @return bool - */ - public function has_enough_stock( $quantity ) { - return ! $this->managing_stock() || $this->backorders_allowed() || $this->get_stock_quantity() >= $quantity ? true : false; - } - - /** - * Returns the availability of the product. - * - * If stock management is enabled at global and product level, a stock message - * will be shown. e.g. In stock, In stock x10, Out of stock. - * - * If stock management is disabled at global or product level, out of stock - * will be shown when needed, but in stock will be hidden from view. - * - * This can all be changed through use of the woocommerce_get_availability filter. - * - * @return string - */ - public function get_availability() { - return apply_filters( 'woocommerce_get_availability', array( - 'availability' => $this->get_availability_text(), - 'class' => $this->get_availability_class(), - ), $this ); - } - - /** - * Get availability text based on stock status. - * - * @return string - */ - protected function get_availability_text() { - if ( ! $this->is_in_stock() ) { - $availability = __( 'Out of stock', 'woocommerce' ); - } elseif ( $this->managing_stock() && $this->is_on_backorder( 1 ) ) { - $availability = $this->backorders_require_notification() ? __( 'Available on backorder', 'woocommerce' ) : __( 'In stock', 'woocommerce' ); - } elseif ( $this->managing_stock() ) { - switch ( get_option( 'woocommerce_stock_format' ) ) { - case 'no_amount' : - $availability = __( 'In stock', 'woocommerce' ); - break; - case 'low_amount' : - if ( $this->get_total_stock() <= get_option( 'woocommerce_notify_low_stock_amount' ) ) { - $availability = sprintf( __( 'Only %s left in stock', 'woocommerce' ), $this->get_total_stock() ); - - if ( $this->backorders_allowed() && $this->backorders_require_notification() ) { - $availability .= ' ' . __( '(also available on backorder)', 'woocommerce' ); - } - } else { - $availability = __( 'In stock', 'woocommerce' ); - } - break; - default : - $availability = sprintf( __( '%s in stock', 'woocommerce' ), $this->get_total_stock() ); - - if ( $this->backorders_allowed() && $this->backorders_require_notification() ) { - $availability .= ' ' . __( '(also available on backorder)', 'woocommerce' ); - } - break; - } - } else { - $availability = ''; - } - return apply_filters( 'woocommerce_get_availability_text', $availability, $this ); - } - - /** - * Get availability classname based on stock status. - * - * @return string - */ - protected function get_availability_class() { - if ( ! $this->is_in_stock() ) { - $class = 'out-of-stock'; - } elseif ( $this->managing_stock() && $this->is_on_backorder( 1 ) && $this->backorders_require_notification() ) { - $class = 'available-on-backorder'; - } else { - $class = 'in-stock'; - } - - return apply_filters( 'woocommerce_get_availability_class', $class, $this ); - } - /** - * Returns whether or not the product is on sale. - * - * @return bool - */ - public function is_on_sale() { - return apply_filters( 'woocommerce_product_is_on_sale', ( $this->get_sale_price() !== $this->get_regular_price() && $this->get_sale_price() === $this->get_price() ), $this ); - } @@ -1811,118 +1761,25 @@ class WC_Product extends WC_Abstract_Legacy_Product { return $display_price; } - /** - * Get the suffix to display after prices > 0. - * - * @param string $price to calculate, left blank to just use get_price() - * @param integer $qty passed on to get_price_including_tax() or get_price_excluding_tax() - * @return string - */ - public function get_price_suffix( $price = '', $qty = 1 ) { - - if ( '' === $price ) { - $price = $this->get_price(); - } - - $price_display_suffix = get_option( 'woocommerce_price_display_suffix' ); - $woocommerce_calc_taxes = get_option( 'woocommerce_calc_taxes', 'no' ); - - if ( $price_display_suffix && 'yes' === $woocommerce_calc_taxes ) { - - $price_display_suffix = ' ' . $price_display_suffix . ''; - - $find = array( - '{price_including_tax}', - '{price_excluding_tax}', - ); - - $replace = array( - wc_price( $this->get_price_including_tax( $qty, $price ) ), - wc_price( $this->get_price_excluding_tax( $qty, $price ) ), - ); - - $price_display_suffix = str_replace( $find, $replace, $price_display_suffix ); - } else { - $price_display_suffix = ''; - } - - return apply_filters( 'woocommerce_get_price_suffix', $price_display_suffix, $this ); - } - /** * Returns the price in html format. * - * @param string $price (default: '') * @return string */ - public function get_price_html( $price = '' ) { + public function get_price_html( $deprecated = '' ) { + if ( '' === $this->get_price() ) { + return apply_filters( 'woocommerce_empty_price_html', '', $this ); + } - $display_price = $this->get_display_price(); - $display_regular_price = $this->get_display_price( $this->get_regular_price() ); - - if ( $this->get_price() > 0 ) { - - if ( $this->is_on_sale() && $this->get_regular_price() ) { - - $price .= $this->get_price_html_from_to( $display_regular_price, $display_price ) . $this->get_price_suffix(); - - $price = apply_filters( 'woocommerce_sale_price_html', $price, $this ); - - } else { - - $price .= wc_price( $display_price ) . $this->get_price_suffix(); - - $price = apply_filters( 'woocommerce_price_html', $price, $this ); - - } - } elseif ( $this->get_price() === '' ) { - - $price = apply_filters( 'woocommerce_empty_price_html', '', $this ); - - } elseif ( $this->get_price() == 0 ) { - - if ( $this->is_on_sale() && $this->get_regular_price() ) { - - $price .= $this->get_price_html_from_to( $display_regular_price, __( 'Free!', 'woocommerce' ) ); - - $price = apply_filters( 'woocommerce_free_sale_price_html', $price, $this ); - - } else { - - $price = '' . __( 'Free!', 'woocommerce' ) . ''; - - $price = apply_filters( 'woocommerce_free_price_html', $price, $this ); - - } + if ( $this->is_on_sale() ) { + $price = wc_format_price_range( $product->get_display_price( $product->get_regular_price() ), $product->get_display_price() ) . wc_get_price_suffix( $this ); + } else { + $price = wc_price( $product->get_display_price() ) . wc_get_price_suffix( $this ); } return apply_filters( 'woocommerce_get_price_html', $price, $this ); } - /** - * Functions for getting parts of a price, in html, used by get_price_html. - * - * @return string - */ - public function get_price_html_from_text() { - $from = '' . _x( 'From:', 'min_price', 'woocommerce' ) . ' '; - - return apply_filters( 'woocommerce_get_price_html_from_text', $from, $this ); - } - - /** - * Functions for getting parts of a price, in html, used by get_price_html. - * - * @param string $from String or float to wrap with 'from' text - * @param mixed $to String or float to wrap with 'to' text - * @return string - */ - public function get_price_html_from_to( $from, $to ) { - $price = '' . ( ( is_numeric( $from ) ) ? wc_price( $from ) : $from ) . ' ' . ( ( is_numeric( $to ) ) ? wc_price( $to ) : $to ) . ''; - - return apply_filters( 'woocommerce_get_price_html_from_to', $price, $from, $to, $this ); - } - @@ -2066,53 +1923,9 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_product_dimensions', $dimensions, $this ); } - /** - * Lists a table of attributes for the product page. - */ - public function list_attributes() { - wc_get_template( 'single-product/product-attributes.php', array( - 'product' => $this, - ) ); - } - /** - * Gets the main product image ID. - * - * @return int - */ - public function get_image_id() { - if ( has_post_thumbnail( $this->get_id() ) ) { - $image_id = get_post_thumbnail_id( $this->get_id() ); - } elseif ( ( $parent_id = wp_get_post_parent_id( $this->get_id() ) ) && has_post_thumbnail( $parent_id ) ) { - $image_id = get_post_thumbnail_id( $parent_id ); - } else { - $image_id = 0; - } - return $image_id; - } - - /** - * Returns the main product image. - * - * @param string $size (default: 'shop_thumbnail') - * @param array $attr - * @param bool True to return $placeholder if no image is found, or false to return an empty string. - * @return string - */ - public function get_image( $size = 'shop_thumbnail', $attr = array(), $placeholder = true ) { - if ( has_post_thumbnail( $this->get_id() ) ) { - $image = get_the_post_thumbnail( $this->get_id(), $size, $attr ); - } elseif ( ( $parent_id = wp_get_post_parent_id( $this->get_id() ) ) && has_post_thumbnail( $parent_id ) ) { - $image = get_the_post_thumbnail( $parent_id, $size, $attr ); - } elseif ( $placeholder ) { - $image = wc_placeholder_img( $size ); - } else { - $image = ''; - } - return str_replace( array( 'https://', 'http://' ), '//', $image ); - } /** * Get product name with SKU or ID. Used within admin. @@ -2128,18 +1941,4 @@ class WC_Product extends WC_Abstract_Legacy_Product { return sprintf( '%s – %s', $identifier, $this->get_title() ); } - - /** - * Save taxonomy terms. - * - * @since 2.7.0 - * @param array $terms_id Terms ID. - * @param string $taxonomy Taxonomy. - * @return array|WP_Error - */ - protected function save_taxonomy_terms( $terms_id, $taxonomy = 'cat' ) { - $terms_id = array_unique( array_map( 'intval', $terms_id ) ); - - return wp_set_object_terms( $this->get_id(), $terms_id, 'product_' . $taxonomy ); - } } diff --git a/includes/class-wc-product-variable.php b/includes/class-wc-product-variable.php index 9d58aedd266..ec5e23d6f16 100644 --- a/includes/class-wc-product-variable.php +++ b/includes/class-wc-product-variable.php @@ -590,10 +590,6 @@ class WC_Product_Variable extends WC_Product { $image = $image_link = $image_title = $image_alt = $image_srcset = $image_sizes = $image_caption = ''; } - $availability = $variation->get_availability(); - $availability_html = empty( $availability['availability'] ) ? '' : '

' . wp_kses_post( $availability['availability'] ) . '

'; - $availability_html = apply_filters( 'woocommerce_stock_html', $availability_html, $availability['availability'], $variation ); - return apply_filters( 'woocommerce_available_variation', array( 'variation_id' => $variation->variation_id, 'variation_is_visible' => $variation->variation_is_visible(), @@ -610,7 +606,7 @@ class WC_Product_Variable extends WC_Product { 'image_srcset' => $image_srcset ? $image_srcset : '', 'image_sizes' => $image_sizes ? $image_sizes : '', 'price_html' => apply_filters( 'woocommerce_show_variation_price', $variation->get_price() === "" || $this->get_variation_price( 'min' ) !== $this->get_variation_price( 'max' ), $this, $variation ) ? '' . $variation->get_price_html() . '' : '', - 'availability_html' => $availability_html, + 'availability_html' => wc_get_product_stock_html( $variation ), 'sku' => $variation->get_sku(), 'weight' => $variation->get_weight() ? $variation->get_weight() . ' ' . esc_attr( get_option( 'woocommerce_weight_unit' ) ) : '', 'dimensions' => $variation->get_dimensions(), diff --git a/includes/wc-formatting-functions.php b/includes/wc-formatting-functions.php index 969383bed9b..bae793712cc 100644 --- a/includes/wc-formatting-functions.php +++ b/includes/wc-formatting-functions.php @@ -873,3 +873,44 @@ if ( ! function_exists( 'wc_make_numeric_postcode' ) ) { return $numeric_postcode; } } + +/** + * Format the stock amount ready for display based on settings. + * @since 2.7.0 + * @param int $stock_amount + * @param boolean $show_backorder_notification + * @return string + */ +function wc_format_stock_for_display( $stock_amount, $show_backorder_notification = false ) { + $display = __( 'In stock', 'woocommerce' ); + + switch ( get_option( 'woocommerce_stock_format' ) ) { + case 'low_amount' : + if ( $stock_amount <= get_option( 'woocommerce_notify_low_stock_amount' ) ) { + $display = sprintf( __( 'Only %s left in stock', 'woocommerce' ), $stock_amount ); + } + break; + case '' : + $display = sprintf( __( '%s in stock', 'woocommerce' ), $stock_amount ); + break; + } + + if ( $show_backorder_notification ) { + $display .= ' ' . __( '(can be backordered)', 'woocommerce' ); + } + + return $display; +} + +/** + * Format a price range for display. + * @since 2.7.0 + * @param string $from + * @param string $to + * @return string + */ +function wc_format_price_range( $from, $to ) { + $price = '' . ( ( is_numeric( $from ) ) ? wc_price( $from ) : $from ) . ' ' . ( ( is_numeric( $to ) ) ? wc_price( $to ) : $to ) . ''; + + return apply_filters( 'woocommerce_format_price_range', $price, $from, $to ); +} diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index a3dbed84882..e22da3a06b3 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -2453,3 +2453,61 @@ if ( ! function_exists( 'wc_display_item_downloads' ) ) { } } } + +/** + * Outputs a list of product attributes. + * @since 2.7.0 + * @param WC_Product $product + */ +function wc_display_product_attributes( $product ) { + wc_get_template( 'single-product/product-attributes.php', array( + 'product' => $product, + ) ); +} + +/** + * Get HTML to show product stock. + * @since 2.7.0 + * @param WC_Product $product + * @return string + */ +function wc_get_product_stock_html( $product ) { + ob_start(); + + wc_get_template( 'single-product/stock.php', array( + 'product' => $product, + ) ); + + return apply_filters( 'woocommerce_get_product_stock_html', ob_get_clean(), $product ); // @todo map old woocommerce_stock_html filter to this +} + +/** + * Get the price suffix for a product if needed. + * @since 2.7.0 + * @param WC_Product $product + * @param string $price + * @param integer $qty + * @return string + */ +function wc_get_price_suffix( $product, $price = '', $qty = 1 ) { + if ( ( $price_display_suffix = get_option( 'woocommerce_price_display_suffix' ) ) && wc_tax_enabled() ) { + $price = '' === $price ? $product->get_price() : $price; + $price_display_suffix = ' ' . wp_kses_post( $price_display_suffix ) . ''; + + $find = array( + '{price_including_tax}', + '{price_excluding_tax}', + ); + + $replace = array( + wc_price( $product->get_price_including_tax( $qty, $price ) ), + wc_price( $product->get_price_excluding_tax( $qty, $price ) ), + ); + + $price_display_suffix = str_replace( $find, $replace, $price_display_suffix ); + } else { + $price_display_suffix = ''; + } + + return apply_filters( 'woocommerce_get_price_suffix', $price_display_suffix, $product ); +} diff --git a/templates/single-product/add-to-cart/grouped.php b/templates/single-product/add-to-cart/grouped.php index 7c919ea4215..0e8d988a70b 100644 --- a/templates/single-product/add-to-cart/grouped.php +++ b/templates/single-product/add-to-cart/grouped.php @@ -70,11 +70,7 @@ do_action( 'woocommerce_before_add_to_cart_form' ); ?> get_price_html(); - - if ( $availability = $product->get_availability() ) { - $availability_html = empty( $availability['availability'] ) ? '' : '

' . esc_html( $availability['availability'] ) . '

'; - echo apply_filters( 'woocommerce_stock_html', $availability_html, $availability['availability'], $product ); - } + echo wc_get_product_stock_html( $product ); ?> diff --git a/templates/single-product/add-to-cart/simple.php b/templates/single-product/add-to-cart/simple.php index 7d03628e084..0015392fa6a 100644 --- a/templates/single-product/add-to-cart/simple.php +++ b/templates/single-product/add-to-cart/simple.php @@ -26,17 +26,9 @@ if ( ! $product->is_purchasable() ) { return; } -?> +echo wc_get_product_stock_html( $product ); -get_availability(); - $availability_html = empty( $availability['availability'] ) ? '' : '

' . esc_html( $availability['availability'] ) . '

'; - - echo apply_filters( 'woocommerce_stock_html', $availability_html, $availability['availability'], $product ); -?> - -is_in_stock() ) : ?> +if ( $product->is_in_stock() ) : ?> diff --git a/templates/single-product/stock.php b/templates/single-product/stock.php new file mode 100644 index 00000000000..1fb180c2ab2 --- /dev/null +++ b/templates/single-product/stock.php @@ -0,0 +1,35 @@ +is_in_stock() ) : ?> + +

+ +managing_stock() && $product->is_on_backorder( 1 ) ) : ?> + +

+ +managing_stock() ) : ?> + +

get_total_stock(), $product->backorders_allowed() && $product->backorders_require_notification() ) ); ?>

+ + diff --git a/templates/single-product/tabs/additional-information.php b/templates/single-product/tabs/additional-information.php index 218d8d809c4..cd4a590e6bf 100644 --- a/templates/single-product/tabs/additional-information.php +++ b/templates/single-product/tabs/additional-information.php @@ -13,7 +13,7 @@ * @see https://docs.woocommerce.com/document/template-structure/ * @author WooThemes * @package WooCommerce/Templates - * @version 2.0.0 + * @version 2.7.0 */ if ( ! defined( 'ABSPATH' ) ) { @@ -30,4 +30,4 @@ $heading = esc_html( apply_filters( 'woocommerce_product_additional_information_

-list_attributes(); ?> + From b6deb23e5c262986ba1fb195d665510ade933a05 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 20 Oct 2016 15:06:44 +0100 Subject: [PATCH 033/163] Tidy/add todos --- includes/abstracts/abstract-wc-product.php | 137 +++++++++------------ 1 file changed, 60 insertions(+), 77 deletions(-) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index d724decd0a5..caca876b9a8 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -1307,6 +1307,20 @@ class WC_Product extends WC_Abstract_Legacy_Product { |-------------------------------------------------------------------------- */ + /** + * Get product name with SKU or ID. Used within admin. + * + * @return string Formatted product name + */ + public function get_formatted_name() { + if ( $this->get_sku() ) { + $identifier = $this->get_sku(); + } else { + $identifier = '#' . $this->get_id(); + } + return sprintf( '%s – %s', $identifier, $this->get_name() ); + } + /** * Get the add to url used mainly in loops. * @@ -1371,29 +1385,12 @@ class WC_Product extends WC_Abstract_Legacy_Product { return str_replace( array( 'https://', 'http://' ), '//', $image ); } - - - - - - - - - - - - - - - /* |-------------------------------------------------------------------------- - | Other Actions + | @todo |-------------------------------------------------------------------------- */ - - /** * Returns the gallery attachment ids. * @@ -1403,6 +1400,30 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_product_gallery_attachment_ids', array_filter( array_filter( (array) explode( ',', $this->product_image_gallery ) ), 'wp_attachment_is_image' ), $this ); } + /** + * Returns the children. + * + * @return array + */ + public function get_children() { + return array(); + } + + /** + * Returns whether or not the product has any child product. + * + * @return bool + */ + public function has_child() { + return 0 < count( $this->get_children() ); + } + + /* + |-------------------------------------------------------------------------- + | @todo stock functions + |-------------------------------------------------------------------------- + */ + /** * Get total stock - This is the stock of parent and children combined. * @@ -1508,9 +1529,11 @@ class WC_Product extends WC_Abstract_Legacy_Product { return $this->set_stock( $amount, 'add' ); } - - - + /* + |-------------------------------------------------------------------------- + | @todo download functions + |-------------------------------------------------------------------------- + */ /** * Check if downloadable product has a file attached. @@ -1600,38 +1623,11 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_product_file_download_path', $file_path, $this, $download_id ); } - - - - - - - /** - * Returns the children. - * - * @return array - */ - public function get_children() { - return array(); - } - - /** - * Returns whether or not the product has any child product. - * - * @return bool - */ - public function has_child() { - return 0 < count( $this->get_children() ); - } - - - - - - - - - + /* + |-------------------------------------------------------------------------- + | @todo price functions + |-------------------------------------------------------------------------- + */ /** * Set a products price dynamically. @@ -1780,9 +1776,11 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_get_price_html', $price, $this ); } - - - + /* + |-------------------------------------------------------------------------- + | @todo taxonomy functions + |-------------------------------------------------------------------------- + */ /** * Returns the product shipping class. @@ -1875,6 +1873,12 @@ class WC_Product extends WC_Abstract_Legacy_Product { return false; } + /* + |-------------------------------------------------------------------------- + | @todo misc + |-------------------------------------------------------------------------- + */ + /** * Returns whether or not we are showing dimensions on the product page. * @@ -1884,8 +1888,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'wc_product_enable_dimensions_display', true ) && ( $this->has_dimensions() || $this->has_weight() ); } - - /** * Does a child have dimensions set? * @@ -1922,23 +1924,4 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_product_dimensions', $dimensions, $this ); } - - - - - - /** - * Get product name with SKU or ID. Used within admin. - * - * @return string Formatted product name - */ - public function get_formatted_name() { - if ( $this->get_sku() ) { - $identifier = $this->get_sku(); - } else { - $identifier = '#' . $this->get_id(); - } - - return sprintf( '%s – %s', $identifier, $this->get_title() ); - } } From 9a8681a713fe8c2177f8de74dde4706083e2537f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 20 Oct 2016 15:07:50 +0100 Subject: [PATCH 034/163] Rename method --- includes/class-wc-product-variable.php | 2 +- includes/wc-template-functions.php | 4 ++-- templates/single-product/add-to-cart/grouped.php | 2 +- templates/single-product/add-to-cart/simple.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/includes/class-wc-product-variable.php b/includes/class-wc-product-variable.php index ec5e23d6f16..44dfa72a5ed 100644 --- a/includes/class-wc-product-variable.php +++ b/includes/class-wc-product-variable.php @@ -606,7 +606,7 @@ class WC_Product_Variable extends WC_Product { 'image_srcset' => $image_srcset ? $image_srcset : '', 'image_sizes' => $image_sizes ? $image_sizes : '', 'price_html' => apply_filters( 'woocommerce_show_variation_price', $variation->get_price() === "" || $this->get_variation_price( 'min' ) !== $this->get_variation_price( 'max' ), $this, $variation ) ? '' . $variation->get_price_html() . '' : '', - 'availability_html' => wc_get_product_stock_html( $variation ), + 'availability_html' => wc_get_stock_html( $variation ), 'sku' => $variation->get_sku(), 'weight' => $variation->get_weight() ? $variation->get_weight() . ' ' . esc_attr( get_option( 'woocommerce_weight_unit' ) ) : '', 'dimensions' => $variation->get_dimensions(), diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index e22da3a06b3..1908030238f 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -2471,14 +2471,14 @@ function wc_display_product_attributes( $product ) { * @param WC_Product $product * @return string */ -function wc_get_product_stock_html( $product ) { +function wc_get_stock_html( $product ) { ob_start(); wc_get_template( 'single-product/stock.php', array( 'product' => $product, ) ); - return apply_filters( 'woocommerce_get_product_stock_html', ob_get_clean(), $product ); // @todo map old woocommerce_stock_html filter to this + return apply_filters( 'woocommerce_get_stock_html', ob_get_clean(), $product ); // @todo map old woocommerce_stock_html filter to this } /** diff --git a/templates/single-product/add-to-cart/grouped.php b/templates/single-product/add-to-cart/grouped.php index 0e8d988a70b..c4eae1ef513 100644 --- a/templates/single-product/add-to-cart/grouped.php +++ b/templates/single-product/add-to-cart/grouped.php @@ -70,7 +70,7 @@ do_action( 'woocommerce_before_add_to_cart_form' ); ?> get_price_html(); - echo wc_get_product_stock_html( $product ); + echo wc_get_stock_html( $product ); ?> diff --git a/templates/single-product/add-to-cart/simple.php b/templates/single-product/add-to-cart/simple.php index 0015392fa6a..592bbeeff11 100644 --- a/templates/single-product/add-to-cart/simple.php +++ b/templates/single-product/add-to-cart/simple.php @@ -26,7 +26,7 @@ if ( ! $product->is_purchasable() ) { return; } -echo wc_get_product_stock_html( $product ); +echo wc_get_stock_html( $product ); if ( $product->is_in_stock() ) : ?> From b5b7ea48f31c8b33a6054e26d5e488bebc271397 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 20 Oct 2016 15:08:49 +0100 Subject: [PATCH 035/163] Put back review functions (still todo) --- includes/abstracts/abstract-wc-product.php | 141 ++++++++++++++++++++- 1 file changed, 140 insertions(+), 1 deletion(-) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index caca876b9a8..9a9ce74fe18 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -1417,7 +1417,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { public function has_child() { return 0 < count( $this->get_children() ); } - + /* |-------------------------------------------------------------------------- | @todo stock functions @@ -1924,4 +1924,143 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_product_dimensions', $dimensions, $this ); } + + /** + * Get the average rating of product. This is calculated once and stored in postmeta. + * @return string + */ + public function get_average_rating() { + // No meta data? Do the calculation + if ( ! metadata_exists( 'post', $this->get_id(), '_wc_average_rating' ) ) { + $this->sync_average_rating( $this->get_id() ); + } + + return (string) floatval( get_post_meta( $this->get_id(), '_wc_average_rating', true ) ); + } + + /** + * Get the total amount (COUNT) of ratings. + * @param int $value Optional. Rating value to get the count for. By default returns the count of all rating values. + * @return int + */ + public function get_rating_count( $value = null ) { + // No meta data? Do the calculation + if ( ! metadata_exists( 'post', $this->get_id(), '_wc_rating_count' ) ) { + $this->sync_rating_count( $this->get_id() ); + } + + $counts = get_post_meta( $this->get_id(), '_wc_rating_count', true ); + + if ( is_null( $value ) ) { + return array_sum( $counts ); + } else { + return isset( $counts[ $value ] ) ? $counts[ $value ] : 0; + } + } + + /** + * Sync product rating. Can be called statically. + * @param int $post_id + */ + public static function sync_average_rating( $post_id ) { + if ( ! metadata_exists( 'post', $post_id, '_wc_rating_count' ) ) { + self::sync_rating_count( $post_id ); + } + + $count = array_sum( (array) get_post_meta( $post_id, '_wc_rating_count', true ) ); + + if ( $count ) { + global $wpdb; + + $ratings = $wpdb->get_var( $wpdb->prepare(" + SELECT SUM(meta_value) FROM $wpdb->commentmeta + LEFT JOIN $wpdb->comments ON $wpdb->commentmeta.comment_id = $wpdb->comments.comment_ID + WHERE meta_key = 'rating' + AND comment_post_ID = %d + AND comment_approved = '1' + AND meta_value > 0 + ", $post_id ) ); + $average = number_format( $ratings / $count, 2, '.', '' ); + } else { + $average = 0; + } + update_post_meta( $post_id, '_wc_average_rating', $average ); + } + + /** + * Sync product rating count. Can be called statically. + * @param int $post_id + */ + public static function sync_rating_count( $post_id ) { + global $wpdb; + + $counts = array(); + $raw_counts = $wpdb->get_results( $wpdb->prepare( " + SELECT meta_value, COUNT( * ) as meta_value_count FROM $wpdb->commentmeta + LEFT JOIN $wpdb->comments ON $wpdb->commentmeta.comment_id = $wpdb->comments.comment_ID + WHERE meta_key = 'rating' + AND comment_post_ID = %d + AND comment_approved = '1' + AND meta_value > 0 + GROUP BY meta_value + ", $post_id ) ); + + foreach ( $raw_counts as $count ) { + $counts[ $count->meta_value ] = $count->meta_value_count; + } + + update_post_meta( $post_id, '_wc_rating_count', $counts ); + } + + /** + * Returns the product rating in html format. + * + * @param string $rating (default: '') + * + * @return string + */ + public function get_rating_html( $rating = null ) { + $rating_html = ''; + + if ( ! is_numeric( $rating ) ) { + $rating = $this->get_average_rating(); + } + + if ( $rating > 0 ) { + + $rating_html = '
'; + + $rating_html .= '' . $rating . ' ' . __( 'out of 5', 'woocommerce' ) . ''; + + $rating_html .= '
'; + } + + return apply_filters( 'woocommerce_product_get_rating_html', $rating_html, $rating ); + } + + /** + * Get the total amount (COUNT) of reviews. + * + * @since 2.3.2 + * @return int The total numver of product reviews + */ + public function get_review_count() { + global $wpdb; + + // No meta date? Do the calculation + if ( ! metadata_exists( 'post', $this->get_id(), '_wc_review_count' ) ) { + $count = $wpdb->get_var( $wpdb->prepare(" + SELECT COUNT(*) FROM $wpdb->comments + WHERE comment_parent = 0 + AND comment_post_ID = %d + AND comment_approved = '1' + ", $this->get_id() ) ); + + update_post_meta( $this->get_id(), '_wc_review_count', $count ); + } else { + $count = get_post_meta( $this->get_id(), '_wc_review_count', true ); + } + + return apply_filters( 'woocommerce_product_review_count', $count, $this ); + } } From 096d2e4edf40931f9b869fd47643eba617dda704 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 20 Oct 2016 15:13:51 +0100 Subject: [PATCH 036/163] missing $this --- includes/abstracts/abstract-wc-product.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 9a9ce74fe18..f78ff4d89d0 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -1768,9 +1768,9 @@ class WC_Product extends WC_Abstract_Legacy_Product { } if ( $this->is_on_sale() ) { - $price = wc_format_price_range( $product->get_display_price( $product->get_regular_price() ), $product->get_display_price() ) . wc_get_price_suffix( $this ); + $price = wc_format_price_range( $this->get_display_price( $this->get_regular_price() ), $this->get_display_price() ) . wc_get_price_suffix( $this ); } else { - $price = wc_price( $product->get_display_price() ) . wc_get_price_suffix( $this ); + $price = wc_price( $this->get_display_price() ) . wc_get_price_suffix( $this ); } return apply_filters( 'woocommerce_get_price_html', $price, $this ); From b13ef434986ef2802d04bf56c25ef4c037c87593 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 20 Oct 2016 16:32:16 +0100 Subject: [PATCH 037/163] get_price_including_tax/excluding_tax functions --- .../abstracts/abstract-wc-legacy-product.php | 27 ++++++ includes/abstracts/abstract-wc-product.php | 85 +------------------ includes/wc-product-functions.php | 76 +++++++++++++++++ 3 files changed, 107 insertions(+), 81 deletions(-) diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index 828bd44ab51..e0f8676d8a6 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -106,6 +106,33 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { wc_display_product_attributes( $this ); } + /** + * Returns the price (including tax). Uses customer tax rates. Can work for a specific $qty for more accurate taxes. + * + * @deprecated 2.7.0 Use wc_get_price_including_tax instead. + * @param int $qty + * @param string $price to calculate, left blank to just use get_price() + * @return string + */ + public function get_price_including_tax( $qty = 1, $price = '' ) { + _deprecated_function( 'WC_Product::get_price_including_tax', '2.7', 'wc_get_price_including_tax' ); + return wc_get_price_including_tax( $this, array( 'qty' => $qty, 'price' => $price ) ); + } + + /** + * Returns the price (excluding tax) - ignores tax_class filters since the price may *include* tax and thus needs subtracting. + * Uses store base tax rates. Can work for a specific $qty for more accurate taxes. + * + * @deprecated 2.7.0 Use wc_get_price_excluding_tax instead. + * @param int $qty + * @param string $price to calculate, left blank to just use get_price() + * @return string + */ + public function get_price_excluding_tax( $qty = 1, $price = '' ) { + _deprecated_function( 'WC_Product::get_price_excluding_tax', '2.7', 'wc_get_price_excluding_tax' ); + return wc_get_price_excluding_tax( $this, array( 'qty' => $qty, 'price' => $price ) ); + } + /** * Returns the availability of the product. * diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index f78ff4d89d0..8e6678cb222 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -1653,89 +1653,12 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return string price */ public function get_price() { - return apply_filters( 'woocommerce_get_price', $this->price, $this ); - } - - /** - * Returns the price (including tax). Uses customer tax rates. Can work for a specific $qty for more accurate taxes. - * - * @param int $qty - * @param string $price to calculate, left blank to just use get_price() - * @return string - */ - public function get_price_including_tax( $qty = 1, $price = '' ) { - - if ( '' === $price ) { - $price = $this->get_price(); - } - - if ( $this->is_taxable() ) { - - if ( get_option( 'woocommerce_prices_include_tax' ) === 'no' ) { - - $tax_rates = WC_Tax::get_rates( $this->get_tax_class() ); - $taxes = WC_Tax::calc_tax( $price * $qty, $tax_rates, false ); - $tax_amount = WC_Tax::get_tax_total( $taxes ); - $price = round( $price * $qty + $tax_amount, wc_get_price_decimals() ); - - } else { - - $tax_rates = WC_Tax::get_rates( $this->get_tax_class() ); - $base_tax_rates = WC_Tax::get_base_tax_rates( $this->tax_class ); - - if ( ! empty( WC()->customer ) && WC()->customer->get_is_vat_exempt() ) { - - $base_taxes = WC_Tax::calc_tax( $price * $qty, $base_tax_rates, true ); - $base_tax_amount = array_sum( $base_taxes ); - $price = round( $price * $qty - $base_tax_amount, wc_get_price_decimals() ); - - /** - * The woocommerce_adjust_non_base_location_prices filter can stop base taxes being taken off when dealing with out of base locations. - * e.g. If a product costs 10 including tax, all users will pay 10 regardless of location and taxes. - * This feature is experimental @since 2.4.7 and may change in the future. Use at your risk. - */ - } elseif ( $tax_rates !== $base_tax_rates && apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ) { - - $base_taxes = WC_Tax::calc_tax( $price * $qty, $base_tax_rates, true ); - $modded_taxes = WC_Tax::calc_tax( ( $price * $qty ) - array_sum( $base_taxes ), $tax_rates, false ); - $price = round( ( $price * $qty ) - array_sum( $base_taxes ) + array_sum( $modded_taxes ), wc_get_price_decimals() ); - - } else { - - $price = $price * $qty; - - } - } + if ( $this->is_on_sale() ) { + $price = $this->get_sale_price(); } else { - $price = $price * $qty; + $price = $this->get_regular_price(); } - - return apply_filters( 'woocommerce_get_price_including_tax', $price, $qty, $this ); - } - - /** - * Returns the price (excluding tax) - ignores tax_class filters since the price may *include* tax and thus needs subtracting. - * Uses store base tax rates. Can work for a specific $qty for more accurate taxes. - * - * @param int $qty - * @param string $price to calculate, left blank to just use get_price() - * @return string - */ - public function get_price_excluding_tax( $qty = 1, $price = '' ) { - - if ( '' === $price ) { - $price = $this->get_price(); - } - - if ( $this->is_taxable() && 'yes' === get_option( 'woocommerce_prices_include_tax' ) ) { - $tax_rates = WC_Tax::get_base_tax_rates( $this->tax_class ); - $taxes = WC_Tax::calc_tax( $price * $qty, $tax_rates, true ); - $price = WC_Tax::round( $price * $qty - array_sum( $taxes ) ); - } else { - $price = $price * $qty; - } - - return apply_filters( 'woocommerce_get_price_excluding_tax', $price, $qty, $this ); + return apply_filters( 'woocommerce_get_price', $price, $this ); } /** diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 8d4f7cde9dc..7a572a0e824 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -864,3 +864,79 @@ function wc_get_related_products_query( $cats_array, $tags_array, $exclude_ids, return $query; } + +/** + * For a given product, and optionally price/qty, work out the price with tax included, based on store settings. + * @since 2.7.0 + * @param WC_Product $product + * @param array $args + * @return float + */ +function wc_get_price_including_tax( $product, $args ) { + $args = wp_parse_args( $args, array( + 'qty' => 1, + 'price' => $product->get_price(), + ) ); + + $price = $args['price']; + $qty = $args['qty']; + + if ( ! $product->is_taxable() ) { + $price = $price * $qty; + } elseif ( wc_prices_include_tax() ) { + $tax_rates = WC_Tax::get_rates( $product->get_tax_class() ); + $taxes = WC_Tax::calc_tax( $price * $qty, $tax_rates, false ); + $tax_amount = WC_Tax::get_tax_total( $taxes ); + $price = round( $price * $qty + $tax_amount, wc_get_price_decimals() ); + } else { + $tax_rates = WC_Tax::get_rates( $product->get_tax_class() ); + $base_tax_rates = WC_Tax::get_base_tax_rates( $product->tax_class ); + + if ( ! empty( WC()->customer ) && WC()->customer->get_is_vat_exempt() ) { + $base_taxes = WC_Tax::calc_tax( $price * $qty, $base_tax_rates, true ); + $base_tax_amount = array_sum( $base_taxes ); + $price = round( $price * $qty - $base_tax_amount, wc_get_price_decimals() ); + + /** + * The woocommerce_adjust_non_base_location_prices filter can stop base taxes being taken off when dealing with out of base locations. + * e.g. If a product costs 10 including tax, all users will pay 10 regardless of location and taxes. + * This feature is experimental @since 2.4.7 and may change in the future. Use at your risk. + */ + } elseif ( $tax_rates !== $base_tax_rates && apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ) { + $base_taxes = WC_Tax::calc_tax( $price * $qty, $base_tax_rates, true ); + $modded_taxes = WC_Tax::calc_tax( ( $price * $qty ) - array_sum( $base_taxes ), $tax_rates, false ); + $price = round( ( $price * $qty ) - array_sum( $base_taxes ) + array_sum( $modded_taxes ), wc_get_price_decimals() ); + + } else { + $price = $price * $qty; + } + } + return apply_filters( 'woocommerce_get_price_including_tax', $price, $qty, $product ); +} + +/** + * For a given product, and optionally price/qty, work out the price with tax excluded, based on store settings. + * @since 2.7.0 + * @param WC_Product $product + * @param array $args + * @return float + */ +function wc_get_price_excluding_tax( $product, $args ) { + $args = wp_parse_args( $args, array( + 'qty' => 1, + 'price' => $product->get_price(), + ) ); + + $price = $args['price']; + $qty = $args['qty']; + + if ( $product->is_taxable() && wc_prices_include_tax() ) { + $tax_rates = WC_Tax::get_base_tax_rates( $product->tax_class ); + $taxes = WC_Tax::calc_tax( $price * $qty, $tax_rates, true ); + $price = WC_Tax::round( $price * $qty - array_sum( $taxes ) ); + } else { + $price = $price * $qty; + } + + return apply_filters( 'woocommerce_get_price_excluding_tax', $price, $qty, $product ); +} From 52aebdbc0d9c418942ca9418ab3b174bd4dcd60b Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 20 Oct 2016 16:40:17 +0100 Subject: [PATCH 038/163] wc_get_price_to_display --- .../abstracts/abstract-wc-legacy-product.php | 13 +++++++++++++ includes/abstracts/abstract-wc-product.php | 19 ------------------- includes/wc-product-functions.php | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index e0f8676d8a6..cf5f368db8a 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -119,6 +119,19 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { return wc_get_price_including_tax( $this, array( 'qty' => $qty, 'price' => $price ) ); } + /** + * Returns the price including or excluding tax, based on the 'woocommerce_tax_display_shop' setting. + * + * @deprecated 2.7.0 Use wc_get_price_to_display instead. + * @param string $price to calculate, left blank to just use get_price() + * @param integer $qty passed on to get_price_including_tax() or get_price_excluding_tax() + * @return string + */ + public function get_display_price( $price = '', $qty = 1 ) { + _deprecated_function( 'WC_Product::get_display_price', '2.7', 'wc_get_price_to_display' ); + return wc_get_price_to_display( $this, array( 'qty' => $qty, 'price' => $price ) ); + } + /** * Returns the price (excluding tax) - ignores tax_class filters since the price may *include* tax and thus needs subtracting. * Uses store base tax rates. Can work for a specific $qty for more accurate taxes. diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 8e6678cb222..599e4a4dbf7 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -1661,25 +1661,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_get_price', $price, $this ); } - /** - * Returns the price including or excluding tax, based on the 'woocommerce_tax_display_shop' setting. - * - * @param string $price to calculate, left blank to just use get_price() - * @param integer $qty passed on to get_price_including_tax() or get_price_excluding_tax() - * @return string - */ - public function get_display_price( $price = '', $qty = 1 ) { - - if ( '' === $price ) { - $price = $this->get_price(); - } - - $tax_display_mode = get_option( 'woocommerce_tax_display_shop' ); - $display_price = ( 'incl' === $tax_display_mode ) ? $this->get_price_including_tax( $qty, $price ) : $this->get_price_excluding_tax( $qty, $price ); - - return $display_price; - } - /** * Returns the price in html format. * diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 7a572a0e824..dc63f504390 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -940,3 +940,22 @@ function wc_get_price_excluding_tax( $product, $args ) { return apply_filters( 'woocommerce_get_price_excluding_tax', $price, $qty, $product ); } + +/** + * Returns the price including or excluding tax, based on the 'woocommerce_tax_display_shop' setting. + * @since 2.7.0 + * @param WC_Product $product + * @param array $args + * @return float + */ +public function wc_get_price_to_display( $product, $args ) { + $args = wp_parse_args( $args, array( + 'qty' => 1, + 'price' => $product->get_price(), + ) ); + + $price = $args['price']; + $qty = $args['qty']; + + return 'incl' === get_option( 'woocommerce_tax_display_shop' ) ) ? wc_get_price_including_tax( $product, array( 'qty' => $qty, 'price' => $price ) ) : wc_get_price_excluding_tax( $product, array( 'qty' => $qty, 'price' => $price ); +} From 49b40ae4bc294e293bcc69e31be9c6f6901c588c Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 20 Oct 2016 17:15:03 +0100 Subject: [PATCH 039/163] Price handling --- .../abstracts/abstract-wc-legacy-product.php | 11 ++ includes/abstracts/abstract-wc-product.php | 125 +++++++++--------- includes/wc-product-functions.php | 8 +- includes/wc-template-functions.php | 5 +- 4 files changed, 82 insertions(+), 67 deletions(-) diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index cf5f368db8a..7d77a02835b 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -146,6 +146,17 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { return wc_get_price_excluding_tax( $this, array( 'qty' => $qty, 'price' => $price ) ); } + /** + * Adjust a products price dynamically. + * + * @deprecated 2.7.0 + * @param mixed $price + */ + public function adjust_price( $price ) { + _deprecated_function( 'WC_Product::adjust_price', '2.7', 'WC_Product::set_price / WC_Product::get_price' ); + $this->data['price'] = $this->data['price'] + $price; + } + /** * Returns the availability of the product. * diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 599e4a4dbf7..f2aed20d3e6 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -37,6 +37,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'description' => '', 'short_description' => '', 'sku' => '', + 'price' => '', // @todo save and set this 'regular_price' => '', 'sale_price' => '', 'date_on_sale_from' => '', @@ -61,8 +62,8 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'attributes' => array(), 'default_attributes' => array(), 'menu_order' => 0, - 'virtual' => false, // @todo - 'downloadable' => false, // @todo + 'virtual' => false, // @todo + 'downloadable' => false, // @todo ); /** @@ -221,6 +222,15 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_get_sku', $this->data['sku'], $this ); } + /** + * Returns the product's active price. + * + * @return string price + */ + public function get_price() { + return apply_filters( 'woocommerce_get_price', $this->data['price'], $this ); + } + /** * Returns the product's regular price. * @@ -624,6 +634,15 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->data['sku'] = $sku; } + /** + * Set the product's active price. + * + * @param string $price Price. + */ + public function set_price( $price ) { + $this->data['price'] = wc_format_decimal( $price ); + } + /** * Set the product's regular price. * @@ -975,6 +994,11 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'default_attributes' => get_post_meta( $id, '_default_attributes', true ), 'menu_order' => $post_object->menu_order, ) ); + if ( $this->is_on_sale() ) { + $this->set_price( $this->get_sale_price() ); + } else { + $this->set_price( $this->get_regular_price() ); + } $this->read_meta_data(); } @@ -1085,6 +1109,12 @@ class WC_Product extends WC_Abstract_Legacy_Product { update_post_meta( $id, '_purchase_note', $this->get_purchase_note() ); update_post_meta( $id, '_attributes', $this->get_attributes() ); update_post_meta( $id, '_default_attributes', $this->get_default_attributes() ); + + if ( $this->is_on_sale() ) { + update_post_meta( $id, '_price', $this->get_sale_price() ); + } else { + update_post_meta( $id, '_price', $this->get_regular_price() ); + } } /* @@ -1197,7 +1227,20 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return bool */ public function is_on_sale() { - return apply_filters( 'woocommerce_product_is_on_sale', ( $this->get_sale_price() !== $this->get_regular_price() && $this->get_sale_price() === $this->get_price() ), $this ); + if ( '' !== $this->get_sale_price() && $this->get_regular_price() > $this->get_sale_price() ) { + $onsale = true; + + if ( '' !== $this->get_date_on_sale_from() && $this->get_date_on_sale_from() > strtotime( 'NOW', current_time( 'timestamp' ) ) ) { + $onsale = false; + } + + if ( '' !== $this->get_date_on_sale_to() && $this->get_date_on_sale_to() < strtotime( 'NOW', current_time( 'timestamp' ) ) ) { + $onsale = false; + } + } else { + $onsale = false; + } + return apply_filters( 'woocommerce_product_is_on_sale', $onsale, $this ); } /** @@ -1307,6 +1350,25 @@ class WC_Product extends WC_Abstract_Legacy_Product { |-------------------------------------------------------------------------- */ + /** + * Returns the price in html format. + * @todo Should this be moved out of the classes? + * @return string + */ + public function get_price_html( $deprecated = '' ) { + if ( '' === $this->get_price() ) { + return apply_filters( 'woocommerce_empty_price_html', '', $this ); + } + + if ( $this->is_on_sale() ) { + $price = wc_format_price_range( wc_get_price_to_display( $this, array( 'price' => $this->get_regular_price() ) ), wc_get_price_to_display( $this ) ) . wc_get_price_suffix( $this ); + } else { + $price = wc_price( wc_get_price_to_display( $this ) ) . wc_get_price_suffix( $this ); + } + + return apply_filters( 'woocommerce_get_price_html', $price, $this ); + } + /** * Get product name with SKU or ID. Used within admin. * @@ -1623,63 +1685,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_product_file_download_path', $file_path, $this, $download_id ); } - /* - |-------------------------------------------------------------------------- - | @todo price functions - |-------------------------------------------------------------------------- - */ - - /** - * Set a products price dynamically. - * - * @param float $price Price to set. - */ - public function set_price( $price ) { - $this->price = $price; - } - - /** - * Adjust a products price dynamically. - * - * @param mixed $price - */ - public function adjust_price( $price ) { - $this->price = $this->price + $price; - } - - /** - * Returns the product's active price. - * - * @return string price - */ - public function get_price() { - if ( $this->is_on_sale() ) { - $price = $this->get_sale_price(); - } else { - $price = $this->get_regular_price(); - } - return apply_filters( 'woocommerce_get_price', $price, $this ); - } - - /** - * Returns the price in html format. - * - * @return string - */ - public function get_price_html( $deprecated = '' ) { - if ( '' === $this->get_price() ) { - return apply_filters( 'woocommerce_empty_price_html', '', $this ); - } - - if ( $this->is_on_sale() ) { - $price = wc_format_price_range( $this->get_display_price( $this->get_regular_price() ), $this->get_display_price() ) . wc_get_price_suffix( $this ); - } else { - $price = wc_price( $this->get_display_price() ) . wc_get_price_suffix( $this ); - } - - return apply_filters( 'woocommerce_get_price_html', $price, $this ); - } - /* |-------------------------------------------------------------------------- | @todo taxonomy functions diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index dc63f504390..65ed5210015 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -872,7 +872,7 @@ function wc_get_related_products_query( $cats_array, $tags_array, $exclude_ids, * @param array $args * @return float */ -function wc_get_price_including_tax( $product, $args ) { +function wc_get_price_including_tax( $product, $args = array() ) { $args = wp_parse_args( $args, array( 'qty' => 1, 'price' => $product->get_price(), @@ -921,7 +921,7 @@ function wc_get_price_including_tax( $product, $args ) { * @param array $args * @return float */ -function wc_get_price_excluding_tax( $product, $args ) { +function wc_get_price_excluding_tax( $product, $args = array() ) { $args = wp_parse_args( $args, array( 'qty' => 1, 'price' => $product->get_price(), @@ -948,7 +948,7 @@ function wc_get_price_excluding_tax( $product, $args ) { * @param array $args * @return float */ -public function wc_get_price_to_display( $product, $args ) { +function wc_get_price_to_display( $product, $args = array() ) { $args = wp_parse_args( $args, array( 'qty' => 1, 'price' => $product->get_price(), @@ -957,5 +957,5 @@ public function wc_get_price_to_display( $product, $args ) { $price = $args['price']; $qty = $args['qty']; - return 'incl' === get_option( 'woocommerce_tax_display_shop' ) ) ? wc_get_price_including_tax( $product, array( 'qty' => $qty, 'price' => $price ) ) : wc_get_price_excluding_tax( $product, array( 'qty' => $qty, 'price' => $price ); + return 'incl' === get_option( 'woocommerce_tax_display_shop' ) ? wc_get_price_including_tax( $product, array( 'qty' => $qty, 'price' => $price ) ) : wc_get_price_excluding_tax( $product, array( 'qty' => $qty, 'price' => $price ) ); } diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 1908030238f..67a97907fca 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -2500,14 +2500,13 @@ function wc_get_price_suffix( $product, $price = '', $qty = 1 ) { ); $replace = array( - wc_price( $product->get_price_including_tax( $qty, $price ) ), - wc_price( $product->get_price_excluding_tax( $qty, $price ) ), + wc_price( wc_get_price_including_tax( $product, array( 'qty' => $qty, 'price' => $price ) ) ), + wc_price( wc_get_price_excluding_tax( $product, array( 'qty' => $qty, 'price' => $price ) ) ), ); $price_display_suffix = str_replace( $find, $replace, $price_display_suffix ); } else { $price_display_suffix = ''; } - return apply_filters( 'woocommerce_get_price_suffix', $price_display_suffix, $product ); } From 880dc53ac95521cb2c74765de6e87903a56131fb Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Mon, 24 Oct 2016 00:28:56 -0700 Subject: [PATCH 040/163] [Product CRUD] Variable (#12146) * [Product CRUD] Variable Products * Handle PR feedback. --- .../abstracts/abstract-wc-legacy-product.php | 16 + includes/abstracts/abstract-wc-product.php | 8 +- includes/class-wc-product-external.php | 3 +- includes/class-wc-product-grouped.php | 2 +- includes/class-wc-product-variable.php | 808 ++++++++++-------- includes/wc-template-functions.php | 2 +- .../single-product/product-attributes.php | 4 +- tests/unit-tests/product/crud.php | 89 ++ 8 files changed, 561 insertions(+), 371 deletions(-) diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index 7d77a02835b..a9eae2af8d8 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -359,6 +359,7 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { /** * Returns the upsell product ids. * + * @deprecated 2.7.0 * @return array */ public function get_upsells() { @@ -369,10 +370,25 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { /** * Returns the cross sell product ids. * + * @deprecated 2.7.0 * @return array */ public function get_cross_sells() { _deprecated_function( 'WC_Product::get_cross_sells', '2.7', 'WC_Product::get_cross_sell_ids' ); return apply_filters( 'woocommerce_product_crosssell_ids', (array) maybe_unserialize( $this->crosssell_ids ), $this ); } + + /** + * Check if variable product has default attributes set. + * + * @deprecated 2.7.0 + * @return bool + */ + public function has_default_attributes() { + _deprecated_function( 'WC_Product_Variable::has_default_attributes', '2.7', 'Check WC_Product::get_default_attributes directly' ); + if ( ! $this->get_default_attributes() ) { + return true; + } + return false; + } } diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index f2aed20d3e6..82257f83981 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -460,7 +460,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { // Check for any attributes which have been removed globally if ( is_array( $attributes ) && count( $attributes ) > 0 ) { foreach ( $attributes as $key => $attribute ) { - if ( $attribute['is_taxonomy'] ) { + if ( ! empty( $attribute['is_taxonomy'] ) ) { if ( ! in_array( substr( $attribute['name'], 3 ), $taxonomies ) ) { unset( $attributes[ $key ] ); } @@ -888,7 +888,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param array $attributes List of product attributes. */ public function set_attributes( $attributes ) { - $this->data['attributes'] = $attributes; // @todo ensure unserialised, array, and filtered out empty values + $this->data['attributes'] = array_filter( (array) maybe_unserialize( $attributes ) ); } /** @@ -990,7 +990,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'parent_id' => $post_object->post_parent, 'reviews_allowed' => $post_object->comment_status, 'purchase_note' => get_post_meta( $id, '_purchase_note', true ), - 'attributes' => get_post_meta( $id, '_attributes', true ), + 'attributes' => get_post_meta( $id, '_product_attributes', true ), 'default_attributes' => get_post_meta( $id, '_default_attributes', true ), 'menu_order' => $post_object->menu_order, ) ); @@ -1107,7 +1107,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { update_post_meta( $id, '_upsell_ids', $this->get_upsell_ids() ); update_post_meta( $id, '_crosssell_ids', $this->get_cross_sell_ids() ); update_post_meta( $id, '_purchase_note', $this->get_purchase_note() ); - update_post_meta( $id, '_attributes', $this->get_attributes() ); + update_post_meta( $id, '_product_attributes', $this->get_attributes() ); update_post_meta( $id, '_default_attributes', $this->get_default_attributes() ); if ( $this->is_on_sale() ) { diff --git a/includes/class-wc-product-external.php b/includes/class-wc-product-external.php index 8c058a516f1..21a248e14ff 100644 --- a/includes/class-wc-product-external.php +++ b/includes/class-wc-product-external.php @@ -1,7 +1,6 @@ get_var( "SELECT 1 FROM $wpdb->postmeta WHERE meta_key = '_sale_price' AND meta_value > 0 AND post_id IN (" . implode( ',', array_map( 'esc_sql', $this->get_children() ) ) . ");" ); + $on_sale = $this->get_children() && 1 === $wpdb->get_var( "SELECT 1 FROM $wpdb->postmeta WHERE meta_key = '_sale_price' AND meta_value > 0 AND post_id IN (" . implode( ',', array_map( 'esc_sql', $this->get_children() ) ) . ");" ); return apply_filters( 'woocommerce_product_is_on_sale', $on_sale, $this ); } diff --git a/includes/class-wc-product-variable.php b/includes/class-wc-product-variable.php index 44dfa72a5ed..a3de62a7198 100644 --- a/includes/class-wc-product-variable.php +++ b/includes/class-wc-product-variable.php @@ -1,7 +1,6 @@ array(), + 'variation_prices' => array(), + 'variation_prices_including_taxes' => array(), + 'variation_attributes' => array(), + ); - /** @private array Array of variation prices. */ + /** + * Cached & hashed prices array, used to populate variation_prices + * @var array + */ private $prices_array = array(); + /** + * Merges variable product data into the parent object. + * @param int|WC_Product|object $product Product to init. + */ + public function __construct( $product = 0 ) { + $this->data = array_merge( $this->data, $this->extra_data ); + parent::__construct( $product ); + } + + /* + |-------------------------------------------------------------------------- + | Getters + |-------------------------------------------------------------------------- + | + | Methods for getting data from the product object. + */ + /** * Get internal type. * @return string @@ -31,321 +59,98 @@ class WC_Product_Variable extends WC_Product { return 'variable'; } - /** - * Get the add to cart button text. - * - * @access public - * @return string - */ - public function add_to_cart_text() { - $text = $this->is_purchasable() && $this->is_in_stock() ? __( 'Select options', 'woocommerce' ) : __( 'Read more', 'woocommerce' ); - - return apply_filters( 'woocommerce_product_add_to_cart_text', $text, $this ); - } - - /** - * Set stock level of the product. - * - * @param mixed $amount (default: null) - * @param string $mode can be set, add, or subtract - * @return int Stock - */ - public function set_stock( $amount = null, $mode = 'set' ) { - $this->total_stock = ''; - delete_transient( 'wc_product_total_stock_' . $this->id . WC_Cache_Helper::get_transient_version( 'product' ) ); - return parent::set_stock( $amount, $mode ); - } - - /** - * Performed after a stock level change at product level. - */ - public function check_stock_status() { - $set_child_stock_status = ''; - - if ( ! $this->backorders_allowed() && $this->get_stock_quantity() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) { - $set_child_stock_status = 'outofstock'; - } elseif ( $this->backorders_allowed() || $this->get_stock_quantity() > get_option( 'woocommerce_notify_no_stock_amount' ) ) { - $set_child_stock_status = 'instock'; - } - - if ( $set_child_stock_status ) { - foreach ( $this->get_children() as $child_id ) { - if ( 'yes' !== get_post_meta( $child_id, '_manage_stock', true ) ) { - wc_update_product_stock_status( $child_id, $set_child_stock_status ); - } - } - - // Children statuses changed, so sync self - self::sync_stock_status( $this->id ); - } - } - - /** - * Set stock status. - */ - public function set_stock_status( $status ) { - $status = 'outofstock' === $status ? 'outofstock' : 'instock'; - - if ( update_post_meta( $this->id, '_stock_status', $status ) ) { - do_action( 'woocommerce_product_set_stock_status', $this->id, $status ); - } - } - /** * Return a products child ids. * - * @param boolean $visible_only Only return variations which are not hidden - * @return array of children ids + * @param boolean $deprecated + * @return array Children ids */ - public function get_children( $visible_only = false ) { - $key = $visible_only ? 'visible' : 'all'; - $transient_name = 'wc_product_children_' . $this->id; - - // Get value of transient - if ( ! is_array( $this->children ) ) { - $this->children = get_transient( $transient_name ); + public function get_children( $deprecated = false ) { + if ( $deprecated ) { + _deprecated_argument( 'visible_only', '2.7', 'WC_Product_Variable::get_visible_children' ); + return $this->get_visible_children(); } - - // Get value from DB - if ( empty( $this->children ) || ! is_array( $this->children ) || ! isset( $this->children[ $key ] ) ) { - $args = array( - 'post_parent' => $this->id, - 'post_type' => 'product_variation', - 'orderby' => 'menu_order', - 'order' => 'ASC', - 'fields' => 'ids', - 'post_status' => 'publish', - 'numberposts' => -1, - ); - - if ( $visible_only ) { - if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) { - $args['meta_query'][] = array( - 'key' => '_stock_status', - 'value' => 'instock', - 'compare' => '=', - ); - } - } - - $args = apply_filters( 'woocommerce_variable_children_args', $args, $this, $visible_only ); - $this->children[ $key ] = get_posts( $args ); - - set_transient( $transient_name, $this->children, DAY_IN_SECONDS * 30 ); - } - - return apply_filters( 'woocommerce_get_children', $this->children[ $key ], $this, $visible_only ); + return apply_filters( 'woocommerce_get_children', $this->data['children'], $this, false ); } /** - * Get child product. + * Return a products child ids - visible only. * - * @access public - * @param mixed $child_id - * @return WC_Product_Variation + * @since 2.7.0 + * @return array Children ids */ - public function get_child( $child_id ) { - return wc_get_product( $child_id, array( - 'parent_id' => $this->id, - 'parent' => $this, - ) ); - } - - /** - * Returns whether or not the product has any child product. - * - * @access public - * @return bool - */ - public function has_child() { - return sizeof( $this->get_children() ) ? true : false; - } - - /** - * Returns whether or not the product is on sale. - * @return bool - */ - public function is_on_sale() { - $is_on_sale = false; - $prices = $this->get_variation_prices(); - - if ( $prices['regular_price'] !== $prices['sale_price'] && $prices['sale_price'] === $prices['price'] ) { - $is_on_sale = true; - } - return apply_filters( 'woocommerce_product_is_on_sale', $is_on_sale, $this ); + public function get_visible_children() { + return apply_filters( 'woocommerce_get_children', $this->data['visible_children'], $this, false ); } /** * Get the min or max variation regular price. - * @param string $min_or_max - min or max - * @param boolean $display Whether the value is going to be displayed + * @param string $min_or_max Min or max price. + * @param boolean $include_taxes Should the price include taxes? * @return string */ - public function get_variation_regular_price( $min_or_max = 'min', $display = false ) { - $prices = $this->get_variation_prices( $display ); + public function get_variation_regular_price( $min_or_max = 'min', $include_taxes = false ) { + $prices = $include_taxes ? $this->data['variation_prices_including_taxes'] : $this->data['variation_prices']; $price = 'min' === $min_or_max ? current( $prices['regular_price'] ) : end( $prices['regular_price'] ); - return apply_filters( 'woocommerce_get_variation_regular_price', $price, $this, $min_or_max, $display ); + return apply_filters( 'woocommerce_get_variation_regular_price', $price, $this, $min_or_max, $include_taxes ); } /** * Get the min or max variation sale price. - * @param string $min_or_max - min or max - * @param boolean $display Whether the value is going to be displayed + * @param string $min_or_max Min or max price. + * @param boolean $include_taxes Should the price include taxes? * @return string */ - public function get_variation_sale_price( $min_or_max = 'min', $display = false ) { - $prices = $this->get_variation_prices( $display ); + public function get_variation_sale_price( $min_or_max = 'min', $inclde_taxes = false ) { + $prices = $include_taxes ? $this->data['variation_prices_including_taxes'] : $this->data['variation_prices']; $price = 'min' === $min_or_max ? current( $prices['sale_price'] ) : end( $prices['sale_price'] ); - return apply_filters( 'woocommerce_get_variation_sale_price', $price, $this, $min_or_max, $display ); + return apply_filters( 'woocommerce_get_variation_sale_price', $price, $this, $min_or_max, $include_taxes ); } /** * Get the min or max variation (active) price. - * @param string $min_or_max - min or max - * @param boolean $display Whether the value is going to be displayed + * @param string $min_or_max Min or max price. + * @param boolean $include_taxes Should the price include taxes? * @return string */ - public function get_variation_price( $min_or_max = 'min', $display = false ) { - $prices = $this->get_variation_prices( $display ); + public function get_variation_price( $min_or_max = 'min', $include_taxes = false ) { + $prices = $include_taxes ? $this->data['variation_prices_including_taxes'] : $this->data['variation_prices']; $price = 'min' === $min_or_max ? current( $prices['price'] ) : end( $prices['price'] ); - return apply_filters( 'woocommerce_get_variation_price', $price, $this, $min_or_max, $display ); + return apply_filters( 'woocommerce_get_variation_price', $price, $this, $min_or_max, $include_taxes ); } /** * Get an array of all sale and regular prices from all variations. This is used for example when displaying the price range at variable product level or seeing if the variable product is on sale. * - * Can be filtered by plugins which modify costs, but otherwise will include the raw meta costs unlike get_price() which runs costs through the woocommerce_get_price filter. - * This is to ensure modified prices are not cached, unless intended. - * - * @param bool $display Are prices for display? If so, taxes will be calculated. + * @param bool $deprecated * @return array() Array of RAW prices, regular prices, and sale prices with keys set to variation ID. */ - public function get_variation_prices( $display = false ) { - global $wp_filter; - - /** - * Transient name for storing prices for this product (note: Max transient length is 45) - * @since 2.5.0 a single transient is used per product for all prices, rather than many transients per product. - */ - $transient_name = 'wc_var_prices_' . $this->id; - - /** - * Create unique cache key based on the tax location (affects displayed/cached prices), product version and active price filters. - * DEVELOPERS should filter this hash if offering conditonal pricing to keep it unique. - * @var string - */ - if ( $display ) { - $price_hash = array( get_option( 'woocommerce_tax_display_shop', 'excl' ), WC_Tax::get_rates() ); - } else { - $price_hash = array( false ); + public function get_variation_prices( $deprecated = false ) { + if ( $deprecated ) { + _deprecated_argument( 'display', '2.7', 'Use WC_Product_Variable::get_variation_prices_including_taxes' ); + return $this->get_variation_prices_including_taxes(); } + return $this->data['variation_prices']; + } - $filter_names = array( 'woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price' ); - - foreach ( $filter_names as $filter_name ) { - if ( ! empty( $wp_filter[ $filter_name ] ) ) { - $price_hash[ $filter_name ] = array(); - - foreach ( $wp_filter[ $filter_name ] as $priority => $callbacks ) { - $price_hash[ $filter_name ][] = array_values( wp_list_pluck( $callbacks, 'function' ) ); - } - } - } - - $price_hash[] = WC_Cache_Helper::get_transient_version( 'product' ); - $price_hash = md5( json_encode( apply_filters( 'woocommerce_get_variation_prices_hash', $price_hash, $this, $display ) ) ); - - /** - * $this->prices_array is an array of values which may have been modified from what is stored in transients - this may not match $transient_cached_prices_array. - * If the value has already been generated, we don't need to grab the values again so just return them. They are already filtered. - */ - if ( ! empty( $this->prices_array[ $price_hash ] ) ) { - return $this->prices_array[ $price_hash ]; - - /** - * No locally cached value? Get the data from the transient or generate it. - */ - } else { - // Get value of transient - $transient_cached_prices_array = array_filter( (array) json_decode( strval( get_transient( $transient_name ) ), true ) ); - - // If the product version has changed since the transient was last saved, reset the transient cache. - if ( empty( $transient_cached_prices_array['version'] ) || WC_Cache_Helper::get_transient_version( 'product' ) !== $transient_cached_prices_array['version'] ) { - $transient_cached_prices_array = array( 'version' => WC_Cache_Helper::get_transient_version( 'product' ) ); - } - - // If the prices are not stored for this hash, generate them and add to the transient. - if ( empty( $transient_cached_prices_array[ $price_hash ] ) ) { - $prices = array(); - $regular_prices = array(); - $sale_prices = array(); - $variation_ids = $this->get_children( true ); - - foreach ( $variation_ids as $variation_id ) { - if ( $variation = $this->get_child( $variation_id ) ) { - $price = apply_filters( 'woocommerce_variation_prices_price', $variation->price, $variation, $this ); - $regular_price = apply_filters( 'woocommerce_variation_prices_regular_price', $variation->regular_price, $variation, $this ); - $sale_price = apply_filters( 'woocommerce_variation_prices_sale_price', $variation->sale_price, $variation, $this ); - - // Skip empty prices - if ( '' === $price ) { - continue; - } - - // If sale price does not equal price, the product is not yet on sale - if ( $sale_price === $regular_price || $sale_price !== $price ) { - $sale_price = $regular_price; - } - - // If we are getting prices for display, we need to account for taxes - if ( $display ) { - if ( 'incl' === get_option( 'woocommerce_tax_display_shop' ) ) { - $price = '' === $price ? '' : $variation->get_price_including_tax( 1, $price ); - $regular_price = '' === $regular_price ? '' : $variation->get_price_including_tax( 1, $regular_price ); - $sale_price = '' === $sale_price ? '' : $variation->get_price_including_tax( 1, $sale_price ); - } else { - $price = '' === $price ? '' : $variation->get_price_excluding_tax( 1, $price ); - $regular_price = '' === $regular_price ? '' : $variation->get_price_excluding_tax( 1, $regular_price ); - $sale_price = '' === $sale_price ? '' : $variation->get_price_excluding_tax( 1, $sale_price ); - } - } - - $prices[ $variation_id ] = wc_format_decimal( $price, wc_get_price_decimals() ); - $regular_prices[ $variation_id ] = wc_format_decimal( $regular_price, wc_get_price_decimals() ); - $sale_prices[ $variation_id ] = wc_format_decimal( $sale_price . '.00', wc_get_price_decimals() ); - } - } - - asort( $prices ); - asort( $regular_prices ); - asort( $sale_prices ); - - $transient_cached_prices_array[ $price_hash ] = array( - 'price' => $prices, - 'regular_price' => $regular_prices, - 'sale_price' => $sale_prices, - ); - - set_transient( $transient_name, json_encode( $transient_cached_prices_array ), DAY_IN_SECONDS * 30 ); - } - - /** - * Give plugins one last chance to filter the variation prices array which has been generated and store locally to the class. - * This value may differ from the transient cache. It is filtered once before storing locally. - */ - return $this->prices_array[ $price_hash ] = apply_filters( 'woocommerce_variation_prices', $transient_cached_prices_array[ $price_hash ], $this, $display ); - } + /** + * Get an array of all sale and regular prices from all variations, includes taxes. + * + * @since 2.7.0 + * @return array() Array of RAW prices, regular prices, and sale prices with keys set to variation ID. + */ + public function get_variation_prices_including_taxes() { + return $this->data['variation_prices_including_taxes']; } /** * Returns the price in html format. * - * @access public * @param string $price (default: '') * @return string */ public function get_price_html( $price = '' ) { - $prices = $this->get_variation_prices( true ); + $prices = $this->get_variation_prices_including_taxes(); // No variations, or no active variation prices if ( $this->get_price() === '' || empty( $prices['price'] ) ) { @@ -373,84 +178,20 @@ class WC_Product_Variable extends WC_Product { /** * Return an array of attributes used for variations, as well as their possible values. * - * @return array of attributes and their available values + * @return array Attributes and their available values */ public function get_variation_attributes() { - global $wpdb; - - $variation_attributes = array(); - $attributes = $this->get_attributes(); - $child_ids = $this->get_children( true ); - - if ( ! empty( $child_ids ) ) { - 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 ) ) { - $values = $attribute['is_taxonomy'] ? wp_get_post_terms( $this->id, $attribute['name'], array( 'fields' => 'slugs' ) ) : 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( $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; + return $this->data['variation_attributes']; } /** * If set, get the default attributes for a variable product. * - * @access public * @return array */ public function get_variation_default_attributes() { - $default = isset( $this->default_attributes ) ? $this->default_attributes : ''; - return apply_filters( 'woocommerce_product_default_attributes', array_filter( (array) maybe_unserialize( $default ) ), $this ); - } - - /** - * Check if variable product has default attributes set. - * - * @access public - * @return bool - */ - public function has_default_attributes() { - if ( ! $this->get_variation_default_attributes() ) { - return true; - } - return false; + _deprecated_function( 'WC_Product_Variable::get_variation_default_attributes', '2.7', 'WC_Product::get_default_attributes' ); + return apply_filters( 'woocommerce_product_default_attributes', array_filter( (array) maybe_unserialize( $this->get_default_attributes() ) ), $this ); } /** @@ -460,7 +201,7 @@ class WC_Product_Variable extends WC_Product { * @return string */ public function get_variation_default_attribute( $attribute_name ) { - $defaults = $this->get_variation_default_attributes(); + $defaults = $this->get_default_attributes(); $attribute_name = sanitize_title( $attribute_name ); return isset( $defaults[ $attribute_name ] ) ? $defaults[ $attribute_name ] : ''; } @@ -475,7 +216,7 @@ class WC_Product_Variable extends WC_Product { global $wpdb; $query_args = array( - 'post_parent' => $this->id, + 'post_parent' => $this->get_id(), 'post_type' => 'product_variation', 'orderby' => 'menu_order', 'order' => 'ASC', @@ -525,7 +266,7 @@ class WC_Product_Variable extends WC_Product { * Pre 2.4 handling where 'slugs' were saved instead of the full text attribute. * Fallback is here because there are cases where data will be 'synced' but the product version will remain the same. @see WC_Product_Variable::sync_attributes. */ - } elseif ( version_compare( get_post_meta( $this->id, '_product_version', true ), '2.4.0', '<' ) ) { + } elseif ( version_compare( get_post_meta( $this->get_id(), '_product_version', true ), '2.4.0', '<' ) ) { return ( array_map( 'sanitize_title', $match_attributes ) === $match_attributes ) ? 0 : $this->get_matching_variation( array_map( 'sanitize_title', $match_attributes ) ); } else { @@ -544,12 +285,12 @@ class WC_Product_Variable extends WC_Product { $variation = $this->get_child( $child_id ); // Hide out of stock variations if 'Hide out of stock items from the catalog' is checked - if ( empty( $variation->variation_id ) || ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ! $variation->is_in_stock() ) ) { + if ( empty( $variation->get_variation_id() ) || ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ! $variation->is_in_stock() ) ) { continue; } // Filter 'woocommerce_hide_invisible_variations' to optionally hide invisible variations (disabled variations and variations with empty price) - if ( apply_filters( 'woocommerce_hide_invisible_variations', false, $this->id, $variation ) && ! $variation->variation_is_visible() ) { + if ( apply_filters( 'woocommerce_hide_invisible_variations', false, $this->get_id(), $variation ) && ! $variation->variation_is_visible() ) { continue; } @@ -591,7 +332,7 @@ class WC_Product_Variable extends WC_Product { } return apply_filters( 'woocommerce_available_variation', array( - 'variation_id' => $variation->variation_id, + 'variation_id' => $variation->get_variation_id(), 'variation_is_visible' => $variation->variation_is_visible(), 'variation_is_active' => $variation->variation_is_active(), 'is_purchasable' => $variation->is_purchasable(), @@ -621,12 +362,66 @@ class WC_Product_Variable extends WC_Product { ), $this, $variation ); } + /* + |-------------------------------------------------------------------------- + | Other Actions + |-------------------------------------------------------------------------- + */ + + /** + * Get the add to cart button text. + * + * @return string + */ + public function add_to_cart_text() { + $text = $this->is_purchasable() && $this->is_in_stock() ? __( 'Select options', 'woocommerce' ) : __( 'Read more', 'woocommerce' ); + return apply_filters( 'woocommerce_product_add_to_cart_text', $text, $this ); + } + + /** + * Performed after a stock level change at product level. + */ + public function check_stock_status() { + $set_child_stock_status = ''; + + if ( ! $this->backorders_allowed() && $this->get_stock_quantity() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) { + $set_child_stock_status = 'outofstock'; + } elseif ( $this->backorders_allowed() || $this->get_stock_quantity() > get_option( 'woocommerce_notify_no_stock_amount' ) ) { + $set_child_stock_status = 'instock'; + } + + if ( $set_child_stock_status ) { + foreach ( $this->get_children() as $child_id ) { + if ( 'yes' !== get_post_meta( $child_id, '_manage_stock', true ) ) { + wc_update_product_stock_status( $child_id, $set_child_stock_status ); + } + } + + // Children statuses changed, so sync self + self::sync_stock_status( $this->get_id() ); + } + } + + /** + * Returns whether or not the product is on sale. + * @return bool + */ + public function is_on_sale() { + $is_on_sale = false; + $prices = $this->read_price_data(); + + if ( $prices['regular_price'] !== $prices['sale_price'] && $prices['sale_price'] === $prices['price'] ) { + $is_on_sale = true; + } + return apply_filters( 'woocommerce_product_is_on_sale', $is_on_sale, $this ); + } + /** * Sync variable product prices with the children lowest/highest prices. */ public function variable_product_sync( $product_id = '' ) { if ( empty( $product_id ) ) { - $product_id = $this->id; + $product_id = $this->get_id(); } // Sync prices with children @@ -653,11 +448,11 @@ class WC_Product_Variable extends WC_Product { */ public static function sync_stock_status( $product_id ) { $children = get_posts( array( - 'post_parent' => $product_id, + 'post_parent' => $product_id, 'posts_per_page' => -1, - 'post_type' => 'product_variation', - 'fields' => 'ids', - 'post_status' => 'publish', + 'post_type' => 'product_variation', + 'fields' => 'ids', + 'post_status' => 'publish', ) ); $stock_status = 'outofstock'; @@ -680,11 +475,11 @@ class WC_Product_Variable extends WC_Product { public static function sync_attributes( $product_id, $children = false ) { if ( ! $children ) { $children = get_posts( array( - 'post_parent' => $product_id, + 'post_parent' => $product_id, 'posts_per_page' => -1, - 'post_type' => 'product_variation', - 'fields' => 'ids', - 'post_status' => 'any', + 'post_type' => 'product_variation', + 'fields' => 'ids', + 'post_status' => 'any', ) ); } @@ -727,7 +522,7 @@ class WC_Product_Variable extends WC_Product { * @return boolean */ public function child_has_weight() { - return (bool) get_post_meta( $this->id, '_child_has_weight', true ); + return (bool) get_post_meta( $this->get_id(), '_child_has_weight', true ); } /** @@ -736,7 +531,7 @@ class WC_Product_Variable extends WC_Product { * @return boolean */ public function child_has_dimensions() { - return (bool) get_post_meta( $this->id, '_child_has_dimensions', true ); + return (bool) get_post_meta( $this->get_id(), '_child_has_dimensions', true ); } /** @@ -755,11 +550,11 @@ class WC_Product_Variable extends WC_Product { global $wpdb; $children = get_posts( array( - 'post_parent' => $product_id, + 'post_parent' => $product_id, 'posts_per_page' => -1, - 'post_type' => 'product_variation', - 'fields' => 'ids', - 'post_status' => 'publish', + 'post_type' => 'product_variation', + 'fields' => 'ids', + 'post_status' => 'publish', ) ); // No published variations - product won't be purchasable. @@ -875,4 +670,295 @@ class WC_Product_Variable extends WC_Product { do_action( 'woocommerce_variable_product_sync', $product_id, $children ); } } + + /* + |-------------------------------------------------------------------------- + | Setters + |-------------------------------------------------------------------------- + | + | Functions for setting product data. Most data in this class + | is pulled from the children products, so we don't have set_* Functions + | for variation prices, etc. If you want to manage a variation, do so + | by modifiying WC_Product_Variation. + */ + + /** + * Set stock status. + */ + public function set_stock_status( $status ) { + $this->data['stock_status'] = 'outofstock' === $status ? 'outofstock' : 'instock'; + } + + /** + * Set stock level of the product. + * + * @param mixed $amount (default: null) + * @param string $mode can be set, add, or subtract + * @return int Stock + */ + public function set_stock( $amount = null, $mode = 'set' ) { + $this->total_stock = ''; + delete_transient( 'wc_product_total_stock_' . $this->get_id() . WC_Cache_Helper::get_transient_version( 'product' ) ); + return parent::set_stock( $amount, $mode ); + } + + /* + |-------------------------------------------------------------------------- + | CRUD methods + |-------------------------------------------------------------------------- + */ + + /** + * Reads a product from the database and sets its data to the class. + * + * @since 2.7.0 + * @param int $id Product ID. + */ + public function read( $id ) { + parent::read( $id ); + $children = $this->read_children(); + + // Set directly since individual data needs changed at the WC_Product_Variation level -- these datasets just pull + $this->data['children'] = array_filter( wp_parse_id_list( (array) $children['all'] ) ); + $this->data['children_visible'] = array_filter( wp_parse_id_list( (array) $children['visible'] ) ); + $this->data['variation_prices'] = $this->read_price_data(); + $this->data['variation_prices_including_taxes'] = $this->read_price_data( true ); + $this->data['variation_attributes'] = $this->read_variation_attributes(); + do_action( 'woocommerce_product_loaded', $this ); + do_action( 'woocommerce_product_' . $this->get_type() . '_loaded', $this ); + } + + /* + |-------------------------------------------------------------------------- + | CRUD helper methods + |-------------------------------------------------------------------------- + */ + + /** + * Loads variation child IDs. + * + * @return array + */ + private function read_children() { + $children_transient_name = 'wc_product_children_' . $this->get_id(); + $children = get_transient( $children_transient_name ); + + if ( empty( $children ) || ! is_array( $children ) || ! isset( $children['all'] ) || ! isset( $children['visible'] ) ) { + $all_args = $visible_only_args = array( + 'post_parent' => $this->get_id(), + 'post_type' => 'product_variation', + 'orderby' => 'menu_order', + 'order' => 'ASC', + 'fields' => 'ids', + 'post_status' => 'publish', + 'numberposts' => -1, + ); + if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) { + $visible_only_args['meta_query'][] = array( + 'key' => '_stock_status', + 'value' => 'instock', + 'compare' => '=', + ); + } + $all_args = apply_filters( 'woocommerce_variable_children_args', $all_args, $this, false ); + $visible_only_args = apply_filters( 'woocommerce_variable_children_args', $visible_only_args, $this, false ); + $children['all'] = get_posts( $all_args ); + $children['visible'] = get_posts( $visible_only_args ); + + set_transient( $children_transient_name, $children, DAY_IN_SECONDS * 30 ); + return $children; + } + + return $children; + } + + /** + * Loads an array of attributes used for variations, as well as their possible values. + * + * @return array Attributes and their available values + */ + private function read_variation_attributes() { + global $wpdb; + $variation_attributes = array(); + $attributes = $this->get_attributes(); + $child_ids = $this->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'] ? wp_get_post_terms( $this->get_id(), $attribute['name'], array( 'fields' => 'slugs' ) ) : 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( $this->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 ); + } + } + return $variation_attributes; + } + + /** + * Get an array of all sale and regular prices from all variations. This is used for example when displaying the price range at variable product level or seeing if the variable product is on sale. + * + * Can be filtered by plugins which modify costs, but otherwise will include the raw meta costs unlike get_price() which runs costs through the woocommerce_get_price filter. + * This is to ensure modified prices are not cached, unless intended. + * + * @param bool $include_taxes If taxes should be calculated or not. + * @return array() Array of RAW prices, regular prices, and sale prices with keys set to variation ID. + */ + private function read_price_data( $include_taxes = false ) { + global $wp_filter; + + /** + * Transient name for storing prices for this product (note: Max transient length is 45) + * @since 2.5.0 a single transient is used per product for all prices, rather than many transients per product. + */ + $transient_name = 'wc_var_prices_' . $this->get_id(); + + /** + * Create unique cache key based on the tax location (affects displayed/cached prices), product version and active price filters. + * DEVELOPERS should filter this hash if offering conditonal pricing to keep it unique. + * @var string + */ + if ( $include_taxes ) { + $price_hash = array( get_option( 'woocommerce_tax_display_shop', 'excl' ), WC_Tax::get_rates() ); + } else { + $price_hash = array( false ); + } + + $filter_names = array( 'woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price' ); + + foreach ( $filter_names as $filter_name ) { + if ( ! empty( $wp_filter[ $filter_name ] ) ) { + $price_hash[ $filter_name ] = array(); + + foreach ( $wp_filter[ $filter_name ] as $priority => $callbacks ) { + $price_hash[ $filter_name ][] = array_values( wp_list_pluck( $callbacks, 'function' ) ); + } + } + } + + $price_hash[] = WC_Cache_Helper::get_transient_version( 'product' ); + $price_hash = md5( json_encode( apply_filters( 'woocommerce_get_variation_prices_hash', $price_hash, $this, $include_taxes ) ) ); + + /** + * $this->prices_array is an array of values which may have been modified from what is stored in transients - this may not match $transient_cached_prices_array. + * If the value has already been generated, we don't need to grab the values again so just return them. They are already filtered. + */ + if ( ! empty( $this->prices_array[ $price_hash ] ) ) { + return $this->prices_array[ $price_hash ]; + + /** + * No locally cached value? Get the data from the transient or generate it. + */ + } else { + // Get value of transient + $transient_cached_prices_array = array_filter( (array) json_decode( strval( get_transient( $transient_name ) ), true ) ); + + // If the product version has changed since the transient was last saved, reset the transient cache. + if ( empty( $transient_cached_prices_array['version'] ) || WC_Cache_Helper::get_transient_version( 'product' ) !== $transient_cached_prices_array['version'] ) { + $transient_cached_prices_array = array( 'version' => WC_Cache_Helper::get_transient_version( 'product' ) ); + } + + // If the prices are not stored for this hash, generate them and add to the transient. + if ( empty( $transient_cached_prices_array[ $price_hash ] ) ) { + $prices = array(); + $regular_prices = array(); + $sale_prices = array(); + $children = $this->read_children(); + $variation_ids = $children['visible']; + foreach ( $variation_ids as $variation_id ) { + if ( $variation = wc_get_product( $variation_id, array( 'parent_id' => $this->get_id(), 'parent' => $this ) ) ) { + // @todo Once WC_Product_Variation is updated, these should be get_price, get_regular_price, etc -- those don't work currently + $price = apply_filters( 'woocommerce_variation_prices_price', $variation->price, $variation, $this ); + $regular_price = apply_filters( 'woocommerce_variation_prices_regular_price', $variation->regular_price, $variation, $this ); + $sale_price = apply_filters( 'woocommerce_variation_prices_sale_price', $variation->sale_price, $variation, $this ); + + // Skip empty prices + if ( '' === $price ) { + continue; + } + + // If sale price does not equal price, the product is not yet on sale + if ( $sale_price === $regular_price || $sale_price !== $price ) { + $sale_price = $regular_price; + } + + // If we are getting prices for display, we need to account for taxes + if ( $include_taxes ) { + if ( 'incl' === get_option( 'woocommerce_tax_display_shop' ) ) { + $price = '' === $price ? '' : wc_get_price_including_tax( $variation, array( 'qty' => 1, 'price' => $price ) ); + $regular_price = '' === $regular_price ? '' : wc_get_price_including_tax( $variation, array( 'qty' => 1, 'price' => $regular_price ) ); + $sale_price = '' === $sale_price ? '' : wc_get_price_including_tax( $variation, array( 'qty' => 1, 'price' => $sale_price ) ); + } else { + $price = '' === $price ? '' : wc_get_price_excluding_tax( $variation, array( 'qty' => 1, 'price' => $price ) ); + $regular_price = '' === $regular_price ? '' : wc_get_price_excluding_tax( $variation, array( 'qty' => 1, 'price' => $regular_price ) ); + $sale_price = '' === $sale_price ? '' : wc_get_price_excluding_tax( $variation, array( 'qty' => 1, 'price' => $sale_price ) ); + } + } + + $prices[ $variation_id ] = wc_format_decimal( $price, wc_get_price_decimals() ); + $regular_prices[ $variation_id ] = wc_format_decimal( $regular_price, wc_get_price_decimals() ); + $sale_prices[ $variation_id ] = wc_format_decimal( $sale_price . '.00', wc_get_price_decimals() ); + } + } + + asort( $prices ); + asort( $regular_prices ); + asort( $sale_prices ); + + $transient_cached_prices_array[ $price_hash ] = array( + 'price' => $prices, + 'regular_price' => $regular_prices, + 'sale_price' => $sale_prices, + ); + + set_transient( $transient_name, json_encode( $transient_cached_prices_array ), DAY_IN_SECONDS * 30 ); + } + + /** + * Give plugins one last chance to filter the variation prices array which has been generated and store locally to the class. + * This value may differ from the transient cache. It is filtered once before storing locally. + */ + return $this->prices_array[ $price_hash ] = apply_filters( 'woocommerce_variation_prices', $transient_cached_prices_array[ $price_hash ], $this, $include_taxes ); + } + } + + /** + * Helper method that updates all the post meta + */ + protected function update_post_meta() { + parent::update_post_meta(); + if ( update_post_meta( $this->get_id(), '_stock_status', $this->get_stock_status() ) ) { + do_action( 'woocommerce_product_set_stock_status', $this->get_id(), $this->get_stock_status() ); + } + } + } diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 67a97907fca..07682b5e223 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -333,7 +333,7 @@ function wc_product_post_class( $classes, $class = '', $post_id = '' ) { $classes[] = "product-type-" . $product->get_type(); } if ( $product->is_type( 'variable' ) ) { - if ( $product->has_default_attributes() ) { + if ( ! $product->get_default_attributes() ) { $classes[] = 'has-default-attributes'; } if ( $product->has_child() ) { diff --git a/templates/single-product/product-attributes.php b/templates/single-product/product-attributes.php index bcf2e29380c..770bc907276 100644 --- a/templates/single-product/product-attributes.php +++ b/templates/single-product/product-attributes.php @@ -15,7 +15,7 @@ * @see https://docs.woocommerce.com/document/template-structure/ * @author WooThemes * @package WooCommerce/Templates - * @version 2.1.3 + * @version 2.7.0 */ if ( ! defined( 'ABSPATH' ) ) { @@ -33,7 +33,7 @@ ob_start(); enable_dimensions_display() ) : ?> - has_weight() || get_post_meta( $product->id, '_child_has_weight', true ) ) : $has_row = true; ?> + has_weight() || $product->child_has_weight() ) : $has_row = true; ?> get_weight() ? wc_format_localized_decimal( $product->get_weight() ) . ' ' . esc_attr( get_option( 'woocommerce_weight_unit' ) ) : __( 'N/A', 'woocommerce' ); ?> diff --git a/tests/unit-tests/product/crud.php b/tests/unit-tests/product/crud.php index 45d1b392dc7..a20e73ce431 100644 --- a/tests/unit-tests/product/crud.php +++ b/tests/unit-tests/product/crud.php @@ -253,4 +253,93 @@ class WC_Tests_Product_CRUD extends WC_Unit_Test_Case { $this->assertEquals( $value, $product->{"get_{$function}"}(), $function ); } } + + /** + * Test reading a variable product. + * + * @since 2.7.0 + */ + public function test_variable_read() { + $product = WC_Helper_Product::create_variation_product(); + $children = $product->get_children(); + + // Test sale prices too + update_post_meta( $children[0], '_price', '8' ); + update_post_meta( $children[0], '_sale_price', '8' ); + delete_transient( 'wc_var_prices_' . $product->get_id() ); + + $product = new WC_Product_Variable( $product->get_id() ); + + $this->assertEquals( 2, count( $product->get_children() ) ); + + $expected_prices['price'][ $children[0] ] = 8.00; + $expected_prices['price'][ $children[1] ] = 15.00; + $expected_prices['regular_price'][ $children[0] ] = 10.00; + $expected_prices['regular_price'][ $children[1] ] = 15.00; + $expected_prices['sale_price'][ $children[0] ] = 8.00; + $expected_prices['sale_price'][ $children[1] ] = 15.00; + + $this->assertEquals( $expected_prices, $product->get_variation_prices() ); + + $expected_attributes = array( 'pa_size' => array( 'small', 'large' ) ); + $this->assertEquals( $expected_attributes, $product->get_variation_attributes() ); + } + + /** + * Test creating a new variable product. + * + * @todo - this test can be improved. Once WC_Product_Variation is updated + * with CRUD as well, this test should add a variation to a variable product + * that way, and test things like get_children and attributes. + * + * @since 2.7.0 + */ + function test_variable_create_and_update() { + $product = new WC_Product_Variable; + $product->set_name( 'Variable Product' ); + $product->set_attributes( array( array( + 'name' => 'pa_size', + 'value' => 'small | large', + 'position' => '1', + 'is_visible' => 0, + 'is_variation' => 1, + 'is_taxonomy' => 0, + ) ) ); + $product->create(); + + $this->assertEquals( 'Variable Product', $product->get_name() ); + + // Create a variation + $variation_id = wp_insert_post( array( + 'post_title' => 'Variation #1 of Dummy Variable CRUD Product', + 'post_type' => 'product_variation', + 'post_parent' => $product->get_id(), + 'post_status' => 'publish', + ) ); + + update_post_meta( $variation_id, '_price', '10' ); + update_post_meta( $variation_id, '_regular_price', '10' ); + update_post_meta( $variation_id, '_sku', 'CRUD DUMMY SKU VARIABLE SMALL' ); + update_post_meta( $variation_id, '_manage_stock', 'no' ); + update_post_meta( $variation_id, '_downloadable', 'no' ); + update_post_meta( $variation_id, '_virtual', 'no' ); + update_post_meta( $variation_id, '_stock_status', 'instock' ); + update_post_meta( $variation_id, 'attribute_pa_size', 'small' ); + + delete_transient( 'wc_product_children_' . $product->get_id() ); + delete_transient( 'wc_var_prices_' . $product->get_id() ); + + $product = new WC_Product_Variable( $product->get_id() ); + $children = $product->get_children(); + $this->assertEquals( $variation_id, $children[0] ); + + $expected_attributes = array( 'pa_size' => array( 'small' ) ); + $this->assertEquals( $expected_attributes, $product->get_variation_attributes() ); + + $product->set_name( 'Renamed Variable Product' ); + $product->update(); + + $this->assertEquals( 'Renamed Variable Product', $product->get_name() ); + } + } From b1dbfd9c5ba6798febab620a151ea010675615cb Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 24 Oct 2016 09:19:29 +0100 Subject: [PATCH 041/163] [Product CRUD] Grouped Handling (#12151) * Handle grouped product saving * Update routine --- .../class-wc-meta-box-product-data.php | 93 +++++-------------- includes/class-wc-ajax.php | 63 ------------- includes/class-wc-install.php | 1 + includes/class-wc-product-grouped.php | 11 ++- includes/wc-update-functions.php | 17 ++++ 5 files changed, 50 insertions(+), 135 deletions(-) diff --git a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php index 3b777b685d3..80d852117aa 100644 --- a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php +++ b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php @@ -506,7 +506,6 @@ class WC_Meta_Box_Product_Data { '_download_limit', // @todo + 'id' => '_download_limit', + 'value' => -1 === $product_object->get_download_limit() ? '' : $product_object->get_download_limit(), 'label' => __( 'Download limit', 'woocommerce' ), 'placeholder' => __( 'Unlimited', 'woocommerce' ), 'description' => __( 'Leave blank for unlimited re-downloads.', 'woocommerce' ), @@ -103,7 +104,8 @@ ) ); woocommerce_wp_text_input( array( - 'id' => '_download_expiry', // @todo + 'id' => '_download_expiry', + 'value' => -1 === $product_object->get_download_expiry() ? '' : $product_object->get_download_expiry(), 'label' => __( 'Download expiry', 'woocommerce' ), 'placeholder' => __( 'Never', 'woocommerce' ), 'description' => __( 'Enter the number of days before a download link expires, or leave blank.', 'woocommerce' ), @@ -114,17 +116,6 @@ ), ) ); - woocommerce_wp_select( array( - 'id' => '_download_type', // @todo - 'label' => __( 'Download type', 'woocommerce' ), - 'description' => sprintf( __( 'Choose a download type - this controls the schema.', 'woocommerce' ), 'http://schema.org/' ), - 'options' => array( - '' => __( 'Standard Product', 'woocommerce' ), - 'application' => __( 'Application/Software', 'woocommerce' ), - 'music' => __( 'Music', 'woocommerce' ), - ), - ) ); - do_action( 'woocommerce_product_options_downloads' ); ?> From db0ce210a34559ddfe6bacbb830fc17301841a23 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 2 Nov 2016 18:50:42 +0000 Subject: [PATCH 051/163] [Product CRUD] Variations - setters, getters and admin. (#12228) * Started on variation changes * Stock functions * Variation class * Bulk change ->id to get_id() to fix variation form display * Missing status * Fix add to cart * Start on stored data save * save variation * Save_variations * Variation edit panel * Save variations code works. * Remove stored data code and fix save * Improve legacy class * wc_bool_to_string * prepare_set_attributes * Use wc_get_products * More feedback fixes * Feedback fixes --- assets/css/admin.css | 2 +- assets/css/admin.scss | 63 +- .../abstracts/abstract-wc-legacy-order.php | 2 +- .../abstracts/abstract-wc-legacy-product.php | 516 ++++++---- includes/abstracts/abstract-wc-order.php | 4 +- includes/abstracts/abstract-wc-product.php | 554 +++++----- includes/admin/class-wc-admin-post-types.php | 9 +- .../class-wc-meta-box-order-downloads.php | 2 +- .../class-wc-meta-box-product-data.php | 407 ++------ .../views/html-order-download-permission.php | 2 +- .../meta-boxes/views/html-order-items.php | 14 +- .../views/html-product-data-general.php | 12 +- .../views/html-product-data-inventory.php | 13 +- .../meta-boxes/views/html-variation-admin.php | 427 ++++---- .../admin/reports/class-wc-report-stock.php | 2 +- .../admin/settings/class-wc-settings-tax.php | 7 - .../admin/settings/views/settings-tax.php | 4 +- .../admin/views/html-bulk-edit-product.php | 19 +- .../admin/views/html-quick-edit-product.php | 13 +- includes/admin/wc-meta-box-functions.php | 96 +- .../api/class-wc-rest-products-controller.php | 12 +- .../api/legacy/v1/class-wc-api-products.php | 22 +- .../api/legacy/v2/class-wc-api-products.php | 22 +- .../api/legacy/v3/class-wc-api-products.php | 22 +- includes/class-wc-ajax.php | 318 +----- includes/class-wc-cart.php | 153 ++- includes/class-wc-coupon.php | 6 +- includes/class-wc-emails.php | 2 +- includes/class-wc-order-item-product.php | 12 +- includes/class-wc-post-data.php | 6 +- includes/class-wc-product-factory.php | 2 +- includes/class-wc-product-simple.php | 2 +- includes/class-wc-product-variable.php | 16 +- includes/class-wc-product-variation.php | 948 ++++++------------ includes/class-wc-tax.php | 6 +- includes/cli/class-wc-cli-order.php | 2 +- includes/cli/class-wc-cli-product.php | 20 +- includes/wc-core-functions.php | 1 + includes/wc-deprecated-functions.php | 4 + includes/wc-formatting-functions.php | 13 + includes/wc-order-functions.php | 97 +- includes/wc-product-functions.php | 95 +- includes/wc-stock-functions.php | 157 +++ includes/wc-template-functions.php | 10 +- includes/wc-update-functions.php | 9 + includes/wc-user-functions.php | 4 +- templates/content-widget-product.php | 2 +- templates/emails/email-order-items.php | 2 +- templates/emails/plain/email-order-items.php | 2 +- templates/loop/add-to-cart.php | 2 +- templates/order/order-details.php | 2 +- templates/single-product-reviews.php | 2 +- .../single-product/add-to-cart/grouped.php | 2 +- .../single-product/add-to-cart/simple.php | 2 +- .../single-product/add-to-cart/variable.php | 2 +- .../variation-add-to-cart-button.php | 4 +- templates/single-product/price.php | 2 +- .../single-product/product-attributes.php | 4 +- templates/single-product/product-image.php | 2 +- .../single-product/product-thumbnails.php | 2 +- templates/single-product/stock.php | 2 +- templates/single-product/up-sells.php | 2 +- tests/unit-tests/api/product-reviews.php | 68 +- tests/unit-tests/api/product-variations.php | 38 +- tests/unit-tests/api/products.php | 2 +- tests/unit-tests/cart/cart.php | 84 +- tests/unit-tests/cart/functions.php | 20 +- tests/unit-tests/coupon/coupon.php | 32 +- tests/unit-tests/customer/customer.php | 8 +- tests/unit-tests/product/crud.php | 72 +- tests/unit-tests/product/functions.php | 2 - tests/unit-tests/product/product-simple.php | 52 +- 72 files changed, 2004 insertions(+), 2539 deletions(-) create mode 100644 includes/wc-stock-functions.php diff --git a/assets/css/admin.css b/assets/css/admin.css index dd4aac0ef29..7e0094fa705 100644 --- a/assets/css/admin.css +++ b/assets/css/admin.css @@ -1 +1 @@ -@charset "UTF-8";.select2-container .select2-choice,.select2-results .select2-result-label{-webkit-user-select:none;-moz-user-select:none;-webkit-touch-callout:none}.button.wc-reload::after,.woocommerce-help-tip::after{speak:none;font-variant:normal;text-transform:none;top:0}@-webkit-keyframes spin{100%{-webkit-transform:rotate(360deg)}}@-moz-keyframes spin{100%{-moz-transform:rotate(360deg)}}@keyframes spin{100%{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}.select2-container{margin:0;position:relative;display:block!important;zoom:1;vertical-align:middle}.select2-container,.select2-drop,.select2-search,.select2-search input{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.select2-container .select2-choice{display:block;padding:0 0 0 8px;overflow:hidden;position:relative;border:1px solid #ccc;white-space:nowrap;color:#444;text-decoration:none;border-radius:3px;background-clip:padding-box;-ms-user-select:none;user-select:none;background-color:#fff;font-weight:400}html[dir=rtl] .select2-container .select2-choice{padding:0 8px 0 0}.select2-container.select2-drop-above .select2-choice{border-bottom-color:#ccc;border-radius:0 0 4px 4px}.select2-container.select2-allowclear .select2-choice .select2-chosen{margin-right:42px}.select2-container .select2-choice>.select2-chosen{margin-right:26px;display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;float:none;width:auto}html[dir=rtl] .select2-container .select2-choice>.select2-chosen{margin-left:26px;margin-right:0}.select2-container .select2-choice abbr{display:none;width:12px;height:12px;position:absolute;right:24px;top:5px;font-size:1px;text-decoration:none;border:0;background:url(../images/select2.png) right top no-repeat;cursor:pointer;outline:0}.select2-container.select2-allowclear .select2-choice abbr{display:inline-block}.select2-container .select2-choice abbr:hover{background-position:right -11px;cursor:pointer}.select2-drop-mask{border:0;margin:0;padding:0;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:9998;background-color:#fff;filter:alpha(opacity=0)}.select2-drop{width:100%;margin-top:-1px;position:absolute;top:100%;background:#fff;color:#000;border:1px solid #ccc;border-top:0;border-radius:0 0 3px 3px}.select2-drop.select2-drop-above{margin-top:1px;border-top:1px solid #ccc;border-bottom:0;border-radius:3px 3px 0 0}.select2-drop-active{border:1px solid #666;border-top:none}.select2-drop.select2-drop-above.select2-drop-active{border-top:1px solid #666}.select2-drop-auto-width{border-top:1px solid #ccc;width:auto}.select2-drop-auto-width .select2-search{padding-top:4px}.select2-container .select2-choice .select2-arrow{display:inline-block;width:18px;height:100%;position:absolute;right:0;top:0;border-radius:0 3px 3px 0;background-clip:padding-box}html[dir=rtl] .select2-container .select2-choice .select2-arrow{left:0;right:auto;border-radius:3px 0 0 3px}.select2-container .select2-choice .select2-arrow b{display:block;width:100%;height:100%;position:relative}.select2-container .select2-choice .select2-arrow b:after{position:absolute;display:block;content:"";top:50%;left:50%;border:4px solid transparent;border-top-color:#666;margin-left:-7px;margin-top:-2px}.select2-search{display:inline-block;width:100%;margin:0;padding-left:4px;padding-right:4px;position:relative;z-index:10000;white-space:nowrap;padding-bottom:4px}.select2-search input{width:100%;height:auto!important;padding:4px 20px 4px 5px!important;margin:0;outline:0;font-family:sans-serif;font-size:1em;border:1px solid #ccc;-webkit-box-shadow:none;box-shadow:none;background:url(../images/select2.png) 100% -22px no-repeat #fff}html[dir=rtl] .select2-search input{padding:4px 5px 4px 20px;background:url(../images/select2.png) -37px -22px no-repeat #fff}.select2-drop.select2-drop-above .select2-search input{margin-top:4px}.select2-search input.select2-active{background:url(../images/select2-spinner.gif) 100% no-repeat #fff}.select2-container-active .select2-choice,.select2-container-active .select2-choices{border:1px solid #666;outline:0}.select2-dropdown-open .select2-choice{border-bottom-color:transparent;-webkit-box-shadow:0 1px 0 #fff inset;box-shadow:0 1px 0 #fff inset;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-dropdown-open .select2-choice .select2-arrow b:after{border-top-color:transparent;border-bottom-color:#666;margin-top:-6px}.select2-dropdown-open.select2-drop-above .select2-choice,.select2-dropdown-open.select2-drop-above .select2-choices{border:1px solid #666;border-top-color:transparent}.select2-dropdown-open .select2-choice .select2-arrow{background:0 0;border-left:none;filter:none}html[dir=rtl] .select2-dropdown-open .select2-choice .select2-arrow{border-right:none}.select2-dropdown-open .select2-choice .select2-arrow b{background-position:-18px 1px}html[dir=rtl] .select2-dropdown-open .select2-choice .select2-arrow b{background-position:-16px 1px}.select2-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.select2-results{max-height:200px;padding:4px;margin:0;position:relative;overflow-x:hidden;overflow-y:auto;-webkit-tap-highlight-color:transparent;background:#fafafa}html[dir=rtl] .select2-results{padding:0 4px 0 0;margin:4px 0 4px 4px}.select2-results ul.select2-result-sub{margin:0;padding-left:0}.select2-results li{list-style:none;display:list-item;background-image:none;margin:3px 0}.select2-results li.select2-result-with-children>.select2-result-label{font-weight:700}.select2-results .select2-result-label{padding:5px 7px;margin:0;cursor:pointer;min-height:1em;-ms-user-select:none;user-select:none}.select2-results-dept-1 .select2-result-label{padding-left:20px}.select2-results-dept-2 .select2-result-label{padding-left:40px}.select2-results-dept-3 .select2-result-label{padding-left:60px}.select2-results-dept-4 .select2-result-label{padding-left:80px}.select2-results-dept-5 .select2-result-label{padding-left:100px}.select2-results-dept-6 .select2-result-label{padding-left:110px}.select2-results-dept-7 .select2-result-label{padding-left:120px}.select2-results .select2-highlighted{background:#f1f1f1;color:#000;border-radius:3px}.select2-results li em{background:#feffde;font-style:normal}.select2-results .select2-highlighted em{background:0 0}.select2-results .select2-highlighted ul{background:#fff;color:#000}.select2-results .select2-ajax-error,.select2-results .select2-no-results,.select2-results .select2-searching,.select2-results .select2-selection-limit{background:#f4f4f4;display:list-item;padding-left:5px}.select2-results .select2-disabled.select2-highlighted{color:#666;background:#f4f4f4;display:list-item;cursor:default}.select2-results .select2-disabled{background:#f4f4f4;display:list-item;cursor:default}.select2-results .select2-selected{display:none}.select2-more-results.select2-active{background:url(../images/select2-spinner.gif) 100% no-repeat #f4f4f4}.select2-results .select2-ajax-error{background:rgba(255,50,50,.2)}.select2-more-results{background:#f4f4f4;display:list-item}.select2-container.select2-container-disabled .select2-choice{background-color:#f4f4f4;background-image:none;border:1px solid #ddd;cursor:default}.select2-container.select2-container-disabled .select2-choice .select2-arrow{background-color:#f4f4f4;background-image:none;border-left:0}.select2-container.select2-container-disabled .select2-choice abbr{display:none}.select2-container-multi .select2-choices{height:auto!important;height:1%;margin:0;padding:0 5px 0 0;position:relative;border:1px solid #ccc;cursor:text;overflow:hidden;background-color:#fff;min-height:26px}html[dir=rtl] .select2-container-multi .select2-choices{padding:0 0 0 5px}.select2-locked{padding:3px 5px!important}.select2-container-multi.select2-container-active .select2-choices{border:1px solid #666;outline:0}.select2-container-multi .select2-choices li{float:left;list-style:none}html[dir=rtl] .select2-container-multi .select2-choices li{float:right}.select2-container-multi .select2-choices .select2-search-field{margin:0;padding:0;white-space:nowrap}.select2-container-multi .select2-choices .select2-search-field:first-child{width:100%}.select2-container-multi .select2-choices .select2-search-field input{margin:1px 0;outline:0;border:0;-webkit-box-shadow:none;box-shadow:none;background:0 0!important}.select2-container-multi .select2-choices .select2-search-field input.select2-active{background:url(../images/select2-spinner.gif) 100% no-repeat #fff!important}.select2-default{color:#999!important}.select2-container-multi .select2-choices .select2-search-choice{padding:5px 8px 5px 24px;margin:3px 0 3px 5px;position:relative;line-height:15px;color:#333;cursor:default;border-radius:2px;background-clip:padding-box;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#e4e4e4}.select2-container-multi .ui-sortable .select2-search-choice{cursor:move}html[dir=rtl] .select2-container-multi .select2-choices .select2-search-choice{margin:3px 5px 3px 0;padding:5px 24px 5px 8px}.select2-container-multi .select2-choices .select2-search-choice .select2-chosen{cursor:default}.select2-container-multi .select2-choices .select2-search-choice-focus{background:#d4d4d4}.select2-search-choice-close{display:block;width:12px;height:13px;position:absolute;right:7px;top:6px;font-size:1px;outline:0;background:url(../images/select2.png) right top no-repeat}html[dir=rtl] .select2-search-choice-close{right:auto;left:7px}.select2-container-multi .select2-search-choice-close{left:7px}html[dir=rtl] .select2-container-multi .select2-search-choice-close{left:auto;right:7px}.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover,.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close{background-position:right -11px}.select2-container-multi.select2-container-disabled .select2-choices{background-color:#f4f4f4;background-image:none;border:1px solid #ddd;cursor:default}.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice{padding:3px 5px;border:1px solid #ddd;background-image:none;background-color:#f4f4f4}.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close{display:none;background:0 0}.select2-result-selectable .select2-match,.select2-result-unselectable .select2-match{text-decoration:underline}.select2-offscreen,.select2-offscreen:focus{clip:rect(0 0 0 0)!important;width:1px!important;height:1px!important;border:0!important;margin:0!important;padding:0!important;overflow:hidden!important;position:absolute!important;outline:0!important;left:0!important;top:0!important}.select2-display-none{display:none}.select2-measure-scrollbar{position:absolute;top:-10000px;left:-10000px;width:100px;height:100px;overflow:scroll}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-resolution:2dppx){.select2-search input{background-image:url(../images/select2x2.png)!important;background-repeat:no-repeat!important;background-size:60px 40px!important;background-position:100% -21px!important}}@font-face{font-family:star;src:url(../fonts/star.eot);src:url(../fonts/star.eot?#iefix) format("embedded-opentype"),url(../fonts/star.woff) format("woff"),url(../fonts/star.ttf) format("truetype"),url(../fonts/star.svg#star) format("svg");font-weight:400;font-style:normal}@font-face{font-family:WooCommerce;src:url(../fonts/WooCommerce.eot);src:url(../fonts/WooCommerce.eot?#iefix) format("embedded-opentype"),url(../fonts/WooCommerce.woff) format("woff"),url(../fonts/WooCommerce.ttf) format("truetype"),url(../fonts/WooCommerce.svg#WooCommerce) format("svg");font-weight:400;font-style:normal}.blockUI.blockOverlay::before{height:1em;width:1em;display:block;position:absolute;top:50%;left:50%;margin-left:-.5em;margin-top:-.5em;content:'';-webkit-animation:spin 1s ease-in-out infinite;-moz-animation:spin 1s ease-in-out infinite;animation:spin 1s ease-in-out infinite;background:url(../images/icons/loader.svg) center center;background-size:cover;line-height:1;text-align:center;font-size:2em;color:rgba(0,0,0,.75)}.wc_addons_wrap .addons-featured{max-width:1140px;margin:-1%}.wc_addons_wrap .addons-banner-block-item-icon,.wc_addons_wrap .addons-column-block-item-icon{align-items:center;display:flex;justify-content:center}.wc_addons_wrap .addons-banner-block{background:#fff;box-shadow:0 0 1px rgba(0,0,0,.2);margin:2% 1% 0;max-width:1140px;padding:20px}.wc_addons_wrap .addons-banner-block img{height:62px}.wc_addons_wrap .addons-banner-block p{margin:0 0 20px}.wc_addons_wrap .addons-banner-block-items{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-around;margin:0 -10px}.wc_addons_wrap .addons-banner-block-item{border:1px solid #e6e6e6;border-radius:3px;flex:1 1 200px;margin:10px;max-width:350px;min-width:200px;width:30%}.wc_addons_wrap .addons-banner-block-item-icon{background:#f7f7f7;height:143px}.wc_addons_wrap .addons-banner-block-item-content{display:flex;flex-direction:column;height:184px;justify-content:space-between;padding:24px}.wc_addons_wrap .addons-banner-block-item-content h3{margin-top:0}.wc_addons_wrap .addons-banner-block-item-content p{margin:0 0 auto}.wc_addons_wrap .addons-column-section{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-around}.wc_addons_wrap .addons-column{flex:1 1 200px;margin:0 1%;width:49.5%}.wc_addons_wrap .addons-column-block,.wc_addons_wrap .addons-small-dark-block,.wc_addons_wrap .addons-small-light-block{box-sizing:border-box;box-shadow:0 0 1px rgba(0,0,0,.2);margin:4% 0 0;padding:20px}.wc_addons_wrap .addons-column-block img{max-height:50px;max-width:50px}.wc_addons_wrap .addons-column-block,.wc_addons_wrap .addons-small-light-block{background:#fff}.wc_addons_wrap .addons-column-block-left{float:left}.wc_addons_wrap .addons-column-block-right{float:right}.wc_addons_wrap .addons-column-block-item{display:flex;border-top:2px solid #f9f9f9;flex-direction:row;flex-wrap:wrap;justify-content:space-between;margin:0 -20px;padding:20px}.wc_addons_wrap .addons-column-block-item-icon{background:#f7f7f7;border:1px solid #e6e6e6;height:100px;margin:0 10px 10px 0;width:100px}.wc_addons_wrap .addons-column-block-item-content{display:flex;flex:1 1 200px;flex-wrap:wrap;height:20%;justify-content:space-between;min-width:200px}.wc_addons_wrap .addons-column-block-item-content h2{float:left;margin-top:8px}.wc_addons_wrap .addons-column-block-item-content a{float:right}.wc_addons_wrap .addons-column-block-item-content p{float:left}.wc_addons_wrap .addons-small-dark-block{background-color:#54687d;text-align:center}.wc_addons_wrap .addons-small-dark-items{display:flex;flex-wrap:wrap;justify-content:space-around}.wc_addons_wrap .addons-small-dark-item{margin:0 0 20px}.wc_addons_wrap .addons-small-dark-block h1{color:#fff}.wc_addons_wrap .addons-small-dark-block p{color:#fafafa}.wc_addons_wrap .addons-small-dark-item-icon img{height:30px}.wc_addons_wrap .addons-small-dark-item a{margin:28px auto 0}.wc_addons_wrap .addons-small-light-block{display:flex;flex-wrap:wrap}.wc_addons_wrap .addons-small-light-block h1{margin-top:-12px}.wc_addons_wrap .addons-small-light-block p{margin-top:0}.wc_addons_wrap .addons-small-light-block img{height:225px;margin:0 0 0 -20px}.wc_addons_wrap .addons-small-light-block-content{display:flex;flex:1 1 100px;flex-direction:column;justify-content:space-around}.wc_addons_wrap .addons-small-light-block-buttons{display:flex;justify-content:space-between}.wc_addons_wrap .addons-small-light-block-content a{width:48%}.wc_addons_wrap .addons-button{border-radius:3px;cursor:pointer;display:block;height:37px;line-height:37px;text-align:center;text-decoration:none;width:124px}.wc_addons_wrap .addons-button-solid{background-color:#955a89;color:#fff}.wc_addons_wrap .addons-button-solid:hover{color:#fff;opacity:.8}.wc_addons_wrap .addons-button-outline-green{border:1px solid #73ae39;color:#73ae39}.wc_addons_wrap .addons-button-outline-green:hover{color:#73ae39;opacity:.8}.wc_addons_wrap .addons-button-outline-white{border:1px solid #fff;color:#fff}.wc_addons_wrap .addons-button-outline-white:hover{color:#fff;opacity:.8}.wc_addons_wrap .addons-button-installed{background:#e6e6e6;color:#3c3c3c}.wc_addons_wrap .addons-button-installed:hover{color:#3c3c3c;opacity:.8}@media only screen and (max-width:400px){.wc_addons_wrap .addons-featured{margin:-1% -5%}.wc_addons_wrap .addons-button,.wc_addons_wrap .addons-small-dark-item{width:100%}.wc_addons_wrap .addons-column-block-item-icon{background:0 0;border:none;height:75px;margin:0 10px 10px 0;width:75px}}.wc_addons_wrap .products{overflow:hidden}.wc_addons_wrap .products li{float:left;margin:0 1em 1em 0!important;padding:0;vertical-align:top;width:300px}.wc_addons_wrap .products li a{text-decoration:none;color:inherit;border:1px solid #ddd;display:block;min-height:220px;overflow:hidden;background:#f5f5f5;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),inset 0 -1px 0 rgba(0,0,0,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),inset 0 -1px 0 rgba(0,0,0,.1)}.wc_addons_wrap .products li a img{max-width:258px;max-height:24px;padding:17px 20px;display:block;margin:0;background:#fff;border-right:260px solid #fff}.wc_addons_wrap .products li a .price,.wc_addons_wrap .products li a img.extension-thumb+h3{display:none}.wc_addons_wrap .products li a h2,.wc_addons_wrap .products li a h3{margin:0!important;padding:20px!important;background:#fff}.wc_addons_wrap .products li a p{padding:20px!important;margin:0!important;border-top:1px solid #f1f1f1}.wc_addons_wrap .products li a:focus,.wc_addons_wrap .products li a:hover{background-color:#fff}.wc_addons_wrap .storefront{background:url(../images/storefront-bg.jpg) bottom right #f6f6f6;border:1px solid #ddd;padding:20px;overflow:hidden;zoom:1}.wc_addons_wrap .storefront img{width:278px;height:auto;float:left;margin:0 20px 0 0;box-shadow:0 1px 6px rgba(0,0,0,.1)}.wc_addons_wrap .storefront p{max-width:750px}.woocommerce-BlankState a.button-primary,.woocommerce-BlankState button.button-primary,.woocommerce-message a.button-primary,.woocommerce-message button.button-primary{background:#bb77ae;border-color:#a36597;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597;box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597;color:#fff;text-shadow:0 -1px 1px #a36597,1px 0 1px #a36597,0 1px 1px #a36597,-1px 0 1px #a36597}.woocommerce-BlankState a.button-primary:active,.woocommerce-BlankState a.button-primary:focus,.woocommerce-BlankState a.button-primary:hover,.woocommerce-BlankState button.button-primary:active,.woocommerce-BlankState button.button-primary:focus,.woocommerce-BlankState button.button-primary:hover,.woocommerce-message a.button-primary:active,.woocommerce-message a.button-primary:focus,.woocommerce-message a.button-primary:hover,.woocommerce-message button.button-primary:active,.woocommerce-message button.button-primary:focus,.woocommerce-message button.button-primary:hover{background:#a36597;border-color:#a36597;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597;box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597}.woocommerce-message{position:relative;border-left-color:#cc99c2!important;overflow:hidden}.woocommerce-message a.docs,.woocommerce-message a.skip{text-decoration:none!important}.woocommerce-message a.woocommerce-message-close{position:absolute;top:10px;right:10px;padding:10px 15px 10px 21px;font-size:13px;line-height:1.23076923;text-decoration:none}.woocommerce-message a.woocommerce-message-close::before{position:absolute;top:8px;left:0;-webkit-transition:all .1s ease-in-out;transition:all .1s ease-in-out}.woocommerce-message .twitter-share-button{margin-top:-3px;margin-left:3px;vertical-align:middle}#variable_product_options #message,#variable_product_options .notice{margin:10px}.clear{clear:both}#woocommerce-fields-bulk.inline-edit-col label,#woocommerce-fields.inline-edit-col{clear:left}.wrap.woocommerce div.error,.wrap.woocommerce div.updated{margin-top:10px}mark.amount{background:0 0;color:inherit}.simplify-commerce-banner{overflow:hidden}.simplify-commerce-banner img{float:right;padding:15px 0;margin-left:1em;width:200px}.woocommerce-help-tip{color:#666;display:inline-block;font-size:1.1em;font-style:normal;height:16px;line-height:16px;position:relative;vertical-align:middle;width:16px}.woocommerce-help-tip::after{font-family:Dashicons;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;left:0;width:100%;height:100%;text-align:center;content:"";cursor:help}h2 .woocommerce-help-tip{margin-top:-5px;margin-left:.25em}table.wc_status_table{margin-bottom:1em}table.wc_status_table h2{font-size:14px;margin:0}table.wc_status_table tr:nth-child(2n) td,table.wc_status_table tr:nth-child(2n) th{background:#fcfcfc}table.wc_status_table th{font-weight:700;padding:9px}table.wc_status_table td:first-child{width:33%}table.wc_status_table td.help{width:1em}table.wc_status_table td{padding:9px;font-size:1.1em}table.wc_status_table td mark{background:0 0}table.wc_status_table td mark.yes{color:#7ad03a}table.wc_status_table td mark.no{color:#999}table.wc_status_table td mark.error{color:#a00}table.wc_status_table td ul{margin:0}table.wc_status_table .help_tip{cursor:help}#debug-report{display:none;margin:10px 0;padding:0;position:relative}#debug-report textarea{font-family:monospace;width:100%;margin:0;height:300px;padding:20px;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;resize:none;font-size:12px;line-height:20px;outline:0}#log-viewer-select{padding:10px 0 8px;line-height:28px}#log-viewer-select h2 a{vertical-align:middle}#log-viewer{background:#fff;border:1px solid #e5e5e5;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.04);box-shadow:0 1px 1px rgba(0,0,0,.04);padding:5px 20px}#log-viewer pre{font-family:monospace;white-space:pre-wrap}.inline-edit-product.quick-edit-row .inline-edit-col-center,.inline-edit-product.quick-edit-row .inline-edit-col-right{float:right!important}#woocommerce-fields.inline-edit-col label.featured,#woocommerce-fields.inline-edit-col label.manage_stock{margin-left:10px}#woocommerce-fields.inline-edit-col .dimensions div{display:block;margin:.2em 0}#woocommerce-fields.inline-edit-col .dimensions div span.title{display:block;float:left;width:5em}#woocommerce-fields.inline-edit-col .dimensions div span.input-text-wrap{display:block;margin-left:5em}#woocommerce-fields.inline-edit-col .text{box-sizing:border-box;width:99%;float:left;margin:1px 1% 1px 1px}#woocommerce-fields.inline-edit-col .height,#woocommerce-fields.inline-edit-col .length,#woocommerce-fields.inline-edit-col .width{width:32.33%}#woocommerce-fields.inline-edit-col .height{margin-right:0}#woocommerce-fields-bulk.inline-edit-col .inline-edit-group label{clear:none;width:49%;margin:.2em 0}#woocommerce-fields-bulk.inline-edit-col .inline-edit-group.dimensions label{width:75%;max-width:75%}#woocommerce-fields-bulk.inline-edit-col .length,#woocommerce-fields-bulk.inline-edit-col .regular_price,#woocommerce-fields-bulk.inline-edit-col .sale_price,#woocommerce-fields-bulk.inline-edit-col .stock,#woocommerce-fields-bulk.inline-edit-col .weight{box-sizing:border-box;width:100%;margin-left:4.4em}#woocommerce-fields-bulk.inline-edit-col .height,#woocommerce-fields-bulk.inline-edit-col .length,#woocommerce-fields-bulk.inline-edit-col .width{box-sizing:border-box;width:25%}.column-coupon_code{line-height:2.25em}.column-coupon_code,ul.wc_coupon_list{margin:0;overflow:hidden;zoom:1;clear:both}ul.wc_coupon_list li{margin:0}ul.wc_coupon_list li.code{display:inline-block}ul.wc_coupon_list li.code::after{content:', '}ul.wc_coupon_list li.code:last-of-type::after{display:none}ul.wc_coupon_list li.code .tips{cursor:pointer}ul.wc_coupon_list_block{margin:0;padding-bottom:2px}ul.wc_coupon_list_block li{border-top:1px solid #fff;border-bottom:1px solid #ccc;line-height:2.5em;margin:0;padding:.5em 0}ul.wc_coupon_list_block li:first-child{border-top:0;padding-top:0}ul.wc_coupon_list_block li:last-child{border-bottom:0;padding-bottom:0}.button.wc-reload{text-indent:-9999px;position:relative;padding:0;height:28px;width:28px!important;display:inline-block}.button.wc-reload::after{font-family:Dashicons;font-weight:400;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;left:0;width:100%;height:100%;text-align:center;content:"";line-height:28px}#order_data h2,#order_data p.order_number{font-family:HelveticaNeue-Light,'Helvetica Neue Light','Helvetica Neue',sans-serif;font-weight:400}.tablenav .actions{overflow:visible}.tablenav .select2-container{float:left;max-width:200px;font-size:14px;vertical-align:middle;margin:1px 6px 1px 1px}#woocommerce-order-data .handlediv,#woocommerce-order-data .hndle{display:none}#woocommerce-order-data .inside{display:block!important}#order_data{padding:23px 24px 12px}#order_data h2{margin:0;font-size:21px;line-height:1.2;text-shadow:1px 1px 1px #fff;padding:0}#order_data h3{font-size:14px}#order_data h3,#order_data h4{color:#333;margin:1.33em 0 0}#order_data p{color:#777}#order_data p.order_number{margin:0;line-height:1.6em;font-size:16px}#order_data .order_data_column_container{clear:both}#order_data .order_data_column{width:32%;padding:0 2% 0 0;float:left}#order_data .order_data_column:last-child{padding-right:0}#order_data .order_data_column p{padding:0!important}#order_data .order_data_column .address strong{display:block}#order_data .order_data_column .form-field{float:left;width:48%;padding:0;margin:9px 0 0}#order_data .order_data_column .form-field label{display:block;padding:0 0 3px}#order_data .order_data_column .form-field input,#order_data .order_data_column .form-field select,#order_data .order_data_column .form-field textarea{width:100%}#order_data .order_data_column .form-field .select2-container{width:100%!important}#order_data .order_data_column .form-field .date-picker{width:50%}#order_data .order_data_column .form-field .hour,#order_data .order_data_column .form-field .minute{width:3.5em}#order_data .order_data_column .form-field small{display:block;margin:5px 0 0;color:#999}#order_data .order_data_column .form-field.last{float:right}#order_data .order_data_column .form-field-wide{width:100%;clear:both}#order_data .order_data_column .form-field-wide .wc-customer-search,#order_data .order_data_column .form-field-wide .wc-enhanced-select,#order_data .order_data_column .form-field-wide input,#order_data .order_data_column .form-field-wide select,#order_data .order_data_column .form-field-wide textarea{width:100%}#order_data .order_data_column p.none_set{color:#999}#order_data .order_data_column ._billing_address_1_field,#order_data .order_data_column ._billing_city_field,#order_data .order_data_column ._billing_country_field,#order_data .order_data_column ._billing_email_field,#order_data .order_data_column ._billing_first_name_field,#order_data .order_data_column ._shipping_address_1_field,#order_data .order_data_column ._shipping_city_field,#order_data .order_data_column ._shipping_country_field,#order_data .order_data_column ._shipping_first_name_field{float:left}#order_data .order_data_column ._billing_address_2_field,#order_data .order_data_column ._billing_last_name_field,#order_data .order_data_column ._billing_phone_field,#order_data .order_data_column ._billing_postcode_field,#order_data .order_data_column ._billing_state_field,#order_data .order_data_column ._shipping_address_2_field,#order_data .order_data_column ._shipping_last_name_field,#order_data .order_data_column ._shipping_postcode_field,#order_data .order_data_column ._shipping_state_field,#order_data .order_data_column .wc-customer-user label a,#order_data .order_data_column .wc-order-status label a{float:right}#order_data .order_data_column ._billing_company_field,#order_data .order_data_column ._shipping_company_field,#order_data .order_data_column ._transaction_id_field{clear:both;width:100%}#order_data .order_data_column ._billing_email_field{clear:left}#order_data .order_data_column div.edit_address{display:none;zoom:1;padding-right:1px}#order_data .order_data_column .billing-same-as-shipping,#order_data .order_data_column .load_customer_billing,#order_data .order_data_column .load_customer_shipping,#order_data .order_data_column a.edit_address{width:14px;height:0;padding:14px 0 0;margin:0 0 0 6px;overflow:hidden;position:relative;color:#999;border:0;float:right}#order_data .order_data_column .billing-same-as-shipping:focus,#order_data .order_data_column .billing-same-as-shipping:hover,#order_data .order_data_column .load_customer_billing:focus,#order_data .order_data_column .load_customer_billing:hover,#order_data .order_data_column .load_customer_shipping:focus,#order_data .order_data_column .load_customer_shipping:hover,#order_data .order_data_column a.edit_address:focus,#order_data .order_data_column a.edit_address:hover{color:#000}#order_data .order_data_column .billing-same-as-shipping::after,#order_data .order_data_column .load_customer_billing::after,#order_data .order_data_column .load_customer_shipping::after,#order_data .order_data_column a.edit_address::after{font-family:WooCommerce;position:absolute;top:0;left:0;text-align:center;vertical-align:top;line-height:14px;font-size:14px;font-weight:400;-webkit-font-smoothing:antialiased}#order_data .order_data_column .billing-same-as-shipping::after{content:'\e008'}#order_data .order_data_column .load_customer_billing::after,#order_data .order_data_column .load_customer_shipping::after{content:'\e03a'}#order_data .order_data_column a.edit_address::after{font-family:Dashicons;content:'\f464'}.order_actions{margin:0;overflow:hidden;zoom:1}.order_actions li{border-top:1px solid #fff;border-bottom:1px solid #ddd;padding:6px 0;margin:0;line-height:1.6em;float:left;width:50%;text-align:center}.order_actions li a{float:none;text-align:center;text-decoration:underline}.order_actions li.wide{width:auto;float:none;clear:both;padding:6px;text-align:left;overflow:hidden}.order_actions li #delete-action{line-height:25px;vertical-align:middle;text-align:left;float:left}.order_actions li .save_order{float:right}.order_actions li#actions{overflow:hidden}.order_actions li#actions .button{width:24px;box-sizing:border-box;float:right}.order_actions li#actions select{width:225px;box-sizing:border-box;float:left}#woocommerce-order-items .inside{margin:0;padding:0;background:#fefefe}#woocommerce-order-items .wc-order-data-row{border-bottom:1px solid #dfdfdf;padding:1.5em 2em;background:#f8f8f8;line-height:2em;text-align:right}#woocommerce-order-items .wc-order-data-row::after,#woocommerce-order-items .wc-order-data-row::before{content:' ';display:table}#woocommerce-order-items .wc-order-data-row::after{clear:both}#woocommerce-order-items .wc-order-data-row p{margin:0;line-height:2em}#woocommerce-order-items .wc-order-data-row .wc-used-coupons{text-align:left}#woocommerce-order-items .wc-order-data-row .wc-used-coupons .tips{display:inline-block}#woocommerce-order-items .wc-order-add-item{background:#fff;vertical-align:top;border-top:none}#woocommerce-order-items .wc-order-add-item .add_item_id,#woocommerce-order-items .wc-order-add-item .select2-container{vertical-align:top}#woocommerce-order-items .wc-order-add-item .add_item_id .search-field input,#woocommerce-order-items .wc-order-add-item .select2-container .search-field input{min-width:100px}#woocommerce-order-items .wc-order-add-item .select2-container{width:400px!important;text-align:left}#woocommerce-order-items .wc-order-add-item .calculate-action,#woocommerce-order-items .wc-order-add-item .cancel-action,#woocommerce-order-items .wc-order-add-item .save-action{float:left;margin-right:2px}#woocommerce-order-items .wc-used-coupons{float:left;width:50%}#woocommerce-order-items .wc-order-totals{float:right;width:50%;margin:0;padding:0;text-align:right}#woocommerce-order-items .wc-order-totals .amount{font-weight:700}#woocommerce-order-items .wc-order-totals .label{vertical-align:top}#woocommerce-order-items .wc-order-totals .total{font-size:1em!important;width:10em;margin:0 0 0 .5em;box-sizing:border-box}#woocommerce-order-items .wc-order-totals .total input[type=text]{width:96%;float:right}#woocommerce-order-items .wc-order-totals .refunded-total{color:#a00}#woocommerce-order-items .refund-actions{margin-top:5px;padding-top:12px;border-top:1px solid #dfdfdf}#woocommerce-order-items .refund-actions .button{float:right;margin-left:4px}#woocommerce-order-items .refund-actions .cancel-action,#woocommerce-order-items .wc-order-item-bulk-edit .cancel-action{float:left;margin-left:0}#woocommerce-order-items .add_meta{margin-left:0!important}#woocommerce-order-items h3 small{color:#999}#woocommerce-order-items .amount{white-space:nowrap}#woocommerce-order-items .add-items .description{margin-right:10px}#woocommerce-order-items .add-items .button{float:left;margin-right:.25em}#woocommerce-order-items .add-items .button-primary{float:none;margin-right:0}#woocommerce-order-items .inside{display:block!important}#woocommerce-order-items .handlediv,#woocommerce-order-items .hndle{display:none}#woocommerce-order-items .woocommerce_order_items_wrapper{margin:0;overflow-x:auto}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items{width:100%;background:#fff}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items thead th{text-align:left;padding:1em;font-weight:400;color:#999;background:#f8f8f8;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items thead th.sortable{cursor:pointer}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items thead th:last-child{padding-right:2em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items thead th:first-child{padding-left:2em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items thead th .wc-arrow{float:right;position:relative;margin-right:-1em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody th,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td{padding:1.5em 1em 1em;text-align:left;line-height:1.5em;vertical-align:top;border-bottom:1px solid #f8f8f8}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody th textarea,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td textarea{width:100%}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody th select,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td select{width:50%}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody th input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody th textarea,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td textarea{font-size:14px;padding:4px;color:#555}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody th:last-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td:last-child{padding-right:2em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody th:first-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td:first-child{padding-left:2em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody tr td{cursor:pointer}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody tr.selected{background:#f5ebf3}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody tr.selected td{border-color:#e6cce1;opacity:.8}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody tr:last-child td{border-bottom:1px solid #dfdfdf}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody tr:first-child td{border-top:8px solid #f8f8f8}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody#order_line_items tr:first-child td{border-top:none}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.thumb{text-align:left;width:38px;padding-bottom:1.5em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.thumb .wc-order-item-thumbnail{width:38px;height:38px;border:2px solid #e8e8e8;background:#f8f8f8;color:#ccc;position:relative;font-size:21px;display:block;text-align:center}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.thumb .wc-order-item-thumbnail::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;height:100%;text-align:center;content:"";width:38px;line-height:38px;display:block}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.thumb .wc-order-item-thumbnail img{width:100%;height:100%;margin:0;padding:0;position:relative}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.name .wc-order-item-sku,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.name .wc-order-item-variation{display:block;margin-top:.5em;font-size:.92em!important;color:#888}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item{min-width:200px}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .center,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .variation-id{text-align:center}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class{text-align:right}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class label{white-space:nowrap;color:#999;font-size:.833em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost label input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost label input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost label input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax label input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity label input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax label input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class label input{display:inline}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class input{width:70px;vertical-align:middle;text-align:right}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost select,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost select,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost select,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax select,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity select,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax select,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class select{width:85px;height:26px;vertical-align:middle;font-size:1em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input{display:inline-block;background:#fff;border:1px solid #ddd;box-shadow:inset 0 1px 2px rgba(0,0,0,.07);margin:1px 0;min-width:80px;overflow:hidden;line-height:1em;text-align:right}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input div.input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input div.input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input div.input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input div.input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input div.input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input div.input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input div.input{width:100%;box-sizing:border-box}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input div.input label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input div.input label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input div.input label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input div.input label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input div.input label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input div.input label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input div.input label{font-size:.75em;padding:4px 6px 0;color:#555;display:block}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input div.input input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input div.input input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input div.input input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input div.input input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input div.input input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input div.input input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input div.input input{width:100%;box-sizing:border-box;border:0;box-shadow:none;margin:0;padding:0 6px 4px;color:#555;background:0 0}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input div.input input::-webkit-input-placeholder,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input div.input input::-webkit-input-placeholder,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input div.input input::-webkit-input-placeholder,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input div.input input::-webkit-input-placeholder,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input div.input input::-webkit-input-placeholder,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input div.input input::-webkit-input-placeholder,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input div.input input::-webkit-input-placeholder{color:#ddd}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input div.input:first-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input div.input:first-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input div.input:first-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input div.input:first-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input div.input:first-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input div.input:first-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input div.input:first-child{border-bottom:1px dashed #ddd;background:#fff}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input div.input:first-child input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input div.input:first-child label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input div.input:first-child input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input div.input:first-child label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input div.input:first-child input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input div.input:first-child label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input div.input:first-child input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input div.input:first-child label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input div.input:first-child input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input div.input:first-child label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input div.input:first-child input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input div.input:first-child label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input div.input:first-child input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input div.input:first-child label{color:#ccc}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .view,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .view,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .view,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .view,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .view,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .view,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .view{white-space:nowrap}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .edit,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .edit,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .edit,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .edit,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .edit,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .edit,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .edit{text-align:left}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost del,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost del,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost del,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax del,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity del,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax del,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class del,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class small.times{font-size:.92em!important;color:#888}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-taxes{margin:0}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-refund-fields label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-taxes label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-refund-fields label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-taxes label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-refund-fields label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-taxes label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-refund-fields label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-taxes label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-refund-fields label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-taxes label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-refund-fields label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-taxes label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-refund-fields label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-taxes label{display:block}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-discount{display:block;margin-top:.5em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class small.times{margin-right:.25em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity{text-align:center}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity input{text-align:center;width:50px}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items span.subtotal{opacity:.5}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.tax_class,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items th.tax_class{text-align:left}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .calculated{border-color:#ae8ca2;border-style:dotted}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta{width:100%}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta{margin:.5em 0 0;font-size:.92em!important;color:#888}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.fee .thumb div::before,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.refund .thumb div::before,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.shipping .thumb div::before{color:#ccc;top:0;left:0;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;text-align:center;font-family:WooCommerce}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr th,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr th{border:0;padding:0 4px .5em 0;line-height:1.5em;width:20%}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr td,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr td{padding:0 4px .5em 0;border:0;line-height:1.5em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr td input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr td input{width:100%;margin:0;position:relative;border-bottom:0;box-shadow:none}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .refund_by,ul.order_notes li p.meta .exact-date{border-bottom:1px dotted #999}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr td textarea,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr td textarea{width:100%;height:4em;margin:0;box-shadow:none}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr td input:focus+textarea,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr td input:focus+textarea{border-top-color:#999}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr td p,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr td p{margin:0 0 .5em;line-height:1.5em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr td p:last-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr td p:last-child{margin:0}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.fee .thumb div{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;font-size:1.5em;line-height:1em;vertical-align:middle;margin:0}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.fee .thumb div::before{line-height:1;margin:0;text-indent:0;position:absolute;width:100%;height:100%;content:""}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.refund .thumb div{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;font-size:1.5em;line-height:1em;vertical-align:middle;margin:0}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.refund .thumb div::before{line-height:1;margin:0;text-indent:0;position:absolute;width:100%;height:100%;content:""}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.shipping .thumb div{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;font-size:1.5em;line-height:1em;vertical-align:middle;margin:0}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.shipping .thumb div::before{line-height:1;margin:0;text-indent:0;position:absolute;width:100%;height:100%;content:""}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.shipping .shipping_method,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.shipping .shipping_method_name{width:100%;margin:0 0 .5em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items th.line_tax{white-space:nowrap}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.line_tax .delete-order-tax,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items th.line_tax .delete-order-tax{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;float:right;font-size:14px;visibility:hidden;margin:3px -18px 0 0}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.line_tax .delete-order-tax::before,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items th.line_tax .delete-order-tax::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";color:#999}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.line_tax .delete-order-tax:hover::before,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items th.line_tax .delete-order-tax:hover::before{color:#a00}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.line_tax:hover .delete-order-tax,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items th.line_tax:hover .delete-order-tax{visibility:visible}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items small.refunded{display:block;color:#a00;white-space:nowrap;margin-top:.5em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items small.refunded::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;text-indent:0;width:100%;height:100%;text-align:center;content:"";position:relative;top:auto;left:auto;margin:-1px 4px 0 0;vertical-align:middle;line-height:1em}#woocommerce-order-items .wc-order-edit-line-item{padding-left:0}#woocommerce-order-items .wc-order-edit-line-item-actions{width:44px;text-align:right;padding-left:0;vertical-align:middle}#woocommerce-order-items .wc-order-edit-line-item-actions a{color:#ccc;display:inline-block;cursor:pointer;padding:0 0 .5em;margin:0 0 0 12px;vertical-align:middle;text-decoration:none;line-height:16px;width:16px;overflow:hidden}#woocommerce-order-items .wc-order-edit-line-item-actions a::before{margin:0;padding:0;font-size:16px;width:16px;height:16px}#woocommerce-order-items .wc-order-edit-line-item-actions .delete-order-item::before,#woocommerce-order-items .wc-order-edit-line-item-actions .delete_refund::before,#woocommerce-order-items .wc-order-edit-line-item-actions .edit-order-item::before{font-family:Dashicons;-webkit-font-smoothing:antialiased;top:0;left:0;width:100%;height:100%;margin:0;text-align:center;position:relative;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;text-indent:0}#woocommerce-order-items .wc-order-edit-line-item-actions a:hover::before{color:#999}#woocommerce-order-items .wc-order-edit-line-item-actions a:first-child{margin-left:0}#woocommerce-order-items .wc-order-edit-line-item-actions .edit-order-item::before{content:""}#woocommerce-order-items .wc-order-edit-line-item-actions .delete-order-item::before,#woocommerce-order-items .wc-order-edit-line-item-actions .delete_refund::before{content:""}#woocommerce-order-items .wc-order-edit-line-item-actions .delete-order-item:hover::before,#woocommerce-order-items .wc-order-edit-line-item-actions .delete_refund:hover::before{color:#a00}#woocommerce-order-items tbody tr .wc-order-edit-line-item-actions{visibility:hidden}#woocommerce-order-items tbody tr:hover .wc-order-edit-line-item-actions{visibility:visible}#woocommerce-order-items .wc-order-totals .wc-order-edit-line-item-actions{width:1.5em;visibility:visible!important}#woocommerce-order-items .wc-order-totals .wc-order-edit-line-item-actions a{padding:0}#woocommerce-order-downloads .buttons{float:left;padding:0;margin:0;vertical-align:top}#woocommerce-order-downloads .buttons .add_item_id,#woocommerce-order-downloads .buttons .select2-container{width:400px!important;margin-right:9px;vertical-align:top;float:left}#woocommerce-order-downloads .buttons button{margin:2px 0 0}#woocommerce-order-downloads h3 small{color:#999}#poststuff #woocommerce-order-actions .inside{margin:0;padding:0}#poststuff #woocommerce-order-actions .inside ul.order_actions li{padding:6px 10px;box-sizing:border-box}#poststuff #woocommerce-order-actions .inside ul.order_actions li:last-child{border-bottom:0}#poststuff #woocommerce-order-notes .inside{margin:0;padding:0}#poststuff #woocommerce-order-notes .inside ul.order_notes li{padding:0 10px}#woocommerce_customers p.search-box{margin:6px 0 4px;float:left}#woocommerce_customers .tablenav{float:right;clear:none}.widefat.customers td{vertical-align:middle;padding:4px 7px}.widefat .column-order_title{width:15%}.widefat .column-order_title time{display:block;color:#999;margin:3px 0}.widefat .column-orders,.widefat .column-paying,.widefat .column-spent{text-align:center;width:8%}.widefat .column-last_order{width:11%}.widefat .column-order_actions,.widefat .column-user_actions,.widefat .column-wc_actions{width:110px}.widefat .column-order_actions a.button,.widefat .column-user_actions a.button,.widefat .column-wc_actions a.button{float:left;margin:0 4px 2px 0;cursor:pointer;padding:3px 4px;height:auto}.widefat .column-order_actions a.button img,.widefat .column-user_actions a.button img,.widefat .column-wc_actions a.button img{display:block;width:12px;height:auto}.widefat small.meta{display:block;color:#999;font-size:inherit;margin:3px 0}.widefat .column-order_date,.widefat .column-order_total{width:9%}.widefat .column-order_status{width:45px;text-align:center}.widefat .column-order_status mark{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;background:0 0;font-size:1.4em;margin:0 auto}.widefat .column-order_status mark.cancelled::after,.widefat .column-order_status mark.completed::after,.widefat .column-order_status mark.failed::after,.widefat .column-order_status mark.on-hold::after,.widefat .column-order_status mark.pending::after,.widefat .column-order_status mark.processing::after,.widefat .column-order_status mark.refunded::after{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}.column-customer_message .note-on::after,.column-order_notes .note-on::after{speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;top:0;left:0;text-align:center;line-height:16px;font-family:WooCommerce}.widefat .column-order_status mark.pending::after{content:'\e012';color:#ffba00}.widefat .column-order_status mark.completed::after{content:'\e015';color:#2ea2cc}.widefat .column-order_status mark.on-hold::after{content:'\e033';color:#999}.widefat .column-order_status mark.failed::after{content:'\e016';color:#d0c21f}.widefat .column-order_status mark.cancelled::after{content:'\e013';color:#a00}.widefat .column-order_status mark.processing::after{content:'\e011';color:#73a724}.widefat .column-order_status mark.refunded::after{content:'\e014';color:#999}.widefat td.column-order_status{padding-top:9px}.column-customer_message .note-on{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;margin:0 auto;color:#999}.column-customer_message .note-on::after{margin:0;text-indent:0;position:absolute;width:100%;height:100%;content:""}.column-order_notes .note-on{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;margin:0 auto;color:#999}.column-order_notes .note-on::after{margin:0;text-indent:0;position:absolute;width:100%;height:100%;content:""}.order_actions .complete,.order_actions .processing,.order_actions .view{display:block;text-indent:-9999px;position:relative;padding:0!important;height:2em!important;width:2em}.order_actions .processing::after{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";line-height:1.85}.order_actions .complete::after,.order_actions .view::after{font-family:Dashicons;text-indent:0;position:absolute;width:100%;height:100%;left:0;line-height:1.85;margin:0;text-align:center;font-weight:400;top:0;speak:none;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased}.order_actions .complete::after{content:""}.order_actions .view::after{content:""}.user_actions .edit,.user_actions .link,.user_actions .refresh,.user_actions .view{display:block;text-indent:-9999px;position:relative;padding:0!important;height:2em!important;width:2em}.user_actions .edit::after,.user_actions .link::after,.user_actions .refresh::after,.user_actions .view::after{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";line-height:1.85}.user_actions .edit::after{font-family:Dashicons;content:'\f464'}.user_actions .link::after{content:'\e00d'}.user_actions .view::after{content:'\e010'}.user_actions .refresh::after{content:'\e031'}.attributes-table td,.attributes-table th{width:15%;vertical-align:top}.attributes-table .attribute-terms{width:32%}.attributes-table .attribute-actions{width:2em}.attributes-table .attribute-actions .configure-terms{display:block;text-indent:-9999px;position:relative;padding:0!important;height:2em!important;width:2em}.attributes-table .attribute-actions .configure-terms::after{speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";font-family:Dashicons;line-height:1.85}ul.order_notes{padding:2px 0 0}ul.order_notes li .note_content{padding:10px;background:#efefef;position:relative}ul.order_notes li .note_content p{margin:0;padding:0;word-wrap:break-word}ul.order_notes li p.meta{padding:10px;color:#999;margin:0;font-size:11px}ul.order_notes li a.delete_note{color:#a00}table.wp-list-table .row-actions,table.wp-list-table span.na{color:#999}ul.order_notes li .note_content::after{content:'';display:block;position:absolute;bottom:-10px;left:20px;width:0;height:0;border-width:10px 10px 0 0;border-style:solid;border-color:#efefef transparent}ul.order_notes li.customer-note .note_content{background:#a7cedc}ul.order_notes li.customer-note .note_content::after{border-color:#a7cedc transparent}ul.order_notes li.system-note .note_content{background:#d7cad2}ul.order_notes li.system-note .note_content::after{border-color:#d7cad2 transparent}.add_note{border-top:1px solid #ddd;padding:10px 10px 0}.add_note h4{margin-top:5px!important}.add_note #add_order_note{width:100%;height:50px}table.wp-list-table .column-thumb{width:52px;text-align:center;white-space:nowrap}table.wp-list-table .column-name{width:22%}table.wp-list-table .column-product_cat,table.wp-list-table .column-product_tag{width:11%!important}table.wp-list-table .column-featured,table.wp-list-table .column-product_type{width:48px;text-align:left!important}table.wp-list-table .column-customer_message,table.wp-list-table .column-order_notes{width:48px;text-align:center}table.wp-list-table .column-customer_message img,table.wp-list-table .column-order_notes img{margin:0 auto;padding-top:0!important}table.wp-list-table .manage-column.column-featured img,table.wp-list-table .manage-column.column-product_type img{padding-left:2px}table.wp-list-table .column-price .woocommerce-price-suffix{display:none}table.wp-list-table img{margin:1px 2px}table.wp-list-table td.column-thumb img{margin:0;width:auto;height:auto;max-width:40px;max-height:40px;vertical-align:middle}table.wp-list-table .column-is_in_stock{text-align:left!important}table.wp-list-table span.wc-featured,table.wp-list-table span.wc-image,table.wp-list-table span.wc-type{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;margin:0 auto}table.wp-list-table span.wc-featured::before,table.wp-list-table span.wc-image::before,table.wp-list-table span.wc-type::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:""}table.wp-list-table span.wc-featured{margin:0;cursor:pointer}table.wp-list-table span.wc-featured::before{content:'\f155'}table.wp-list-table span.wc-featured.not-featured::before{content:'\f154'}table.wp-list-table td.column-featured span.wc-featured{font-size:1.6em}table.wp-list-table span.wc-type{margin:0}table.wp-list-table span.wc-type::before{font-family:WooCommerce;content:'\e006'}table.wp-list-table span.product-type{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;font-size:1.6em}table.wp-list-table span.product-type::before{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:""}table.wp-list-table span.product-type.grouped::before{content:'\e002'}table.wp-list-table span.product-type.external::before{content:'\e034'}table.wp-list-table span.product-type.variable::before{content:'\e003'}table.wp-list-table span.product-type.downloadable::before{content:'\e001'}table.wp-list-table span.product-type.virtual::before{content:'\e000'}table.wp-list-table mark.instock{font-weight:700;color:#7ad03a;background:0 0;line-height:1}table.wp-list-table mark.outofstock{font-weight:700;color:#a44;background:0 0;line-height:1}table.wp-list-table .notes_head,table.wp-list-table .order-notes_head,table.wp-list-table .status_head{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;margin:0 auto}table.wp-list-table .notes_head::after,table.wp-list-table .order-notes_head::after,table.wp-list-table .status_head::after{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}table.wc_emails .wc-email-settings-table-name,table.wc_emails td.name,table.wc_gateways .wc-email-settings-table-name,table.wc_gateways td.name,table.wc_shipping .wc-email-settings-table-name,table.wc_shipping td.name{font-weight:700}table.wp-list-table .order-notes_head::after{content:'\e028'}table.wp-list-table .notes_head::after{content:'\e026'}table.wp-list-table .status_head::after{content:'\e011'}table.wp-list-table .column-order_items{width:12%}table.wp-list-table .column-order_items table.order_items{width:100%;margin:3px 0 0;padding:0;display:none}table.wp-list-table .column-order_items table.order_items td{border:0;margin:0;padding:0 0 3px}table.wp-list-table .column-order_items table.order_items td.qty{color:#999;padding-right:6px;text-align:left}mark.notice{background:#fff;color:#a00;margin:0 0 0 10px}a.export_rates,a.import_rates{float:right;margin-left:9px;margin-top:-2px;margin-bottom:0}#rates-search{float:right}#rates-search input.wc-tax-rates-search-field{padding:4px 8px;font-size:1.2em}#rates-pagination{float:right;margin-right:.5em}#rates-pagination .tablenav{margin:0}table.wc_input_table,table.wc_tax_rates{width:100%}table.wc_input_table span.tips,table.wc_tax_rates span.tips{color:#2ea2cc}table.wc_input_table th,table.wc_tax_rates th{white-space:nowrap;padding:10px}table.wc_input_table td,table.wc_tax_rates td{padding:0;border-right:1px solid #dfdfdf;border-bottom:1px solid #dfdfdf;border-top:0;background:#fff;cursor:default}table.wc_input_table td input[type=text],table.wc_input_table td input[type=number],table.wc_tax_rates td input[type=text],table.wc_tax_rates td input[type=number]{width:100%;padding:8px 10px;margin:0;border:0;outline:0;background:0 0}table.wc_input_table td input[type=text]:focus,table.wc_input_table td input[type=number]:focus,table.wc_tax_rates td input[type=text]:focus,table.wc_tax_rates td input[type=number]:focus{outline:0;box-shadow:none}table.wc_input_table td.apply_to_shipping,table.wc_input_table td.compound,table.wc_tax_rates td.apply_to_shipping,table.wc_tax_rates td.compound{padding:5px 7px;vertical-align:middle}table.wc_input_table td.apply_to_shipping input,table.wc_input_table td.compound input,table.wc_tax_rates td.apply_to_shipping input,table.wc_tax_rates td.compound input{width:auto;padding:0}table.wc_input_table td:last-child,table.wc_tax_rates td:last-child{border-right:0}table.wc_input_table tr.current td,table.wc_tax_rates tr.current td{background-color:#fefbcc}table.wc_input_table .cost,table.wc_input_table .cost input,table.wc_input_table .item_cost,table.wc_input_table .item_cost input,table.wc_tax_rates .cost,table.wc_tax_rates .cost input,table.wc_tax_rates .item_cost,table.wc_tax_rates .item_cost input{text-align:right}table.wc_input_table th.sort,table.wc_tax_rates th.sort{width:17px;padding:0 4px}table.wc_input_table td.sort,table.wc_tax_rates td.sort{padding:0 4px}table.wc_input_table .ui-sortable:not(.ui-sortable-disabled) td.sort,table.wc_tax_rates .ui-sortable:not(.ui-sortable-disabled) td.sort{cursor:move;font-size:15px;background:#f9f9f9;text-align:center;vertical-align:middle}table.wc_input_table .ui-sortable:not(.ui-sortable-disabled) td.sort::before,table.wc_tax_rates .ui-sortable:not(.ui-sortable-disabled) td.sort::before{content:'\f333';font-family:Dashicons;text-align:center;line-height:1;color:#999;display:block;width:17px;float:left;height:100%}table.wc_input_table .ui-sortable:not(.ui-sortable-disabled) td.sort:hover::before,table.wc_tax_rates .ui-sortable:not(.ui-sortable-disabled) td.sort:hover::before{color:#333}table.wc_input_table .button,table.wc_tax_rates .button{float:left;margin-right:5px}table.wc_input_table .export,table.wc_input_table .import,table.wc_tax_rates .export,table.wc_tax_rates .import{float:right;margin-right:0;margin-left:5px}table.wc_input_table span.tips,table.wc_tax_rates span.tips{padding:0 3px}table.wc_input_table .pagination,table.wc_tax_rates .pagination{float:right}table.wc_input_table .pagination .button,table.wc_tax_rates .pagination .button{margin-left:5px;margin-right:0}table.wc_input_table .pagination .current,table.wc_tax_rates .pagination .current{background:#bbb;text-shadow:none}table.wc_input_table tr:last-child td,table.wc_tax_rates tr:last-child td{border-bottom:0}table.wc_emails,table.wc_gateways,table.wc_shipping{position:relative}table.wc_emails td,table.wc_gateways td,table.wc_shipping td{vertical-align:middle;padding:7px;line-height:2em}table.wc_emails tr:nth-child(odd) td,table.wc_gateways tr:nth-child(odd) td,table.wc_shipping tr:nth-child(odd) td{background:#f9f9f9}table.wc_emails th,table.wc_gateways th,table.wc_shipping th{padding:9px 7px!important;vertical-align:middle}table.wc_emails .settings,table.wc_gateways .settings,table.wc_shipping .settings{text-align:right}table.wc_emails .default,table.wc_emails .radio,table.wc_emails .status,table.wc_gateways .default,table.wc_gateways .radio,table.wc_gateways .status,table.wc_shipping .default,table.wc_shipping .radio,table.wc_shipping .status{text-align:center}table.wc_emails .default .tips,table.wc_emails .radio .tips,table.wc_emails .status .tips,table.wc_gateways .default .tips,table.wc_gateways .radio .tips,table.wc_gateways .status .tips,table.wc_shipping .default .tips,table.wc_shipping .radio .tips,table.wc_shipping .status .tips{margin:0 auto}table.wc_emails .default input,table.wc_emails .radio input,table.wc_emails .status input,table.wc_gateways .default input,table.wc_gateways .radio input,table.wc_gateways .status input,table.wc_shipping .default input,table.wc_shipping .radio input,table.wc_shipping .status input{margin:0}table.wc_emails th.sort,table.wc_gateways th.sort,table.wc_shipping th.sort{width:28px;padding:0}table.wc_emails td.sort,table.wc_gateways td.sort,table.wc_shipping td.sort{padding:0 7px;cursor:move;font-size:15px;text-align:center;vertical-align:middle}table.wc_emails td.sort::before,table.wc_gateways td.sort::before,table.wc_shipping td.sort::before{content:'\f333';font-family:Dashicons;text-align:center;line-height:1;color:#999;display:block;width:17px;float:left;height:100%}table.wc_emails .wc-email-settings-table-name span,table.wc_gateways .wc-email-settings-table-name span,table.wc_shipping .wc-email-settings-table-name span{font-weight:400;color:#999;margin:0 0 0 4px!important}table.wc_emails .wc-email-settings-table-status,table.wc_gateways .wc-email-settings-table-status,table.wc_shipping .wc-email-settings-table-status{text-align:center;width:1em}table.wc_emails .wc-email-settings-table-status .tips,table.wc_gateways .wc-email-settings-table-status .tips,table.wc_shipping .wc-email-settings-table-status .tips{margin:0 auto}table.wc_emails .wc-email-settings-table-actions a,table.wc_gateways .wc-email-settings-table-actions a,table.wc_shipping .wc-email-settings-table-actions a{display:block;text-indent:-9999px;position:relative;padding:0!important;height:2em!important;width:2em}table.wc_emails .wc-email-settings-table-actions a::after,table.wc_gateways .wc-email-settings-table-actions a::after,table.wc_shipping .wc-email-settings-table-actions a::after{speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";font-family:Dashicons;line-height:1.85}.wc-shipping-zone-settings th{padding:24px 24px 24px 0}.wc-shipping-zone-settings td.forminp{padding:15px 10px}.wc-shipping-zone-settings td.forminp input,.wc-shipping-zone-settings td.forminp textarea{padding:8px;width:448px;max-width:100%!important}.wc-shipping-zone-settings td.forminp .wc-shipping-zone-region-select{width:448px;max-width:100%!important}.wc-shipping-zone-settings td.forminp .wc-shipping-zone-region-select .select2-choices{padding:8px 8px 4px;border-color:#DDD;min-height:0;line-height:1}.wc-shipping-zone-settings td.forminp .wc-shipping-zone-region-select .select2-choices input{padding:0}.wc-shipping-zone-settings td.forminp .wc-shipping-zone-region-select .select2-choices li{margin:0 4px 4px 0}.wc-shipping-zone-settings td.forminp .select2-container-active .select2-choices{border-color:#777}.wc-shipping-zone-settings .wc-shipping-zone-postcodes-toggle{margin:0;font-size:.9em;text-decoration:underline}.wc-shipping-zone-settings .wc-shipping-zone-postcodes-toggle+.wc-shipping-zone-postcodes{display:none}.wc-shipping-zone-settings .wc-shipping-zone-postcodes textarea{margin:10px 0}.wc-shipping-zone-settings .wc-shipping-zone-postcodes .description{font-size:.9em;color:#999}table tr table.wc-shipping-zone-methods tr .row-actions,table tr:hover table.wc-shipping-zone-methods tr .row-actions{position:relative}table tr table.wc-shipping-zone-methods tr:hover .row-actions,table tr:hover table.wc-shipping-zone-methods tr:hover .row-actions{position:static}table.wc-shipping-classes td,table.wc-shipping-classes th,table.wc-shipping-zone-methods td,table.wc-shipping-zone-methods th,table.wc-shipping-zones td,table.wc-shipping-zones th{vertical-align:top;line-height:24px;padding:1em!important;font-size:14px;background:#fff}table.wc-shipping-classes td li,table.wc-shipping-classes th li,table.wc-shipping-zone-methods td li,table.wc-shipping-zone-methods th li,table.wc-shipping-zones td li,table.wc-shipping-zones th li{line-height:24px;font-size:14px}table.wc-shipping-classes td .woocommerce-help-tip,table.wc-shipping-classes th .woocommerce-help-tip,table.wc-shipping-zone-methods td .woocommerce-help-tip,table.wc-shipping-zone-methods th .woocommerce-help-tip,table.wc-shipping-zones td .woocommerce-help-tip,table.wc-shipping-zones th .woocommerce-help-tip{margin:0!important}table.wc-shipping-classes thead th,table.wc-shipping-zone-methods thead th,table.wc-shipping-zones thead th{vertical-align:middle}table.wc-shipping-classes thead .wc-shipping-zone-sort,table.wc-shipping-zone-methods thead .wc-shipping-zone-sort,table.wc-shipping-zones thead .wc-shipping-zone-sort{text-align:center}table.wc-shipping-classes tbody td,table.wc-shipping-zone-methods tbody td,table.wc-shipping-zones tbody td{padding:1.5em 1em!important}table.wc-shipping-classes td.wc-shipping-zone-method-blank-state,table.wc-shipping-classes td.wc-shipping-zones-blank-state,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state,table.wc-shipping-zones td.wc-shipping-zones-blank-state{background:#f7f1f6!important;overflow:hidden;position:relative;padding:7.5em 7.5%!important;border-bottom:2px solid #eee2ec}table.wc-shipping-classes td.wc-shipping-zone-method-blank-state li,table.wc-shipping-classes td.wc-shipping-zone-method-blank-state p,table.wc-shipping-classes td.wc-shipping-zones-blank-state li,table.wc-shipping-classes td.wc-shipping-zones-blank-state p,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state li,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state p,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state li,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state p,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state li,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state p,table.wc-shipping-zones td.wc-shipping-zones-blank-state li,table.wc-shipping-zones td.wc-shipping-zones-blank-state p{color:#a46497;font-size:1.5em;line-height:1.5em;margin:0 0 1em;position:relative;z-index:1;text-shadow:1px 1px 1px #fff}table.wc-shipping-classes td.wc-shipping-zone-method-blank-state li.main,table.wc-shipping-classes td.wc-shipping-zone-method-blank-state p.main,table.wc-shipping-classes td.wc-shipping-zones-blank-state li.main,table.wc-shipping-classes td.wc-shipping-zones-blank-state p.main,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state li.main,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state p.main,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state li.main,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state p.main,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state li.main,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state p.main,table.wc-shipping-zones td.wc-shipping-zones-blank-state li.main,table.wc-shipping-zones td.wc-shipping-zones-blank-state p.main{font-size:2em}table.wc-shipping-classes td.wc-shipping-zone-method-blank-state li,table.wc-shipping-classes td.wc-shipping-zones-blank-state li,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state li,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state li,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state li,table.wc-shipping-zones td.wc-shipping-zones-blank-state li{margin-left:1em;list-style:circle inside}table.wc-shipping-classes td.wc-shipping-zone-method-blank-state::before,table.wc-shipping-classes td.wc-shipping-zones-blank-state::before,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state::before,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state::before,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state::before,table.wc-shipping-zones td.wc-shipping-zones-blank-state::before{content:'\e01b';font-family:WooCommerce;text-align:center;line-height:1;color:#eee2ec;display:block;width:1em;font-size:40em;top:50%;right:-3.75%;margin-top:-.1875em;position:absolute}table.wc-shipping-classes td.wc-shipping-zone-method-blank-state .button-primary,table.wc-shipping-classes td.wc-shipping-zones-blank-state .button-primary,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state .button-primary,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state .button-primary,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state .button-primary,table.wc-shipping-zones td.wc-shipping-zones-blank-state .button-primary{background-color:#804877;border-color:#804877;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 0 rgba(0,0,0,.15);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 0 rgba(0,0,0,.15);margin:0;opacity:1;text-shadow:0 -1px 1px #8a4f7f,1px 0 1px #8a4f7f,0 1px 1px #8a4f7f,-1px 0 1px #8a4f7f;font-size:1.5em;padding:.75em 1em;height:auto;position:relative;z-index:1}table.wc-shipping-classes .wc-shipping-class-rows tr:nth-child(odd) td,table.wc-shipping-classes .wc-shipping-zone-method-rows tr:nth-child(odd) td,table.wc-shipping-classes tbody.wc-shipping-zone-rows tr:nth-child(odd) td,table.wc-shipping-classes tr.odd td,table.wc-shipping-zone-methods .wc-shipping-class-rows tr:nth-child(odd) td,table.wc-shipping-zone-methods .wc-shipping-zone-method-rows tr:nth-child(odd) td,table.wc-shipping-zone-methods tbody.wc-shipping-zone-rows tr:nth-child(odd) td,table.wc-shipping-zone-methods tr.odd td,table.wc-shipping-zones .wc-shipping-class-rows tr:nth-child(odd) td,table.wc-shipping-zones .wc-shipping-zone-method-rows tr:nth-child(odd) td,table.wc-shipping-zones tbody.wc-shipping-zone-rows tr:nth-child(odd) td,table.wc-shipping-zones tr.odd td{background:#f9f9f9}table.wc-shipping-classes p,table.wc-shipping-classes ul,table.wc-shipping-zone-methods p,table.wc-shipping-zone-methods ul,table.wc-shipping-zones p,table.wc-shipping-zones ul{margin:0}table.wc-shipping-classes td.wc-shipping-zone-method-sort,table.wc-shipping-classes td.wc-shipping-zone-sort,table.wc-shipping-zone-methods td.wc-shipping-zone-method-sort,table.wc-shipping-zone-methods td.wc-shipping-zone-sort,table.wc-shipping-zones td.wc-shipping-zone-method-sort,table.wc-shipping-zones td.wc-shipping-zone-sort{cursor:move;font-size:15px;text-align:center}table.wc-shipping-classes td.wc-shipping-zone-method-sort::before,table.wc-shipping-classes td.wc-shipping-zone-sort::before,table.wc-shipping-zone-methods td.wc-shipping-zone-method-sort::before,table.wc-shipping-zone-methods td.wc-shipping-zone-sort::before,table.wc-shipping-zones td.wc-shipping-zone-method-sort::before,table.wc-shipping-zones td.wc-shipping-zone-sort::before{content:'\f333';font-family:Dashicons;text-align:center;color:#999;display:block;width:17px;float:left;height:100%;line-height:24px}table.wc-shipping-classes td.wc-shipping-zone-method-sort:hover::before,table.wc-shipping-classes td.wc-shipping-zone-sort:hover::before,table.wc-shipping-zone-methods td.wc-shipping-zone-method-sort:hover::before,table.wc-shipping-zone-methods td.wc-shipping-zone-sort:hover::before,table.wc-shipping-zones td.wc-shipping-zone-method-sort:hover::before,table.wc-shipping-zones td.wc-shipping-zone-sort:hover::before{color:#333}table.wc-shipping-classes td.wc-shipping-zone-worldwide,table.wc-shipping-zone-methods td.wc-shipping-zone-worldwide,table.wc-shipping-zones td.wc-shipping-zone-worldwide{text-align:center}table.wc-shipping-classes td.wc-shipping-zone-worldwide::before,table.wc-shipping-zone-methods td.wc-shipping-zone-worldwide::before,table.wc-shipping-zones td.wc-shipping-zone-worldwide::before{content:'\f319';font-family:dashicons;text-align:center;color:#999;display:block;width:17px;float:left;height:100%;line-height:24px}table.wc-shipping-classes .wc-shipping-zone-methods,table.wc-shipping-classes .wc-shipping-zone-name,table.wc-shipping-zone-methods .wc-shipping-zone-methods,table.wc-shipping-zone-methods .wc-shipping-zone-name,table.wc-shipping-zones .wc-shipping-zone-methods,table.wc-shipping-zones .wc-shipping-zone-name{width:25%}table.wc-shipping-classes .wc-shipping-class-description input,table.wc-shipping-classes .wc-shipping-class-description select,table.wc-shipping-classes .wc-shipping-class-description textarea,table.wc-shipping-classes .wc-shipping-class-name input,table.wc-shipping-classes .wc-shipping-class-name select,table.wc-shipping-classes .wc-shipping-class-name textarea,table.wc-shipping-classes .wc-shipping-class-slug input,table.wc-shipping-classes .wc-shipping-class-slug select,table.wc-shipping-classes .wc-shipping-class-slug textarea,table.wc-shipping-classes .wc-shipping-zone-name input,table.wc-shipping-classes .wc-shipping-zone-name select,table.wc-shipping-classes .wc-shipping-zone-name textarea,table.wc-shipping-classes .wc-shipping-zone-region input,table.wc-shipping-classes .wc-shipping-zone-region select,table.wc-shipping-classes .wc-shipping-zone-region textarea,table.wc-shipping-zone-methods .wc-shipping-class-description input,table.wc-shipping-zone-methods .wc-shipping-class-description select,table.wc-shipping-zone-methods .wc-shipping-class-description textarea,table.wc-shipping-zone-methods .wc-shipping-class-name input,table.wc-shipping-zone-methods .wc-shipping-class-name select,table.wc-shipping-zone-methods .wc-shipping-class-name textarea,table.wc-shipping-zone-methods .wc-shipping-class-slug input,table.wc-shipping-zone-methods .wc-shipping-class-slug select,table.wc-shipping-zone-methods .wc-shipping-class-slug textarea,table.wc-shipping-zone-methods .wc-shipping-zone-name input,table.wc-shipping-zone-methods .wc-shipping-zone-name select,table.wc-shipping-zone-methods .wc-shipping-zone-name textarea,table.wc-shipping-zone-methods .wc-shipping-zone-region input,table.wc-shipping-zone-methods .wc-shipping-zone-region select,table.wc-shipping-zone-methods .wc-shipping-zone-region textarea,table.wc-shipping-zones .wc-shipping-class-description input,table.wc-shipping-zones .wc-shipping-class-description select,table.wc-shipping-zones .wc-shipping-class-description textarea,table.wc-shipping-zones .wc-shipping-class-name input,table.wc-shipping-zones .wc-shipping-class-name select,table.wc-shipping-zones .wc-shipping-class-name textarea,table.wc-shipping-zones .wc-shipping-class-slug input,table.wc-shipping-zones .wc-shipping-class-slug select,table.wc-shipping-zones .wc-shipping-class-slug textarea,table.wc-shipping-zones .wc-shipping-zone-name input,table.wc-shipping-zones .wc-shipping-zone-name select,table.wc-shipping-zones .wc-shipping-zone-name textarea,table.wc-shipping-zones .wc-shipping-zone-region input,table.wc-shipping-zones .wc-shipping-zone-region select,table.wc-shipping-zones .wc-shipping-zone-region textarea{width:100%}table.wc-shipping-classes .wc-shipping-class-description a.wc-shipping-class-delete,table.wc-shipping-classes .wc-shipping-class-description a.wc-shipping-zone-delete,table.wc-shipping-classes .wc-shipping-class-name a.wc-shipping-class-delete,table.wc-shipping-classes .wc-shipping-class-name a.wc-shipping-zone-delete,table.wc-shipping-classes .wc-shipping-class-slug a.wc-shipping-class-delete,table.wc-shipping-classes .wc-shipping-class-slug a.wc-shipping-zone-delete,table.wc-shipping-classes .wc-shipping-zone-name a.wc-shipping-class-delete,table.wc-shipping-classes .wc-shipping-zone-name a.wc-shipping-zone-delete,table.wc-shipping-classes .wc-shipping-zone-region a.wc-shipping-class-delete,table.wc-shipping-classes .wc-shipping-zone-region a.wc-shipping-zone-delete,table.wc-shipping-zone-methods .wc-shipping-class-description a.wc-shipping-class-delete,table.wc-shipping-zone-methods .wc-shipping-class-description a.wc-shipping-zone-delete,table.wc-shipping-zone-methods .wc-shipping-class-name a.wc-shipping-class-delete,table.wc-shipping-zone-methods .wc-shipping-class-name a.wc-shipping-zone-delete,table.wc-shipping-zone-methods .wc-shipping-class-slug a.wc-shipping-class-delete,table.wc-shipping-zone-methods .wc-shipping-class-slug a.wc-shipping-zone-delete,table.wc-shipping-zone-methods .wc-shipping-zone-name a.wc-shipping-class-delete,table.wc-shipping-zone-methods .wc-shipping-zone-name a.wc-shipping-zone-delete,table.wc-shipping-zone-methods .wc-shipping-zone-region a.wc-shipping-class-delete,table.wc-shipping-zone-methods .wc-shipping-zone-region a.wc-shipping-zone-delete,table.wc-shipping-zones .wc-shipping-class-description a.wc-shipping-class-delete,table.wc-shipping-zones .wc-shipping-class-description a.wc-shipping-zone-delete,table.wc-shipping-zones .wc-shipping-class-name a.wc-shipping-class-delete,table.wc-shipping-zones .wc-shipping-class-name a.wc-shipping-zone-delete,table.wc-shipping-zones .wc-shipping-class-slug a.wc-shipping-class-delete,table.wc-shipping-zones .wc-shipping-class-slug a.wc-shipping-zone-delete,table.wc-shipping-zones .wc-shipping-zone-name a.wc-shipping-class-delete,table.wc-shipping-zones .wc-shipping-zone-name a.wc-shipping-zone-delete,table.wc-shipping-zones .wc-shipping-zone-region a.wc-shipping-class-delete,table.wc-shipping-zones .wc-shipping-zone-region a.wc-shipping-zone-delete{color:#a00}table.wc-shipping-classes .wc-shipping-class-description a.wc-shipping-class-delete:hover,table.wc-shipping-classes .wc-shipping-class-description a.wc-shipping-zone-delete:hover,table.wc-shipping-classes .wc-shipping-class-name a.wc-shipping-class-delete:hover,table.wc-shipping-classes .wc-shipping-class-name a.wc-shipping-zone-delete:hover,table.wc-shipping-classes .wc-shipping-class-slug a.wc-shipping-class-delete:hover,table.wc-shipping-classes .wc-shipping-class-slug a.wc-shipping-zone-delete:hover,table.wc-shipping-classes .wc-shipping-zone-name a.wc-shipping-class-delete:hover,table.wc-shipping-classes .wc-shipping-zone-name a.wc-shipping-zone-delete:hover,table.wc-shipping-classes .wc-shipping-zone-region a.wc-shipping-class-delete:hover,table.wc-shipping-classes .wc-shipping-zone-region a.wc-shipping-zone-delete:hover,table.wc-shipping-zone-methods .wc-shipping-class-description a.wc-shipping-class-delete:hover,table.wc-shipping-zone-methods .wc-shipping-class-description a.wc-shipping-zone-delete:hover,table.wc-shipping-zone-methods .wc-shipping-class-name a.wc-shipping-class-delete:hover,table.wc-shipping-zone-methods .wc-shipping-class-name a.wc-shipping-zone-delete:hover,table.wc-shipping-zone-methods .wc-shipping-class-slug a.wc-shipping-class-delete:hover,table.wc-shipping-zone-methods .wc-shipping-class-slug a.wc-shipping-zone-delete:hover,table.wc-shipping-zone-methods .wc-shipping-zone-name a.wc-shipping-class-delete:hover,table.wc-shipping-zone-methods .wc-shipping-zone-name a.wc-shipping-zone-delete:hover,table.wc-shipping-zone-methods .wc-shipping-zone-region a.wc-shipping-class-delete:hover,table.wc-shipping-zone-methods .wc-shipping-zone-region a.wc-shipping-zone-delete:hover,table.wc-shipping-zones .wc-shipping-class-description a.wc-shipping-class-delete:hover,table.wc-shipping-zones .wc-shipping-class-description a.wc-shipping-zone-delete:hover,table.wc-shipping-zones .wc-shipping-class-name a.wc-shipping-class-delete:hover,table.wc-shipping-zones .wc-shipping-class-name a.wc-shipping-zone-delete:hover,table.wc-shipping-zones .wc-shipping-class-slug a.wc-shipping-class-delete:hover,table.wc-shipping-zones .wc-shipping-class-slug a.wc-shipping-zone-delete:hover,table.wc-shipping-zones .wc-shipping-zone-name a.wc-shipping-class-delete:hover,table.wc-shipping-zones .wc-shipping-zone-name a.wc-shipping-zone-delete:hover,table.wc-shipping-zones .wc-shipping-zone-region a.wc-shipping-class-delete:hover,table.wc-shipping-zones .wc-shipping-zone-region a.wc-shipping-zone-delete:hover{color:red}table.wc-shipping-classes .wc-shipping-class-count,table.wc-shipping-zone-methods .wc-shipping-class-count,table.wc-shipping-zones .wc-shipping-class-count{text-align:center}table.wc-shipping-classes td.wc-shipping-zone-methods,table.wc-shipping-zone-methods td.wc-shipping-zone-methods,table.wc-shipping-zones td.wc-shipping-zone-methods{color:#555}table.wc-shipping-classes td.wc-shipping-zone-methods .method_disabled,table.wc-shipping-zone-methods td.wc-shipping-zone-methods .method_disabled,table.wc-shipping-zones td.wc-shipping-zone-methods .method_disabled{text-decoration:line-through}table.wc-shipping-classes td.wc-shipping-zone-methods ul,table.wc-shipping-zone-methods td.wc-shipping-zone-methods ul,table.wc-shipping-zones td.wc-shipping-zone-methods ul{position:relative;padding-right:32px}table.wc-shipping-classes td.wc-shipping-zone-methods ul li,table.wc-shipping-zone-methods td.wc-shipping-zone-methods ul li,table.wc-shipping-zones td.wc-shipping-zone-methods ul li{color:#555;display:inline;margin:0}table.wc-shipping-classes td.wc-shipping-zone-methods ul li::before,table.wc-shipping-zone-methods td.wc-shipping-zone-methods ul li::before,table.wc-shipping-zones td.wc-shipping-zone-methods ul li::before{content:', '}table.wc-shipping-classes td.wc-shipping-zone-methods ul li:first-child::before,table.wc-shipping-zone-methods td.wc-shipping-zone-methods ul li:first-child::before,table.wc-shipping-zones td.wc-shipping-zone-methods ul li:first-child::before{content:''}table.wc-shipping-classes td.wc-shipping-zone-methods .add_shipping_method,table.wc-shipping-zone-methods td.wc-shipping-zone-methods .add_shipping_method,table.wc-shipping-zones td.wc-shipping-zone-methods .add_shipping_method{display:block;width:24px;padding:24px 0 0;height:0;overflow:hidden;cursor:pointer}table.wc-shipping-classes td.wc-shipping-zone-methods .add_shipping_method::before,table.wc-shipping-zone-methods td.wc-shipping-zone-methods .add_shipping_method::before,table.wc-shipping-zones td.wc-shipping-zone-methods .add_shipping_method::before{speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;font-family:Dashicons;content:'\f502';color:#999;vertical-align:middle;line-height:24px;font-size:16px;margin:0}table.wc-shipping-classes .wc-shipping-zone-method-enabled .status-disabled::before,table.wc-shipping-classes .wc-shipping-zone-method-enabled .status-enabled::before,table.wc-shipping-zone-methods .wc-shipping-zone-method-enabled .status-disabled::before,table.wc-shipping-zone-methods .wc-shipping-zone-method-enabled .status-enabled::before,table.wc-shipping-zones .wc-shipping-zone-method-enabled .status-disabled::before,table.wc-shipping-zones .wc-shipping-zone-method-enabled .status-enabled::before{line-height:inherit}table.wc-shipping-classes .wc-shipping-zone-method-enabled .status-disabled,table.wc-shipping-classes .wc-shipping-zone-method-enabled .status-enabled,table.wc-shipping-zone-methods .wc-shipping-zone-method-enabled .status-disabled,table.wc-shipping-zone-methods .wc-shipping-zone-method-enabled .status-enabled,table.wc-shipping-zones .wc-shipping-zone-method-enabled .status-disabled,table.wc-shipping-zones .wc-shipping-zone-method-enabled .status-enabled{margin-top:1px;display:inline-block}table.wc-shipping-classes td.wc-shipping-zone-methods .add_shipping_method.disabled,table.wc-shipping-zone-methods td.wc-shipping-zone-methods .add_shipping_method.disabled,table.wc-shipping-zones td.wc-shipping-zone-methods .add_shipping_method.disabled{cursor:not-allowed}table.wc-shipping-classes td.wc-shipping-zone-methods .add_shipping_method.disabled::before,table.wc-shipping-zone-methods td.wc-shipping-zone-methods .add_shipping_method.disabled::before,table.wc-shipping-zones td.wc-shipping-zone-methods .add_shipping_method.disabled::before{color:#ccc}table.wc-shipping-classes .wc-shipping-zone-method-title,table.wc-shipping-zone-methods .wc-shipping-zone-method-title,table.wc-shipping-zones .wc-shipping-zone-method-title{width:33%}table.wc-shipping-classes .wc-shipping-zone-method-title .wc-shipping-zone-method-delete,table.wc-shipping-zone-methods .wc-shipping-zone-method-title .wc-shipping-zone-method-delete,table.wc-shipping-zones .wc-shipping-zone-method-title .wc-shipping-zone-method-delete{color:red}table.wc-shipping-classes .wc-shipping-zone-method-enabled,table.wc-shipping-zone-methods .wc-shipping-zone-method-enabled,table.wc-shipping-zones .wc-shipping-zone-method-enabled{text-align:center}table.wc-shipping-classes tfoot input,table.wc-shipping-classes tfoot select,table.wc-shipping-zone-methods tfoot input,table.wc-shipping-zone-methods tfoot select,table.wc-shipping-zones tfoot input,table.wc-shipping-zones tfoot select{vertical-align:middle!important}table.wc-shipping-classes tfoot .button-secondary,table.wc-shipping-zone-methods tfoot .button-secondary,table.wc-shipping-zones tfoot .button-secondary{float:right}table.wc-shipping-classes .editing .wc-shipping-zone-edit,table.wc-shipping-classes .editing .wc-shipping-zone-view,table.wc-shipping-zone-methods .editing .wc-shipping-zone-edit,table.wc-shipping-zone-methods .editing .wc-shipping-zone-view,table.wc-shipping-zones .editing .wc-shipping-zone-edit,table.wc-shipping-zones .editing .wc-shipping-zone-view{display:none}.wc-modal-shipping-method-settings{background:#f8f8f8;padding:1em!important}.wc-modal-shipping-method-settings form .form-table{width:100%;background:#fff;margin:0 0 1.5em}.wc-modal-shipping-method-settings form .form-table tr th{width:30%;position:relative}.wc-modal-shipping-method-settings form .form-table tr th .woocommerce-help-tip{float:right;margin:-8px -.5em 0 0;vertical-align:middle;right:0;top:50%;position:absolute}.wc-modal-shipping-method-settings form .form-table tr td input,.wc-modal-shipping-method-settings form .form-table tr td select,.wc-modal-shipping-method-settings form .form-table tr td textarea{width:50%;min-width:250px}.wc-modal-shipping-method-settings form .form-table tr td input[type=checkbox]{width:auto;min-width:16px}.wc-modal-shipping-method-settings form .form-table tr td,.wc-modal-shipping-method-settings form .form-table tr th{vertical-align:middle;margin:0;line-height:24px;padding:1em;border-bottom:1px solid #f8f8f8}.wc-modal-shipping-method-settings form .form-table:last-of-type{margin-bottom:0}.wc-backbone-modal .wc-shipping-zone-method-selector p{margin-top:0}.wc-backbone-modal .wc-shipping-zone-method-selector .wc-shipping-zone-method-description{margin:.75em 1px 0;line-height:1.5em;color:#999;font-style:italic}.wc-backbone-modal .wc-shipping-zone-method-selector select{width:100%;cursor:pointer}img.help_tip{margin:0 0 0 9px;vertical-align:middle}.postbox img.help_tip{margin-top:0}.postbox .woocommerce-help-tip{margin:0 0 0 9px}.status-disabled,.status-enabled,.status-manual{font-size:1.4em;display:block;text-indent:-9999px;position:relative;height:1em;width:1em}.status-disabled::before,.status-enabled::before,.status-manual::before{font-family:WooCommerce;line-height:1;margin:0;position:absolute;width:100%;height:100%;text-indent:0;top:0;font-variant:normal;-webkit-font-smoothing:antialiased;font-weight:400;text-align:center;left:0;speak:none;text-transform:none}.status-manual::before{content:"";color:#999}.status-enabled::before{content:"";color:#a46497}.status-disabled::before{content:"";color:#ccc}.woocommerce h2.woo-nav-tab-wrapper{margin-bottom:1em}.woocommerce nav.woo-nav-tab-wrapper{margin:1.5em 0 1em;border-bottom:1px solid #ccc}.woocommerce .subsubsub{margin:-8px 0 0}.woocommerce .wc-admin-breadcrumb{margin-left:.5em}.woocommerce .wc-admin-breadcrumb a{color:#a46497}.woocommerce #template div{margin:0}.woocommerce #template div p .button{float:right;margin-left:10px;margin-top:-4px}.woocommerce #template div .editor textarea{margin-bottom:8px}.woocommerce textarea[disabled=disabled]{background:#dfdfdf!important}.woocommerce table.form-table{margin:0;position:relative}.woocommerce table.form-table .forminp-radio ul{margin:0}.woocommerce table.form-table .forminp-radio ul li{line-height:1.4em}.woocommerce table.form-table textarea.input-text{height:100%;min-width:150px;display:block}.woocommerce table.form-table input.regular-input{width:25em}.woocommerce table.form-table textarea.wide-input{width:100%}.woocommerce table.form-table .woocommerce-help-tip,.woocommerce table.form-table img.help_tip{padding:0;margin:-4px 0 0 5px;vertical-align:middle;cursor:help;line-height:1}.woocommerce table.form-table span.help_tip{cursor:help;color:#2ea2cc}.woocommerce table.form-table th{position:relative;padding-right:24px}.woocommerce table.form-table .select2-container{display:block;max-width:350px;vertical-align:top;margin-bottom:3px}.woocommerce table.form-table table.widefat th{padding-right:inherit}.woocommerce table.form-table th .woocommerce-help-tip,.woocommerce table.form-table th img.help_tip{margin:0 -24px 0 0;float:right}.woocommerce table.form-table .wp-list-table .woocommerce-help-tip{float:none}.woocommerce table.form-table fieldset{margin-top:4px}.woocommerce table.form-table fieldset .woocommerce-help-tip,.woocommerce table.form-table fieldset img.help_tip{margin:-3px 0 0 5px}.woocommerce table.form-table fieldset p.description{margin-bottom:8px}.woocommerce table.form-table fieldset:first-child{margin-top:0}.woocommerce table.form-table .iris-picker{z-index:100;display:none;position:absolute;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.2)}.woocommerce table.form-table .iris-picker .ui-slider{border:0!important;margin:0!important;width:auto!important;height:auto!important;background:none!important}.woocommerce table.form-table .iris-picker .ui-slider .ui-slider-handle{margin-bottom:0!important}.woocommerce table.form-table .colorpickpreview{padding:3px 3px 3px 20px;border:1px solid #ddd;border-right:0}.woocommerce table.form-table .colorpick{border-left:0}.woocommerce table.form-table .image_width_settings{vertical-align:middle}.woocommerce table.form-table .image_width_settings label{margin-left:10px}.woocommerce table.form-table .wc_emails_wrapper{padding:0 15px 10px 0}.woocommerce #tabs-wrap table a.remove{margin-left:4px}.woocommerce #tabs-wrap table p{margin:0 0 4px!important;overflow:hidden;zoom:1}.woocommerce #tabs-wrap table p a.add{float:left}#wp-excerpt-editor-container{background:#fff}#product_variation-parent #parent_id{width:100%}#postimagediv img{border:1px solid #d5d5d5;max-width:100%}#woocommerce-product-images .inside{margin:0;padding:0}#woocommerce-product-images .inside .add_product_images{padding:0 12px 12px}#woocommerce-product-images .inside #product_images_container{padding:0 0 0 9px}#woocommerce-product-images .inside #product_images_container ul{margin:0;padding:0}#woocommerce-product-images .inside #product_images_container ul::after,#woocommerce-product-images .inside #product_images_container ul::before{content:' ';display:table}#woocommerce-product-images .inside #product_images_container ul::after{clear:both}#woocommerce-product-images .inside #product_images_container ul li.add,#woocommerce-product-images .inside #product_images_container ul li.image,#woocommerce-product-images .inside #product_images_container ul li.wc-metabox-sortable-placeholder{width:80px;float:left;cursor:move;border:1px solid #d5d5d5;margin:9px 9px 0 0;background:#f7f7f7;border-radius:2px;position:relative;box-sizing:border-box}#woocommerce-product-images .inside #product_images_container ul li.add img,#woocommerce-product-images .inside #product_images_container ul li.image img,#woocommerce-product-images .inside #product_images_container ul li.wc-metabox-sortable-placeholder img{width:100%;height:auto;display:block}#woocommerce-product-images .inside #product_images_container ul li.wc-metabox-sortable-placeholder{border:3px dashed #ddd;position:relative}#woocommerce-product-images .inside #product_images_container ul li.wc-metabox-sortable-placeholder::after{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";font-size:2.618em;line-height:72px;color:#ddd}#woocommerce-product-images .inside #product_images_container ul ul.actions{position:absolute;top:-8px;right:-8px;padding:2px;display:none}#woocommerce-product-images .inside #product_images_container ul ul.actions li{float:right;margin:0 0 0 2px}#woocommerce-product-images .inside #product_images_container ul ul.actions li a{width:1em;margin:0;height:0;display:block;overflow:hidden}#woocommerce-product-images .inside #product_images_container ul ul.actions li a.tips{cursor:pointer}#woocommerce-product-images .inside #product_images_container ul ul.actions li a.delete{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;font-size:1.4em}#woocommerce-product-images .inside #product_images_container ul ul.actions li a.delete::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";color:#999}#woocommerce-product-images .inside #product_images_container ul ul.actions li a.delete:hover::before{color:#a00}#woocommerce-product-images .inside #product_images_container ul li:hover ul.actions{display:block}#woocommerce-product-data .hndle{padding:10px}#woocommerce-product-data .hndle span{display:block;vertical-align:middle;line-height:24px}#woocommerce-product-data .hndle span span{display:inline;line-height:inherit;vertical-align:baseline}#woocommerce-product-data .hndle label{padding-right:1em;font-size:12px;vertical-align:baseline}#woocommerce-product-data .hndle label:first-child{margin-right:1em;border-right:1px solid #dfdfdf}#woocommerce-product-data .hndle input,#woocommerce-product-data .hndle select{margin:-3px 0 0 .5em;vertical-align:middle}#woocommerce-product-data>.handlediv{margin-top:4px}#woocommerce-product-data .wrap{margin:0}#woocommerce-coupon-description{padding:3px 8px;font-size:1.7em;line-height:1.42em;height:auto;width:100%;outline:0;margin:10px 0;display:block}#woocommerce-coupon-description::-webkit-input-placeholder{line-height:1.42em;color:#bbb}#woocommerce-coupon-description::-moz-placeholder{line-height:1.42em;color:#bbb}#woocommerce-coupon-description:-ms-input-placeholder{line-height:1.42em;color:#bbb}#woocommerce-coupon-description:-moz-placeholder{line-height:1.42em;color:#bbb}#woocommerce-coupon-data .panel-wrap,#woocommerce-product-data .panel-wrap{background:#fff}#woocommerce-coupon-data .wc-metaboxes-wrapper,#woocommerce-coupon-data .woocommerce_options_panel,#woocommerce-product-data .wc-metaboxes-wrapper,#woocommerce-product-data .woocommerce_options_panel{float:left;width:80%}#woocommerce-coupon-data .wc-metaboxes-wrapper .wc-radios,#woocommerce-coupon-data .woocommerce_options_panel .wc-radios,#woocommerce-product-data .wc-metaboxes-wrapper .wc-radios,#woocommerce-product-data .woocommerce_options_panel .wc-radios{display:block;float:left;margin:0}#woocommerce-coupon-data .wc-metaboxes-wrapper .wc-radios li,#woocommerce-coupon-data .woocommerce_options_panel .wc-radios li,#woocommerce-product-data .wc-metaboxes-wrapper .wc-radios li,#woocommerce-product-data .woocommerce_options_panel .wc-radios li{display:block;padding:0 0 10px}#woocommerce-coupon-data .wc-metaboxes-wrapper .wc-radios li input,#woocommerce-coupon-data .woocommerce_options_panel .wc-radios li input,#woocommerce-product-data .wc-metaboxes-wrapper .wc-radios li input,#woocommerce-product-data .woocommerce_options_panel .wc-radios li input{width:auto}#woocommerce-coupon-data .panel-wrap,#woocommerce-product-data .panel-wrap,.woocommerce .panel-wrap{overflow:hidden}#woocommerce-coupon-data ul.wc-tabs,#woocommerce-product-data ul.wc-tabs,.woocommerce ul.wc-tabs{margin:0;width:20%;float:left;line-height:1em;padding:0 0 10px;position:relative;background-color:#fafafa;border-right:1px solid #eee;box-sizing:border-box}#woocommerce-coupon-data ul.wc-tabs::after,#woocommerce-product-data ul.wc-tabs::after,.woocommerce ul.wc-tabs::after{content:'';display:block;width:100%;height:9999em;position:absolute;bottom:-9999em;left:0;background-color:#fafafa;border-right:1px solid #eee}#woocommerce-coupon-data ul.wc-tabs li,#woocommerce-product-data ul.wc-tabs li,.woocommerce ul.wc-tabs li{margin:0;padding:0;display:block;position:relative}#woocommerce-coupon-data ul.wc-tabs li a,#woocommerce-product-data ul.wc-tabs li a,.woocommerce ul.wc-tabs li a{margin:0;padding:10px;display:block;box-shadow:none;text-decoration:none;line-height:20px!important;border-bottom:1px solid #eee}#woocommerce-coupon-data ul.wc-tabs li a::before,#woocommerce-product-data ul.wc-tabs li a::before,.woocommerce ul.wc-tabs li a::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin-right:.618em;content:"";text-decoration:none}#woocommerce-coupon-data ul.wc-tabs li.general_options a::before,#woocommerce-product-data ul.wc-tabs li.general_options a::before,.woocommerce ul.wc-tabs li.general_options a::before{content:'\f107'}#woocommerce-coupon-data ul.wc-tabs li.inventory_options a::before,#woocommerce-product-data ul.wc-tabs li.inventory_options a::before,.woocommerce ul.wc-tabs li.inventory_options a::before{content:'\f481'}#woocommerce-coupon-data ul.wc-tabs li.shipping_options a::before,#woocommerce-product-data ul.wc-tabs li.shipping_options a::before,.woocommerce ul.wc-tabs li.shipping_options a::before{font-family:WooCommerce;content:'\e01a'}#woocommerce-coupon-data ul.wc-tabs li.linked_product_options a::before,#woocommerce-product-data ul.wc-tabs li.linked_product_options a::before,.woocommerce ul.wc-tabs li.linked_product_options a::before{content:'\f103'}#woocommerce-coupon-data ul.wc-tabs li.attribute_options a::before,#woocommerce-product-data ul.wc-tabs li.attribute_options a::before,.woocommerce ul.wc-tabs li.attribute_options a::before{content:'\f175'}#woocommerce-coupon-data ul.wc-tabs li.advanced_options a::before,#woocommerce-product-data ul.wc-tabs li.advanced_options a::before,.woocommerce ul.wc-tabs li.advanced_options a::before{font-family:Dashicons;content:'\f111'}#woocommerce-coupon-data ul.wc-tabs li.variations_options a::before,#woocommerce-product-data ul.wc-tabs li.variations_options a::before,.woocommerce ul.wc-tabs li.variations_options a::before{content:'\f509'}#woocommerce-coupon-data ul.wc-tabs li.usage_restriction_options a::before,#woocommerce-product-data ul.wc-tabs li.usage_restriction_options a::before,.woocommerce ul.wc-tabs li.usage_restriction_options a::before{font-family:WooCommerce;content:'\e602'}#woocommerce-coupon-data ul.wc-tabs li.usage_limit_options a::before,#woocommerce-product-data ul.wc-tabs li.usage_limit_options a::before,.woocommerce ul.wc-tabs li.usage_limit_options a::before{font-family:WooCommerce;content:'\e601'}#woocommerce-coupon-data ul.wc-tabs li.general_coupon_data a::before,#woocommerce-product-data ul.wc-tabs li.general_coupon_data a::before,.woocommerce ul.wc-tabs li.general_coupon_data a::before{font-family:WooCommerce;content:'\e600'}#woocommerce-coupon-data ul.wc-tabs li.active a,#woocommerce-product-data ul.wc-tabs li.active a,.woocommerce ul.wc-tabs li.active a{color:#555;position:relative;background-color:#eee}.woocommerce_page_wc-settings .shippingrows th.check-column{padding-top:20px}.woocommerce_page_wc-settings .shippingrows tfoot th{padding-left:10px}.woocommerce_page_wc-settings .shippingrows .add.button::before{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin-right:.618em;content:"";text-decoration:none}.woocommerce_page_wc-settings h3.wc-settings-sub-title{font-size:1.2em}#woocommerce-coupon-data .inside,#woocommerce-order-data .inside,#woocommerce-order-downloads .inside,#woocommerce-product-data .inside,#woocommerce-product-type-options .inside{margin:0;padding:0}.panel,.woocommerce_options_panel{padding:9px;color:#555}.panel .form-field .woocommerce-help-tip,.woocommerce_options_panel .form-field .woocommerce-help-tip{font-size:1.4em}.panel,.woocommerce_page_settings .woocommerce_options_panel{padding:0}#woocommerce-product-specs .inside,#woocommerce-product-type-options .panel{margin:0;padding:9px}#woocommerce-product-type-options .panel p,.woocommerce_options_panel fieldset.form-field,.woocommerce_options_panel p{margin:0 0 9px;font-size:12px;padding:5px 9px;line-height:24px}#woocommerce-product-type-options .panel p::after,.woocommerce_options_panel fieldset.form-field::after,.woocommerce_options_panel p::after{content:'.';display:block;height:0;clear:both;visibility:hidden}.woocommerce_options_panel .checkbox,.woocommerce_variable_attributes .checkbox{width:auto;margin:3px 0;vertical-align:middle}.woocommerce_options_panel .downloadable_files table,.woocommerce_variations .downloadable_files table{width:100%;padding:0!important}.woocommerce_options_panel .downloadable_files table th,.woocommerce_variations .downloadable_files table th{padding:7px 0 7px 7px!important}.woocommerce_options_panel .downloadable_files table th.sort,.woocommerce_variations .downloadable_files table th.sort{width:17px;padding:7px!important}.woocommerce_options_panel .downloadable_files table th .woocommerce-help-tip,.woocommerce_variations .downloadable_files table th .woocommerce-help-tip{font-size:1.1em;margin-left:0}.woocommerce_options_panel .downloadable_files table td,.woocommerce_variations .downloadable_files table td{vertical-align:middle!important;padding:4px 0 4px 7px!important;position:relative}.woocommerce_options_panel .downloadable_files table td:last-child,.woocommerce_variations .downloadable_files table td:last-child{padding-right:7px!important}.woocommerce_options_panel .downloadable_files table td input.input_text,.woocommerce_variations .downloadable_files table td input.input_text{width:100%;float:none;min-width:0;margin:1px 0}.woocommerce_options_panel .downloadable_files table td .upload_file_button,.woocommerce_variations .downloadable_files table td .upload_file_button{width:auto;float:right;cursor:pointer}.woocommerce_options_panel .downloadable_files table td .delete,.woocommerce_variations .downloadable_files table td .delete{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;font-size:1.2em}.woocommerce_options_panel .downloadable_files table td .delete::before,.woocommerce_variations .downloadable_files table td .delete::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";color:#999}.woocommerce_options_panel .downloadable_files table td .delete:hover::before,.woocommerce_variations .downloadable_files table td .delete:hover::before{color:#a00}.woocommerce_options_panel .downloadable_files table td.sort,.woocommerce_variations .downloadable_files table td.sort{width:17px;cursor:move;font-size:15px;text-align:center;background:#f9f9f9;padding-right:7px!important}.woocommerce_options_panel .downloadable_files table td.sort::before,.woocommerce_variations .downloadable_files table td.sort::before{content:'\f333';font-family:Dashicons;text-align:center;line-height:1;color:#999;display:block;width:17px;float:left;height:100%}.woocommerce_options_panel .downloadable_files table td.sort:hover::before,.woocommerce_variations .downloadable_files table td.sort:hover::before{color:#333}.woocommerce_variation h3 .sort{width:17px;height:26px;cursor:move;float:right;font-size:15px;font-weight:400;margin-right:.5em;visibility:hidden;text-align:center;vertical-align:middle}.woocommerce_variation h3 .sort::before{content:'\f333';font-family:Dashicons;text-align:center;line-height:28px;color:#999;display:block;width:17px;float:left;height:100%}.woocommerce_variation h3 .sort:hover::before{color:#777}.woocommerce_variation h3:hover .sort,.woocommerce_variation.ui-sortable-helper .sort{visibility:visible}.woocommerce_options_panel{min-height:175px;box-sizing:border-box}.woocommerce_options_panel .downloadable_files{padding:0 9px 0 162px;position:relative;margin:9px 0}.woocommerce_options_panel .downloadable_files label{position:absolute;left:0;margin:0 0 0 12px;line-height:24px}.woocommerce_options_panel p{margin:9px 0}.woocommerce_options_panel fieldset.form-field,.woocommerce_options_panel p.form-field{padding:5px 20px 5px 162px!important}.woocommerce_options_panel .sale_price_dates_fields .short:first-of-type{margin-bottom:1em}.woocommerce_options_panel .sale_price_dates_fields .short:nth-of-type(2){clear:left}.woocommerce_options_panel label,.woocommerce_options_panel legend{float:left;width:150px;padding:0;margin:0 0 0 -150px}.woocommerce_options_panel label .req,.woocommerce_options_panel legend .req{font-weight:700;font-style:normal;color:#a00}.woocommerce_options_panel .description{padding:0;margin:0 0 0 7px;clear:none;display:inline}.woocommerce_options_panel .description-block{margin-left:0;display:block}.woocommerce_options_panel input,.woocommerce_options_panel select,.woocommerce_options_panel textarea{margin:0}.woocommerce_options_panel textarea{height:3.5em;line-height:1.5em;vertical-align:top}.woocommerce_options_panel input[type=text],.woocommerce_options_panel input[type=number],.woocommerce_options_panel input[type=email],.woocommerce_options_panel input[type=password]{width:50%;float:left}.woocommerce_options_panel input.button{width:auto;margin-left:8px}.woocommerce_options_panel select{float:left}.woocommerce_options_panel .short,.woocommerce_options_panel input[type=text].short,.woocommerce_options_panel input[type=number].short,.woocommerce_options_panel input[type=email].short,.woocommerce_options_panel input[type=password].short{width:50%}.woocommerce_options_panel .sized{width:auto!important;margin-right:6px}.woocommerce_options_panel .options_group{border-top:1px solid #fff;border-bottom:1px solid #eee}.woocommerce_options_panel .options_group:first-child{border-top:0}.woocommerce_options_panel .options_group:last-child{border-bottom:0}.woocommerce_options_panel .options_group fieldset{margin:9px 0;font-size:12px;padding:5px 9px;line-height:24px}.woocommerce_options_panel .options_group fieldset label{width:auto;float:none}.woocommerce_options_panel .options_group fieldset ul{float:left;width:50%;margin:0;padding:0}.woocommerce_options_panel .options_group fieldset ul li{margin:0;width:auto}.woocommerce_options_panel .options_group fieldset ul li input{width:auto;float:none;margin-right:4px}.woocommerce_options_panel .options_group fieldset ul.wc-radios label{margin-left:0}.woocommerce_options_panel .dimensions_field .wrap{display:block;width:50%}.woocommerce_options_panel .dimensions_field .wrap input{width:30.75%;margin-right:3.8%}.woocommerce_options_panel .dimensions_field .wrap .last{margin-right:0}.woocommerce_options_panel.padded{padding:1em}#woocommerce-product-data input.dp-applied,.woocommerce_options_panel .select2-container{float:left}#grouped_product_options,#simple_product_options,#virtual_product_options{padding:12px;font-style:italic;color:#666}.wc-metaboxes-wrapper .toolbar{margin:0!important;border-top:1px solid #fff;border-bottom:1px solid #eee;padding:9px 12px!important}.wc-metaboxes-wrapper .toolbar:first-child{border-top:0}.wc-metaboxes-wrapper .toolbar:last-child{border-bottom:0}.wc-metaboxes-wrapper .toolbar .add_variation{float:right;margin-left:5px}.wc-metaboxes-wrapper .toolbar .cancel-variation-changes,.wc-metaboxes-wrapper .toolbar .save-variation-changes{float:left;margin-right:5px}.wc-metaboxes-wrapper p.toolbar{overflow:hidden;zoom:1}.wc-metaboxes-wrapper .expand-close{margin-right:2px;color:#777;font-size:12px;font-style:italic}.wc-metaboxes-wrapper .expand-close a{background:0 0;padding:0;font-size:12px;text-decoration:none}.wc-metaboxes-wrapper#product_attributes .expand-close{float:right;line-height:28px}.wc-metaboxes-wrapper .fr,.wc-metaboxes-wrapper button.add_variable_attribute{float:right;margin:0 0 0 6px}.wc-metaboxes-wrapper .wc-metaboxes{border-bottom:1px solid #eee}.wc-metaboxes-wrapper .wc-metabox-sortable-placeholder{border-color:#bbb;background-color:#f5f5f5;margin-bottom:9px;border-width:1px;border-style:dashed}.wc-metaboxes-wrapper .wc-metabox{background:#fff;border-bottom:1px solid #eee;margin:0!important}.wc-metaboxes-wrapper .wc-metabox select{font-weight:400}.wc-metaboxes-wrapper .wc-metabox:last-of-type{border-bottom:0}.wc-metaboxes-wrapper .wc-metabox .handlediv{width:27px}.wc-metaboxes-wrapper .wc-metabox .handlediv::before{content:'\f142'!important;cursor:pointer;display:inline-block;font:400 20px/1 Dashicons;line-height:.5!important;padding:8px 10px;position:relative;right:12px;top:0}.wc-metaboxes-wrapper .wc-metabox.closed{border-radius:3px}.wc-metaboxes-wrapper .wc-metabox.closed .handlediv::before{content:'\f140'!important}.wc-metaboxes-wrapper .wc-metabox.closed h3{border:0}.wc-metaboxes-wrapper .wc-metabox h3{margin:0!important;padding:.75em .75em .75em 1em!important;font-size:1em!important;overflow:hidden;zoom:1;cursor:move}.wc-metaboxes-wrapper .wc-metabox h3 a.delete,.wc-metaboxes-wrapper .wc-metabox h3 button{float:right}.wc-metaboxes-wrapper .wc-metabox h3 a.delete{color:red;font-weight:400;line-height:26px;text-decoration:none;position:relative;visibility:hidden}.wc-metaboxes-wrapper .wc-metabox h3 strong{line-height:26px;font-weight:700}.wc-metaboxes-wrapper .wc-metabox h3 select{font-family:sans-serif;max-width:20%;margin:.25em .25em .25em 0}.wc-metaboxes-wrapper .wc-metabox h3 .handlediv{background-position:6px 5px!important;visibility:hidden;height:26px}.wc-metaboxes-wrapper .wc-metabox h3.fixed{cursor:pointer!important}.wc-metaboxes-wrapper .wc-metabox.woocommerce_variation h3{cursor:pointer;padding:.5em .75em .5em 1em!important}.wc-metaboxes-wrapper .wc-metabox.woocommerce_variation h3 .handlediv,.wc-metaboxes-wrapper .wc-metabox.woocommerce_variation h3 .sort,.wc-metaboxes-wrapper .wc-metabox.woocommerce_variation h3 a.delete{margin-top:.25em}.wc-metaboxes-wrapper .wc-metabox h3:hover .handlediv,.wc-metaboxes-wrapper .wc-metabox h3:hover a.delete,.wc-metaboxes-wrapper .wc-metabox.ui-sortable-helper .handlediv,.wc-metaboxes-wrapper .wc-metabox.ui-sortable-helper a.delete{visibility:visible}.wc-metaboxes-wrapper .wc-metabox table{width:100%;position:relative;background-color:#fdfdfd;padding:1em;border-top:1px solid #eee}.wc-metaboxes-wrapper .wc-metabox table td{text-align:left;padding:0 6px 1em 0;vertical-align:top;border:0}.wc-metaboxes-wrapper .wc-metabox table td label{text-align:left;display:block;line-height:21px}.wc-metaboxes-wrapper .wc-metabox table td input{float:left;min-width:200px}.wc-metaboxes-wrapper .wc-metabox table td input,.wc-metaboxes-wrapper .wc-metabox table td textarea{width:100%;margin:0;display:block;font-size:14px;padding:4px;color:#555}.wc-metaboxes-wrapper .wc-metabox table td .select2-container,.wc-metaboxes-wrapper .wc-metabox table td select{width:100%!important}.wc-metaboxes-wrapper .wc-metabox table td input.short{width:200px}.wc-metaboxes-wrapper .wc-metabox table td input.checkbox{width:16px;min-width:inherit;vertical-align:text-bottom;display:inline-block;float:none}.wc-metaboxes-wrapper .wc-metabox table td.attribute_name{width:200px}.wc-metaboxes-wrapper .wc-metabox table .minus,.wc-metaboxes-wrapper .wc-metabox table .plus{margin-top:6px}.wc-metaboxes-wrapper .wc-metabox table .fl{float:left}.wc-metaboxes-wrapper .wc-metabox table .fr{float:right}.variations-pagenav{float:right;line-height:24px}.variations-pagenav .displaying-num{color:#777;font-size:12px;font-style:italic}.variations-pagenav a{padding:0 10px 3px;background:rgba(0,0,0,.05);font-size:16px;font-weight:400;text-decoration:none}.variations-pagenav a.disabled,.variations-pagenav a.disabled:active,.variations-pagenav a.disabled:focus,.variations-pagenav a.disabled:hover{color:#a0a5aa;background:rgba(0,0,0,.05)}.variations-defaults{float:left}.variations-defaults select{margin:.25em .25em .25em 0}.woocommerce_variable_attributes{background-color:#fdfdfd;border-top:1px solid #eee}.woocommerce_variable_attributes .data{padding:1em 2em}.woocommerce_variable_attributes .data::after,.woocommerce_variable_attributes .data::before{content:' ';display:table}.woocommerce_variable_attributes .data::after{clear:both}.woocommerce_variable_attributes .upload_image_button{display:block;width:48px;height:48px;float:left;margin-right:20px;position:relative;cursor:pointer}.woocommerce_variable_attributes .upload_image_button img{width:100%;height:auto;display:none}.woocommerce_variable_attributes .upload_image_button::before{content:'\f128';font-family:Dashicons;position:absolute;top:0;left:0;right:0;bottom:0;text-align:center;line-height:48px;font-size:48px;font-weight:400;-webkit-font-smoothing:antialiased}.woocommerce_variable_attributes .upload_image_button.remove img{display:block}.woocommerce_variable_attributes .upload_image_button.remove::before{content:'\f335';display:none}.woocommerce_variable_attributes .upload_image_button.remove:hover::before{display:block}.woocommerce_variable_attributes .options{border:1px solid #eee;border-width:1px 0;padding:.25em 0}.woocommerce_variable_attributes .options label{display:inline-block;padding:4px 1em 2px 0}.woocommerce_variable_attributes .options input[type=checkbox]{margin-top:5px;margin-right:3px}.form-row label{display:block}.form-row input[type=number],.form-row input[type=text],.form-row select{width:100%}.form-row.dimensions_field input{width:25%;float:left;margin-right:1%}.form-row.dimensions_field input:last-of-type{margin-right:0}.form-row-first,.form-row-last{width:48%;float:right}.form-row-first{clear:both;float:left}.form-row-full{clear:both}.tips{cursor:help;text-decoration:none}img.tips{padding:5px 0 0}#tiptip_holder{display:none;position:absolute;top:0;left:0;z-index:9999999}#tiptip_holder.tip_top{padding-bottom:5px}#tiptip_holder.tip_top #tiptip_arrow_inner{margin-top:-7px;margin-left:-6px;border-top-color:#333}#tiptip_holder.tip_bottom{padding-top:5px}#tiptip_holder.tip_bottom #tiptip_arrow_inner{margin-top:-5px;margin-left:-6px;border-bottom-color:#333}#tiptip_holder.tip_right{padding-left:5px}#tiptip_holder.tip_right #tiptip_arrow_inner{margin-top:-6px;margin-left:-5px;border-right-color:#333}#tiptip_holder.tip_left{padding-right:5px}#tiptip_holder.tip_left #tiptip_arrow_inner{margin-top:-6px;margin-left:-7px;border-left-color:#333}#tiptip_content,.chart-tooltip,.wc_error_tip{color:#fff;font-size:.8em;max-width:150px;background:#333;text-align:center;border-radius:3px;padding:.618em 1em;box-shadow:0 1px 3px rgba(0,0,0,.2)}#tiptip_content code,.chart-tooltip code,.wc_error_tip code{padding:1px;background:#888}#tiptip_arrow,#tiptip_arrow_inner{position:absolute;border-color:transparent;border-style:solid;border-width:6px;height:0;width:0}.wc_error_tip{max-width:20em;line-height:1.8em;position:absolute;white-space:normal;background:#d82223;margin:1.5em 1px 0 -1em;z-index:9999999}.wc_error_tip::after{content:'';display:block;border:8px solid #d82223;border-right-color:transparent;border-left-color:transparent;border-top-color:transparent;position:absolute;top:-3px;left:50%;margin:-1em 0 0 -3px}img.ui-datepicker-trigger{vertical-align:middle;margin-top:-1px;cursor:pointer}.wc-metabox-content img.ui-datepicker-trigger,.woocommerce_options_panel img.ui-datepicker-trigger{float:left;margin-right:8px;margin-top:4px;margin-left:4px}#ui-datepicker-div{display:none}.woocommerce-reports-wide.woocommerce-reports-wrap,.woocommerce-reports-wrap.woocommerce-reports-wrap{margin-left:300px;padding-top:18px}.woocommerce-reports-wide.halved,.woocommerce-reports-wrap.halved{margin:0;overflow:hidden;zoom:1}.woocommerce-reports-wide .widefat td,.woocommerce-reports-wrap .widefat td{vertical-align:top;padding:7px}.woocommerce-reports-wide .widefat td .description,.woocommerce-reports-wrap .widefat td .description{margin:4px 0 0}.woocommerce-reports-wide .postbox::after,.woocommerce-reports-wrap .postbox::after{content:'.';display:block;height:0;clear:both;visibility:hidden}.woocommerce-reports-wide .postbox h3,.woocommerce-reports-wrap .postbox h3{cursor:default!important}.woocommerce-reports-wide .postbox .inside,.woocommerce-reports-wrap .postbox .inside{padding:10px;margin:0!important}.woocommerce-reports-wide .postbox div.stats_range,.woocommerce-reports-wide .postbox h3.stats_range,.woocommerce-reports-wrap .postbox div.stats_range,.woocommerce-reports-wrap .postbox h3.stats_range{border-bottom-color:#dfdfdf;margin:0;padding:0!important}.woocommerce-reports-wide .postbox div.stats_range .export_csv,.woocommerce-reports-wide .postbox h3.stats_range .export_csv,.woocommerce-reports-wrap .postbox div.stats_range .export_csv,.woocommerce-reports-wrap .postbox h3.stats_range .export_csv{float:right;line-height:26px;border-left:1px solid #dfdfdf;padding:10px;display:block;text-decoration:none}.woocommerce-reports-wide .postbox div.stats_range .export_csv::before,.woocommerce-reports-wide .postbox h3.stats_range .export_csv::before,.woocommerce-reports-wrap .postbox div.stats_range .export_csv::before,.woocommerce-reports-wrap .postbox h3.stats_range .export_csv::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;content:"";text-decoration:none;margin-right:4px}.woocommerce-reports-wide .postbox div.stats_range ul,.woocommerce-reports-wide .postbox h3.stats_range ul,.woocommerce-reports-wrap .postbox div.stats_range ul,.woocommerce-reports-wrap .postbox h3.stats_range ul{list-style:none;margin:0;padding:0;zoom:1;background:#f5f5f5;border-bottom:1px solid #ccc}.woocommerce-reports-wide .postbox div.stats_range ul::after,.woocommerce-reports-wide .postbox div.stats_range ul::before,.woocommerce-reports-wide .postbox h3.stats_range ul::after,.woocommerce-reports-wide .postbox h3.stats_range ul::before,.woocommerce-reports-wrap .postbox div.stats_range ul::after,.woocommerce-reports-wrap .postbox div.stats_range ul::before,.woocommerce-reports-wrap .postbox h3.stats_range ul::after,.woocommerce-reports-wrap .postbox h3.stats_range ul::before{content:' ';display:table}.woocommerce-reports-wide .postbox div.stats_range ul::after,.woocommerce-reports-wide .postbox h3.stats_range ul::after,.woocommerce-reports-wrap .postbox div.stats_range ul::after,.woocommerce-reports-wrap .postbox h3.stats_range ul::after{clear:both}.woocommerce-reports-wide .postbox div.stats_range ul li,.woocommerce-reports-wide .postbox h3.stats_range ul li,.woocommerce-reports-wrap .postbox div.stats_range ul li,.woocommerce-reports-wrap .postbox h3.stats_range ul li{float:left;margin:0;padding:0;line-height:26px;font-weight:700;font-size:14px}.woocommerce-reports-wide .postbox div.stats_range ul li a,.woocommerce-reports-wide .postbox h3.stats_range ul li a,.woocommerce-reports-wrap .postbox div.stats_range ul li a,.woocommerce-reports-wrap .postbox h3.stats_range ul li a{border-right:1px solid #dfdfdf;padding:10px;display:block;text-decoration:none}.woocommerce-reports-wide .postbox div.stats_range ul li.active,.woocommerce-reports-wide .postbox h3.stats_range ul li.active,.woocommerce-reports-wrap .postbox div.stats_range ul li.active,.woocommerce-reports-wrap .postbox h3.stats_range ul li.active{background:#fff;-webkit-box-shadow:0 4px 0 0 #fff;box-shadow:0 4px 0 0 #fff}.woocommerce-reports-wide .postbox div.stats_range ul li.active a,.woocommerce-reports-wide .postbox h3.stats_range ul li.active a,.woocommerce-reports-wrap .postbox div.stats_range ul li.active a,.woocommerce-reports-wrap .postbox h3.stats_range ul li.active a{color:#777}.woocommerce-reports-wide .postbox div.stats_range ul li.custom,.woocommerce-reports-wide .postbox h3.stats_range ul li.custom,.woocommerce-reports-wrap .postbox div.stats_range ul li.custom,.woocommerce-reports-wrap .postbox h3.stats_range ul li.custom{padding:9px 10px;vertical-align:middle}.woocommerce-reports-wide .postbox div.stats_range ul li.custom div,.woocommerce-reports-wide .postbox div.stats_range ul li.custom form,.woocommerce-reports-wide .postbox h3.stats_range ul li.custom div,.woocommerce-reports-wide .postbox h3.stats_range ul li.custom form,.woocommerce-reports-wrap .postbox div.stats_range ul li.custom div,.woocommerce-reports-wrap .postbox div.stats_range ul li.custom form,.woocommerce-reports-wrap .postbox h3.stats_range ul li.custom div,.woocommerce-reports-wrap .postbox h3.stats_range ul li.custom form{display:inline;margin:0}.woocommerce-reports-wide .postbox div.stats_range ul li.custom div input.range_datepicker,.woocommerce-reports-wide .postbox div.stats_range ul li.custom form input.range_datepicker,.woocommerce-reports-wide .postbox h3.stats_range ul li.custom div input.range_datepicker,.woocommerce-reports-wide .postbox h3.stats_range ul li.custom form input.range_datepicker,.woocommerce-reports-wrap .postbox div.stats_range ul li.custom div input.range_datepicker,.woocommerce-reports-wrap .postbox div.stats_range ul li.custom form input.range_datepicker,.woocommerce-reports-wrap .postbox h3.stats_range ul li.custom div input.range_datepicker,.woocommerce-reports-wrap .postbox h3.stats_range ul li.custom form input.range_datepicker{padding:0;margin:0 10px 0 0;background:0 0;border:0;color:#777;text-align:center;-webkit-box-shadow:none;box-shadow:none}.woocommerce-reports-wide .postbox div.stats_range ul li.custom div input.range_datepicker.from,.woocommerce-reports-wide .postbox div.stats_range ul li.custom form input.range_datepicker.from,.woocommerce-reports-wide .postbox h3.stats_range ul li.custom div input.range_datepicker.from,.woocommerce-reports-wide .postbox h3.stats_range ul li.custom form input.range_datepicker.from,.woocommerce-reports-wrap .postbox div.stats_range ul li.custom div input.range_datepicker.from,.woocommerce-reports-wrap .postbox div.stats_range ul li.custom form input.range_datepicker.from,.woocommerce-reports-wrap .postbox h3.stats_range ul li.custom div input.range_datepicker.from,.woocommerce-reports-wrap .postbox h3.stats_range ul li.custom form input.range_datepicker.from{margin-right:0}.woocommerce-reports-wide .postbox .chart-with-sidebar,.woocommerce-reports-wrap .postbox .chart-with-sidebar{padding:12px 12px 12px 249px;margin:0!important}.woocommerce-reports-wide .postbox .chart-with-sidebar .chart-sidebar,.woocommerce-reports-wrap .postbox .chart-with-sidebar .chart-sidebar{width:225px;margin-left:-237px;float:left}.woocommerce-reports-wide .postbox .chart-widgets,.woocommerce-reports-wrap .postbox .chart-widgets{margin:0;padding:0}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget{margin:0 0 1em;background:#fafafa;border:1px solid #dfdfdf}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget::after,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget::after{content:'.';display:block;height:0;clear:both;visibility:hidden}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget h4,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget h4{background:#fff;border:1px solid #dfdfdf;border-left-width:0;border-right-width:0;padding:10px;margin:0;color:#2ea2cc;border-top-width:0;background-image:-webkit-gradient(linear,left bottom,left top,from(#ececec),to(#f9f9f9));background-image:-webkit-linear-gradient(bottom,#ececec,#f9f9f9);background-image:-moz-linear-gradient(bottom,#ececec,#f9f9f9);background-image:-o-linear-gradient(bottom,#ececec,#f9f9f9);background-image:linear-gradient(to top,#ececec,#f9f9f9)}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table td.count,.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table tr.active td,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table td.count,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table tr.active td{background:#f5f5f5}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget h4.section_title:hover,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget h4.section_title:hover{color:#a00}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section_title,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section_title{cursor:pointer}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section_title span,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section_title span{display:block}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section_title span::after,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section_title span::after{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin-left:.618em;content:"";text-decoration:none;float:right;font-size:.9em;line-height:1.618}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section_title.open,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section_title.open{color:#333}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section_title.open span::after,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section_title.open span::after{display:none}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section{border-bottom:1px solid #dfdfdf}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section .select2-container,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section .select2-container{width:100%!important}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section:last-of-type,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section:last-of-type{border-radius:0 0 3px 3px}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table{width:100%}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table td,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table td{padding:7px 10px;vertical-align:top;border-top:1px solid #e5e5e5;line-height:1.4em}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table td.sparkline,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table td.sparkline,form.report_filters div,form.report_filters input,form.report_filters label,form.report_filters p{vertical-align:middle}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table tr:first-child td,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table tr:first-child td{border-top:0}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table td.name,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table td.name{max-width:175px}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table td.name a,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table td.name a{word-wrap:break-word}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table .wc_sparkline,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table .wc_sparkline{width:32px;height:1em;display:block;float:right}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget form,.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget p,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget form,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget p{margin:0;padding:10px}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget form .submit,.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget p .submit,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget form .submit,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget p .submit{margin-top:10px}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget #product_ids,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget #product_ids{width:100%}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .select_all,.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .select_none,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .select_all,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .select_none{float:right;color:#999;margin-left:4px;margin-top:10px}.woocommerce-reports-wide .postbox .chart-legend,.woocommerce-reports-wrap .postbox .chart-legend{list-style:none;margin:0 0 1em;padding:0;border:1px solid #dfdfdf;border-right-width:0;border-bottom-width:0;background:#fff}.woocommerce-reports-wide .postbox .chart-legend li,.woocommerce-reports-wrap .postbox .chart-legend li{border-right:5px solid #aaa;color:#aaa;padding:1em;display:block;margin:0;-webkit-transition:all ease .5s;box-shadow:inset 0 -1px 0 0 #dfdfdf}.woocommerce-reports-wide .postbox .chart-legend li strong,.woocommerce-reports-wrap .postbox .chart-legend li strong{font-size:1.618em;line-height:1.2em;color:#464646;font-weight:400;display:block;font-family:HelveticaNeue-Light,'Helvetica Neue Light','Helvetica Neue',sans-serif}.woocommerce-reports-wide .postbox .chart-legend li strong del,.woocommerce-reports-wrap .postbox .chart-legend li strong del{color:#e74c3c;font-weight:400}.woocommerce-reports-wide .postbox .chart-legend li:hover,.woocommerce-reports-wrap .postbox .chart-legend li:hover{box-shadow:inset 0 -1px 0 0 #dfdfdf,inset 300px 0 0 rgba(156,93,144,.1);border-right:5px solid #9c5d90!important;padding-left:1.5em;color:#9c5d90}.woocommerce-reports-wide .postbox .pie-chart-legend,.woocommerce-reports-wrap .postbox .pie-chart-legend{margin:12px 0 0;overflow:hidden}.woocommerce-reports-wide .postbox .pie-chart-legend li,.woocommerce-reports-wrap .postbox .pie-chart-legend li{float:left;margin:0;padding:6px 0 0;border-top:4px solid #999;text-align:center;box-sizing:border-box;width:50%}.woocommerce-reports-wide .postbox .stat,.woocommerce-reports-wrap .postbox .stat{font-size:1.5em!important;font-weight:700;text-align:center}.woocommerce-reports-wide .postbox .chart-placeholder,.woocommerce-reports-wrap .postbox .chart-placeholder{width:100%;height:650px;overflow:hidden;position:relative}.woocommerce-reports-wide .postbox .chart-prompt,.woocommerce-reports-wrap .postbox .chart-prompt{line-height:650px;margin:0;color:#999;font-size:1.2em;font-style:italic;text-align:center}.woocommerce-reports-wide .postbox .chart-container,.woocommerce-reports-wrap .postbox .chart-container{background:#fff;padding:12px;position:relative;border:1px solid #dfdfdf;border-radius:3px}.woocommerce-reports-wide .postbox .main .chart-legend,.woocommerce-reports-wrap .postbox .main .chart-legend{margin-top:12px}.woocommerce-reports-wide .postbox .main .chart-legend li,.woocommerce-reports-wrap .postbox .main .chart-legend li{border-right:0;margin:0 8px 0 0;float:left;border-top:4px solid #aaa}.woocommerce-reports-wide .woocommerce-reports-main,.woocommerce-reports-wrap .woocommerce-reports-main{float:left;min-width:100%}.woocommerce-reports-wide .woocommerce-reports-main table td,.woocommerce-reports-wrap .woocommerce-reports-main table td{padding:9px}.woocommerce-reports-wide .woocommerce-reports-sidebar,.woocommerce-reports-wrap .woocommerce-reports-sidebar{display:inline;width:281px;margin-left:-300px;clear:both;float:left}.woocommerce-reports-wide .woocommerce-reports-left,.woocommerce-reports-wrap .woocommerce-reports-left{width:49.5%;float:left}.woocommerce-reports-wide .woocommerce-reports-right,.woocommerce-reports-wrap .woocommerce-reports-right{width:49.5%;float:right}.woocommerce-reports-wide .column-wc_actions a.edit,.woocommerce-reports-wide .column-wc_actions a.view,.woocommerce-reports-wrap .column-wc_actions a.edit,.woocommerce-reports-wrap .column-wc_actions a.view{display:block;text-indent:-9999px;position:relative;padding:0!important;height:2em!important;width:2em}.woocommerce-reports-wide .column-wc_actions a.edit::after,.woocommerce-reports-wide .column-wc_actions a.view::after,.woocommerce-reports-wrap .column-wc_actions a.edit::after,.woocommerce-reports-wrap .column-wc_actions a.view::after{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;line-height:1.85}.woocommerce-reports-wide .column-wc_actions a.edit::after,.woocommerce-reports-wrap .column-wc_actions a.edit::after{content:'\f464'}.woocommerce-reports-wide .column-wc_actions a.view::after,.woocommerce-reports-wrap .column-wc_actions a.view::after{content:'\f177'}.woocommerce-wide-reports-wrap{padding-bottom:11px}.woocommerce-wide-reports-wrap .widefat .export-data{float:right}.woocommerce-wide-reports-wrap .widefat td,.woocommerce-wide-reports-wrap .widefat th{vertical-align:middle;padding:7px}.chart-tooltip{position:absolute;display:none;line-height:1}table.bar_chart{width:100%}table.bar_chart thead th{text-align:left;color:#ccc;padding:6px 0}table.bar_chart tbody th{padding:6px 0;width:25%;text-align:left!important;font-weight:400!important;border-bottom:1px solid #fee}table.bar_chart tbody td{text-align:right;line-height:24px;padding:6px 6px 6px 0;border-bottom:1px solid #fee}table.bar_chart tbody td span{color:#8a4b75;display:block}table.bar_chart tbody td span.alt{color:#47a03e;margin-top:6px}table.bar_chart tbody td.bars{position:relative;text-align:left;padding:6px 6px 6px 0;border-bottom:1px solid #fee}table.bar_chart tbody td.bars a,table.bar_chart tbody td.bars span{text-decoration:none;clear:both;background:#8a4b75;float:left;display:block;line-height:24px;height:24px;-moz-border-radius:3px;-webkit-border-radius:3px;-o-border-radius:3px;-khtml-border-radius:3px;border-radius:3px}.post-type-product .woocommerce-BlankState-message::before,.post-type-shop_coupon .woocommerce-BlankState-message::before,.post-type-shop_order .woocommerce-BlankState-message::before{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}table.bar_chart tbody td.bars span.alt{clear:both;background:#47a03e}table.bar_chart tbody td.bars span.alt span{margin:0;color:#c5dec2!important;text-shadow:0 1px 0 #47a03e;background:0 0}.post-type-shop_order .woocommerce-BlankState-message::before{content:""}.post-type-shop_coupon .woocommerce-BlankState-message::before{content:""}.post-type-product .woocommerce-BlankState-message::before{content:""}.woocommerce-BlankState{text-align:center;padding:5em 0 0}.woocommerce-BlankState .woocommerce-BlankState-message{color:#aaa;margin:0 auto 1.5em;line-height:1.5em;font-size:1.2em;max-width:500px}.woocommerce-BlankState .woocommerce-BlankState-message::before{color:#ddd;text-shadow:0 -1px 1px rgba(0,0,0,.2),0 1px 0 rgba(255,255,255,.8);font-size:8em;display:block;position:relative!important;top:auto;left:auto;line-height:1em;margin:0 0 .1875em}.woocommerce-BlankState .woocommerce-BlankState-cta{font-size:1.2em;padding:.75em 1.5em;height:auto}@media only screen and (max-width:1280px){#order_data .order_data_column{width:48%}#order_data .order_data_column:first-child{width:100%}.woocommerce_options_panel .description{display:block;clear:both;margin-left:0}.woocommerce_options_panel .dimensions_field .wrap,.woocommerce_options_panel .short,.woocommerce_options_panel input[type=text].short,.woocommerce_options_panel input[type=number].short,.woocommerce_options_panel input[type=email].short,.woocommerce_options_panel input[type=password].short{width:80%}.woocommerce_options_panel .downloadable_files,.woocommerce_variations .downloadable_files{padding:0;clear:both}.woocommerce_options_panel .downloadable_files label,.woocommerce_variations .downloadable_files label{position:static}.woocommerce_options_panel .downloadable_files table,.woocommerce_variations .downloadable_files table{margin:0 12px 24px;width:94%}.woocommerce_options_panel .downloadable_files table .sort,.woocommerce_variations .downloadable_files table .sort{visibility:hidden}.woocommerce_options_panel .woocommerce_variable_attributes .downloadable_files table,.woocommerce_variations .woocommerce_variable_attributes .downloadable_files table{margin:0 0 1em;width:100%}}@media only screen and (max-width:900px){#woocommerce-coupon-data ul.coupon_data_tabs,#woocommerce-product-data .wc-tabs-back,#woocommerce-product-data ul.product_data_tabs{width:10%}#woocommerce-coupon-data .wc-metaboxes-wrapper,#woocommerce-coupon-data .woocommerce_options_panel,#woocommerce-product-data .wc-metaboxes-wrapper,#woocommerce-product-data .woocommerce_options_panel{width:90%}#woocommerce-coupon-data ul.coupon_data_tabs li a,#woocommerce-product-data ul.product_data_tabs li a{position:relative;text-indent:-999px;padding:10px}#woocommerce-coupon-data ul.coupon_data_tabs li a::before,#woocommerce-product-data ul.product_data_tabs li a::before{position:absolute;top:0;right:0;bottom:0;left:0;text-indent:0;text-align:center;line-height:40px;width:100%;height:40px}}@media only screen and (max-width:782px){#wp-excerpt-media-buttons a{font-size:16px;line-height:37px;height:39px;padding:0 20px 0 15px}#wp-excerpt-editor-tools{padding-top:20px;padding-right:15px;overflow:hidden;margin-bottom:-1px}.post-type-product .wp-list-table .is-expanded td:not(.hidden),.post-type-shop_order .wp-list-table .is-expanded td:not(.hidden){overflow:visible}#woocommerce-product-data .checkbox{width:25px}.variations-pagenav{float:none;text-align:center;font-size:18px}.variations-pagenav .displaying-num{font-size:16px}.variations-pagenav a{padding:8px 20px 11px;font-size:18px}.variations-pagenav select{padding:0 20px}.variations-defaults{float:none;text-align:center;margin-top:10px}.post-type-product .wp-list-table .column-thumb{display:none;text-align:left;padding-bottom:0}.post-type-product .wp-list-table .column-thumb::before{display:none!important}.post-type-product .wp-list-table .column-thumb img{max-width:32px}.post-type-product .wp-list-table .toggle-row{top:-28px}.post-type-shop_order .wp-list-table .column-order_status{display:none;text-align:left;padding-bottom:0}.post-type-shop_order .wp-list-table .column-order_status mark{margin:0}.post-type-shop_order .wp-list-table .column-order_status::before{display:none!important}.post-type-shop_order .wp-list-table .column-customer_message,.post-type-shop_order .wp-list-table .column-order_notes{text-align:inherit}.post-type-shop_order .wp-list-table .column-order_notes .note-on{font-size:1.3em;margin:0}.post-type-shop_order .wp-list-table .toggle-row{top:-15px}}@media only screen and (max-width:500px){.woocommerce_options_panel label,.woocommerce_options_panel legend{float:none;width:auto;display:block;margin:0}.woocommerce_options_panel fieldset.form-field,.woocommerce_options_panel p.form-field{padding:5px 20px!important}}.wc-backbone-modal *{box-sizing:border-box}.wc-backbone-modal .wc-backbone-modal-content{position:fixed;background:#fff;z-index:100000;left:50%;top:50%;transform:translate(-50%,-50%);width:500px}.wc-backbone-modal .wc-backbone-modal-content article{overflow:auto}.wc-backbone-modal.wc-backbone-modal-shipping-method-settings .wc-backbone-modal-content{width:75%;min-width:500px}.wc-backbone-modal-backdrop{position:fixed;top:0;left:0;right:0;bottom:0;min-height:360px;background:#000;opacity:.7;z-index:99900}.wc-backbone-modal-main{padding-bottom:55px}.wc-backbone-modal-main article,.wc-backbone-modal-main header{display:block;position:relative}.wc-backbone-modal-main .wc-backbone-modal-header{height:auto;background:#fcfcfc;padding:1em 1.5em;border-bottom:1px solid #ddd}.wc-backbone-modal-main .wc-backbone-modal-header h1{margin:0;font-size:18px;font-weight:700;line-height:1.5em}.wc-backbone-modal-main .wc-backbone-modal-header .modal-close-link{cursor:pointer;color:#777;height:54px;width:54px;padding:0;position:absolute;top:0;right:0;text-align:center;border:0;border-left:1px solid #ddd;background-color:transparent;-webkit-transition:color .1s ease-in-out,background .1s ease-in-out;transition:color .1s ease-in-out,background .1s ease-in-out}.wc-backbone-modal-main .wc-backbone-modal-header .modal-close-link::before{font:400 22px/50px dashicons!important;color:#666;display:block;content:'\f335';font-weight:300}.wc-backbone-modal-main .wc-backbone-modal-header .modal-close-link:focus,.wc-backbone-modal-main .wc-backbone-modal-header .modal-close-link:hover{background:#ddd;border-color:#ccc;color:#000}.wc-backbone-modal-main .wc-backbone-modal-header .modal-close-link:focus{outline:0}.wc-backbone-modal-main article{padding:1.5em}.wc-backbone-modal-main article p{margin:1.5em 0}.wc-backbone-modal-main article p:last-child,.wc-backbone-modal-main footer .inner .button{margin-bottom:0}.wc-backbone-modal-main article p:first-child{margin-top:0}.wc-backbone-modal-main article .pagination{padding:10px 0 0;text-align:center}.wc-backbone-modal-main footer{position:absolute;left:0;right:0;bottom:0;z-index:100;padding:1em 1.5em;background:#fcfcfc;border-top:1px solid #dfdfdf;box-shadow:0 -4px 4px -4px rgba(0,0,0,.1)}.wc-backbone-modal-main footer .inner{float:right;line-height:23px}.select2-drop{z-index:999999!important}.select2-container-multi .select2-choices .select2-search-field input{font-family:inherit;font-size:inherit;font-weight:inherit;padding:3px 5px}.select2-container{line-height:1.85em;font-size:14px} \ No newline at end of file +@charset "UTF-8";.select2-container .select2-choice,.select2-results .select2-result-label{-webkit-user-select:none;-moz-user-select:none;-webkit-touch-callout:none}.button.wc-reload::after,.woocommerce-help-tip::after{speak:none;font-variant:normal;text-transform:none;top:0}@-webkit-keyframes spin{100%{-webkit-transform:rotate(360deg)}}@-moz-keyframes spin{100%{-moz-transform:rotate(360deg)}}@keyframes spin{100%{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}.select2-container{margin:0;position:relative;display:block!important;zoom:1;vertical-align:middle}.select2-container,.select2-drop,.select2-search,.select2-search input{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.select2-container .select2-choice{display:block;padding:0 0 0 8px;overflow:hidden;position:relative;border:1px solid #ccc;white-space:nowrap;color:#444;text-decoration:none;border-radius:3px;background-clip:padding-box;-ms-user-select:none;user-select:none;background-color:#fff;font-weight:400}html[dir=rtl] .select2-container .select2-choice{padding:0 8px 0 0}.select2-container.select2-drop-above .select2-choice{border-bottom-color:#ccc;border-radius:0 0 4px 4px}.select2-container.select2-allowclear .select2-choice .select2-chosen{margin-right:42px}.select2-container .select2-choice>.select2-chosen{margin-right:26px;display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;float:none;width:auto}html[dir=rtl] .select2-container .select2-choice>.select2-chosen{margin-left:26px;margin-right:0}.select2-container .select2-choice abbr{display:none;width:12px;height:12px;position:absolute;right:24px;top:5px;font-size:1px;text-decoration:none;border:0;background:url(../images/select2.png) right top no-repeat;cursor:pointer;outline:0}.select2-container.select2-allowclear .select2-choice abbr{display:inline-block}.select2-container .select2-choice abbr:hover{background-position:right -11px;cursor:pointer}.select2-drop-mask{border:0;margin:0;padding:0;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:9998;background-color:#fff;filter:alpha(opacity=0)}.select2-drop{width:100%;margin-top:-1px;position:absolute;top:100%;background:#fff;color:#000;border:1px solid #ccc;border-top:0;border-radius:0 0 3px 3px}.select2-drop.select2-drop-above{margin-top:1px;border-top:1px solid #ccc;border-bottom:0;border-radius:3px 3px 0 0}.select2-drop-active{border:1px solid #666;border-top:none}.select2-drop.select2-drop-above.select2-drop-active{border-top:1px solid #666}.select2-drop-auto-width{border-top:1px solid #ccc;width:auto}.select2-drop-auto-width .select2-search{padding-top:4px}.select2-container .select2-choice .select2-arrow{display:inline-block;width:18px;height:100%;position:absolute;right:0;top:0;border-radius:0 3px 3px 0;background-clip:padding-box}html[dir=rtl] .select2-container .select2-choice .select2-arrow{left:0;right:auto;border-radius:3px 0 0 3px}.select2-container .select2-choice .select2-arrow b{display:block;width:100%;height:100%;position:relative}.select2-container .select2-choice .select2-arrow b:after{position:absolute;display:block;content:"";top:50%;left:50%;border:4px solid transparent;border-top-color:#666;margin-left:-7px;margin-top:-2px}.select2-search{display:inline-block;width:100%;margin:0;padding-left:4px;padding-right:4px;position:relative;z-index:10000;white-space:nowrap;padding-bottom:4px}.select2-search input{width:100%;height:auto!important;padding:4px 20px 4px 5px!important;margin:0;outline:0;font-family:sans-serif;font-size:1em;border:1px solid #ccc;-webkit-box-shadow:none;box-shadow:none;background:url(../images/select2.png) 100% -22px no-repeat #fff}html[dir=rtl] .select2-search input{padding:4px 5px 4px 20px;background:url(../images/select2.png) -37px -22px no-repeat #fff}.select2-drop.select2-drop-above .select2-search input{margin-top:4px}.select2-search input.select2-active{background:url(../images/select2-spinner.gif) 100% no-repeat #fff}.select2-container-active .select2-choice,.select2-container-active .select2-choices{border:1px solid #666;outline:0}.select2-dropdown-open .select2-choice{border-bottom-color:transparent;-webkit-box-shadow:0 1px 0 #fff inset;box-shadow:0 1px 0 #fff inset;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-dropdown-open .select2-choice .select2-arrow b:after{border-top-color:transparent;border-bottom-color:#666;margin-top:-6px}.select2-dropdown-open.select2-drop-above .select2-choice,.select2-dropdown-open.select2-drop-above .select2-choices{border:1px solid #666;border-top-color:transparent}.select2-dropdown-open .select2-choice .select2-arrow{background:0 0;border-left:none;filter:none}html[dir=rtl] .select2-dropdown-open .select2-choice .select2-arrow{border-right:none}.select2-dropdown-open .select2-choice .select2-arrow b{background-position:-18px 1px}html[dir=rtl] .select2-dropdown-open .select2-choice .select2-arrow b{background-position:-16px 1px}.select2-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.select2-results{max-height:200px;padding:4px;margin:0;position:relative;overflow-x:hidden;overflow-y:auto;-webkit-tap-highlight-color:transparent;background:#fafafa}html[dir=rtl] .select2-results{padding:0 4px 0 0;margin:4px 0 4px 4px}.select2-results ul.select2-result-sub{margin:0;padding-left:0}.select2-results li{list-style:none;display:list-item;background-image:none;margin:3px 0}.select2-results li.select2-result-with-children>.select2-result-label{font-weight:700}.select2-results .select2-result-label{padding:5px 7px;margin:0;cursor:pointer;min-height:1em;-ms-user-select:none;user-select:none}.select2-results-dept-1 .select2-result-label{padding-left:20px}.select2-results-dept-2 .select2-result-label{padding-left:40px}.select2-results-dept-3 .select2-result-label{padding-left:60px}.select2-results-dept-4 .select2-result-label{padding-left:80px}.select2-results-dept-5 .select2-result-label{padding-left:100px}.select2-results-dept-6 .select2-result-label{padding-left:110px}.select2-results-dept-7 .select2-result-label{padding-left:120px}.select2-results .select2-highlighted{background:#f1f1f1;color:#000;border-radius:3px}.select2-results li em{background:#feffde;font-style:normal}.select2-results .select2-highlighted em{background:0 0}.select2-results .select2-highlighted ul{background:#fff;color:#000}.select2-results .select2-ajax-error,.select2-results .select2-no-results,.select2-results .select2-searching,.select2-results .select2-selection-limit{background:#f4f4f4;display:list-item;padding-left:5px}.select2-results .select2-disabled.select2-highlighted{color:#666;background:#f4f4f4;display:list-item;cursor:default}.select2-results .select2-disabled{background:#f4f4f4;display:list-item;cursor:default}.select2-results .select2-selected{display:none}.select2-more-results.select2-active{background:url(../images/select2-spinner.gif) 100% no-repeat #f4f4f4}.select2-results .select2-ajax-error{background:rgba(255,50,50,.2)}.select2-more-results{background:#f4f4f4;display:list-item}.select2-container.select2-container-disabled .select2-choice{background-color:#f4f4f4;background-image:none;border:1px solid #ddd;cursor:default}.select2-container.select2-container-disabled .select2-choice .select2-arrow{background-color:#f4f4f4;background-image:none;border-left:0}.select2-container.select2-container-disabled .select2-choice abbr{display:none}.select2-container-multi .select2-choices{height:auto!important;height:1%;margin:0;padding:0 5px 0 0;position:relative;border:1px solid #ccc;cursor:text;overflow:hidden;background-color:#fff;min-height:26px}html[dir=rtl] .select2-container-multi .select2-choices{padding:0 0 0 5px}.select2-locked{padding:3px 5px!important}.select2-container-multi.select2-container-active .select2-choices{border:1px solid #666;outline:0}.select2-container-multi .select2-choices li{float:left;list-style:none}html[dir=rtl] .select2-container-multi .select2-choices li{float:right}.select2-container-multi .select2-choices .select2-search-field{margin:0;padding:0;white-space:nowrap}.select2-container-multi .select2-choices .select2-search-field:first-child{width:100%}.select2-container-multi .select2-choices .select2-search-field input{margin:1px 0;outline:0;border:0;-webkit-box-shadow:none;box-shadow:none;background:0 0!important}.select2-container-multi .select2-choices .select2-search-field input.select2-active{background:url(../images/select2-spinner.gif) 100% no-repeat #fff!important}.select2-default{color:#999!important}.select2-container-multi .select2-choices .select2-search-choice{padding:5px 8px 5px 24px;margin:3px 0 3px 5px;position:relative;line-height:15px;color:#333;cursor:default;border-radius:2px;background-clip:padding-box;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#e4e4e4}.select2-container-multi .ui-sortable .select2-search-choice{cursor:move}html[dir=rtl] .select2-container-multi .select2-choices .select2-search-choice{margin:3px 5px 3px 0;padding:5px 24px 5px 8px}.select2-container-multi .select2-choices .select2-search-choice .select2-chosen{cursor:default}.select2-container-multi .select2-choices .select2-search-choice-focus{background:#d4d4d4}.select2-search-choice-close{display:block;width:12px;height:13px;position:absolute;right:7px;top:6px;font-size:1px;outline:0;background:url(../images/select2.png) right top no-repeat}html[dir=rtl] .select2-search-choice-close{right:auto;left:7px}.select2-container-multi .select2-search-choice-close{left:7px}html[dir=rtl] .select2-container-multi .select2-search-choice-close{left:auto;right:7px}.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover,.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close{background-position:right -11px}.select2-container-multi.select2-container-disabled .select2-choices{background-color:#f4f4f4;background-image:none;border:1px solid #ddd;cursor:default}.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice{padding:3px 5px;border:1px solid #ddd;background-image:none;background-color:#f4f4f4}.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close{display:none;background:0 0}.select2-result-selectable .select2-match,.select2-result-unselectable .select2-match{text-decoration:underline}.select2-offscreen,.select2-offscreen:focus{clip:rect(0 0 0 0)!important;width:1px!important;height:1px!important;border:0!important;margin:0!important;padding:0!important;overflow:hidden!important;position:absolute!important;outline:0!important;left:0!important;top:0!important}.select2-display-none{display:none}.select2-measure-scrollbar{position:absolute;top:-10000px;left:-10000px;width:100px;height:100px;overflow:scroll}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-resolution:2dppx){.select2-search input{background-image:url(../images/select2x2.png)!important;background-repeat:no-repeat!important;background-size:60px 40px!important;background-position:100% -21px!important}}@font-face{font-family:star;src:url(../fonts/star.eot);src:url(../fonts/star.eot?#iefix) format("embedded-opentype"),url(../fonts/star.woff) format("woff"),url(../fonts/star.ttf) format("truetype"),url(../fonts/star.svg#star) format("svg");font-weight:400;font-style:normal}@font-face{font-family:WooCommerce;src:url(../fonts/WooCommerce.eot);src:url(../fonts/WooCommerce.eot?#iefix) format("embedded-opentype"),url(../fonts/WooCommerce.woff) format("woff"),url(../fonts/WooCommerce.ttf) format("truetype"),url(../fonts/WooCommerce.svg#WooCommerce) format("svg");font-weight:400;font-style:normal}.blockUI.blockOverlay::before{height:1em;width:1em;display:block;position:absolute;top:50%;left:50%;margin-left:-.5em;margin-top:-.5em;content:'';-webkit-animation:spin 1s ease-in-out infinite;-moz-animation:spin 1s ease-in-out infinite;animation:spin 1s ease-in-out infinite;background:url(../images/icons/loader.svg) center center;background-size:cover;line-height:1;text-align:center;font-size:2em;color:rgba(0,0,0,.75)}.wc_addons_wrap .addons-featured{max-width:1140px;margin:-1%}.wc_addons_wrap .addons-banner-block-item-icon,.wc_addons_wrap .addons-column-block-item-icon{align-items:center;display:flex;justify-content:center}.wc_addons_wrap .addons-banner-block{background:#fff;box-shadow:0 0 1px rgba(0,0,0,.2);margin:2% 1% 0;max-width:1140px;padding:20px}.wc_addons_wrap .addons-banner-block img{height:62px}.wc_addons_wrap .addons-banner-block p{margin:0 0 20px}.wc_addons_wrap .addons-banner-block-items{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-around;margin:0 -10px}.wc_addons_wrap .addons-banner-block-item{border:1px solid #e6e6e6;border-radius:3px;flex:1 1 200px;margin:10px;max-width:350px;min-width:200px;width:30%}.wc_addons_wrap .addons-banner-block-item-icon{background:#f7f7f7;height:143px}.wc_addons_wrap .addons-banner-block-item-content{display:flex;flex-direction:column;height:184px;justify-content:space-between;padding:24px}.wc_addons_wrap .addons-banner-block-item-content h3{margin-top:0}.wc_addons_wrap .addons-banner-block-item-content p{margin:0 0 auto}.wc_addons_wrap .addons-column-section{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-around}.wc_addons_wrap .addons-column{flex:1 1 200px;margin:0 1%;width:49.5%}.wc_addons_wrap .addons-column-block,.wc_addons_wrap .addons-small-dark-block,.wc_addons_wrap .addons-small-light-block{box-sizing:border-box;box-shadow:0 0 1px rgba(0,0,0,.2);margin:4% 0 0;padding:20px}.wc_addons_wrap .addons-column-block img{max-height:50px;max-width:50px}.wc_addons_wrap .addons-column-block,.wc_addons_wrap .addons-small-light-block{background:#fff}.wc_addons_wrap .addons-column-block-left{float:left}.wc_addons_wrap .addons-column-block-right{float:right}.wc_addons_wrap .addons-column-block-item{display:flex;border-top:2px solid #f9f9f9;flex-direction:row;flex-wrap:wrap;justify-content:space-between;margin:0 -20px;padding:20px}.wc_addons_wrap .addons-column-block-item-icon{background:#f7f7f7;border:1px solid #e6e6e6;height:100px;margin:0 10px 10px 0;width:100px}.wc_addons_wrap .addons-column-block-item-content{display:flex;flex:1 1 200px;flex-wrap:wrap;height:20%;justify-content:space-between;min-width:200px}.wc_addons_wrap .addons-column-block-item-content h2{float:left;margin-top:8px}.wc_addons_wrap .addons-column-block-item-content a{float:right}.wc_addons_wrap .addons-column-block-item-content p{float:left}.wc_addons_wrap .addons-small-dark-block{background-color:#54687d;text-align:center}.wc_addons_wrap .addons-small-dark-items{display:flex;flex-wrap:wrap;justify-content:space-around}.wc_addons_wrap .addons-small-dark-item{margin:0 0 20px}.wc_addons_wrap .addons-small-dark-block h1{color:#fff}.wc_addons_wrap .addons-small-dark-block p{color:#fafafa}.wc_addons_wrap .addons-small-dark-item-icon img{height:30px}.wc_addons_wrap .addons-small-dark-item a{margin:28px auto 0}.wc_addons_wrap .addons-small-light-block{display:flex;flex-wrap:wrap}.wc_addons_wrap .addons-small-light-block h1{margin-top:-12px}.wc_addons_wrap .addons-small-light-block p{margin-top:0}.wc_addons_wrap .addons-small-light-block img{height:225px;margin:0 0 0 -20px}.wc_addons_wrap .addons-small-light-block-content{display:flex;flex:1 1 100px;flex-direction:column;justify-content:space-around}.wc_addons_wrap .addons-small-light-block-buttons{display:flex;justify-content:space-between}.wc_addons_wrap .addons-small-light-block-content a{width:48%}.wc_addons_wrap .addons-button{border-radius:3px;cursor:pointer;display:block;height:37px;line-height:37px;text-align:center;text-decoration:none;width:124px}.wc_addons_wrap .addons-button-solid{background-color:#955a89;color:#fff}.wc_addons_wrap .addons-button-solid:hover{color:#fff;opacity:.8}.wc_addons_wrap .addons-button-outline-green{border:1px solid #73ae39;color:#73ae39}.wc_addons_wrap .addons-button-outline-green:hover{color:#73ae39;opacity:.8}.wc_addons_wrap .addons-button-outline-white{border:1px solid #fff;color:#fff}.wc_addons_wrap .addons-button-outline-white:hover{color:#fff;opacity:.8}.wc_addons_wrap .addons-button-installed{background:#e6e6e6;color:#3c3c3c}.wc_addons_wrap .addons-button-installed:hover{color:#3c3c3c;opacity:.8}@media only screen and (max-width:400px){.wc_addons_wrap .addons-featured{margin:-1% -5%}.wc_addons_wrap .addons-button,.wc_addons_wrap .addons-small-dark-item{width:100%}.wc_addons_wrap .addons-column-block-item-icon{background:0 0;border:none;height:75px;margin:0 10px 10px 0;width:75px}}.wc_addons_wrap .products{overflow:hidden}.wc_addons_wrap .products li{float:left;margin:0 1em 1em 0!important;padding:0;vertical-align:top;width:300px}.wc_addons_wrap .products li a{text-decoration:none;color:inherit;border:1px solid #ddd;display:block;min-height:220px;overflow:hidden;background:#f5f5f5;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),inset 0 -1px 0 rgba(0,0,0,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),inset 0 -1px 0 rgba(0,0,0,.1)}.wc_addons_wrap .products li a img{max-width:258px;max-height:24px;padding:17px 20px;display:block;margin:0;background:#fff;border-right:260px solid #fff}.wc_addons_wrap .products li a .price,.wc_addons_wrap .products li a img.extension-thumb+h3{display:none}.wc_addons_wrap .products li a h2,.wc_addons_wrap .products li a h3{margin:0!important;padding:20px!important;background:#fff}.wc_addons_wrap .products li a p{padding:20px!important;margin:0!important;border-top:1px solid #f1f1f1}.wc_addons_wrap .products li a:focus,.wc_addons_wrap .products li a:hover{background-color:#fff}.wc_addons_wrap .storefront{background:url(../images/storefront-bg.jpg) bottom right #f6f6f6;border:1px solid #ddd;padding:20px;overflow:hidden;zoom:1}.wc_addons_wrap .storefront img{width:278px;height:auto;float:left;margin:0 20px 0 0;box-shadow:0 1px 6px rgba(0,0,0,.1)}.wc_addons_wrap .storefront p{max-width:750px}.woocommerce-BlankState a.button-primary,.woocommerce-BlankState button.button-primary,.woocommerce-message a.button-primary,.woocommerce-message button.button-primary{background:#bb77ae;border-color:#a36597;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597;box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597;color:#fff;text-shadow:0 -1px 1px #a36597,1px 0 1px #a36597,0 1px 1px #a36597,-1px 0 1px #a36597}.woocommerce-BlankState a.button-primary:active,.woocommerce-BlankState a.button-primary:focus,.woocommerce-BlankState a.button-primary:hover,.woocommerce-BlankState button.button-primary:active,.woocommerce-BlankState button.button-primary:focus,.woocommerce-BlankState button.button-primary:hover,.woocommerce-message a.button-primary:active,.woocommerce-message a.button-primary:focus,.woocommerce-message a.button-primary:hover,.woocommerce-message button.button-primary:active,.woocommerce-message button.button-primary:focus,.woocommerce-message button.button-primary:hover{background:#a36597;border-color:#a36597;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597;box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 0 #a36597}.woocommerce-message{position:relative;border-left-color:#cc99c2!important;overflow:hidden}.woocommerce-message a.docs,.woocommerce-message a.skip{text-decoration:none!important}.woocommerce-message a.woocommerce-message-close{position:absolute;top:10px;right:10px;padding:10px 15px 10px 21px;font-size:13px;line-height:1.23076923;text-decoration:none}.woocommerce-message a.woocommerce-message-close::before{position:absolute;top:8px;left:0;-webkit-transition:all .1s ease-in-out;transition:all .1s ease-in-out}.woocommerce-message .twitter-share-button{margin-top:-3px;margin-left:3px;vertical-align:middle}#variable_product_options #message,#variable_product_options .notice{margin:10px}.clear{clear:both}#woocommerce-fields-bulk.inline-edit-col label,#woocommerce-fields.inline-edit-col{clear:left}.wrap.woocommerce div.error,.wrap.woocommerce div.updated{margin-top:10px}mark.amount{background:0 0;color:inherit}.simplify-commerce-banner{overflow:hidden}.simplify-commerce-banner img{float:right;padding:15px 0;margin-left:1em;width:200px}.woocommerce-help-tip{color:#666;display:inline-block;font-size:1.1em;font-style:normal;height:16px;line-height:16px;position:relative;vertical-align:middle;width:16px}.woocommerce-help-tip::after{font-family:Dashicons;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;left:0;width:100%;height:100%;text-align:center;content:"";cursor:help}h2 .woocommerce-help-tip{margin-top:-5px;margin-left:.25em}table.wc_status_table{margin-bottom:1em}table.wc_status_table h2{font-size:14px;margin:0}table.wc_status_table tr:nth-child(2n) td,table.wc_status_table tr:nth-child(2n) th{background:#fcfcfc}table.wc_status_table th{font-weight:700;padding:9px}table.wc_status_table td:first-child{width:33%}table.wc_status_table td.help{width:1em}table.wc_status_table td{padding:9px;font-size:1.1em}table.wc_status_table td mark{background:0 0}table.wc_status_table td mark.yes{color:#7ad03a}table.wc_status_table td mark.no{color:#999}table.wc_status_table td mark.error{color:#a00}table.wc_status_table td ul{margin:0}table.wc_status_table .help_tip{cursor:help}#debug-report{display:none;margin:10px 0;padding:0;position:relative}#debug-report textarea{font-family:monospace;width:100%;margin:0;height:300px;padding:20px;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;resize:none;font-size:12px;line-height:20px;outline:0}#log-viewer-select{padding:10px 0 8px;line-height:28px}#log-viewer-select h2 a{vertical-align:middle}#log-viewer{background:#fff;border:1px solid #e5e5e5;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.04);box-shadow:0 1px 1px rgba(0,0,0,.04);padding:5px 20px}#log-viewer pre{font-family:monospace;white-space:pre-wrap}.inline-edit-product.quick-edit-row .inline-edit-col-center,.inline-edit-product.quick-edit-row .inline-edit-col-right{float:right!important}#woocommerce-fields.inline-edit-col label.featured,#woocommerce-fields.inline-edit-col label.manage_stock{margin-left:10px}#woocommerce-fields.inline-edit-col .dimensions div{display:block;margin:.2em 0}#woocommerce-fields.inline-edit-col .dimensions div span.title{display:block;float:left;width:5em}#woocommerce-fields.inline-edit-col .dimensions div span.input-text-wrap{display:block;margin-left:5em}#woocommerce-fields.inline-edit-col .text{box-sizing:border-box;width:99%;float:left;margin:1px 1% 1px 1px}#woocommerce-fields.inline-edit-col .height,#woocommerce-fields.inline-edit-col .length,#woocommerce-fields.inline-edit-col .width{width:32.33%}#woocommerce-fields.inline-edit-col .height{margin-right:0}#woocommerce-fields-bulk.inline-edit-col .inline-edit-group label{clear:none;width:49%;margin:.2em 0}#woocommerce-fields-bulk.inline-edit-col .inline-edit-group.dimensions label{width:75%;max-width:75%}#woocommerce-fields-bulk.inline-edit-col .length,#woocommerce-fields-bulk.inline-edit-col .regular_price,#woocommerce-fields-bulk.inline-edit-col .sale_price,#woocommerce-fields-bulk.inline-edit-col .stock,#woocommerce-fields-bulk.inline-edit-col .weight{box-sizing:border-box;width:100%;margin-left:4.4em}#woocommerce-fields-bulk.inline-edit-col .height,#woocommerce-fields-bulk.inline-edit-col .length,#woocommerce-fields-bulk.inline-edit-col .width{box-sizing:border-box;width:25%}.column-coupon_code{line-height:2.25em}.column-coupon_code,ul.wc_coupon_list{margin:0;overflow:hidden;zoom:1;clear:both}ul.wc_coupon_list li{margin:0}ul.wc_coupon_list li.code{display:inline-block}ul.wc_coupon_list li.code::after{content:', '}ul.wc_coupon_list li.code:last-of-type::after{display:none}ul.wc_coupon_list li.code .tips{cursor:pointer}ul.wc_coupon_list_block{margin:0;padding-bottom:2px}ul.wc_coupon_list_block li{border-top:1px solid #fff;border-bottom:1px solid #ccc;line-height:2.5em;margin:0;padding:.5em 0}ul.wc_coupon_list_block li:first-child{border-top:0;padding-top:0}ul.wc_coupon_list_block li:last-child{border-bottom:0;padding-bottom:0}.button.wc-reload{text-indent:-9999px;position:relative;padding:0;height:28px;width:28px!important;display:inline-block}.button.wc-reload::after{font-family:Dashicons;font-weight:400;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;left:0;width:100%;height:100%;text-align:center;content:"";line-height:28px}#order_data h2,#order_data p.order_number{font-family:HelveticaNeue-Light,'Helvetica Neue Light','Helvetica Neue',sans-serif;font-weight:400}.tablenav .actions{overflow:visible}.tablenav .select2-container{float:left;max-width:200px;font-size:14px;vertical-align:middle;margin:1px 6px 1px 1px}#woocommerce-order-data .handlediv,#woocommerce-order-data .hndle{display:none}#woocommerce-order-data .inside{display:block!important}#order_data{padding:23px 24px 12px}#order_data h2{margin:0;font-size:21px;line-height:1.2;text-shadow:1px 1px 1px #fff;padding:0}#order_data h3{font-size:14px}#order_data h3,#order_data h4{color:#333;margin:1.33em 0 0}#order_data p{color:#777}#order_data p.order_number{margin:0;line-height:1.6em;font-size:16px}#order_data .order_data_column_container{clear:both}#order_data .order_data_column{width:32%;padding:0 2% 0 0;float:left}#order_data .order_data_column:last-child{padding-right:0}#order_data .order_data_column p{padding:0!important}#order_data .order_data_column .address strong{display:block}#order_data .order_data_column .form-field{float:left;width:48%;padding:0;margin:9px 0 0}#order_data .order_data_column .form-field label{display:block;padding:0 0 3px}#order_data .order_data_column .form-field input,#order_data .order_data_column .form-field select,#order_data .order_data_column .form-field textarea{width:100%}#order_data .order_data_column .form-field .select2-container{width:100%!important}#order_data .order_data_column .form-field .date-picker{width:50%}#order_data .order_data_column .form-field .hour,#order_data .order_data_column .form-field .minute{width:3.5em}#order_data .order_data_column .form-field small{display:block;margin:5px 0 0;color:#999}#order_data .order_data_column .form-field.last{float:right}#order_data .order_data_column .form-field-wide{width:100%;clear:both}#order_data .order_data_column .form-field-wide .wc-customer-search,#order_data .order_data_column .form-field-wide .wc-enhanced-select,#order_data .order_data_column .form-field-wide input,#order_data .order_data_column .form-field-wide select,#order_data .order_data_column .form-field-wide textarea{width:100%}#order_data .order_data_column p.none_set{color:#999}#order_data .order_data_column ._billing_address_1_field,#order_data .order_data_column ._billing_city_field,#order_data .order_data_column ._billing_country_field,#order_data .order_data_column ._billing_email_field,#order_data .order_data_column ._billing_first_name_field,#order_data .order_data_column ._shipping_address_1_field,#order_data .order_data_column ._shipping_city_field,#order_data .order_data_column ._shipping_country_field,#order_data .order_data_column ._shipping_first_name_field{float:left}#order_data .order_data_column ._billing_address_2_field,#order_data .order_data_column ._billing_last_name_field,#order_data .order_data_column ._billing_phone_field,#order_data .order_data_column ._billing_postcode_field,#order_data .order_data_column ._billing_state_field,#order_data .order_data_column ._shipping_address_2_field,#order_data .order_data_column ._shipping_last_name_field,#order_data .order_data_column ._shipping_postcode_field,#order_data .order_data_column ._shipping_state_field,#order_data .order_data_column .wc-customer-user label a,#order_data .order_data_column .wc-order-status label a{float:right}#order_data .order_data_column ._billing_company_field,#order_data .order_data_column ._shipping_company_field,#order_data .order_data_column ._transaction_id_field{clear:both;width:100%}#order_data .order_data_column ._billing_email_field{clear:left}#order_data .order_data_column div.edit_address{display:none;zoom:1;padding-right:1px}#order_data .order_data_column .billing-same-as-shipping,#order_data .order_data_column .load_customer_billing,#order_data .order_data_column .load_customer_shipping,#order_data .order_data_column a.edit_address{width:14px;height:0;padding:14px 0 0;margin:0 0 0 6px;overflow:hidden;position:relative;color:#999;border:0;float:right}#order_data .order_data_column .billing-same-as-shipping:focus,#order_data .order_data_column .billing-same-as-shipping:hover,#order_data .order_data_column .load_customer_billing:focus,#order_data .order_data_column .load_customer_billing:hover,#order_data .order_data_column .load_customer_shipping:focus,#order_data .order_data_column .load_customer_shipping:hover,#order_data .order_data_column a.edit_address:focus,#order_data .order_data_column a.edit_address:hover{color:#000}#order_data .order_data_column .billing-same-as-shipping::after,#order_data .order_data_column .load_customer_billing::after,#order_data .order_data_column .load_customer_shipping::after,#order_data .order_data_column a.edit_address::after{font-family:WooCommerce;position:absolute;top:0;left:0;text-align:center;vertical-align:top;line-height:14px;font-size:14px;font-weight:400;-webkit-font-smoothing:antialiased}#order_data .order_data_column .billing-same-as-shipping::after{content:'\e008'}#order_data .order_data_column .load_customer_billing::after,#order_data .order_data_column .load_customer_shipping::after{content:'\e03a'}#order_data .order_data_column a.edit_address::after{font-family:Dashicons;content:'\f464'}.order_actions{margin:0;overflow:hidden;zoom:1}.order_actions li{border-top:1px solid #fff;border-bottom:1px solid #ddd;padding:6px 0;margin:0;line-height:1.6em;float:left;width:50%;text-align:center}.order_actions li a{float:none;text-align:center;text-decoration:underline}.order_actions li.wide{width:auto;float:none;clear:both;padding:6px;text-align:left;overflow:hidden}.order_actions li #delete-action{line-height:25px;vertical-align:middle;text-align:left;float:left}.order_actions li .save_order{float:right}.order_actions li#actions{overflow:hidden}.order_actions li#actions .button{width:24px;box-sizing:border-box;float:right}.order_actions li#actions select{width:225px;box-sizing:border-box;float:left}#woocommerce-order-items .inside{margin:0;padding:0;background:#fefefe}#woocommerce-order-items .wc-order-data-row{border-bottom:1px solid #dfdfdf;padding:1.5em 2em;background:#f8f8f8;line-height:2em;text-align:right}#woocommerce-order-items .wc-order-data-row::after,#woocommerce-order-items .wc-order-data-row::before{content:' ';display:table}#woocommerce-order-items .wc-order-data-row::after{clear:both}#woocommerce-order-items .wc-order-data-row p{margin:0;line-height:2em}#woocommerce-order-items .wc-order-data-row .wc-used-coupons{text-align:left}#woocommerce-order-items .wc-order-data-row .wc-used-coupons .tips{display:inline-block}#woocommerce-order-items .wc-order-add-item{background:#fff;vertical-align:top;border-top:none}#woocommerce-order-items .wc-order-add-item .add_item_id,#woocommerce-order-items .wc-order-add-item .select2-container{vertical-align:top}#woocommerce-order-items .wc-order-add-item .add_item_id .search-field input,#woocommerce-order-items .wc-order-add-item .select2-container .search-field input{min-width:100px}#woocommerce-order-items .wc-order-add-item .select2-container{width:400px!important;text-align:left}#woocommerce-order-items .wc-order-add-item .calculate-action,#woocommerce-order-items .wc-order-add-item .cancel-action,#woocommerce-order-items .wc-order-add-item .save-action{float:left;margin-right:2px}#woocommerce-order-items .wc-used-coupons{float:left;width:50%}#woocommerce-order-items .wc-order-totals{float:right;width:50%;margin:0;padding:0;text-align:right}#woocommerce-order-items .wc-order-totals .amount{font-weight:700}#woocommerce-order-items .wc-order-totals .label{vertical-align:top}#woocommerce-order-items .wc-order-totals .total{font-size:1em!important;width:10em;margin:0 0 0 .5em;box-sizing:border-box}#woocommerce-order-items .wc-order-totals .total input[type=text]{width:96%;float:right}#woocommerce-order-items .wc-order-totals .refunded-total{color:#a00}#woocommerce-order-items .refund-actions{margin-top:5px;padding-top:12px;border-top:1px solid #dfdfdf}#woocommerce-order-items .refund-actions .button{float:right;margin-left:4px}#woocommerce-order-items .refund-actions .cancel-action,#woocommerce-order-items .wc-order-item-bulk-edit .cancel-action{float:left;margin-left:0}#woocommerce-order-items .add_meta{margin-left:0!important}#woocommerce-order-items h3 small{color:#999}#woocommerce-order-items .amount{white-space:nowrap}#woocommerce-order-items .add-items .description{margin-right:10px}#woocommerce-order-items .add-items .button{float:left;margin-right:.25em}#woocommerce-order-items .add-items .button-primary{float:none;margin-right:0}#woocommerce-order-items .inside{display:block!important}#woocommerce-order-items .handlediv,#woocommerce-order-items .hndle{display:none}#woocommerce-order-items .woocommerce_order_items_wrapper{margin:0;overflow-x:auto}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items{width:100%;background:#fff}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items thead th{text-align:left;padding:1em;font-weight:400;color:#999;background:#f8f8f8;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items thead th.sortable{cursor:pointer}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items thead th:last-child{padding-right:2em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items thead th:first-child{padding-left:2em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items thead th .wc-arrow{float:right;position:relative;margin-right:-1em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody th,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td{padding:1.5em 1em 1em;text-align:left;line-height:1.5em;vertical-align:top;border-bottom:1px solid #f8f8f8}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody th textarea,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td textarea{width:100%}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody th select,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td select{width:50%}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody th input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody th textarea,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td textarea{font-size:14px;padding:4px;color:#555}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody th:last-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td:last-child{padding-right:2em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody th:first-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td:first-child{padding-left:2em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody tr td{cursor:pointer}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody tr.selected{background:#f5ebf3}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody tr.selected td{border-color:#e6cce1;opacity:.8}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody tr:last-child td{border-bottom:1px solid #dfdfdf}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody tr:first-child td{border-top:8px solid #f8f8f8}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tbody#order_line_items tr:first-child td{border-top:none}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.thumb{text-align:left;width:38px;padding-bottom:1.5em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.thumb .wc-order-item-thumbnail{width:38px;height:38px;border:2px solid #e8e8e8;background:#f8f8f8;color:#ccc;position:relative;font-size:21px;display:block;text-align:center}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.thumb .wc-order-item-thumbnail::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;height:100%;text-align:center;content:"";width:38px;line-height:38px;display:block}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.thumb .wc-order-item-thumbnail img{width:100%;height:100%;margin:0;padding:0;position:relative}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.name .wc-order-item-sku,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.name .wc-order-item-variation{display:block;margin-top:.5em;font-size:.92em!important;color:#888}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item{min-width:200px}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .center,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .variation-id{text-align:center}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class{text-align:right}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class label{white-space:nowrap;color:#999;font-size:.833em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost label input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost label input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost label input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax label input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity label input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax label input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class label input{display:inline}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class input{width:70px;vertical-align:middle;text-align:right}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost select,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost select,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost select,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax select,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity select,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax select,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class select{width:85px;height:26px;vertical-align:middle;font-size:1em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input{display:inline-block;background:#fff;border:1px solid #ddd;box-shadow:inset 0 1px 2px rgba(0,0,0,.07);margin:1px 0;min-width:80px;overflow:hidden;line-height:1em;text-align:right}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input div.input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input div.input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input div.input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input div.input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input div.input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input div.input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input div.input{width:100%;box-sizing:border-box}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input div.input label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input div.input label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input div.input label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input div.input label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input div.input label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input div.input label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input div.input label{font-size:.75em;padding:4px 6px 0;color:#555;display:block}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input div.input input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input div.input input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input div.input input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input div.input input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input div.input input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input div.input input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input div.input input{width:100%;box-sizing:border-box;border:0;box-shadow:none;margin:0;padding:0 6px 4px;color:#555;background:0 0}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input div.input input::-webkit-input-placeholder,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input div.input input::-webkit-input-placeholder,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input div.input input::-webkit-input-placeholder,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input div.input input::-webkit-input-placeholder,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input div.input input::-webkit-input-placeholder,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input div.input input::-webkit-input-placeholder,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input div.input input::-webkit-input-placeholder{color:#ddd}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input div.input:first-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input div.input:first-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input div.input:first-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input div.input:first-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input div.input:first-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input div.input:first-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input div.input:first-child{border-bottom:1px dashed #ddd;background:#fff}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input div.input:first-child input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .split-input div.input:first-child label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input div.input:first-child input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .split-input div.input:first-child label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input div.input:first-child input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .split-input div.input:first-child label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input div.input:first-child input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .split-input div.input:first-child label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input div.input:first-child input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .split-input div.input:first-child label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input div.input:first-child input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .split-input div.input:first-child label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input div.input:first-child input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .split-input div.input:first-child label{color:#ccc}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .view,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .view,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .view,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .view,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .view,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .view,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .view{white-space:nowrap}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .edit,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .edit,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .edit,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .edit,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .edit,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .edit,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .edit{text-align:left}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost del,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost del,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost del,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax del,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity del,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax del,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class del,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class small.times{font-size:.92em!important;color:#888}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-taxes,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-refund-fields,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-taxes{margin:0}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-refund-fields label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-taxes label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-refund-fields label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-taxes label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-refund-fields label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-taxes label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-refund-fields label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-taxes label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-refund-fields label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-taxes label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-refund-fields label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-taxes label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-refund-fields label,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-taxes label{display:block}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax .wc-order-item-discount,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class .wc-order-item-discount{display:block;margin-top:.5em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .cost small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .item_cost small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_cost small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .line_tax small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax small.times,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .tax_class small.times{margin-right:.25em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity{text-align:center}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .quantity input{text-align:center;width:50px}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items span.subtotal{opacity:.5}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.tax_class,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items th.tax_class{text-align:left}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .calculated{border-color:#ae8ca2;border-style:dotted}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta{width:100%}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta{margin:.5em 0 0;font-size:.92em!important;color:#888}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.fee .thumb div::before,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.refund .thumb div::before,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.shipping .thumb div::before{color:#ccc;top:0;left:0;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;text-align:center;font-family:WooCommerce}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr th,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr th{border:0;padding:0 4px .5em 0;line-height:1.5em;width:20%}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr td,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr td{padding:0 4px .5em 0;border:0;line-height:1.5em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr td input,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr td input{width:100%;margin:0;position:relative;border-bottom:0;box-shadow:none}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items .refund_by,ul.order_notes li p.meta .exact-date{border-bottom:1px dotted #999}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr td textarea,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr td textarea{width:100%;height:4em;margin:0;box-shadow:none}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr td input:focus+textarea,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr td input:focus+textarea{border-top-color:#999}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr td p,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr td p{margin:0 0 .5em;line-height:1.5em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr td p:last-child,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr td p:last-child{margin:0}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.fee .thumb div{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;font-size:1.5em;line-height:1em;vertical-align:middle;margin:0}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.fee .thumb div::before{line-height:1;margin:0;text-indent:0;position:absolute;width:100%;height:100%;content:""}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.refund .thumb div{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;font-size:1.5em;line-height:1em;vertical-align:middle;margin:0}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.refund .thumb div::before{line-height:1;margin:0;text-indent:0;position:absolute;width:100%;height:100%;content:""}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.shipping .thumb div{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;font-size:1.5em;line-height:1em;vertical-align:middle;margin:0}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.shipping .thumb div::before{line-height:1;margin:0;text-indent:0;position:absolute;width:100%;height:100%;content:""}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.shipping .shipping_method,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items tr.shipping .shipping_method_name{width:100%;margin:0 0 .5em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items th.line_tax{white-space:nowrap}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.line_tax .delete-order-tax,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items th.line_tax .delete-order-tax{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;float:right;font-size:14px;visibility:hidden;margin:3px -18px 0 0}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.line_tax .delete-order-tax::before,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items th.line_tax .delete-order-tax::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";color:#999}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.line_tax .delete-order-tax:hover::before,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items th.line_tax .delete-order-tax:hover::before{color:#a00}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items td.line_tax:hover .delete-order-tax,#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items th.line_tax:hover .delete-order-tax{visibility:visible}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items small.refunded{display:block;color:#a00;white-space:nowrap;margin-top:.5em}#woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items small.refunded::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;text-indent:0;width:100%;height:100%;text-align:center;content:"";position:relative;top:auto;left:auto;margin:-1px 4px 0 0;vertical-align:middle;line-height:1em}#woocommerce-order-items .wc-order-edit-line-item{padding-left:0}#woocommerce-order-items .wc-order-edit-line-item-actions{width:44px;text-align:right;padding-left:0;vertical-align:middle}#woocommerce-order-items .wc-order-edit-line-item-actions a{color:#ccc;display:inline-block;cursor:pointer;padding:0 0 .5em;margin:0 0 0 12px;vertical-align:middle;text-decoration:none;line-height:16px;width:16px;overflow:hidden}#woocommerce-order-items .wc-order-edit-line-item-actions a::before{margin:0;padding:0;font-size:16px;width:16px;height:16px}#woocommerce-order-items .wc-order-edit-line-item-actions .delete-order-item::before,#woocommerce-order-items .wc-order-edit-line-item-actions .delete_refund::before,#woocommerce-order-items .wc-order-edit-line-item-actions .edit-order-item::before{font-family:Dashicons;-webkit-font-smoothing:antialiased;top:0;left:0;width:100%;height:100%;margin:0;text-align:center;position:relative;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;text-indent:0}#woocommerce-order-items .wc-order-edit-line-item-actions a:hover::before{color:#999}#woocommerce-order-items .wc-order-edit-line-item-actions a:first-child{margin-left:0}#woocommerce-order-items .wc-order-edit-line-item-actions .edit-order-item::before{content:""}#woocommerce-order-items .wc-order-edit-line-item-actions .delete-order-item::before,#woocommerce-order-items .wc-order-edit-line-item-actions .delete_refund::before{content:""}#woocommerce-order-items .wc-order-edit-line-item-actions .delete-order-item:hover::before,#woocommerce-order-items .wc-order-edit-line-item-actions .delete_refund:hover::before{color:#a00}#woocommerce-order-items tbody tr .wc-order-edit-line-item-actions{visibility:hidden}#woocommerce-order-items tbody tr:hover .wc-order-edit-line-item-actions{visibility:visible}#woocommerce-order-items .wc-order-totals .wc-order-edit-line-item-actions{width:1.5em;visibility:visible!important}#woocommerce-order-items .wc-order-totals .wc-order-edit-line-item-actions a{padding:0}#woocommerce-order-downloads .buttons{float:left;padding:0;margin:0;vertical-align:top}#woocommerce-order-downloads .buttons .add_item_id,#woocommerce-order-downloads .buttons .select2-container{width:400px!important;margin-right:9px;vertical-align:top;float:left}#woocommerce-order-downloads .buttons button{margin:2px 0 0}#woocommerce-order-downloads h3 small{color:#999}#poststuff #woocommerce-order-actions .inside{margin:0;padding:0}#poststuff #woocommerce-order-actions .inside ul.order_actions li{padding:6px 10px;box-sizing:border-box}#poststuff #woocommerce-order-actions .inside ul.order_actions li:last-child{border-bottom:0}#poststuff #woocommerce-order-notes .inside{margin:0;padding:0}#poststuff #woocommerce-order-notes .inside ul.order_notes li{padding:0 10px}#woocommerce_customers p.search-box{margin:6px 0 4px;float:left}#woocommerce_customers .tablenav{float:right;clear:none}#woocommerce-product-images .inside #product_images_container ul::after,.woocommerce_variable_attributes .data::after{clear:both}.widefat.customers td{vertical-align:middle;padding:4px 7px}.widefat .column-order_title{width:15%}.widefat .column-order_title time{display:block;color:#999;margin:3px 0}.widefat .column-orders,.widefat .column-paying,.widefat .column-spent{text-align:center;width:8%}.widefat .column-last_order{width:11%}.widefat .column-order_actions,.widefat .column-user_actions,.widefat .column-wc_actions{width:110px}.widefat .column-order_actions a.button,.widefat .column-user_actions a.button,.widefat .column-wc_actions a.button{float:left;margin:0 4px 2px 0;cursor:pointer;padding:3px 4px;height:auto}.widefat .column-order_actions a.button img,.widefat .column-user_actions a.button img,.widefat .column-wc_actions a.button img{display:block;width:12px;height:auto}.widefat small.meta{display:block;color:#999;font-size:inherit;margin:3px 0}.widefat .column-order_date,.widefat .column-order_total{width:9%}.widefat .column-order_status{width:45px;text-align:center}.widefat .column-order_status mark{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;background:0 0;font-size:1.4em;margin:0 auto}.widefat .column-order_status mark.cancelled::after,.widefat .column-order_status mark.completed::after,.widefat .column-order_status mark.failed::after,.widefat .column-order_status mark.on-hold::after,.widefat .column-order_status mark.pending::after,.widefat .column-order_status mark.processing::after,.widefat .column-order_status mark.refunded::after{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}.column-customer_message .note-on::after,.column-order_notes .note-on::after{speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;top:0;left:0;text-align:center;line-height:16px;font-family:WooCommerce}.widefat .column-order_status mark.pending::after{content:'\e012';color:#ffba00}.widefat .column-order_status mark.completed::after{content:'\e015';color:#2ea2cc}.widefat .column-order_status mark.on-hold::after{content:'\e033';color:#999}.widefat .column-order_status mark.failed::after{content:'\e016';color:#d0c21f}.widefat .column-order_status mark.cancelled::after{content:'\e013';color:#a00}.widefat .column-order_status mark.processing::after{content:'\e011';color:#73a724}.widefat .column-order_status mark.refunded::after{content:'\e014';color:#999}.widefat td.column-order_status{padding-top:9px}.column-customer_message .note-on{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;margin:0 auto;color:#999}.column-customer_message .note-on::after{margin:0;text-indent:0;position:absolute;width:100%;height:100%;content:""}.column-order_notes .note-on{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;margin:0 auto;color:#999}.column-order_notes .note-on::after{margin:0;text-indent:0;position:absolute;width:100%;height:100%;content:""}.order_actions .complete,.order_actions .processing,.order_actions .view{display:block;text-indent:-9999px;position:relative;padding:0!important;height:2em!important;width:2em}.order_actions .processing::after{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";line-height:1.85}.order_actions .complete::after,.order_actions .view::after{font-family:Dashicons;text-indent:0;position:absolute;width:100%;height:100%;left:0;line-height:1.85;margin:0;text-align:center;font-weight:400;top:0;speak:none;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased}.order_actions .complete::after{content:""}.order_actions .view::after{content:""}.user_actions .edit,.user_actions .link,.user_actions .refresh,.user_actions .view{display:block;text-indent:-9999px;position:relative;padding:0!important;height:2em!important;width:2em}.user_actions .edit::after,.user_actions .link::after,.user_actions .refresh::after,.user_actions .view::after{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";line-height:1.85}.user_actions .edit::after{font-family:Dashicons;content:'\f464'}.user_actions .link::after{content:'\e00d'}.user_actions .view::after{content:'\e010'}.user_actions .refresh::after{content:'\e031'}.attributes-table td,.attributes-table th{width:15%;vertical-align:top}.attributes-table .attribute-terms{width:32%}.attributes-table .attribute-actions{width:2em}.attributes-table .attribute-actions .configure-terms{display:block;text-indent:-9999px;position:relative;padding:0!important;height:2em!important;width:2em}.attributes-table .attribute-actions .configure-terms::after{speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";font-family:Dashicons;line-height:1.85}ul.order_notes{padding:2px 0 0}ul.order_notes li .note_content{padding:10px;background:#efefef;position:relative}ul.order_notes li .note_content p{margin:0;padding:0;word-wrap:break-word}ul.order_notes li p.meta{padding:10px;color:#999;margin:0;font-size:11px}ul.order_notes li a.delete_note{color:#a00}table.wp-list-table .row-actions,table.wp-list-table span.na{color:#999}ul.order_notes li .note_content::after{content:'';display:block;position:absolute;bottom:-10px;left:20px;width:0;height:0;border-width:10px 10px 0 0;border-style:solid;border-color:#efefef transparent}ul.order_notes li.customer-note .note_content{background:#a7cedc}ul.order_notes li.customer-note .note_content::after{border-color:#a7cedc transparent}ul.order_notes li.system-note .note_content{background:#d7cad2}ul.order_notes li.system-note .note_content::after{border-color:#d7cad2 transparent}.add_note{border-top:1px solid #ddd;padding:10px 10px 0}.add_note h4{margin-top:5px!important}.add_note #add_order_note{width:100%;height:50px}table.wp-list-table .column-thumb{width:52px;text-align:center;white-space:nowrap}table.wp-list-table .column-name{width:22%}table.wp-list-table .column-product_cat,table.wp-list-table .column-product_tag{width:11%!important}table.wp-list-table .column-featured,table.wp-list-table .column-product_type{width:48px;text-align:left!important}table.wp-list-table .column-customer_message,table.wp-list-table .column-order_notes{width:48px;text-align:center}table.wp-list-table .column-customer_message img,table.wp-list-table .column-order_notes img{margin:0 auto;padding-top:0!important}table.wp-list-table .manage-column.column-featured img,table.wp-list-table .manage-column.column-product_type img{padding-left:2px}table.wp-list-table .column-price .woocommerce-price-suffix{display:none}table.wp-list-table img{margin:1px 2px}table.wp-list-table td.column-thumb img{margin:0;width:auto;height:auto;max-width:40px;max-height:40px;vertical-align:middle}table.wp-list-table .column-is_in_stock{text-align:left!important}table.wp-list-table span.wc-featured,table.wp-list-table span.wc-image,table.wp-list-table span.wc-type{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;margin:0 auto}table.wp-list-table span.wc-featured::before,table.wp-list-table span.wc-image::before,table.wp-list-table span.wc-type::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:""}table.wp-list-table span.wc-featured{margin:0;cursor:pointer}table.wp-list-table span.wc-featured::before{content:'\f155'}table.wp-list-table span.wc-featured.not-featured::before{content:'\f154'}table.wp-list-table td.column-featured span.wc-featured{font-size:1.6em}table.wp-list-table span.wc-type{margin:0}table.wp-list-table span.wc-type::before{font-family:WooCommerce;content:'\e006'}table.wp-list-table span.product-type{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;font-size:1.6em}table.wp-list-table span.product-type::before{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:""}table.wp-list-table span.product-type.grouped::before{content:'\e002'}table.wp-list-table span.product-type.external::before{content:'\e034'}table.wp-list-table span.product-type.variable::before{content:'\e003'}table.wp-list-table span.product-type.downloadable::before{content:'\e001'}table.wp-list-table span.product-type.virtual::before{content:'\e000'}table.wp-list-table mark.instock{font-weight:700;color:#7ad03a;background:0 0;line-height:1}table.wp-list-table mark.outofstock{font-weight:700;color:#a44;background:0 0;line-height:1}table.wp-list-table .notes_head,table.wp-list-table .order-notes_head,table.wp-list-table .status_head{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;margin:0 auto}table.wp-list-table .notes_head::after,table.wp-list-table .order-notes_head::after,table.wp-list-table .status_head::after{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}table.wc_emails .wc-email-settings-table-name,table.wc_emails td.name,table.wc_gateways .wc-email-settings-table-name,table.wc_gateways td.name,table.wc_shipping .wc-email-settings-table-name,table.wc_shipping td.name{font-weight:700}table.wp-list-table .order-notes_head::after{content:'\e028'}table.wp-list-table .notes_head::after{content:'\e026'}table.wp-list-table .status_head::after{content:'\e011'}table.wp-list-table .column-order_items{width:12%}table.wp-list-table .column-order_items table.order_items{width:100%;margin:3px 0 0;padding:0;display:none}table.wp-list-table .column-order_items table.order_items td{border:0;margin:0;padding:0 0 3px}table.wp-list-table .column-order_items table.order_items td.qty{color:#999;padding-right:6px;text-align:left}mark.notice{background:#fff;color:#a00;margin:0 0 0 10px}a.export_rates,a.import_rates{float:right;margin-left:9px;margin-top:-2px;margin-bottom:0}#rates-search{float:right}#rates-search input.wc-tax-rates-search-field{padding:4px 8px;font-size:1.2em}#rates-pagination{float:right;margin-right:.5em}#rates-pagination .tablenav{margin:0}table.wc_input_table,table.wc_tax_rates{width:100%}table.wc_input_table span.tips,table.wc_tax_rates span.tips{color:#2ea2cc}table.wc_input_table th,table.wc_tax_rates th{white-space:nowrap;padding:10px}table.wc_input_table td,table.wc_tax_rates td{padding:0;border-right:1px solid #dfdfdf;border-bottom:1px solid #dfdfdf;border-top:0;background:#fff;cursor:default}table.wc_input_table td input[type=text],table.wc_input_table td input[type=number],table.wc_tax_rates td input[type=text],table.wc_tax_rates td input[type=number]{width:100%;padding:8px 10px;margin:0;border:0;outline:0;background:0 0}table.wc_input_table td input[type=text]:focus,table.wc_input_table td input[type=number]:focus,table.wc_tax_rates td input[type=text]:focus,table.wc_tax_rates td input[type=number]:focus{outline:0;box-shadow:none}table.wc_input_table td.apply_to_shipping,table.wc_input_table td.compound,table.wc_tax_rates td.apply_to_shipping,table.wc_tax_rates td.compound{padding:5px 7px;vertical-align:middle}table.wc_input_table td.apply_to_shipping input,table.wc_input_table td.compound input,table.wc_tax_rates td.apply_to_shipping input,table.wc_tax_rates td.compound input{width:auto;padding:0}table.wc_input_table td:last-child,table.wc_tax_rates td:last-child{border-right:0}table.wc_input_table tr.current td,table.wc_tax_rates tr.current td{background-color:#fefbcc}table.wc_input_table .cost,table.wc_input_table .cost input,table.wc_input_table .item_cost,table.wc_input_table .item_cost input,table.wc_tax_rates .cost,table.wc_tax_rates .cost input,table.wc_tax_rates .item_cost,table.wc_tax_rates .item_cost input{text-align:right}table.wc_input_table th.sort,table.wc_tax_rates th.sort{width:17px;padding:0 4px}table.wc_input_table td.sort,table.wc_tax_rates td.sort{padding:0 4px}table.wc_input_table .ui-sortable:not(.ui-sortable-disabled) td.sort,table.wc_tax_rates .ui-sortable:not(.ui-sortable-disabled) td.sort{cursor:move;font-size:15px;background:#f9f9f9;text-align:center;vertical-align:middle}table.wc_input_table .ui-sortable:not(.ui-sortable-disabled) td.sort::before,table.wc_tax_rates .ui-sortable:not(.ui-sortable-disabled) td.sort::before{content:'\f333';font-family:Dashicons;text-align:center;line-height:1;color:#999;display:block;width:17px;float:left;height:100%}table.wc_input_table .ui-sortable:not(.ui-sortable-disabled) td.sort:hover::before,table.wc_tax_rates .ui-sortable:not(.ui-sortable-disabled) td.sort:hover::before{color:#333}table.wc_input_table .button,table.wc_tax_rates .button{float:left;margin-right:5px}table.wc_input_table .export,table.wc_input_table .import,table.wc_tax_rates .export,table.wc_tax_rates .import{float:right;margin-right:0;margin-left:5px}table.wc_input_table span.tips,table.wc_tax_rates span.tips{padding:0 3px}table.wc_input_table .pagination,table.wc_tax_rates .pagination{float:right}table.wc_input_table .pagination .button,table.wc_tax_rates .pagination .button{margin-left:5px;margin-right:0}table.wc_input_table .pagination .current,table.wc_tax_rates .pagination .current{background:#bbb;text-shadow:none}table.wc_input_table tr:last-child td,table.wc_tax_rates tr:last-child td{border-bottom:0}table.wc_emails,table.wc_gateways,table.wc_shipping{position:relative}table.wc_emails td,table.wc_gateways td,table.wc_shipping td{vertical-align:middle;padding:7px;line-height:2em}table.wc_emails tr:nth-child(odd) td,table.wc_gateways tr:nth-child(odd) td,table.wc_shipping tr:nth-child(odd) td{background:#f9f9f9}table.wc_emails th,table.wc_gateways th,table.wc_shipping th{padding:9px 7px!important;vertical-align:middle}table.wc_emails .settings,table.wc_gateways .settings,table.wc_shipping .settings{text-align:right}table.wc_emails .default,table.wc_emails .radio,table.wc_emails .status,table.wc_gateways .default,table.wc_gateways .radio,table.wc_gateways .status,table.wc_shipping .default,table.wc_shipping .radio,table.wc_shipping .status{text-align:center}table.wc_emails .default .tips,table.wc_emails .radio .tips,table.wc_emails .status .tips,table.wc_gateways .default .tips,table.wc_gateways .radio .tips,table.wc_gateways .status .tips,table.wc_shipping .default .tips,table.wc_shipping .radio .tips,table.wc_shipping .status .tips{margin:0 auto}table.wc_emails .default input,table.wc_emails .radio input,table.wc_emails .status input,table.wc_gateways .default input,table.wc_gateways .radio input,table.wc_gateways .status input,table.wc_shipping .default input,table.wc_shipping .radio input,table.wc_shipping .status input{margin:0}table.wc_emails th.sort,table.wc_gateways th.sort,table.wc_shipping th.sort{width:28px;padding:0}table.wc_emails td.sort,table.wc_gateways td.sort,table.wc_shipping td.sort{padding:0 7px;cursor:move;font-size:15px;text-align:center;vertical-align:middle}table.wc_emails td.sort::before,table.wc_gateways td.sort::before,table.wc_shipping td.sort::before{content:'\f333';font-family:Dashicons;text-align:center;line-height:1;color:#999;display:block;width:17px;float:left;height:100%}table.wc_emails .wc-email-settings-table-name span,table.wc_gateways .wc-email-settings-table-name span,table.wc_shipping .wc-email-settings-table-name span{font-weight:400;color:#999;margin:0 0 0 4px!important}table.wc_emails .wc-email-settings-table-status,table.wc_gateways .wc-email-settings-table-status,table.wc_shipping .wc-email-settings-table-status{text-align:center;width:1em}table.wc_emails .wc-email-settings-table-status .tips,table.wc_gateways .wc-email-settings-table-status .tips,table.wc_shipping .wc-email-settings-table-status .tips{margin:0 auto}table.wc_emails .wc-email-settings-table-actions a,table.wc_gateways .wc-email-settings-table-actions a,table.wc_shipping .wc-email-settings-table-actions a{display:block;text-indent:-9999px;position:relative;padding:0!important;height:2em!important;width:2em}table.wc_emails .wc-email-settings-table-actions a::after,table.wc_gateways .wc-email-settings-table-actions a::after,table.wc_shipping .wc-email-settings-table-actions a::after{speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";font-family:Dashicons;line-height:1.85}.wc-shipping-zone-settings th{padding:24px 24px 24px 0}.wc-shipping-zone-settings td.forminp{padding:15px 10px}.wc-shipping-zone-settings td.forminp input,.wc-shipping-zone-settings td.forminp textarea{padding:8px;width:448px;max-width:100%!important}.wc-shipping-zone-settings td.forminp .wc-shipping-zone-region-select{width:448px;max-width:100%!important}.wc-shipping-zone-settings td.forminp .wc-shipping-zone-region-select .select2-choices{padding:8px 8px 4px;border-color:#DDD;min-height:0;line-height:1}.wc-shipping-zone-settings td.forminp .wc-shipping-zone-region-select .select2-choices input{padding:0}.wc-shipping-zone-settings td.forminp .wc-shipping-zone-region-select .select2-choices li{margin:0 4px 4px 0}.wc-shipping-zone-settings td.forminp .select2-container-active .select2-choices{border-color:#777}.wc-shipping-zone-settings .wc-shipping-zone-postcodes-toggle{margin:0;font-size:.9em;text-decoration:underline}.wc-shipping-zone-settings .wc-shipping-zone-postcodes-toggle+.wc-shipping-zone-postcodes{display:none}.wc-shipping-zone-settings .wc-shipping-zone-postcodes textarea{margin:10px 0}.wc-shipping-zone-settings .wc-shipping-zone-postcodes .description{font-size:.9em;color:#999}table tr table.wc-shipping-zone-methods tr .row-actions,table tr:hover table.wc-shipping-zone-methods tr .row-actions{position:relative}table tr table.wc-shipping-zone-methods tr:hover .row-actions,table tr:hover table.wc-shipping-zone-methods tr:hover .row-actions{position:static}table.wc-shipping-classes td,table.wc-shipping-classes th,table.wc-shipping-zone-methods td,table.wc-shipping-zone-methods th,table.wc-shipping-zones td,table.wc-shipping-zones th{vertical-align:top;line-height:24px;padding:1em!important;font-size:14px;background:#fff}table.wc-shipping-classes td li,table.wc-shipping-classes th li,table.wc-shipping-zone-methods td li,table.wc-shipping-zone-methods th li,table.wc-shipping-zones td li,table.wc-shipping-zones th li{line-height:24px;font-size:14px}table.wc-shipping-classes td .woocommerce-help-tip,table.wc-shipping-classes th .woocommerce-help-tip,table.wc-shipping-zone-methods td .woocommerce-help-tip,table.wc-shipping-zone-methods th .woocommerce-help-tip,table.wc-shipping-zones td .woocommerce-help-tip,table.wc-shipping-zones th .woocommerce-help-tip{margin:0!important}table.wc-shipping-classes thead th,table.wc-shipping-zone-methods thead th,table.wc-shipping-zones thead th{vertical-align:middle}table.wc-shipping-classes thead .wc-shipping-zone-sort,table.wc-shipping-zone-methods thead .wc-shipping-zone-sort,table.wc-shipping-zones thead .wc-shipping-zone-sort{text-align:center}table.wc-shipping-classes tbody td,table.wc-shipping-zone-methods tbody td,table.wc-shipping-zones tbody td{padding:1.5em 1em!important}table.wc-shipping-classes td.wc-shipping-zone-method-blank-state,table.wc-shipping-classes td.wc-shipping-zones-blank-state,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state,table.wc-shipping-zones td.wc-shipping-zones-blank-state{background:#f7f1f6!important;overflow:hidden;position:relative;padding:7.5em 7.5%!important;border-bottom:2px solid #eee2ec}table.wc-shipping-classes td.wc-shipping-zone-method-blank-state li,table.wc-shipping-classes td.wc-shipping-zone-method-blank-state p,table.wc-shipping-classes td.wc-shipping-zones-blank-state li,table.wc-shipping-classes td.wc-shipping-zones-blank-state p,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state li,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state p,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state li,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state p,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state li,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state p,table.wc-shipping-zones td.wc-shipping-zones-blank-state li,table.wc-shipping-zones td.wc-shipping-zones-blank-state p{color:#a46497;font-size:1.5em;line-height:1.5em;margin:0 0 1em;position:relative;z-index:1;text-shadow:1px 1px 1px #fff}table.wc-shipping-classes td.wc-shipping-zone-method-blank-state li.main,table.wc-shipping-classes td.wc-shipping-zone-method-blank-state p.main,table.wc-shipping-classes td.wc-shipping-zones-blank-state li.main,table.wc-shipping-classes td.wc-shipping-zones-blank-state p.main,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state li.main,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state p.main,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state li.main,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state p.main,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state li.main,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state p.main,table.wc-shipping-zones td.wc-shipping-zones-blank-state li.main,table.wc-shipping-zones td.wc-shipping-zones-blank-state p.main{font-size:2em}table.wc-shipping-classes td.wc-shipping-zone-method-blank-state li,table.wc-shipping-classes td.wc-shipping-zones-blank-state li,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state li,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state li,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state li,table.wc-shipping-zones td.wc-shipping-zones-blank-state li{margin-left:1em;list-style:circle inside}table.wc-shipping-classes td.wc-shipping-zone-method-blank-state::before,table.wc-shipping-classes td.wc-shipping-zones-blank-state::before,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state::before,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state::before,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state::before,table.wc-shipping-zones td.wc-shipping-zones-blank-state::before{content:'\e01b';font-family:WooCommerce;text-align:center;line-height:1;color:#eee2ec;display:block;width:1em;font-size:40em;top:50%;right:-3.75%;margin-top:-.1875em;position:absolute}table.wc-shipping-classes td.wc-shipping-zone-method-blank-state .button-primary,table.wc-shipping-classes td.wc-shipping-zones-blank-state .button-primary,table.wc-shipping-zone-methods td.wc-shipping-zone-method-blank-state .button-primary,table.wc-shipping-zone-methods td.wc-shipping-zones-blank-state .button-primary,table.wc-shipping-zones td.wc-shipping-zone-method-blank-state .button-primary,table.wc-shipping-zones td.wc-shipping-zones-blank-state .button-primary{background-color:#804877;border-color:#804877;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 0 rgba(0,0,0,.15);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 0 rgba(0,0,0,.15);margin:0;opacity:1;text-shadow:0 -1px 1px #8a4f7f,1px 0 1px #8a4f7f,0 1px 1px #8a4f7f,-1px 0 1px #8a4f7f;font-size:1.5em;padding:.75em 1em;height:auto;position:relative;z-index:1}table.wc-shipping-classes .wc-shipping-class-rows tr:nth-child(odd) td,table.wc-shipping-classes .wc-shipping-zone-method-rows tr:nth-child(odd) td,table.wc-shipping-classes tbody.wc-shipping-zone-rows tr:nth-child(odd) td,table.wc-shipping-classes tr.odd td,table.wc-shipping-zone-methods .wc-shipping-class-rows tr:nth-child(odd) td,table.wc-shipping-zone-methods .wc-shipping-zone-method-rows tr:nth-child(odd) td,table.wc-shipping-zone-methods tbody.wc-shipping-zone-rows tr:nth-child(odd) td,table.wc-shipping-zone-methods tr.odd td,table.wc-shipping-zones .wc-shipping-class-rows tr:nth-child(odd) td,table.wc-shipping-zones .wc-shipping-zone-method-rows tr:nth-child(odd) td,table.wc-shipping-zones tbody.wc-shipping-zone-rows tr:nth-child(odd) td,table.wc-shipping-zones tr.odd td{background:#f9f9f9}table.wc-shipping-classes p,table.wc-shipping-classes ul,table.wc-shipping-zone-methods p,table.wc-shipping-zone-methods ul,table.wc-shipping-zones p,table.wc-shipping-zones ul{margin:0}table.wc-shipping-classes td.wc-shipping-zone-method-sort,table.wc-shipping-classes td.wc-shipping-zone-sort,table.wc-shipping-zone-methods td.wc-shipping-zone-method-sort,table.wc-shipping-zone-methods td.wc-shipping-zone-sort,table.wc-shipping-zones td.wc-shipping-zone-method-sort,table.wc-shipping-zones td.wc-shipping-zone-sort{cursor:move;font-size:15px;text-align:center}table.wc-shipping-classes td.wc-shipping-zone-method-sort::before,table.wc-shipping-classes td.wc-shipping-zone-sort::before,table.wc-shipping-zone-methods td.wc-shipping-zone-method-sort::before,table.wc-shipping-zone-methods td.wc-shipping-zone-sort::before,table.wc-shipping-zones td.wc-shipping-zone-method-sort::before,table.wc-shipping-zones td.wc-shipping-zone-sort::before{content:'\f333';font-family:Dashicons;text-align:center;color:#999;display:block;width:17px;float:left;height:100%;line-height:24px}table.wc-shipping-classes td.wc-shipping-zone-method-sort:hover::before,table.wc-shipping-classes td.wc-shipping-zone-sort:hover::before,table.wc-shipping-zone-methods td.wc-shipping-zone-method-sort:hover::before,table.wc-shipping-zone-methods td.wc-shipping-zone-sort:hover::before,table.wc-shipping-zones td.wc-shipping-zone-method-sort:hover::before,table.wc-shipping-zones td.wc-shipping-zone-sort:hover::before{color:#333}table.wc-shipping-classes td.wc-shipping-zone-worldwide,table.wc-shipping-zone-methods td.wc-shipping-zone-worldwide,table.wc-shipping-zones td.wc-shipping-zone-worldwide{text-align:center}table.wc-shipping-classes td.wc-shipping-zone-worldwide::before,table.wc-shipping-zone-methods td.wc-shipping-zone-worldwide::before,table.wc-shipping-zones td.wc-shipping-zone-worldwide::before{content:'\f319';font-family:dashicons;text-align:center;color:#999;display:block;width:17px;float:left;height:100%;line-height:24px}table.wc-shipping-classes .wc-shipping-zone-methods,table.wc-shipping-classes .wc-shipping-zone-name,table.wc-shipping-zone-methods .wc-shipping-zone-methods,table.wc-shipping-zone-methods .wc-shipping-zone-name,table.wc-shipping-zones .wc-shipping-zone-methods,table.wc-shipping-zones .wc-shipping-zone-name{width:25%}table.wc-shipping-classes .wc-shipping-class-description input,table.wc-shipping-classes .wc-shipping-class-description select,table.wc-shipping-classes .wc-shipping-class-description textarea,table.wc-shipping-classes .wc-shipping-class-name input,table.wc-shipping-classes .wc-shipping-class-name select,table.wc-shipping-classes .wc-shipping-class-name textarea,table.wc-shipping-classes .wc-shipping-class-slug input,table.wc-shipping-classes .wc-shipping-class-slug select,table.wc-shipping-classes .wc-shipping-class-slug textarea,table.wc-shipping-classes .wc-shipping-zone-name input,table.wc-shipping-classes .wc-shipping-zone-name select,table.wc-shipping-classes .wc-shipping-zone-name textarea,table.wc-shipping-classes .wc-shipping-zone-region input,table.wc-shipping-classes .wc-shipping-zone-region select,table.wc-shipping-classes .wc-shipping-zone-region textarea,table.wc-shipping-zone-methods .wc-shipping-class-description input,table.wc-shipping-zone-methods .wc-shipping-class-description select,table.wc-shipping-zone-methods .wc-shipping-class-description textarea,table.wc-shipping-zone-methods .wc-shipping-class-name input,table.wc-shipping-zone-methods .wc-shipping-class-name select,table.wc-shipping-zone-methods .wc-shipping-class-name textarea,table.wc-shipping-zone-methods .wc-shipping-class-slug input,table.wc-shipping-zone-methods .wc-shipping-class-slug select,table.wc-shipping-zone-methods .wc-shipping-class-slug textarea,table.wc-shipping-zone-methods .wc-shipping-zone-name input,table.wc-shipping-zone-methods .wc-shipping-zone-name select,table.wc-shipping-zone-methods .wc-shipping-zone-name textarea,table.wc-shipping-zone-methods .wc-shipping-zone-region input,table.wc-shipping-zone-methods .wc-shipping-zone-region select,table.wc-shipping-zone-methods .wc-shipping-zone-region textarea,table.wc-shipping-zones .wc-shipping-class-description input,table.wc-shipping-zones .wc-shipping-class-description select,table.wc-shipping-zones .wc-shipping-class-description textarea,table.wc-shipping-zones .wc-shipping-class-name input,table.wc-shipping-zones .wc-shipping-class-name select,table.wc-shipping-zones .wc-shipping-class-name textarea,table.wc-shipping-zones .wc-shipping-class-slug input,table.wc-shipping-zones .wc-shipping-class-slug select,table.wc-shipping-zones .wc-shipping-class-slug textarea,table.wc-shipping-zones .wc-shipping-zone-name input,table.wc-shipping-zones .wc-shipping-zone-name select,table.wc-shipping-zones .wc-shipping-zone-name textarea,table.wc-shipping-zones .wc-shipping-zone-region input,table.wc-shipping-zones .wc-shipping-zone-region select,table.wc-shipping-zones .wc-shipping-zone-region textarea{width:100%}table.wc-shipping-classes .wc-shipping-class-description a.wc-shipping-class-delete,table.wc-shipping-classes .wc-shipping-class-description a.wc-shipping-zone-delete,table.wc-shipping-classes .wc-shipping-class-name a.wc-shipping-class-delete,table.wc-shipping-classes .wc-shipping-class-name a.wc-shipping-zone-delete,table.wc-shipping-classes .wc-shipping-class-slug a.wc-shipping-class-delete,table.wc-shipping-classes .wc-shipping-class-slug a.wc-shipping-zone-delete,table.wc-shipping-classes .wc-shipping-zone-name a.wc-shipping-class-delete,table.wc-shipping-classes .wc-shipping-zone-name a.wc-shipping-zone-delete,table.wc-shipping-classes .wc-shipping-zone-region a.wc-shipping-class-delete,table.wc-shipping-classes .wc-shipping-zone-region a.wc-shipping-zone-delete,table.wc-shipping-zone-methods .wc-shipping-class-description a.wc-shipping-class-delete,table.wc-shipping-zone-methods .wc-shipping-class-description a.wc-shipping-zone-delete,table.wc-shipping-zone-methods .wc-shipping-class-name a.wc-shipping-class-delete,table.wc-shipping-zone-methods .wc-shipping-class-name a.wc-shipping-zone-delete,table.wc-shipping-zone-methods .wc-shipping-class-slug a.wc-shipping-class-delete,table.wc-shipping-zone-methods .wc-shipping-class-slug a.wc-shipping-zone-delete,table.wc-shipping-zone-methods .wc-shipping-zone-name a.wc-shipping-class-delete,table.wc-shipping-zone-methods .wc-shipping-zone-name a.wc-shipping-zone-delete,table.wc-shipping-zone-methods .wc-shipping-zone-region a.wc-shipping-class-delete,table.wc-shipping-zone-methods .wc-shipping-zone-region a.wc-shipping-zone-delete,table.wc-shipping-zones .wc-shipping-class-description a.wc-shipping-class-delete,table.wc-shipping-zones .wc-shipping-class-description a.wc-shipping-zone-delete,table.wc-shipping-zones .wc-shipping-class-name a.wc-shipping-class-delete,table.wc-shipping-zones .wc-shipping-class-name a.wc-shipping-zone-delete,table.wc-shipping-zones .wc-shipping-class-slug a.wc-shipping-class-delete,table.wc-shipping-zones .wc-shipping-class-slug a.wc-shipping-zone-delete,table.wc-shipping-zones .wc-shipping-zone-name a.wc-shipping-class-delete,table.wc-shipping-zones .wc-shipping-zone-name a.wc-shipping-zone-delete,table.wc-shipping-zones .wc-shipping-zone-region a.wc-shipping-class-delete,table.wc-shipping-zones .wc-shipping-zone-region a.wc-shipping-zone-delete{color:#a00}table.wc-shipping-classes .wc-shipping-class-description a.wc-shipping-class-delete:hover,table.wc-shipping-classes .wc-shipping-class-description a.wc-shipping-zone-delete:hover,table.wc-shipping-classes .wc-shipping-class-name a.wc-shipping-class-delete:hover,table.wc-shipping-classes .wc-shipping-class-name a.wc-shipping-zone-delete:hover,table.wc-shipping-classes .wc-shipping-class-slug a.wc-shipping-class-delete:hover,table.wc-shipping-classes .wc-shipping-class-slug a.wc-shipping-zone-delete:hover,table.wc-shipping-classes .wc-shipping-zone-name a.wc-shipping-class-delete:hover,table.wc-shipping-classes .wc-shipping-zone-name a.wc-shipping-zone-delete:hover,table.wc-shipping-classes .wc-shipping-zone-region a.wc-shipping-class-delete:hover,table.wc-shipping-classes .wc-shipping-zone-region a.wc-shipping-zone-delete:hover,table.wc-shipping-zone-methods .wc-shipping-class-description a.wc-shipping-class-delete:hover,table.wc-shipping-zone-methods .wc-shipping-class-description a.wc-shipping-zone-delete:hover,table.wc-shipping-zone-methods .wc-shipping-class-name a.wc-shipping-class-delete:hover,table.wc-shipping-zone-methods .wc-shipping-class-name a.wc-shipping-zone-delete:hover,table.wc-shipping-zone-methods .wc-shipping-class-slug a.wc-shipping-class-delete:hover,table.wc-shipping-zone-methods .wc-shipping-class-slug a.wc-shipping-zone-delete:hover,table.wc-shipping-zone-methods .wc-shipping-zone-name a.wc-shipping-class-delete:hover,table.wc-shipping-zone-methods .wc-shipping-zone-name a.wc-shipping-zone-delete:hover,table.wc-shipping-zone-methods .wc-shipping-zone-region a.wc-shipping-class-delete:hover,table.wc-shipping-zone-methods .wc-shipping-zone-region a.wc-shipping-zone-delete:hover,table.wc-shipping-zones .wc-shipping-class-description a.wc-shipping-class-delete:hover,table.wc-shipping-zones .wc-shipping-class-description a.wc-shipping-zone-delete:hover,table.wc-shipping-zones .wc-shipping-class-name a.wc-shipping-class-delete:hover,table.wc-shipping-zones .wc-shipping-class-name a.wc-shipping-zone-delete:hover,table.wc-shipping-zones .wc-shipping-class-slug a.wc-shipping-class-delete:hover,table.wc-shipping-zones .wc-shipping-class-slug a.wc-shipping-zone-delete:hover,table.wc-shipping-zones .wc-shipping-zone-name a.wc-shipping-class-delete:hover,table.wc-shipping-zones .wc-shipping-zone-name a.wc-shipping-zone-delete:hover,table.wc-shipping-zones .wc-shipping-zone-region a.wc-shipping-class-delete:hover,table.wc-shipping-zones .wc-shipping-zone-region a.wc-shipping-zone-delete:hover{color:red}table.wc-shipping-classes .wc-shipping-class-count,table.wc-shipping-zone-methods .wc-shipping-class-count,table.wc-shipping-zones .wc-shipping-class-count{text-align:center}table.wc-shipping-classes td.wc-shipping-zone-methods,table.wc-shipping-zone-methods td.wc-shipping-zone-methods,table.wc-shipping-zones td.wc-shipping-zone-methods{color:#555}table.wc-shipping-classes td.wc-shipping-zone-methods .method_disabled,table.wc-shipping-zone-methods td.wc-shipping-zone-methods .method_disabled,table.wc-shipping-zones td.wc-shipping-zone-methods .method_disabled{text-decoration:line-through}table.wc-shipping-classes td.wc-shipping-zone-methods ul,table.wc-shipping-zone-methods td.wc-shipping-zone-methods ul,table.wc-shipping-zones td.wc-shipping-zone-methods ul{position:relative;padding-right:32px}table.wc-shipping-classes td.wc-shipping-zone-methods ul li,table.wc-shipping-zone-methods td.wc-shipping-zone-methods ul li,table.wc-shipping-zones td.wc-shipping-zone-methods ul li{color:#555;display:inline;margin:0}table.wc-shipping-classes td.wc-shipping-zone-methods ul li::before,table.wc-shipping-zone-methods td.wc-shipping-zone-methods ul li::before,table.wc-shipping-zones td.wc-shipping-zone-methods ul li::before{content:', '}table.wc-shipping-classes td.wc-shipping-zone-methods ul li:first-child::before,table.wc-shipping-zone-methods td.wc-shipping-zone-methods ul li:first-child::before,table.wc-shipping-zones td.wc-shipping-zone-methods ul li:first-child::before{content:''}table.wc-shipping-classes td.wc-shipping-zone-methods .add_shipping_method,table.wc-shipping-zone-methods td.wc-shipping-zone-methods .add_shipping_method,table.wc-shipping-zones td.wc-shipping-zone-methods .add_shipping_method{display:block;width:24px;padding:24px 0 0;height:0;overflow:hidden;cursor:pointer}table.wc-shipping-classes td.wc-shipping-zone-methods .add_shipping_method::before,table.wc-shipping-zone-methods td.wc-shipping-zone-methods .add_shipping_method::before,table.wc-shipping-zones td.wc-shipping-zone-methods .add_shipping_method::before{speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;font-family:Dashicons;content:'\f502';color:#999;vertical-align:middle;line-height:24px;font-size:16px;margin:0}table.wc-shipping-classes .wc-shipping-zone-method-enabled .status-disabled::before,table.wc-shipping-classes .wc-shipping-zone-method-enabled .status-enabled::before,table.wc-shipping-zone-methods .wc-shipping-zone-method-enabled .status-disabled::before,table.wc-shipping-zone-methods .wc-shipping-zone-method-enabled .status-enabled::before,table.wc-shipping-zones .wc-shipping-zone-method-enabled .status-disabled::before,table.wc-shipping-zones .wc-shipping-zone-method-enabled .status-enabled::before{line-height:inherit}table.wc-shipping-classes .wc-shipping-zone-method-enabled .status-disabled,table.wc-shipping-classes .wc-shipping-zone-method-enabled .status-enabled,table.wc-shipping-zone-methods .wc-shipping-zone-method-enabled .status-disabled,table.wc-shipping-zone-methods .wc-shipping-zone-method-enabled .status-enabled,table.wc-shipping-zones .wc-shipping-zone-method-enabled .status-disabled,table.wc-shipping-zones .wc-shipping-zone-method-enabled .status-enabled{margin-top:1px;display:inline-block}table.wc-shipping-classes td.wc-shipping-zone-methods .add_shipping_method.disabled,table.wc-shipping-zone-methods td.wc-shipping-zone-methods .add_shipping_method.disabled,table.wc-shipping-zones td.wc-shipping-zone-methods .add_shipping_method.disabled{cursor:not-allowed}table.wc-shipping-classes td.wc-shipping-zone-methods .add_shipping_method.disabled::before,table.wc-shipping-zone-methods td.wc-shipping-zone-methods .add_shipping_method.disabled::before,table.wc-shipping-zones td.wc-shipping-zone-methods .add_shipping_method.disabled::before{color:#ccc}table.wc-shipping-classes .wc-shipping-zone-method-title,table.wc-shipping-zone-methods .wc-shipping-zone-method-title,table.wc-shipping-zones .wc-shipping-zone-method-title{width:33%}table.wc-shipping-classes .wc-shipping-zone-method-title .wc-shipping-zone-method-delete,table.wc-shipping-zone-methods .wc-shipping-zone-method-title .wc-shipping-zone-method-delete,table.wc-shipping-zones .wc-shipping-zone-method-title .wc-shipping-zone-method-delete{color:red}table.wc-shipping-classes .wc-shipping-zone-method-enabled,table.wc-shipping-zone-methods .wc-shipping-zone-method-enabled,table.wc-shipping-zones .wc-shipping-zone-method-enabled{text-align:center}table.wc-shipping-classes tfoot input,table.wc-shipping-classes tfoot select,table.wc-shipping-zone-methods tfoot input,table.wc-shipping-zone-methods tfoot select,table.wc-shipping-zones tfoot input,table.wc-shipping-zones tfoot select{vertical-align:middle!important}table.wc-shipping-classes tfoot .button-secondary,table.wc-shipping-zone-methods tfoot .button-secondary,table.wc-shipping-zones tfoot .button-secondary{float:right}table.wc-shipping-classes .editing .wc-shipping-zone-edit,table.wc-shipping-classes .editing .wc-shipping-zone-view,table.wc-shipping-zone-methods .editing .wc-shipping-zone-edit,table.wc-shipping-zone-methods .editing .wc-shipping-zone-view,table.wc-shipping-zones .editing .wc-shipping-zone-edit,table.wc-shipping-zones .editing .wc-shipping-zone-view{display:none}.wc-modal-shipping-method-settings{background:#f8f8f8;padding:1em!important}.wc-modal-shipping-method-settings form .form-table{width:100%;background:#fff;margin:0 0 1.5em}.wc-modal-shipping-method-settings form .form-table tr th{width:30%;position:relative}.wc-modal-shipping-method-settings form .form-table tr th .woocommerce-help-tip{float:right;margin:-8px -.5em 0 0;vertical-align:middle;right:0;top:50%;position:absolute}.wc-modal-shipping-method-settings form .form-table tr td input,.wc-modal-shipping-method-settings form .form-table tr td select,.wc-modal-shipping-method-settings form .form-table tr td textarea{width:50%;min-width:250px}.wc-modal-shipping-method-settings form .form-table tr td input[type=checkbox]{width:auto;min-width:16px}.wc-modal-shipping-method-settings form .form-table tr td,.wc-modal-shipping-method-settings form .form-table tr th{vertical-align:middle;margin:0;line-height:24px;padding:1em;border-bottom:1px solid #f8f8f8}.wc-modal-shipping-method-settings form .form-table:last-of-type{margin-bottom:0}.wc-backbone-modal .wc-shipping-zone-method-selector p{margin-top:0}.wc-backbone-modal .wc-shipping-zone-method-selector .wc-shipping-zone-method-description{margin:.75em 1px 0;line-height:1.5em;color:#999;font-style:italic}.wc-backbone-modal .wc-shipping-zone-method-selector select{width:100%;cursor:pointer}img.help_tip{margin:0 0 0 9px;vertical-align:middle}.postbox img.help_tip{margin-top:0}.postbox .woocommerce-help-tip{margin:0 0 0 9px}.status-disabled,.status-enabled,.status-manual{font-size:1.4em;display:block;text-indent:-9999px;position:relative;height:1em;width:1em}.status-disabled::before,.status-enabled::before,.status-manual::before{font-family:WooCommerce;line-height:1;margin:0;position:absolute;width:100%;height:100%;text-indent:0;top:0;font-variant:normal;-webkit-font-smoothing:antialiased;font-weight:400;text-align:center;left:0;speak:none;text-transform:none}.status-manual::before{content:"";color:#999}.status-enabled::before{content:"";color:#a46497}.status-disabled::before{content:"";color:#ccc}.woocommerce h2.woo-nav-tab-wrapper{margin-bottom:1em}.woocommerce nav.woo-nav-tab-wrapper{margin:1.5em 0 1em;border-bottom:1px solid #ccc}.woocommerce .subsubsub{margin:-8px 0 0}.woocommerce .wc-admin-breadcrumb{margin-left:.5em}.woocommerce .wc-admin-breadcrumb a{color:#a46497}.woocommerce #template div{margin:0}.woocommerce #template div p .button{float:right;margin-left:10px;margin-top:-4px}.woocommerce #template div .editor textarea{margin-bottom:8px}.woocommerce textarea[disabled=disabled]{background:#dfdfdf!important}.woocommerce table.form-table{margin:0;position:relative}.woocommerce table.form-table .forminp-radio ul{margin:0}.woocommerce table.form-table .forminp-radio ul li{line-height:1.4em}.woocommerce table.form-table textarea.input-text{height:100%;min-width:150px;display:block}.woocommerce table.form-table input.regular-input{width:25em}.woocommerce table.form-table textarea.wide-input{width:100%}.woocommerce table.form-table .woocommerce-help-tip,.woocommerce table.form-table img.help_tip{padding:0;margin:-4px 0 0 5px;vertical-align:middle;cursor:help;line-height:1}.woocommerce table.form-table span.help_tip{cursor:help;color:#2ea2cc}.woocommerce table.form-table th{position:relative;padding-right:24px}.woocommerce table.form-table .select2-container{display:block;max-width:350px;vertical-align:top;margin-bottom:3px}.woocommerce table.form-table table.widefat th{padding-right:inherit}.woocommerce table.form-table th .woocommerce-help-tip,.woocommerce table.form-table th img.help_tip{margin:0 -24px 0 0;float:right}.woocommerce table.form-table .wp-list-table .woocommerce-help-tip{float:none}.woocommerce table.form-table fieldset{margin-top:4px}.woocommerce table.form-table fieldset .woocommerce-help-tip,.woocommerce table.form-table fieldset img.help_tip{margin:-3px 0 0 5px}.woocommerce table.form-table fieldset p.description{margin-bottom:8px}.woocommerce table.form-table fieldset:first-child{margin-top:0}.woocommerce table.form-table .iris-picker{z-index:100;display:none;position:absolute;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.2)}.woocommerce table.form-table .iris-picker .ui-slider{border:0!important;margin:0!important;width:auto!important;height:auto!important;background:none!important}.woocommerce table.form-table .iris-picker .ui-slider .ui-slider-handle{margin-bottom:0!important}.woocommerce table.form-table .colorpickpreview{padding:3px 3px 3px 20px;border:1px solid #ddd;border-right:0}.woocommerce table.form-table .colorpick{border-left:0}.woocommerce table.form-table .image_width_settings{vertical-align:middle}.woocommerce table.form-table .image_width_settings label{margin-left:10px}.woocommerce table.form-table .wc_emails_wrapper{padding:0 15px 10px 0}.woocommerce #tabs-wrap table a.remove{margin-left:4px}.woocommerce #tabs-wrap table p{margin:0 0 4px!important;overflow:hidden;zoom:1}.woocommerce #tabs-wrap table p a.add{float:left}#wp-excerpt-editor-container{background:#fff}#product_variation-parent #parent_id{width:100%}#postimagediv img{border:1px solid #d5d5d5;max-width:100%}#woocommerce-product-images .inside{margin:0;padding:0}#woocommerce-product-images .inside .add_product_images{padding:0 12px 12px}#woocommerce-product-images .inside #product_images_container{padding:0 0 0 9px}#woocommerce-product-images .inside #product_images_container ul{margin:0;padding:0}#woocommerce-product-images .inside #product_images_container ul::after,#woocommerce-product-images .inside #product_images_container ul::before{content:' ';display:table}#woocommerce-product-images .inside #product_images_container ul li.add,#woocommerce-product-images .inside #product_images_container ul li.image,#woocommerce-product-images .inside #product_images_container ul li.wc-metabox-sortable-placeholder{width:80px;float:left;cursor:move;border:1px solid #d5d5d5;margin:9px 9px 0 0;background:#f7f7f7;border-radius:2px;position:relative;box-sizing:border-box}#woocommerce-product-images .inside #product_images_container ul li.add img,#woocommerce-product-images .inside #product_images_container ul li.image img,#woocommerce-product-images .inside #product_images_container ul li.wc-metabox-sortable-placeholder img{width:100%;height:auto;display:block}#woocommerce-product-images .inside #product_images_container ul li.wc-metabox-sortable-placeholder{border:3px dashed #ddd;position:relative}#woocommerce-product-images .inside #product_images_container ul li.wc-metabox-sortable-placeholder::after{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";font-size:2.618em;line-height:72px;color:#ddd}#woocommerce-product-images .inside #product_images_container ul ul.actions{position:absolute;top:-8px;right:-8px;padding:2px;display:none}#woocommerce-product-images .inside #product_images_container ul ul.actions li{float:right;margin:0 0 0 2px}#woocommerce-product-images .inside #product_images_container ul ul.actions li a{width:1em;margin:0;height:0;display:block;overflow:hidden}#woocommerce-product-images .inside #product_images_container ul ul.actions li a.tips{cursor:pointer}#woocommerce-product-images .inside #product_images_container ul ul.actions li a.delete{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;font-size:1.4em}#woocommerce-product-images .inside #product_images_container ul ul.actions li a.delete::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";color:#999}#woocommerce-product-images .inside #product_images_container ul ul.actions li a.delete:hover::before{color:#a00}#woocommerce-product-images .inside #product_images_container ul li:hover ul.actions{display:block}#woocommerce-product-data .hndle{padding:10px}#woocommerce-product-data .hndle span{display:block;vertical-align:middle;line-height:24px}#woocommerce-product-data .hndle span span{display:inline;line-height:inherit;vertical-align:baseline}#woocommerce-product-data .hndle label{padding-right:1em;font-size:12px;vertical-align:baseline}#woocommerce-product-data .hndle label:first-child{margin-right:1em;border-right:1px solid #dfdfdf}#woocommerce-product-data .hndle input,#woocommerce-product-data .hndle select{margin:-3px 0 0 .5em;vertical-align:middle}#woocommerce-product-data>.handlediv{margin-top:4px}#woocommerce-product-data .wrap{margin:0}#woocommerce-coupon-description{padding:3px 8px;font-size:1.7em;line-height:1.42em;height:auto;width:100%;outline:0;margin:10px 0;display:block}#woocommerce-coupon-description::-webkit-input-placeholder{line-height:1.42em;color:#bbb}#woocommerce-coupon-description::-moz-placeholder{line-height:1.42em;color:#bbb}#woocommerce-coupon-description:-ms-input-placeholder{line-height:1.42em;color:#bbb}#woocommerce-coupon-description:-moz-placeholder{line-height:1.42em;color:#bbb}#woocommerce-coupon-data .panel-wrap,#woocommerce-product-data .panel-wrap{background:#fff}#woocommerce-coupon-data .wc-metaboxes-wrapper,#woocommerce-coupon-data .woocommerce_options_panel,#woocommerce-product-data .wc-metaboxes-wrapper,#woocommerce-product-data .woocommerce_options_panel{float:left;width:80%}#woocommerce-coupon-data .wc-metaboxes-wrapper .wc-radios,#woocommerce-coupon-data .woocommerce_options_panel .wc-radios,#woocommerce-product-data .wc-metaboxes-wrapper .wc-radios,#woocommerce-product-data .woocommerce_options_panel .wc-radios{display:block;float:left;margin:0}#woocommerce-coupon-data .wc-metaboxes-wrapper .wc-radios li,#woocommerce-coupon-data .woocommerce_options_panel .wc-radios li,#woocommerce-product-data .wc-metaboxes-wrapper .wc-radios li,#woocommerce-product-data .woocommerce_options_panel .wc-radios li{display:block;padding:0 0 10px}#woocommerce-coupon-data .wc-metaboxes-wrapper .wc-radios li input,#woocommerce-coupon-data .woocommerce_options_panel .wc-radios li input,#woocommerce-product-data .wc-metaboxes-wrapper .wc-radios li input,#woocommerce-product-data .woocommerce_options_panel .wc-radios li input{width:auto}#woocommerce-coupon-data .panel-wrap,#woocommerce-product-data .panel-wrap,.woocommerce .panel-wrap{overflow:hidden}#woocommerce-coupon-data ul.wc-tabs,#woocommerce-product-data ul.wc-tabs,.woocommerce ul.wc-tabs{margin:0;width:20%;float:left;line-height:1em;padding:0 0 10px;position:relative;background-color:#fafafa;border-right:1px solid #eee;box-sizing:border-box}#woocommerce-coupon-data ul.wc-tabs::after,#woocommerce-product-data ul.wc-tabs::after,.woocommerce ul.wc-tabs::after{content:'';display:block;width:100%;height:9999em;position:absolute;bottom:-9999em;left:0;background-color:#fafafa;border-right:1px solid #eee}#woocommerce-coupon-data ul.wc-tabs li,#woocommerce-product-data ul.wc-tabs li,.woocommerce ul.wc-tabs li{margin:0;padding:0;display:block;position:relative}#woocommerce-coupon-data ul.wc-tabs li a,#woocommerce-product-data ul.wc-tabs li a,.woocommerce ul.wc-tabs li a{margin:0;padding:10px;display:block;box-shadow:none;text-decoration:none;line-height:20px!important;border-bottom:1px solid #eee}#woocommerce-coupon-data ul.wc-tabs li a::before,#woocommerce-product-data ul.wc-tabs li a::before,.woocommerce ul.wc-tabs li a::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin-right:.618em;content:"";text-decoration:none}#woocommerce-coupon-data ul.wc-tabs li.general_options a::before,#woocommerce-product-data ul.wc-tabs li.general_options a::before,.woocommerce ul.wc-tabs li.general_options a::before{content:'\f107'}#woocommerce-coupon-data ul.wc-tabs li.inventory_options a::before,#woocommerce-product-data ul.wc-tabs li.inventory_options a::before,.woocommerce ul.wc-tabs li.inventory_options a::before{content:'\f481'}#woocommerce-coupon-data ul.wc-tabs li.shipping_options a::before,#woocommerce-product-data ul.wc-tabs li.shipping_options a::before,.woocommerce ul.wc-tabs li.shipping_options a::before{font-family:WooCommerce;content:'\e01a'}#woocommerce-coupon-data ul.wc-tabs li.linked_product_options a::before,#woocommerce-product-data ul.wc-tabs li.linked_product_options a::before,.woocommerce ul.wc-tabs li.linked_product_options a::before{content:'\f103'}#woocommerce-coupon-data ul.wc-tabs li.attribute_options a::before,#woocommerce-product-data ul.wc-tabs li.attribute_options a::before,.woocommerce ul.wc-tabs li.attribute_options a::before{content:'\f175'}#woocommerce-coupon-data ul.wc-tabs li.advanced_options a::before,#woocommerce-product-data ul.wc-tabs li.advanced_options a::before,.woocommerce ul.wc-tabs li.advanced_options a::before{font-family:Dashicons;content:'\f111'}#woocommerce-coupon-data ul.wc-tabs li.variations_options a::before,#woocommerce-product-data ul.wc-tabs li.variations_options a::before,.woocommerce ul.wc-tabs li.variations_options a::before{content:'\f509'}#woocommerce-coupon-data ul.wc-tabs li.usage_restriction_options a::before,#woocommerce-product-data ul.wc-tabs li.usage_restriction_options a::before,.woocommerce ul.wc-tabs li.usage_restriction_options a::before{font-family:WooCommerce;content:'\e602'}#woocommerce-coupon-data ul.wc-tabs li.usage_limit_options a::before,#woocommerce-product-data ul.wc-tabs li.usage_limit_options a::before,.woocommerce ul.wc-tabs li.usage_limit_options a::before{font-family:WooCommerce;content:'\e601'}#woocommerce-coupon-data ul.wc-tabs li.general_coupon_data a::before,#woocommerce-product-data ul.wc-tabs li.general_coupon_data a::before,.woocommerce ul.wc-tabs li.general_coupon_data a::before{font-family:WooCommerce;content:'\e600'}#woocommerce-coupon-data ul.wc-tabs li.active a,#woocommerce-product-data ul.wc-tabs li.active a,.woocommerce ul.wc-tabs li.active a{color:#555;position:relative;background-color:#eee}.woocommerce_page_wc-settings .shippingrows th.check-column{padding-top:20px}.woocommerce_page_wc-settings .shippingrows tfoot th{padding-left:10px}.woocommerce_page_wc-settings .shippingrows .add.button::before{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin-right:.618em;content:"";text-decoration:none}.woocommerce_page_wc-settings h3.wc-settings-sub-title{font-size:1.2em}#woocommerce-coupon-data .inside,#woocommerce-order-data .inside,#woocommerce-order-downloads .inside,#woocommerce-product-data .inside,#woocommerce-product-type-options .inside{margin:0;padding:0}.panel,.woocommerce_options_panel{padding:9px;color:#555}.panel .form-field .woocommerce-help-tip,.woocommerce_options_panel .form-field .woocommerce-help-tip{font-size:1.4em}.panel,.woocommerce_page_settings .woocommerce_options_panel{padding:0}#woocommerce-product-specs .inside,#woocommerce-product-type-options .panel{margin:0;padding:9px}#woocommerce-product-type-options .panel p,.woocommerce_options_panel fieldset.form-field,.woocommerce_options_panel p{margin:0 0 9px;font-size:12px;padding:5px 9px;line-height:24px}#woocommerce-product-type-options .panel p::after,.woocommerce_options_panel fieldset.form-field::after,.woocommerce_options_panel p::after{content:'.';display:block;height:0;clear:both;visibility:hidden}.woocommerce_options_panel .checkbox,.woocommerce_variable_attributes .checkbox{width:auto;margin:3px 0;vertical-align:middle}.woocommerce_options_panel .downloadable_files table,.woocommerce_variations .downloadable_files table{width:100%;padding:0!important}.woocommerce_options_panel .downloadable_files table th,.woocommerce_variations .downloadable_files table th{padding:7px 0 7px 7px!important}.woocommerce_options_panel .downloadable_files table th.sort,.woocommerce_variations .downloadable_files table th.sort{width:17px;padding:7px!important}.woocommerce_options_panel .downloadable_files table th .woocommerce-help-tip,.woocommerce_variations .downloadable_files table th .woocommerce-help-tip{font-size:1.1em;margin-left:0}.woocommerce_options_panel .downloadable_files table td,.woocommerce_variations .downloadable_files table td{vertical-align:middle!important;padding:4px 0 4px 7px!important;position:relative}.woocommerce_options_panel .downloadable_files table td:last-child,.woocommerce_variations .downloadable_files table td:last-child{padding-right:7px!important}.woocommerce_options_panel .downloadable_files table td input.input_text,.woocommerce_variations .downloadable_files table td input.input_text{width:100%;float:none;min-width:0;margin:1px 0}.woocommerce_options_panel .downloadable_files table td .upload_file_button,.woocommerce_variations .downloadable_files table td .upload_file_button{width:auto;float:right;cursor:pointer}.woocommerce_options_panel .downloadable_files table td .delete,.woocommerce_variations .downloadable_files table td .delete{display:block;text-indent:-9999px;position:relative;height:1em;width:1em;font-size:1.2em}.woocommerce_options_panel .downloadable_files table td .delete::before,.woocommerce_variations .downloadable_files table td .delete::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;content:"";color:#999}.woocommerce_options_panel .downloadable_files table td .delete:hover::before,.woocommerce_variations .downloadable_files table td .delete:hover::before{color:#a00}.woocommerce_options_panel .downloadable_files table td.sort,.woocommerce_variations .downloadable_files table td.sort{width:17px;cursor:move;font-size:15px;text-align:center;background:#f9f9f9;padding-right:7px!important}.woocommerce_options_panel .downloadable_files table td.sort::before,.woocommerce_variations .downloadable_files table td.sort::before{content:'\f333';font-family:Dashicons;text-align:center;line-height:1;color:#999;display:block;width:17px;float:left;height:100%}.woocommerce_options_panel .downloadable_files table td.sort:hover::before,.woocommerce_variations .downloadable_files table td.sort:hover::before{color:#333}.woocommerce_variation h3 .sort{width:17px;height:26px;cursor:move;float:right;font-size:15px;font-weight:400;margin-right:.5em;visibility:hidden;text-align:center;vertical-align:middle}.woocommerce_variation h3 .sort::before{content:'\f333';font-family:Dashicons;text-align:center;line-height:28px;color:#999;display:block;width:17px;float:left;height:100%}.woocommerce_variation h3 .sort:hover::before{color:#777}.woocommerce_variation h3:hover .sort,.woocommerce_variation.ui-sortable-helper .sort{visibility:visible}.woocommerce_options_panel{min-height:175px;box-sizing:border-box}.woocommerce_options_panel .downloadable_files{padding:0 9px 0 162px;position:relative;margin:9px 0}.woocommerce_options_panel .downloadable_files label{position:absolute;left:0;margin:0 0 0 12px;line-height:24px}.woocommerce_options_panel p{margin:9px 0}.woocommerce_options_panel fieldset.form-field,.woocommerce_options_panel p.form-field{padding:5px 20px 5px 162px!important}.woocommerce_options_panel .sale_price_dates_fields .short:first-of-type{margin-bottom:1em}.woocommerce_options_panel .sale_price_dates_fields .short:nth-of-type(2){clear:left}.woocommerce_options_panel label,.woocommerce_options_panel legend{float:left;width:150px;padding:0;margin:0 0 0 -150px}.woocommerce_options_panel label .req,.woocommerce_options_panel legend .req{font-weight:700;font-style:normal;color:#a00}.woocommerce_options_panel .description{padding:0;margin:0 0 0 7px;clear:none;display:inline}.woocommerce_options_panel .description-block{margin-left:0;display:block}.woocommerce_options_panel input,.woocommerce_options_panel select,.woocommerce_options_panel textarea{margin:0}.woocommerce_options_panel textarea{float:left;height:3.5em;line-height:1.5em;vertical-align:top}.woocommerce_options_panel input[type=text],.woocommerce_options_panel input[type=number],.woocommerce_options_panel input[type=email],.woocommerce_options_panel input[type=password]{width:50%;float:left}.woocommerce_options_panel input.button{width:auto;margin-left:8px}.woocommerce_options_panel select{float:left}.woocommerce_options_panel .short,.woocommerce_options_panel input[type=text].short,.woocommerce_options_panel input[type=number].short,.woocommerce_options_panel input[type=email].short,.woocommerce_options_panel input[type=password].short{width:50%}.woocommerce_options_panel .sized{width:auto!important;margin-right:6px}.woocommerce_options_panel .options_group{border-top:1px solid #fff;border-bottom:1px solid #eee}.woocommerce_options_panel .options_group:first-child{border-top:0}.woocommerce_options_panel .options_group:last-child{border-bottom:0}.woocommerce_options_panel .options_group fieldset{margin:9px 0;font-size:12px;padding:5px 9px;line-height:24px}.woocommerce_options_panel .options_group fieldset label{width:auto;float:none}.woocommerce_options_panel .options_group fieldset ul{float:left;width:50%;margin:0;padding:0}.woocommerce_options_panel .options_group fieldset ul li{margin:0;width:auto}.woocommerce_options_panel .options_group fieldset ul li input{width:auto;float:none;margin-right:4px}.woocommerce_options_panel .options_group fieldset ul.wc-radios label{margin-left:0}.woocommerce_options_panel .dimensions_field .wrap{display:block;width:50%}.woocommerce_options_panel .dimensions_field .wrap input{width:30.75%;margin-right:3.8%}.woocommerce_options_panel .dimensions_field .wrap .last{margin-right:0}.woocommerce_options_panel.padded{padding:1em}#woocommerce-product-data input.dp-applied,.woocommerce_options_panel .select2-container{float:left}#grouped_product_options,#simple_product_options,#virtual_product_options{padding:12px;font-style:italic;color:#666}.wc-metaboxes-wrapper .toolbar{margin:0!important;border-top:1px solid #fff;border-bottom:1px solid #eee;padding:9px 12px!important}.wc-metaboxes-wrapper .toolbar:first-child{border-top:0}.wc-metaboxes-wrapper .toolbar:last-child{border-bottom:0}.wc-metaboxes-wrapper .toolbar .add_variation{float:right;margin-left:5px}.wc-metaboxes-wrapper .toolbar .cancel-variation-changes,.wc-metaboxes-wrapper .toolbar .save-variation-changes{float:left;margin-right:5px}.wc-metaboxes-wrapper p.toolbar{overflow:hidden;zoom:1}.wc-metaboxes-wrapper .expand-close{margin-right:2px;color:#777;font-size:12px;font-style:italic}.wc-metaboxes-wrapper .expand-close a{background:0 0;padding:0;font-size:12px;text-decoration:none}.wc-metaboxes-wrapper#product_attributes .expand-close{float:right;line-height:28px}.wc-metaboxes-wrapper .fr,.wc-metaboxes-wrapper button.add_variable_attribute{float:right;margin:0 0 0 6px}.wc-metaboxes-wrapper .wc-metaboxes{border-bottom:1px solid #eee}.wc-metaboxes-wrapper .wc-metabox-sortable-placeholder{border-color:#bbb;background-color:#f5f5f5;margin-bottom:9px;border-width:1px;border-style:dashed}.wc-metaboxes-wrapper .wc-metabox{background:#fff;border-bottom:1px solid #eee;margin:0!important}.wc-metaboxes-wrapper .wc-metabox select{font-weight:400}.wc-metaboxes-wrapper .wc-metabox:last-of-type{border-bottom:0}.wc-metaboxes-wrapper .wc-metabox .handlediv{width:27px}.wc-metaboxes-wrapper .wc-metabox .handlediv::before{content:'\f142'!important;cursor:pointer;display:inline-block;font:400 20px/1 Dashicons;line-height:.5!important;padding:8px 10px;position:relative;right:12px;top:0}.wc-metaboxes-wrapper .wc-metabox.closed{border-radius:3px}.wc-metaboxes-wrapper .wc-metabox.closed .handlediv::before{content:'\f140'!important}.wc-metaboxes-wrapper .wc-metabox.closed h3{border:0}.wc-metaboxes-wrapper .wc-metabox h3{margin:0!important;padding:.75em .75em .75em 1em!important;font-size:1em!important;overflow:hidden;zoom:1;cursor:move}.wc-metaboxes-wrapper .wc-metabox h3 a.delete,.wc-metaboxes-wrapper .wc-metabox h3 button{float:right}.wc-metaboxes-wrapper .wc-metabox h3 a.delete{color:red;font-weight:400;line-height:26px;text-decoration:none;position:relative;visibility:hidden}.wc-metaboxes-wrapper .wc-metabox h3 strong{line-height:26px;font-weight:700}.wc-metaboxes-wrapper .wc-metabox h3 select{font-family:sans-serif;max-width:20%;margin:.25em .25em .25em 0}.wc-metaboxes-wrapper .wc-metabox h3 .handlediv{background-position:6px 5px!important;visibility:hidden;height:26px}.wc-metaboxes-wrapper .wc-metabox h3.fixed{cursor:pointer!important}.wc-metaboxes-wrapper .wc-metabox.woocommerce_variation h3{cursor:pointer;padding:.5em .75em .5em 1em!important}.wc-metaboxes-wrapper .wc-metabox.woocommerce_variation h3 .handlediv,.wc-metaboxes-wrapper .wc-metabox.woocommerce_variation h3 .sort,.wc-metaboxes-wrapper .wc-metabox.woocommerce_variation h3 a.delete{margin-top:.25em}.wc-metaboxes-wrapper .wc-metabox h3:hover .handlediv,.wc-metaboxes-wrapper .wc-metabox h3:hover a.delete,.wc-metaboxes-wrapper .wc-metabox.ui-sortable-helper .handlediv,.wc-metaboxes-wrapper .wc-metabox.ui-sortable-helper a.delete{visibility:visible}.wc-metaboxes-wrapper .wc-metabox table{width:100%;position:relative;background-color:#fdfdfd;padding:1em;border-top:1px solid #eee}.wc-metaboxes-wrapper .wc-metabox table td{text-align:left;padding:0 6px 1em 0;vertical-align:top;border:0}.wc-metaboxes-wrapper .wc-metabox table td label{text-align:left;display:block;line-height:21px}.wc-metaboxes-wrapper .wc-metabox table td input{float:left;min-width:200px}.wc-metaboxes-wrapper .wc-metabox table td input,.wc-metaboxes-wrapper .wc-metabox table td textarea{width:100%;margin:0;display:block;font-size:14px;padding:4px;color:#555}.wc-metaboxes-wrapper .wc-metabox table td .select2-container,.wc-metaboxes-wrapper .wc-metabox table td select{width:100%!important}.wc-metaboxes-wrapper .wc-metabox table td input.short{width:200px}.wc-metaboxes-wrapper .wc-metabox table td input.checkbox{width:16px;min-width:inherit;vertical-align:text-bottom;display:inline-block;float:none}.wc-metaboxes-wrapper .wc-metabox table td.attribute_name{width:200px}.wc-metaboxes-wrapper .wc-metabox table .minus,.wc-metaboxes-wrapper .wc-metabox table .plus{margin-top:6px}.wc-metaboxes-wrapper .wc-metabox table .fl{float:left}.wc-metaboxes-wrapper .wc-metabox table .fr{float:right}.variations-pagenav{float:right;line-height:24px}.variations-pagenav .displaying-num{color:#777;font-size:12px;font-style:italic}.variations-pagenav a{padding:0 10px 3px;background:rgba(0,0,0,.05);font-size:16px;font-weight:400;text-decoration:none}.variations-pagenav a.disabled,.variations-pagenav a.disabled:active,.variations-pagenav a.disabled:focus,.variations-pagenav a.disabled:hover{color:#a0a5aa;background:rgba(0,0,0,.05)}.variations-defaults{float:left}.variations-defaults select{margin:.25em .25em .25em 0}.woocommerce_variable_attributes{background-color:#fdfdfd;border-top:1px solid #eee}.woocommerce_variable_attributes .data{padding:1em 2em}.woocommerce_variable_attributes .data::after,.woocommerce_variable_attributes .data::before{content:' ';display:table}.woocommerce_variable_attributes .upload_image_button{display:block;width:64px;height:64px;float:left;margin-right:20px;position:relative;cursor:pointer}.woocommerce_variable_attributes .upload_image_button img{width:100%;height:auto;display:none}.woocommerce_variable_attributes .upload_image_button::before{content:'\f128';font-family:Dashicons;position:absolute;top:0;left:0;right:0;bottom:0;text-align:center;line-height:64px;font-size:64px;font-weight:400;-webkit-font-smoothing:antialiased}.woocommerce_variable_attributes .upload_image_button.remove img{display:block}.woocommerce_variable_attributes .upload_image_button.remove::before{content:'\f335';display:none}.woocommerce_variable_attributes .upload_image_button.remove:hover::before{display:block}.woocommerce_variable_attributes .options{border:1px solid #eee;border-width:1px 0;padding:.25em 0}.woocommerce_variable_attributes .options label{display:inline-block;padding:4px 1em 2px 0}.woocommerce_variable_attributes .options input[type=checkbox]{margin:-3px 0 0 .5em;vertical-align:middle}.form-row label{display:inline-block}.form-row .woocommerce-help-tip{float:right}.form-row input[type=number],.form-row input[type=text],.form-row select,.form-row textarea{width:100%;vertical-align:middle;margin:2px 0 0;padding:6px}.form-row select{height:30px;line-height:30px}.form-row.dimensions_field .wrap{clear:left;display:block}.form-row.dimensions_field input{width:33%;float:left;vertical-align:middle}.form-row.dimensions_field input:last-of-type{margin-right:0;width:34%}.form-row.form-row-first,.form-row.form-row-last{width:48%;float:right}.form-row.form-row-first{clear:both;float:left}.form-row.form-row-full{clear:both}.tips{cursor:help;text-decoration:none}img.tips{padding:5px 0 0}#tiptip_holder{display:none;position:absolute;top:0;left:0;z-index:9999999}#tiptip_holder.tip_top{padding-bottom:5px}#tiptip_holder.tip_top #tiptip_arrow_inner{margin-top:-7px;margin-left:-6px;border-top-color:#333}#tiptip_holder.tip_bottom{padding-top:5px}#tiptip_holder.tip_bottom #tiptip_arrow_inner{margin-top:-5px;margin-left:-6px;border-bottom-color:#333}#tiptip_holder.tip_right{padding-left:5px}#tiptip_holder.tip_right #tiptip_arrow_inner{margin-top:-6px;margin-left:-5px;border-right-color:#333}#tiptip_holder.tip_left{padding-right:5px}#tiptip_holder.tip_left #tiptip_arrow_inner{margin-top:-6px;margin-left:-7px;border-left-color:#333}#tiptip_content,.chart-tooltip,.wc_error_tip{color:#fff;font-size:.8em;max-width:150px;background:#333;text-align:center;border-radius:3px;padding:.618em 1em;box-shadow:0 1px 3px rgba(0,0,0,.2)}#tiptip_content code,.chart-tooltip code,.wc_error_tip code{padding:1px;background:#888}#tiptip_arrow,#tiptip_arrow_inner{position:absolute;border-color:transparent;border-style:solid;border-width:6px;height:0;width:0}.wc_error_tip{max-width:20em;line-height:1.8em;position:absolute;white-space:normal;background:#d82223;margin:1.5em 1px 0 -1em;z-index:9999999}.wc_error_tip::after{content:'';display:block;border:8px solid #d82223;border-right-color:transparent;border-left-color:transparent;border-top-color:transparent;position:absolute;top:-3px;left:50%;margin:-1em 0 0 -3px}img.ui-datepicker-trigger{vertical-align:middle;margin-top:-1px;cursor:pointer}.wc-metabox-content img.ui-datepicker-trigger,.woocommerce_options_panel img.ui-datepicker-trigger{float:left;margin-right:8px;margin-top:4px;margin-left:4px}#ui-datepicker-div{display:none}.woocommerce-reports-wide.woocommerce-reports-wrap,.woocommerce-reports-wrap.woocommerce-reports-wrap{margin-left:300px;padding-top:18px}.woocommerce-reports-wide.halved,.woocommerce-reports-wrap.halved{margin:0;overflow:hidden;zoom:1}.woocommerce-reports-wide .widefat td,.woocommerce-reports-wrap .widefat td{vertical-align:top;padding:7px}.woocommerce-reports-wide .widefat td .description,.woocommerce-reports-wrap .widefat td .description{margin:4px 0 0}.woocommerce-reports-wide .postbox::after,.woocommerce-reports-wrap .postbox::after{content:'.';display:block;height:0;clear:both;visibility:hidden}.woocommerce-reports-wide .postbox h3,.woocommerce-reports-wrap .postbox h3{cursor:default!important}.woocommerce-reports-wide .postbox .inside,.woocommerce-reports-wrap .postbox .inside{padding:10px;margin:0!important}.woocommerce-reports-wide .postbox div.stats_range,.woocommerce-reports-wide .postbox h3.stats_range,.woocommerce-reports-wrap .postbox div.stats_range,.woocommerce-reports-wrap .postbox h3.stats_range{border-bottom-color:#dfdfdf;margin:0;padding:0!important}.woocommerce-reports-wide .postbox div.stats_range .export_csv,.woocommerce-reports-wide .postbox h3.stats_range .export_csv,.woocommerce-reports-wrap .postbox div.stats_range .export_csv,.woocommerce-reports-wrap .postbox h3.stats_range .export_csv{float:right;line-height:26px;border-left:1px solid #dfdfdf;padding:10px;display:block;text-decoration:none}.woocommerce-reports-wide .postbox div.stats_range .export_csv::before,.woocommerce-reports-wide .postbox h3.stats_range .export_csv::before,.woocommerce-reports-wrap .postbox div.stats_range .export_csv::before,.woocommerce-reports-wrap .postbox h3.stats_range .export_csv::before{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;content:"";text-decoration:none;margin-right:4px}.woocommerce-reports-wide .postbox div.stats_range ul,.woocommerce-reports-wide .postbox h3.stats_range ul,.woocommerce-reports-wrap .postbox div.stats_range ul,.woocommerce-reports-wrap .postbox h3.stats_range ul{list-style:none;margin:0;padding:0;zoom:1;background:#f5f5f5;border-bottom:1px solid #ccc}.woocommerce-reports-wide .postbox div.stats_range ul::after,.woocommerce-reports-wide .postbox div.stats_range ul::before,.woocommerce-reports-wide .postbox h3.stats_range ul::after,.woocommerce-reports-wide .postbox h3.stats_range ul::before,.woocommerce-reports-wrap .postbox div.stats_range ul::after,.woocommerce-reports-wrap .postbox div.stats_range ul::before,.woocommerce-reports-wrap .postbox h3.stats_range ul::after,.woocommerce-reports-wrap .postbox h3.stats_range ul::before{content:' ';display:table}.woocommerce-reports-wide .postbox div.stats_range ul::after,.woocommerce-reports-wide .postbox h3.stats_range ul::after,.woocommerce-reports-wrap .postbox div.stats_range ul::after,.woocommerce-reports-wrap .postbox h3.stats_range ul::after{clear:both}.woocommerce-reports-wide .postbox div.stats_range ul li,.woocommerce-reports-wide .postbox h3.stats_range ul li,.woocommerce-reports-wrap .postbox div.stats_range ul li,.woocommerce-reports-wrap .postbox h3.stats_range ul li{float:left;margin:0;padding:0;line-height:26px;font-weight:700;font-size:14px}.woocommerce-reports-wide .postbox div.stats_range ul li a,.woocommerce-reports-wide .postbox h3.stats_range ul li a,.woocommerce-reports-wrap .postbox div.stats_range ul li a,.woocommerce-reports-wrap .postbox h3.stats_range ul li a{border-right:1px solid #dfdfdf;padding:10px;display:block;text-decoration:none}.woocommerce-reports-wide .postbox div.stats_range ul li.active,.woocommerce-reports-wide .postbox h3.stats_range ul li.active,.woocommerce-reports-wrap .postbox div.stats_range ul li.active,.woocommerce-reports-wrap .postbox h3.stats_range ul li.active{background:#fff;-webkit-box-shadow:0 4px 0 0 #fff;box-shadow:0 4px 0 0 #fff}.woocommerce-reports-wide .postbox div.stats_range ul li.active a,.woocommerce-reports-wide .postbox h3.stats_range ul li.active a,.woocommerce-reports-wrap .postbox div.stats_range ul li.active a,.woocommerce-reports-wrap .postbox h3.stats_range ul li.active a{color:#777}.woocommerce-reports-wide .postbox div.stats_range ul li.custom,.woocommerce-reports-wide .postbox h3.stats_range ul li.custom,.woocommerce-reports-wrap .postbox div.stats_range ul li.custom,.woocommerce-reports-wrap .postbox h3.stats_range ul li.custom{padding:9px 10px;vertical-align:middle}.woocommerce-reports-wide .postbox div.stats_range ul li.custom div,.woocommerce-reports-wide .postbox div.stats_range ul li.custom form,.woocommerce-reports-wide .postbox h3.stats_range ul li.custom div,.woocommerce-reports-wide .postbox h3.stats_range ul li.custom form,.woocommerce-reports-wrap .postbox div.stats_range ul li.custom div,.woocommerce-reports-wrap .postbox div.stats_range ul li.custom form,.woocommerce-reports-wrap .postbox h3.stats_range ul li.custom div,.woocommerce-reports-wrap .postbox h3.stats_range ul li.custom form{display:inline;margin:0}.woocommerce-reports-wide .postbox div.stats_range ul li.custom div input.range_datepicker,.woocommerce-reports-wide .postbox div.stats_range ul li.custom form input.range_datepicker,.woocommerce-reports-wide .postbox h3.stats_range ul li.custom div input.range_datepicker,.woocommerce-reports-wide .postbox h3.stats_range ul li.custom form input.range_datepicker,.woocommerce-reports-wrap .postbox div.stats_range ul li.custom div input.range_datepicker,.woocommerce-reports-wrap .postbox div.stats_range ul li.custom form input.range_datepicker,.woocommerce-reports-wrap .postbox h3.stats_range ul li.custom div input.range_datepicker,.woocommerce-reports-wrap .postbox h3.stats_range ul li.custom form input.range_datepicker{padding:0;margin:0 10px 0 0;background:0 0;border:0;color:#777;text-align:center;-webkit-box-shadow:none;box-shadow:none}.woocommerce-reports-wide .postbox div.stats_range ul li.custom div input.range_datepicker.from,.woocommerce-reports-wide .postbox div.stats_range ul li.custom form input.range_datepicker.from,.woocommerce-reports-wide .postbox h3.stats_range ul li.custom div input.range_datepicker.from,.woocommerce-reports-wide .postbox h3.stats_range ul li.custom form input.range_datepicker.from,.woocommerce-reports-wrap .postbox div.stats_range ul li.custom div input.range_datepicker.from,.woocommerce-reports-wrap .postbox div.stats_range ul li.custom form input.range_datepicker.from,.woocommerce-reports-wrap .postbox h3.stats_range ul li.custom div input.range_datepicker.from,.woocommerce-reports-wrap .postbox h3.stats_range ul li.custom form input.range_datepicker.from{margin-right:0}.woocommerce-reports-wide .postbox .chart-with-sidebar,.woocommerce-reports-wrap .postbox .chart-with-sidebar{padding:12px 12px 12px 249px;margin:0!important}.woocommerce-reports-wide .postbox .chart-with-sidebar .chart-sidebar,.woocommerce-reports-wrap .postbox .chart-with-sidebar .chart-sidebar{width:225px;margin-left:-237px;float:left}.woocommerce-reports-wide .postbox .chart-widgets,.woocommerce-reports-wrap .postbox .chart-widgets{margin:0;padding:0}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget{margin:0 0 1em;background:#fafafa;border:1px solid #dfdfdf}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget::after,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget::after{content:'.';display:block;height:0;clear:both;visibility:hidden}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget h4,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget h4{background:#fff;border:1px solid #dfdfdf;border-left-width:0;border-right-width:0;padding:10px;margin:0;color:#2ea2cc;border-top-width:0;background-image:-webkit-gradient(linear,left bottom,left top,from(#ececec),to(#f9f9f9));background-image:-webkit-linear-gradient(bottom,#ececec,#f9f9f9);background-image:-moz-linear-gradient(bottom,#ececec,#f9f9f9);background-image:-o-linear-gradient(bottom,#ececec,#f9f9f9);background-image:linear-gradient(to top,#ececec,#f9f9f9)}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table td.count,.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table tr.active td,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table td.count,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table tr.active td{background:#f5f5f5}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget h4.section_title:hover,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget h4.section_title:hover{color:#a00}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section_title,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section_title{cursor:pointer}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section_title span,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section_title span{display:block}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section_title span::after,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section_title span::after{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin-left:.618em;content:"";text-decoration:none;float:right;font-size:.9em;line-height:1.618}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section_title.open,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section_title.open{color:#333}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section_title.open span::after,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section_title.open span::after{display:none}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section{border-bottom:1px solid #dfdfdf}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section .select2-container,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section .select2-container{width:100%!important}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .section:last-of-type,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .section:last-of-type{border-radius:0 0 3px 3px}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table{width:100%}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table td,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table td{padding:7px 10px;vertical-align:top;border-top:1px solid #e5e5e5;line-height:1.4em}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table td.sparkline,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table td.sparkline,form.report_filters div,form.report_filters input,form.report_filters label,form.report_filters p{vertical-align:middle}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table tr:first-child td,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table tr:first-child td{border-top:0}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table td.name,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table td.name{max-width:175px}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table td.name a,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table td.name a{word-wrap:break-word}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget table .wc_sparkline,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget table .wc_sparkline{width:32px;height:1em;display:block;float:right}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget form,.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget p,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget form,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget p{margin:0;padding:10px}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget form .submit,.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget p .submit,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget form .submit,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget p .submit{margin-top:10px}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget #product_ids,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget #product_ids{width:100%}.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .select_all,.woocommerce-reports-wide .postbox .chart-widgets li.chart-widget .select_none,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .select_all,.woocommerce-reports-wrap .postbox .chart-widgets li.chart-widget .select_none{float:right;color:#999;margin-left:4px;margin-top:10px}.woocommerce-reports-wide .postbox .chart-legend,.woocommerce-reports-wrap .postbox .chart-legend{list-style:none;margin:0 0 1em;padding:0;border:1px solid #dfdfdf;border-right-width:0;border-bottom-width:0;background:#fff}.woocommerce-reports-wide .postbox .chart-legend li,.woocommerce-reports-wrap .postbox .chart-legend li{border-right:5px solid #aaa;color:#aaa;padding:1em;display:block;margin:0;-webkit-transition:all ease .5s;box-shadow:inset 0 -1px 0 0 #dfdfdf}.woocommerce-reports-wide .postbox .chart-legend li strong,.woocommerce-reports-wrap .postbox .chart-legend li strong{font-size:1.618em;line-height:1.2em;color:#464646;font-weight:400;display:block;font-family:HelveticaNeue-Light,'Helvetica Neue Light','Helvetica Neue',sans-serif}.woocommerce-reports-wide .postbox .chart-legend li strong del,.woocommerce-reports-wrap .postbox .chart-legend li strong del{color:#e74c3c;font-weight:400}.woocommerce-reports-wide .postbox .chart-legend li:hover,.woocommerce-reports-wrap .postbox .chart-legend li:hover{box-shadow:inset 0 -1px 0 0 #dfdfdf,inset 300px 0 0 rgba(156,93,144,.1);border-right:5px solid #9c5d90!important;padding-left:1.5em;color:#9c5d90}.woocommerce-reports-wide .postbox .pie-chart-legend,.woocommerce-reports-wrap .postbox .pie-chart-legend{margin:12px 0 0;overflow:hidden}.woocommerce-reports-wide .postbox .pie-chart-legend li,.woocommerce-reports-wrap .postbox .pie-chart-legend li{float:left;margin:0;padding:6px 0 0;border-top:4px solid #999;text-align:center;box-sizing:border-box;width:50%}.woocommerce-reports-wide .postbox .stat,.woocommerce-reports-wrap .postbox .stat{font-size:1.5em!important;font-weight:700;text-align:center}.woocommerce-reports-wide .postbox .chart-placeholder,.woocommerce-reports-wrap .postbox .chart-placeholder{width:100%;height:650px;overflow:hidden;position:relative}.woocommerce-reports-wide .postbox .chart-prompt,.woocommerce-reports-wrap .postbox .chart-prompt{line-height:650px;margin:0;color:#999;font-size:1.2em;font-style:italic;text-align:center}.woocommerce-reports-wide .postbox .chart-container,.woocommerce-reports-wrap .postbox .chart-container{background:#fff;padding:12px;position:relative;border:1px solid #dfdfdf;border-radius:3px}.woocommerce-reports-wide .postbox .main .chart-legend,.woocommerce-reports-wrap .postbox .main .chart-legend{margin-top:12px}.woocommerce-reports-wide .postbox .main .chart-legend li,.woocommerce-reports-wrap .postbox .main .chart-legend li{border-right:0;margin:0 8px 0 0;float:left;border-top:4px solid #aaa}.woocommerce-reports-wide .woocommerce-reports-main,.woocommerce-reports-wrap .woocommerce-reports-main{float:left;min-width:100%}.woocommerce-reports-wide .woocommerce-reports-main table td,.woocommerce-reports-wrap .woocommerce-reports-main table td{padding:9px}.woocommerce-reports-wide .woocommerce-reports-sidebar,.woocommerce-reports-wrap .woocommerce-reports-sidebar{display:inline;width:281px;margin-left:-300px;clear:both;float:left}.woocommerce-reports-wide .woocommerce-reports-left,.woocommerce-reports-wrap .woocommerce-reports-left{width:49.5%;float:left}.woocommerce-reports-wide .woocommerce-reports-right,.woocommerce-reports-wrap .woocommerce-reports-right{width:49.5%;float:right}.woocommerce-reports-wide .column-wc_actions a.edit,.woocommerce-reports-wide .column-wc_actions a.view,.woocommerce-reports-wrap .column-wc_actions a.edit,.woocommerce-reports-wrap .column-wc_actions a.view{display:block;text-indent:-9999px;position:relative;padding:0!important;height:2em!important;width:2em}.woocommerce-reports-wide .column-wc_actions a.edit::after,.woocommerce-reports-wide .column-wc_actions a.view::after,.woocommerce-reports-wrap .column-wc_actions a.edit::after,.woocommerce-reports-wrap .column-wc_actions a.view::after{font-family:Dashicons;speak:none;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;line-height:1.85}.woocommerce-reports-wide .column-wc_actions a.edit::after,.woocommerce-reports-wrap .column-wc_actions a.edit::after{content:'\f464'}.woocommerce-reports-wide .column-wc_actions a.view::after,.woocommerce-reports-wrap .column-wc_actions a.view::after{content:'\f177'}.woocommerce-wide-reports-wrap{padding-bottom:11px}.woocommerce-wide-reports-wrap .widefat .export-data{float:right}.woocommerce-wide-reports-wrap .widefat td,.woocommerce-wide-reports-wrap .widefat th{vertical-align:middle;padding:7px}.chart-tooltip{position:absolute;display:none;line-height:1}table.bar_chart{width:100%}table.bar_chart thead th{text-align:left;color:#ccc;padding:6px 0}table.bar_chart tbody th{padding:6px 0;width:25%;text-align:left!important;font-weight:400!important;border-bottom:1px solid #fee}table.bar_chart tbody td{text-align:right;line-height:24px;padding:6px 6px 6px 0;border-bottom:1px solid #fee}table.bar_chart tbody td span{color:#8a4b75;display:block}table.bar_chart tbody td span.alt{color:#47a03e;margin-top:6px}table.bar_chart tbody td.bars{position:relative;text-align:left;padding:6px 6px 6px 0;border-bottom:1px solid #fee}table.bar_chart tbody td.bars a,table.bar_chart tbody td.bars span{text-decoration:none;clear:both;background:#8a4b75;float:left;display:block;line-height:24px;height:24px;-moz-border-radius:3px;-webkit-border-radius:3px;-o-border-radius:3px;-khtml-border-radius:3px;border-radius:3px}.post-type-product .woocommerce-BlankState-message::before,.post-type-shop_coupon .woocommerce-BlankState-message::before,.post-type-shop_order .woocommerce-BlankState-message::before{font-family:WooCommerce;speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}table.bar_chart tbody td.bars span.alt{clear:both;background:#47a03e}table.bar_chart tbody td.bars span.alt span{margin:0;color:#c5dec2!important;text-shadow:0 1px 0 #47a03e;background:0 0}.post-type-shop_order .woocommerce-BlankState-message::before{content:""}.post-type-shop_coupon .woocommerce-BlankState-message::before{content:""}.post-type-product .woocommerce-BlankState-message::before{content:""}.woocommerce-BlankState{text-align:center;padding:5em 0 0}.woocommerce-BlankState .woocommerce-BlankState-message{color:#aaa;margin:0 auto 1.5em;line-height:1.5em;font-size:1.2em;max-width:500px}.woocommerce-BlankState .woocommerce-BlankState-message::before{color:#ddd;text-shadow:0 -1px 1px rgba(0,0,0,.2),0 1px 0 rgba(255,255,255,.8);font-size:8em;display:block;position:relative!important;top:auto;left:auto;line-height:1em;margin:0 0 .1875em}.woocommerce-BlankState .woocommerce-BlankState-cta{font-size:1.2em;padding:.75em 1.5em;height:auto}@media only screen and (max-width:1280px){#order_data .order_data_column{width:48%}#order_data .order_data_column:first-child{width:100%}.woocommerce_options_panel .description{display:block;clear:both;margin-left:0}.woocommerce_options_panel .dimensions_field .wrap,.woocommerce_options_panel .short,.woocommerce_options_panel input[type=text].short,.woocommerce_options_panel input[type=number].short,.woocommerce_options_panel input[type=email].short,.woocommerce_options_panel input[type=password].short{width:80%}.woocommerce_options_panel .downloadable_files,.woocommerce_variations .downloadable_files{padding:0;clear:both}.woocommerce_options_panel .downloadable_files label,.woocommerce_variations .downloadable_files label{position:static}.woocommerce_options_panel .downloadable_files table,.woocommerce_variations .downloadable_files table{margin:0 12px 24px;width:94%}.woocommerce_options_panel .downloadable_files table .sort,.woocommerce_variations .downloadable_files table .sort{visibility:hidden}.woocommerce_options_panel .woocommerce_variable_attributes .downloadable_files table,.woocommerce_variations .woocommerce_variable_attributes .downloadable_files table{margin:0 0 1em;width:100%}}@media only screen and (max-width:900px){#woocommerce-coupon-data ul.coupon_data_tabs,#woocommerce-product-data .wc-tabs-back,#woocommerce-product-data ul.product_data_tabs{width:10%}#woocommerce-coupon-data .wc-metaboxes-wrapper,#woocommerce-coupon-data .woocommerce_options_panel,#woocommerce-product-data .wc-metaboxes-wrapper,#woocommerce-product-data .woocommerce_options_panel{width:90%}#woocommerce-coupon-data ul.coupon_data_tabs li a,#woocommerce-product-data ul.product_data_tabs li a{position:relative;text-indent:-999px;padding:10px}#woocommerce-coupon-data ul.coupon_data_tabs li a::before,#woocommerce-product-data ul.product_data_tabs li a::before{position:absolute;top:0;right:0;bottom:0;left:0;text-indent:0;text-align:center;line-height:40px;width:100%;height:40px}}@media only screen and (max-width:782px){#wp-excerpt-media-buttons a{font-size:16px;line-height:37px;height:39px;padding:0 20px 0 15px}#wp-excerpt-editor-tools{padding-top:20px;padding-right:15px;overflow:hidden;margin-bottom:-1px}.post-type-product .wp-list-table .is-expanded td:not(.hidden),.post-type-shop_order .wp-list-table .is-expanded td:not(.hidden){overflow:visible}#woocommerce-product-data .checkbox{width:25px}.variations-pagenav{float:none;text-align:center;font-size:18px}.variations-pagenav .displaying-num{font-size:16px}.variations-pagenav a{padding:8px 20px 11px;font-size:18px}.variations-pagenav select{padding:0 20px}.variations-defaults{float:none;text-align:center;margin-top:10px}.post-type-product .wp-list-table .column-thumb{display:none;text-align:left;padding-bottom:0}.post-type-product .wp-list-table .column-thumb::before{display:none!important}.post-type-product .wp-list-table .column-thumb img{max-width:32px}.post-type-product .wp-list-table .toggle-row{top:-28px}.post-type-shop_order .wp-list-table .column-order_status{display:none;text-align:left;padding-bottom:0}.post-type-shop_order .wp-list-table .column-order_status mark{margin:0}.post-type-shop_order .wp-list-table .column-order_status::before{display:none!important}.post-type-shop_order .wp-list-table .column-customer_message,.post-type-shop_order .wp-list-table .column-order_notes{text-align:inherit}.post-type-shop_order .wp-list-table .column-order_notes .note-on{font-size:1.3em;margin:0}.post-type-shop_order .wp-list-table .toggle-row{top:-15px}}@media only screen and (max-width:500px){.woocommerce_options_panel label,.woocommerce_options_panel legend{float:none;width:auto;display:block;margin:0}.woocommerce_options_panel fieldset.form-field,.woocommerce_options_panel p.form-field{padding:5px 20px!important}}.wc-backbone-modal *{box-sizing:border-box}.wc-backbone-modal .wc-backbone-modal-content{position:fixed;background:#fff;z-index:100000;left:50%;top:50%;transform:translate(-50%,-50%);width:500px}.wc-backbone-modal .wc-backbone-modal-content article{overflow:auto}.wc-backbone-modal.wc-backbone-modal-shipping-method-settings .wc-backbone-modal-content{width:75%;min-width:500px}.wc-backbone-modal-backdrop{position:fixed;top:0;left:0;right:0;bottom:0;min-height:360px;background:#000;opacity:.7;z-index:99900}.wc-backbone-modal-main{padding-bottom:55px}.wc-backbone-modal-main article,.wc-backbone-modal-main header{display:block;position:relative}.wc-backbone-modal-main .wc-backbone-modal-header{height:auto;background:#fcfcfc;padding:1em 1.5em;border-bottom:1px solid #ddd}.wc-backbone-modal-main .wc-backbone-modal-header h1{margin:0;font-size:18px;font-weight:700;line-height:1.5em}.wc-backbone-modal-main .wc-backbone-modal-header .modal-close-link{cursor:pointer;color:#777;height:54px;width:54px;padding:0;position:absolute;top:0;right:0;text-align:center;border:0;border-left:1px solid #ddd;background-color:transparent;-webkit-transition:color .1s ease-in-out,background .1s ease-in-out;transition:color .1s ease-in-out,background .1s ease-in-out}.wc-backbone-modal-main .wc-backbone-modal-header .modal-close-link::before{font:400 22px/50px dashicons!important;color:#666;display:block;content:'\f335';font-weight:300}.wc-backbone-modal-main .wc-backbone-modal-header .modal-close-link:focus,.wc-backbone-modal-main .wc-backbone-modal-header .modal-close-link:hover{background:#ddd;border-color:#ccc;color:#000}.wc-backbone-modal-main .wc-backbone-modal-header .modal-close-link:focus{outline:0}.wc-backbone-modal-main article{padding:1.5em}.wc-backbone-modal-main article p{margin:1.5em 0}.wc-backbone-modal-main article p:last-child,.wc-backbone-modal-main footer .inner .button{margin-bottom:0}.wc-backbone-modal-main article p:first-child{margin-top:0}.wc-backbone-modal-main article .pagination{padding:10px 0 0;text-align:center}.wc-backbone-modal-main footer{position:absolute;left:0;right:0;bottom:0;z-index:100;padding:1em 1.5em;background:#fcfcfc;border-top:1px solid #dfdfdf;box-shadow:0 -4px 4px -4px rgba(0,0,0,.1)}.wc-backbone-modal-main footer .inner{float:right;line-height:23px}.select2-drop{z-index:999999!important}.select2-container-multi .select2-choices .select2-search-field input{font-family:inherit;font-size:inherit;font-weight:inherit;padding:3px 5px}.select2-container{line-height:1.85em;font-size:14px} \ No newline at end of file diff --git a/assets/css/admin.scss b/assets/css/admin.scss index 63264d6fdaf..e929cddb7e3 100644 --- a/assets/css/admin.scss +++ b/assets/css/admin.scss @@ -3777,6 +3777,7 @@ img.help_tip { } textarea { + float: left; height: 3.5em; line-height: 1.5em; vertical-align: top; @@ -4189,8 +4190,8 @@ img.help_tip { .upload_image_button { display: block; - width: 48px; - height: 48px; + width: 64px; + height: 64px; float: left; margin-right: 20px; position: relative; @@ -4211,8 +4212,8 @@ img.help_tip { right: 0; bottom: 0; text-align: center; - line-height: 48px; - font-size: 48px; + line-height: 64px; + font-size: 64px; font-weight: 400; -webkit-font-smoothing: antialiased; } @@ -4246,49 +4247,67 @@ img.help_tip { } input[type=checkbox] { - margin-top: 5px; - margin-right: 3px; + margin: -3px 0 0 .5em; + vertical-align: middle; } } } .form-row { label { - display: block; + display: inline-block; + } + + .woocommerce-help-tip { + float: right; } input[type=text], input[type=number], - select { + select, + textarea { width: 100%; + vertical-align: middle; + margin: 2px 0 0; + padding: 6px; + } + + select { + height: 30px; + line-height: 30px; } &.dimensions_field { + .wrap { + clear:left; + display: block; + } input { - width: 25%; + width: 33%; float: left; - margin-right: 1%; + vertical-align: middle; &:last-of-type { margin-right: 0; + width: 34%; } } } -} -.form-row-first, -.form-row-last { - width: 48%; - float: right; -} + &.form-row-first, + &.form-row-last { + width: 48%; + float: right; + } -.form-row-first { - clear: both; - float: left; -} + &.form-row-first { + clear: both; + float: left; + } -.form-row-full { - clear: both; + &.form-row-full { + clear: both; + } } /** diff --git a/includes/abstracts/abstract-wc-legacy-order.php b/includes/abstracts/abstract-wc-legacy-order.php index 653e0f067dc..d20d49a626b 100644 --- a/includes/abstracts/abstract-wc-legacy-order.php +++ b/includes/abstracts/abstract-wc-legacy-order.php @@ -151,7 +151,7 @@ abstract class WC_Abstract_Legacy_Order extends WC_Data { // Handly qty if set if ( isset( $args['qty'] ) ) { if ( $product->backorders_require_notification() && $product->is_on_backorder( $args['qty'] ) ) { - $item->add_meta_data( apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce' ) ), $args['qty'] - max( 0, $product->get_total_stock() ), true ); + $item->add_meta_data( apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce' ) ), $args['qty'] - max( 0, $product->get_stock_quantity() ), true ); } $args['subtotal'] = $args['subtotal'] ? $args['subtotal'] : $product->get_price_excluding_tax( $args['qty'] ); $args['total'] = $args['total'] ? $args['total'] : $product->get_price_excluding_tax( $args['qty'] ); diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index 8ca1d80b787..56ff97446b3 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -1,4 +1,8 @@ data ) ) ) || metadata_exists( 'post', $this->get_id(), '_' . $key ) || metadata_exists( 'post', $this->get_parent_id(), '_' . $key ); + } + + /** + * Magic __get method for backwards compatibility. Maps legacy vars to new getters. + * + * @param string $key Key name. + * @return mixed + */ + public function __get( $key ) { + _doing_it_wrong( $key, __( 'Product properties should not be accessed directly.', 'woocommerce' ), '2.7' ); + + switch ( $key ) { + case 'id' : + $value = $this->is_type( 'variation' ) ? $this->get_parent_id() : $this->get_id(); + break; + case 'variation_id' : + $value = $this->get_id(); + break; + case 'product_attributes' : + $value = isset( $this->data['attributes'] ) ? $this->data['attributes'] : ''; + break; + case 'visibility' : + $value = $this->get_catalog_visibility(); + break; + case 'sale_price_dates_from' : + $value = $this->get_date_on_sale_from(); + break; + case 'sale_price_dates_to' : + $value = $this->get_date_on_sale_to(); + break; + case 'post' : + $value = get_post( $this->get_id() ); + break; + case 'download_type' : + return 'standard'; + break; + case 'product_image_gallery' : + $value = $this->get_gallery_image_ids(); + break; + case 'variation_shipping_class' : + case 'shipping_class' : + $value = $this->get_shipping_class(); + break; + case 'total_stock' : + $value = $this->get_total_stock(); + break; + case 'downloadable' : + case 'virtual' : + case 'manage_stock' : + case 'featured' : + case 'sold_individually' : + $value = $this->{"get_$key"}() ? 'yes' : 'no'; + break; + case 'crosssell_ids' : + $value = $this->get_cross_sell_ids(); + break; + case 'upsell_ids' : + $value = $this->get_upsell_ids(); + break; + case 'parent' : + $value = wc_get_product( $this->get_parent_id() ); + break; + case 'variation_data' : + $value = wc_get_product_variation_attributes( $this->get_id() ); + break; + case 'variation_has_stock' : + $value = $this->managing_stock(); + break; + case 'variation_shipping_class_id' : + $value = $this->get_shipping_class_id(); + break; + default : + if ( in_array( $key, array_keys( $this->data ) ) ) { + $value = $this->{"get_$key"}(); + } else { + $value = get_post_meta( $this->id, '_' . $key, true ); + } + break; + } + return $value; + } + + /** + * Returns the gallery attachment ids. + * + * @deprecated 2.7.0 + * @return array + */ + public function get_gallery_attachment_ids() { + _deprecated_function( 'WC_Product::get_gallery_attachment_ids', '2.7', 'WC_Product::get_gallery_image_ids' ); + return $this->get_gallery_image_ids(); + } + + /** + * Set stock level of the product. + * + * @deprecated 2.7.0 + */ + public function set_stock( $amount = null, $mode = 'set' ) { + _deprecated_function( 'WC_Product::set_stock', '2.7', 'wc_update_product_stock' ); + return wc_update_product_stock( $this, $amount, $mode ); + } + + /** + * Reduce stock level of the product. + * + * @deprecated 2.7.0 + * @param int $amount Amount to reduce by. Default: 1 + * @return int new stock level + */ + public function reduce_stock( $amount = 1 ) { + _deprecated_function( 'WC_Product::reduce_stock', '2.7', 'wc_update_product_stock' ); + wc_update_product_stock( $this, $amount, 'subtract' ); + } + + /** + * Increase stock level of the product. + * + * @deprecated 2.7.0 + * @param int $amount Amount to increase by. Default 1. + * @return int new stock level + */ + public function increase_stock( $amount = 1 ) { + _deprecated_function( 'WC_Product::increase_stock', '2.7', 'wc_update_product_stock' ); + wc_update_product_stock( $this, $amount, 'add' ); + } + + /** + * Check if the stock status needs changing. + * + * @deprecated 2.7.0 + */ + public function check_stock_status() { + _deprecated_function( 'WC_Product::check_stock_status', '2.7', 'wc_check_product_stock_status' ); + wc_check_product_stock_status( $this ); + } + + /** + * Returns the availability of the product. + * + * @deprecated 2.7.0 + * @return string + */ + public function get_availability() { + _deprecated_function( 'WC_Product::get_availability', '2.7', 'Handled in stock.php template file and wc_format_stock_for_display function.' ); + return apply_filters( 'woocommerce_get_availability', array( + 'availability' => $this->get_availability_text(), + 'class' => $this->get_availability_class(), + ), $this ); + } + + /** + * Get availability text based on stock status. + * + * @deprecated 2.7.0 + * @return string + */ + protected function get_availability_text() { + _deprecated_function( 'WC_Product::get_availability_text', '2.7' ); + if ( ! $this->is_in_stock() ) { + $availability = __( 'Out of stock', 'woocommerce' ); + } elseif ( $this->managing_stock() && $this->is_on_backorder( 1 ) ) { + $availability = $this->backorders_require_notification() ? __( 'Available on backorder', 'woocommerce' ) : __( 'In stock', 'woocommerce' ); + } elseif ( $this->managing_stock() ) { + switch ( get_option( 'woocommerce_stock_format' ) ) { + case 'no_amount' : + $availability = __( 'In stock', 'woocommerce' ); + break; + case 'low_amount' : + if ( $this->get_total_stock() <= get_option( 'woocommerce_notify_low_stock_amount' ) ) { + $availability = sprintf( __( 'Only %s left in stock', 'woocommerce' ), $this->get_total_stock() ); + + if ( $this->backorders_allowed() && $this->backorders_require_notification() ) { + $availability .= ' ' . __( '(also available on backorder)', 'woocommerce' ); + } + } else { + $availability = __( 'In stock', 'woocommerce' ); + } + break; + default : + $availability = sprintf( __( '%s in stock', 'woocommerce' ), $this->get_total_stock() ); + + if ( $this->backorders_allowed() && $this->backorders_require_notification() ) { + $availability .= ' ' . __( '(also available on backorder)', 'woocommerce' ); + } + break; + } + } else { + $availability = ''; + } + return apply_filters( 'woocommerce_get_availability_text', $availability, $this ); + } + + /** + * Get availability classname based on stock status. + * + * @deprecated 2.7.0 + * @return string + */ + protected function get_availability_class() { + _deprecated_function( 'WC_Product::get_availability_class', '2.7' ); + if ( ! $this->is_in_stock() ) { + $class = 'out-of-stock'; + } elseif ( $this->managing_stock() && $this->is_on_backorder( 1 ) && $this->backorders_require_notification() ) { + $class = 'available-on-backorder'; + } else { + $class = 'in-stock'; + } + return apply_filters( 'woocommerce_get_availability_class', $class, $this ); + } + /** * Get and return related products. * @deprecated 2.7.0 Use wc_get_related_products instead. @@ -185,176 +425,6 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { return wc_get_product_tag_list( $this->get_id(), $sep, $before, $after ); } - /** - * Returns the availability of the product. - * - * If stock management is enabled at global and product level, a stock message - * will be shown. e.g. In stock, In stock x10, Out of stock. - * - * If stock management is disabled at global or product level, out of stock - * will be shown when needed, but in stock will be hidden from view. - * - * This can all be changed through use of the woocommerce_get_availability filter. - * - * @return string - */ - public function get_availability() { - return apply_filters( 'woocommerce_get_availability', array( - 'availability' => $this->get_availability_text(), - 'class' => $this->get_availability_class(), - ), $this ); - } - - /** - * Get availability text based on stock status. - * - * @return string - */ - protected function get_availability_text() { - if ( ! $this->is_in_stock() ) { - $availability = __( 'Out of stock', 'woocommerce' ); - } elseif ( $this->managing_stock() && $this->is_on_backorder( 1 ) ) { - $availability = $this->backorders_require_notification() ? __( 'Available on backorder', 'woocommerce' ) : __( 'In stock', 'woocommerce' ); - } elseif ( $this->managing_stock() ) { - switch ( get_option( 'woocommerce_stock_format' ) ) { - case 'no_amount' : - $availability = __( 'In stock', 'woocommerce' ); - break; - case 'low_amount' : - if ( $this->get_total_stock() <= get_option( 'woocommerce_notify_low_stock_amount' ) ) { - $availability = sprintf( __( 'Only %s left in stock', 'woocommerce' ), $this->get_total_stock() ); - - if ( $this->backorders_allowed() && $this->backorders_require_notification() ) { - $availability .= ' ' . __( '(also available on backorder)', 'woocommerce' ); - } - } else { - $availability = __( 'In stock', 'woocommerce' ); - } - break; - default : - $availability = sprintf( __( '%s in stock', 'woocommerce' ), $this->get_total_stock() ); - - if ( $this->backorders_allowed() && $this->backorders_require_notification() ) { - $availability .= ' ' . __( '(also available on backorder)', 'woocommerce' ); - } - break; - } - } else { - $availability = ''; - } - return apply_filters( 'woocommerce_get_availability_text', $availability, $this ); - } - - /** - * Get availability classname based on stock status. - * - * @return string - */ - protected function get_availability_class() { - if ( ! $this->is_in_stock() ) { - $class = 'out-of-stock'; - } elseif ( $this->managing_stock() && $this->is_on_backorder( 1 ) && $this->backorders_require_notification() ) { - $class = 'available-on-backorder'; - } else { - $class = 'in-stock'; - } - - return apply_filters( 'woocommerce_get_availability_class', $class, $this ); - } - - /** - * The product's type (simple, variable etc). - * - * @var string - */ - public $product_type = null; - - /** - * Product shipping class. - * - * @var string - */ - protected $shipping_class = ''; - - /** - * ID of the shipping class this product has. - * - * @var int - */ - protected $shipping_class_id = 0; - - /** @public string The product's total stock, including that of its children. */ - public $total_stock; - - - /** - * Magic __isset method for backwards compatibility. Legacy properties which could be accessed directly in the past. - * - * @param string $key Key name. - * @return bool - */ - public function __isset( $key ) { - return metadata_exists( 'post', $this->id, '_' . $key ); - } - - /** - * Magic __get method for backwards compatibility.Maps legacy vars to new getters. - * - * @param string $key Key name. - * @return mixed - */ - public function __get( $key ) { - _doing_it_wrong( $key, __( 'Product properties should not be accessed directly.', 'woocommerce' ), '2.7' ); - - switch ( $key ) { - case 'id' : - $value = $this->get_id(); - break; - case 'product_attributes' : - $value = isset( $this->data['attributes'] ) ? $this->data['attributes'] : ''; - break; - case 'visibility' : - $value = $this->get_catalog_visibility(); - break; - case 'sale_price_dates_from' : - $value = $this->get_date_on_sale_from(); - break; - case 'sale_price_dates_to' : - $value = $this->get_date_on_sale_to(); - break; - case 'post' : - $value = get_post( $this->get_id() ); - break; - case 'product_type' : // @todo What do we do with 3rd party use of product_type now it's hardcoded? - $value = $this->get_type(); - break; - case 'download_type' : - return 'standard'; - break; - case 'product_image_gallery' : - $value = $this->get_gallery_attachment_ids(); - break; - default : - $value = get_post_meta( $this->id, '_' . $key, true ); - - // Get values or default if not set. - if ( in_array( $key, array( 'downloadable', 'virtual', 'backorders', 'manage_stock', 'featured', 'sold_individually' ) ) ) { - $value = $value ? $value : 'no'; - } elseif ( in_array( $key, array( 'product_attributes', 'crosssell_ids', 'upsell_ids' ) ) ) { - $value = $value ? $value : array(); - } elseif ( 'stock' === $key ) { - $value = $value ? $value : 0; - } elseif ( 'stock_status' === $key ) { - $value = $value ? $value : 'instock'; - } elseif ( 'tax_status' === $key ) { - $value = $value ? $value : 'taxable'; - } - break; - } - - return $value; - } - /** * Get the product's post data. * @@ -362,6 +432,7 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { * @return WP_Post */ public function get_post_data() { + _deprecated_function( 'WC_Product::get_post_data', '2.7', 'get_post' ); return get_post( $this->get_id() ); } @@ -421,4 +492,93 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { } return false; } + + /** + * Get variation ID. + * + * @deprecated 2.7.0 + * @return int + */ + public function get_variation_id() { + _deprecated_function( 'WC_Product::get_variation_id', '2.7', 'WC_Product::get_id() will always be the variation ID if this is a variation.' ); + return $this->get_id(); + } + + /** + * Get product variation description. + * + * @deprecated 2.7.0 + * @return string + */ + public function get_variation_description() { + _deprecated_function( 'WC_Product::get_variation_description', '2.7', 'WC_Product::get_description()' ); + return $this->get_description(); + } + + /** + * Check if all variation's attributes are set. + * + * @deprecated 2.7.0 + * @return boolean + */ + public function has_all_attributes_set() { + _deprecated_function( 'WC_Product::has_all_attributes_set', '2.7' ); + $set = true; + + // undefined attributes have null strings as array values + foreach ( $this->get_variation_attributes() as $att ) { + if ( ! $att ) { + $set = false; + break; + } + } + return $set; + } + + /** + * Returns whether or not the variations parent is visible. + * + * @deprecated 2.7.0 + * @return bool + */ + public function parent_is_visible() { + _deprecated_function( 'WC_Product::parent_is_visible', '2.7' ); + return $this->is_visible(); + } + + /** + * Get total stock - This is the stock of parent and children combined. + * + * @deprecated 2.7.0 + * @return int + */ + public function get_total_stock() { + _deprecated_function( 'WC_Product::get_total_stock', '2.7', 'Use get_stock_quantity on each child. Beware of performance issues in doing so.' ); + if ( empty( $this->total_stock ) ) { + if ( sizeof( $this->get_children() ) > 0 ) { + $this->total_stock = max( 0, $this->get_stock_quantity() ); + + foreach ( $this->get_children() as $child_id ) { + if ( 'yes' === get_post_meta( $child_id, '_manage_stock', true ) ) { + $stock = get_post_meta( $child_id, '_stock', true ); + $this->total_stock += max( 0, wc_stock_amount( $stock ) ); + } + } + } else { + $this->total_stock = $this->get_stock_quantity(); + } + } + return wc_stock_amount( $this->total_stock ); + } + + /** + * Get formatted variation data with WC < 2.4 back compat and proper formatting of text-based attribute names. + * + * @deprecated 2.7.0 + * @return string + */ + public function get_formatted_variation_attributes( $flat = false ) { + _deprecated_function( 'WC_Product::get_formatted_variation_attributes', '2.7', 'wc_get_formatted_variation' ); + return wc_get_formatted_variation( $this->get_variation_attributes(), $flat ); + } } diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index 6fbed783640..f443b32dc85 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -1163,7 +1163,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { $shipping_tax_class = get_option( 'woocommerce_shipping_tax_class' ); // Inherit tax class from items - if ( '' === $shipping_tax_class ) { + if ( 'inherit' === $shipping_tax_class ) { $tax_rates = array(); $tax_classes = array_merge( array( '' ), WC_Tax::get_tax_classes() ); $found_tax_classes = $this->get_items_tax_classes(); @@ -1187,7 +1187,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { 'state' => $args['state'], 'postcode' => $args['postcode'], 'city' => $args['city'], - 'tax_class' => 'standard' === $shipping_tax_class ? '' : $shipping_tax_class, + 'tax_class' => $shipping_tax_class, ) ); } diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 50f8d848c42..3510bdbfd75 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -1,4 +1,14 @@ '', - 'slug' => '', - 'date_created' => '', - 'date_modified' => '', - 'status' => false, - 'featured' => false, - 'catalog_visibility' => 'hidden', - 'description' => '', - 'short_description' => '', - 'sku' => '', - 'price' => '', - 'regular_price' => '', - 'sale_price' => '', - 'date_on_sale_from' => '', - 'date_on_sale_to' => '', - 'total_sales' => '0', - 'tax_status' => 'taxable', - 'tax_class' => '', - 'manage_stock' => false, - 'stock_quantity' => null, - 'stock_status' => '', - 'backorders' => 'no', - 'sold_individually' => false, - 'weight' => '', - 'length' => '', - 'width' => '', - 'height' => '', - 'upsell_ids' => array(), - 'cross_sell_ids' => array(), - 'parent_id' => 0, - 'reviews_allowed' => true, - 'purchase_note' => '', - 'attributes' => array(), - 'default_attributes' => array(), - 'menu_order' => 0, - 'virtual' => false, - 'downloadable' => false, - 'category_ids' => array(), - 'tag_ids' => array(), - 'shipping_class_id' => 0, - 'downloads' => array(), - 'thumbnail_id' => '', - 'gallery_attachment_ids' => array(), - 'download_limit' => -1, - 'download_expiry' => -1, + 'name' => '', + 'slug' => '', + 'date_created' => '', + 'date_modified' => '', + 'status' => false, + 'featured' => false, + 'catalog_visibility' => 'hidden', + 'description' => '', + 'short_description' => '', + 'sku' => '', + 'price' => '', + 'regular_price' => '', + 'sale_price' => '', + 'date_on_sale_from' => '', + 'date_on_sale_to' => '', + 'total_sales' => '0', + 'tax_status' => 'taxable', + 'tax_class' => '', + 'manage_stock' => false, + 'stock_quantity' => null, + 'stock_status' => '', + 'backorders' => 'no', + 'sold_individually' => false, + 'weight' => '', + 'length' => '', + 'width' => '', + 'height' => '', + 'upsell_ids' => array(), + 'cross_sell_ids' => array(), + 'parent_id' => 0, + 'reviews_allowed' => true, + 'purchase_note' => '', + 'attributes' => array(), + 'default_attributes' => array(), + 'menu_order' => 0, + 'virtual' => false, + 'downloadable' => false, + 'category_ids' => array(), + 'tag_ids' => array(), + 'shipping_class_id' => 0, + 'downloads' => array(), + 'image_id' => '', + 'gallery_image_ids' => array(), + 'download_limit' => -1, + 'download_expiry' => -1, ); /** @@ -145,20 +151,13 @@ class WC_Product extends WC_Abstract_Legacy_Product { */ /** - * Get internal type. + * Get internal type. Should return string and *should be overridden* by child classes. * @since 2.7.0 * @return string */ public function get_type() { - return 'simple'; - } - - /** - * Product permalink. - * @return string - */ - public function get_permalink() { - return get_permalink( $this->get_id() ); + // product_type is @deprecated but here for BW compat with child classes. + return $this->product_type; } /** @@ -327,11 +326,15 @@ class WC_Product extends WC_Abstract_Legacy_Product { /** * Returns the tax class. - * + * @param bool $raw Get unfiltered value. * @return string */ - public function get_tax_class() { - return apply_filters( 'woocommerce_product_tax_class', $this->data['tax_class'], $this ); + public function get_tax_class( $raw = false ) { + if ( $raw ) { + return $this->data['tax_class']; + } else { + return apply_filters( 'woocommerce_product_tax_class', $this->data['tax_class'], $this ); + } } /** @@ -350,7 +353,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return int|null */ public function get_stock_quantity() { - return apply_filters( 'woocommerce_get_stock_quantity', $this->get_manage_stock() ? wc_stock_amount( $this->data['stock_quantity'] ) : null, $this ); + return apply_filters( 'woocommerce_get_stock_quantity', $this->get_manage_stock() ? $this->data['stock_quantity'] : null, $this ); } /** @@ -389,11 +392,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return string */ public function get_weight() { - // Legacy filter. - $weight = apply_filters( 'woocommerce_product_weight', $this->data['weight'], $this ); // @todo standardize these filter names and move BW compat to deprecated class file. - - // New filter. - return apply_filters( 'woocommerce_product_get_weight', $weight, $this ); + return apply_filters( 'woocommerce_product_get_weight', $this->data['weight'], $this ); } /** @@ -402,11 +401,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return string */ public function get_length() { - // Legacy filter. - $length = apply_filters( 'woocommerce_product_length', $this->data['length'], $this ); - - // New filter since 2.7. - return apply_filters( 'woocommerce_product_get_length', $length, $this ); + return apply_filters( 'woocommerce_product_get_length', $this->data['length'], $this ); } /** @@ -415,11 +410,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return string */ public function get_width() { - // Legacy filter. - $width = apply_filters( 'woocommerce_product_width', $this->data['width'], $this ); - - // New filter since 2.7. - return apply_filters( 'woocommerce_product_get_width', $width, $this ); + return apply_filters( 'woocommerce_product_get_width', $this->data['width'], $this ); } /** @@ -428,11 +419,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return string */ public function get_height() { - // Legacy filter. - $height = apply_filters( 'woocommerce_product_height', $this->data['height'], $this ); - - // New filter since 2.7. - return apply_filters( 'woocommerce_product_get_height', $height, $this ); + return apply_filters( 'woocommerce_product_get_height', $this->data['height'], $this ); } /** @@ -549,8 +536,8 @@ class WC_Product extends WC_Abstract_Legacy_Product { * * @return array */ - public function get_gallery_attachment_ids() { - return apply_filters( 'woocommerce_product_gallery_attachment_ids', array_filter( array_filter( $this->data['gallery_attachment_ids'] ), 'wp_attachment_is_image' ), $this ); + public function get_gallery_image_ids() { + return apply_filters( 'woocommerce_product_gallery_attachment_ids', array_filter( array_filter( $this->data['gallery_image_ids'] ), 'wp_attachment_is_image' ), $this ); } /** @@ -604,13 +591,13 @@ class WC_Product extends WC_Abstract_Legacy_Product { } /** - * Get thumbnail ID. + * Get main image ID. @todo ensure read handles parent like get_image_id used to? * * @since 2.7.0 * @return string */ - public function get_thumbnail_id() { - return $this->data['thumbnail_id']; + public function get_image_id() { + return $this->data['image_id']; } /* @@ -845,7 +832,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param float|null $quantity Stock quantity. */ public function set_stock_quantity( $quantity ) { - $this->data['stock_quantity'] = $quantity; + $this->data['stock_quantity'] = '' !== $quantity ? wc_stock_amount( $quantity ) : null; } /** @@ -856,17 +843,12 @@ class WC_Product extends WC_Abstract_Legacy_Product { public function set_stock_status( $status ) { $status = 'outofstock' === $status ? 'outofstock' : 'instock'; - // Sanity check. if ( $this->managing_stock() ) { if ( ! $this->backorders_allowed() && $this->get_stock_quantity() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) { $status = 'outofstock'; } } - if ( update_post_meta( $this->get_id(), '_stock_status', $status ) ) { - do_action( 'woocommerce_product_set_stock_status', $this->get_id(), $status ); - } - $this->data['stock_status'] = $status; } @@ -1116,7 +1098,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { } } - // Validate the file exists + // Validate the file exists. if ( 'relative' === $file_is ) { $_file_url = $file_url; if ( '..' === substr( $file_url, 0, 2 ) || '/' !== substr( $file_url, 0, 1 ) ) { @@ -1168,18 +1150,18 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @since 2.7.0 * @param array $gallery_ids */ - public function set_gallery_attachment_ids( $gallery_ids ) { - $this->data['gallery_attachment_ids'] = $gallery_ids; + public function set_gallery_image_ids( $gallery_ids ) { + $this->data['gallery_image_ids'] = $gallery_ids; } /** - * Set thumbnail ID. + * Set main image ID. * * @since 2.7.0 - * @param int $thumbnail_id + * @param int $image_id */ - public function set_thumbnail_id( $thumbnail_id = '' ) { - $this->data['thumbnail_id'] = $thumbnail_id; + public function set_image_id( $image_id = '' ) { + $this->data['image_id'] = $image_id; } /* @@ -1244,51 +1226,50 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->set_id( $id ); $this->set_props( array( - 'name' => get_the_title( $post_object ), - 'slug' => $post_object->post_name, - 'permalink' => get_permalink( $post_object ), - 'date_created' => $post_object->post_date, - 'date_modified' => $post_object->post_modified, - 'type' => '', - 'status' => $post_object->post_status, - 'featured' => get_post_meta( $id, '_featured', true ), - 'catalog_visibility' => get_post_meta( $id, '_visibility', true ), - 'description' => $post_object->post_content, - 'short_description' => $post_object->post_excerpt, - 'sku' => get_post_meta( $id, '_sku', true ), - 'regular_price' => get_post_meta( $id, '_regular_price', true ), - 'sale_price' => get_post_meta( $id, '_sale_price', true ), - 'date_on_sale_from' => get_post_meta( $id, '_sale_price_dates_from', true ), - 'date_on_sale_to' => get_post_meta( $id, '_sale_price_dates_to', true ), - 'total_sales' => get_post_meta( $id, 'total_sales', true ), - 'tax_status' => get_post_meta( $id, '_tax_status', true ), - 'tax_class' => get_post_meta( $id, '_tax_class', true ), - 'manage_stock' => get_post_meta( $id, '_manage_stock', true ), - 'stock_quantity' => get_post_meta( $id, '_stock', true ), - 'stock_status' => get_post_meta( $id, '_stock_status', true ), - 'backorders' => get_post_meta( $id, '_backorders', true ), - 'sold_individually' => get_post_meta( $id, '_sold_individually', true ), - 'weight' => get_post_meta( $id, '_weight', true ), - 'length' => get_post_meta( $id, '_length', true ), - 'width' => get_post_meta( $id, '_width', true ), - 'height' => get_post_meta( $id, '_height', true ), - 'upsell_ids' => get_post_meta( $id, '_upsell_ids', true ), - 'cross_sell_ids' => get_post_meta( $id, '_crosssell_ids', true ), - 'parent_id' => $post_object->post_parent, - 'reviews_allowed' => 'open' === $post_object->comment_status, - 'purchase_note' => get_post_meta( $id, '_purchase_note', true ), - 'default_attributes' => get_post_meta( $id, '_default_attributes', true ), - 'menu_order' => $post_object->menu_order, - 'category_ids' => $this->get_term_ids( 'product_cat' ), - 'tag_ids' => $this->get_term_ids( 'product_tag' ), - 'shipping_class_id' => current( $this->get_term_ids( 'product_shipping_class' ) ), - 'virtual' => get_post_meta( $id, '_virtual', true ), - 'downloadable' => get_post_meta( $id, '_downloadable', true ), - 'downloads' => array_filter( (array) get_post_meta( $id, '_downloadable_files', true ) ), - 'gallery_attachment_ids' => array_filter( explode( ',', get_post_meta( $id, '_product_image_gallery', true ) ) ), - 'download_limit' => get_post_meta( $id, '_download_limit', true ), - 'download_expiry' => get_post_meta( $id, '_download_expiry', true ), - 'thumbnail_id' => get_post_thumbnail_id( $id ), + 'name' => get_the_title( $post_object ), + 'slug' => $post_object->post_name, + 'date_created' => $post_object->post_date, + 'date_modified' => $post_object->post_modified, + 'type' => '', + 'status' => $post_object->post_status, + 'featured' => get_post_meta( $id, '_featured', true ), + 'catalog_visibility' => get_post_meta( $id, '_visibility', true ), + 'description' => $post_object->post_content, + 'short_description' => $post_object->post_excerpt, + 'sku' => get_post_meta( $id, '_sku', true ), + 'regular_price' => get_post_meta( $id, '_regular_price', true ), + 'sale_price' => get_post_meta( $id, '_sale_price', true ), + 'date_on_sale_from' => get_post_meta( $id, '_sale_price_dates_from', true ), + 'date_on_sale_to' => get_post_meta( $id, '_sale_price_dates_to', true ), + 'total_sales' => get_post_meta( $id, 'total_sales', true ), + 'tax_status' => get_post_meta( $id, '_tax_status', true ), + 'tax_class' => get_post_meta( $id, '_tax_class', true ), + 'manage_stock' => get_post_meta( $id, '_manage_stock', true ), + 'stock_quantity' => get_post_meta( $id, '_stock', true ), + 'stock_status' => get_post_meta( $id, '_stock_status', true ), + 'backorders' => get_post_meta( $id, '_backorders', true ), + 'sold_individually' => get_post_meta( $id, '_sold_individually', true ), + 'weight' => get_post_meta( $id, '_weight', true ), + 'length' => get_post_meta( $id, '_length', true ), + 'width' => get_post_meta( $id, '_width', true ), + 'height' => get_post_meta( $id, '_height', true ), + 'upsell_ids' => get_post_meta( $id, '_upsell_ids', true ), + 'cross_sell_ids' => get_post_meta( $id, '_crosssell_ids', true ), + 'parent_id' => $post_object->post_parent, + 'reviews_allowed' => 'open' === $post_object->comment_status, + 'purchase_note' => get_post_meta( $id, '_purchase_note', true ), + 'default_attributes' => get_post_meta( $id, '_default_attributes', true ), + 'menu_order' => $post_object->menu_order, + 'category_ids' => $this->get_term_ids( 'product_cat' ), + 'tag_ids' => $this->get_term_ids( 'product_tag' ), + 'shipping_class_id' => current( $this->get_term_ids( 'product_shipping_class' ) ), + 'virtual' => get_post_meta( $id, '_virtual', true ), + 'downloadable' => get_post_meta( $id, '_downloadable', true ), + 'downloads' => array_filter( (array) get_post_meta( $id, '_downloadable_files', true ) ), + 'gallery_image_ids' => array_filter( explode( ',', get_post_meta( $id, '_product_image_gallery', true ) ) ), + 'download_limit' => get_post_meta( $id, '_download_limit', true ), + 'download_expiry' => get_post_meta( $id, '_download_expiry', true ), + 'image_id' => get_post_thumbnail_id( $id ), ) ); if ( $this->is_on_sale() ) { $this->set_price( $this->get_sale_price() ); @@ -1340,7 +1321,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->set_date_created( current_time( 'timestamp' ) ); $id = wp_insert_post( apply_filters( 'woocommerce_new_product_data', array( - 'post_type' => 'product', + 'post_type' => $this->post_type, 'post_status' => $this->get_status() ? $this->get_status() : 'publish', 'post_author' => get_current_user_id(), 'post_title' => $this->get_name() ? $this->get_name() : __( 'Product', 'woocommerce' ), @@ -1360,7 +1341,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->update_terms(); $this->update_attributes(); $this->save_meta_data(); - do_action( 'woocommerce_new_product', $id ); + do_action( 'woocommerce_new_' . $this->post_type, $id ); } } @@ -1385,7 +1366,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->update_terms(); $this->update_attributes(); $this->save_meta_data(); - do_action( 'woocommerce_update_product', $this->get_id() ); + do_action( 'woocommerce_update_' . $this->post_type, $this->get_id() ); } /** @@ -1406,6 +1387,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { // Version is set to current WC version to track data changes. update_post_meta( $this->get_id(), '_product_version', WC_VERSION ); wc_delete_product_transients( $this->get_id() ); + return $this->get_id(); } /** @@ -1415,7 +1397,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { */ public function delete() { wp_delete_post( $this->get_id() ); - do_action( 'woocommerce_delete_product', $this->get_id() ); + do_action( 'woocommerce_delete_' . $this->post_type, $this->get_id() ); $this->set_id( 0 ); } @@ -1425,54 +1407,93 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @since 2.7.0 */ protected function update_post_meta() { - $id = $this->get_id(); - update_post_meta( $id, '_visibility', $this->get_catalog_visibility() ); - update_post_meta( $id, '_sku', $this->get_sku() ); - update_post_meta( $id, '_regular_price', $this->get_regular_price() ); - update_post_meta( $id, '_sale_price', $this->get_sale_price() ); - update_post_meta( $id, '_sale_price_dates_from', $this->get_date_on_sale_from() ); - update_post_meta( $id, '_sale_price_dates_to', $this->get_date_on_sale_to() ); - update_post_meta( $id, 'total_sales', $this->get_total_sales() ); - update_post_meta( $id, '_tax_status', $this->get_tax_status() ); - update_post_meta( $id, '_tax_class', $this->get_tax_class() ); - update_post_meta( $id, '_manage_stock', $this->get_manage_stock() ); - update_post_meta( $id, '_stock', $this->get_stock_quantity() ); - update_post_meta( $id, '_stock_status', $this->get_stock_status() ); - update_post_meta( $id, '_backorders', $this->get_backorders() ); - update_post_meta( $id, '_sold_individually', $this->get_sold_individually() ); - update_post_meta( $id, '_weight', $this->get_weight() ); - update_post_meta( $id, '_length', $this->get_length() ); - update_post_meta( $id, '_width', $this->get_width() ); - update_post_meta( $id, '_height', $this->get_height() ); - update_post_meta( $id, '_upsell_ids', $this->get_upsell_ids() ); - update_post_meta( $id, '_crosssell_ids', $this->get_cross_sell_ids() ); - update_post_meta( $id, '_purchase_note', $this->get_purchase_note() ); - update_post_meta( $id, '_default_attributes', $this->get_default_attributes() ); - update_post_meta( $id, '_virtual', $this->get_virtual() ? 'yes' : 'no' ); - update_post_meta( $id, '_downloadable', $this->get_downloadable() ? 'yes' : 'no' ); - update_post_meta( $id, '_product_image_gallery', implode( ',', $this->get_gallery_attachment_ids() ) ); - update_post_meta( $id, '_download_limit', $this->get_download_limit() ); - update_post_meta( $id, '_download_expiry', $this->get_download_expiry() ); + $updated_props = array(); + $meta_key_to_props = array( + '_visibility' => 'catalog_visibility', + '_sku' => 'sku', + '_regular_price' => 'regular_price', + '_sale_price' => 'sale_price', + '_sale_price_dates_from' => 'date_on_sale_from', + '_sale_price_dates_to' => 'date_on_sale_to', + 'total_sales' => 'total_sales', + '_tax_status' => 'tax_status', + '_tax_class' => 'tax_class', + '_manage_stock' => 'manage_stock', + '_backorders' => 'backorders', + '_sold_individually' => 'sold_individually', + '_weight' => 'weight', + '_length' => 'length', + '_width' => 'width', + '_height' => 'height', + '_upsell_ids' => 'upsell_ids', + '_crosssell_ids' => 'cross_sell_ids', + '_purchase_note' => 'purchase_note', + '_default_attributes' => 'default_attributes', + '_virtual' => 'virtual', + '_downloadable' => 'downloadable', + '_product_image_gallery' => 'gallery_image_ids', + '_download_limit' => 'download_limit', + '_download_expiry' => 'download_expiry', + '_featured' => 'featured', + '_thumbnail_id' => 'image_id', + '_downloadable_files' => 'downloads', + '_stock' => 'stock_quantity', + '_stock_status' => 'stock_status', + ); - if ( update_post_meta( $id, '_featured', $this->get_featured() ) ) { - delete_transient( 'wc_featured_products' ); - } - - if ( ! empty( $this->get_thumbnail_id() ) ) { - set_post_thumbnail( $id, $this->get_thumbnail_id() ); - } else { - delete_post_meta( $id, '_thumbnail_id' ); + foreach ( $meta_key_to_props as $meta_key => $prop ) { + $value = $this->data[ $prop ]; + // @todo this is where state should be checked? + switch ( $prop ) { + case 'virtual' : + case 'downloadable' : + $updated = update_post_meta( $this->get_id(), $meta_key, wc_bool_to_string( $value ) ); + break; + case 'gallery_image_ids' : + $updated = update_post_meta( $this->get_id(), $meta_key, implode( ',', $value ) ); + break; + case 'image_id' : + if ( ! empty( $value ) ) { + set_post_thumbnail( $this->get_id(), $value ); + } else { + delete_post_meta( $this->get_id(), '_thumbnail_id' ); + } + $updated = true; + break; + default : + $updated = update_post_meta( $this->get_id(), $meta_key, $value ); + break; + } + if ( $updated ) { + $updated_props[] = $prop; + } } if ( $this->is_on_sale() ) { - update_post_meta( $id, '_price', $this->get_sale_price() ); + update_post_meta( $this->get_id(), '_price', $this->get_sale_price() ); } else { - update_post_meta( $id, '_price', $this->get_regular_price() ); + update_post_meta( $this->get_id(), '_price', $this->get_regular_price() ); } - if ( update_post_meta( $id, '_downloadable_files', $this->get_downloads() ) ) { - // grant permission to any newly added files on any existing orders for this product prior to saving @todo hook for variations? - do_action( 'woocommerce_process_product_file_download_paths', $id, 0, $this->get_downloads() ); + if ( in_array( 'featured', $updated_props ) ) { + delete_transient( 'wc_featured_products' ); + } + + if ( in_array( 'downloads', $updated_props ) ) { + // grant permission to any newly added files on any existing orders for this product prior to saving. + if ( $this->is_type( 'variation' ) ) { + do_action( 'woocommerce_process_product_file_download_paths', $this->get_parent_id(), $this->get_id(), $this->get_downloads() ); + } else { + do_action( 'woocommerce_process_product_file_download_paths', $this->get_id(), 0, $this->get_downloads() ); + } + } + + if ( in_array( 'stock_quantity', $updated_props ) ) { + do_action( $this->is_type( 'variation' ) ? 'woocommerce_variation_set_stock' : 'woocommerce_product_set_stock' , $this ); + } + + if ( in_array( 'stock_status', $updated_props ) ) { + do_action( $this->is_type( 'variation' ) ? 'woocommerce_variation_set_stock_status' : 'woocommerce_product_set_stock_status' , $this->get_id(), $this->get_stock_status() ); } } @@ -1629,7 +1650,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return bool */ public function is_purchasable() { - return apply_filters( 'woocommerce_is_purchasable', $this->exists() && ( 'publish' === $this->get_status() || current_user_can( 'edit_post', $this->get_id() ) ) && '' !== $this->get_price(), $this ); + return apply_filters( 'woocommerce_is_purchasable', $this->exists() && $this->is_in_stock() && ( 'publish' === $this->get_status() || current_user_can( 'edit_post', $this->get_id() ) ) && '' !== $this->get_price(), $this ); } /** @@ -1742,7 +1763,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return bool */ public function is_on_backorder( $qty_in_cart = 0 ) { - return $this->managing_stock() && $this->backorders_allowed() && ( $this->get_total_stock() - $qty_in_cart ) < 0 ? true : false; + return $this->managing_stock() && $this->backorders_allowed() && ( $this->get_stock_quantity() - $qty_in_cart ) < 0 ? true : false; } /** @@ -1793,6 +1814,14 @@ class WC_Product extends WC_Abstract_Legacy_Product { |-------------------------------------------------------------------------- */ + /** + * Product permalink. + * @return string + */ + public function get_permalink() { + return get_permalink( $this->get_id() ); + } + /** * Returns the children IDs if applicable. Overridden by child classes. * @@ -1841,7 +1870,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return string */ public function add_to_cart_url() { - return apply_filters( 'woocommerce_product_add_to_cart_url', get_permalink( $this->get_id() ), $this ); + return apply_filters( 'woocommerce_product_add_to_cart_url', $this->get_permalink(), $this ); } /** @@ -1862,22 +1891,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_product_add_to_cart_text', __( 'Read more', 'woocommerce' ), $this ); } - /** - * Gets the main product image ID. - * - * @return int - */ - public function get_image_id() { - if ( has_post_thumbnail( $this->get_id() ) ) { - $image_id = get_post_thumbnail_id( $this->get_id() ); - } elseif ( ( $parent_id = wp_get_post_parent_id( $this->get_id() ) ) && has_post_thumbnail( $parent_id ) ) { - $image_id = get_post_thumbnail_id( $parent_id ); - } else { - $image_id = 0; - } - return $image_id; - } - /** * Returns the main product image. * @@ -1934,116 +1947,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { return $attribute_object->is_taxonomy() ? implode( ', ', wc_get_product_terms( $this->get_id(), $attribute_object->get_name(), array( 'fields' => 'names' ) ) ) : wc_implode_text_attributes( $attribute_object->get_options() ); } - /* - |-------------------------------------------------------------------------- - | @todo stock functions - |-------------------------------------------------------------------------- - */ - /** - * Get total stock - This is the stock of parent and children combined. - * - * @return int - */ - public function get_total_stock() { - if ( empty( $this->total_stock ) ) { - if ( sizeof( $this->get_children() ) > 0 ) { - $this->total_stock = max( 0, $this->get_stock_quantity() ); - - foreach ( $this->get_children() as $child_id ) { - if ( 'yes' === get_post_meta( $child_id, '_manage_stock', true ) ) { - $stock = get_post_meta( $child_id, '_stock', true ); - $this->total_stock += max( 0, wc_stock_amount( $stock ) ); - } - } - } else { - $this->total_stock = $this->get_stock_quantity(); - } - } - return wc_stock_amount( $this->total_stock ); - } - - /** - * Check if the stock status needs changing. - */ - public function check_stock_status() { - if ( ! $this->backorders_allowed() && $this->get_total_stock() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) { - if ( 'outofstock' !== $this->stock_status ) { - $this->set_stock_status( 'outofstock' ); - } - } elseif ( $this->backorders_allowed() || $this->get_total_stock() > get_option( 'woocommerce_notify_no_stock_amount' ) ) { - if ( 'instock' !== $this->stock_status ) { - $this->set_stock_status( 'instock' ); - } - } - } - - /** - * Set stock level of the product. @todo '' stock if not managing it. - * - * Uses queries rather than update_post_meta so we can do this in one query (to avoid stock issues). - * We cannot rely on the original loaded value in case another order was made since then. - * - * @param int $amount (default: null) - * @param string $mode can be set, add, or subtract - * @return int new stock level - */ - public function set_stock( $amount = null, $mode = 'set' ) { - global $wpdb; - - if ( ! is_null( $amount ) && $this->managing_stock() ) { - - // Ensure key exists - add_post_meta( $this->get_id(), '_stock', 0, true ); - - // Update stock in DB directly - switch ( $mode ) { - case 'add' : - $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value + %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->get_id() ) ); - break; - case 'subtract' : - $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value - %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->get_id() ) ); - break; - default : - $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->get_id() ) ); - break; - } - - // Clear caches - wp_cache_delete( $this->get_id(), 'post_meta' ); - delete_transient( 'wc_low_stock_count' ); - delete_transient( 'wc_outofstock_count' ); - unset( $this->stock ); - - // Stock status - $this->check_stock_status(); - - // Trigger action - do_action( 'woocommerce_product_set_stock', $this ); - } - - return $this->get_stock_quantity(); - } - - /** - * Reduce stock level of the product. - * - * @param int $amount Amount to reduce by. Default: 1 - * @return int new stock level - */ - public function reduce_stock( $amount = 1 ) { - return $this->set_stock( $amount, 'subtract' ); - } - - /** - * Increase stock level of the product. - * - * @param int $amount Amount to increase by. Default 1. - * @return int new stock level - */ - public function increase_stock( $amount = 1 ) { - return $this->set_stock( $amount, 'add' ); - } /* |-------------------------------------------------------------------------- diff --git a/includes/admin/class-wc-admin-post-types.php b/includes/admin/class-wc-admin-post-types.php index a81643513ba..fd3f3031a3d 100644 --- a/includes/admin/class-wc-admin-post-types.php +++ b/includes/admin/class-wc-admin-post-types.php @@ -291,7 +291,7 @@ class WC_Admin_Post_Types { public function render_product_columns( $column ) { global $post, $the_product; - if ( empty( $the_product ) || $the_product->id != $post->ID ) { + if ( empty( $the_product ) || $the_product->get_id() != $post->ID ) { $the_product = wc_get_product( $post ); } @@ -406,9 +406,8 @@ class WC_Admin_Post_Types { $stock_html = '' . __( 'Out of stock', 'woocommerce' ) . ''; } - // If the product has children, a single stock level would be misleading as some could be -ve and some +ve, some managed/some unmanaged etc so hide stock level in this case. - if ( $the_product->managing_stock() && ! sizeof( $the_product->get_children() ) ) { - $stock_html .= ' (' . $the_product->get_total_stock() . ')'; + if ( $the_product->managing_stock() ) { + $stock_html .= ' (' . $the_product->get_stock_quantity() . ')'; } echo apply_filters( 'woocommerce_admin_stock_html', $stock_html, $the_product ); @@ -653,7 +652,7 @@ class WC_Admin_Post_Types { get_quantity() ); ?> - get_sku() ) ? $product->get_sku() . ' - ' : ''; ?>get_name(), $item, false ); ?> + get_sku() ) ? $product->get_sku() . ' - ' : ''; ?>get_name(), $item, false ); ?> get_name(), $item, false ); ?> diff --git a/includes/admin/meta-boxes/class-wc-meta-box-order-downloads.php b/includes/admin/meta-boxes/class-wc-meta-box-order-downloads.php index 10b17296dcc..d80ac0238d0 100644 --- a/includes/admin/meta-boxes/class-wc-meta-box-order-downloads.php +++ b/includes/admin/meta-boxes/class-wc-meta-box-order-downloads.php @@ -40,7 +40,7 @@ class WC_Meta_Box_Order_Downloads { if ( $download_permissions && sizeof( $download_permissions ) > 0 ) foreach ( $download_permissions as $download ) { - if ( ! $product || $product->id != $download->product_id ) { + if ( ! $product || $product->get_id() != $download->product_id ) { $product = wc_get_product( absint( $download->product_id ) ); $file_counter = 1; } diff --git a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php index 7f01171454e..b619a9325e5 100644 --- a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php +++ b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php @@ -142,19 +142,17 @@ class WC_Meta_Box_Product_Data { * Prepare downloads for save. * @return array */ - private static function prepare_downloads() { + private static function prepare_downloads( $file_names, $file_urls ) { $downloads = 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'] ) ? wp_unslash( array_map( 'trim', $_POST['_wc_file_urls'] ) ) : array(); + if ( ! empty( $file_urls ) ) { $file_url_size = sizeof( $file_urls ); for ( $i = 0; $i < $file_url_size; $i ++ ) { if ( ! empty( $file_urls[ $i ] ) ) { $downloads[] = array( 'name' => wc_clean( $file_names[ $i ] ), - 'file' => $file_urls[ $i ], + 'file' => wp_unslash( trim( $file_urls[ $i ] ) ), ); } } @@ -219,6 +217,36 @@ class WC_Meta_Box_Product_Data { return $attributes; } + /** + * Prepare attributes for a specific variation or defaults. + * @param array $all_attributes + * @param string $key_prefix + * @param int $index + * @return array + */ + private static function prepare_set_attributes( $all_attributes, $key_prefix = 'attribute_', $index = null ) { + $attributes = array(); + + if ( $all_attributes ) { + foreach ( $all_attributes as $attribute ) { + if ( $attribute->get_variation() ) { + $attribute_key = sanitize_title( $attribute->get_name() ); + + if ( ! is_null( $index ) ) { + $value = isset( $_POST[ $key_prefix . $attribute_key ][ $index ] ) ? stripslashes( $_POST[ $key_prefix . $attribute_key ][ $index ] ) : ''; + } else { + $value = isset( $_POST[ $key_prefix . $attribute_key ] ) ? stripslashes( $_POST[ $key_prefix . $attribute_key ] ) : ''; + } + + $value = $attribute->is_taxonomy() ? sanitize_title( $value ) : wc_clean( $value ); // Don't use wc_clean as it destroys sanitized characters in terms. + $attributes[ $attribute_key ] = $value; + } + } + } + + return $attributes; + } + /** * Save meta box data. */ @@ -231,38 +259,40 @@ class WC_Meta_Box_Product_Data { $classname = 'WC_Product_Simple'; } - $product = new $classname( $post_id ); - $errors = $product->set_props( array( - 'sku' => isset( $_POST['_sku'] ) ? wc_clean( $_POST['_sku'] ) : null, - 'purchase_note' => wp_kses_post( stripslashes( $_POST['_purchase_note'] ) ), - 'downloadable' => isset( $_POST['_downloadable'] ), - 'virtual' => isset( $_POST['_virtual'] ), - 'tax_status' => wc_clean( $_POST['_tax_status'] ), - 'tax_class' => wc_clean( $_POST['_tax_class'] ), - 'weight' => wc_clean( $_POST['_weight'] ), - 'length' => wc_clean( $_POST['_length'] ), - 'width' => wc_clean( $_POST['_width'] ), - 'height' => wc_clean( $_POST['_height'] ), - 'shipping_class_id' => absint( $_POST['product_shipping_class'] ), - 'sold_individually' => ! empty( $_POST['_sold_individually'] ), - 'upsell_ids' => array_map( 'intval', explode( ',', $_POST['upsell_ids'] ) ), - 'crosssell_ids' => array_map( 'intval', explode( ',', $_POST['crosssell_ids'] ) ), - 'regular_price' => wc_clean( $_POST['_regular_price'] ), - 'sale_price' => wc_clean( $_POST['_sale_price'] ), - 'date_on_sale_from' => wc_clean( $_POST['_sale_price_dates_from'] ), - 'date_on_sale_to' => wc_clean( $_POST['_sale_price_dates_to'] ), - 'manage_stock' => ! empty( $_POST['_manage_stock'] ), - 'backorders' => wc_clean( $_POST['_backorders'] ), - 'stock_status' => wc_clean( $_POST['_stock_status'] ), - 'stock_quantity' => wc_stock_amount( $_POST['_stock'] ), - 'attributes' => self::prepare_attributes(), - 'download_limit' => '' === $_POST['_download_limit'] ? '' : absint( $_POST['_download_limit'] ), - 'download_expiry' => '' === $_POST['_download_expiry'] ? '' : absint( $_POST['_download_expiry'] ), - 'downloads' => self::prepare_downloads(), - 'product_url' => esc_url_raw( $_POST['_product_url'] ), - 'button_text' => wc_clean( $_POST['_button_text'] ), - 'children' => 'grouped' === $product_type ? self::prepare_children() : null, - 'reviews_allowed' => ! empty( $_POST['_reviews_allowed'] ), + $product = new $classname( $post_id ); + $attributes = self::prepare_attributes(); + $errors = $product->set_props( array( + 'sku' => isset( $_POST['_sku'] ) ? wc_clean( $_POST['_sku'] ) : null, + 'purchase_note' => wp_kses_post( stripslashes( $_POST['_purchase_note'] ) ), + 'downloadable' => isset( $_POST['_downloadable'] ), + 'virtual' => isset( $_POST['_virtual'] ), + 'tax_status' => wc_clean( $_POST['_tax_status'] ), + 'tax_class' => wc_clean( $_POST['_tax_class'] ), + 'weight' => wc_clean( $_POST['_weight'] ), + 'length' => wc_clean( $_POST['_length'] ), + 'width' => wc_clean( $_POST['_width'] ), + 'height' => wc_clean( $_POST['_height'] ), + 'shipping_class_id' => absint( $_POST['product_shipping_class'] ), + 'sold_individually' => ! empty( $_POST['_sold_individually'] ), + 'upsell_ids' => array_map( 'intval', explode( ',', $_POST['upsell_ids'] ) ), + 'crosssell_ids' => array_map( 'intval', explode( ',', $_POST['crosssell_ids'] ) ), + 'regular_price' => wc_clean( $_POST['_regular_price'] ), + 'sale_price' => wc_clean( $_POST['_sale_price'] ), + 'date_on_sale_from' => wc_clean( $_POST['_sale_price_dates_from'] ), + 'date_on_sale_to' => wc_clean( $_POST['_sale_price_dates_to'] ), + 'manage_stock' => ! empty( $_POST['_manage_stock'] ), + 'backorders' => wc_clean( $_POST['_backorders'] ), + 'stock_status' => wc_clean( $_POST['_stock_status'] ), + 'stock_quantity' => wc_stock_amount( $_POST['_stock'] ), + 'download_limit' => '' === $_POST['_download_limit'] ? '' : absint( $_POST['_download_limit'] ), + 'download_expiry' => '' === $_POST['_download_expiry'] ? '' : absint( $_POST['_download_expiry'] ), + 'downloads' => self::prepare_downloads( isset( $_POST['_wc_file_names'] ) ? $_POST['_wc_file_names'] : array(), isset( $_POST['_wc_file_urls'] ) ? $_POST['_wc_file_urls'] : array() ), + 'product_url' => esc_url_raw( $_POST['_product_url'] ), + 'button_text' => wc_clean( $_POST['_button_text'] ), + 'children' => 'grouped' === $product_type ? self::prepare_children() : null, + 'reviews_allowed' => ! empty( $_POST['_reviews_allowed'] ), + 'attributes' => $attributes, + 'default_attributes' => self::prepare_set_attributes( $attributes, 'default_attribute_' ), ) ); if ( is_wp_error( $errors ) ) { @@ -281,287 +311,54 @@ class WC_Meta_Box_Product_Data { * @param WP_Post $post */ public static function save_variations( $post_id, $post ) { - global $wpdb; - - $attributes = (array) maybe_unserialize( get_post_meta( $post_id, '_product_attributes', true ) ); - - if ( isset( $_POST['variable_sku'] ) ) { - $variable_post_id = $_POST['variable_post_id']; - $variable_sku = $_POST['variable_sku']; - $variable_regular_price = $_POST['variable_regular_price']; - $variable_sale_price = $_POST['variable_sale_price']; - $upload_image_id = $_POST['upload_image_id']; - $variable_download_limit = $_POST['variable_download_limit']; - $variable_download_expiry = $_POST['variable_download_expiry']; - $variable_shipping_class = $_POST['variable_shipping_class']; - $variable_tax_class = isset( $_POST['variable_tax_class'] ) ? $_POST['variable_tax_class'] : array(); - $variable_menu_order = $_POST['variation_menu_order']; - $variable_sale_price_dates_from = $_POST['variable_sale_price_dates_from']; - $variable_sale_price_dates_to = $_POST['variable_sale_price_dates_to']; - - $variable_weight = isset( $_POST['variable_weight'] ) ? $_POST['variable_weight'] : array(); - $variable_length = isset( $_POST['variable_length'] ) ? $_POST['variable_length'] : array(); - $variable_width = isset( $_POST['variable_width'] ) ? $_POST['variable_width'] : array(); - $variable_height = isset( $_POST['variable_height'] ) ? $_POST['variable_height'] : array(); - $variable_enabled = isset( $_POST['variable_enabled'] ) ? $_POST['variable_enabled'] : array(); - $variable_is_virtual = isset( $_POST['variable_is_virtual'] ) ? $_POST['variable_is_virtual'] : array(); - $variable_is_downloadable = isset( $_POST['variable_is_downloadable'] ) ? $_POST['variable_is_downloadable'] : array(); - - $variable_manage_stock = isset( $_POST['variable_manage_stock'] ) ? $_POST['variable_manage_stock'] : array(); - $variable_stock = isset( $_POST['variable_stock'] ) ? $_POST['variable_stock'] : array(); - $variable_backorders = isset( $_POST['variable_backorders'] ) ? $_POST['variable_backorders'] : array(); - $variable_stock_status = isset( $_POST['variable_stock_status'] ) ? $_POST['variable_stock_status'] : array(); - - $variable_description = isset( $_POST['variable_description'] ) ? $_POST['variable_description'] : array(); - + if ( isset( $_POST['variable_post_id'] ) ) { + $parent = wc_get_product( $post_id ); $max_loop = max( array_keys( $_POST['variable_post_id'] ) ); for ( $i = 0; $i <= $max_loop; $i ++ ) { - if ( ! isset( $variable_post_id[ $i ] ) ) { + if ( ! isset( $_POST['variable_post_id'][ $i ] ) ) { continue; } - $variation_id = absint( $variable_post_id[ $i ] ); - - // Checkboxes - $is_virtual = isset( $variable_is_virtual[ $i ] ) ? 'yes' : 'no'; - $is_downloadable = isset( $variable_is_downloadable[ $i ] ) ? 'yes' : 'no'; - $post_status = isset( $variable_enabled[ $i ] ) ? 'publish' : 'private'; - $manage_stock = isset( $variable_manage_stock[ $i ] ) ? 'yes' : 'no'; - - // Generate a useful post title - $variation_post_title = sprintf( __( 'Variation #%1$s of %2$s', 'woocommerce' ), absint( $variation_id ), esc_html( get_the_title( $post_id ) ) ); - - // Update or Add post - if ( ! $variation_id ) { - - $variation = array( - 'post_title' => $variation_post_title, - 'post_content' => '', - 'post_status' => $post_status, - 'post_author' => get_current_user_id(), - 'post_parent' => $post_id, - 'post_type' => 'product_variation', - 'menu_order' => $variable_menu_order[ $i ], - ); - - $variation_id = wp_insert_post( $variation ); - - do_action( 'woocommerce_create_product_variation', $variation_id ); - - } else { - - $modified_date = date_i18n( 'Y-m-d H:i:s', current_time( 'timestamp' ) ); - - $wpdb->update( $wpdb->posts, array( - 'post_status' => $post_status, - 'post_title' => $variation_post_title, - 'menu_order' => $variable_menu_order[ $i ], - 'post_modified' => $modified_date, - 'post_modified_gmt' => get_gmt_from_date( $modified_date ), - ), array( 'ID' => $variation_id ) ); - - clean_post_cache( $variation_id ); - - do_action( 'woocommerce_update_product_variation', $variation_id ); + $variation_id = absint( $_POST['variable_post_id'][ $i ] ); + $variation = new WC_Product_Variation( $variation_id ); + $errors = $variation->set_props( array( + 'status' => isset( $_POST['variable_enabled'][ $i ] ) ? 'publish' : 'private', + 'menu_order' => wc_clean( $_POST['variation_menu_order'][ $i ] ), + 'regular_price' => wc_clean( $_POST['variable_regular_price'][ $i ] ), + 'sale_price' => wc_clean( $_POST['variable_sale_price'][ $i ] ), + 'virtual' => isset( $_POST['variable_is_virtual'][ $i ] ), + 'downloadable' => isset( $_POST['variable_is_downloadable'][ $i ] ), + 'date_on_sale_from' => wc_clean( $_POST['variable_sale_price_dates_from'][ $i ] ), + 'date_on_sale_to' => wc_clean( $_POST['variable_sale_price_dates_to'][ $i ] ), + 'description' => wp_kses_post( wc_sanitize_textarea( $_POST['variable_description'][ $i ] ) ), + 'download_limit' => wc_clean( $_POST['variable_download_limit'][ $i ] ), + 'download_expiry' => wc_clean( $_POST['variable_download_expiry'][ $i ] ), + 'downloads' => self::prepare_downloads( isset( $_POST['_wc_variation_file_names'][ $variation_id ] ) ? $_POST['_wc_variation_file_names'][ $variation_id ] : array(), isset( $_POST['_wc_variation_file_urls'][ $variation_id ] ) ? $_POST['_wc_variation_file_urls'][ $variation_id ] : array() ), + 'manage_stock' => isset( $_POST['variable_manage_stock'][ $i ] ), + 'stock_quantity' => wc_clean( $_POST['variable_stock'][ $i ] ), + 'backorders' => wc_clean( $_POST['variable_backorders'][ $i ] ), + 'stock_status' => wc_clean( $_POST['variable_stock_status'][ $i ] ), + 'image_id' => wc_clean( $_POST['upload_image_id'][ $i ] ), + 'attributes' => self::prepare_set_attributes( $parent->get_attributes(), 'attribute_', $i ), + 'sku' => isset( $_POST['variable_sku'][ $i ] ) ? wc_clean( $_POST['variable_sku'][ $i ] ) : '', + 'weight' => isset( $_POST['variable_weight'][ $i ] ) ? wc_clean( $_POST['variable_weight'][ $i ] ) : '', + 'length' => isset( $_POST['variable_length'][ $i ] ) ? wc_clean( $_POST['variable_length'][ $i ] ) : '', + 'width' => isset( $_POST['variable_width'][ $i ] ) ? wc_clean( $_POST['variable_width'][ $i ] ) : '', + 'height' => isset( $_POST['variable_height'][ $i ] ) ? wc_clean( $_POST['variable_height'][ $i ] ) : '', + 'shipping_class_id' => wc_clean( $_POST['variable_shipping_class'][ $i ] ), + 'tax_class' => wc_clean( $_POST['variable_tax_class'][ $i ] ), + ) ); + if ( is_wp_error( $errors ) ) { + WC_Admin_Meta_Boxes::add_error( $errors->get_error_message() ); } - // Only continue if we have a variation ID - if ( ! $variation_id ) { - continue; - } - - // Unique SKU - $sku = get_post_meta( $variation_id, '_sku', true ); - $new_sku = wc_clean( $variable_sku[ $i ] ); - - if ( '' == $new_sku ) { - update_post_meta( $variation_id, '_sku', '' ); - } elseif ( $new_sku !== $sku ) { - if ( ! empty( $new_sku ) ) { - $unique_sku = wc_product_has_unique_sku( $variation_id, $new_sku ); - - if ( ! $unique_sku ) { - WC_Admin_Meta_Boxes::add_error( sprintf( __( '#%s – Variation SKU must be unique.', 'woocommerce' ), $variation_id ) ); - } else { - update_post_meta( $variation_id, '_sku', $new_sku ); - } - } else { - update_post_meta( $variation_id, '_sku', '' ); - } - } - - // Update post meta - update_post_meta( $variation_id, '_thumbnail_id', absint( $upload_image_id[ $i ] ) ); - update_post_meta( $variation_id, '_virtual', wc_clean( $is_virtual ) ); - update_post_meta( $variation_id, '_downloadable', wc_clean( $is_downloadable ) ); - - if ( isset( $variable_weight[ $i ] ) ) { - update_post_meta( $variation_id, '_weight', ( '' === $variable_weight[ $i ] ) ? '' : wc_format_decimal( $variable_weight[ $i ] ) ); - } - - if ( isset( $variable_length[ $i ] ) ) { - update_post_meta( $variation_id, '_length', ( '' === $variable_length[ $i ] ) ? '' : wc_format_decimal( $variable_length[ $i ] ) ); - } - - if ( isset( $variable_width[ $i ] ) ) { - update_post_meta( $variation_id, '_width', ( '' === $variable_width[ $i ] ) ? '' : wc_format_decimal( $variable_width[ $i ] ) ); - } - - if ( isset( $variable_height[ $i ] ) ) { - update_post_meta( $variation_id, '_height', ( '' === $variable_height[ $i ] ) ? '' : wc_format_decimal( $variable_height[ $i ] ) ); - } - - // Stock handling - update_post_meta( $variation_id, '_manage_stock', $manage_stock ); - - if ( 'yes' === $manage_stock ) { - update_post_meta( $variation_id, '_backorders', wc_clean( $variable_backorders[ $i ] ) ); - wc_update_product_stock( $variation_id, wc_stock_amount( $variable_stock[ $i ] ) ); - } else { - delete_post_meta( $variation_id, '_backorders' ); - wc_update_product_stock( $variation_id, '' ); - } - - // Only update stock status to user setting if changed by the user, but do so before looking at stock levels at variation level - if ( ! empty( $variable_stock_status[ $i ] ) ) { - wc_update_product_stock_status( $variation_id, $variable_stock_status[ $i ] ); - } - - // Price handling - _wc_save_product_price( $variation_id, $variable_regular_price[ $i ], $variable_sale_price[ $i ], $variable_sale_price_dates_from[ $i ], $variable_sale_price_dates_to[ $i ] ); - - if ( isset( $variable_tax_class[ $i ] ) && 'parent' !== $variable_tax_class[ $i ] ) { - update_post_meta( $variation_id, '_tax_class', wc_clean( $variable_tax_class[ $i ] ) ); - } else { - delete_post_meta( $variation_id, '_tax_class' ); - } - - if ( 'yes' == $is_downloadable ) { - update_post_meta( $variation_id, '_download_limit', wc_clean( $variable_download_limit[ $i ] ) ); - update_post_meta( $variation_id, '_download_expiry', wc_clean( $variable_download_expiry[ $i ] ) ); - - $files = array(); - $file_names = isset( $_POST['_wc_variation_file_names'][ $variation_id ] ) ? array_map( 'wc_clean', $_POST['_wc_variation_file_names'][ $variation_id ] ) : array(); - $file_urls = isset( $_POST['_wc_variation_file_urls'][ $variation_id ] ) ? array_map( 'wc_clean', $_POST['_wc_variation_file_urls'][ $variation_id ] ) : array(); - $file_url_size = sizeof( $file_urls ); - $allowed_file_types = get_allowed_mime_types(); - - for ( $ii = 0; $ii < $file_url_size; $ii ++ ) { - if ( ! empty( $file_urls[ $ii ] ) ) { - // Find type and file URL - if ( 0 === strpos( $file_urls[ $ii ], 'http' ) ) { - $file_is = 'absolute'; - $file_url = esc_url_raw( $file_urls[ $ii ] ); - } elseif ( '[' === substr( $file_urls[ $ii ], 0, 1 ) && ']' === substr( $file_urls[ $ii ], -1 ) ) { - $file_is = 'shortcode'; - $file_url = wc_clean( $file_urls[ $ii ] ); - } else { - $file_is = 'relative'; - $file_url = wc_clean( $file_urls[ $ii ] ); - } - - $file_name = wc_clean( $file_names[ $ii ] ); - $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, '?' ), $allowed_file_types ); - $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( __( '#%1$s – The downloadable file %2$s cannot be used as it does not have an allowed file type. Allowed types include: %3$s', 'woocommerce' ), $variation_id, '' . basename( $file_url ) . '', '' . implode( ', ', array_keys( $allowed_file_types ) ) . '' ) ); - continue; - } - } - - // Validate the file exists - if ( 'relative' === $file_is && ! apply_filters( 'woocommerce_downloadable_file_exists', file_exists( $file_url ), $file_url ) ) { - WC_Admin_Meta_Boxes::add_error( sprintf( __( '#%1$s – The downloadable file %2$s cannot be used as it does not exist on the server.', 'woocommerce' ), $variation_id, '' . $file_url . '' ) ); - 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, $variation_id, $files ); - - update_post_meta( $variation_id, '_downloadable_files', $files ); - } else { - update_post_meta( $variation_id, '_download_limit', '' ); - update_post_meta( $variation_id, '_download_expiry', '' ); - update_post_meta( $variation_id, '_downloadable_files', '' ); - } - - update_post_meta( $variation_id, '_variation_description', wp_kses_post( $variable_description[ $i ] ) ); - - // Save shipping class - $variable_shipping_class[ $i ] = ! empty( $variable_shipping_class[ $i ] ) ? (int) $variable_shipping_class[ $i ] : ''; - wp_set_object_terms( $variation_id, $variable_shipping_class[ $i ], 'product_shipping_class' ); - - // Update Attributes - $updated_attribute_keys = array(); - foreach ( $attributes as $attribute ) { - if ( $attribute['is_variation'] ) { - $attribute_key = 'attribute_' . sanitize_title( $attribute['name'] ); - $updated_attribute_keys[] = $attribute_key; - - if ( $attribute['is_taxonomy'] ) { - // Don't use wc_clean as it destroys sanitized characters - $value = isset( $_POST[ $attribute_key ][ $i ] ) ? sanitize_title( stripslashes( $_POST[ $attribute_key ][ $i ] ) ) : ''; - } else { - $value = isset( $_POST[ $attribute_key ][ $i ] ) ? wc_clean( stripslashes( $_POST[ $attribute_key ][ $i ] ) ) : ''; - } - - update_post_meta( $variation_id, $attribute_key, $value ); - } - } - - // Remove old taxonomies attributes so data is kept up to date - first get attribute key names - $delete_attribute_keys = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM {$wpdb->postmeta} WHERE meta_key LIKE 'attribute_%%' AND meta_key NOT IN ( '" . implode( "','", $updated_attribute_keys ) . "' ) AND post_id = %d;", $variation_id ) ); - - foreach ( $delete_attribute_keys as $key ) { - delete_post_meta( $variation_id, $key ); - } + $variation->save(); do_action( 'woocommerce_save_product_variation', $variation_id, $i ); } } - - // Update parent if variable so price sorting works and stays in sync with the cheapest child - WC_Product_Variable::sync( $post_id ); - - // Update default attribute options setting - $default_attributes = array(); - - foreach ( $attributes as $attribute ) { - - if ( $attribute['is_variation'] ) { - $value = ''; - - if ( isset( $_POST[ 'default_attribute_' . sanitize_title( $attribute['name'] ) ] ) ) { - if ( $attribute['is_taxonomy'] ) { - // Don't use wc_clean as it destroys sanitized characters - $value = sanitize_title( trim( stripslashes( $_POST[ 'default_attribute_' . sanitize_title( $attribute['name'] ) ] ) ) ); - } else { - $value = wc_clean( trim( stripslashes( $_POST[ 'default_attribute_' . sanitize_title( $attribute['name'] ) ] ) ) ); - } - } - - if ( $value ) { - $default_attributes[ sanitize_title( $attribute['name'] ) ] = $value; - } - } - } - - update_post_meta( $post_id, '_default_attributes', $default_attributes ); } } diff --git a/includes/admin/meta-boxes/views/html-order-download-permission.php b/includes/admin/meta-boxes/views/html-order-download-permission.php index d42e36df43b..b3e2b03ace2 100644 --- a/includes/admin/meta-boxes/views/html-order-download-permission.php +++ b/includes/admin/meta-boxes/views/html-order-download-permission.php @@ -10,7 +10,7 @@ if ( ! defined( 'ABSPATH' ) ) {
- id ) . ' — ' . apply_filters( 'woocommerce_admin_download_permissions_title', $product->get_title(), $download->product_id, $download->order_id, $download->order_key, $download->download_id ) . ' — ' . esc_html( $file_count ) . ': ' . wc_get_filename_from_url( $product->get_file_download_path( $download->download_id ) ) . ' — ' . sprintf( _n( 'Downloaded %s time', 'Downloaded %s times', absint( $download->download_count ), 'woocommerce' ), absint( $download->download_count ) ); ?> + get_id() ) . ' — ' . apply_filters( 'woocommerce_admin_download_permissions_title', $product->get_title(), $download->product_id, $download->order_id, $download->order_key, $download->download_id ) . ' — ' . esc_html( $file_count ) . ': ' . wc_get_filename_from_url( $product->get_file_download_path( $download->download_id ) ) . ' — ' . sprintf( _n( 'Downloaded %s time', 'Downloaded %s times', absint( $download->download_count ), 'woocommerce' ), absint( $download->download_count ) ); ?> diff --git a/includes/admin/meta-boxes/views/html-order-items.php b/includes/admin/meta-boxes/views/html-order-items.php index 81e74c0e5f4..1696facc01c 100644 --- a/includes/admin/meta-boxes/views/html-order-items.php +++ b/includes/admin/meta-boxes/views/html-order-items.php @@ -15,17 +15,9 @@ $line_items_fee = $order->get_items( 'fee' ); $line_items_shipping = $order->get_items( 'shipping' ); if ( wc_tax_enabled() ) { - $order_taxes = $order->get_taxes(); - $tax_classes = WC_Tax::get_tax_classes(); - $classes_options = array(); - $classes_options[''] = __( 'Standard', 'woocommerce' ); - - if ( ! empty( $tax_classes ) ) { - foreach ( $tax_classes as $class ) { - $classes_options[ sanitize_title( $class ) ] = $class; - } - } - + $order_taxes = $order->get_taxes(); + $tax_classes = WC_Tax::get_tax_classes(); + $classes_options = wc_get_product_tax_class_options(); $show_tax_columns = sizeof( $order_taxes ) === 1; } ?> diff --git a/includes/admin/meta-boxes/views/html-product-data-general.php b/includes/admin/meta-boxes/views/html-product-data-general.php index 08c34ea8880..2f5e7690c94 100644 --- a/includes/admin/meta-boxes/views/html-product-data-general.php +++ b/includes/admin/meta-boxes/views/html-product-data-general.php @@ -136,21 +136,11 @@ 'description' => __( 'Define whether or not the entire product is taxable, or just the cost of shipping it.', 'woocommerce' ), ) ); - $tax_classes = WC_Tax::get_tax_classes(); - $classes_options = array(); - $classes_options[''] = __( 'Standard', 'woocommerce' ); - - if ( ! empty( $tax_classes ) ) { - foreach ( $tax_classes as $class ) { - $classes_options[ sanitize_title( $class ) ] = esc_html( $class ); - } - } - woocommerce_wp_select( array( 'id' => '_tax_class', 'value' => $product_object->get_tax_class(), 'label' => __( 'Tax class', 'woocommerce' ), - 'options' => $classes_options, + 'options' => wc_get_product_tax_class_options(), 'desc_tip' => 'true', 'description' => __( 'Choose a tax class for this product. Tax classes are used to apply different tax rates specific to certain types of product.', 'woocommerce' ), ) ); diff --git a/includes/admin/meta-boxes/views/html-product-data-inventory.php b/includes/admin/meta-boxes/views/html-product-data-inventory.php index bc4465d7765..a784e63d1ef 100644 --- a/includes/admin/meta-boxes/views/html-product-data-inventory.php +++ b/includes/admin/meta-boxes/views/html-product-data-inventory.php @@ -45,11 +45,7 @@ 'id' => '_backorders', 'value' => $product_object->get_backorders(), 'label' => __( 'Allow backorders?', 'woocommerce' ), - 'options' => array( - 'no' => __( 'Do not allow', 'woocommerce' ), - 'notify' => __( 'Allow, but notify customer', 'woocommerce' ), - 'yes' => __( 'Allow', 'woocommerce' ), - ), + 'options' => wc_get_product_backorder_options(), 'desc_tip' => true, 'description' => __( 'If managing stock, this controls whether or not backorders are allowed. If enabled, stock quantity can go below 0.', 'woocommerce' ), ) ); @@ -64,10 +60,7 @@ 'value' => $product_object->get_stock_status(), 'wrapper_class' => 'hide_if_variable hide_if_external', 'label' => __( 'Stock status', 'woocommerce' ), - 'options' => array( - 'instock' => __( 'In stock', 'woocommerce' ), - 'outofstock' => __( 'Out of stock', 'woocommerce' ), - ), + 'options' => wc_get_product_stock_status_options(), 'desc_tip' => true, 'description' => __( 'Controls whether or not the product is listed as "in stock" or "out of stock" on the frontend.', 'woocommerce' ), ) ); @@ -83,7 +76,7 @@ 'value' => $product_object->get_sold_individually() ? 'yes' : 'no', 'wrapper_class' => 'show_if_simple show_if_variable', 'label' => __( 'Sold individually', 'woocommerce' ), - 'description' => __( 'Enable this to only allow one of this item to be bought in a single order', 'woocommerce' ) + 'description' => __( 'Enable this to only allow one of this item to be bought in a single order', 'woocommerce' ), ) ); do_action( 'woocommerce_product_options_sold_individually' ); diff --git a/includes/admin/meta-boxes/views/html-variation-admin.php b/includes/admin/meta-boxes/views/html-variation-admin.php index bba8fefa710..5960d7cc905 100644 --- a/includes/admin/meta-boxes/views/html-variation-admin.php +++ b/includes/admin/meta-boxes/views/html-variation-admin.php @@ -1,16 +1,15 @@

@@ -19,95 +18,111 @@ extract( $variation_data );
# get_attributes(); - // Only deal with attributes that are variations - if ( ! $attribute['is_variation'] || 'false' === $attribute['is_variation'] ) { + foreach ( $product_object->get_attributes() as $attribute ) { + if ( ! $attribute->get_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 ''; + $selected_value = isset( $attribute_values[ 'attribute_' . sanitize_title( $attribute->get_name() ) ] ) ? $attribute_values[ 'attribute_' . sanitize_title( $attribute->get_name() ) ] : ''; + ?> + + - +

$file ) { + if ( $downloads = $variation_object->get_downloads() ) { + foreach ( $downloads as $key => $file ) { if ( ! is_array( $file ) ) { $file = array( 'file' => $file, @@ -292,16 +328,39 @@ extract( $variation_data ); diff --git a/templates/emails/plain/email-order-items.php b/templates/emails/plain/email-order-items.php index 2da59343a1a..3a5b20e3b00 100644 --- a/templates/emails/plain/email-order-items.php +++ b/templates/emails/plain/email-order-items.php @@ -51,7 +51,7 @@ foreach ( $items as $item_id => $item ) : do_action( 'woocommerce_order_item_meta_end', $item_id, $item, $order, $plain_text ); } // Note - if ( $show_purchase_note && ( $purchase_note = get_post_meta( $product->id, '_purchase_note', true ) ) ) { + if ( $show_purchase_note && is_object( $product ) && ( $purchase_note = $product->get_purchase_note() ) ) { echo "\n" . do_shortcode( wp_kses_post( $purchase_note ) ); } echo "\n\n"; diff --git a/templates/loop/add-to-cart.php b/templates/loop/add-to-cart.php index c44c5260792..72702739a92 100644 --- a/templates/loop/add-to-cart.php +++ b/templates/loop/add-to-cart.php @@ -26,7 +26,7 @@ echo apply_filters( 'woocommerce_loop_add_to_cart_link', sprintf( '%s', esc_url( $product->add_to_cart_url() ), esc_attr( isset( $quantity ) ? $quantity : 1 ), - esc_attr( $product->id ), + esc_attr( $product->get_id() ), esc_attr( $product->get_sku() ), esc_attr( isset( $class ) ? $class : 'button' ), esc_html( $product->add_to_cart_text() ) diff --git a/templates/order/order-details.php b/templates/order/order-details.php index 8f21039a22f..f597f0af57a 100644 --- a/templates/order/order-details.php +++ b/templates/order/order-details.php @@ -49,7 +49,7 @@ $show_customer_details = is_user_logged_in() && $order->get_user_id() === get_cu 'item_id' => $item_id, 'item' => $item, 'show_purchase_note' => $show_purchase_note, - 'purchase_note' => $product ? get_post_meta( $product->id, '_purchase_note', true ) : '', + 'purchase_note' => $product ? $product->get_purchase_note() : '', 'product' => $product, ) ); } diff --git a/templates/single-product-reviews.php b/templates/single-product-reviews.php index f0808825abd..8c438419458 100644 --- a/templates/single-product-reviews.php +++ b/templates/single-product-reviews.php @@ -58,7 +58,7 @@ if ( ! comments_open() ) { - id ) ) : ?> + get_id() ) ) : ?>
diff --git a/templates/single-product/add-to-cart/grouped.php b/templates/single-product/add-to-cart/grouped.php index c4eae1ef513..6460bed5af4 100644 --- a/templates/single-product/add-to-cart/grouped.php +++ b/templates/single-product/add-to-cart/grouped.php @@ -85,7 +85,7 @@ do_action( 'woocommerce_before_add_to_cart_form' ); ?>
- + diff --git a/templates/single-product/add-to-cart/simple.php b/templates/single-product/add-to-cart/simple.php index 592bbeeff11..96e71467638 100644 --- a/templates/single-product/add-to-cart/simple.php +++ b/templates/single-product/add-to-cart/simple.php @@ -45,7 +45,7 @@ if ( $product->is_in_stock() ) : ?> } ?> - + diff --git a/templates/single-product/add-to-cart/variable.php b/templates/single-product/add-to-cart/variable.php index afb80425572..7c6e377faf4 100644 --- a/templates/single-product/add-to-cart/variable.php +++ b/templates/single-product/add-to-cart/variable.php @@ -25,7 +25,7 @@ $attribute_keys = array_keys( $attributes ); do_action( 'woocommerce_before_add_to_cart_form' ); ?> -
+ diff --git a/templates/single-product/add-to-cart/variation-add-to-cart-button.php b/templates/single-product/add-to-cart/variation-add-to-cart-button.php index 42e38f0f87f..b3dded7892b 100644 --- a/templates/single-product/add-to-cart/variation-add-to-cart-button.php +++ b/templates/single-product/add-to-cart/variation-add-to-cart-button.php @@ -18,7 +18,7 @@ global $product; isset( $_POST['quantity'] ) ? wc_stock_amount( $_POST['quantity'] ) : 1 ) ); ?> - - + + diff --git a/templates/single-product/price.php b/templates/single-product/price.php index b7460fbdde4..3a9ed26798c 100644 --- a/templates/single-product/price.php +++ b/templates/single-product/price.php @@ -27,7 +27,7 @@ global $product;

get_price_html(); ?>

- + diff --git a/templates/single-product/product-attributes.php b/templates/single-product/product-attributes.php index 770bc907276..52c7db28509 100644 --- a/templates/single-product/product-attributes.php +++ b/templates/single-product/product-attributes.php @@ -40,7 +40,7 @@ ob_start(); - has_dimensions() || get_post_meta( $product->id, '_child_has_dimensions', true ) ) : $has_row = true; ?> + has_dimensions() || $product->child_has_dimensions() ) : $has_row = true; ?> get_dimensions() ? $product->get_dimensions() : __( 'N/A', 'woocommerce' ); ?> @@ -61,7 +61,7 @@ ob_start(); id, $attribute['name'], array( 'fields' => 'names' ) ); + $values = wc_get_product_terms( $product->get_id(), $attribute['name'], array( 'fields' => 'names' ) ); echo apply_filters( 'woocommerce_attribute', wpautop( wptexturize( implode( ', ', $values ) ) ), $attribute, $values ); } else { diff --git a/templates/single-product/product-image.php b/templates/single-product/product-image.php index 1cfda4c01cd..623ac94b3e2 100644 --- a/templates/single-product/product-image.php +++ b/templates/single-product/product-image.php @@ -25,7 +25,7 @@ global $post, $product;
get_gallery_attachment_ids() ); + $attachment_count = count( $product->get_gallery_image_ids() ); $gallery = $attachment_count > 0 ? '[product-gallery]' : ''; $props = wc_get_product_attachment_props( get_post_thumbnail_id(), $post ); $image = get_the_post_thumbnail( $post->ID, apply_filters( 'single_product_large_thumbnail_size', 'shop_single' ), array( diff --git a/templates/single-product/product-thumbnails.php b/templates/single-product/product-thumbnails.php index d4bef1725f4..81687862b21 100644 --- a/templates/single-product/product-thumbnails.php +++ b/templates/single-product/product-thumbnails.php @@ -22,7 +22,7 @@ if ( ! defined( 'ABSPATH' ) ) { global $post, $product, $woocommerce; -$attachment_ids = $product->get_gallery_attachment_ids(); +$attachment_ids = $product->get_gallery_image_ids(); if ( $attachment_ids ) { $loop = 0; diff --git a/templates/single-product/stock.php b/templates/single-product/stock.php index 1fb180c2ab2..0974dce1b29 100644 --- a/templates/single-product/stock.php +++ b/templates/single-product/stock.php @@ -30,6 +30,6 @@ if ( ! $product->is_in_stock() ) : ?> managing_stock() ) : ?> -

get_total_stock(), $product->backorders_allowed() && $product->backorders_require_notification() ) ); ?>

+

get_stock_quantity(), $product->backorders_allowed() && $product->backorders_require_notification() ) ); ?>

diff --git a/templates/single-product/up-sells.php b/templates/single-product/up-sells.php index af09d978c05..27d17f8f619 100644 --- a/templates/single-product/up-sells.php +++ b/templates/single-product/up-sells.php @@ -33,7 +33,7 @@ $args = array( 'posts_per_page' => $posts_per_page, 'orderby' => $orderby, 'post__in' => $upsells, - 'post__not_in' => array( $product->id ), + 'post__not_in' => array( $product->get_id() ), 'meta_query' => WC()->query->get_meta_query(), ); diff --git a/tests/unit-tests/api/product-reviews.php b/tests/unit-tests/api/product-reviews.php index fa31e027820..e2e6d04463a 100644 --- a/tests/unit-tests/api/product-reviews.php +++ b/tests/unit-tests/api/product-reviews.php @@ -40,10 +40,10 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { $product = WC_Helper_Product::create_simple_product(); // Create 10 products reviews for the product for ( $i = 0; $i < 10; $i++ ) { - $review_id = WC_Helper_Product::create_product_review( $product->id ); + $review_id = WC_Helper_Product::create_product_review( $product->get_id() ); } - $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/reviews' ) ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/reviews' ) ); $product_reviews = $response->get_data(); $this->assertEquals( 200, $response->get_status() ); @@ -59,17 +59,17 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { '_links' => array( 'self' => array( array( - 'href' => rest_url( '/wc/v1/products/' . $product->id . '/reviews/' . $review_id ), + 'href' => rest_url( '/wc/v1/products/' . $product->get_id() . '/reviews/' . $review_id ), ), ), 'collection' => array( array( - 'href' => rest_url( '/wc/v1/products/' . $product->id . '/reviews' ), + 'href' => rest_url( '/wc/v1/products/' . $product->get_id() . '/reviews' ), ), ), 'up' => array( array( - 'href' => rest_url( '/wc/v1/products/' . $product->id ), + 'href' => rest_url( '/wc/v1/products/' . $product->get_id() ), ), ), ), @@ -84,7 +84,7 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { public function test_get_product_reviews_without_permission() { wp_set_current_user( 0 ); $product = WC_Helper_Product::create_simple_product(); - $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/reviews' ) ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/reviews' ) ); $this->assertEquals( 401, $response->get_status() ); } @@ -107,9 +107,9 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { public function test_get_product_review() { wp_set_current_user( $this->user ); $product = WC_Helper_Product::create_simple_product(); - $product_review_id = WC_Helper_Product::create_product_review( $product->id ); + $product_review_id = WC_Helper_Product::create_product_review( $product->get_id() ); - $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/reviews/' . $product_review_id ) ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/reviews/' . $product_review_id ) ); $data = $response->get_data(); $this->assertEquals( 200, $response->get_status() ); @@ -132,8 +132,8 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { public function test_get_product_review_without_permission() { wp_set_current_user( 0 ); $product = WC_Helper_Product::create_simple_product(); - $product_review_id = WC_Helper_Product::create_product_review( $product->id ); - $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/reviews/' . $product_review_id ) ); + $product_review_id = WC_Helper_Product::create_product_review( $product->get_id() ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/reviews/' . $product_review_id ) ); $this->assertEquals( 401, $response->get_status() ); } @@ -145,7 +145,7 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { public function test_get_product_review_invalid_id() { wp_set_current_user( $this->user ); $product = WC_Helper_Product::create_simple_product(); - $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/reviews/0' ) ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/reviews/0' ) ); $this->assertEquals( 404, $response->get_status() ); } @@ -157,7 +157,7 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { public function test_create_product_review() { wp_set_current_user( $this->user ); $product = WC_Helper_Product::create_simple_product(); - $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->id . '/reviews' ); + $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->get_id() . '/reviews' ); $request->set_body_params( array( 'review' => 'Hello world.', 'name' => 'Admin', @@ -189,7 +189,7 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { $product = WC_Helper_Product::create_simple_product(); // missing review - $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->id . '/reviews' ); + $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->get_id() . '/reviews' ); $request->set_body_params( array( 'name' => 'Admin', 'email' => 'woo@woo.local', @@ -200,7 +200,7 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { $this->assertEquals( 400, $response->get_status() ); // missing name - $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->id . '/reviews' ); + $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->get_id() . '/reviews' ); $request->set_body_params( array( 'review' => 'Hello world.', 'email' => 'woo@woo.local', @@ -211,7 +211,7 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { $this->assertEquals( 400, $response->get_status() ); // missing email - $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->id . '/reviews' ); + $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->get_id() . '/reviews' ); $request->set_body_params( array( 'review' => 'Hello world.', 'name' => 'Admin', @@ -230,16 +230,16 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { public function test_update_product_review() { wp_set_current_user( $this->user ); $product = WC_Helper_Product::create_simple_product(); - $product_review_id = WC_Helper_Product::create_product_review( $product->id ); + $product_review_id = WC_Helper_Product::create_product_review( $product->get_id() ); - $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/reviews/' . $product_review_id ) ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/reviews/' . $product_review_id ) ); $data = $response->get_data(); $this->assertEquals( 'Review content here', $data['review'] ); $this->assertEquals( 'admin', $data['name'] ); $this->assertEquals( 'woo@woo.local', $data['email'] ); $this->assertEquals( 0, $data['rating'] ); - $request = new WP_REST_Request( 'PUT', '/wc/v1/products/' . $product->id . '/reviews/' . $product_review_id ); + $request = new WP_REST_Request( 'PUT', '/wc/v1/products/' . $product->get_id() . '/reviews/' . $product_review_id ); $request->set_body_params( array( 'review' => 'Hello world - updated.', 'name' => 'Justin', @@ -262,9 +262,9 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { public function test_update_product_review_without_permission() { wp_set_current_user( 0 ); $product = WC_Helper_Product::create_simple_product(); - $product_review_id = WC_Helper_Product::create_product_review( $product->id ); + $product_review_id = WC_Helper_Product::create_product_review( $product->get_id() ); - $request = new WP_REST_Request( 'PUT', '/wc/v1/products/' . $product->id . '/reviews/' . $product_review_id ); + $request = new WP_REST_Request( 'PUT', '/wc/v1/products/' . $product->get_id() . '/reviews/' . $product_review_id ); $request->set_body_params( array( 'review' => 'Hello world.', 'name' => 'Admin', @@ -285,7 +285,7 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { wp_set_current_user( $this->user ); $product = WC_Helper_Product::create_simple_product(); - $request = new WP_REST_Request( 'PUT', '/wc/v1/products/' . $product->id . '/reviews/0' ); + $request = new WP_REST_Request( 'PUT', '/wc/v1/products/' . $product->get_id() . '/reviews/0' ); $request->set_body_params( array( 'review' => 'Hello world.', 'name' => 'Admin', @@ -305,9 +305,9 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { public function test_delete_product_review() { wp_set_current_user( $this->user ); $product = WC_Helper_Product::create_simple_product(); - $product_review_id = WC_Helper_Product::create_product_review( $product->id ); + $product_review_id = WC_Helper_Product::create_product_review( $product->get_id() ); - $request = new WP_REST_Request( 'DELETE', '/wc/v1/products/' . $product->id . '/reviews/' . $product_review_id ); + $request = new WP_REST_Request( 'DELETE', '/wc/v1/products/' . $product->get_id() . '/reviews/' . $product_review_id ); $request->set_param( 'force', true ); $response = $this->server->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); @@ -321,9 +321,9 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { public function test_delete_product_without_permission() { wp_set_current_user( 0 ); $product = WC_Helper_Product::create_simple_product(); - $product_review_id = WC_Helper_Product::create_product_review( $product->id ); + $product_review_id = WC_Helper_Product::create_product_review( $product->get_id() ); - $request = new WP_REST_Request( 'DELETE', '/wc/v1/products/' . $product->id . '/reviews/' . $product_review_id ); + $request = new WP_REST_Request( 'DELETE', '/wc/v1/products/' . $product->get_id() . '/reviews/' . $product_review_id ); $response = $this->server->dispatch( $request ); $this->assertEquals( 401, $response->get_status() ); @@ -337,9 +337,9 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { public function test_delete_product_review_invalid_id() { wp_set_current_user( $this->user ); $product = WC_Helper_Product::create_simple_product(); - $product_review_id = WC_Helper_Product::create_product_review( $product->id ); + $product_review_id = WC_Helper_Product::create_product_review( $product->get_id() ); - $request = new WP_REST_Request( 'DELETE', '/wc/v1/products/' . $product->id . '/reviews/0' ); + $request = new WP_REST_Request( 'DELETE', '/wc/v1/products/' . $product->get_id() . '/reviews/0' ); $request->set_param( 'force', true ); $response = $this->server->dispatch( $request ); @@ -353,12 +353,12 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { wp_set_current_user( $this->user ); $product = WC_Helper_Product::create_simple_product(); - $review_1_id = WC_Helper_Product::create_product_review( $product->id ); - $review_2_id = WC_Helper_Product::create_product_review( $product->id ); - $review_3_id = WC_Helper_Product::create_product_review( $product->id ); - $review_4_id = WC_Helper_Product::create_product_review( $product->id ); + $review_1_id = WC_Helper_Product::create_product_review( $product->get_id() ); + $review_2_id = WC_Helper_Product::create_product_review( $product->get_id() ); + $review_3_id = WC_Helper_Product::create_product_review( $product->get_id() ); + $review_4_id = WC_Helper_Product::create_product_review( $product->get_id() ); - $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->id . '/reviews/batch' ); + $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->get_id() . '/reviews/batch' ); $request->set_body_params( array( 'update' => array( array( @@ -390,7 +390,7 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { $this->assertEquals( $review_2_id, $data['delete'][0]['id'] ); $this->assertEquals( $review_3_id, $data['delete'][1]['id'] ); - $request = new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/reviews' ); + $request = new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/reviews' ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); @@ -405,7 +405,7 @@ class Product_Reviews extends WC_REST_Unit_Test_Case { public function test_product_review_schema() { wp_set_current_user( $this->user ); $product = WC_Helper_Product::create_simple_product(); - $request = new WP_REST_Request( 'OPTIONS', '/wc/v1/products/' . $product->id . '/reviews' ); + $request = new WP_REST_Request( 'OPTIONS', '/wc/v1/products/' . $product->get_id() . '/reviews' ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; diff --git a/tests/unit-tests/api/product-variations.php b/tests/unit-tests/api/product-variations.php index b01a18dd6fc..f0208152967 100644 --- a/tests/unit-tests/api/product-variations.php +++ b/tests/unit-tests/api/product-variations.php @@ -39,7 +39,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { public function test_get_variations() { wp_set_current_user( $this->user ); $product = WC_Helper_Product::create_variation_product(); - $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/variations' ) ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/variations' ) ); $variations = $response->get_data(); $this->assertEquals( 200, $response->get_status() ); @@ -56,7 +56,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { public function test_get_variations_without_permission() { wp_set_current_user( 0 ); $product = WC_Helper_Product::create_variation_product(); - $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/variations' ) ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/variations' ) ); $this->assertEquals( 401, $response->get_status() ); } @@ -71,7 +71,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { $children = $product->get_children(); $variation_id = $children[0]; - $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/variations/' . $variation_id ) ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/variations/' . $variation_id ) ); $variation = $response->get_data(); $this->assertEquals( 200, $response->get_status() ); @@ -89,7 +89,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { $product = WC_Helper_Product::create_variation_product(); $children = $product->get_children(); $variation_id = $children[0]; - $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/variations/' . $variation_id ) ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/variations/' . $variation_id ) ); $this->assertEquals( 401, $response->get_status() ); } @@ -104,12 +104,12 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { $children = $product->get_children(); $variation_id = $children[0]; - $request = new WP_REST_Request( 'DELETE', '/wc/v1/products/' . $product->id . '/variations/' . $variation_id ); + $request = new WP_REST_Request( 'DELETE', '/wc/v1/products/' . $product->get_id() . '/variations/' . $variation_id ); $request->set_param( 'force', true ); $response = $this->server->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); - $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/variations' ) ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/variations' ) ); $variations = $response->get_data(); $this->assertEquals( 1, count( $variations ) ); } @@ -125,7 +125,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { $children = $product->get_children(); $variation_id = $children[0]; - $request = new WP_REST_Request( 'DELETE', '/wc/v1/products/' . $product->id . '/variations/' . $variation_id ); + $request = new WP_REST_Request( 'DELETE', '/wc/v1/products/' . $product->get_id() . '/variations/' . $variation_id ); $request->set_param( 'force', true ); $response = $this->server->dispatch( $request ); $this->assertEquals( 401, $response->get_status() ); @@ -139,7 +139,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { public function test_delete_variation_with_invalid_id() { wp_set_current_user( 0 ); $product = WC_Helper_Product::create_variation_product(); - $request = new WP_REST_Request( 'DELETE', '/wc/v1/products/' . $product->id . '/variations/0' ); + $request = new WP_REST_Request( 'DELETE', '/wc/v1/products/' . $product->get_id() . '/variations/0' ); $request->set_param( 'force', true ); $response = $this->server->dispatch( $request ); $this->assertEquals( 404, $response->get_status() ); @@ -156,7 +156,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { $children = $product->get_children(); $variation_id = $children[0]; - $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/variations/' . $variation_id ) ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/variations/' . $variation_id ) ); $variation = $response->get_data(); $this->assertEquals( 'DUMMY SKU VARIABLE SMALL', $variation['sku'] ); @@ -164,7 +164,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { $this->assertEmpty( $variation['sale_price'] ); $this->assertEquals( 'small', $variation['attributes'][0]['option'] ); - $request = new WP_REST_Request( 'PUT', '/wc/v1/products/' . $product->id . '/variations/' . $variation_id ); + $request = new WP_REST_Request( 'PUT', '/wc/v1/products/' . $product->get_id() . '/variations/' . $variation_id ); $request->set_body_params( array( 'sku' => 'FIXED-SKU', 'sale_price' => '8', @@ -197,7 +197,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { $children = $product->get_children(); $variation_id = $children[0]; - $request = new WP_REST_Request( 'PUT', '/wc/v1/products/' . $product->id . '/variations/' . $variation_id ); + $request = new WP_REST_Request( 'PUT', '/wc/v1/products/' . $product->get_id() . '/variations/' . $variation_id ); $request->set_body_params( array( 'sku' => 'FIXED-SKU-NO-PERMISSION', ) ); @@ -213,7 +213,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { public function test_update_variation_with_invalid_id() { wp_set_current_user( $this->user ); $product = WC_Helper_Product::create_variation_product(); - $request = new WP_REST_Request( 'PUT', '/wc/v1/products/' . $product->id . '/variations/0' ); + $request = new WP_REST_Request( 'PUT', '/wc/v1/products/' . $product->get_id() . '/variations/0' ); $request->set_body_params( array( 'sku' => 'FIXED-SKU-NO-PERMISSION', ) ); @@ -230,11 +230,11 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { wp_set_current_user( $this->user ); $product = WC_Helper_Product::create_variation_product(); - $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/variations' ) ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/variations' ) ); $variations = $response->get_data(); $this->assertEquals( 2, count( $variations ) ); - $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->id . '/variations' ); + $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->get_id() . '/variations' ); $request->set_body_params( array( 'sku' => 'DUMMY SKU VARIABLE MEDIUM', 'regular_price' => '12', @@ -251,7 +251,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { $this->assertEquals( 'DUMMY SKU VARIABLE MEDIUM', $variation['sku'] ); $this->assertEquals( 'medium', $variation['attributes'][0]['option'] ); - $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/variations' ) ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/variations' ) ); $variations = $response->get_data(); $this->assertEquals( 3, count( $variations ) ); } @@ -265,7 +265,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { wp_set_current_user( 0 ); $product = WC_Helper_Product::create_variation_product(); - $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->id . '/variations' ); + $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->get_id() . '/variations' ); $request->set_body_params( array( 'sku' => 'DUMMY SKU VARIABLE MEDIUM', 'regular_price' => '12', @@ -283,7 +283,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { wp_set_current_user( $this->user ); $product = WC_Helper_Product::create_variation_product(); $children = $product->get_children(); - $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->id . '/variations/batch' ); + $request = new WP_REST_Request( 'POST', '/wc/v1/products/' . $product->get_id() . '/variations/batch' ); $request->set_body_params( array( 'update' => array( array( @@ -314,7 +314,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { $this->assertEquals( 'medium', $data['create'][0]['attributes'][0]['option'] ); $this->assertEquals( $children[1], $data['delete'][0]['id'] ); - $request = new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->id . '/variations' ); + $request = new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/variations' ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); @@ -329,7 +329,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case { public function test_variation_schema() { wp_set_current_user( $this->user ); $product = WC_Helper_Product::create_simple_product(); - $request = new WP_REST_Request( 'OPTIONS', '/wc/v1/products/' . $product->id . '/variations' ); + $request = new WP_REST_Request( 'OPTIONS', '/wc/v1/products/' . $product->get_id() . '/variations' ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; diff --git a/tests/unit-tests/api/products.php b/tests/unit-tests/api/products.php index 4a9988023b1..b351fb1d8de 100644 --- a/tests/unit-tests/api/products.php +++ b/tests/unit-tests/api/products.php @@ -186,7 +186,7 @@ class Products_API extends WC_REST_Unit_Test_Case { $this->assertEquals( array( 'small' ), $data['attributes'][0]['options'] ); - $request = new WP_REST_Request( 'PUT', '/wc/v1/products/' . $product->id ); + $request = new WP_REST_Request( 'PUT', '/wc/v1/products/' . $product->get_id() ); $request->set_body_params( array( 'attributes' => array( array( 'id' => 0, 'name' => 'pa_color', 'options' => array( 'red', 'yellow' ), 'visible' => false, 'variation' => 1 ), diff --git a/tests/unit-tests/cart/cart.php b/tests/unit-tests/cart/cart.php index 5c18fa4c8af..3251884a180 100644 --- a/tests/unit-tests/cart/cart.php +++ b/tests/unit-tests/cart/cart.php @@ -34,19 +34,19 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { $product = WC_Helper_Product::create_simple_product(); // Add product to cart x1, calc and test - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); WC()->cart->calculate_totals(); $this->assertEquals( '9.00', number_format( WC()->cart->total, 2, '.', '' ) ); $this->assertEquals( '1.00', number_format( WC()->cart->discount_cart, 2, '.', '' ) ); // Add product to cart x2, calc and test - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); WC()->cart->calculate_totals(); $this->assertEquals( '19.00', number_format( WC()->cart->total, 2, '.', '' ) ); $this->assertEquals( '1.00', number_format( WC()->cart->discount_cart, 2, '.', '' ) ); // Add product to cart x3, calc and test - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); WC()->cart->calculate_totals(); $this->assertEquals( '29.00', number_format( WC()->cart->total, 2, '.', '' ) ); $this->assertEquals( '1.00', number_format( WC()->cart->discount_cart, 2, '.', '' ) ); @@ -56,8 +56,8 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { WC()->cart->remove_coupons(); # Test case 2 #10573 - update_post_meta( $product->id, '_regular_price', '29.95' ); - update_post_meta( $product->id, '_price', '29.95' ); + update_post_meta( $product->get_id(), '_regular_price', '29.95' ); + update_post_meta( $product->get_id(), '_price', '29.95' ); update_post_meta( $coupon->get_id(), 'discount_type', 'percent' ); update_post_meta( $coupon->get_id(), 'coupon_amount', '10' ); update_option( 'woocommerce_prices_include_tax', 'yes' ); @@ -74,9 +74,9 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { 'tax_rate_class' => '', ); WC_Tax::_insert_tax_rate( $tax_rate ); - $product = wc_get_product( $product->id ); + $product = wc_get_product( $product->get_id() ); - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); WC()->cart->add_discount( $coupon->get_code() ); WC()->cart->calculate_totals(); @@ -88,7 +88,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { $wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations" ); WC()->cart->empty_cart(); WC()->cart->remove_coupons(); - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); # Test case 3 #11626 update_post_meta( $coupon->get_id(), 'discount_type', 'percent' ); @@ -122,10 +122,10 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { ); foreach ( $products_data as $price ) { $loop_product = WC_Helper_Product::create_simple_product(); - $product_ids[] = $loop_product->id; - update_post_meta( $loop_product->id, '_regular_price', $price ); - update_post_meta( $loop_product->id, '_price', $price ); - WC()->cart->add_to_cart( $loop_product->id, 1 ); + $product_ids[] = $loop_product->get_id(); + update_post_meta( $loop_product->get_id(), '_regular_price', $price ); + update_post_meta( $loop_product->get_id(), '_price', $price ); + WC()->cart->add_to_cart( $loop_product->get_id(), 1 ); } WC()->cart->add_discount( $coupon->get_code() ); @@ -179,7 +179,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { WC()->cart->empty_cart(); // Add the product to the cart. Methods returns boolean on failure, string on success. - $this->assertNotFalse( WC()->cart->add_to_cart( $product->id, 1 ) ); + $this->assertNotFalse( WC()->cart->add_to_cart( $product->get_id(), 1 ) ); // Check if the item is in the cart $this->assertEquals( 1, WC()->cart->get_cart_contents_count() ); @@ -188,7 +188,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { WC()->cart->empty_cart(); // Clean up product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -199,19 +199,19 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { $product = WC_Helper_Product::create_simple_product(); // Trash product - wp_trash_post( $product->id ); + wp_trash_post( $product->get_id() ); // Refetch product, to be sure - $product = wc_get_product( $product->id ); + $product = wc_get_product( $product->get_id() ); // Add product to cart - $this->assertFalse( WC()->cart->add_to_cart( $product->id, 1 ) ); + $this->assertFalse( WC()->cart->add_to_cart( $product->get_id(), 1 ) ); // Clean up the cart WC()->cart->empty_cart(); // Clean up product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -225,7 +225,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { $variation = array_shift( $variations ); // Add the product to the cart. Methods returns boolean on failure, string on success. - $this->assertNotFalse( WC()->cart->add_to_cart( $product->id, 1, $variation['variation_id'], array( 'Size' => ucfirst( $variation['attributes']['attribute_pa_size'] ) ) ) ); + $this->assertNotFalse( WC()->cart->add_to_cart( $product->get_id(), 1, $variation['variation_id'], array( 'Size' => ucfirst( $variation['attributes']['attribute_pa_size'] ) ) ) ); // Check if the item is in the cart $this->assertEquals( 1, WC()->cart->get_cart_contents_count() ); @@ -247,10 +247,10 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { // Set sold_individually to yes $product->sold_individually = 'yes'; - update_post_meta( $product->id, '_sold_individually', 'yes' ); + update_post_meta( $product->get_id(), '_sold_individually', 'yes' ); // Add the product twice to cart, should be corrected to 1. Methods returns boolean on failure, string on success. - $this->assertNotFalse( WC()->cart->add_to_cart( $product->id, 2 ) ); + $this->assertNotFalse( WC()->cart->add_to_cart( $product->get_id(), 2 ) ); // Check if the item is in the cart $this->assertEquals( 1, WC()->cart->get_cart_contents_count() ); @@ -259,7 +259,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { WC()->cart->empty_cart(); // Clean up product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -273,10 +273,10 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { $product = WC_Helper_Product::create_simple_product(); // Add product to cart - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); // Generate cart id - $cart_id = WC()->cart->generate_cart_id( $product->id ); + $cart_id = WC()->cart->generate_cart_id( $product->get_id() ); // Get the product from the cart $this->assertNotEquals( '', WC()->cart->find_product_in_cart( $cart_id ) ); @@ -285,7 +285,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { WC()->cart->empty_cart(); // Clean up product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } @@ -355,10 +355,10 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { $product = WC_Helper_Product::create_simple_product(); // Add 1 product to cart - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); // Get cart id - $cart_id = WC()->cart->generate_cart_id( $product->id ); + $cart_id = WC()->cart->generate_cart_id( $product->get_id() ); // Set quantity of product in cart to 2 $this->assertTrue( WC()->cart->set_quantity( $cart_id, 2 ) ); @@ -376,7 +376,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { WC()->cart->empty_cart(); // Clean up product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -390,7 +390,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { $product = WC_Helper_Product::create_simple_product(); // Add product to cart - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); // Check cart validity, should pass $this->assertTrue( WC()->cart->check_cart_item_validity() ); @@ -399,7 +399,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { WC()->cart->empty_cart(); // Clean up product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } @@ -419,7 +419,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { } // Add product to cart - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); // Check $this->assertEquals( apply_filters( 'woocommerce_cart_total', wc_price( WC()->cart->total ) ), WC()->cart->get_total() ); @@ -428,7 +428,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { WC()->cart->empty_cart(); // Clean up product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -450,7 +450,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { } // Add product to cart - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); // Calc total $total = WC()->cart->total - WC()->cart->tax_total - WC()->cart->shipping_tax_total; @@ -465,7 +465,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { WC()->cart->empty_cart(); // Clean up product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); // Restore option update_option( 'woocommerce_calc_taxes', 'no' ); @@ -492,8 +492,8 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { public function test_shipping_total() { // Create product $product = WC_Helper_Product::create_simple_product(); - update_post_meta( $product->id, '_price', '10' ); - update_post_meta( $product->id, '_regular_price', '10' ); + update_post_meta( $product->get_id(), '_price', '10' ); + update_post_meta( $product->get_id(), '_regular_price', '10' ); // Create a flat rate method WC_Helper_Shipping::create_simple_flat_rate(); @@ -504,7 +504,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { } // Add product to cart - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); // Set the flat_rate shipping method WC()->session->set( 'chosen_shipping_methods', array( 'flat_rate' ) ); @@ -524,7 +524,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { WC_Helper_Shipping::delete_simple_flat_rate(); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -535,8 +535,8 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { public function test_cart_fee() { // Create product $product = WC_Helper_Product::create_simple_product(); - update_post_meta( $product->id, '_price', '10' ); - update_post_meta( $product->id, '_regular_price', '10' ); + update_post_meta( $product->get_id(), '_price', '10' ); + update_post_meta( $product->get_id(), '_regular_price', '10' ); // We need this to have the calculate_totals() method calculate totals if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) { @@ -547,7 +547,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { WC_Helper_Fee::add_cart_fee(); // Add product to cart - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); // Test if the cart total amount is equal 20 $this->assertEquals( 20, WC()->cart->total ); @@ -562,7 +562,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case { WC_Helper_Fee::remove_cart_fee(); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** diff --git a/tests/unit-tests/cart/functions.php b/tests/unit-tests/cart/functions.php index 7206227532a..d949e7ec1a6 100644 --- a/tests/unit-tests/cart/functions.php +++ b/tests/unit-tests/cart/functions.php @@ -92,7 +92,7 @@ class WC_Tests_Cart_Functions extends WC_Unit_Test_Case { $product = WC_Helper_Product::create_simple_product(); // Add the product to the cart - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); // Empty the cart wc_empty_cart(); @@ -101,7 +101,7 @@ class WC_Tests_Cart_Functions extends WC_Unit_Test_Case { $this->assertEquals( 0, WC()->cart->get_cart_contents_count() ); // Delete the previously created product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -124,11 +124,11 @@ class WC_Tests_Cart_Functions extends WC_Unit_Test_Case { public function test_wc_cart_totals_subtotal_html() { $product = WC_Helper_Product::create_simple_product(); - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); $this->expectOutputString( wc_price( $product->price ), wc_cart_totals_subtotal_html() ); - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -161,22 +161,22 @@ class WC_Tests_Cart_Functions extends WC_Unit_Test_Case { public function test_wc_add_to_cart_message() { $product = WC_Helper_Product::create_simple_product(); - $message = wc_add_to_cart_message( array( $product->id => 1 ), false, true ); + $message = wc_add_to_cart_message( array( $product->get_id() => 1 ), false, true ); $this->assertEquals( 'View Cart “Dummy Product” has been added to your cart.', $message ); - $message = wc_add_to_cart_message( array( $product->id => 3 ), false, true ); + $message = wc_add_to_cart_message( array( $product->get_id() => 3 ), false, true ); $this->assertEquals( 'View Cart “Dummy Product” has been added to your cart.', $message ); - $message = wc_add_to_cart_message( array( $product->id => 1 ), true, true ); + $message = wc_add_to_cart_message( array( $product->get_id() => 1 ), true, true ); $this->assertEquals( 'View Cart “Dummy Product” has been added to your cart.', $message ); - $message = wc_add_to_cart_message( array( $product->id => 3 ), true, true ); + $message = wc_add_to_cart_message( array( $product->get_id() => 3 ), true, true ); $this->assertEquals( 'View Cart 3 × “Dummy Product” have been added to your cart.', $message ); - $message = wc_add_to_cart_message( $product->id, false, true ); + $message = wc_add_to_cart_message( $product->get_id(), false, true ); $this->assertEquals( 'View Cart “Dummy Product” has been added to your cart.', $message ); - $message = wc_add_to_cart_message( $product->id, true, true ); + $message = wc_add_to_cart_message( $product->get_id(), true, true ); $this->assertEquals( 'View Cart “Dummy Product” has been added to your cart.', $message ); } } diff --git a/tests/unit-tests/coupon/coupon.php b/tests/unit-tests/coupon/coupon.php index 07430fc6aea..75d8f8197fc 100644 --- a/tests/unit-tests/coupon/coupon.php +++ b/tests/unit-tests/coupon/coupon.php @@ -73,8 +73,8 @@ class WC_Tests_Coupon extends WC_Unit_Test_Case { // Create product $product = WC_Helper_Product::create_simple_product(); - update_post_meta( $product->id, '_price', '10' ); - update_post_meta( $product->id, '_regular_price', '10' ); + update_post_meta( $product->get_id(), '_price', '10' ); + update_post_meta( $product->get_id(), '_regular_price', '10' ); // Create coupon $coupon = WC_Helper_Coupon::create_coupon(); @@ -90,7 +90,7 @@ class WC_Tests_Coupon extends WC_Unit_Test_Case { } // Add product to cart - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); // Add coupon WC()->cart->add_discount( $coupon->get_code() ); @@ -119,7 +119,7 @@ class WC_Tests_Coupon extends WC_Unit_Test_Case { WC_Helper_Coupon::delete_coupon( $coupon->get_id() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -131,8 +131,8 @@ class WC_Tests_Coupon extends WC_Unit_Test_Case { // Create product $product = WC_Helper_Product::create_simple_product(); - update_post_meta( $product->id, '_price', '10' ); - update_post_meta( $product->id, '_regular_price', '10' ); + update_post_meta( $product->get_id(), '_price', '10' ); + update_post_meta( $product->get_id(), '_regular_price', '10' ); // Create coupon $coupon = WC_Helper_Coupon::create_coupon(); @@ -148,7 +148,7 @@ class WC_Tests_Coupon extends WC_Unit_Test_Case { } // Add product to cart - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); // Add coupon WC()->cart->add_discount( $coupon->get_code() ); @@ -177,7 +177,7 @@ class WC_Tests_Coupon extends WC_Unit_Test_Case { WC_Helper_Coupon::delete_coupon( $coupon->get_id() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -189,8 +189,8 @@ class WC_Tests_Coupon extends WC_Unit_Test_Case { // Create product $product = WC_Helper_Product::create_simple_product(); - update_post_meta( $product->id, '_price', '10' ); - update_post_meta( $product->id, '_regular_price', '10' ); + update_post_meta( $product->get_id(), '_price', '10' ); + update_post_meta( $product->get_id(), '_regular_price', '10' ); // Create coupon $coupon = WC_Helper_Coupon::create_coupon(); @@ -209,7 +209,7 @@ class WC_Tests_Coupon extends WC_Unit_Test_Case { WC_Helper_Fee::add_cart_fee(); // Add product to cart - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); // Add coupon WC()->cart->add_discount( $coupon->get_code() ); @@ -241,7 +241,7 @@ class WC_Tests_Coupon extends WC_Unit_Test_Case { WC_Helper_Coupon::delete_coupon( $coupon->get_id() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -253,8 +253,8 @@ class WC_Tests_Coupon extends WC_Unit_Test_Case { // Create product $product = WC_Helper_Product::create_simple_product(); - update_post_meta( $product->id, '_price', '10' ); - update_post_meta( $product->id, '_regular_price', '10' ); + update_post_meta( $product->get_id(), '_price', '10' ); + update_post_meta( $product->get_id(), '_regular_price', '10' ); // Create coupon $coupon = WC_Helper_Coupon::create_coupon(); @@ -273,7 +273,7 @@ class WC_Tests_Coupon extends WC_Unit_Test_Case { WC_Helper_Fee::add_cart_fee(); // Add product to cart - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); // Add coupon WC()->cart->add_discount( $coupon->get_code() ); @@ -305,6 +305,6 @@ class WC_Tests_Coupon extends WC_Unit_Test_Case { WC_Helper_Coupon::delete_coupon( $coupon->get_id() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } } diff --git a/tests/unit-tests/customer/customer.php b/tests/unit-tests/customer/customer.php index 66cc0135d60..b06569b71e6 100644 --- a/tests/unit-tests/customer/customer.php +++ b/tests/unit-tests/customer/customer.php @@ -22,7 +22,7 @@ class WC_Tests_Customer extends WC_Unit_Test_Case { // Create dummy product, and add the product to the cart. $product = WC_Helper_Product::create_simple_product(); - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); // Customer is going with the Local Pickup option, and the store calculates tax based on the store location. WC_Helper_Customer::set_chosen_shipping_methods( array( 'local_pickup' ) ); @@ -53,7 +53,7 @@ class WC_Tests_Customer extends WC_Unit_Test_Case { WC()->cart->empty_cart(); // Clean up product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -71,7 +71,7 @@ class WC_Tests_Customer extends WC_Unit_Test_Case { // Create dummy product, and add the product to the cart. $product = WC_Helper_Product::create_simple_product(); - WC()->cart->add_to_cart( $product->id, 1 ); + WC()->cart->add_to_cart( $product->get_id(), 1 ); // Customer is going with the Local Pickup option, and the store calculates tax based on the store location. WC_Helper_Customer::set_chosen_shipping_methods( array( 'local_pickup' ) ); @@ -102,6 +102,6 @@ class WC_Tests_Customer extends WC_Unit_Test_Case { WC()->cart->empty_cart(); // Clean up product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } } diff --git a/tests/unit-tests/product/crud.php b/tests/unit-tests/product/crud.php index 82affe430b2..a9fee6cd5ec 100644 --- a/tests/unit-tests/product/crud.php +++ b/tests/unit-tests/product/crud.php @@ -72,42 +72,42 @@ class WC_Tests_Product_CRUD extends WC_Unit_Test_Case { */ public function test_product_getters_and_setters() { $getters_and_setters = array( - 'name' => 'Test', - 'slug' => 'test', - 'status' => 'publish', - 'catalog_visibility' => 'search', - 'featured' => false, - 'description' => 'Hello world', - 'short_description' => 'hello', - 'sku' => 'TEST SKU', - 'regular_price' => 15.00, - 'sale_price' => 10.00, - 'date_on_sale_from' => '1475798400', - 'date_on_sale_to' => '1477267200', - 'total_sales' => 20, - 'tax_status' => 'none', - 'tax_class' => '', - 'manage_stock' => true, - 'stock_quantity' => 10, - 'stock_status' => 'instock', - 'backorders' => 'notify', - 'sold_individually' => false, - 'weight' => 100, - 'length' => 10, - 'width' => 10, - 'height' => 10, - 'upsell_ids' => array( 2, 3 ), - 'cross_sell_ids' => array( 4, 5 ), - 'parent_id' => 0, - 'reviews_allowed' => true, - 'default_attributes' => array(), - 'purchase_note' => 'A note', - 'menu_order' => 2, - 'gallery_attachment_ids' => array(), - 'download_type' => 'standard', - 'download_expiry' => -1, - 'download_limit' => 5, - 'thumbnail_id' => 2, + 'name' => 'Test', + 'slug' => 'test', + 'status' => 'publish', + 'catalog_visibility' => 'search', + 'featured' => false, + 'description' => 'Hello world', + 'short_description' => 'hello', + 'sku' => 'TEST SKU', + 'regular_price' => 15.00, + 'sale_price' => 10.00, + 'date_on_sale_from' => '1475798400', + 'date_on_sale_to' => '1477267200', + 'total_sales' => 20, + 'tax_status' => 'none', + 'tax_class' => '', + 'manage_stock' => true, + 'stock_quantity' => 10, + 'stock_status' => 'instock', + 'backorders' => 'notify', + 'sold_individually' => false, + 'weight' => 100, + 'length' => 10, + 'width' => 10, + 'height' => 10, + 'upsell_ids' => array( 2, 3 ), + 'cross_sell_ids' => array( 4, 5 ), + 'parent_id' => 0, + 'reviews_allowed' => true, + 'default_attributes' => array(), + 'purchase_note' => 'A note', + 'menu_order' => 2, + 'gallery_image_ids' => array(), + 'download_type' => 'standard', + 'download_expiry' => -1, + 'download_limit' => 5, + 'image_id' => 2, ); $product = new WC_Product; foreach ( $getters_and_setters as $function => $value ) { diff --git a/tests/unit-tests/product/functions.php b/tests/unit-tests/product/functions.php index 50cb5b0a3af..78ebf1809ce 100644 --- a/tests/unit-tests/product/functions.php +++ b/tests/unit-tests/product/functions.php @@ -134,8 +134,6 @@ class WC_Tests_Product_Functions extends WC_Unit_Test_Case { update_post_meta( $product->get_id(), '_manage_stock', 'yes' ); wc_update_product_stock( $product->get_id(), 5 ); - - $product = new WC_Product_Simple( $product->get_id() ); $this->assertEquals( 5, $product->get_stock_quantity() ); // Delete Product diff --git a/tests/unit-tests/product/product-simple.php b/tests/unit-tests/product/product-simple.php index 782eb5117f3..5d682297876 100644 --- a/tests/unit-tests/product/product-simple.php +++ b/tests/unit-tests/product/product-simple.php @@ -22,7 +22,7 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { $this->assertEquals( __( 'Read more', 'woocommerce' ), $product->add_to_cart_text() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -37,7 +37,7 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { $this->assertEquals( __( 'Add to cart', 'woocommerce' ), $product->single_add_to_cart_text() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -52,7 +52,7 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { $this->assertEquals( 'Dummy Product', $product->get_title() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -64,10 +64,10 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { // Create product $product = WC_Helper_Product::create_simple_product(); - $this->assertEquals( get_permalink( $product->id ), $product->get_permalink() ); + $this->assertEquals( get_permalink( $product->get_id() ), $product->get_permalink() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -82,7 +82,7 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { $this->assertEquals( $product->sku, $product->get_sku() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -101,25 +101,7 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { $this->assertEquals( 0, $product->get_stock_quantity() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); - } - - /** - * Test get_total_stock(). - * - * @since 2.3 - */ - public function test_get_total_stock() { - // Create product - $product = WC_Helper_Product::create_simple_product(); - - $this->assertEmpty( $product->get_total_stock() ); - - $product->manage_stock = 'yes'; - $this->assertEquals( 0, $product->get_total_stock() ); - - // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -137,7 +119,7 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { $this->assertEquals( 5, $product->set_stock( 3, 'add' ) ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -154,7 +136,7 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { $this->assertEquals( 2, $product->reduce_stock( 3 ) ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -171,7 +153,7 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { $this->assertEquals( 8, $product->increase_stock( 3 ) ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -189,7 +171,7 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { $this->assertFalse( $product->is_type( 'external' ) ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -210,7 +192,7 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { $this->assertFalse( $product->is_downloadable() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -231,7 +213,7 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { $this->assertFalse( $product->is_virtual() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -250,7 +232,7 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { $this->assertTrue( $product->needs_shipping() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -269,7 +251,7 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { $this->assertFalse( $product->is_sold_individually() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -291,7 +273,7 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { $this->assertFalse( $product->backorders_allowed() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } /** @@ -318,6 +300,6 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case { $this->assertFalse( $product->backorders_require_notification() ); // Delete product - WC_Helper_Product::delete_product( $product->id ); + WC_Helper_Product::delete_product( $product->get_id() ); } } From 674a203487d48c90d9eff3b2cb555cef39b9fd2d Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Wed, 2 Nov 2016 03:08:47 -0700 Subject: [PATCH 052/163] Implement CRUD in the legacy REST API --- includes/abstracts/abstract-wc-product.php | 6 +- .../api/class-wc-rest-products-controller.php | 34 +- .../api/legacy/v1/class-wc-api-products.php | 69 ++- .../api/legacy/v2/class-wc-api-products.php | 491 ++++++++--------- .../api/legacy/v3/class-wc-api-products.php | 512 +++++++++--------- includes/class-wc-product-grouped.php | 8 +- includes/wc-product-functions.php | 45 +- tests/unit-tests/product/functions.php | 11 +- 8 files changed, 562 insertions(+), 614 deletions(-) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 3510bdbfd75..173d08c8420 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -1947,8 +1947,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { return $attribute_object->is_taxonomy() ? implode( ', ', wc_get_product_terms( $this->get_id(), $attribute_object->get_name(), array( 'fields' => 'names' ) ) ) : wc_implode_text_attributes( $attribute_object->get_options() ); } - - /* |-------------------------------------------------------------------------- | @todo download functions @@ -1975,9 +1973,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return array */ public function get_files() { - - $downloadable_files = array_filter( isset( $this->downloadable_files ) ? (array) maybe_unserialize( $this->downloadable_files ) : array() ); - + $downloadable_files = array_filter( ! empty( $this->get_downloads() ) ? (array) maybe_unserialize( $this->get_downloads() ) : array() ); if ( ! empty( $downloadable_files ) ) { foreach ( $downloadable_files as $key => $file ) { diff --git a/includes/api/class-wc-rest-products-controller.php b/includes/api/class-wc-rest-products-controller.php index 1c253bb60d3..2337af37250 100644 --- a/includes/api/class-wc-rest-products-controller.php +++ b/includes/api/class-wc-rest-products-controller.php @@ -977,8 +977,6 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { // 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', $product->get_id(), $variation_id, $files ); - $id = ( 0 === $variation_id ) ? $product->get_id() : $variation_id; - $product->set_downloads( $files ); return $product; @@ -1016,9 +1014,6 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { protected function save_product_meta( $product, $request ) { global $wpdb; - // Default total sales. - $product->set_total_sales( 0 ); // @todo check this - // Virtual. if ( isset( $request['virtual'] ) ) { $product->set_virtual( true === $request['virtual'] ? 'yes' : 'no' ); @@ -1114,7 +1109,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { $attribute_object = new WC_Product_Attribute(); $attribute_object->set_id( $attribute_id ); $attribute_object->set_name( $attribute_name ); - $attribute_object->set_options( array() ); + $attribute_object->set_options( $values ); $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); @@ -1129,7 +1124,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { } $attribute_object = new WC_Product_Attribute(); $attribute_object->set_name( $attribute_name ); - $attribute_object->set_options( $values); + $attribute_object->set_options( $values ); $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); @@ -1169,15 +1164,13 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { if ( isset( $request['date_on_sale_from'] ) ) { $date_from = $request['date_on_sale_from']; } else { - $date_from = $product->get_date_on_sale_from(); - $date_from = ( '' === (string) $date_from ) ? '' : date( 'Y-m-d', $date_from ); + $date_from = ( $product->get_date_on_sale_from() ) ? date( 'Y-m-d', $date_from ) : ''; } if ( isset( $request['date_on_sale_to'] ) ) { $date_to = $request['date_on_sale_to']; } else { - $date_to = $product->get_date_on_sale_to(); - $date_to = ( '' === (string) $date_to ) ? '' : date( 'Y-m-d', $date_to ); + $date_to = ( $product->get_date_on_sale_to() ) ? date( 'Y-m-d', $date_to ) : ''; } if ( $date_to && ! $date_from ) { @@ -1194,18 +1187,21 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { } // Product parent ID for groups. - $parent_id = 0; if ( isset( $request['parent_id'] ) ) { $product->set_parent_id( absint( $request['parent_id'] ) ); } // Update parent if grouped so price sorting works and stays in sync with the cheapest child. - if ( $parent_id > 0 || 'grouped' === $product->get_type() ) { + if ( $product->get_parent_id() > 0 || 'grouped' === $product->get_type() ) { $clear_parent_ids = array(); - if ( $parent_id > 0 ) { - $clear_parent_ids[] = $parent_id; + if ( $product->get_parent_id() > 0 ) { + $clear_parent_ids[] = $product->get_parent_id(); + $parent = wc_get_product( $product->get_parent_id() ); + $children = $parent->get_children(); + $parent->set_children( array_filter( array_merge( $children, array( $product->get_id() ) ) ) ); + $parent->save(); } if ( 'grouped' === $product->get_type() ) { @@ -1773,7 +1769,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { // Save variations. if ( isset( $request['type'] ) && 'variable' === $request['type'] && isset( $request['variations'] ) && is_array( $request['variations'] ) ) { - $product = $this->save_variations_data( $product, $request ); + $this->save_variations_data( $product, $request ); } return true; @@ -1805,7 +1801,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { // Save variations. if ( $product->is_type( 'variable' ) ) { if ( isset( $request['variations'] ) && is_array( $request['variations'] ) ) { - $product = $this->save_variations_data( $product, $request ); + $this->save_variations_data( $product, $request ); } else { // Just sync variations. WC_Product_Variable::sync( $product->get_id() ); @@ -1887,7 +1883,6 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { // If we're forcing, then delete permanently. if ( $force ) { - if ( $product->is_type( 'variable' ) ) { foreach ( $product->get_children() as $child_id ) { $child = wc_get_product( $child_id ); @@ -1901,9 +1896,8 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { } } - $result = wp_delete_post( $id, true ); $product->delete(); - $result = $product->get_id() > 0 ? fase : true; + $result = $product->get_id() > 0 ? false : true; } else { // If we don't support trashing for this type, error out. if ( ! $supports_trash ) { diff --git a/includes/api/legacy/v1/class-wc-api-products.php b/includes/api/legacy/v1/class-wc-api-products.php index 65afe0d9ea4..157481faa1d 100644 --- a/includes/api/legacy/v1/class-wc-api-products.php +++ b/includes/api/legacy/v1/class-wc-api-products.php @@ -8,7 +8,7 @@ * @category API * @package WooCommerce/API * @since 2.1 - * @version 2.1 + * @version 2.7 */ if ( ! defined( 'ABSPATH' ) ) { @@ -113,14 +113,12 @@ class WC_API_Products extends WC_API_Resource { // add variations to variable products if ( $product->is_type( 'variable' ) && $product->has_child() ) { - $product_data['variations'] = $this->get_variation_data( $product ); } // add the parent product data to an individual variation if ( $product->is_type( 'variation' ) ) { - - $product_data['parent'] = $this->get_product_data( $product->parent ); + $product_data['parent'] = $this->get_product_data( $product->get_parent_id() ); } return array( 'product' => apply_filters( 'woocommerce_api_product_response', $product_data, $product, $fields, $this->server ) ); @@ -268,14 +266,14 @@ class WC_API_Products extends WC_API_Resource { * @return array */ private function get_product_data( $product ) { - + $post = get_post( $product->get_id() ); return array( 'title' => $product->get_title(), 'id' => (int) $product->is_type( 'variation' ) ? $product->get_variation_id() : $product->get_id(), - 'created_at' => $this->server->format_datetime( $product->get_post_data()->post_date_gmt ), - 'updated_at' => $this->server->format_datetime( $product->get_post_data()->post_modified_gmt ), - 'type' => $product->product_type, - 'status' => $product->get_post_data()->post_status, + 'created_at' => $this->server->format_datetime( $post->post_date_gmt ), + 'updated_at' => $this->server->format_datetime( $post->post_modified_gmt ), + 'type' => $product->get_type(), + 'status' => $product->get_status(), 'downloadable' => $product->is_downloadable(), 'virtual' => $product->is_virtual(), 'permalink' => $product->get_permalink(), @@ -296,37 +294,37 @@ class WC_API_Products extends WC_API_Resource { 'purchaseable' => $product->is_purchasable(), 'featured' => $product->is_featured(), 'visible' => $product->is_visible(), - 'catalog_visibility' => $product->visibility, + 'catalog_visibility' => $product->get_catalog_visibility(), 'on_sale' => $product->is_on_sale(), 'weight' => $product->get_weight() ? wc_format_decimal( $product->get_weight(), 2 ) : null, 'dimensions' => array( - 'length' => $product->length, - 'width' => $product->width, - 'height' => $product->height, + 'length' => $product->get_length(), + 'width' => $product->get_width(), + 'height' => $product->get_height(), 'unit' => get_option( 'woocommerce_dimension_unit' ), ), 'shipping_required' => $product->needs_shipping(), 'shipping_taxable' => $product->is_shipping_taxable(), 'shipping_class' => $product->get_shipping_class(), 'shipping_class_id' => ( 0 !== $product->get_shipping_class_id() ) ? $product->get_shipping_class_id() : null, - 'description' => apply_filters( 'the_content', $product->get_post_data()->post_content ), - 'short_description' => apply_filters( 'woocommerce_short_description', $product->get_post_data()->post_excerpt ), - 'reviews_allowed' => ( 'open' === $product->get_post_data()->comment_status ), + 'description' => apply_filters( 'the_content', $product->get_description() ), + 'short_description' => apply_filters( 'woocommerce_short_description', $product->get_short_description() ), + 'reviews_allowed' => $product->get_reviews_allowed(), 'average_rating' => wc_format_decimal( $product->get_average_rating(), 2 ), 'rating_count' => (int) $product->get_rating_count(), - 'related_ids' => array_map( 'absint', array_values( $product->get_related() ) ), - 'upsell_ids' => array_map( 'absint', $product->get_upsells() ), - 'cross_sell_ids' => array_map( 'absint', $product->get_cross_sells() ), + 'related_ids' => array_map( 'absint', array_values( wc_get_related_products( $product->get_id() ) ) ), + 'upsell_ids' => array_map( 'absint', $product->get_upsell_ids() ), + 'cross_sell_ids' => array_map( 'absint', $product->get_cross_sell_ids() ), 'categories' => wp_get_post_terms( $product->get_id(), 'product_cat', array( 'fields' => 'names' ) ), 'tags' => wp_get_post_terms( $product->get_id(), 'product_tag', array( 'fields' => 'names' ) ), 'images' => $this->get_images( $product ), 'featured_src' => wp_get_attachment_url( get_post_thumbnail_id( $product->is_type( 'variation' ) ? $product->variation_id : $product->get_id() ) ), 'attributes' => $this->get_attributes( $product ), 'downloads' => $this->get_downloads( $product ), - 'download_limit' => (int) $product->download_limit, - 'download_expiry' => (int) $product->download_expiry, - 'download_type' => $product->download_type, - 'purchase_note' => apply_filters( 'the_content', $product->purchase_note ), + 'download_limit' => (int) $product->get_download_limit(), + 'download_expiry' => (int) $product->get_download_expiry(), + 'download_type' => 'standard', + 'purchase_note' => apply_filters( 'the_content', $product->get_purchase_note() ), 'total_sales' => metadata_exists( 'post', $product->get_id(), 'total_sales' ) ? (int) get_post_meta( $product->get_id(), 'total_sales', true ) : 0, 'variations' => array(), 'parent' => array(), @@ -335,6 +333,7 @@ class WC_API_Products extends WC_API_Resource { /** * Get an individual variation's data + * @todo after variations CRUD is done * * @since 2.1 * @param WC_Product $product @@ -346,7 +345,7 @@ class WC_API_Products extends WC_API_Resource { foreach ( $product->get_children() as $child_id ) { - $variation = $product->get_child( $child_id ); + $variation = wc_get_product( $child_id ); if ( ! $variation->exists() ) { continue; @@ -376,9 +375,9 @@ class WC_API_Products extends WC_API_Resource { 'on_sale' => $variation->is_on_sale(), 'weight' => $variation->get_weight() ? wc_format_decimal( $variation->get_weight(), 2 ) : null, 'dimensions' => array( - 'length' => $variation->length, - 'width' => $variation->width, - 'height' => $variation->height, + 'length' => $variation->get_length(), + 'width' => $variation->get_width(), + 'height' => $variation->get_height(), 'unit' => get_option( 'woocommerce_dimension_unit' ), ), 'shipping_class' => $variation->get_shipping_class(), @@ -386,8 +385,8 @@ class WC_API_Products extends WC_API_Resource { 'image' => $this->get_images( $variation ), 'attributes' => $this->get_attributes( $variation ), 'downloads' => $this->get_downloads( $variation ), - 'download_limit' => (int) $product->download_limit, - 'download_expiry' => (int) $product->download_expiry, + 'download_limit' => (int) $product->get_download_limit(), + 'download_expiry' => (int) $product->get_download_expiry(), ); } @@ -406,22 +405,22 @@ class WC_API_Products extends WC_API_Resource { $images = $attachment_ids = array(); if ( $product->is_type( 'variation' ) ) { - + // @todo variation if ( has_post_thumbnail( $product->get_variation_id() ) ) { // add variation image if set $attachment_ids[] = get_post_thumbnail_id( $product->get_variation_id() ); - } elseif ( has_post_thumbnail( $product->get_id() ) ) { + } elseif ( ! empty( $product->get_thumbnail_id() ) ) { // otherwise use the parent product featured image if set - $attachment_ids[] = get_post_thumbnail_id( $product->get_id() ); + $attachment_ids[] = $product->get_thumbnail_id(); } } else { - // add featured image - if ( has_post_thumbnail( $product->get_id() ) ) { - $attachment_ids[] = get_post_thumbnail_id( $product->get_id() ); + // Add featured image + if ( ! empty( $product->get_thumbnail_id() ) ) { + $attachment_ids[] = $product->get_thumbnail_id(); } // add gallery images diff --git a/includes/api/legacy/v2/class-wc-api-products.php b/includes/api/legacy/v2/class-wc-api-products.php index 7a34cf14f43..28c96a8e67e 100644 --- a/includes/api/legacy/v2/class-wc-api-products.php +++ b/includes/api/legacy/v2/class-wc-api-products.php @@ -8,6 +8,7 @@ * @category API * @package WooCommerce/API * @since 2.1 + * @version 2.7 */ if ( ! defined( 'ABSPATH' ) ) { @@ -156,14 +157,13 @@ class WC_API_Products extends WC_API_Resource { // add variations to variable products if ( $product->is_type( 'variable' ) && $product->has_child() ) { - $product_data['variations'] = $this->get_variation_data( $product ); } // add the parent product data to an individual variation - if ( $product->is_type( 'variation' ) && $product->parent ) { - - $product_data['parent'] = $this->get_product_data( $product->parent ); + if ( $product->is_type( 'variation' ) && $product->get_parent_id() ) { + $_product = wc_get_product( $product->get_parent_id() ); + $product_data['parent'] = $this->get_product_data( $_product ); } return array( 'product' => apply_filters( 'woocommerce_api_product_response', $product_data, $product, $fields, $this->server ) ); @@ -252,34 +252,38 @@ class WC_API_Products extends WC_API_Resource { $post_excerpt = $data['short_description']; } - $new_product = array( - 'post_title' => wc_clean( $data['title'] ), - 'post_status' => ( isset( $data['status'] ) ? wc_clean( $data['status'] ) : 'publish' ), - 'post_type' => 'product', - 'post_excerpt' => ( isset( $data['short_description'] ) ? $post_excerpt : '' ), - 'post_content' => ( isset( $data['description'] ) ? $post_content : '' ), - 'post_author' => get_current_user_id(), - ); + $classname = WC_Product_Factory::get_classname_from_product_type( $data['type'] ); + if ( ! class_exists( $classname ) ) { + $classname = 'WC_Product_Simple'; + } + $product = new $classname(); - // Attempts to create the new product - $id = wp_insert_post( $new_product, true ); + $product->set_name( wc_clean( $data['title'] ) ); + $product->set_status( isset( $data['status'] ) ? wc_clean( $data['status'] ) : 'publish' ); + $product->set_short_description( isset( $data['short_description'] ) ? $post_excerpt : '' ); + $product->set_description( isset( $data['description'] ) ? $post_content : '' ); + + // Attempts to create the new product. + $product->create(); + $id = $product->get_id(); // Checks for an error in the product creation - if ( is_wp_error( $id ) ) { + if ( ! $id > 0 ) { throw new WC_API_Exception( 'woocommerce_api_cannot_create_product', $id->get_error_message(), 400 ); } // Check for featured/gallery images, upload it and set it if ( isset( $data['images'] ) ) { - $this->save_product_images( $id, $data['images'] ); + $product = $this->save_product_images( $product, $data['images'] ); } // Save product meta fields - $this->save_product_meta( $id, $data ); + $product = $this->save_product_meta( $product, $data ); + $product->save(); // Save variations if ( isset( $data['type'] ) && 'variable' == $data['type'] && isset( $data['variations'] ) && is_array( $data['variations'] ) ) { - $this->save_variations( $id, $data ); + $this->save_variations( $product, $data ); } do_action( 'woocommerce_api_create_product', $id, $data ); @@ -320,37 +324,37 @@ class WC_API_Products extends WC_API_Resource { return $id; } + $product = wc_get_product( $id ); + $data = apply_filters( 'woocommerce_api_edit_product_data', $data, $this ); // Product title. if ( isset( $data['title'] ) ) { - wp_update_post( array( 'ID' => $id, 'post_title' => wc_clean( $data['title'] ) ) ); + $product->set_name( wc_clean( $data['title'] ) ); } // Product name (slug). if ( isset( $data['name'] ) ) { - wp_update_post( array( 'ID' => $id, 'post_name' => sanitize_title( $data['name'] ) ) ); + $product->set_slug( wc_clean( $data['name'] ) ); } // Product status. if ( isset( $data['status'] ) ) { - wp_update_post( array( 'ID' => $id, 'post_status' => wc_clean( $data['status'] ) ) ); + $product->set_status( wc_clean( $data['status'] ) ); } // Product short description. if ( isset( $data['short_description'] ) ) { // Enable short description html tags. $post_excerpt = ( isset( $data['enable_html_short_description'] ) && true === $data['enable_html_short_description'] ) ? $data['short_description'] : wc_clean( $data['short_description'] ); - - wp_update_post( array( 'ID' => $id, 'post_excerpt' => $post_excerpt ) ); + $product->set_short_description( $post_excerpt ); } // Product description. if ( isset( $data['description'] ) ) { // Enable description html tags. $post_content = ( isset( $data['enable_html_description'] ) && true === $data['enable_html_description'] ) ? $data['description'] : wc_clean( $data['description'] ); - - wp_update_post( array( 'ID' => $id, 'post_content' => $post_content ) ); + $product->set_description( $post_content ); } // Validate the product type @@ -360,17 +364,19 @@ class WC_API_Products extends WC_API_Resource { // Check for featured/gallery images, upload it and set it if ( isset( $data['images'] ) ) { - $this->save_product_images( $id, $data['images'] ); + $product = $this->save_product_images( $product, $data['images'] ); } // Save product meta fields - $this->save_product_meta( $id, $data ); + $product = $this->save_product_meta( $product, $data ); + + $product->save(); // Save variations $product = get_product( $id ); if ( $product->is_type( 'variable' ) ) { if ( isset( $data['variations'] ) && is_array( $data['variations'] ) ) { - $this->save_variations( $id, $data ); + $this->save_variations( $product, $data ); } else { // Just sync variations WC_Product_Variable::sync( $id ); @@ -405,30 +411,27 @@ class WC_API_Products extends WC_API_Resource { return $id; } + $product = wc_get_product( $id ); + do_action( 'woocommerce_api_delete_product', $id, $this ); // If we're forcing, then delete permanently. if ( $force ) { - $child_product_variations = get_children( 'post_parent=' . $id . '&post_type=product_variation' ); - - if ( ! empty( $child_product_variations ) ) { - foreach ( $child_product_variations as $child ) { - wp_delete_post( $child->ID, true ); + if ( $product->is_type( 'variable' ) ) { + foreach ( $product->get_children() as $child_id ) { + $child = wc_get_product( $child_id ); + $child->delete(); + } + } elseif ( $product->is_type( 'grouped' ) ) { + foreach ( $product->get_children() as $child_id ) { + $child = wc_get_product( $child_id ); + $child->set_parent_id( 0 ); + $child->save(); } } - $child_products = get_children( 'post_parent=' . $id . '&post_type=product' ); - - if ( ! empty( $child_products ) ) { - foreach ( $child_products as $child ) { - $child_post = array(); - $child_post['ID'] = $child->ID; - $child_post['post_parent'] = 0; - wp_update_post( $child_post ); - } - } - - $result = wp_delete_post( $id, true ); + $product->delete(); + $result = $product->get_id() > 0 ? false : true; } else { $result = wp_trash_post( $id ); } @@ -676,14 +679,14 @@ class WC_API_Products extends WC_API_Resource { */ private function get_product_data( $product ) { $prices_precision = wc_get_price_decimals(); - + $post = get_post( $product->get_id() ); return array( 'title' => $product->get_title(), 'id' => (int) $product->is_type( 'variation' ) ? $product->get_variation_id() : $product->get_id(), - 'created_at' => $this->server->format_datetime( $product->get_post_data()->post_date_gmt ), - 'updated_at' => $this->server->format_datetime( $product->get_post_data()->post_modified_gmt ), - 'type' => $product->product_type, - 'status' => $product->get_post_data()->post_status, + 'created_at' => $this->server->format_datetime( $post->post_date_gmt ), + 'updated_at' => $this->server->format_datetime( $post->post_modified_gmt ), + 'type' => $product->get_type(), + 'status' => $product->get_status(), 'downloadable' => $product->is_downloadable(), 'virtual' => $product->is_virtual(), 'permalink' => $product->get_permalink(), @@ -704,41 +707,41 @@ class WC_API_Products extends WC_API_Resource { 'purchaseable' => $product->is_purchasable(), 'featured' => $product->is_featured(), 'visible' => $product->is_visible(), - 'catalog_visibility' => $product->visibility, + 'catalog_visibility' => $product->get_catalog_visibility(), 'on_sale' => $product->is_on_sale(), 'product_url' => $product->is_type( 'external' ) ? $product->get_product_url() : '', 'button_text' => $product->is_type( 'external' ) ? $product->get_button_text() : '', 'weight' => $product->get_weight() ? wc_format_decimal( $product->get_weight(), 2 ) : null, 'dimensions' => array( - 'length' => $product->length, - 'width' => $product->width, - 'height' => $product->height, + 'length' => $product->get_length(), + 'width' => $product->get_width(), + 'height' => $product->get_height(), 'unit' => get_option( 'woocommerce_dimension_unit' ), ), 'shipping_required' => $product->needs_shipping(), 'shipping_taxable' => $product->is_shipping_taxable(), 'shipping_class' => $product->get_shipping_class(), 'shipping_class_id' => ( 0 !== $product->get_shipping_class_id() ) ? $product->get_shipping_class_id() : null, - 'description' => wpautop( do_shortcode( $product->get_post_data()->post_content ) ), - 'short_description' => apply_filters( 'woocommerce_short_description', $product->get_post_data()->post_excerpt ), - 'reviews_allowed' => ( 'open' === $product->get_post_data()->comment_status ), + 'description' => wpautop( do_shortcode( $product->get_description() ) ), + 'short_description' => apply_filters( 'woocommerce_short_description', $product->get_short_description() ), + 'reviews_allowed' => $product->get_reviews_allowed(), 'average_rating' => wc_format_decimal( $product->get_average_rating(), 2 ), 'rating_count' => (int) $product->get_rating_count(), - 'related_ids' => array_map( 'absint', array_values( $product->get_related() ) ), - 'upsell_ids' => array_map( 'absint', $product->get_upsells() ), - 'cross_sell_ids' => array_map( 'absint', $product->get_cross_sells() ), - 'parent_id' => $product->post->post_parent, + 'related_ids' => array_map( 'absint', array_values( wc_get_related_products( $product->get_id() ) ) ), + 'upsell_ids' => array_map( 'absint', $product->get_upsell_ids() ), + 'cross_sell_ids' => array_map( 'absint', $product->get_cross_sell_ids() ), + 'parent_id' => $product->get_parent_id(), 'categories' => wp_get_post_terms( $product->get_id(), 'product_cat', array( 'fields' => 'names' ) ), 'tags' => wp_get_post_terms( $product->get_id(), 'product_tag', array( 'fields' => 'names' ) ), 'images' => $this->get_images( $product ), 'featured_src' => (string) wp_get_attachment_url( get_post_thumbnail_id( $product->is_type( 'variation' ) ? $product->variation_id : $product->get_id() ) ), 'attributes' => $this->get_attributes( $product ), 'downloads' => $this->get_downloads( $product ), - 'download_limit' => (int) $product->download_limit, - 'download_expiry' => (int) $product->download_expiry, - 'download_type' => $product->download_type, - 'purchase_note' => wpautop( do_shortcode( wp_kses_post( $product->purchase_note ) ) ), - 'total_sales' => metadata_exists( 'post', $product->get_id(), 'total_sales' ) ? (int) get_post_meta( $product->get_id(), 'total_sales', true ) : 0, + 'download_limit' => (int) $product->get_download_limit(), + 'download_expiry' => (int) $product->get_download_expiry(), + 'download_type' => 'standard', + 'purchase_note' => wpautop( do_shortcode( wp_kses_post( $product->get_purchase_note() ) ) ), + 'total_sales' => $product->get_total_sales(), 'variations' => array(), 'parent' => array(), ); @@ -746,6 +749,7 @@ class WC_API_Products extends WC_API_Resource { /** * Get an individual variation's data + * @todo after variations CRUD is done * * @since 2.1 * @param WC_Product $product @@ -757,7 +761,7 @@ class WC_API_Products extends WC_API_Resource { foreach ( $product->get_children() as $child_id ) { - $variation = $product->get_child( $child_id ); + $variation = wc_get_product( $child_id ); if ( ! $variation->exists() ) { continue; @@ -798,8 +802,8 @@ class WC_API_Products extends WC_API_Resource { 'image' => $this->get_images( $variation ), 'attributes' => $this->get_attributes( $variation ), 'downloads' => $this->get_downloads( $variation ), - 'download_limit' => (int) $product->download_limit, - 'download_expiry' => (int) $product->download_expiry, + 'download_limit' => (int) $product->get_download_limit(), + 'download_expiry' => (int) $product->get_download_expiry(), ); } @@ -810,80 +814,64 @@ class WC_API_Products extends WC_API_Resource { * Save product meta * * @since 2.2 - * @param int $product_id + * @param WC_Product $product * @param array $data - * @return bool + * @return WC_Product * @throws WC_API_Exception */ - protected function save_product_meta( $product_id, $data ) { + protected function save_product_meta( $product, $data ) { global $wpdb; - // Product Type - $product_type = null; - if ( isset( $data['type'] ) ) { - $product_type = wc_clean( $data['type'] ); - wp_set_object_terms( $product_id, $product_type, 'product_type' ); - } else { - $_product_type = get_the_terms( $product_id, 'product_type' ); - if ( is_array( $_product_type ) ) { - $_product_type = current( $_product_type ); - $product_type = $_product_type->slug; - } - } - - // Default total sales. - add_post_meta( $product_id, 'total_sales', '0', true ); - // Virtual if ( isset( $data['virtual'] ) ) { - update_post_meta( $product_id, '_virtual', ( true === $data['virtual'] ) ? 'yes' : 'no' ); + $product->set_virtual( $data['virtual'] ); } // Tax status if ( isset( $data['tax_status'] ) ) { - update_post_meta( $product_id, '_tax_status', wc_clean( $data['tax_status'] ) ); + $product->set_tax_status( wc_clean( $data['tax_status'] ) ); } // Tax Class if ( isset( $data['tax_class'] ) ) { - update_post_meta( $product_id, '_tax_class', wc_clean( $data['tax_class'] ) ); + $product->set_tax_class( wc_clean( $data['tax_class'] ) ); } // Catalog Visibility if ( isset( $data['catalog_visibility'] ) ) { - update_post_meta( $product_id, '_visibility', wc_clean( $data['catalog_visibility'] ) ); + $product->set_catalog_visibility( wc_clean( $data['catalog_visibility'] ) ); } // Purchase Note if ( isset( $data['purchase_note'] ) ) { - update_post_meta( $product_id, '_purchase_note', wc_clean( $data['purchase_note'] ) ); + $product->set_purchase_note( wc_clean( $data['purchase_note'] ) ); } // Featured Product if ( isset( $data['featured'] ) ) { - update_post_meta( $product_id, '_featured', ( true === $data['featured'] ) ? 'yes' : 'no' ); + $product->set_featured( $data['featured'] ); } // Shipping data - $this->save_product_shipping_data( $product_id, $data ); + $product = $this->save_product_shipping_data( $product, $data ); // SKU if ( isset( $data['sku'] ) ) { - $sku = get_post_meta( $product_id, '_sku', true ); + $sku = $product->get_sku(); $new_sku = wc_clean( $data['sku'] ); if ( '' == $new_sku ) { - update_post_meta( $product_id, '_sku', '' ); + $product->set_sku( '' ); } elseif ( $new_sku !== $sku ) { if ( ! empty( $new_sku ) ) { - $unique_sku = wc_product_has_unique_sku( $product_id, $new_sku ); + $unique_sku = wc_product_has_unique_sku( $product->get_id(), $new_sku ); if ( ! $unique_sku ) { throw new WC_API_Exception( 'woocommerce_api_product_sku_already_exists', __( 'The SKU already exists on another product', 'woocommerce' ), 400 ); } else { - update_post_meta( $product_id, '_sku', $new_sku ); + $product->set_sku( $new_sku ); } } else { - update_post_meta( $product_id, '_sku', '' ); + $product->set_sku( '' ); } } } @@ -913,6 +901,8 @@ class WC_API_Products extends WC_API_Resource { if ( $is_taxonomy ) { + $attribute_id = wc_attribute_taxonomy_id_by_name( $attribute['name'] ); + if ( isset( $attribute['options'] ) ) { $options = $attribute['options']; @@ -929,56 +919,55 @@ class WC_API_Products extends WC_API_Resource { // Update post terms if ( taxonomy_exists( $taxonomy ) ) { - wp_set_object_terms( $product_id, $values, $taxonomy ); + wp_set_object_terms( $product->get_id(), $values, $taxonomy ); } if ( ! empty( $values ) ) { - // Add attribute to array, but don't set values - $attributes[ $taxonomy ] = array( - 'name' => $taxonomy, - 'value' => '', - 'position' => isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0', - 'is_visible' => ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0, - 'is_variation' => ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0, - 'is_taxonomy' => $is_taxonomy, - ); + // Add attribute to array, but don't set values. + $attribute_object = new WC_Product_Attribute(); + $attribute_object->set_id( $attribute_id ); + $attribute_object->set_name( $taxonomy ); + $attribute_object->set_options( $values ); + $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); + $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); + $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); + $attributes[] = $attribute_object; } } elseif ( isset( $attribute['options'] ) ) { // Array based if ( is_array( $attribute['options'] ) ) { - $values = wc_implode_text_attributes( array_map( 'wc_clean', $attribute['options'] ) ); + $values = $attribute['options']; // Text based, separate by pipe } else { - $values = wc_implode_text_attributes( array_map( 'wc_clean', explode( WC_DELIMITER, $attribute['options'] ) ) ); + $values = array_map( 'wc_clean', explode( WC_DELIMITER, $attribute['options'] ) ); } - // Custom attribute - Add attribute to array and set the values - $attributes[ $attribute_slug ] = array( - 'name' => wc_clean( $attribute['name'] ), - 'value' => $values, - 'position' => isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0', - 'is_visible' => ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0, - 'is_variation' => ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0, - 'is_taxonomy' => $is_taxonomy, - ); + // Custom attribute - Add attribute to array and set the values. + $attribute_object = new WC_Product_Attribute(); + $attribute_object->set_name( $attribute['name'] ); + $attribute_object->set_options( $values ); + $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); + $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); + $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); + $attributes[] = $attribute_object; } } uasort( $attributes, 'wc_product_attribute_uasort_comparison' ); - update_post_meta( $product_id, '_product_attributes', $attributes ); + $product->set_attributes( $attributes ); } // Sales and prices - if ( in_array( $product_type, array( 'variable', 'grouped' ) ) ) { + if ( in_array( $product->get_type(), array( 'variable', 'grouped' ) ) ) { - // Variable and grouped products have no prices - update_post_meta( $product_id, '_regular_price', '' ); - update_post_meta( $product_id, '_sale_price', '' ); - update_post_meta( $product_id, '_sale_price_dates_from', '' ); - update_post_meta( $product_id, '_sale_price_dates_to', '' ); - update_post_meta( $product_id, '_price', '' ); + // Variable and grouped products have no prices. + $product->set_regular_price( '' ); + $product->set_sale_price( '' ); + $product->set_date_on_sale_to( '' ); + $product->set_date_on_sale_from( '' ); + $product->set_price( '' ); } else { @@ -986,86 +975,70 @@ class WC_API_Products extends WC_API_Resource { if ( isset( $data['regular_price'] ) ) { $regular_price = ( '' === $data['regular_price'] ) ? '' : $data['regular_price']; } else { - $regular_price = get_post_meta( $product_id, '_regular_price', true ); + $regular_price = $product->get_regular_price(); } // Sale Price if ( isset( $data['sale_price'] ) ) { $sale_price = ( '' === $data['sale_price'] ) ? '' : $data['sale_price']; } else { - $sale_price = get_post_meta( $product_id, '_sale_price', true ); + $sale_price = $product->get_sale_price(); } + $product->set_regular_price( $regular_price ); + $product->set_sale_price( $sale_price ); + if ( isset( $data['sale_price_dates_from'] ) ) { $date_from = $data['sale_price_dates_from']; } else { - $date_from = get_post_meta( $product_id, '_sale_price_dates_from', true ); - $date_from = ( '' === $date_from ) ? '' : date( 'Y-m-d', $date_from ); + $date_from = ( $product->get_date_on_sale_from() ) ? date( 'Y-m-d', $date_from ) : ''; } if ( isset( $data['sale_price_dates_to'] ) ) { $date_to = $data['sale_price_dates_to']; } else { - $date_to = get_post_meta( $product_id, '_sale_price_dates_to', true ); - $date_to = ( '' === $date_to ) ? '' : date( 'Y-m-d', $date_to ); + $date_to = ( $product->get_date_on_sale_to() ) ? date( 'Y-m-d', $date_to ) : ''; } - _wc_save_product_price( $product_id, $regular_price, $sale_price, $date_from, $date_to ); + if ( $date_to && ! $date_from ) { + $date_from = strtotime( 'NOW', current_time( 'timestamp' ) ); + } + + $product->set_date_on_sale_to( $date_to ); + $product->set_date_on_sale_from( $date_from ); + if ( $product->is_on_sale() ) { + $product->set_price( $product->get_sale_price() ); + } else { + $product->set_price( $product->get_regular_price() ); + } } // Product parent ID for groups if ( isset( $data['parent_id'] ) ) { - wp_update_post( array( 'ID' => $product_id, 'post_parent' => absint( $data['parent_id'] ) ) ); + $product->set_parent_id( absint( $data['parent_id'] ) ); } // Update parent if grouped so price sorting works and stays in sync with the cheapest child - $_product = wc_get_product( $product_id ); - if ( $_product->post->post_parent > 0 || 'grouped' === $product_type ) { - - $clear_parent_ids = array(); - - if ( $_product->post->post_parent > 0 ) { - $clear_parent_ids[] = $_product->post->post_parent; - } - - if ( 'grouped' === $product_type ) { - $clear_parent_ids[] = $product_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 ); - } - } - } + if ( $product->get_parent_id() > 0 || 'grouped' === $product->get_type() ) { + if ( $product->get_parent_id() > 0 ) { + $parent = wc_get_product( $product->get_parent_id() ); + $children = $parent->get_children(); + $parent->set_children( array_filter( array_merge( $children, array( $product->get_id() ) ) ) ); + $parent->save(); } } // Sold Individually if ( isset( $data['sold_individually'] ) ) { - update_post_meta( $product_id, '_sold_individually', ( true === $data['sold_individually'] ) ? 'yes' : '' ); + $product->set_sold_individually( true === $data['sold_individually'] ? 'yes' : '' ); } // Stock status if ( isset( $data['in_stock'] ) ) { $stock_status = ( true === $data['in_stock'] ) ? 'instock' : 'outofstock'; } else { - $stock_status = get_post_meta( $product_id, '_stock_status', true ); + $stock_status = $product->get_stock_status(); if ( '' === $stock_status ) { $stock_status = 'instock'; @@ -1077,9 +1050,9 @@ class WC_API_Products extends WC_API_Resource { // Manage stock if ( isset( $data['managing_stock'] ) ) { $managing_stock = ( true === $data['managing_stock'] ) ? 'yes' : 'no'; - update_post_meta( $product_id, '_manage_stock', $managing_stock ); + $product->set_manage_stock( $managing_stock ); } else { - $managing_stock = get_post_meta( $product_id, '_manage_stock', true ); + $managing_stock = $product->get_manage_stock() ? 'yes' : 'no'; } // Backorders @@ -1090,49 +1063,42 @@ class WC_API_Products extends WC_API_Resource { $backorders = ( true === $data['backorders'] ) ? 'yes' : 'no'; } - update_post_meta( $product_id, '_backorders', $backorders ); + $product->set_backorders( $backorders ); } else { - $backorders = get_post_meta( $product_id, '_backorders', true ); + $backorders = $product->get_backorders(); } - if ( 'grouped' == $product_type ) { - - update_post_meta( $product_id, '_manage_stock', 'no' ); - update_post_meta( $product_id, '_backorders', 'no' ); - update_post_meta( $product_id, '_stock', '' ); - - wc_update_product_stock_status( $product_id, $stock_status ); - - } elseif ( 'external' == $product_type ) { - - update_post_meta( $product_id, '_manage_stock', 'no' ); - update_post_meta( $product_id, '_backorders', 'no' ); - update_post_meta( $product_id, '_stock', '' ); - - wc_update_product_stock_status( $product_id, 'instock' ); + if ( 'grouped' == $product->get_type() ) { + $product->set_manage_stock( 'no' ); + $product->set_backorders( 'no' ); + $product->set_stock( '' ); + $product->set_stock_status( $stock_status ); + } elseif ( 'external' == $product->get_type() ) { + $product->set_manage_stock( 'no' ); + $product->set_backorders( 'no' ); + $product->set_stock( '' ); + $product->set_stock_status( 'instock' ); } elseif ( 'yes' == $managing_stock ) { - update_post_meta( $product_id, '_backorders', $backorders ); + $product->set_backorders( $backorders ); // Stock status is always determined by children so sync later. - if ( 'variable' !== $product_type ) { - wc_update_product_stock_status( $product_id, $stock_status ); + if ( 'variable' !== $product->get_type() ) { + $product->set_stock_status( $stock_status ); } // Stock quantity if ( isset( $data['stock_quantity'] ) ) { - wc_update_product_stock( $product_id, intval( $data['stock_quantity'] ) ); + $product->set_stock( wc_stock_amount( $data['stock_quantity'] ) ); } } else { - - // Don't manage stock - update_post_meta( $product_id, '_manage_stock', 'no' ); - update_post_meta( $product_id, '_backorders', $backorders ); - update_post_meta( $product_id, '_stock', '' ); - - wc_update_product_stock_status( $product_id, $stock_status ); + // Don't manage stock. + $product->set_manage_stock( 'no' ); + $product->set_backorders( $backorders ); + $product->set_stock( '' ); + $product->set_stock_status( $stock_status ); } - } elseif ( 'variable' !== $product_type ) { - wc_update_product_stock_status( $product_id, $stock_status ); + } elseif ( 'variable' !== $product->get_type() ) { + $product->set_stock_status( $stock_status ); } // Upsells @@ -1147,9 +1113,9 @@ class WC_API_Products extends WC_API_Resource { } } - update_post_meta( $product_id, '_upsell_ids', $upsells ); + $product->set_upsell_ids( $upsells ); } else { - delete_post_meta( $product_id, '_upsell_ids' ); + $product->set_upsell_ids( array() ); } } @@ -1165,30 +1131,30 @@ class WC_API_Products extends WC_API_Resource { } } - update_post_meta( $product_id, '_crosssell_ids', $crosssells ); + $product->set_cross_sell_ids( $crosssells ); } else { - delete_post_meta( $product_id, '_crosssell_ids' ); + $product->set_cross_sell_ids( array() ); } } // Product categories if ( isset( $data['categories'] ) && is_array( $data['categories'] ) ) { $term_ids = array_unique( array_map( 'intval', $data['categories'] ) ); - wp_set_object_terms( $product_id, $term_ids, 'product_cat' ); + $product->set_category_ids( $term_ids ); } // Product tags if ( isset( $data['tags'] ) && is_array( $data['tags'] ) ) { $term_ids = array_unique( array_map( 'intval', $data['tags'] ) ); - wp_set_object_terms( $product_id, $term_ids, 'product_tag' ); + $product->set_tag_ids( $term_ids ); } // Downloadable if ( isset( $data['downloadable'] ) ) { $is_downloadable = ( true === $data['downloadable'] ) ? 'yes' : 'no'; - update_post_meta( $product_id, '_downloadable', $is_downloadable ); + $product->set_downloadable( $is_downloadable ); } else { - $is_downloadable = get_post_meta( $product_id, '_downloadable', true ); + $is_downloadable = $product->get_downloadable() ? 'yes' : 'no'; } // Downloadable options @@ -1196,61 +1162,56 @@ class WC_API_Products extends WC_API_Resource { // Downloadable files if ( isset( $data['downloads'] ) && is_array( $data['downloads'] ) ) { - $this->save_downloadable_files( $product_id, $data['downloads'] ); + $product = $this->save_downloadable_files( $product, $data['downloads'] ); } // Download limit if ( isset( $data['download_limit'] ) ) { - update_post_meta( $product_id, '_download_limit', ( '' === $data['download_limit'] ) ? '' : absint( $data['download_limit'] ) ); + $product->set_download_limit( $data['download_limit'] ); } // Download expiry if ( isset( $data['download_expiry'] ) ) { - update_post_meta( $product_id, '_download_expiry', ( '' === $data['download_expiry'] ) ? '' : absint( $data['download_expiry'] ) ); - } - - // Download type - if ( isset( $data['download_type'] ) ) { - update_post_meta( $product_id, '_download_type', wc_clean( $data['download_type'] ) ); + $product->set_download_expiry( $data['download_expiry'] ); } } // Product url - if ( 'external' === $product_type ) { + if ( 'external' === $product->get_type() ) { if ( isset( $data['product_url'] ) ) { - update_post_meta( $product_id, '_product_url', wc_clean( $data['product_url'] ) ); + $product->set_product_url( $data['product_url'] ); } if ( isset( $data['button_text'] ) ) { - update_post_meta( $product_id, '_button_text', wc_clean( $data['button_text'] ) ); + $product->set_button_text( $data['button_text'] ); } } // Reviews allowed if ( isset( $data['reviews_allowed'] ) ) { - $reviews_allowed = ( true === $data['reviews_allowed'] ) ? 'open' : 'closed'; - - $wpdb->update( $wpdb->posts, array( 'comment_status' => $reviews_allowed ), array( 'ID' => $product_id ) ); + $product->set_reviews_allowed( $data['reviews_allowed'] ); } // Do action for product type - do_action( 'woocommerce_api_process_product_meta_' . $product_type, $product_id, $data ); + do_action( 'woocommerce_api_process_product_meta_' . $product->get_type(), $product->get_id(), $data ); - return true; + return $product; } /** * Save variations + * @todo after variations CRUD is done * * @since 2.2 - * @param int $id + * @param WC_Product $product * @param array $data - * @return bool + * @return WC_Product * @throws WC_API_Exception */ - protected function save_variations( $id, $data ) { + protected function save_variations( $product, $data ) { global $wpdb; + $id = $product->get_id(); $variations = $data['variations']; $attributes = (array) maybe_unserialize( get_post_meta( $id, '_product_attributes', true ) ); @@ -1554,29 +1515,30 @@ class WC_API_Products extends WC_API_Resource { * Save product shipping data * * @since 2.2 - * @param int $id + * @param WC_Product $product * @param array $data + * @return WC_Product */ - private function save_product_shipping_data( $id, $data ) { + private function save_product_shipping_data( $product, $data ) { if ( isset( $data['weight'] ) ) { - update_post_meta( $id, '_weight', ( '' === $data['weight'] ) ? '' : wc_format_decimal( $data['weight'] ) ); + $product->set_weight( '' === $data['weight'] ? '' : wc_format_decimal( $data['weight'] ) ); } // Product dimensions if ( isset( $data['dimensions'] ) ) { // Height if ( isset( $data['dimensions']['height'] ) ) { - update_post_meta( $id, '_height', ( '' === $data['dimensions']['height'] ) ? '' : wc_format_decimal( $data['dimensions']['height'] ) ); + $product->set_height( '' === $data['dimensions']['height'] ? '' : wc_format_decimal( $data['dimensions']['height'] ) ); } // Width if ( isset( $data['dimensions']['width'] ) ) { - update_post_meta( $id, '_width', ( '' === $data['dimensions']['width'] ) ? '' : wc_format_decimal( $data['dimensions']['width'] ) ); + $product->set_width( '' === $data['dimensions']['width'] ? '' : wc_format_decimal( $data['dimensions']['width'] ) ); } // Length if ( isset( $data['dimensions']['length'] ) ) { - update_post_meta( $id, '_length', ( '' === $data['dimensions']['length'] ) ? '' : wc_format_decimal( $data['dimensions']['length'] ) ); + $product->set_length( '' === $data['dimensions']['length'] ? '' : wc_format_decimal( $data['dimensions']['length'] ) ); } } @@ -1585,28 +1547,34 @@ class WC_API_Products extends WC_API_Resource { $virtual = ( true === $data['virtual'] ) ? 'yes' : 'no'; if ( 'yes' == $virtual ) { - update_post_meta( $id, '_weight', '' ); - update_post_meta( $id, '_length', '' ); - update_post_meta( $id, '_width', '' ); - update_post_meta( $id, '_height', '' ); + $product->set_weight( '' ); + $product->set_height( '' ); + $product->set_length( '' ); + $product->set_width( '' ); } } // Shipping class if ( isset( $data['shipping_class'] ) ) { - wp_set_object_terms( $id, wc_clean( $data['shipping_class'] ), 'product_shipping_class' ); + $shipping_class_term = get_term_by( 'slug', wc_clean( $data['shipping_class'] ), 'product_shipping_class' ); + if ( $shipping_class_term ) { + $product->set_shipping_class_id( $shipping_class_term->term_id ); + } } + + return $product; } /** * Save downloadable files * * @since 2.2 - * @param int $product_id + * @param WC_Product $product * @param array $downloads * @param int $variation_id + * @return WC_Product */ - private function save_downloadable_files( $product_id, $downloads, $variation_id = 0 ) { + private function save_downloadable_files( $product, $downloads, $variation_id = 0 ) { $files = array(); // File paths will be stored in an array keyed off md5(file path) @@ -1634,10 +1602,11 @@ class WC_API_Products extends WC_API_Resource { } // 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', $product_id, $variation_id, $files ); + do_action( 'woocommerce_process_product_file_download_paths', $product->get_id(), $variation_id, $files ); - $id = ( 0 === $variation_id ) ? $product_id : $variation_id; - update_post_meta( $id, '_downloadable_files', $files ); + $product->set_downloads( $files ); + + return $product; } /** @@ -1680,16 +1649,15 @@ class WC_API_Products extends WC_API_Resource { // Add variation image if set $attachment_ids[] = get_post_thumbnail_id( $product->get_variation_id() ); - } elseif ( has_post_thumbnail( $product->get_id() ) ) { - + } elseif ( ! empty( $product->get_thumbnail_id() ) ) { // Otherwise use the parent product featured image if set - $attachment_ids[] = get_post_thumbnail_id( $product->get_id() ); + $attachment_ids[] = $product->get_thumbnail_id(); } } else { // Add featured image - if ( has_post_thumbnail( $product->get_id() ) ) { - $attachment_ids[] = get_post_thumbnail_id( $product->get_id() ); + if ( ! empty( $product->get_thumbnail_id() ) ) { + $attachment_ids[] = $product->get_thumbnail_id(); } // Add gallery images @@ -1743,11 +1711,11 @@ class WC_API_Products extends WC_API_Resource { * Save product images * * @since 2.2 + * @param WC_Product $product * @param array $images - * @param int $id * @throws WC_API_Exception */ - protected function save_product_images( $id, $images ) { + protected function save_product_images( $product, $images ) { if ( is_array( $images ) ) { $gallery = array(); @@ -1762,10 +1730,10 @@ class WC_API_Products extends WC_API_Resource { throw new WC_API_Exception( 'woocommerce_api_cannot_upload_product_image', $upload->get_error_message(), 400 ); } - $attachment_id = $this->set_product_image_as_attachment( $upload, $id ); + $attachment_id = $this->set_product_image_as_attachment( $upload, $product->get_id() ); } - set_post_thumbnail( $id, $attachment_id ); + $product->set_thumbnail_id( $attachment_id ); } else { $attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0; @@ -1784,12 +1752,14 @@ class WC_API_Products extends WC_API_Resource { } if ( ! empty( $gallery ) ) { - update_post_meta( $id, '_product_image_gallery', implode( ',', $gallery ) ); + $product->set_gallery_attachment_ids( $gallery ); } } else { - delete_post_thumbnail( $id ); - update_post_meta( $id, '_product_image_gallery', '' ); + $product->set_thumbnail_id( '' ); + $product->set_gallery_attachment_ids( array() ); } + + return $product; } /** @@ -2372,7 +2342,8 @@ class WC_API_Products extends WC_API_Resource { } // Delete product - wp_delete_post( $product_id, true ); + $product = wc_get_product( $product_id ); + $product->delete(); } /** diff --git a/includes/api/legacy/v3/class-wc-api-products.php b/includes/api/legacy/v3/class-wc-api-products.php index 39d30a244f6..e3464114a39 100644 --- a/includes/api/legacy/v3/class-wc-api-products.php +++ b/includes/api/legacy/v3/class-wc-api-products.php @@ -8,6 +8,7 @@ * @category API * @package WooCommerce/API * @since 2.1 + * @version 2.7 */ if ( ! defined( 'ABSPATH' ) ) { @@ -197,8 +198,8 @@ class WC_API_Products extends WC_API_Resource { } // add the parent product data to an individual variation - if ( $product->is_type( 'variation' ) && $product->parent ) { - $product_data['parent'] = $this->get_product_data( $product->parent ); + if ( $product->is_type( 'variation' ) && $product->get_parent_id() ) { + $product_data['parent'] = $this->get_product_data( $product->get_parent_id() ); } // Add grouped products data @@ -206,8 +207,8 @@ class WC_API_Products extends WC_API_Resource { $product_data['grouped_products'] = $this->get_grouped_products_data( $product ); } - if ( $product->is_type( 'simple' ) && ! empty( $product->post->post_parent ) ) { - $_product = wc_get_product( $product->post->post_parent ); + if ( $product->is_type( 'simple' ) && ! empty( $product->get_parent_id() ) ) { + $_product = wc_get_product( $product->get_parent_id() ); $product_data['parent'] = $this->get_product_data( $_product ); } @@ -297,39 +298,43 @@ class WC_API_Products extends WC_API_Resource { $post_excerpt = $data['short_description']; } - $new_product = array( - 'post_title' => wc_clean( $data['title'] ), - 'post_status' => isset( $data['status'] ) ? wc_clean( $data['status'] ) : 'publish', - 'post_type' => 'product', - 'post_excerpt' => isset( $data['short_description'] ) ? $post_excerpt : '', - 'post_content' => isset( $data['description'] ) ? $post_content : '', - 'post_author' => get_current_user_id(), - 'menu_order' => isset( $data['menu_order'] ) ? intval( $data['menu_order'] ) : 0, - ); + $classname = WC_Product_Factory::get_classname_from_product_type( $data['type'] ); + if ( ! class_exists( $classname ) ) { + $classname = 'WC_Product_Simple'; + } + $product = new $classname(); + + $product->set_name( wc_clean( $data['title'] ) ); + $product->set_status( isset( $data['status'] ) ? wc_clean( $data['status'] ) : 'publish' ); + $product->set_short_description( isset( $data['short_description'] ) ? $post_excerpt : '' ); + $product->set_description( isset( $data['description'] ) ? $post_content : '' ); + $product->set_menu_order( isset( $data['menu_order'] ) ? intval( $data['menu_order'] ) : 0 ); if ( ! empty( $data['name'] ) ) { - $new_product = array_merge( $new_product, array( 'post_name' => sanitize_title( $data['name'] ) ) ); + $product->set_slug( sanitize_title( $data['name'] ) ); } // Attempts to create the new product. - $id = wp_insert_post( $new_product, true ); + $product->create(); + $id = $product->get_id(); // Checks for an error in the product creation. - if ( is_wp_error( $id ) ) { + if ( ! $id > 0 ) { throw new WC_API_Exception( 'woocommerce_api_cannot_create_product', $id->get_error_message(), 400 ); } // Check for featured/gallery images, upload it and set it. if ( isset( $data['images'] ) ) { - $this->save_product_images( $id, $data['images'] ); + $product = $this->save_product_images( $product, $data['images'] ); } // Save product meta fields. - $this->save_product_meta( $id, $data ); + $product = $this->save_product_meta( $product, $data ); + $product->save(); // Save variations. if ( isset( $data['type'] ) && 'variable' == $data['type'] && isset( $data['variations'] ) && is_array( $data['variations'] ) ) { - $this->save_variations( $id, $data ); + $this->save_variations( $product, $data ); } do_action( 'woocommerce_api_create_product', $id, $data ); @@ -370,62 +375,64 @@ class WC_API_Products extends WC_API_Resource { return $id; } + $product = wc_get_product( $id ); + $data = apply_filters( 'woocommerce_api_edit_product_data', $data, $this ); // Product title. if ( isset( $data['title'] ) ) { - wp_update_post( array( 'ID' => $id, 'post_title' => wc_clean( $data['title'] ) ) ); + $product->set_name( wc_clean( $data['title'] ) ); } // Product name (slug). if ( isset( $data['name'] ) ) { - wp_update_post( array( 'ID' => $id, 'post_name' => sanitize_title( $data['name'] ) ) ); + $product->set_slug( wc_clean( $data['name'] ) ); } // Product status. if ( isset( $data['status'] ) ) { - wp_update_post( array( 'ID' => $id, 'post_status' => wc_clean( $data['status'] ) ) ); + $product->set_status( wc_clean( $data['status'] ) ); } // Product short description. if ( isset( $data['short_description'] ) ) { // Enable short description html tags. $post_excerpt = ( isset( $data['enable_html_short_description'] ) && true === $data['enable_html_short_description'] ) ? $data['short_description'] : wc_clean( $data['short_description'] ); - - wp_update_post( array( 'ID' => $id, 'post_excerpt' => $post_excerpt ) ); + $product->set_short_description( $post_excerpt ); } // Product description. if ( isset( $data['description'] ) ) { // Enable description html tags. $post_content = ( isset( $data['enable_html_description'] ) && true === $data['enable_html_description'] ) ? $data['description'] : wc_clean( $data['description'] ); - - wp_update_post( array( 'ID' => $id, 'post_content' => $post_content ) ); + $product->set_description( $post_content ); } - // Menu order. - if ( isset( $data['menu_order'] ) ) { - wp_update_post( array( 'ID' => $id, 'menu_order' => intval( $data['menu_order'] ) ) ); - } - - // Validate the product type. + // Validate the product type if ( isset( $data['type'] ) && ! in_array( wc_clean( $data['type'] ), array_keys( wc_get_product_types() ) ) ) { throw new WC_API_Exception( 'woocommerce_api_invalid_product_type', sprintf( __( 'Invalid product type - the product type must be any of these: %s', 'woocommerce' ), implode( ', ', array_keys( wc_get_product_types() ) ) ), 400 ); } + // Menu order. + if ( isset( $data['menu_order'] ) ) { + $product->set_menu_order( intval( $data['menu_order'] ) ); + } + // Check for featured/gallery images, upload it and set it. if ( isset( $data['images'] ) ) { - $this->save_product_images( $id, $data['images'] ); + $product = $this->save_product_images( $product, $data['images'] ); } // Save product meta fields. - $this->save_product_meta( $id, $data ); + $product = $this->save_product_meta( $product, $data ); + + $product->save(); // Save variations. $product = get_product( $id ); if ( $product->is_type( 'variable' ) ) { if ( isset( $data['variations'] ) && is_array( $data['variations'] ) ) { - $this->save_variations( $id, $data ); + $this->save_variations( $product, $data ); } else { // Just sync variations WC_Product_Variable::sync( $id ); @@ -460,30 +467,27 @@ class WC_API_Products extends WC_API_Resource { return $id; } + $product = wc_get_product( $id ); + do_action( 'woocommerce_api_delete_product', $id, $this ); // If we're forcing, then delete permanently. if ( $force ) { - $child_product_variations = get_children( 'post_parent=' . $id . '&post_type=product_variation' ); - - if ( ! empty( $child_product_variations ) ) { - foreach ( $child_product_variations as $child ) { - wp_delete_post( $child->ID, true ); + if ( $product->is_type( 'variable' ) ) { + foreach ( $product->get_children() as $child_id ) { + $child = wc_get_product( $child_id ); + $child->delete(); + } + } elseif ( $product->is_type( 'grouped' ) ) { + foreach ( $product->get_children() as $child_id ) { + $child = wc_get_product( $child_id ); + $child->set_parent_id( 0 ); + $child->save(); } } - $child_products = get_children( 'post_parent=' . $id . '&post_type=product' ); - - if ( ! empty( $child_products ) ) { - foreach ( $child_products as $child ) { - $child_post = array(); - $child_post['ID'] = $child->ID; - $child_post['post_parent'] = 0; - wp_update_post( $child_post ); - } - } - - $result = wp_delete_post( $id, true ); + $product->delete(); + $result = $product->get_id() > 0 ? false : true; } else { $result = wp_trash_post( $id ); } @@ -1117,13 +1121,14 @@ class WC_API_Products extends WC_API_Resource { * @return WC_Product */ private function get_product_data( $product ) { + $post = get_post( $product->get_id() ); return array( 'title' => $product->get_title(), 'id' => (int) $product->is_type( 'variation' ) ? $product->get_variation_id() : $product->get_id(), - 'created_at' => $this->server->format_datetime( $product->get_post_data()->post_date_gmt ), - 'updated_at' => $this->server->format_datetime( $product->get_post_data()->post_modified_gmt ), - 'type' => $product->product_type, - 'status' => $product->get_post_data()->post_status, + 'created_at' => $this->server->format_datetime( $post->post_date_gmt ), + 'updated_at' => $this->server->format_datetime( $post->post_modified_gmt ), + 'type' => $product->get_type(), + 'status' => $product->get_status(), 'downloadable' => $product->is_downloadable(), 'virtual' => $product->is_virtual(), 'permalink' => $product->get_permalink(), @@ -1144,41 +1149,41 @@ class WC_API_Products extends WC_API_Resource { 'purchaseable' => $product->is_purchasable(), 'featured' => $product->is_featured(), 'visible' => $product->is_visible(), - 'catalog_visibility' => $product->visibility, + 'catalog_visibility' => $product->get_catalog_visibility(), 'on_sale' => $product->is_on_sale(), 'product_url' => $product->is_type( 'external' ) ? $product->get_product_url() : '', 'button_text' => $product->is_type( 'external' ) ? $product->get_button_text() : '', 'weight' => $product->get_weight() ? $product->get_weight() : null, 'dimensions' => array( - 'length' => $product->length, - 'width' => $product->width, - 'height' => $product->height, + 'length' => $product->get_length(), + 'width' => $product->get_width(), + 'height' => $product->get_height(), 'unit' => get_option( 'woocommerce_dimension_unit' ), ), 'shipping_required' => $product->needs_shipping(), 'shipping_taxable' => $product->is_shipping_taxable(), 'shipping_class' => $product->get_shipping_class(), 'shipping_class_id' => ( 0 !== $product->get_shipping_class_id() ) ? $product->get_shipping_class_id() : null, - 'description' => wpautop( do_shortcode( $product->get_post_data()->post_content ) ), - 'short_description' => apply_filters( 'woocommerce_short_description', $product->get_post_data()->post_excerpt ), + 'description' => wpautop( do_shortcode( $product->get_description() ) ), + 'short_description' => apply_filters( 'woocommerce_short_description', $product->get_short_description() ), 'reviews_allowed' => ( 'open' === $product->get_post_data()->comment_status ), 'average_rating' => wc_format_decimal( $product->get_average_rating(), 2 ), 'rating_count' => (int) $product->get_rating_count(), - 'related_ids' => array_map( 'absint', array_values( $product->get_related() ) ), - 'upsell_ids' => array_map( 'absint', $product->get_upsells() ), - 'cross_sell_ids' => array_map( 'absint', $product->get_cross_sells() ), - 'parent_id' => $product->is_type( 'variation' ) ? $product->parent->id : $product->post->post_parent, + 'related_ids' => array_map( 'absint', array_values( wc_get_related_products( $product->get_id() ) ) ), + 'upsell_ids' => array_map( 'absint', $product->get_upsell_ids() ), + 'cross_sell_ids' => array_map( 'absint', $product->get_cross_sell_ids() ), + 'parent_id' => $product->get_parent_id(), 'categories' => wp_get_post_terms( $product->get_id(), 'product_cat', array( 'fields' => 'names' ) ), 'tags' => wp_get_post_terms( $product->get_id(), 'product_tag', array( 'fields' => 'names' ) ), 'images' => $this->get_images( $product ), 'featured_src' => (string) wp_get_attachment_url( get_post_thumbnail_id( $product->is_type( 'variation' ) ? $product->variation_id : $product->get_id() ) ), 'attributes' => $this->get_attributes( $product ), 'downloads' => $this->get_downloads( $product ), - 'download_limit' => (int) $product->download_limit, - 'download_expiry' => (int) $product->download_expiry, - 'download_type' => $product->download_type, - 'purchase_note' => wpautop( do_shortcode( wp_kses_post( $product->purchase_note ) ) ), - 'total_sales' => metadata_exists( 'post', $product->get_id(), 'total_sales' ) ? (int) get_post_meta( $product->get_id(), 'total_sales', true ) : 0, + 'download_limit' => (int) $product->get_download_limit(), + 'download_expiry' => (int) $product->get_download_expiry(), + 'download_type' => 'standard', + 'purchase_note' => wpautop( do_shortcode( wp_kses_post( $product->get_purchase_note() ) ) ), + 'total_sales' => $product->get_total_sales(), 'variations' => array(), 'parent' => array(), 'grouped_products' => array(), @@ -1194,11 +1199,11 @@ class WC_API_Products extends WC_API_Resource { * @return int */ private function get_product_menu_order( $product ) { - $menu_order = $product->post->menu_order; + $menu_order = $product->get_menu_order(); if ( $product->is_type( 'variation' ) ) { - $_product = get_post( $product->get_variation_id() ); - $menu_order = $_product->menu_order; + $_product = wc_get_product( $product->get_variation_id() ); + $menu_order = $_product->get_menu_order(); } return apply_filters( 'woocommerce_api_product_menu_order', $menu_order, $product ); @@ -1206,6 +1211,7 @@ class WC_API_Products extends WC_API_Resource { /** * Get an individual variation's data + * @todo after variations CRUD is done * * @since 2.1 * @param WC_Product $product @@ -1216,7 +1222,7 @@ class WC_API_Products extends WC_API_Resource { foreach ( $product->get_children() as $child_id ) { - $variation = $product->get_child( $child_id ); + $variation = wc_get_product( $child_id ); if ( ! $variation->exists() ) { continue; @@ -1258,8 +1264,8 @@ class WC_API_Products extends WC_API_Resource { 'image' => $this->get_images( $variation ), 'attributes' => $this->get_attributes( $variation ), 'downloads' => $this->get_downloads( $variation ), - 'download_limit' => (int) $product->download_limit, - 'download_expiry' => (int) $product->download_expiry, + 'download_limit' => (int) $product->get_download_limit(), + 'download_expiry' => (int) $product->get_download_expiry(), ); } @@ -1278,7 +1284,7 @@ class WC_API_Products extends WC_API_Resource { $products = array(); foreach ( $product->get_children() as $child_id ) { - $_product = $product->get_child( $child_id ); + $_product = wc_get_product( $child_id ); if ( ! $_product->exists() ) { continue; @@ -1295,80 +1301,64 @@ class WC_API_Products extends WC_API_Resource { * Save product meta. * * @since 2.2 - * @param int $product_id + * @param WC_Product $product * @param array $data - * @return bool + * @return WC_Product * @throws WC_API_Exception */ - protected function save_product_meta( $product_id, $data ) { + protected function save_product_meta( $product, $data ) { global $wpdb; - // Product Type. - $product_type = null; - if ( isset( $data['type'] ) ) { - $product_type = wc_clean( $data['type'] ); - wp_set_object_terms( $product_id, $product_type, 'product_type' ); - } else { - $_product_type = get_the_terms( $product_id, 'product_type' ); - if ( is_array( $_product_type ) ) { - $_product_type = current( $_product_type ); - $product_type = $_product_type->slug; - } - } - - // Default total sales. - add_post_meta( $product_id, 'total_sales', '0', true ); - // Virtual. if ( isset( $data['virtual'] ) ) { - update_post_meta( $product_id, '_virtual', ( true === $data['virtual'] ) ? 'yes' : 'no' ); + $product->set_virtual( $data['virtual'] ); } // Tax status. if ( isset( $data['tax_status'] ) ) { - update_post_meta( $product_id, '_tax_status', wc_clean( $data['tax_status'] ) ); + $product->set_tax_status( wc_clean( $data['tax_status'] ) ); } // Tax Class. if ( isset( $data['tax_class'] ) ) { - update_post_meta( $product_id, '_tax_class', wc_clean( $data['tax_class'] ) ); + $product->set_tax_class( wc_clean( $data['tax_class'] ) ); } // Catalog Visibility. if ( isset( $data['catalog_visibility'] ) ) { - update_post_meta( $product_id, '_visibility', wc_clean( $data['catalog_visibility'] ) ); + $product->set_catalog_visibility( wc_clean( $data['catalog_visibility'] ) ); } // Purchase Note. if ( isset( $data['purchase_note'] ) ) { - update_post_meta( $product_id, '_purchase_note', wc_clean( $data['purchase_note'] ) ); + $product->set_purchase_note( wc_clean( $data['purchase_note'] ) ); } // Featured Product. if ( isset( $data['featured'] ) ) { - update_post_meta( $product_id, '_featured', ( true === $data['featured'] ) ? 'yes' : 'no' ); + $product->set_featured( $data['featured'] ); } // Shipping data. - $this->save_product_shipping_data( $product_id, $data ); + $product = $this->save_product_shipping_data( $product, $data ); // SKU. if ( isset( $data['sku'] ) ) { - $sku = get_post_meta( $product_id, '_sku', true ); + $sku = $product->get_sku(); $new_sku = wc_clean( $data['sku'] ); if ( '' == $new_sku ) { - update_post_meta( $product_id, '_sku', '' ); + $product->set_sku( '' ); } elseif ( $new_sku !== $sku ) { if ( ! empty( $new_sku ) ) { - $unique_sku = wc_product_has_unique_sku( $product_id, $new_sku ); + $unique_sku = wc_product_has_unique_sku( $product->get_id(), $new_sku ); if ( ! $unique_sku ) { throw new WC_API_Exception( 'woocommerce_api_product_sku_already_exists', __( 'The SKU already exists on another product', 'woocommerce' ), 400 ); } else { - update_post_meta( $product_id, '_sku', $new_sku ); + $product->set_sku( $new_sku ); } } else { - update_post_meta( $product_id, '_sku', '' ); + $product->set_sku( '' ); } } } @@ -1398,6 +1388,8 @@ class WC_API_Products extends WC_API_Resource { if ( $is_taxonomy ) { + $attribute_id = wc_attribute_taxonomy_id_by_name( $attribute['name'] ); + if ( isset( $attribute['options'] ) ) { $options = $attribute['options']; @@ -1412,58 +1404,57 @@ class WC_API_Products extends WC_API_Resource { $values = array(); } - // Update post terms. + // Update post terms if ( taxonomy_exists( $taxonomy ) ) { - wp_set_object_terms( $product_id, $values, $taxonomy ); + wp_set_object_terms( $product->get_id(), $values, $taxonomy ); } if ( ! empty( $values ) ) { // Add attribute to array, but don't set values. - $attributes[ $taxonomy ] = array( - 'name' => $taxonomy, - 'value' => '', - 'position' => isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0', - 'is_visible' => ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0, - 'is_variation' => ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0, - 'is_taxonomy' => $is_taxonomy, - ); + $attribute_object = new WC_Product_Attribute(); + $attribute_object->set_id( $attribute_id ); + $attribute_object->set_name( $taxonomy ); + $attribute_object->set_options( $values ); + $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); + $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); + $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); + $attributes[] = $attribute_object; } } elseif ( isset( $attribute['options'] ) ) { // Array based. if ( is_array( $attribute['options'] ) ) { - $values = wc_implode_text_attributes( array_map( 'wc_clean', $attribute['options'] ) ); + $values = $attribute['options']; // Text based, separate by pipe. } else { - $values = wc_implode_text_attributes( array_map( 'wc_clean', explode( WC_DELIMITER, $attribute['options'] ) ) ); + $values = array_map( 'wc_clean', explode( WC_DELIMITER, $attribute['options'] ) ); } // Custom attribute - Add attribute to array and set the values. - $attributes[ $attribute_slug ] = array( - 'name' => wc_clean( $attribute['name'] ), - 'value' => $values, - 'position' => isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0', - 'is_visible' => ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0, - 'is_variation' => ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0, - 'is_taxonomy' => $is_taxonomy, - ); + $attribute_object = new WC_Product_Attribute(); + $attribute_object->set_name( $attribute['name'] ); + $attribute_object->set_options( $values ); + $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); + $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); + $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); + $attributes[] = $attribute_object; } } uasort( $attributes, 'wc_product_attribute_uasort_comparison' ); - update_post_meta( $product_id, '_product_attributes', $attributes ); + $product->set_attributes( $attributes ); } // Sales and prices. - if ( in_array( $product_type, array( 'variable', 'grouped' ) ) ) { + if ( in_array( $product->get_type(), array( 'variable', 'grouped' ) ) ) { // Variable and grouped products have no prices. - update_post_meta( $product_id, '_regular_price', '' ); - update_post_meta( $product_id, '_sale_price', '' ); - update_post_meta( $product_id, '_sale_price_dates_from', '' ); - update_post_meta( $product_id, '_sale_price_dates_to', '' ); - update_post_meta( $product_id, '_price', '' ); + $product->set_regular_price( '' ); + $product->set_sale_price( '' ); + $product->set_date_on_sale_to( '' ); + $product->set_date_on_sale_from( '' ); + $product->set_price( '' ); } else { @@ -1471,86 +1462,69 @@ class WC_API_Products extends WC_API_Resource { if ( isset( $data['regular_price'] ) ) { $regular_price = ( '' === $data['regular_price'] ) ? '' : $data['regular_price']; } else { - $regular_price = get_post_meta( $product_id, '_regular_price', true ); + $regular_price = $product->get_regular_price(); } // Sale Price if ( isset( $data['sale_price'] ) ) { $sale_price = ( '' === $data['sale_price'] ) ? '' : $data['sale_price']; } else { - $sale_price = get_post_meta( $product_id, '_sale_price', true ); + $sale_price = $product->get_sale_price(); } + $product->set_regular_price( $regular_price ); + $product->set_sale_price( $sale_price ); + if ( isset( $data['sale_price_dates_from'] ) ) { $date_from = $data['sale_price_dates_from']; } else { - $date_from = get_post_meta( $product_id, '_sale_price_dates_from', true ); - $date_from = ( '' === $date_from ) ? '' : date( 'Y-m-d', $date_from ); + $date_from = ( $product->get_date_on_sale_from() ) ? date( 'Y-m-d', $date_from ) : ''; } if ( isset( $data['sale_price_dates_to'] ) ) { $date_to = $data['sale_price_dates_to']; } else { - $date_to = get_post_meta( $product_id, '_sale_price_dates_to', true ); - $date_to = ( '' === $date_to ) ? '' : date( 'Y-m-d', $date_to ); + $date_to = ( $product->get_date_on_sale_to() ) ? date( 'Y-m-d', $date_to ) : ''; } - _wc_save_product_price( $product_id, $regular_price, $sale_price, $date_from, $date_to ); + if ( $date_to && ! $date_from ) { + $date_from = strtotime( 'NOW', current_time( 'timestamp' ) ); + } + $product->set_date_on_sale_to( $date_to ); + $product->set_date_on_sale_from( $date_from ); + if ( $product->is_on_sale() ) { + $product->set_price( $product->get_sale_price() ); + } else { + $product->set_price( $product->get_regular_price() ); + } } // Product parent ID for groups. if ( isset( $data['parent_id'] ) ) { - wp_update_post( array( 'ID' => $product_id, 'post_parent' => absint( $data['parent_id'] ) ) ); + $product->set_parent_id( absint( $data['parent_id'] ) ); } // Update parent if grouped so price sorting works and stays in sync with the cheapest child. - $_product = wc_get_product( $product_id ); - if ( $_product->post->post_parent > 0 || 'grouped' === $product_type ) { - - $clear_parent_ids = array(); - - if ( $_product->post->post_parent > 0 ) { - $clear_parent_ids[] = $_product->post->post_parent; - } - - if ( 'grouped' === $product_type ) { - $clear_parent_ids[] = $product_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 ); - } - } - } + if ( $product->get_parent_id() > 0 || 'grouped' === $product->get_type() ) { + if ( $product->get_parent_id() > 0 ) { + $parent = wc_get_product( $product->get_parent_id() ); + $children = $parent->get_children(); + $parent->set_children( array_filter( array_merge( $children, array( $product->get_id() ) ) ) ); + $parent->save(); } } // Sold Individually. if ( isset( $data['sold_individually'] ) ) { - update_post_meta( $product_id, '_sold_individually', ( true === $data['sold_individually'] ) ? 'yes' : '' ); + $product->set_sold_individually( true === $data['sold_individually'] ? 'yes' : '' ); } // Stock status. if ( isset( $data['in_stock'] ) ) { $stock_status = ( true === $data['in_stock'] ) ? 'instock' : 'outofstock'; } else { - $stock_status = get_post_meta( $product_id, '_stock_status', true ); + $stock_status = $product->get_stock_status(); if ( '' === $stock_status ) { $stock_status = 'instock'; @@ -1562,9 +1536,9 @@ class WC_API_Products extends WC_API_Resource { // Manage stock. if ( isset( $data['managing_stock'] ) ) { $managing_stock = ( true === $data['managing_stock'] ) ? 'yes' : 'no'; - update_post_meta( $product_id, '_manage_stock', $managing_stock ); + $product->set_manage_stock( $managing_stock ); } else { - $managing_stock = get_post_meta( $product_id, '_manage_stock', true ); + $managing_stock = $product->get_manage_stock() ? 'yes' : 'no'; } // Backorders. @@ -1575,54 +1549,46 @@ class WC_API_Products extends WC_API_Resource { $backorders = ( true === $data['backorders'] ) ? 'yes' : 'no'; } - update_post_meta( $product_id, '_backorders', $backorders ); + $product->set_backorders( $backorders ); } else { - $backorders = get_post_meta( $product_id, '_backorders', true ); + $backorders = $product->get_backorders(); } - if ( 'grouped' == $product_type ) { - - update_post_meta( $product_id, '_manage_stock', 'no' ); - update_post_meta( $product_id, '_backorders', 'no' ); - update_post_meta( $product_id, '_stock', '' ); - - wc_update_product_stock_status( $product_id, $stock_status ); - - } elseif ( 'external' == $product_type ) { - - update_post_meta( $product_id, '_manage_stock', 'no' ); - update_post_meta( $product_id, '_backorders', 'no' ); - update_post_meta( $product_id, '_stock', '' ); - - wc_update_product_stock_status( $product_id, 'instock' ); + if ( 'grouped' == $product->get_type() ) { + $product->set_manage_stock( 'no' ); + $product->set_backorders( 'no' ); + $product->set_stock( '' ); + $product->set_stock_status( $stock_status ); + } elseif ( 'external' == $product->get_type() ) { + $product->set_manage_stock( 'no' ); + $product->set_backorders( 'no' ); + $product->set_stock( '' ); + $product->set_stock_status( 'instock' ); } elseif ( 'yes' == $managing_stock ) { - update_post_meta( $product_id, '_backorders', $backorders ); + $product->set_backorders( $backorders ); // Stock status is always determined by children so sync later. - if ( 'variable' !== $product_type ) { - wc_update_product_stock_status( $product_id, $stock_status ); + if ( 'variable' !== $product->get_type() ) { + $product->set_stock_status( $stock_status ); } // Stock quantity. if ( isset( $data['stock_quantity'] ) ) { - wc_update_product_stock( $product_id, wc_stock_amount( $data['stock_quantity'] ) ); + $product->set_stock( wc_stock_amount( $data['stock_quantity'] ) ); } else if ( isset( $data['inventory_delta'] ) ) { - $stock_quantity = wc_stock_amount( get_post_meta( $product_id, '_stock', true ) ); + $stock_quantity = wc_stock_amount( $product->get_stock() ); $stock_quantity += wc_stock_amount( $data['inventory_delta'] ); - - wc_update_product_stock( $product_id, wc_stock_amount( $stock_quantity ) ); + $product->set_stock( wc_stock_amount( $stock_quantity ) ); } } else { - // Don't manage stock. - update_post_meta( $product_id, '_manage_stock', 'no' ); - update_post_meta( $product_id, '_backorders', $backorders ); - update_post_meta( $product_id, '_stock', '' ); - - wc_update_product_stock_status( $product_id, $stock_status ); + $product->set_manage_stock( 'no' ); + $product->set_backorders( $backorders ); + $product->set_stock( '' ); + $product->set_stock_status( $stock_status ); } - } elseif ( 'variable' !== $product_type ) { - wc_update_product_stock_status( $product_id, $stock_status ); + } elseif ( 'variable' !== $product->get_type() ) { + $product->set_stock_status( $stock_status ); } // Upsells. @@ -1637,9 +1603,9 @@ class WC_API_Products extends WC_API_Resource { } } - update_post_meta( $product_id, '_upsell_ids', $upsells ); + $product->set_upsell_ids( $upsells ); } else { - delete_post_meta( $product_id, '_upsell_ids' ); + $product->set_upsell_ids( array() ); } } @@ -1655,30 +1621,30 @@ class WC_API_Products extends WC_API_Resource { } } - update_post_meta( $product_id, '_crosssell_ids', $crosssells ); + $product->set_cross_sell_ids( $crosssells ); } else { - delete_post_meta( $product_id, '_crosssell_ids' ); + $product->set_cross_sell_ids( array() ); } } // Product categories. if ( isset( $data['categories'] ) && is_array( $data['categories'] ) ) { $term_ids = array_unique( array_map( 'intval', $data['categories'] ) ); - wp_set_object_terms( $product_id, $term_ids, 'product_cat' ); + $product->set_category_ids( $term_ids ); } // Product tags. if ( isset( $data['tags'] ) && is_array( $data['tags'] ) ) { $term_ids = array_unique( array_map( 'intval', $data['tags'] ) ); - wp_set_object_terms( $product_id, $term_ids, 'product_tag' ); + $product->set_tag_ids( $term_ids ); } // Downloadable. if ( isset( $data['downloadable'] ) ) { $is_downloadable = ( true === $data['downloadable'] ) ? 'yes' : 'no'; - update_post_meta( $product_id, '_downloadable', $is_downloadable ); + $product->set_downloadable( $is_downloadable ); } else { - $is_downloadable = get_post_meta( $product_id, '_downloadable', true ); + $is_downloadable = $product->get_downloadable() ? 'yes' : 'no'; } // Downloadable options. @@ -1686,61 +1652,56 @@ class WC_API_Products extends WC_API_Resource { // Downloadable files. if ( isset( $data['downloads'] ) && is_array( $data['downloads'] ) ) { - $this->save_downloadable_files( $product_id, $data['downloads'] ); + $product = $this->save_downloadable_files( $product, $data['downloads'] ); } // Download limit. if ( isset( $data['download_limit'] ) ) { - update_post_meta( $product_id, '_download_limit', ( '' === $data['download_limit'] ) ? '' : absint( $data['download_limit'] ) ); + $product->set_download_limit( $data['download_limit'] ); } // Download expiry. if ( isset( $data['download_expiry'] ) ) { - update_post_meta( $product_id, '_download_expiry', ( '' === $data['download_expiry'] ) ? '' : absint( $data['download_expiry'] ) ); - } - - // Download type. - if ( isset( $data['download_type'] ) ) { - update_post_meta( $product_id, '_download_type', wc_clean( $data['download_type'] ) ); + $product->set_download_expiry( $data['download_expiry'] ); } } // Product url. - if ( 'external' === $product_type ) { + if ( 'external' === $product->get_type() ) { if ( isset( $data['product_url'] ) ) { - update_post_meta( $product_id, '_product_url', wc_clean( $data['product_url'] ) ); + $product->set_product_url( $data['product_url'] ); } if ( isset( $data['button_text'] ) ) { - update_post_meta( $product_id, '_button_text', wc_clean( $data['button_text'] ) ); + $product->set_button_text( $data['button_text'] ); } } // Reviews allowed. if ( isset( $data['reviews_allowed'] ) ) { - $reviews_allowed = ( true === $data['reviews_allowed'] ) ? 'open' : 'closed'; - - $wpdb->update( $wpdb->posts, array( 'comment_status' => $reviews_allowed ), array( 'ID' => $product_id ) ); + $product->set_reviews_allowed( $data['reviews_allowed'] ); } // Do action for product type - do_action( 'woocommerce_api_process_product_meta_' . $product_type, $product_id, $data ); + do_action( 'woocommerce_api_process_product_meta_' . $product->get_type(), $product->get_id(), $data ); - return true; + return $product; } /** * Save variations + * @todo after variations CRUD is done * * @since 2.2 - * @param int $id + * @param WC_Product $product * @param array $data - * @return bool + * @return WC_Product * @throws WC_API_Exception */ - protected function save_variations( $id, $data ) { + protected function save_variations( $product, $data ) { global $wpdb; + $id = $product->get_id(); $variations = $data['variations']; $attributes = (array) maybe_unserialize( get_post_meta( $id, '_product_attributes', true ) ); @@ -2076,29 +2037,30 @@ class WC_API_Products extends WC_API_Resource { * Save product shipping data * * @since 2.2 - * @param int $id + * @param WC_Product $product * @param array $data + * @return WC_Product */ - private function save_product_shipping_data( $id, $data ) { + private function save_product_shipping_data( $product, $data ) { if ( isset( $data['weight'] ) ) { - update_post_meta( $id, '_weight', ( '' === $data['weight'] ) ? '' : wc_format_decimal( $data['weight'] ) ); + $product->set_weight( '' === $data['weight'] ? '' : wc_format_decimal( $data['weight'] ) ); } // Product dimensions if ( isset( $data['dimensions'] ) ) { // Height if ( isset( $data['dimensions']['height'] ) ) { - update_post_meta( $id, '_height', ( '' === $data['dimensions']['height'] ) ? '' : wc_format_decimal( $data['dimensions']['height'] ) ); + $product->set_height( '' === $data['dimensions']['height'] ? '' : wc_format_decimal( $data['dimensions']['height'] ) ); } // Width if ( isset( $data['dimensions']['width'] ) ) { - update_post_meta( $id, '_width', ( '' === $data['dimensions']['width'] ) ? '' : wc_format_decimal( $data['dimensions']['width'] ) ); + $product->set_width( '' === $data['dimensions']['width'] ? '' : wc_format_decimal( $data['dimensions']['width'] ) ); } // Length if ( isset( $data['dimensions']['length'] ) ) { - update_post_meta( $id, '_length', ( '' === $data['dimensions']['length'] ) ? '' : wc_format_decimal( $data['dimensions']['length'] ) ); + $product->set_length( '' === $data['dimensions']['length'] ? '' : wc_format_decimal( $data['dimensions']['length'] ) ); } } @@ -2107,28 +2069,34 @@ class WC_API_Products extends WC_API_Resource { $virtual = ( true === $data['virtual'] ) ? 'yes' : 'no'; if ( 'yes' == $virtual ) { - update_post_meta( $id, '_weight', '' ); - update_post_meta( $id, '_length', '' ); - update_post_meta( $id, '_width', '' ); - update_post_meta( $id, '_height', '' ); + $product->set_weight( '' ); + $product->set_height( '' ); + $product->set_length( '' ); + $product->set_width( '' ); } } // Shipping class if ( isset( $data['shipping_class'] ) ) { - wp_set_object_terms( $id, wc_clean( $data['shipping_class'] ), 'product_shipping_class' ); + $shipping_class_term = get_term_by( 'slug', wc_clean( $data['shipping_class'] ), 'product_shipping_class' ); + if ( $shipping_class_term ) { + $product->set_shipping_class_id( $shipping_class_term->term_id ); + } } + + return $product; } /** * Save downloadable files * * @since 2.2 - * @param int $product_id + * @param WC_Product $product * @param array $downloads * @param int $variation_id + * @return WC_Product */ - private function save_downloadable_files( $product_id, $downloads, $variation_id = 0 ) { + private function save_downloadable_files( $product, $downloads, $variation_id = 0 ) { $files = array(); // File paths will be stored in an array keyed off md5(file path) @@ -2156,10 +2124,11 @@ class WC_API_Products extends WC_API_Resource { } // 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', $product_id, $variation_id, $files ); + do_action( 'woocommerce_process_product_file_download_paths', $product->get_id(), $variation_id, $files ); - $id = ( 0 === $variation_id ) ? $product_id : $variation_id; - update_post_meta( $id, '_downloadable_files', $files ); + $product->set_downloads( $files ); + + return $product; } /** @@ -2196,22 +2165,21 @@ class WC_API_Products extends WC_API_Resource { $images = $attachment_ids = array(); if ( $product->is_type( 'variation' ) ) { - + // @todo variation if ( has_post_thumbnail( $product->get_variation_id() ) ) { // Add variation image if set $attachment_ids[] = get_post_thumbnail_id( $product->get_variation_id() ); - } elseif ( has_post_thumbnail( $product->get_id() ) ) { - + } elseif ( ! empty( $product->get_thumbnail_id() ) ) { // Otherwise use the parent product featured image if set - $attachment_ids[] = get_post_thumbnail_id( $product->get_id() ); + $attachment_ids[] = $product->get_thumbnail_id(); } } else { // Add featured image - if ( has_post_thumbnail( $product->get_id() ) ) { - $attachment_ids[] = get_post_thumbnail_id( $product->get_id() ); + if ( ! empty( $product->get_thumbnail_id() ) ) { + $attachment_ids[] = $product->get_thumbnail_id(); } // Add gallery images @@ -2265,11 +2233,12 @@ class WC_API_Products extends WC_API_Resource { * Save product images. * * @since 2.2 + * @param WC_Product $product * @param array $images - * @param int $id * @throws WC_API_Exception + * @return WC_Product */ - protected function save_product_images( $id, $images ) { + protected function save_product_images( $product, $images ) { if ( is_array( $images ) ) { $gallery = array(); @@ -2284,10 +2253,10 @@ class WC_API_Products extends WC_API_Resource { throw new WC_API_Exception( 'woocommerce_api_cannot_upload_product_image', $upload->get_error_message(), 400 ); } - $attachment_id = $this->set_product_image_as_attachment( $upload, $id ); + $attachment_id = $this->set_product_image_as_attachment( $upload, $product->get_id() ); } - set_post_thumbnail( $id, $attachment_id ); + $product->set_thumbnail_id( $attachment_id ); } else { $attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0; @@ -2316,12 +2285,14 @@ class WC_API_Products extends WC_API_Resource { } if ( ! empty( $gallery ) ) { - update_post_meta( $id, '_product_image_gallery', implode( ',', $gallery ) ); + $product->set_gallery_attachment_ids( $gallery ); } } else { - delete_post_thumbnail( $id ); - update_post_meta( $id, '_product_image_gallery', '' ); + $product->set_thumbnail_id( '' ); + $product->set_gallery_attachment_ids( array() ); } + + return $product; } /** @@ -3200,7 +3171,8 @@ class WC_API_Products extends WC_API_Resource { } // Delete product - wp_delete_post( $product_id, true ); + $product = wc_get_product( $product_id ); + $product->delete(); } /** diff --git a/includes/class-wc-product-grouped.php b/includes/class-wc-product-grouped.php index 8241c2a65d4..85c8003c1c7 100644 --- a/includes/class-wc-product-grouped.php +++ b/includes/class-wc-product-grouped.php @@ -180,9 +180,13 @@ class WC_Product_Grouped extends WC_Product { $child = wc_get_product( $child_id ); $child_prices[] = $child->get_price(); } + $child_prices = array_filter( $child_prices ); delete_post_meta( $this->get_id(), '_price' ); - add_post_meta( $this->get_id(), '_price', min( $child_prices ) ); - add_post_meta( $this->get_id(), '_price', max( $child_prices ) ); + + if ( ! empty( $child_prices ) ) { + add_post_meta( $this->get_id(), '_price', min( $child_prices ) ); + add_post_meta( $this->get_id(), '_price', max( $child_prices ) ); + } } parent::update_post_meta(); } diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 3d6f2bd2ba2..1d06bab14db 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -48,20 +48,21 @@ if ( ! defined( 'ABSPATH' ) ) { */ function wc_get_products( $args ) { $args = wp_parse_args( $args, array( - 'status' => array( 'draft', 'pending', 'private', 'publish' ), - 'type' => array_merge( array_keys( wc_get_product_types() ), array( 'variation' ) ), - 'parent' => null, - 'sku' => '', - 'category' => array(), - 'tag' => array(), - 'limit' => get_option( 'posts_per_page' ), - 'offset' => null, - 'page' => 1, - 'exclude' => array(), - 'orderby' => 'date', - 'order' => 'DESC', - 'return' => 'objects', - 'paginate' => false, + 'status' => array( 'draft', 'pending', 'private', 'publish' ), + 'type' => array_merge( array_keys( wc_get_product_types() ), array( 'variation' ) ), + 'parent' => null, + 'sku' => '', + 'category' => array(), + 'tag' => array(), + 'limit' => get_option( 'posts_per_page' ), + 'offset' => null, + 'page' => 1, + 'exclude' => array(), + 'orderby' => 'date', + 'order' => 'DESC', + 'return' => 'objects', + 'paginate' => false, + 'shipping_class' => array(), ) ); // Handle some BW compatibility arg names where wp_query args differ in naming. @@ -112,7 +113,7 @@ function wc_get_products( $args ) { if ( ! empty( $args['category'] ) ) { $wp_query_args['tax_query'][] = array( 'taxonomy' => 'product_cat', - 'field' => 'term_id', + 'field' => 'slug', 'terms' => $args['category'], ); } @@ -120,11 +121,19 @@ function wc_get_products( $args ) { if ( ! empty( $args['tag'] ) ) { $wp_query_args['tax_query'][] = array( 'taxonomy' => 'product_tag', - 'field' => 'term_id', + 'field' => 'slug', 'terms' => $args['tag'], ); } + if ( ! empty( $args['shipping_class'] ) ) { + $wp_query_args['tax_query'][] = array( + 'taxonomy' => 'product_shipping_class', + 'field' => 'slug', + 'terms' => $args['shipping_class'], + ); + } + if ( ! is_null( $args['parent'] ) ) { $wp_query_args['post_parent'] = absint( $args['parent'] ); } @@ -156,7 +165,7 @@ function wc_get_products( $args ) { return (object) array( 'products' => $return, 'total' => $products->found_posts, - 'max_num_pages' => $procuts->max_num_pages, + 'max_num_pages' => $products->max_num_pages, ); } else { return $return; @@ -1017,7 +1026,7 @@ function wc_get_related_products_query( $cats_array, $tags_array, $exclude_ids, if ( $cats_array ) { $query['where'] .= " ( tt.taxonomy = 'product_cat' AND t.term_id IN ( {$cats_array} ) ) "; - if ( $relate_by_tag ) { + if ( $tags_array ) { $query['where'] .= ' OR '; } } diff --git a/tests/unit-tests/product/functions.php b/tests/unit-tests/product/functions.php index 78ebf1809ce..1bab388257e 100644 --- a/tests/unit-tests/product/functions.php +++ b/tests/unit-tests/product/functions.php @@ -16,6 +16,9 @@ class WC_Tests_Product_Functions extends WC_Unit_Test_Case { $test_cat_1 = wp_insert_term( 'Testing 1', 'product_cat' ); $test_tag_1 = wp_insert_term( 'Tag 1', 'product_tag' ); $test_tag_2 = wp_insert_term( 'Tag 2', 'product_tag' ); + $term_cat_1 = get_term_by( 'id', $test_cat_1['term_id'], 'product_cat' ); + $term_tag_1 = get_term_by( 'id', $test_tag_1['term_id'], 'product_tag' ); + $term_tag_2 = get_term_by( 'id', $test_tag_2['term_id'], 'product_tag' ); $product = WC_Helper_Product::create_simple_product(); $product->set_tag_ids( array( $test_tag_1['term_id'] ) ); @@ -68,17 +71,17 @@ class WC_Tests_Product_Functions extends WC_Unit_Test_Case { $this->assertContains( $external->get_id(), $products ); // test categories - $products = wc_get_products( array( 'return' => 'ids', 'category' => array( $test_cat_1['term_id'] ) ) ); + $products = wc_get_products( array( 'return' => 'ids', 'category' => array( $term_cat_1->slug ) ) ); $this->assertEquals( 3, count( $products ) ); // test tags - $products = wc_get_products( array( 'return' => 'ids', 'tag' => array( $test_tag_1['term_id'] ) ) ); + $products = wc_get_products( array( 'return' => 'ids', 'tag' => array( $term_tag_1->slug ) ) ); $this->assertEquals( 2, count( $products ) ); - $products = wc_get_products( array( 'return' => 'ids', 'tag' => array( $test_tag_2['term_id'] ) ) ); + $products = wc_get_products( array( 'return' => 'ids', 'tag' => array( $term_tag_2->slug ) ) ); $this->assertEquals( 1, count( $products ) ); - $products = wc_get_products( array( 'return' => 'ids', 'tag' => array( $test_tag_1['term_id'], $test_tag_2['term_id'] ) ) ); + $products = wc_get_products( array( 'return' => 'ids', 'tag' => array( $term_tag_1->slug, $term_tag_2->slug ) ) ); $this->assertEquals( 3, count( $products ) ); // test limit From db9ca040af92c0d0d5e1f49057b50add8b027475 Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Wed, 2 Nov 2016 10:08:05 -0700 Subject: [PATCH 053/163] Handle PR feedback --- includes/abstracts/abstract-wc-product.php | 3 +- .../api/class-wc-rest-products-controller.php | 47 ++++++------ .../api/legacy/v1/class-wc-api-products.php | 31 ++++---- .../api/legacy/v2/class-wc-api-products.php | 67 ++++++++-------- .../api/legacy/v3/class-wc-api-products.php | 76 ++++++++++--------- includes/class-wc-product-grouped.php | 4 +- 6 files changed, 115 insertions(+), 113 deletions(-) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 173d08c8420..762e7fa6bdd 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -1973,7 +1973,8 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return array */ public function get_files() { - $downloadable_files = array_filter( ! empty( $this->get_downloads() ) ? (array) maybe_unserialize( $this->get_downloads() ) : array() ); + $downloads = $this->get_downloads(); + $downloadable_files = array_filter( ! empty( $downloads ) ? (array) maybe_unserialize( $downloads ) : array() ); if ( ! empty( $downloadable_files ) ) { foreach ( $downloadable_files as $key => $file ) { diff --git a/includes/api/class-wc-rest-products-controller.php b/includes/api/class-wc-rest-products-controller.php index 2337af37250..d1587fec9d3 100644 --- a/includes/api/class-wc-rest-products-controller.php +++ b/includes/api/class-wc-rest-products-controller.php @@ -469,20 +469,19 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { * @return array */ protected function get_product_data( $product ) { - $post = get_post( $product->get_id() ); $data = array( 'id' => (int) $product->is_type( 'variation' ) ? $product->get_variation_id() : $product->get_id(), - 'name' => $product->get_title(), - 'slug' => $post->post_name, + 'name' => $product->get_name(), + 'slug' => $product->get_slug(), 'permalink' => $product->get_permalink(), - 'date_created' => wc_rest_prepare_date_response( $post->post_date_gmt ), - 'date_modified' => wc_rest_prepare_date_response( $post->post_modified_gmt ), + 'date_created' => wc_rest_prepare_date_response( $product->get_date_created() ), + 'date_modified' => wc_rest_prepare_date_response( $product->get_date_modified() ), 'type' => $product->get_type(), - 'status' => $post->post_status, + 'status' => $product->get_status(), 'featured' => $product->is_featured(), 'catalog_visibility' => $product->get_catalog_visibility(), - 'description' => wpautop( do_shortcode( $post->post_content ) ), - 'short_description' => apply_filters( 'woocommerce_short_description', $post->post_excerpt ), + 'description' => wpautop( do_shortcode( $product->get_description() ) ), + 'short_description' => apply_filters( 'woocommerce_short_description', $product->get_short_description() ), 'sku' => $product->get_sku(), 'price' => $product->get_price(), 'regular_price' => $product->get_regular_price(), @@ -520,13 +519,13 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { 'shipping_taxable' => $product->is_shipping_taxable(), 'shipping_class' => $product->get_shipping_class(), 'shipping_class_id' => (int) $product->get_shipping_class_id(), - 'reviews_allowed' => ( 'open' === $post->comment_status ), + 'reviews_allowed' => $product->get_reviews_allowed(), 'average_rating' => wc_format_decimal( $product->get_average_rating(), 2 ), 'rating_count' => (int) $product->get_rating_count(), 'related_ids' => array_map( 'absint', array_values( wc_get_related_products( $product->get_id() ) ) ), 'upsell_ids' => array_map( 'absint', $product->get_upsell_ids() ), 'cross_sell_ids' => array_map( 'absint', $product->get_cross_sell_ids() ), - 'parent_id' => $product->is_type( 'variation' ) ? $product->parent->id : $post->post_parent, + 'parent_id' => $product->get_parent_id(), 'purchase_note' => wpautop( do_shortcode( wp_kses_post( $product->get_purchase_note() ) ) ), 'categories' => $this->get_taxonomy_terms( $product ), 'tags' => $this->get_taxonomy_terms( $product, 'tag' ), @@ -866,7 +865,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { } if ( isset( $image['position'] ) && 0 === $image['position'] ) { - $product->set_thumbnail_id( $attachment_id ); + $product->set_image_id( $attachment_id ); } else { $gallery[] = $attachment_id; } @@ -886,7 +885,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { $product->set_gallery_image_ids( $gallery ); } } else { - $product->set_thumbnail_id( '' ); + $product->set_image_id( '' ); $product->set_gallery_image_ids( array() ); } @@ -1192,7 +1191,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { } // Update parent if grouped so price sorting works and stays in sync with the cheapest child. - if ( $product->get_parent_id() > 0 || 'grouped' === $product->get_type() ) { + if ( $product->get_parent_id() > 0 || $product->is_type( 'grouped' ) ) { $clear_parent_ids = array(); @@ -1204,7 +1203,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { $parent->save(); } - if ( 'grouped' === $product->get_type() ) { + if ( $product->is_type( 'grouped' ) ) { $clear_parent_ids[] = $product->get_id(); } @@ -1265,40 +1264,40 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { $backorders = $product->get_backorders(); } - if ( 'grouped' === $product->get_type() ) { + if ( $product->is_type( 'grouped' ) ) { $product->set_manage_stock( 'no' ); $product->set_backorders( 'no' ); - $product->set_stock( '' ); + $product->set_stock_quantity( '' ); $product->set_stock_status( $status ); - } elseif ( 'external' === $product->get_type() ) { + } elseif ( $product->is_type( 'external' ) ) { $product->set_manage_stock( 'no' ); $product->set_backorders( 'no' ); - $product->set_stock( '' ); + $product->set_stock_quantity( '' ); $product->set_stock_status( 'instock' ); } elseif ( 'yes' === $manage_stock ) { $product->set_backorders( $backorders ); // Stock status is always determined by children so sync later. - if ( 'variable' !== $product->get_type() ) { + if ( ! $product->is_type( 'variable' ) ) { $product->set_stock_status( $stock_status ); } // Stock quantity. if ( isset( $request['stock_quantity'] ) ) { - $product->set_stock( wc_stock_amount( $request['stock_quantity'] ) ); + $product->set_stock_quantity( wc_stock_amount( $request['stock_quantity'] ) ); } elseif ( isset( $request['inventory_delta'] ) ) { $stock_quantity = wc_stock_amount( $product->get_stock() ); $stock_quantity += wc_stock_amount( $request['inventory_delta'] ); - $product->set_stock( wc_stock_amount( $stock_quantity ) ); + $product->set_stock_quantity( wc_stock_amount( $stock_quantity ) ); } } else { // Don't manage stock. $product->set_manage_stock( 'no' ); $product->set_backorders( $backorders ); - $product->set_stock( '' ); + $product->set_stock_quantity( '' ); $product->set_stock_status( $stock_status ); } - } elseif ( 'variable' !== $product->get_type() ) { + } elseif ( ! $product->is_type( 'variable' ) ) { $product->set_stock_status( $stock_status ); } @@ -1376,7 +1375,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller { } // Product url and button text for external products. - if ( 'external' === $product->get_type() ) { + if ( $product->is_type( 'external' ) ) { if ( isset( $request['external_url'] ) ) { $product->set_product_url( $request['external_url'] ); } diff --git a/includes/api/legacy/v1/class-wc-api-products.php b/includes/api/legacy/v1/class-wc-api-products.php index 157481faa1d..a2346572e5a 100644 --- a/includes/api/legacy/v1/class-wc-api-products.php +++ b/includes/api/legacy/v1/class-wc-api-products.php @@ -266,12 +266,11 @@ class WC_API_Products extends WC_API_Resource { * @return array */ private function get_product_data( $product ) { - $post = get_post( $product->get_id() ); return array( - 'title' => $product->get_title(), - 'id' => (int) $product->is_type( 'variation' ) ? $product->get_variation_id() : $product->get_id(), - 'created_at' => $this->server->format_datetime( $post->post_date_gmt ), - 'updated_at' => $this->server->format_datetime( $post->post_modified_gmt ), + 'title' => $product->get_name(), + 'id' => (int) $product->is_type( 'variation' ) ? $product->get_variation_id() : $product->get_id(), // @todo variation + 'created_at' => $this->server->format_datetime( $product->get_date_created() ), + 'updated_at' => $this->server->format_datetime( $product->get_date_modified() ), 'type' => $product->get_type(), 'status' => $product->get_status(), 'downloadable' => $product->is_downloadable(), @@ -286,7 +285,7 @@ class WC_API_Products extends WC_API_Resource { 'tax_status' => $product->get_tax_status(), 'tax_class' => $product->get_tax_class(), 'managing_stock' => $product->managing_stock(), - 'stock_quantity' => (int) $product->get_stock_quantity(), + 'stock_quantity' => $product->get_stock_quantity(), 'in_stock' => $product->is_in_stock(), 'backorders_allowed' => $product->backorders_allowed(), 'backordered' => $product->is_on_backorder(), @@ -311,7 +310,7 @@ class WC_API_Products extends WC_API_Resource { 'short_description' => apply_filters( 'woocommerce_short_description', $product->get_short_description() ), 'reviews_allowed' => $product->get_reviews_allowed(), 'average_rating' => wc_format_decimal( $product->get_average_rating(), 2 ), - 'rating_count' => (int) $product->get_rating_count(), + 'rating_count' => $product->get_rating_count(), 'related_ids' => array_map( 'absint', array_values( wc_get_related_products( $product->get_id() ) ) ), 'upsell_ids' => array_map( 'absint', $product->get_upsell_ids() ), 'cross_sell_ids' => array_map( 'absint', $product->get_cross_sell_ids() ), @@ -321,11 +320,11 @@ class WC_API_Products extends WC_API_Resource { 'featured_src' => wp_get_attachment_url( get_post_thumbnail_id( $product->is_type( 'variation' ) ? $product->variation_id : $product->get_id() ) ), 'attributes' => $this->get_attributes( $product ), 'downloads' => $this->get_downloads( $product ), - 'download_limit' => (int) $product->get_download_limit(), - 'download_expiry' => (int) $product->get_download_expiry(), + 'download_limit' => $product->get_download_limit(), + 'download_expiry' => $product->get_download_expiry(), 'download_type' => 'standard', 'purchase_note' => apply_filters( 'the_content', $product->get_purchase_note() ), - 'total_sales' => metadata_exists( 'post', $product->get_id(), 'total_sales' ) ? (int) get_post_meta( $product->get_id(), 'total_sales', true ) : 0, + 'total_sales' => $product->get_total_sales(), 'variations' => array(), 'parent' => array(), ); @@ -401,8 +400,8 @@ class WC_API_Products extends WC_API_Resource { * @return array */ private function get_images( $product ) { - - $images = $attachment_ids = array(); + $images = $attachment_ids = array(); + $product_image = $product->get_image_id(); if ( $product->is_type( 'variation' ) ) { // @todo variation @@ -411,16 +410,16 @@ class WC_API_Products extends WC_API_Resource { // add variation image if set $attachment_ids[] = get_post_thumbnail_id( $product->get_variation_id() ); - } elseif ( ! empty( $product->get_thumbnail_id() ) ) { + } elseif ( ! empty( $product_image ) ) { // otherwise use the parent product featured image if set - $attachment_ids[] = $product->get_thumbnail_id(); + $attachment_ids[] = $product_image; } } else { // Add featured image - if ( ! empty( $product->get_thumbnail_id() ) ) { - $attachment_ids[] = $product->get_thumbnail_id(); + if ( ! empty( $product_image ) ) { + $attachment_ids[] = $product_image; } // add gallery images diff --git a/includes/api/legacy/v2/class-wc-api-products.php b/includes/api/legacy/v2/class-wc-api-products.php index 28c96a8e67e..5ea18323b93 100644 --- a/includes/api/legacy/v2/class-wc-api-products.php +++ b/includes/api/legacy/v2/class-wc-api-products.php @@ -268,7 +268,7 @@ class WC_API_Products extends WC_API_Resource { $id = $product->get_id(); // Checks for an error in the product creation - if ( ! $id > 0 ) { + if ( 0 >= $id ) { throw new WC_API_Exception( 'woocommerce_api_cannot_create_product', $id->get_error_message(), 400 ); } @@ -679,12 +679,11 @@ class WC_API_Products extends WC_API_Resource { */ private function get_product_data( $product ) { $prices_precision = wc_get_price_decimals(); - $post = get_post( $product->get_id() ); return array( - 'title' => $product->get_title(), - 'id' => (int) $product->is_type( 'variation' ) ? $product->get_variation_id() : $product->get_id(), - 'created_at' => $this->server->format_datetime( $post->post_date_gmt ), - 'updated_at' => $this->server->format_datetime( $post->post_modified_gmt ), + 'title' => $product->get_name(), + 'id' => (int) $product->is_type( 'variation' ) ? $product->get_variation_id() : $product->get_id(), // @todo variation + 'created_at' => $this->server->format_datetime( $product->get_date_created() ), + 'updated_at' => $this->server->format_datetime( $product->get_date_modified() ), 'type' => $product->get_type(), 'status' => $product->get_status(), 'downloadable' => $product->is_downloadable(), @@ -699,7 +698,7 @@ class WC_API_Products extends WC_API_Resource { 'tax_status' => $product->get_tax_status(), 'tax_class' => $product->get_tax_class(), 'managing_stock' => $product->managing_stock(), - 'stock_quantity' => (int) $product->get_stock_quantity(), + 'stock_quantity' => $product->get_stock_quantity(), 'in_stock' => $product->is_in_stock(), 'backorders_allowed' => $product->backorders_allowed(), 'backordered' => $product->is_on_backorder(), @@ -726,7 +725,7 @@ class WC_API_Products extends WC_API_Resource { 'short_description' => apply_filters( 'woocommerce_short_description', $product->get_short_description() ), 'reviews_allowed' => $product->get_reviews_allowed(), 'average_rating' => wc_format_decimal( $product->get_average_rating(), 2 ), - 'rating_count' => (int) $product->get_rating_count(), + 'rating_count' => $product->get_rating_count(), 'related_ids' => array_map( 'absint', array_values( wc_get_related_products( $product->get_id() ) ) ), 'upsell_ids' => array_map( 'absint', $product->get_upsell_ids() ), 'cross_sell_ids' => array_map( 'absint', $product->get_cross_sell_ids() ), @@ -734,11 +733,11 @@ class WC_API_Products extends WC_API_Resource { 'categories' => wp_get_post_terms( $product->get_id(), 'product_cat', array( 'fields' => 'names' ) ), 'tags' => wp_get_post_terms( $product->get_id(), 'product_tag', array( 'fields' => 'names' ) ), 'images' => $this->get_images( $product ), - 'featured_src' => (string) wp_get_attachment_url( get_post_thumbnail_id( $product->is_type( 'variation' ) ? $product->variation_id : $product->get_id() ) ), + 'featured_src' => wp_get_attachment_url( get_post_thumbnail_id( $product->is_type( 'variation' ) ? $product->variation_id : $product->get_id() ) ), 'attributes' => $this->get_attributes( $product ), 'downloads' => $this->get_downloads( $product ), - 'download_limit' => (int) $product->get_download_limit(), - 'download_expiry' => (int) $product->get_download_expiry(), + 'download_limit' => $product->get_download_limit(), + 'download_expiry' => $product->get_download_expiry(), 'download_type' => 'standard', 'purchase_note' => wpautop( do_shortcode( wp_kses_post( $product->get_purchase_note() ) ) ), 'total_sales' => $product->get_total_sales(), @@ -928,7 +927,7 @@ class WC_API_Products extends WC_API_Resource { $attribute_object->set_id( $attribute_id ); $attribute_object->set_name( $taxonomy ); $attribute_object->set_options( $values ); - $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); + $attribute_object->set_position( isset( $attribute['position'] ) ? absint( $attribute['position'] ) : 0 ); $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); $attributes[] = $attribute_object; @@ -947,7 +946,7 @@ class WC_API_Products extends WC_API_Resource { $attribute_object = new WC_Product_Attribute(); $attribute_object->set_name( $attribute['name'] ); $attribute_object->set_options( $values ); - $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); + $attribute_object->set_position( isset( $attribute['position'] ) ? absint( $attribute['position'] ) : 0 ); $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); $attributes[] = $attribute_object; @@ -1020,7 +1019,7 @@ class WC_API_Products extends WC_API_Resource { } // Update parent if grouped so price sorting works and stays in sync with the cheapest child - if ( $product->get_parent_id() > 0 || 'grouped' === $product->get_type() ) { + if ( $product->get_parent_id() > 0 || $product->is_type( 'grouped' ) ) { if ( $product->get_parent_id() > 0 ) { $parent = wc_get_product( $product->get_parent_id() ); $children = $parent->get_children(); @@ -1068,36 +1067,36 @@ class WC_API_Products extends WC_API_Resource { $backorders = $product->get_backorders(); } - if ( 'grouped' == $product->get_type() ) { + if ( $product->is_type( 'grouped' ) ) { $product->set_manage_stock( 'no' ); $product->set_backorders( 'no' ); - $product->set_stock( '' ); + $product->set_stock_quantity( '' ); $product->set_stock_status( $stock_status ); - } elseif ( 'external' == $product->get_type() ) { + } elseif ( $product->is_type( 'external' ) ) { $product->set_manage_stock( 'no' ); $product->set_backorders( 'no' ); - $product->set_stock( '' ); + $product->set_stock_quantity( '' ); $product->set_stock_status( 'instock' ); } elseif ( 'yes' == $managing_stock ) { $product->set_backorders( $backorders ); // Stock status is always determined by children so sync later. - if ( 'variable' !== $product->get_type() ) { + if ( ! $product->is_type( 'variable' ) ) { $product->set_stock_status( $stock_status ); } // Stock quantity if ( isset( $data['stock_quantity'] ) ) { - $product->set_stock( wc_stock_amount( $data['stock_quantity'] ) ); + $product->set_stock_quantity( wc_stock_amount( $data['stock_quantity'] ) ); } } else { // Don't manage stock. $product->set_manage_stock( 'no' ); $product->set_backorders( $backorders ); - $product->set_stock( '' ); + $product->set_stock_quantity( '' ); $product->set_stock_status( $stock_status ); } - } elseif ( 'variable' !== $product->get_type() ) { + } elseif ( ! $product->is_type( 'variable' ) ) { $product->set_stock_status( $stock_status ); } @@ -1177,7 +1176,7 @@ class WC_API_Products extends WC_API_Resource { } // Product url - if ( 'external' === $product->get_type() ) { + if ( $product->is_type( 'external' ) ) { if ( isset( $data['product_url'] ) ) { $product->set_product_url( $data['product_url'] ); } @@ -1639,25 +1638,25 @@ class WC_API_Products extends WC_API_Resource { * @return array */ private function get_images( $product ) { - - $images = $attachment_ids = array(); + $images = $attachment_ids = array(); + $product_image = $product->get_image_id(); if ( $product->is_type( 'variation' ) ) { - + // @todo variation if ( has_post_thumbnail( $product->get_variation_id() ) ) { // Add variation image if set $attachment_ids[] = get_post_thumbnail_id( $product->get_variation_id() ); - } elseif ( ! empty( $product->get_thumbnail_id() ) ) { + } elseif ( ! empty( $product_image ) ) { // Otherwise use the parent product featured image if set - $attachment_ids[] = $product->get_thumbnail_id(); + $attachment_ids[] = $product_image; } } else { // Add featured image - if ( ! empty( $product->get_thumbnail_id() ) ) { - $attachment_ids[] = $product->get_thumbnail_id(); + if ( ! empty( $product_image ) ) { + $attachment_ids[] = $product_image; } // Add gallery images @@ -1733,7 +1732,7 @@ class WC_API_Products extends WC_API_Resource { $attachment_id = $this->set_product_image_as_attachment( $upload, $product->get_id() ); } - $product->set_thumbnail_id( $attachment_id ); + $product->set_image_id( $attachment_id ); } else { $attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0; @@ -1752,11 +1751,11 @@ class WC_API_Products extends WC_API_Resource { } if ( ! empty( $gallery ) ) { - $product->set_gallery_attachment_ids( $gallery ); + $product->set_gallery_image_ids( $gallery ); } } else { - $product->set_thumbnail_id( '' ); - $product->set_gallery_attachment_ids( array() ); + $product->set_image_id( '' ); + $product->set_gallery_image_ids( array() ); } return $product; diff --git a/includes/api/legacy/v3/class-wc-api-products.php b/includes/api/legacy/v3/class-wc-api-products.php index e3464114a39..33ac4dac94e 100644 --- a/includes/api/legacy/v3/class-wc-api-products.php +++ b/includes/api/legacy/v3/class-wc-api-products.php @@ -207,9 +207,12 @@ class WC_API_Products extends WC_API_Resource { $product_data['grouped_products'] = $this->get_grouped_products_data( $product ); } - if ( $product->is_type( 'simple' ) && ! empty( $product->get_parent_id() ) ) { - $_product = wc_get_product( $product->get_parent_id() ); - $product_data['parent'] = $this->get_product_data( $_product ); + if ( $product->is_type( 'simple' ) ) { + $parent_id = $product->get_parent_id(); + if ( ! empty( $parent_id ) ) { + $_product = wc_get_product( $parent_id ); + $product_data['parent'] = $this->get_product_data( $_product ); + } } return array( 'product' => apply_filters( 'woocommerce_api_product_response', $product_data, $product, $fields, $this->server ) ); @@ -319,7 +322,7 @@ class WC_API_Products extends WC_API_Resource { $id = $product->get_id(); // Checks for an error in the product creation. - if ( ! $id > 0 ) { + if ( 0 >= $id ) { throw new WC_API_Exception( 'woocommerce_api_cannot_create_product', $id->get_error_message(), 400 ); } @@ -1121,12 +1124,11 @@ class WC_API_Products extends WC_API_Resource { * @return WC_Product */ private function get_product_data( $product ) { - $post = get_post( $product->get_id() ); return array( - 'title' => $product->get_title(), - 'id' => (int) $product->is_type( 'variation' ) ? $product->get_variation_id() : $product->get_id(), - 'created_at' => $this->server->format_datetime( $post->post_date_gmt ), - 'updated_at' => $this->server->format_datetime( $post->post_modified_gmt ), + 'title' => $product->get_name(), + 'id' => (int) $product->is_type( 'variation' ) ? $product->get_variation_id() : $product->get_id(), // @todo variation + 'created_at' => $this->server->format_datetime( $product->get_date_created() ), + 'updated_at' => $this->server->format_datetime( $product->get_date_modified() ), 'type' => $product->get_type(), 'status' => $product->get_status(), 'downloadable' => $product->is_downloadable(), @@ -1166,9 +1168,9 @@ class WC_API_Products extends WC_API_Resource { 'shipping_class_id' => ( 0 !== $product->get_shipping_class_id() ) ? $product->get_shipping_class_id() : null, 'description' => wpautop( do_shortcode( $product->get_description() ) ), 'short_description' => apply_filters( 'woocommerce_short_description', $product->get_short_description() ), - 'reviews_allowed' => ( 'open' === $product->get_post_data()->comment_status ), + 'reviews_allowed' => $product->get_reviews_allowed(), 'average_rating' => wc_format_decimal( $product->get_average_rating(), 2 ), - 'rating_count' => (int) $product->get_rating_count(), + 'rating_count' => $product->get_rating_count(), 'related_ids' => array_map( 'absint', array_values( wc_get_related_products( $product->get_id() ) ) ), 'upsell_ids' => array_map( 'absint', $product->get_upsell_ids() ), 'cross_sell_ids' => array_map( 'absint', $product->get_cross_sell_ids() ), @@ -1176,11 +1178,11 @@ class WC_API_Products extends WC_API_Resource { 'categories' => wp_get_post_terms( $product->get_id(), 'product_cat', array( 'fields' => 'names' ) ), 'tags' => wp_get_post_terms( $product->get_id(), 'product_tag', array( 'fields' => 'names' ) ), 'images' => $this->get_images( $product ), - 'featured_src' => (string) wp_get_attachment_url( get_post_thumbnail_id( $product->is_type( 'variation' ) ? $product->variation_id : $product->get_id() ) ), + 'featured_src' => wp_get_attachment_url( get_post_thumbnail_id( $product->is_type( 'variation' ) ? $product->variation_id : $product->get_id() ) ), 'attributes' => $this->get_attributes( $product ), 'downloads' => $this->get_downloads( $product ), - 'download_limit' => (int) $product->get_download_limit(), - 'download_expiry' => (int) $product->get_download_expiry(), + 'download_limit' => $product->get_download_limit(), + 'download_expiry' => $product->get_download_expiry(), 'download_type' => 'standard', 'purchase_note' => wpautop( do_shortcode( wp_kses_post( $product->get_purchase_note() ) ) ), 'total_sales' => $product->get_total_sales(), @@ -1415,7 +1417,7 @@ class WC_API_Products extends WC_API_Resource { $attribute_object->set_id( $attribute_id ); $attribute_object->set_name( $taxonomy ); $attribute_object->set_options( $values ); - $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); + $attribute_object->set_position( isset( $attribute['position'] ) ? absint( $attribute['position'] ) : 0 ); $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); $attributes[] = $attribute_object; @@ -1434,7 +1436,7 @@ class WC_API_Products extends WC_API_Resource { $attribute_object = new WC_Product_Attribute(); $attribute_object->set_name( $attribute['name'] ); $attribute_object->set_options( $values ); - $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); + $attribute_object->set_position( isset( $attribute['position'] ) ? absint( $attribute['position'] ) : 0 ); $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); $attributes[] = $attribute_object; @@ -1506,7 +1508,7 @@ class WC_API_Products extends WC_API_Resource { } // Update parent if grouped so price sorting works and stays in sync with the cheapest child. - if ( $product->get_parent_id() > 0 || 'grouped' === $product->get_type() ) { + if ( $product->get_parent_id() > 0 || $product->is_type( 'grouped' ) ) { if ( $product->get_parent_id() > 0 ) { $parent = wc_get_product( $product->get_parent_id() ); $children = $parent->get_children(); @@ -1554,40 +1556,40 @@ class WC_API_Products extends WC_API_Resource { $backorders = $product->get_backorders(); } - if ( 'grouped' == $product->get_type() ) { + if ( $product->is_type( 'grouped' ) ) { $product->set_manage_stock( 'no' ); $product->set_backorders( 'no' ); - $product->set_stock( '' ); + $product->set_stock_quantity( '' ); $product->set_stock_status( $stock_status ); - } elseif ( 'external' == $product->get_type() ) { + } elseif ( $product->is_type( 'external' ) ) { $product->set_manage_stock( 'no' ); $product->set_backorders( 'no' ); - $product->set_stock( '' ); + $product->set_stock_quantity( '' ); $product->set_stock_status( 'instock' ); } elseif ( 'yes' == $managing_stock ) { $product->set_backorders( $backorders ); // Stock status is always determined by children so sync later. - if ( 'variable' !== $product->get_type() ) { + if ( ! $product->is_type( 'variable' ) ) { $product->set_stock_status( $stock_status ); } // Stock quantity. if ( isset( $data['stock_quantity'] ) ) { - $product->set_stock( wc_stock_amount( $data['stock_quantity'] ) ); + $product->set_stock_quantity( wc_stock_amount( $data['stock_quantity'] ) ); } else if ( isset( $data['inventory_delta'] ) ) { $stock_quantity = wc_stock_amount( $product->get_stock() ); $stock_quantity += wc_stock_amount( $data['inventory_delta'] ); - $product->set_stock( wc_stock_amount( $stock_quantity ) ); + $product->set_stock_quantity( wc_stock_amount( $stock_quantity ) ); } } else { // Don't manage stock. $product->set_manage_stock( 'no' ); $product->set_backorders( $backorders ); - $product->set_stock( '' ); + $product->set_stock_quantity( '' ); $product->set_stock_status( $stock_status ); } - } elseif ( 'variable' !== $product->get_type() ) { + } elseif ( ! $product->is_type( 'variable' ) ) { $product->set_stock_status( $stock_status ); } @@ -1667,7 +1669,7 @@ class WC_API_Products extends WC_API_Resource { } // Product url. - if ( 'external' === $product->get_type() ) { + if ( $product->is_type( 'external' ) ) { if ( isset( $data['product_url'] ) ) { $product->set_product_url( $data['product_url'] ); } @@ -2161,8 +2163,8 @@ class WC_API_Products extends WC_API_Resource { * @return array */ private function get_images( $product ) { - - $images = $attachment_ids = array(); + $images = $attachment_ids = array(); + $product_image = $product->get_image_id(); if ( $product->is_type( 'variation' ) ) { // @todo variation @@ -2171,15 +2173,15 @@ class WC_API_Products extends WC_API_Resource { // Add variation image if set $attachment_ids[] = get_post_thumbnail_id( $product->get_variation_id() ); - } elseif ( ! empty( $product->get_thumbnail_id() ) ) { + } elseif ( ! empty( $product_image ) ) { // Otherwise use the parent product featured image if set - $attachment_ids[] = $product->get_thumbnail_id(); + $attachment_ids[] = $product_image; } } else { // Add featured image - if ( ! empty( $product->get_thumbnail_id() ) ) { - $attachment_ids[] = $product->get_thumbnail_id(); + if ( ! empty( $product_image ) ) { + $attachment_ids[] = $product_image; } // Add gallery images @@ -2256,7 +2258,7 @@ class WC_API_Products extends WC_API_Resource { $attachment_id = $this->set_product_image_as_attachment( $upload, $product->get_id() ); } - $product->set_thumbnail_id( $attachment_id ); + $product->set_image_id( $attachment_id ); } else { $attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0; @@ -2285,11 +2287,11 @@ class WC_API_Products extends WC_API_Resource { } if ( ! empty( $gallery ) ) { - $product->set_gallery_attachment_ids( $gallery ); + $product->set_gallery_image_ids( $gallery ); } } else { - $product->set_thumbnail_id( '' ); - $product->set_gallery_attachment_ids( array() ); + $product->set_image_id( '' ); + $product->set_gallery_image_ids( array() ); } return $product; diff --git a/includes/class-wc-product-grouped.php b/includes/class-wc-product-grouped.php index 85c8003c1c7..2f462e9cdb9 100644 --- a/includes/class-wc-product-grouped.php +++ b/includes/class-wc-product-grouped.php @@ -178,7 +178,9 @@ class WC_Product_Grouped extends WC_Product { $child_prices = array(); foreach ( $this->get_children() as $child_id ) { $child = wc_get_product( $child_id ); - $child_prices[] = $child->get_price(); + if ( $child ) { + $child_prices[] = $child->get_price(); + } } $child_prices = array_filter( $child_prices ); delete_post_meta( $this->get_id(), '_price' ); From ad37a68ffb1c06b8e4168719fec824cc2b18cf21 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 3 Nov 2016 12:03:19 +0000 Subject: [PATCH 054/163] [Product CRUD] Getter setter proxy methods (#12236) * Started on variation changes * Stock functions * Variation class * Bulk change ->id to get_id() to fix variation form display * Missing status * Fix add to cart * Start on stored data save * save variation * Save_variations * Variation edit panel * Save variations code works. * Remove stored data code and fix save * Improve legacy class * wc_bool_to_string * prepare_set_attributes * Use wc_get_products * More feedback fixes * get_prop implementation in abstract and data classes * Implement set_prop * Change handling * Array key exists * set_object_read --- includes/abstracts/abstract-wc-data.php | 112 +++++- includes/abstracts/abstract-wc-product.php | 406 ++++++++++++--------- includes/class-wc-product-external.php | 50 +-- includes/class-wc-product-grouped.php | 48 ++- includes/class-wc-product-simple.php | 2 +- includes/class-wc-product-variable.php | 2 +- includes/class-wc-product-variation.php | 9 +- includes/wc-deprecated-functions.php | 20 +- 8 files changed, 418 insertions(+), 231 deletions(-) diff --git a/includes/abstracts/abstract-wc-data.php b/includes/abstracts/abstract-wc-data.php index d1aedce5fa7..6260f5f7c4e 100644 --- a/includes/abstracts/abstract-wc-data.php +++ b/includes/abstracts/abstract-wc-data.php @@ -27,6 +27,18 @@ abstract class WC_Data { */ protected $data = array(); + /** + * Core data changes for this object. + * @var array + */ + protected $changes = array(); + + /** + * This is false until the object is read from the DB. + * @var bool + */ + protected $object_read = false; + /** * Set to _data on construct so we can track and reset data if needed. * @var array @@ -106,8 +118,18 @@ abstract class WC_Data { /** * Save should create or update based on object existance. + * + * @return int */ - public function save() {} + public function save() { + if ( $this->get_id() ) { + $this->update(); + } else { + $this->create(); + } + $this->apply_changes(); + return $this->get_id(); + } /** * Change data to JSON format. @@ -401,7 +423,17 @@ abstract class WC_Data { * Set all props to default values. */ protected function set_defaults() { - $this->data = $this->default_data; + $this->data = $this->default_data; + $this->changes = array(); + $this->set_object_read( false ); + } + + /** + * Set object read property. + * @param boolean $read + */ + public function set_object_read( $read = true ) { + $this->object_read = (bool) $read; } /** @@ -410,7 +442,7 @@ abstract class WC_Data { * @param array $props Key value pairs to set. Key is the prop and should map to a setter function name. * @return WP_Error|bool */ - public function set_props( $props ) { + public function set_props( $props, $context = 'set' ) { $errors = new WP_Error(); foreach ( $props as $prop => $value ) { @@ -434,6 +466,80 @@ abstract class WC_Data { return sizeof( $errors->get_error_codes() ) ? $errors : true; } + /** + * Sets a prop for a setter method. + * + * This stores changes in a special array so we can track what needs saving + * the the DB later. + * + * @since 2.7.0 + * @param string $prop Name of prop to set. + * @param mixed $value Value of the prop. + */ + protected function set_prop( $prop, $value ) { + if ( array_key_exists( $prop, $this->data ) ) { + if ( true === $this->object_read ) { + $this->changes[ $prop ] = $value; + } else { + $this->data[ $prop ] = $value; + } + } + } + + /** + * Return data changes only. + * + * @since 2.7.0 + * @return array + */ + protected function get_changes() { + return $this->changes; + } + + /** + * Merge changes with data and clear. + * + * @since 2.7.0 + */ + protected function apply_changes() { + $this->data = array_merge( $this->data, $this->changes ); + $this->changes = array(); + } + + /** + * Prefix for action and filter hooks on data. + * + * @since 2.7.0 + * @return string + */ + protected function get_hook_prefix() { + return 'woocommerce_get_'; + } + + /** + * Gets a prop for a getter method. + * + * Gets the value from either current pending changes, or the data itself. + * Context controls what happens to the value before it's returned. + * + * @since 2.7.0 + * @param string $prop Name of prop to get. + * @param string $context What the value is for. Valid values are view and edit. + * @return mixed + */ + public function get_prop( $prop, $context = 'view' ) { + $value = null; + + if ( array_key_exists( $prop, $this->data ) ) { + $value = isset( $this->changes[ $prop ] ) ? $this->changes[ $prop ] : $this->data[ $prop ]; + + if ( 'view' === $context ) { + $value = apply_filters( $this->get_hook_prefix() . $prop, $value, $this ); + } + } + return $value; + } + /** * When invalid data is found, throw an exception unless reading from the DB. * @param string $error_code Error code. diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 762e7fa6bdd..2b1ee2e4f87 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -142,13 +142,15 @@ class WC_Product extends WC_Abstract_Legacy_Product { } } - /* - |-------------------------------------------------------------------------- - | Getters - |-------------------------------------------------------------------------- - | - | Methods for getting data from the product object. - */ + /** + * Prefix for action and filter hooks on data. + * + * @since 2.7.0 + * @return string + */ + protected function get_hook_prefix() { + return 'woocommerce_product_get_'; + } /** * Get internal type. Should return string and *should be overridden* by child classes. @@ -160,444 +162,494 @@ class WC_Product extends WC_Abstract_Legacy_Product { return $this->product_type; } + /* + |-------------------------------------------------------------------------- + | Getters + |-------------------------------------------------------------------------- + | + | Methods for getting data from the product object. + */ + /** * Get product name. * * @since 2.7.0 + * @param string $context * @return string */ - public function get_name() { - return apply_filters( 'woocommerce_product_get_name', $this->data['name'], $this ); + public function get_name( $context = 'view' ) { + return $this->get_prop( 'name', $context ); } /** * Get product slug. + * * @since 2.7.0 + * @param string $context * @return string */ - public function get_slug() { - return $this->data['slug']; + public function get_slug( $context = 'view' ) { + return $this->get_prop( 'slug', $context ); } /** * Get product created date. * * @since 2.7.0 + * @param string $context * @return string Timestamp. */ - public function get_date_created() { - return $this->data['date_created']; + public function get_date_created( $context = 'view' ) { + return $this->get_prop( 'date_created', $context ); } /** * Get product modified date. * * @since 2.7.0 + * @param string $context * @return string Timestamp. */ - public function get_date_modified() { - return $this->data['date_modified']; + public function get_date_modified( $context = 'view' ) { + return $this->get_prop( 'date_modified', $context ); } /** * Get product status. * * @since 2.7.0 + * @param string $context * @return string */ - public function get_status() { - return $this->data['status']; + public function get_status( $context = 'view' ) { + return $this->get_prop( 'status', $context ); } /** * If the product is featured. * * @since 2.7.0 + * @param string $context * @return boolean */ - public function get_featured() { - return $this->data['featured']; + public function get_featured( $context = 'view' ) { + return $this->get_prop( 'featured', $context ); } /** * Get catalog visibility. * * @since 2.7.0 + * @param string $context * @return string */ - public function get_catalog_visibility() { - return $this->data['catalog_visibility']; + public function get_catalog_visibility( $context = 'view' ) { + return $this->get_prop( 'catalog_visibility', $context ); } /** * Get product description. * * @since 2.7.0 + * @param string $context * @return string */ - public function get_description() { - return $this->data['description']; + public function get_description( $context = 'view' ) { + return $this->get_prop( 'description', $context ); } /** * Get product short description. * * @since 2.7.0 + * @param string $context * @return string */ - public function get_short_description() { - return $this->data['short_description']; + public function get_short_description( $context = 'view' ) { + return $this->get_prop( 'short_description', $context ); } /** * Get SKU (Stock-keeping unit) - product unique ID. * + * @param string $context * @return string */ - public function get_sku() { - return apply_filters( 'woocommerce_get_sku', $this->data['sku'], $this ); + public function get_sku( $context = 'view' ) { + return $this->get_prop( 'sku', $context ); } /** * Returns the product's active price. * + * @param string $context * @return string price */ - public function get_price() { - return apply_filters( 'woocommerce_get_price', $this->data['price'], $this ); + public function get_price( $context = 'view' ) { + return $this->get_prop( 'price', $context ); } /** * Returns the product's regular price. * + * @param string $context * @return string price */ - public function get_regular_price() { - return apply_filters( 'woocommerce_get_regular_price', $this->data['regular_price'], $this ); + public function get_regular_price( $context = 'view' ) { + return $this->get_prop( 'regular_price', $context ); } /** * Returns the product's sale price. * + * @param string $context * @return string price */ - public function get_sale_price() { - return apply_filters( 'woocommerce_get_sale_price', $this->data['sale_price'], $this ); + public function get_sale_price( $context = 'view' ) { + return $this->get_prop( 'sale_price', $context ); } /** * Get date on sale from. * * @since 2.7.0 + * @param string $context * @return string */ - public function get_date_on_sale_from() { - return $this->data['date_on_sale_from']; + public function get_date_on_sale_from( $context = 'view' ) { + return $this->get_prop( 'date_on_sale_from', $context ); } /** * Get date on sale to. * * @since 2.7.0 + * @param string $context * @return string */ - public function get_date_on_sale_to() { - return $this->data['date_on_sale_to']; + public function get_date_on_sale_to( $context = 'view' ) { + return $this->get_prop( 'date_on_sale_to', $context ); } /** * Get number total of sales. * * @since 2.7.0 + * @param string $context * @return int */ - public function get_total_sales() { - return $this->data['total_sales']; + public function get_total_sales( $context = 'view' ) { + return $this->get_prop( 'total_sales', $context ); } /** * Returns the tax status. * + * @param string $context * @return string */ - public function get_tax_status() { - return $this->data['tax_status']; + public function get_tax_status( $context = 'view' ) { + return $this->get_prop( 'tax_status', $context ); } /** * Returns the tax class. - * @param bool $raw Get unfiltered value. + * + * @param string $context * @return string */ - public function get_tax_class( $raw = false ) { - if ( $raw ) { - return $this->data['tax_class']; - } else { - return apply_filters( 'woocommerce_product_tax_class', $this->data['tax_class'], $this ); - } + public function get_tax_class( $context = 'view' ) { + return $this->get_prop( 'tax_class', $context ); } /** * Return if product manage stock. * * @since 2.7.0 + * @param string $context * @return boolean */ - public function get_manage_stock() { - return $this->data['manage_stock']; + public function get_manage_stock( $context = 'view' ) { + return $this->get_prop( 'manage_stock', $context ); } /** * Returns number of items available for sale. * + * @param string $context * @return int|null */ - public function get_stock_quantity() { - return apply_filters( 'woocommerce_get_stock_quantity', $this->get_manage_stock() ? $this->data['stock_quantity'] : null, $this ); + public function get_stock_quantity( $context = 'view' ) { + return $this->get_prop( 'stock_quantity', $context ); } /** * Return the stock status. * + * @param string $context * @since 2.7.0 * @return string */ - public function get_stock_status() { - return $this->data['stock_status']; + public function get_stock_status( $context = 'view' ) { + return $this->get_prop( 'stock_status', $context ); } /** * Get backorders. * + * @param string $context * @since 2.7.0 * @return string yes no or notify */ - public function get_backorders() { - return $this->data['backorders']; + public function get_backorders( $context = 'view' ) { + return $this->get_prop( 'backorders', $context ); } /** * Return if should be sold individually. * + * @param string $context * @since 2.7.0 * @return boolean */ - public function get_sold_individually() { - return $this->data['sold_individually']; + public function get_sold_individually( $context = 'view' ) { + return $this->get_prop( 'sold_individually', $context ); } /** * Returns the product's weight. * + * @param string $context * @return string */ - public function get_weight() { - return apply_filters( 'woocommerce_product_get_weight', $this->data['weight'], $this ); + public function get_weight( $context = 'view' ) { + return $this->get_prop( 'weight', $context ); } /** * Returns the product length. * + * @param string $context * @return string */ - public function get_length() { - return apply_filters( 'woocommerce_product_get_length', $this->data['length'], $this ); + public function get_length( $context = 'view' ) { + return $this->get_prop( 'length', $context ); } /** * Returns the product width. * + * @param string $context * @return string */ - public function get_width() { - return apply_filters( 'woocommerce_product_get_width', $this->data['width'], $this ); + public function get_width( $context = 'view' ) { + return $this->get_prop( 'width', $context ); } /** * Returns the product height. * + * @param string $context * @return string */ - public function get_height() { - return apply_filters( 'woocommerce_product_get_height', $this->data['height'], $this ); + public function get_height( $context = 'view' ) { + return $this->get_prop( 'height', $context ); } /** * Get upsel IDs. * * @since 2.7.0 + * @param string $context * @return array */ - public function get_upsell_ids() { - return $this->data['upsell_ids']; + public function get_upsell_ids( $context = 'view' ) { + return $this->get_prop( 'upsell_ids', $context ); } /** * Get cross sell IDs. * * @since 2.7.0 + * @param string $context * @return array */ - public function get_cross_sell_ids() { - return $this->data['cross_sell_ids']; + public function get_cross_sell_ids( $context = 'view' ) { + return $this->get_prop( 'cross_sell_ids', $context ); } /** * Get parent ID. * * @since 2.7.0 + * @param string $context * @return int */ - public function get_parent_id() { - return $this->data['parent_id']; + public function get_parent_id( $context = 'view' ) { + return $this->get_prop( 'parent_id', $context ); } /** * Return if reviews is allowed. * * @since 2.7.0 + * @param string $context * @return bool */ - public function get_reviews_allowed() { - return $this->data['reviews_allowed']; + public function get_reviews_allowed( $context = 'view' ) { + return $this->get_prop( 'reviews_allowed', $context ); } /** * Get purchase note. * * @since 2.7.0 + * @param string $context * @return string */ - public function get_purchase_note() { - return $this->data['purchase_note']; + public function get_purchase_note( $context = 'view' ) { + return $this->get_prop( 'purchase_note', $context ); } /** * Returns product attributes. * + * @param string $context * @return array */ - public function get_attributes() { - return apply_filters( 'woocommerce_get_product_attributes', $this->data['attributes'] ); + public function get_attributes( $context = 'view' ) { + return $this->get_prop( 'attributes', $context ); } /** * Get default attributes. * * @since 2.7.0 + * @param string $context * @return array */ - public function get_default_attributes() { - return $this->data['default_attributes']; + public function get_default_attributes( $context = 'view' ) { + return $this->get_prop( 'default_attributes', $context ); } /** * Get menu order. * * @since 2.7.0 + * @param string $context * @return int */ - public function get_menu_order() { - return $this->data['menu_order']; + public function get_menu_order( $context = 'view' ) { + return $this->get_prop( 'menu_order', $context ); } /** * Get category ids. * * @since 2.7.0 + * @param string $context * @return array */ - public function get_category_ids() { - return $this->data['category_ids']; + public function get_category_ids( $context = 'view' ) { + return $this->get_prop( 'category_ids', $context ); } /** * Get tag ids. * * @since 2.7.0 + * @param string $context * @return array */ - public function get_tag_ids() { - return $this->data['tag_ids']; + public function get_tag_ids( $context = 'view' ) { + return $this->get_prop( 'tag_ids', $context ); } /** * Get virtual. * * @since 2.7.0 + * @param string $context * @return bool */ - public function get_virtual() { - return $this->data['virtual']; + public function get_virtual( $context = 'view' ) { + return $this->get_prop( 'virtual', $context ); } /** * Returns the gallery attachment ids. * + * @param string $context * @return array */ - public function get_gallery_image_ids() { - return apply_filters( 'woocommerce_product_gallery_attachment_ids', array_filter( array_filter( $this->data['gallery_image_ids'] ), 'wp_attachment_is_image' ), $this ); + public function get_gallery_image_ids( $context = 'view' ) { + return $this->get_prop( 'gallery_image_ids', $context ); } /** * Get shipping class ID. * * @since 2.7.0 + * @param string $context * @return int */ - public function get_shipping_class_id() { - return $this->data['shipping_class_id']; + public function get_shipping_class_id( $context = 'view' ) { + return $this->get_prop( 'shipping_class_id', $context ); } /** * Get downloads. * * @since 2.7.0 + * @param string $context * @return array */ - public function get_downloads() { - return $this->data['downloads']; + public function get_downloads( $context = 'view' ) { + return $this->get_prop( 'downloads', $context ); } /** * Get download expiry. * * @since 2.7.0 + * @param string $context * @return int */ - public function get_download_expiry() { - return $this->data['download_expiry']; + public function get_download_expiry( $context = 'view' ) { + return $this->get_prop( 'download_expiry', $context ); } /** * Get downloadable. * * @since 2.7.0 + * @param string $context * @return bool */ - public function get_downloadable() { - return $this->data['downloadable']; + public function get_downloadable( $context = 'view' ) { + return $this->get_prop( 'downloadable', $context ); } /** * Get download limit. * * @since 2.7.0 + * @param string $context * @return int */ - public function get_download_limit() { - return $this->data['download_limit']; + public function get_download_limit( $context = 'view' ) { + return $this->get_prop( 'download_limit', $context ); } /** * Get main image ID. @todo ensure read handles parent like get_image_id used to? * * @since 2.7.0 + * @param string $context * @return string */ - public function get_image_id() { - return $this->data['image_id']; + public function get_image_id( $context = 'view' ) { + return $this->get_prop( 'image_id', $context ); } /* @@ -617,7 +669,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $name Product name. */ public function set_name( $name ) { - $this->data['name'] = $name; + $this->set_prop( 'name', $name ); } /** @@ -627,7 +679,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $slug Product slug. */ public function set_slug( $slug ) { - $this->data['slug'] = $slug; + $this->set_prop( 'slug', $slug ); } /** @@ -637,7 +689,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $timestamp Timestamp. */ public function set_date_created( $timestamp ) { - $this->data['date_created'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ); + $this->set_prop( 'date_created', is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ) ); } /** @@ -647,7 +699,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $timestamp Timestamp. */ public function set_date_modified( $timestamp ) { - $this->data['date_modified'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ); + $this->set_prop( 'date_modified', is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ) ); } /** @@ -657,7 +709,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $status Product status. */ public function set_status( $status ) { - $this->data['status'] = $status; + $this->set_prop( 'status', $status ); } /** @@ -667,7 +719,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param bool|string */ public function set_featured( $featured ) { - $this->data['featured'] = wc_string_to_bool( $featured ); + $this->set_prop( 'featured', wc_string_to_bool( $featured ) ); } /** @@ -682,7 +734,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { if ( ! in_array( $visibility, $options, true ) ) { $this->error( 'product_invalid_catalog_visibility', __( 'Invalid catalog visibility option.', 'woocommerce' ) ); } - $this->data['catalog_visibility'] = $visibility; + $this->set_prop( 'catalog_visibility', $visibility ); } /** @@ -692,7 +744,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $description Product description. */ public function set_description( $description ) { - $this->data['description'] = $description; + $this->set_prop( 'description', $description ); } /** @@ -702,7 +754,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $short_description Product short description. */ public function set_short_description( $short_description ) { - $this->data['short_description'] = $short_description; + $this->set_prop( 'short_description', $short_description ); } /** @@ -717,7 +769,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { if ( ! empty( $sku ) && ! wc_product_has_unique_sku( $this->get_id(), $sku ) ) { $this->error( 'product_invalid_sku', __( 'Invalid or duplicated SKU.', 'woocommerce' ) ); } - $this->data['sku'] = $sku; + $this->set_prop( 'sku', $sku ); } /** @@ -726,7 +778,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $price Price. */ public function set_price( $price ) { - $this->data['price'] = wc_format_decimal( $price ); + $this->set_prop( 'price', wc_format_decimal( $price ) ); } /** @@ -736,7 +788,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $price Regular price. */ public function set_regular_price( $price ) { - $this->data['regular_price'] = wc_format_decimal( $price ); + $this->set_prop( 'regular_price', wc_format_decimal( $price ) ); } /** @@ -746,7 +798,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $price sale price. */ public function set_sale_price( $price ) { - $this->data['sale_price'] = wc_format_decimal( $price ); + $this->set_prop( 'sale_price', wc_format_decimal( $price ) ); } /** @@ -756,7 +808,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $timestamp Sale from date. */ public function set_date_on_sale_from( $timestamp ) { - $this->data['date_on_sale_from'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ); + $this->set_prop( 'date_on_sale_from', is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ) ); } /** @@ -766,7 +818,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $timestamp Sale to date. */ public function set_date_on_sale_to( $timestamp ) { - return $this->data['date_on_sale_to'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ); + $this->set_prop( 'date_on_sale_to', is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp ) ); } /** @@ -776,7 +828,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param int $total Total of sales. */ public function set_total_sales( $total ) { - $this->data['total_sales'] = absint( $total ); + $this->set_prop( 'total_sales', absint( $total ) ); } /** @@ -802,7 +854,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->error( 'product_invalid_tax_status', __( 'Invalid product tax status.', 'woocommerce' ) ); } - $this->data['tax_status'] = $status; + $this->set_prop( 'tax_status', $status ); } /** @@ -812,7 +864,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $class Tax class. */ public function set_tax_class( $class ) { - $this->data['tax_class'] = wc_clean( $class ); + $this->set_prop( 'tax_class', wc_clean( $class ) ); } /** @@ -822,7 +874,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param bool */ public function set_manage_stock( $manage_stock ) { - $this->data['manage_stock'] = wc_string_to_bool( $manage_stock ); + $this->set_prop( 'manage_stock', wc_string_to_bool( $manage_stock ) ); } /** @@ -832,7 +884,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param float|null $quantity Stock quantity. */ public function set_stock_quantity( $quantity ) { - $this->data['stock_quantity'] = '' !== $quantity ? wc_stock_amount( $quantity ) : null; + $this->set_prop( 'stock_quantity', '' !== $quantity ? wc_stock_amount( $quantity ) : null ); } /** @@ -849,7 +901,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { } } - $this->data['stock_status'] = $status; + $this->set_prop( 'stock_status', $status ); } /** @@ -859,7 +911,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $backorders Options: 'yes', 'no' or 'notify'. */ public function set_backorders( $backorders ) { - $this->data['backorders'] = $backorders; + $this->set_prop( 'backorders', $backorders ); } /** @@ -869,7 +921,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param bool */ public function set_sold_individually( $sold_individually ) { - $this->data['sold_individually'] = wc_string_to_bool( $sold_individually ); + $this->set_prop( 'sold_individually', wc_string_to_bool( $sold_individually ) ); } /** @@ -879,7 +931,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param float $weigth Total weigth. */ public function set_weight( $weight ) { - $this->data['weight'] = '' === $weight ? '' : wc_format_decimal( $weight ); + $this->set_prop( 'weight', '' === $weight ? '' : wc_format_decimal( $weight ) ); } /** @@ -889,7 +941,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param float $weigth Total weigth. */ public function set_length( $length ) { - $this->data['length'] = '' === $length ? '' : wc_format_decimal( $length ); + $this->set_prop( 'length', '' === $length ? '' : wc_format_decimal( $length ) ); } /** @@ -899,7 +951,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param float $width Total width. */ public function set_width( $width ) { - $this->data['width'] = '' === $width ? '' : wc_format_decimal( $width ); + $this->set_prop( 'width', '' === $width ? '' : wc_format_decimal( $width ) ); } /** @@ -909,7 +961,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param float $height Total height. */ public function set_height( $height ) { - $this->data['height'] = '' === $height ? '' : wc_format_decimal( $height ); + $this->set_prop( 'height', '' === $height ? '' : wc_format_decimal( $height ) ); } /** @@ -919,7 +971,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $upsell_ids IDs from the up-sell products. */ public function set_upsell_ids( $upsell_ids ) { - $this->data['upsell_ids'] = array_filter( (array) $upsell_ids ); + $this->set_prop( 'upsell_ids', array_filter( (array) $upsell_ids ) ); } /** @@ -929,7 +981,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $cross_sell_ids IDs from the cross-sell products. */ public function set_cross_sell_ids( $cross_sell_ids ) { - $this->data['cross_sell_ids'] = array_filter( (array) $cross_sell_ids ); + $this->set_prop( 'cross_sell_ids', array_filter( (array) $cross_sell_ids ) ); } /** @@ -939,7 +991,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param int $parent_id Product parent ID. */ public function set_parent_id( $parent_id ) { - $this->data['parent_id'] = absint( $parent_id ); + $this->set_prop( 'parent_id', absint( $parent_id ) ); } /** @@ -949,7 +1001,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param bool $reviews_allowed Reviews allowed or not. */ public function set_reviews_allowed( $reviews_allowed ) { - $this->data['reviews_allowed'] = wc_string_to_bool( $reviews_allowed ); + $this->set_prop( 'reviews_allowed', wc_string_to_bool( $reviews_allowed ) ); } /** @@ -959,7 +1011,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $purchase_note Purchase note. */ public function set_purchase_note( $purchase_note ) { - $this->data['purchase_note'] = $purchase_note; + $this->set_prop( 'purchase_note', $purchase_note ); } /** @@ -986,7 +1038,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { } uasort( $attributes, 'wc_product_attribute_uasort_comparison' ); - $this->data['attributes'] = $attributes; + $this->set_prop( 'attributes', $attributes ); } /** @@ -996,7 +1048,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param array $default_attributes List of default attributes. */ public function set_default_attributes( $default_attributes ) { - $this->data['default_attributes'] = $default_attributes; + $this->set_prop( 'default_attributes', $default_attributes ); } /** @@ -1006,7 +1058,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param int $menu_order Menu order. */ public function set_menu_order( $menu_order ) { - $this->data['menu_order'] = intval( $menu_order ); + $this->set_prop( 'menu_order', intval( $menu_order ) ); } /** @@ -1016,7 +1068,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param array $term_ids List of terms IDs. */ public function set_category_ids( $term_ids ) { - $this->data['category_ids'] = $this->sanitize_term_ids( $term_ids, 'product_cat' ); + $this->set_prop( 'category_ids', $this->sanitize_term_ids( $term_ids, 'product_cat' ) ); } /** @@ -1026,7 +1078,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param array $term_ids List of terms IDs. */ public function set_tag_ids( $term_ids ) { - $this->data['tag_ids'] = $this->sanitize_term_ids( $term_ids, 'product_tag' ); + $this->set_prop( 'tag_ids', $this->sanitize_term_ids( $term_ids, 'product_tag' ) ); } /** @@ -1036,7 +1088,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param bool|string */ public function set_virtual( $virtual ) { - $this->data['virtual'] = wc_string_to_bool( $virtual ); + $this->set_prop( 'virtual', wc_string_to_bool( $virtual ) ); } /** @@ -1046,7 +1098,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param int */ public function set_shipping_class_id( $id ) { - $this->data['shipping_class_id'] = absint( $id ); + $this->set_prop( 'shipping_class_id', absint( $id ) ); } /** @@ -1056,7 +1108,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param bool|string */ public function set_downloadable( $downloadable ) { - $this->data['downloadable'] = wc_string_to_bool( $downloadable ); + $this->set_prop( 'downloadable', wc_string_to_bool( $downloadable ) ); } /** @@ -1117,7 +1169,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { ); } - $this->data['downloads'] = $downloads; + $this->set_prop( 'downloads', $downloads ); if ( $errors ) { $this->error( 'product_invalid_download', $errors[0] ); @@ -1131,7 +1183,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param int $download_limit */ public function set_download_limit( $download_limit ) { - $this->data['download_limit'] = -1 === (int) $download_limit || '' === $download_limit ? -1 : absint( $download_limit ); + $this->set_prop( 'download_limit', -1 === (int) $download_limit || '' === $download_limit ? -1 : absint( $download_limit ) ); } /** @@ -1141,17 +1193,17 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param int $download_expiry */ public function set_download_expiry( $download_expiry ) { - $this->data['download_expiry'] = -1 === (int) $download_expiry || '' === $download_expiry ? -1 : absint( $download_expiry ); + $this->set_prop( 'download_expiry', -1 === (int) $download_expiry || '' === $download_expiry ? -1 : absint( $download_expiry ) ); } /** * Set gallery attachment ids. * * @since 2.7.0 - * @param array $gallery_ids + * @param array $image_ids */ - public function set_gallery_image_ids( $gallery_ids ) { - $this->data['gallery_image_ids'] = $gallery_ids; + public function set_gallery_image_ids( $image_ids ) { + $this->set_prop( 'gallery_image_ids', array_filter( array_filter( $image_ids ), 'wp_attachment_is_image' ) ); } /** @@ -1161,7 +1213,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param int $image_id */ public function set_image_id( $image_id = '' ) { - $this->data['image_id'] = $image_id; + $this->set_prop( 'image_id', $image_id ); } /* @@ -1226,16 +1278,35 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->set_id( $id ); $this->set_props( array( - 'name' => get_the_title( $post_object ), - 'slug' => $post_object->post_name, - 'date_created' => $post_object->post_date, - 'date_modified' => $post_object->post_modified, - 'type' => '', - 'status' => $post_object->post_status, + 'name' => get_the_title( $post_object ), + 'slug' => $post_object->post_name, + 'date_created' => $post_object->post_date, + 'date_modified' => $post_object->post_modified, + 'status' => $post_object->post_status, + 'description' => $post_object->post_content, + 'short_description' => $post_object->post_excerpt, + 'parent_id' => $post_object->post_parent, + 'menu_order' => $post_object->menu_order, + 'reviews_allowed' => 'open' === $post_object->comment_status, + ) ); + $this->read_product_data(); + $this->read_meta_data(); + $this->read_attributes(); + + // Set object_read true once all data is read. + $this->set_object_read( true ); + } + + /** + * Read post data. Can be overridden by child classes to load other props. + * + * @since 2.7.0 + */ + protected function read_product_data() { + $id = $this->get_id(); + $this->set_props( array( 'featured' => get_post_meta( $id, '_featured', true ), 'catalog_visibility' => get_post_meta( $id, '_visibility', true ), - 'description' => $post_object->post_content, - 'short_description' => $post_object->post_excerpt, 'sku' => get_post_meta( $id, '_sku', true ), 'regular_price' => get_post_meta( $id, '_regular_price', true ), 'sale_price' => get_post_meta( $id, '_sale_price', true ), @@ -1255,11 +1326,8 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'height' => get_post_meta( $id, '_height', true ), 'upsell_ids' => get_post_meta( $id, '_upsell_ids', true ), 'cross_sell_ids' => get_post_meta( $id, '_crosssell_ids', true ), - 'parent_id' => $post_object->post_parent, - 'reviews_allowed' => 'open' === $post_object->comment_status, 'purchase_note' => get_post_meta( $id, '_purchase_note', true ), 'default_attributes' => get_post_meta( $id, '_default_attributes', true ), - 'menu_order' => $post_object->menu_order, 'category_ids' => $this->get_term_ids( 'product_cat' ), 'tag_ids' => $this->get_term_ids( 'product_tag' ), 'shipping_class_id' => current( $this->get_term_ids( 'product_shipping_class' ) ), @@ -1271,13 +1339,12 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'download_expiry' => get_post_meta( $id, '_download_expiry', true ), 'image_id' => get_post_thumbnail_id( $id ), ) ); + if ( $this->is_on_sale() ) { $this->set_price( $this->get_sale_price() ); } else { $this->set_price( $this->get_regular_price() ); } - $this->read_meta_data(); - $this->read_attributes(); } /** @@ -1375,11 +1442,8 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @since 2.7.0 */ public function save() { - if ( $this->get_id() ) { - $this->update(); - } else { - $this->create(); - } + parent::save(); + // Make sure we store the product type. $type_term = get_term_by( 'name', $this->get_type(), 'product_type' ); wp_set_object_terms( $this->get_id(), absint( $type_term->term_id ), 'product_type' ); @@ -1387,6 +1451,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { // Version is set to current WC version to track data changes. update_post_meta( $this->get_id(), '_product_version', WC_VERSION ); wc_delete_product_transients( $this->get_id() ); + return $this->get_id(); } @@ -1408,6 +1473,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { */ protected function update_post_meta() { $updated_props = array(); + $changed_props = array_keys( $this->get_changes() ); $meta_key_to_props = array( '_visibility' => 'catalog_visibility', '_sku' => 'sku', @@ -1442,8 +1508,10 @@ class WC_Product extends WC_Abstract_Legacy_Product { ); foreach ( $meta_key_to_props as $meta_key => $prop ) { - $value = $this->data[ $prop ]; - // @todo this is where state should be checked? + if ( ! in_array( $prop, $changed_props ) ) { + continue; + } + $value = $this->{"get_$prop"}( 'edit' ); switch ( $prop ) { case 'virtual' : case 'downloadable' : diff --git a/includes/class-wc-product-external.php b/includes/class-wc-product-external.php index aa0358329db..6b473d6f06b 100644 --- a/includes/class-wc-product-external.php +++ b/includes/class-wc-product-external.php @@ -35,6 +35,14 @@ class WC_Product_External extends WC_Product { parent::__construct( $product ); } + /** + * Get internal type. + * @return string + */ + public function get_type() { + return 'external'; + } + /* |-------------------------------------------------------------------------- | Getters @@ -43,30 +51,24 @@ class WC_Product_External extends WC_Product { | Methods for getting data from the product object. */ - /** - * Get internal type. - * @return string - */ - public function get_type() { - return 'external'; - } - /** * Get product url. * + * @param string $context * @return string */ - public function get_product_url() { - return esc_url( $this->data['product_url'] ); + public function get_product_url( $context = 'view' ) { + return esc_url( $this->get_prop( 'product_url', $context ) ); } /** * Get button text. * + * @param string $context * @return string */ - public function get_button_text() { - return $this->data['button_text'] ? $this->data['button_text'] : __( 'Buy product', 'woocommerce' ); + public function get_button_text( $context = 'view' ) { + return $this->get_prop( 'button_text', $context ); } /* @@ -86,7 +88,7 @@ class WC_Product_External extends WC_Product { * @param string $product_url Product URL. */ public function set_product_url( $product_url ) { - $this->data['product_url'] = $product_url; + $this->set_prop( 'product_url', $product_url ); } /** @@ -96,7 +98,7 @@ class WC_Product_External extends WC_Product { * @param string $button_text Button text. */ public function set_button_text( $button_text ) { - $this->data['button_text'] = $button_text; + $this->set_prop( 'button_text', $button_text ); } /** @@ -106,7 +108,7 @@ class WC_Product_External extends WC_Product { * @param bool */ public function set_manage_stock( $manage_stock ) { - $this->data['manage_stock'] = false; + $this->set_prop( 'manage_stock', false ); if ( true === $manage_stock ) { $this->error( 'product_external_invalid_manage_stock', __( 'External products cannot be stock managed.', 'woocommerce' ) ); @@ -120,7 +122,7 @@ class WC_Product_External extends WC_Product { * @param bool */ public function set_stock_status( $stock_status ) { - $this->data['stock_status'] = 'instock'; + $this->set_prop( 'stock_status', 'instock' ); if ( 'instock' !== $stock_status ) { $this->error( 'product_external_invalid_stock_status', __( 'External products cannot be stock managed.', 'woocommerce' ) ); @@ -134,7 +136,7 @@ class WC_Product_External extends WC_Product { * @param string $backorders Options: 'yes', 'no' or 'notify'. */ public function set_backorders( $backorders ) { - $this->data['backorders'] = 'no'; + $this->set_prop( 'backorders', 'no' ); if ( 'no' !== $backorders ) { $this->error( 'product_external_invalid_backorders', __( 'External products cannot be backordered.', 'woocommerce' ) ); @@ -194,19 +196,17 @@ class WC_Product_External extends WC_Product { */ /** - * Reads a product from the database and sets its data to the class. + * Read post data. * * @since 2.7.0 - * @param int $id Product ID. */ - public function read( $id ) { - parent::read( $id ); + protected function read_product_data() { + parent::read_product_data(); + $this->set_props( array( - 'product_url' => get_post_meta( $id, '_product_url', true ), - 'button_text' => get_post_meta( $id, '_button_text', true ), + 'product_url' => get_post_meta( $this->get_id(), '_product_url', true ), + 'button_text' => get_post_meta( $this->get_id(), '_button_text', true ) ? get_post_meta( $this->get_id(), '_button_text', true ) : __( 'Buy product', 'woocommerce' ), ) ); - do_action( 'woocommerce_product_loaded', $this ); - do_action( 'woocommerce_product_' . $this->get_type() . '_loaded', $this ); } /** diff --git a/includes/class-wc-product-grouped.php b/includes/class-wc-product-grouped.php index 2f462e9cdb9..5d2db08c60b 100644 --- a/includes/class-wc-product-grouped.php +++ b/includes/class-wc-product-grouped.php @@ -34,14 +34,6 @@ class WC_Product_Grouped extends WC_Product { parent::__construct( $product ); } - /* - |-------------------------------------------------------------------------- - | Getters - |-------------------------------------------------------------------------- - | - | Methods for getting data from the product object. - */ - /** * Get internal type. * @return string @@ -50,15 +42,6 @@ class WC_Product_Grouped extends WC_Product { return 'grouped'; } - /** - * Return the children of this product. - * - * @return array - */ - public function get_children() { - return $this->data['children']; - } - /** * Get the add to cart button text. * @@ -131,6 +114,24 @@ class WC_Product_Grouped extends WC_Product { return apply_filters( 'woocommerce_get_price_html', $price, $this ); } + /* + |-------------------------------------------------------------------------- + | Getters + |-------------------------------------------------------------------------- + | + | Methods for getting data from the product object. + */ + + /** + * Return the children of this product. + * + * @param string $context + * @return array + */ + public function get_children( $context = 'view' ) { + return $this->get_prop( 'children', $context ); + } + /* |-------------------------------------------------------------------------- | Setters @@ -145,7 +146,7 @@ class WC_Product_Grouped extends WC_Product { * @param array $children */ public function set_children( $children ) { - $this->data['children'] = array_filter( wp_parse_id_list( (array) $children ) ); + $this->set_prop( 'children', array_filter( wp_parse_id_list( (array) $children ) ) ); } /* @@ -155,19 +156,16 @@ class WC_Product_Grouped extends WC_Product { */ /** - * Reads a product from the database and sets its data to the class. + * Read post data. * * @since 2.7.0 - * @param int $id Product ID. */ - public function read( $id ) { - parent::read( $id ); + protected function read_product_data() { + parent::read_product_data(); $this->set_props( array( - 'children' => wp_parse_id_list( get_post_meta( $id, '_children', true ) ), + 'children' => wp_parse_id_list( get_post_meta( $this->get_id(), '_children', true ) ), ) ); - do_action( 'woocommerce_product_loaded', $this ); - do_action( 'woocommerce_product_' . $this->get_type() . '_loaded', $this ); } /** diff --git a/includes/class-wc-product-simple.php b/includes/class-wc-product-simple.php index cadb41877e5..c58132c5ecf 100644 --- a/includes/class-wc-product-simple.php +++ b/includes/class-wc-product-simple.php @@ -57,7 +57,7 @@ class WC_Product_Simple extends WC_Product { } /** - * Get the title of the post. + * Get the title of the post. @todo should this be deprecated or not? It's in deprecated class and needs review. * * @return string */ diff --git a/includes/class-wc-product-variable.php b/includes/class-wc-product-variable.php index fc8a4a41e5b..4b772fd4bc6 100644 --- a/includes/class-wc-product-variable.php +++ b/includes/class-wc-product-variable.php @@ -4,7 +4,7 @@ if ( ! defined( 'ABSPATH' ) ) { } /** - * Variable Product Class. + * Variable Product Class. @todo needs new getters/setters/changes code * * The WooCommerce product class handles individual product data. * diff --git a/includes/class-wc-product-variation.php b/includes/class-wc-product-variation.php index 017da0a6e64..cd8471e5952 100644 --- a/includes/class-wc-product-variation.php +++ b/includes/class-wc-product-variation.php @@ -4,7 +4,14 @@ if ( ! defined( 'ABSPATH' ) ) { } /** - * Product Variation Class. + * Product Variation Class. @todo needs new getters/setters/changes code + * + * @todo removed filters need to be mapped via add_action to the product actions of similar naming. + * woocommerce_variation_is_in_stock + * woocommerce_variation_sale_price_html + * woocommerce_variation_price_html + * woocommerce_variation_free_price_html + * woocommerce_get_variation_price_html * * @todo removed filters need to be mapped via add_action to the product actions of similar naming. * woocommerce_variation_is_in_stock diff --git a/includes/wc-deprecated-functions.php b/includes/wc-deprecated-functions.php index 0360e651c28..49b6ea9c453 100644 --- a/includes/wc-deprecated-functions.php +++ b/includes/wc-deprecated-functions.php @@ -508,12 +508,20 @@ function woocommerce_list_pages( $pages ) { global $wc_map_deprecated_filters; $wc_map_deprecated_filters = array( - 'woocommerce_add_to_cart_fragments' => 'add_to_cart_fragments', - 'woocommerce_add_to_cart_redirect' => 'add_to_cart_redirect', - 'woocommerce_product_get_width' => 'woocommerce_product_width', - 'woocommerce_product_get_height' => 'woocommerce_product_height', - 'woocommerce_product_get_length' => 'woocommerce_product_length', - 'woocommerce_product_get_weight' => 'woocommerce_product_weight', + 'woocommerce_add_to_cart_fragments' => 'add_to_cart_fragments', + 'woocommerce_add_to_cart_redirect' => 'add_to_cart_redirect', + 'woocommerce_product_get_width' => 'woocommerce_product_width', + 'woocommerce_product_get_height' => 'woocommerce_product_height', + 'woocommerce_product_get_length' => 'woocommerce_product_length', + 'woocommerce_product_get_weight' => 'woocommerce_product_weight', + 'woocommerce_product_get_sku' => 'woocommerce_get_sku', + 'woocommerce_product_get_price' => 'woocommerce_get_price', + 'woocommerce_product_get_regular_price' => 'woocommerce_get_regular_price', + 'woocommerce_product_get_sale_price' => 'woocommerce_get_sale_price', + 'woocommerce_product_get_tax_class' => 'woocommerce_product_tax_class', + 'woocommerce_product_get_stock_quantity' => 'woocommerce_get_stock_quantity', + 'woocommerce_product_get_attributes' => 'woocommerce_get_product_attributes', + 'woocommerce_product_get_gallery_image_ids' => 'woocommerce_product_gallery_attachment_ids', ); foreach ( $wc_map_deprecated_filters as $new => $old ) { From 2c2239a3b0b9c5c786297aa29bece40d55212d21 Mon Sep 17 00:00:00 2001 From: Lee Willis Date: Wed, 9 Nov 2016 11:27:24 +0000 Subject: [PATCH 055/163] Use get_the_terms() instead of wp_get_post_terms() wp_get_post_terms() is a wrapper around wp_get_object_terms() which does not use the object cache, and generates a database query every time it is used. get_the_terms() however can use data from the object cache if present. --- includes/abstracts/abstract-wc-product.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 2b1ee2e4f87..7eabe149e8d 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -1235,7 +1235,11 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return array of terms */ protected function get_term_ids( $taxonomy ) { - return wp_get_post_terms( $this->get_id(), $taxonomy, array( 'fields' => 'ids' ) ); + $terms = get_the_terms( $this->get_id(), $taxonomy ); + if ( false === $terms || is_wp_error( $terms ) ) { + return array(); + } + return wp_list_pluck( $terms, 'term_id' ); } /** From 90d8291ace4c47ca84b39a3cc75b688cf1a62e3a Mon Sep 17 00:00:00 2001 From: Lee Willis Date: Wed, 9 Nov 2016 11:44:11 +0000 Subject: [PATCH 056/163] Allow WP_Query to preload post data, and meta in wc_get_products() Allow WP_Query to bulk query for post data and meta if more than just IDs are requested from wc_get_products(). Reduces query count significantly. --- includes/wc-product-functions.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 1d06bab14db..56d69daa1bf 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -88,11 +88,14 @@ function wc_get_products( $args ) { 'post_status' => $args['status'], 'posts_per_page' => $args['limit'], 'meta_query' => array(), - 'fields' => 'ids', 'orderby' => $args['orderby'], 'order' => $args['order'], 'tax_query' => array(), ); + // Do not load unneccessary post data if the user only wants IDs. + if ( 'ids' === $args['return'] ) { + $wp_query_args['fields'] = 'ids'; + } if ( 'variation' !== $args['type'] ) { $wp_query_args['tax_query'][] = array( From ed46abf3a351c7c27a02050b52b7b108e7cde2af Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 9 Nov 2016 12:26:46 +0000 Subject: [PATCH 057/163] [Product CRUD] Variable, variation, notices, and stock handling (#12277) * No longer needed * Remove old todos * Use getters in admin list * Related and upsells update for CRUD * Fix notice in gallery * Variable fixes and todos * Context * Price sync * Revert variation attributes change * Return parent data in view context * Defer term counting * wc_find_matching_product_variation * Stock manage tweaks * Stock fixes * Correct id * correct id * Better sync * Data logic setter fix * feedback --- apigen.neon | 2 +- assets/js/frontend/add-to-cart-variation.js | 34 +- .../js/frontend/add-to-cart-variation.min.js | 2 +- includes/abstracts/abstract-wc-data.php | 4 +- .../abstracts/abstract-wc-legacy-product.php | 118 ++- includes/abstracts/abstract-wc-order.php | 6 +- includes/abstracts/abstract-wc-product.php | 167 ++- .../class-wc-admin-duplicate-product.php | 1 - includes/admin/class-wc-admin-post-types.php | 48 +- .../views/html-product-data-advanced.php | 6 +- .../views/html-product-data-attributes.php | 2 +- .../views/html-product-data-general.php | 22 +- .../views/html-product-data-inventory.php | 12 +- .../html-product-data-linked-products.php | 6 +- .../views/html-product-data-shipping.php | 10 +- .../meta-boxes/views/html-variation-admin.php | 58 +- .../api/class-wc-rest-products-controller.php | 2 +- .../api/legacy/v1/class-wc-api-coupons.php | 3 - .../api/legacy/v1/class-wc-api-customers.php | 3 - .../api/legacy/v1/class-wc-api-orders.php | 1 - .../api/legacy/v1/class-wc-api-products.php | 4 +- .../api/legacy/v2/class-wc-api-products.php | 2 +- .../api/legacy/v3/class-wc-api-products.php | 2 +- includes/class-wc-ajax.php | 12 +- includes/class-wc-cart.php | 51 +- includes/class-wc-checkout.php | 6 +- includes/class-wc-countries.php | 1 - includes/class-wc-form-handler.php | 4 +- includes/class-wc-post-data.php | 43 +- includes/class-wc-product-grouped.php | 2 +- includes/class-wc-product-variable.php | 977 +++++++----------- includes/class-wc-product-variation.php | 319 ++++-- includes/class-wc-shortcodes.php | 2 +- includes/class-wc-tax.php | 1 - includes/cli/class-wc-cli-order.php | 1 - includes/wc-core-functions.php | 3 - includes/wc-deprecated-functions.php | 50 - includes/wc-formatting-functions.php | 16 +- includes/wc-product-functions.php | 229 +++- includes/wc-stock-functions.php | 78 +- includes/wc-template-functions.php | 73 +- includes/wc-term-functions.php | 16 +- readme.txt | 3 + .../single-product/add-to-cart/variation.php | 2 +- .../single-product/product-attributes.php | 2 +- .../single-product/product-thumbnails.php | 2 +- templates/single-product/related.php | 36 +- templates/single-product/up-sells.php | 34 +- tests/unit-tests/cart/functions.php | 1 - tests/unit-tests/util/install.php | 2 - 50 files changed, 1330 insertions(+), 1151 deletions(-) diff --git a/apigen.neon b/apigen.neon index 98dc528246e..7fdf2e0f4a3 100644 --- a/apigen.neon +++ b/apigen.neon @@ -46,7 +46,7 @@ tree: true # generate documentation for deprecated elements deprecated: true -# generate list of tasks with @todo annotation +# generate list of tasks with @ todo annotation todo: true # add link to ZIP archive of documentation diff --git a/assets/js/frontend/add-to-cart-variation.js b/assets/js/frontend/add-to-cart-variation.js index 9007b074556..392cf8b8eae 100644 --- a/assets/js/frontend/add-to-cart-variation.js +++ b/assets/js/frontend/add-to-cart-variation.js @@ -194,15 +194,15 @@ } // Display weight - if ( variation.weight ) { - $weight.wc_set_content( variation.weight ); + if ( variation.weight_html ) { + $weight.wc_set_content( variation.weight_html ); } else { $weight.wc_reset_content(); } // Display dimensions - if ( variation.dimensions ) { - $dimensions.wc_set_content( variation.dimensions ); + if ( variation.dimensions_html ) { + $dimensions.wc_set_content( variation.dimensions_html ); } else { $dimensions.wc_reset_content(); } @@ -228,11 +228,11 @@ $template_html = $template_html.replace( '/**/', '' ); $single_variation.html( $template_html ); - $form.find( 'input[name="variation_id"], input.variation_id' ).val( variation.variation_id ).change(); + $form.find( 'input[name="variation_id"], input.variation_id' ).val( variation.id ).change(); } // Hide or show qty input - if ( variation.is_sold_individually === 'yes' ) { + if ( variation.sold_individually ) { $qty.find( 'input.qty' ).val( '1' ).attr( 'min', '1' ).attr( 'max', '' ); $qty.hide(); } else { @@ -468,7 +468,11 @@ if ( undefined === this.attr( 'data-o_' + attr ) ) { this.attr( 'data-o_' + attr, ( ! this.attr( attr ) ) ? '' : this.attr( attr ) ); } - this.attr( attr, value ); + if ( false === value ) { + this.removeAttr( attr ); + } else { + this.attr( attr, value ); + } }; /** @@ -489,14 +493,14 @@ $product_img = $product.find( 'div.images img:eq(0)' ), $product_link = $product.find( 'div.images a.zoom:eq(0)' ); - if ( variation && variation.image_src && variation.image_src.length > 1 ) { - $product_img.wc_set_variation_attr( 'src', variation.image_src ); - $product_img.wc_set_variation_attr( 'title', variation.image_title ); - $product_img.wc_set_variation_attr( 'alt', variation.image_alt ); - $product_img.wc_set_variation_attr( 'srcset', variation.image_srcset ); - $product_img.wc_set_variation_attr( 'sizes', variation.image_sizes ); - $product_link.wc_set_variation_attr( 'href', variation.image_link ); - $product_link.wc_set_variation_attr( 'title', variation.image_caption ); + if ( variation && variation.image && variation.image.src.length > 1 ) { + $product_img.wc_set_variation_attr( 'src', variation.image.src ); + $product_img.wc_set_variation_attr( 'title', variation.image.title ); + $product_img.wc_set_variation_attr( 'alt', variation.image.alt ); + $product_img.wc_set_variation_attr( 'srcset', variation.image.srcset ); + $product_img.wc_set_variation_attr( 'sizes', variation.image.sizes ); + $product_link.wc_set_variation_attr( 'href', variation.image.url ); + $product_link.wc_set_variation_attr( 'title', variation.image.caption ); } else { $product_img.wc_reset_variation_attr( 'src' ); $product_img.wc_reset_variation_attr( 'title' ); diff --git a/assets/js/frontend/add-to-cart-variation.min.js b/assets/js/frontend/add-to-cart-variation.min.js index e5ed06e5d5d..d0bd0afd6f4 100644 --- a/assets/js/frontend/add-to-cart-variation.min.js +++ b/assets/js/frontend/add-to-cart-variation.min.js @@ -1,4 +1,4 @@ /*! * Variations Plugin */ -!function(a,b,c,d){a.fn.wc_variation_form=function(){var c=this,d=c.find(".single_variation"),f=c.closest(".product"),g=parseInt(c.data("product_id"),10),h=c.data("product_variations"),i=h===!1,j=!1,k=c.find(".reset_variations"),l=wp.template("variation-template"),m=wp.template("unavailable-variation-template"),n=c.find(".single_variation_wrap");return n.show(),c.unbind("check_variations update_variation_values found_variation"),c.find(".reset_variations").unbind("click"),c.find(".variations select").unbind("change focusin"),c.on("click",".reset_variations",function(a){a.preventDefault(),c.find(".variations select").val("").change(),c.trigger("reset_data")}).on("hide_variation",function(a){a.preventDefault(),c.find(".single_add_to_cart_button").removeClass("wc-variation-is-unavailable").addClass("disabled wc-variation-selection-needed"),c.find(".woocommerce-variation-add-to-cart").removeClass("woocommerce-variation-add-to-cart-enabled").addClass("woocommerce-variation-add-to-cart-disabled")}).on("show_variation",function(a,b,d){a.preventDefault(),d?(c.find(".single_add_to_cart_button").removeClass("disabled wc-variation-selection-needed wc-variation-is-unavailable"),c.find(".woocommerce-variation-add-to-cart").removeClass("woocommerce-variation-add-to-cart-disabled").addClass("woocommerce-variation-add-to-cart-enabled")):(c.find(".single_add_to_cart_button").removeClass("wc-variation-selection-needed").addClass("disabled wc-variation-is-unavailable"),c.find(".woocommerce-variation-add-to-cart").removeClass("woocommerce-variation-add-to-cart-enabled").addClass("woocommerce-variation-add-to-cart-disabled"))}).on("click",".single_add_to_cart_button",function(c){var d=a(this);d.is(".disabled")&&(c.preventDefault(),d.is(".wc-variation-is-unavailable")?b.alert(wc_add_to_cart_variation_params.i18n_unavailable_text):d.is(".wc-variation-selection-needed")&&b.alert(wc_add_to_cart_variation_params.i18n_make_a_selection_text))}).on("reload_product_variations",function(){h=c.data("product_variations"),i=h===!1}).on("reset_data",function(){f.find(".product_meta").find(".sku").wc_reset_content(),a(".product_weight").wc_reset_content(),a(".product_dimensions").wc_reset_content(),c.trigger("reset_image"),d.slideUp(200).trigger("hide_variation")}).on("reset_image",function(){c.wc_variations_image_update(!1)}).on("change",".variations select",function(){if(c.find('input[name="variation_id"], input.variation_id').val("").change(),c.find(".wc-no-matching-variations").remove(),i){j&&j.abort();var b=!0,d=!1,e={};c.find(".variations select").each(function(){var c=a(this).data("attribute_name")||a(this).attr("name"),f=a(this).val()||"";0===f.length?b=!1:d=!0,e[c]=f}),b?(e.product_id=g,e.custom_data=c.data("custom_data"),c.block({message:null,overlayCSS:{background:"#fff",opacity:.6}}),j=a.ajax({url:wc_add_to_cart_variation_params.wc_ajax_url.toString().replace("%%endpoint%%","get_variation"),type:"POST",data:e,success:function(a){a?c.trigger("found_variation",[a]):(c.trigger("reset_data"),c.find(".single_variation").after('

'+wc_add_to_cart_variation_params.i18n_no_matching_variations_text+"

"),c.find(".wc-no-matching-variations").slideDown(200))},complete:function(){c.unblock()}})):c.trigger("reset_data"),d?"hidden"===k.css("visibility")&&k.css("visibility","visible").hide().fadeIn():k.css("visibility","hidden")}else c.trigger("woocommerce_variation_select_change"),c.trigger("check_variations",["",!1]),a(this).blur();a(".product.has-default-attributes > .images").fadeTo(200,1),c.trigger("woocommerce_variation_has_changed")}).on("focusin touchstart",".variations select",function(){a(this).find("option:selected").attr("selected","selected"),i||(c.trigger("woocommerce_variation_select_focusin"),c.trigger("check_variations",[a(this).data("attribute_name")||a(this).attr("name"),!0]))}).on("found_variation",function(b,e){var g=f.find(".product_meta").find(".sku"),h=f.find(".product_weight"),i=f.find(".product_dimensions"),j=n.find(".quantity"),k=!0;e.sku?g.wc_set_content(e.sku):g.wc_reset_content(),e.weight?h.wc_set_content(e.weight):h.wc_reset_content(),e.dimensions?i.wc_set_content(e.dimensions):i.wc_reset_content(),c.wc_variations_image_update(e);var o="";e.variation_is_visible?(o=l({variation:e}),o=o.replace("/**/",""),d.html(o),c.find('input[name="variation_id"], input.variation_id').val(e.variation_id).change()):(o=m(),o=o.replace("/**/",""),d.html(o),c.find('input[name="variation_id"], input.variation_id').val("").change()),"yes"===e.is_sold_individually?(j.find("input.qty").val("1").attr("min","1").attr("max",""),j.hide()):(j.find("input.qty").attr("min",e.min_qty).attr("max",e.max_qty),j.show()),e.is_purchasable&&e.is_in_stock&&e.variation_is_visible||(k=!1),a.trim(d.text())?d.slideDown(200).trigger("show_variation",[e,k]):d.show().trigger("show_variation",[e,k])}).on("check_variations",function(c,f,g){if(!i){var j=!0,k=!1,l={},m=a(this),n=m.find(".reset_variations");m.find(".variations select").each(function(){var b=a(this).data("attribute_name")||a(this).attr("name"),c=a(this).val()||"";0===c.length?j=!1:k=!0,f&&b===f?(j=!1,l[b]=""):l[b]=c});var o=e.find_matching_variations(h,l);if(j){var p=o.shift();p?m.trigger("found_variation",[p]):(m.find(".variations select").val(""),g||m.trigger("reset_data"),b.alert(wc_add_to_cart_variation_params.i18n_no_matching_variations_text))}else m.trigger("update_variation_values",[o]),g||m.trigger("reset_data"),f||d.slideUp(200).trigger("hide_variation");k?"hidden"===n.css("visibility")&&n.css("visibility","visible").hide().fadeIn():n.css("visibility","hidden")}}).on("update_variation_values",function(b,d){i||(c.find(".variations select").each(function(b,c){var e,f=a(c);f.data("attribute_options")||f.data("attribute_options",f.find("option:gt(0)").get()),f.find("option:gt(0)").remove(),f.append(f.data("attribute_options")),f.find("option:gt(0)").removeClass("attached"),f.find("option:gt(0)").removeClass("enabled"),f.find("option:gt(0)").removeAttr("disabled"),e="undefined"!=typeof f.data("attribute_name")?f.data("attribute_name"):f.attr("name");for(var g in d)if("undefined"!=typeof d[g]){var h=d[g].attributes;for(var i in h)if(h.hasOwnProperty(i)){var j=h[i];if(i===e){var k="";d[g].variation_is_active&&(k="enabled"),j?(j=a("
").html(j).text(),j=j.replace(/'/g,"\\'"),j=j.replace(/"/g,'\\"'),f.find('option[value="'+j+'"]').addClass("attached "+k)):f.find("option:gt(0)").addClass("attached "+k)}}}f.find("option:gt(0):not(.attached)").remove(),f.find("option:gt(0):not(.enabled)").attr("disabled","disabled")}),c.trigger("woocommerce_update_variation_values"))}),c.trigger("wc_variation_form"),c};var e={find_matching_variations:function(a,b){for(var c=[],d=0;d1?(d.wc_set_variation_attr("src",a.image_src),d.wc_set_variation_attr("title",a.image_title),d.wc_set_variation_attr("alt",a.image_alt),d.wc_set_variation_attr("srcset",a.image_srcset),d.wc_set_variation_attr("sizes",a.image_sizes),e.wc_set_variation_attr("href",a.image_link),e.wc_set_variation_attr("title",a.image_caption)):(d.wc_reset_variation_attr("src"),d.wc_reset_variation_attr("title"),d.wc_reset_variation_attr("alt"),d.wc_reset_variation_attr("srcset"),d.wc_reset_variation_attr("sizes"),e.wc_reset_variation_attr("href"),e.wc_reset_variation_attr("title"))},a(function(){"undefined"!=typeof wc_add_to_cart_variation_params&&a(".variations_form").each(function(){a(this).wc_variation_form().find(".variations select:eq(0)").change()})})}(jQuery,window,document); \ No newline at end of file +!function(a,b,c,d){a.fn.wc_variation_form=function(){var c=this,d=c.find(".single_variation"),f=c.closest(".product"),g=parseInt(c.data("product_id"),10),h=c.data("product_variations"),i=h===!1,j=!1,k=c.find(".reset_variations"),l=wp.template("variation-template"),m=wp.template("unavailable-variation-template"),n=c.find(".single_variation_wrap");return n.show(),c.unbind("check_variations update_variation_values found_variation"),c.find(".reset_variations").unbind("click"),c.find(".variations select").unbind("change focusin"),c.on("click",".reset_variations",function(a){a.preventDefault(),c.find(".variations select").val("").change(),c.trigger("reset_data")}).on("hide_variation",function(a){a.preventDefault(),c.find(".single_add_to_cart_button").removeClass("wc-variation-is-unavailable").addClass("disabled wc-variation-selection-needed"),c.find(".woocommerce-variation-add-to-cart").removeClass("woocommerce-variation-add-to-cart-enabled").addClass("woocommerce-variation-add-to-cart-disabled")}).on("show_variation",function(a,b,d){a.preventDefault(),d?(c.find(".single_add_to_cart_button").removeClass("disabled wc-variation-selection-needed wc-variation-is-unavailable"),c.find(".woocommerce-variation-add-to-cart").removeClass("woocommerce-variation-add-to-cart-disabled").addClass("woocommerce-variation-add-to-cart-enabled")):(c.find(".single_add_to_cart_button").removeClass("wc-variation-selection-needed").addClass("disabled wc-variation-is-unavailable"),c.find(".woocommerce-variation-add-to-cart").removeClass("woocommerce-variation-add-to-cart-enabled").addClass("woocommerce-variation-add-to-cart-disabled"))}).on("click",".single_add_to_cart_button",function(c){var d=a(this);d.is(".disabled")&&(c.preventDefault(),d.is(".wc-variation-is-unavailable")?b.alert(wc_add_to_cart_variation_params.i18n_unavailable_text):d.is(".wc-variation-selection-needed")&&b.alert(wc_add_to_cart_variation_params.i18n_make_a_selection_text))}).on("reload_product_variations",function(){h=c.data("product_variations"),i=h===!1}).on("reset_data",function(){f.find(".product_meta").find(".sku").wc_reset_content(),a(".product_weight").wc_reset_content(),a(".product_dimensions").wc_reset_content(),c.trigger("reset_image"),d.slideUp(200).trigger("hide_variation")}).on("reset_image",function(){c.wc_variations_image_update(!1)}).on("change",".variations select",function(){if(c.find('input[name="variation_id"], input.variation_id').val("").change(),c.find(".wc-no-matching-variations").remove(),i){j&&j.abort();var b=!0,d=!1,e={};c.find(".variations select").each(function(){var c=a(this).data("attribute_name")||a(this).attr("name"),f=a(this).val()||"";0===f.length?b=!1:d=!0,e[c]=f}),b?(e.product_id=g,e.custom_data=c.data("custom_data"),c.block({message:null,overlayCSS:{background:"#fff",opacity:.6}}),j=a.ajax({url:wc_add_to_cart_variation_params.wc_ajax_url.toString().replace("%%endpoint%%","get_variation"),type:"POST",data:e,success:function(a){a?c.trigger("found_variation",[a]):(c.trigger("reset_data"),c.find(".single_variation").after('

'+wc_add_to_cart_variation_params.i18n_no_matching_variations_text+"

"),c.find(".wc-no-matching-variations").slideDown(200))},complete:function(){c.unblock()}})):c.trigger("reset_data"),d?"hidden"===k.css("visibility")&&k.css("visibility","visible").hide().fadeIn():k.css("visibility","hidden")}else c.trigger("woocommerce_variation_select_change"),c.trigger("check_variations",["",!1]),a(this).blur();a(".product.has-default-attributes > .images").fadeTo(200,1),c.trigger("woocommerce_variation_has_changed")}).on("focusin touchstart",".variations select",function(){a(this).find("option:selected").attr("selected","selected"),i||(c.trigger("woocommerce_variation_select_focusin"),c.trigger("check_variations",[a(this).data("attribute_name")||a(this).attr("name"),!0]))}).on("found_variation",function(b,e){var g=f.find(".product_meta").find(".sku"),h=f.find(".product_weight"),i=f.find(".product_dimensions"),j=n.find(".quantity"),k=!0;e.sku?g.wc_set_content(e.sku):g.wc_reset_content(),e.weight_html?h.wc_set_content(e.weight_html):h.wc_reset_content(),e.dimensions_html?i.wc_set_content(e.dimensions_html):i.wc_reset_content(),c.wc_variations_image_update(e);var o="";e.variation_is_visible?(o=l({variation:e}),o=o.replace("/**/",""),d.html(o),c.find('input[name="variation_id"], input.variation_id').val(e.id).change()):(o=m(),o=o.replace("/**/",""),d.html(o),c.find('input[name="variation_id"], input.variation_id').val("").change()),e.sold_individually?(j.find("input.qty").val("1").attr("min","1").attr("max",""),j.hide()):(j.find("input.qty").attr("min",e.min_qty).attr("max",e.max_qty),j.show()),e.is_purchasable&&e.is_in_stock&&e.variation_is_visible||(k=!1),a.trim(d.text())?d.slideDown(200).trigger("show_variation",[e,k]):d.show().trigger("show_variation",[e,k])}).on("check_variations",function(c,f,g){if(!i){var j=!0,k=!1,l={},m=a(this),n=m.find(".reset_variations");m.find(".variations select").each(function(){var b=a(this).data("attribute_name")||a(this).attr("name"),c=a(this).val()||"";0===c.length?j=!1:k=!0,f&&b===f?(j=!1,l[b]=""):l[b]=c});var o=e.find_matching_variations(h,l);if(j){var p=o.shift();p?m.trigger("found_variation",[p]):(m.find(".variations select").val(""),g||m.trigger("reset_data"),b.alert(wc_add_to_cart_variation_params.i18n_no_matching_variations_text))}else m.trigger("update_variation_values",[o]),g||m.trigger("reset_data"),f||d.slideUp(200).trigger("hide_variation");k?"hidden"===n.css("visibility")&&n.css("visibility","visible").hide().fadeIn():n.css("visibility","hidden")}}).on("update_variation_values",function(b,d){i||(c.find(".variations select").each(function(b,c){var e,f=a(c);f.data("attribute_options")||f.data("attribute_options",f.find("option:gt(0)").get()),f.find("option:gt(0)").remove(),f.append(f.data("attribute_options")),f.find("option:gt(0)").removeClass("attached"),f.find("option:gt(0)").removeClass("enabled"),f.find("option:gt(0)").removeAttr("disabled"),e="undefined"!=typeof f.data("attribute_name")?f.data("attribute_name"):f.attr("name");for(var g in d)if("undefined"!=typeof d[g]){var h=d[g].attributes;for(var i in h)if(h.hasOwnProperty(i)){var j=h[i];if(i===e){var k="";d[g].variation_is_active&&(k="enabled"),j?(j=a("
").html(j).text(),j=j.replace(/'/g,"\\'"),j=j.replace(/"/g,'\\"'),f.find('option[value="'+j+'"]').addClass("attached "+k)):f.find("option:gt(0)").addClass("attached "+k)}}}f.find("option:gt(0):not(.attached)").remove(),f.find("option:gt(0):not(.enabled)").attr("disabled","disabled")}),c.trigger("woocommerce_update_variation_values"))}),c.trigger("wc_variation_form"),c};var e={find_matching_variations:function(a,b){for(var c=[],d=0;d1?(d.wc_set_variation_attr("src",a.image.src),d.wc_set_variation_attr("title",a.image.title),d.wc_set_variation_attr("alt",a.image.alt),d.wc_set_variation_attr("srcset",a.image.srcset),d.wc_set_variation_attr("sizes",a.image.sizes),e.wc_set_variation_attr("href",a.image.url),e.wc_set_variation_attr("title",a.image.caption)):(d.wc_reset_variation_attr("src"),d.wc_reset_variation_attr("title"),d.wc_reset_variation_attr("alt"),d.wc_reset_variation_attr("srcset"),d.wc_reset_variation_attr("sizes"),e.wc_reset_variation_attr("href"),e.wc_reset_variation_attr("title"))},a(function(){"undefined"!=typeof wc_add_to_cart_variation_params&&a(".variations_form").each(function(){a(this).wc_variation_form().find(".variations select:eq(0)").change()})})}(jQuery,window,document); \ No newline at end of file diff --git a/includes/abstracts/abstract-wc-data.php b/includes/abstracts/abstract-wc-data.php index 6260f5f7c4e..141a7022526 100644 --- a/includes/abstracts/abstract-wc-data.php +++ b/includes/abstracts/abstract-wc-data.php @@ -479,7 +479,9 @@ abstract class WC_Data { protected function set_prop( $prop, $value ) { if ( array_key_exists( $prop, $this->data ) ) { if ( true === $this->object_read ) { - $this->changes[ $prop ] = $value; + if ( $value !== $this->data[ $prop ] || array_key_exists( $prop, $this->changes ) ) { + $this->changes[ $prop ] = $value; + } } else { $this->data[ $prop ] = $value; } diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index 56ff97446b3..5727d2fb8d0 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -132,6 +132,17 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { return $value; } + /** + * If set, get the default attributes for a variable product. + * + * @deprecated 2.7.0 + * @return array + */ + public function get_variation_default_attributes() { + _deprecated_function( 'WC_Product_Variable::get_variation_default_attributes', '2.7', 'WC_Product::get_default_attributes' ); + return apply_filters( 'woocommerce_product_default_attributes', array_filter( (array) maybe_unserialize( $this->get_default_attributes() ) ), $this ); + } + /** * Returns the gallery attachment ids. * @@ -162,7 +173,7 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { */ public function reduce_stock( $amount = 1 ) { _deprecated_function( 'WC_Product::reduce_stock', '2.7', 'wc_update_product_stock' ); - wc_update_product_stock( $this, $amount, 'subtract' ); + wc_update_product_stock( $this, $amount, 'decrease' ); } /** @@ -174,17 +185,16 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { */ public function increase_stock( $amount = 1 ) { _deprecated_function( 'WC_Product::increase_stock', '2.7', 'wc_update_product_stock' ); - wc_update_product_stock( $this, $amount, 'add' ); + wc_update_product_stock( $this, $amount, 'increase' ); } /** * Check if the stock status needs changing. * - * @deprecated 2.7.0 + * @deprecated 2.7.0 Sync is done automatically on read/save, so calling this should not be needed any more. */ public function check_stock_status() { - _deprecated_function( 'WC_Product::check_stock_status', '2.7', 'wc_check_product_stock_status' ); - wc_check_product_stock_status( $this ); + _deprecated_function( 'WC_Product::check_stock_status', '2.7' ); } /** @@ -314,14 +324,14 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { /** * Functions for getting parts of a price, in html, used by get_price_html. * - * @deprecated 2.7.0 Use wc_format_price_range instead. + * @deprecated 2.7.0 Use wc_format_sale_price instead. * @param string $from String or float to wrap with 'from' text * @param mixed $to String or float to wrap with 'to' text * @return string */ public function get_price_html_from_to( $from, $to ) { - _deprecated_function( 'WC_Product::get_price_html_from_to', '2.7', 'wc_format_price_range' ); - return apply_filters( 'woocommerce_get_price_html_from_to', wc_format_price_range( $from, $to ), $from, $to, $this ); + _deprecated_function( 'WC_Product::get_price_html_from_to', '2.7', 'wc_format_sale_price' ); + return apply_filters( 'woocommerce_get_price_html_from_to', wc_format_sale_price( $from, $to ), $from, $to, $this ); } /** @@ -554,21 +564,19 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { */ public function get_total_stock() { _deprecated_function( 'WC_Product::get_total_stock', '2.7', 'Use get_stock_quantity on each child. Beware of performance issues in doing so.' ); - if ( empty( $this->total_stock ) ) { - if ( sizeof( $this->get_children() ) > 0 ) { - $this->total_stock = max( 0, $this->get_stock_quantity() ); + if ( sizeof( $this->get_children() ) > 0 ) { + $total_stock = max( 0, $this->get_stock_quantity() ); - foreach ( $this->get_children() as $child_id ) { - if ( 'yes' === get_post_meta( $child_id, '_manage_stock', true ) ) { - $stock = get_post_meta( $child_id, '_stock', true ); - $this->total_stock += max( 0, wc_stock_amount( $stock ) ); - } + foreach ( $this->get_children() as $child_id ) { + if ( 'yes' === get_post_meta( $child_id, '_manage_stock', true ) ) { + $stock = get_post_meta( $child_id, '_stock', true ); + $total_stock += max( 0, wc_stock_amount( $stock ) ); } - } else { - $this->total_stock = $this->get_stock_quantity(); } + } else { + $total_stock = $this->get_stock_quantity(); } - return wc_stock_amount( $this->total_stock ); + return wc_stock_amount( $total_stock ); } /** @@ -581,4 +589,76 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { _deprecated_function( 'WC_Product::get_formatted_variation_attributes', '2.7', 'wc_get_formatted_variation' ); return wc_get_formatted_variation( $this->get_variation_attributes(), $flat ); } + + /** + * Sync variable product prices with the children lowest/highest prices. + * + * @deprecated 2.7.0 not used in core. + */ + public function variable_product_sync( $product_id = '' ) { + _deprecated_function( 'WC_Product::variable_product_sync', '2.7' ); + if ( empty( $product_id ) ) { + $product_id = $this->get_id(); + } + + // Sync prices with children + self::sync( $product_id ); + + // Re-load prices + $this->read_product_data(); + } + + /** + * Sync the variable product's attributes with the variations. + */ + public static function sync_attributes( $product, $children = false ) { + /** + * 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', '<' ) ) { + if ( ! is_a( $product, 'WC_Product' ) ) { + $product = wc_get_product( $product ); + } + + $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; + } + } + } + } + } + } + } + } + + /** + * Match a variation to a given set of attributes using a WP_Query. + * @deprecated 2.7.0 in favour of wc_find_matching_product_variation. + */ + public function get_matching_variation( $match_attributes = array() ) { + _deprecated_function( 'WC_Product::get_matching_variation', '2.7', 'wc_find_matching_product_variation' ); + return wc_find_matching_product_variation( $this, $match_attributes ); + } } diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index f443b32dc85..32f77692d46 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -979,9 +979,9 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { $default_args = array( 'name' => $product->get_title(), 'tax_class' => $product->get_tax_class(), - 'product_id' => $product->get_id(), - 'variation_id' => isset( $product->variation_id ) ? $product->variation_id : 0, - 'variation' => isset( $product->variation_id ) ? $product->get_variation_attributes() : array(), + 'product_id' => $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id(), + 'variation_id' => $product->is_type( 'variation' ) ? $product->get_id() : 0, + 'variation' => $product->is_type( 'variation' ) ? $product->get_attributes() : array(), 'subtotal' => $product->get_price_excluding_tax( $qty ), 'total' => $product->get_price_excluding_tax( $qty ), 'quantity' => $qty, diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 2b1ee2e4f87..ef0f7c48679 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -170,6 +170,23 @@ class WC_Product extends WC_Abstract_Legacy_Product { | Methods for getting data from the product object. */ + /** + * Get all class data in array format. + * @since 2.7.0 + * @return array + */ + public function get_data() { + return array_merge( + array( + 'id' => $this->get_id(), + ), + $this->data, + array( + 'meta_data' => $this->get_meta_data(), + ) + ); + } + /** * Get product name. * @@ -893,15 +910,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param string $status New status. */ public function set_stock_status( $status ) { - $status = 'outofstock' === $status ? 'outofstock' : 'instock'; - - if ( $this->managing_stock() ) { - if ( ! $this->backorders_allowed() && $this->get_stock_quantity() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) { - $status = 'outofstock'; - } - } - - $this->set_prop( 'stock_status', $status ); + $this->set_prop( 'stock_status', 'outofstock' === $status ? 'outofstock' : 'instock' ); } /** @@ -1030,7 +1039,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param array $raw_attributes Array of WC_Product_Attribute objects. */ public function set_attributes( $raw_attributes ) { - $attributes = array_fill_keys( array_keys( $this->data['attributes'] ), null ); + $attributes = array_fill_keys( array_keys( $this->get_attributes( 'edit' ) ), null ); foreach ( $raw_attributes as $attribute ) { if ( is_a( $attribute, 'WC_Product_Attribute' ) ) { $attributes[ sanitize_title( $attribute->get_name() ) ] = $attribute; @@ -1289,16 +1298,16 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'menu_order' => $post_object->menu_order, 'reviews_allowed' => 'open' === $post_object->comment_status, ) ); - $this->read_product_data(); $this->read_meta_data(); $this->read_attributes(); + $this->read_product_data(); // Set object_read true once all data is read. $this->set_object_read( true ); } /** - * Read post data. Can be overridden by child classes to load other props. + * Read product data. Can be overridden by child classes to load other props. * * @since 2.7.0 */ @@ -1310,6 +1319,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'sku' => get_post_meta( $id, '_sku', true ), 'regular_price' => get_post_meta( $id, '_regular_price', true ), 'sale_price' => get_post_meta( $id, '_sale_price', true ), + 'price' => get_post_meta( $id, '_price', true ), 'date_on_sale_from' => get_post_meta( $id, '_sale_price_dates_from', true ), 'date_on_sale_to' => get_post_meta( $id, '_sale_price_dates_to', true ), 'total_sales' => get_post_meta( $id, 'total_sales', true ), @@ -1339,12 +1349,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'download_expiry' => get_post_meta( $id, '_download_expiry', true ), 'image_id' => get_post_thumbnail_id( $id ), ) ); - - if ( $this->is_on_sale() ) { - $this->set_price( $this->get_sale_price() ); - } else { - $this->set_price( $this->get_regular_price() ); - } } /** @@ -1436,25 +1440,90 @@ class WC_Product extends WC_Abstract_Legacy_Product { do_action( 'woocommerce_update_' . $this->post_type, $this->get_id() ); } + /** + * Ensure properties are set correctly before save. + * @since 2.7.0 + */ + public function validate_props() { + // Before updating, ensure stock props are all aligned. Qty and backorders are not needed if not stock managed. + if ( ! $this->get_manage_stock() ) { + $this->set_stock_quantity( '' ); + $this->set_backorders( 'no' ); + + // If we are stock managing and we don't have stock, force out of stock status. + } elseif ( $this->get_stock_quantity() <= get_option( 'woocommerce_notify_no_stock_amount' ) && 'no' === $this->get_backorders() ) { + $this->set_stock_status( 'outofstock' ); + + // If the stock level is changing and we do now have enough, force in stock status. + } elseif ( $this->get_stock_quantity() > get_option( 'woocommerce_notify_no_stock_amount' ) && array_key_exists( 'stock_quantity', $this->get_changes() ) ) { + $this->set_stock_status( 'instock' ); + } + } + /** * Save data (either create or update depending on if we are working on an existing product). * * @since 2.7.0 */ public function save() { - parent::save(); + $this->validate_props(); - // Make sure we store the product type. - $type_term = get_term_by( 'name', $this->get_type(), 'product_type' ); - wp_set_object_terms( $this->get_id(), absint( $type_term->term_id ), 'product_type' ); + if ( $this->get_id() ) { + $this->update(); + } else { + $this->create(); + } - // Version is set to current WC version to track data changes. - update_post_meta( $this->get_id(), '_product_version', WC_VERSION ); - wc_delete_product_transients( $this->get_id() ); + $this->apply_changes(); + $this->update_product_type(); + $this->update_product_version(); + $this->update_term_counts(); + $this->clear_caches(); return $this->get_id(); } + /** + * Make sure we store the product type. + */ + protected function update_product_type() { + if ( 'product' === $this->post_type ) { + $type_term = get_term_by( 'name', $this->get_type(), 'product_type' ); + wp_set_object_terms( $this->get_id(), absint( $type_term->term_id ), 'product_type' ); + } + } + + /** + * Version is set to current WC version to track data changes. + */ + protected function update_product_version() { + update_post_meta( $this->get_id(), '_product_version', WC_VERSION ); + } + + /** + * Count terms. These are done at this point so all product props are set in advance. + */ + protected function update_term_counts() { + if ( ! wp_defer_term_counting() ) { + global $wc_allow_term_recount; + + $wc_allow_term_recount = true; + + // Update counts for the post's terms. + foreach ( (array) get_object_taxonomies( $this->post_type ) as $taxonomy ) { + $tt_ids = wp_get_object_terms( $this->get_id(), $taxonomy, array( 'fields' => 'tt_ids' ) ); + wp_update_term_count( $tt_ids, $taxonomy ); + } + } + } + + /** + * Clear any caches. + */ + protected function clear_caches() { + wc_delete_product_transients( $this->get_id() ); + } + /** * Delete product from the database. * @@ -1515,6 +1584,9 @@ class WC_Product extends WC_Abstract_Legacy_Product { switch ( $prop ) { case 'virtual' : case 'downloadable' : + case 'manage_stock' : + case 'featured' : + case 'sold_individually' : $updated = update_post_meta( $this->get_id(), $meta_key, wc_bool_to_string( $value ) ); break; case 'gallery_image_ids' : @@ -1537,16 +1609,22 @@ class WC_Product extends WC_Abstract_Legacy_Product { } } - if ( $this->is_on_sale() ) { - update_post_meta( $this->get_id(), '_price', $this->get_sale_price() ); - } else { - update_post_meta( $this->get_id(), '_price', $this->get_regular_price() ); + if ( in_array( 'date_on_sale_from', $updated_props ) || in_array( 'date_on_sale_to', $updated_props ) || in_array( 'regular_price', $updated_props ) || in_array( 'sale_price', $updated_props ) ) { + if ( $this->is_on_sale() ) { + update_post_meta( $this->get_id(), '_price', $this->get_sale_price() ); + } else { + update_post_meta( $this->get_id(), '_price', $this->get_regular_price() ); + } } if ( in_array( 'featured', $updated_props ) ) { delete_transient( 'wc_featured_products' ); } + if ( in_array( 'catalog_visibility', $updated_props ) ) { + do_action( 'woocommerce_product_set_visibility', $this->get_id(), $this->get_catalog_visibility() ); + } + if ( in_array( 'downloads', $updated_props ) ) { // grant permission to any newly added files on any existing orders for this product prior to saving. if ( $this->is_type( 'variation' ) ) { @@ -1571,9 +1649,9 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @since 2.7.0 */ protected function update_terms() { - wp_set_post_terms( $this->get_id(), $this->data['category_ids'], 'product_cat', false ); - wp_set_post_terms( $this->get_id(), $this->data['tag_ids'], 'product_tag', false ); - wp_set_post_terms( $this->get_id(), array( $this->data['shipping_class_id'] ), 'product_shipping_class', false ); + wp_set_post_terms( $this->get_id(), $this->get_category_ids( 'edit' ), 'product_cat', false ); + wp_set_post_terms( $this->get_id(), $this->get_tag_ids( 'edit' ), 'product_tag', false ); + wp_set_post_terms( $this->get_id(), array( $this->get_shipping_class_id( 'edit' ) ), 'product_shipping_class', false ); } /** @@ -1663,7 +1741,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return bool */ public function is_downloadable() { - return apply_filters( 'woocommerce_is_downloadable', true === $this->data['downloadable'] , $this ); + return apply_filters( 'woocommerce_is_downloadable', true === $this->get_downloadable(), $this ); } /** @@ -1672,7 +1750,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return bool */ public function is_virtual() { - return apply_filters( 'woocommerce_is_virtual', true === $this->data['virtual'], $this ); + return apply_filters( 'woocommerce_is_virtual', true === $this->get_virtual(), $this ); } /** @@ -1718,7 +1796,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return bool */ public function is_purchasable() { - return apply_filters( 'woocommerce_is_purchasable', $this->exists() && $this->is_in_stock() && ( 'publish' === $this->get_status() || current_user_can( 'edit_post', $this->get_id() ) ) && '' !== $this->get_price(), $this ); + return apply_filters( 'woocommerce_is_purchasable', $this->exists() && ( 'publish' === $this->get_status() || current_user_can( 'edit_post', $this->get_id() ) ) && '' !== $this->get_price(), $this ); } /** @@ -1803,7 +1881,10 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return bool */ public function managing_stock() { - return $this->get_manage_stock() && 'yes' === get_option( 'woocommerce_manage_stock' ); + if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) { + return $this->get_manage_stock(); + } + return false; } /** @@ -1841,7 +1922,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return bool */ public function has_enough_stock( $quantity ) { - return ! $this->managing_stock() || $this->backorders_allowed() || $this->get_stock_quantity() >= $quantity ? true : false; + return ! $this->managing_stock() || $this->backorders_allowed() || $this->get_stock_quantity() >= $quantity; } /** @@ -1899,9 +1980,17 @@ class WC_Product extends WC_Abstract_Legacy_Product { return array(); } + /** + * If the stock level comes from another product ID, this should be modified. + * @since 2.7.0 + * @return int + */ + public function get_stock_managed_by_id() { + return $this->get_id(); + } + /** * Returns the price in html format. - * @todo Should this be moved out of the classes? * @return string */ public function get_price_html( $deprecated = '' ) { @@ -1910,7 +1999,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { } if ( $this->is_on_sale() ) { - $price = wc_format_price_range( wc_get_price_to_display( $this, array( 'price' => $this->get_regular_price() ) ), wc_get_price_to_display( $this ) ) . wc_get_price_suffix( $this ); + $price = wc_format_sale_price( wc_get_price_to_display( $this, array( 'price' => $this->get_regular_price() ) ), wc_get_price_to_display( $this ) ) . wc_get_price_suffix( $this ); } else { $price = wc_price( wc_get_price_to_display( $this ) ) . wc_get_price_suffix( $this ); } diff --git a/includes/admin/class-wc-admin-duplicate-product.php b/includes/admin/class-wc-admin-duplicate-product.php index 0c2bb653cc0..fcff6c383fa 100644 --- a/includes/admin/class-wc-admin-duplicate-product.php +++ b/includes/admin/class-wc-admin-duplicate-product.php @@ -216,7 +216,6 @@ class WC_Admin_Duplicate_Product { * * @param mixed $id * @return WP_Post|bool - * @todo Returning false? Need to check for it in... * @see duplicate_product */ private function get_product_to_duplicate( $id ) { diff --git a/includes/admin/class-wc-admin-post-types.php b/includes/admin/class-wc-admin-post-types.php index fd3f3031a3d..7bced0ef5e9 100644 --- a/includes/admin/class-wc-admin-post-types.php +++ b/includes/admin/class-wc-admin-post-types.php @@ -325,25 +325,25 @@ class WC_Admin_Post_Types { /* Custom inline data for woocommerce. */ echo ' '; @@ -352,11 +352,11 @@ class WC_Admin_Post_Types { echo $the_product->get_sku() ? $the_product->get_sku() : ''; break; case 'product_type' : - if ( 'grouped' == $the_product->product_type ) { + if ( $the_product->is_type( 'grouped' ) ) { echo ''; - } elseif ( 'external' == $the_product->product_type ) { + } elseif ( $the_product->is_type( 'external' ) ) { echo ''; - } elseif ( 'simple' == $the_product->product_type ) { + } elseif ( $the_product->is_type( 'simple' ) ) { if ( $the_product->is_virtual() ) { echo ''; @@ -365,11 +365,11 @@ class WC_Admin_Post_Types { } else { echo ''; } - } elseif ( 'variable' == $the_product->product_type ) { + } elseif ( $the_product->is_type( 'variable' ) ) { echo ''; } else { // Assuming that we have other types in future - echo ''; + echo ''; } break; case 'price' : @@ -1033,7 +1033,7 @@ class WC_Admin_Post_Types { } /** - * Quick edit. + * Quick edit. @todo CRUDIFY * * @param integer $post_id * @param WC_Product $product diff --git a/includes/admin/meta-boxes/views/html-product-data-advanced.php b/includes/admin/meta-boxes/views/html-product-data-advanced.php index b91f0982172..b63eaef6e3e 100644 --- a/includes/admin/meta-boxes/views/html-product-data-advanced.php +++ b/includes/admin/meta-boxes/views/html-product-data-advanced.php @@ -4,7 +4,7 @@ '_purchase_note', - 'value' => $product_object->get_purchase_note(), + 'value' => $product_object->get_purchase_note( 'edit' ), 'label' => __( 'Purchase note', 'woocommerce' ), 'desc_tip' => true, 'description' => __( 'Enter an optional note to send the customer after purchase.', 'woocommerce' ), @@ -16,7 +16,7 @@ 'menu_order', - 'value' => $product_object->get_menu_order(), + 'value' => $product_object->get_menu_order( 'edit' ), 'label' => __( 'Menu order', 'woocommerce' ), 'desc_tip' => true, 'description' => __( 'Custom ordering position.', 'woocommerce' ), @@ -32,7 +32,7 @@ '_reviews_allowed', - 'value' => $product_object->get_reviews_allowed() ? 'open' : 'closed', + 'value' => $product_object->get_reviews_allowed( 'edit' ) ? 'open' : 'closed', 'label' => __( 'Enable reviews', 'woocommerce' ), 'cbvalue' => 'open', ) ); diff --git a/includes/admin/meta-boxes/views/html-product-data-attributes.php b/includes/admin/meta-boxes/views/html-product-data-attributes.php index a4fc63dd5ee..dae1a3761d6 100644 --- a/includes/admin/meta-boxes/views/html-product-data-attributes.php +++ b/includes/admin/meta-boxes/views/html-product-data-attributes.php @@ -25,7 +25,7 @@
get_attributes(); + $attributes = $product_object->get_attributes( 'edit' ); $i = -1; foreach ( $attributes as $attribute ) { diff --git a/includes/admin/meta-boxes/views/html-product-data-general.php b/includes/admin/meta-boxes/views/html-product-data-general.php index 2f5e7690c94..e5d759bd007 100644 --- a/includes/admin/meta-boxes/views/html-product-data-general.php +++ b/includes/admin/meta-boxes/views/html-product-data-general.php @@ -4,7 +4,7 @@ '_product_url', - 'value' => is_callable( array( $product_object, 'get_product_url' ) ) ? $product_object->get_product_url() : '', + 'value' => is_callable( array( $product_object, 'get_product_url' ) ) ? $product_object->get_product_url( 'edit' ) : '', 'label' => __( 'Product URL', 'woocommerce' ), 'placeholder' => 'http://', 'description' => __( 'Enter the external URL to the product.', 'woocommerce' ), @@ -12,7 +12,7 @@ woocommerce_wp_text_input( array( 'id' => '_button_text', - 'value' => is_callable( array( $product_object, 'get_button_text' ) ) ? $product_object->get_button_text() : '', + 'value' => is_callable( array( $product_object, 'get_button_text' ) ) ? $product_object->get_button_text( 'edit' ) : '', 'label' => __( 'Button text', 'woocommerce' ), 'placeholder' => _x( 'Buy product', 'placeholder', 'woocommerce' ), 'description' => __( 'This text will be shown on the button linking to the external product.', 'woocommerce' ), @@ -24,21 +24,21 @@ '_regular_price', - 'value' => $product_object->get_regular_price(), + 'value' => $product_object->get_regular_price( 'edit' ), 'label' => __( 'Regular price', 'woocommerce' ) . ' (' . get_woocommerce_currency_symbol() . ')', 'data_type' => 'price', ) ); woocommerce_wp_text_input( array( 'id' => '_sale_price', - 'value' => $product_object->get_sale_price(), + 'value' => $product_object->get_sale_price( 'edit' ), 'data_type' => 'price', 'label' => __( 'Sale price', 'woocommerce' ) . ' (' . get_woocommerce_currency_symbol() . ')', 'description' => '' . __( 'Schedule', 'woocommerce' ) . '', ) ); - $sale_price_dates_from = ( $date = $product_object->get_date_on_sale_from() ) ? date_i18n( 'Y-m-d', $date ) : ''; - $sale_price_dates_to = ( $date = $product_object->get_date_on_sale_to() ) ? date_i18n( 'Y-m-d', $date ) : ''; + $sale_price_dates_from = ( $date = $product_object->get_date_on_sale_from( 'edit' ) ) ? date_i18n( 'Y-m-d', $date ) : ''; + $sale_price_dates_to = ( $date = $product_object->get_date_on_sale_to( 'edit' ) ) ? date_i18n( 'Y-m-d', $date ) : ''; echo '

@@ -65,7 +65,7 @@ get_downloads() ) { + if ( $downloadable_files = $product_object->get_downloads( 'edit' ) ) { foreach ( $downloadable_files as $key => $file ) { include( 'html-product-download.php' ); } @@ -92,7 +92,7 @@ '_download_limit', - 'value' => -1 === $product_object->get_download_limit() ? '' : $product_object->get_download_limit(), + 'value' => -1 === $product_object->get_download_limit( 'edit' ) ? '' : $product_object->get_download_limit( 'edit' ), 'label' => __( 'Download limit', 'woocommerce' ), 'placeholder' => __( 'Unlimited', 'woocommerce' ), 'description' => __( 'Leave blank for unlimited re-downloads.', 'woocommerce' ), @@ -105,7 +105,7 @@ woocommerce_wp_text_input( array( 'id' => '_download_expiry', - 'value' => -1 === $product_object->get_download_expiry() ? '' : $product_object->get_download_expiry(), + 'value' => -1 === $product_object->get_download_expiry( 'edit' ) ? '' : $product_object->get_download_expiry( 'edit' ), 'label' => __( 'Download expiry', 'woocommerce' ), 'placeholder' => __( 'Never', 'woocommerce' ), 'description' => __( 'Enter the number of days before a download link expires, or leave blank.', 'woocommerce' ), @@ -125,7 +125,7 @@ '_tax_status', - 'value' => $product_object->get_tax_status(), + 'value' => $product_object->get_tax_status( 'edit' ), 'label' => __( 'Tax status', 'woocommerce' ), 'options' => array( 'taxable' => __( 'Taxable', 'woocommerce' ), @@ -138,7 +138,7 @@ woocommerce_wp_select( array( 'id' => '_tax_class', - 'value' => $product_object->get_tax_class(), + 'value' => $product_object->get_tax_class( 'edit' ), 'label' => __( 'Tax class', 'woocommerce' ), 'options' => wc_get_product_tax_class_options(), 'desc_tip' => 'true', diff --git a/includes/admin/meta-boxes/views/html-product-data-inventory.php b/includes/admin/meta-boxes/views/html-product-data-inventory.php index a784e63d1ef..bc37b5c878c 100644 --- a/includes/admin/meta-boxes/views/html-product-data-inventory.php +++ b/includes/admin/meta-boxes/views/html-product-data-inventory.php @@ -5,7 +5,7 @@ if ( wc_product_sku_enabled() ) { woocommerce_wp_text_input( array( 'id' => '_sku', - 'value' => $product_object->get_sku(), + 'value' => $product_object->get_sku( 'edit' ), 'label' => '' . __( 'SKU', 'woocommerce' ) . '', 'desc_tip' => true, 'description' => __( 'SKU refers to a Stock-keeping unit, a unique identifier for each distinct product and service that can be purchased.', 'woocommerce' ), @@ -18,7 +18,7 @@ woocommerce_wp_checkbox( array( 'id' => '_manage_stock', - 'value' => $product_object->get_manage_stock() ? 'yes' : 'no', + 'value' => $product_object->get_manage_stock( 'edit' ) ? 'yes' : 'no', 'wrapper_class' => 'show_if_simple show_if_variable', 'label' => __( 'Manage stock?', 'woocommerce' ), 'description' => __( 'Enable stock management at product level', 'woocommerce' ), @@ -30,7 +30,7 @@ woocommerce_wp_text_input( array( 'id' => '_stock', - 'value' => $product_object->get_stock_quantity(), + 'value' => $product_object->get_stock_quantity( 'edit' ), 'label' => __( 'Stock quantity', 'woocommerce' ), 'desc_tip' => true, 'description' => __( 'Stock quantity. If this is a variable product this value will be used to control stock for all variations, unless you define stock at variation level.', 'woocommerce' ), @@ -43,7 +43,7 @@ woocommerce_wp_select( array( 'id' => '_backorders', - 'value' => $product_object->get_backorders(), + 'value' => $product_object->get_backorders( 'edit' ), 'label' => __( 'Allow backorders?', 'woocommerce' ), 'options' => wc_get_product_backorder_options(), 'desc_tip' => true, @@ -57,7 +57,7 @@ woocommerce_wp_select( array( 'id' => '_stock_status', - 'value' => $product_object->get_stock_status(), + 'value' => $product_object->get_stock_status( 'edit' ), 'wrapper_class' => 'hide_if_variable hide_if_external', 'label' => __( 'Stock status', 'woocommerce' ), 'options' => wc_get_product_stock_status_options(), @@ -73,7 +73,7 @@ '_sold_individually', - 'value' => $product_object->get_sold_individually() ? 'yes' : 'no', + 'value' => $product_object->get_sold_individually( 'edit' ) ? 'yes' : 'no', 'wrapper_class' => 'show_if_simple show_if_variable', 'label' => __( 'Sold individually', 'woocommerce' ), 'description' => __( 'Enable this to only allow one of this item to be bought in a single order', 'woocommerce' ), diff --git a/includes/admin/meta-boxes/views/html-product-data-linked-products.php b/includes/admin/meta-boxes/views/html-product-data-linked-products.php index 4ec1bd6b011..7d9f49518e9 100644 --- a/includes/admin/meta-boxes/views/html-product-data-linked-products.php +++ b/includes/admin/meta-boxes/views/html-product-data-linked-products.php @@ -4,7 +4,7 @@

- - - + + +

__( 'No shipping class', 'woocommerce' ), 'name' => 'product_shipping_class', 'id' => 'product_shipping_class', - 'selected' => $product_object->get_shipping_class_id(), + 'selected' => $product_object->get_shipping_class_id( 'edit' ), 'class' => 'select short', ); ?>

diff --git a/includes/admin/meta-boxes/views/html-variation-admin.php b/includes/admin/meta-boxes/views/html-variation-admin.php index 5960d7cc905..fbe793b142f 100644 --- a/includes/admin/meta-boxes/views/html-variation-admin.php +++ b/includes/admin/meta-boxes/views/html-variation-admin.php @@ -18,9 +18,9 @@ if ( ! defined( 'ABSPATH' ) ) {

# get_attributes(); + $attribute_values = $variation_object->get_attributes( 'edit' ); - foreach ( $product_object->get_attributes() as $attribute ) { + foreach ( $product_object->get_attributes( 'edit' ) as $attribute ) { if ( ! $attribute->get_variation() ) { continue; } @@ -42,13 +42,13 @@ if ( ! defined( 'ABSPATH' ) ) { } ?> - +