Synchronized with master

This commit is contained in:
Claudio Sanches 2017-01-12 17:53:15 -02:00
commit 9c27661dea
85 changed files with 819 additions and 556 deletions

View File

@ -6,8 +6,6 @@ sudo: false
php: php:
- 5.2 - 5.2
- 5.3 - 5.3
# - 5.4
# - 5.5
- 5.6 - 5.6
- 7.0 - 7.0
- 7.1 - 7.1
@ -21,8 +19,6 @@ matrix:
include: include:
- php: 5.6 - php: 5.6
env: WP_VERSION=latest WP_MULTISITE=1 env: WP_VERSION=latest WP_MULTISITE=1
allow_failures:
- php: 7.1
before_script: before_script:
- bash tests/bin/install.sh woocommerce_test root '' localhost $WP_VERSION - bash tests/bin/install.sh woocommerce_test root '' localhost $WP_VERSION

View File

@ -1,5 +1,43 @@
== Changelog == == Changelog ==
= 2.6.12 - 2017-01-12 =
* Fix - Make images shown up on pageload when using ajax variations.
* Fix - Allow variations options to be deselected in IE11.
* Fix - Disabled-button and pagination styling in 2017.
* Fix - PHP 7.1 compatibility issues with non-numeric math operations.
* Fix - Fix notices in abstract class when price is empty.
= 2.6.11 - 2016-12-22 =
* Fix - Variation form compatibility with quotes in attribute values, and initial variation image fadeIn on certain configs.
= 2.6.10 - 2016-12-22 =
* Fix - Flat rate no class costs when no shipping classes exist.
* Fix - Returned REST API coupon expiry date.
* Fix - reviews_allowed being set to false in Rest API.
* Fix - Sales date series for some custom ranges.
* Fix - Missing attributes when an option is chosen by default on variations. This was the result of a Firefox 50 compatibility fix. In order to support both Firefox, Chrome, IE, and Edge we've done some refactoring of the variation add to cart scripts.
* Tweak - Updated Geo IP API services.
* Dev - Added support for WP VIP/VIP GO GEO IP headers.
* Dev - API - Throw error messages when product image ID is not a valid WordPress attachment ID.
= 2.6.9 - 2016-12-07 =
* Theme - Added support for Twenty Seventeen Theme.
* Fix - Excluded webhook delivery logs from comments count.
* Fix - Included password strength meter in "Lost Password" page.
* Fix - Order fee currency in admin screen.
* Fix - Variation selection on Firefox 40.
* Fix - Don't prevent submission when table is not found on cart.
* Fix - Improved layered nav counts on attribute archives.
* Fix - Fixed pagination when removing layered nav items via widget.
* Fix - Default BE tax rate.
* Fix - Downloads should store variation ID rather than product if set. Also fixes link on account page.
* Fix - Use wp_list_sort instead of _usort_terms_by_ID to be compatible with 4.7.
* Fix - Only return empty string if empty for weight and dimension functions.
* Fix - Added correct fallbacks for logout/lost password URLs when endpoints are not defined.
* Security - Wrapped admin tax rate table values in _escape to thwart evil CSVs an admin user could upload. Vulnerability was discovered by Fortinets FortiGuard Labs.
* Dev - API - Only update categories menu order and display if defined.
* Dev - Fixed when should deliver wp_trash_post webhooks.
= 2.6.8 - 2016-11-10 = = 2.6.8 - 2016-11-10 =
* Fix - REQUEST_URI was missing a trailing slash when being compared in the cache prevention functions. * Fix - REQUEST_URI was missing a trailing slash when being compared in the cache prevention functions.
* Fix - Prevent issues when sending empty prices to PayPal. * Fix - Prevent issues when sending empty prices to PayPal.
@ -207,7 +245,7 @@
* Tweak - Allow failed orders to be edited. * Tweak - Allow failed orders to be edited.
* Tweak - If there are no shipping methods setup, dont prompt for shipping at checkout. * Tweak - If there are no shipping methods setup, dont prompt for shipping at checkout.
* Tweak - Allowed country exclusion, rather than just inclusion, in sell to setting. * Tweak - Allowed country exclusion, rather than just inclusion, in sell to setting.
* Lots, lots more - [see the comparison here: https://github.com/woocommerce/woocommerce/compare/2.5.5...2.6.0 * Lots, lots more - [see the comparison here](https://github.com/woocommerce/woocommerce/compare/2.5.5...2.6.0).
= 2.5.5 - 2016-03-11 = = 2.5.5 - 2016-03-11 =
* Fix - Before running dbdelta, drop indexes to prevent duplicate key notices. * Fix - Before running dbdelta, drop indexes to prevent duplicate key notices.

File diff suppressed because one or more lines are too long

View File

@ -5495,6 +5495,9 @@ table.bar_chart {
.select2-dropdown--above { .select2-dropdown--above {
box-shadow: 0 -1px 1px rgba(0, 0, 0, 0.1); box-shadow: 0 -1px 1px rgba(0, 0, 0, 0.1);
} }
.select2-selection__rendered.ui-sortable li {
cursor: move;
}
.select2-container { .select2-container {
.select2-selection { .select2-selection {
border-color: #ddd; border-color: #ddd;

File diff suppressed because one or more lines are too long

View File

@ -76,6 +76,7 @@
ul.page-numbers { ul.page-numbers {
padding: 0; padding: 0;
display: block;
} }
span.page-numbers, span.page-numbers,
@ -339,6 +340,9 @@ dl.variation {
padding-top: .72em; padding-top: .72em;
padding-bottom: .72em; padding-bottom: .72em;
} }
.button.disabled {
opacity: 0.2;
}
} }
} }
@ -753,6 +757,23 @@ table.variations {
} }
} }
#shipping_method {
list-style: none;
margin-top: 1em;
li {
margin-bottom: .5em;
input {
float: left;
}
label {
line-height: 1.15;
}
}
}
.checkout-button { .checkout-button {
display: block; display: block;
padding: 1em 2em; padding: 1em 2em;
@ -952,4 +973,4 @@ table.variations {
body.page-two-column.woocommerce-account:not(.archive) #primary .entry-content { body.page-two-column.woocommerce-account:not(.archive) #primary .entry-content {
width: 78%; width: 78%;
} }
} }

File diff suppressed because one or more lines are too long

View File

@ -15,6 +15,14 @@
*/ */
.woocommerce, .woocommerce-page { .woocommerce, .woocommerce-page {
.woocommerce-message,
.woocommerce-error,
.woocommerce-info {
.button {
float: right;
}
}
/** /**
* General layout styles * General layout styles
*/ */

View File

@ -114,12 +114,6 @@ jQuery( function( $ ) {
}).change(); }).change();
$( document.body ).on( 'woocommerce-product-type-change', function( e, select_val ) {
if ( 'variable' !== select_val && 0 < $( '#variable_product_options' ).find( 'input[name^=variable_sku]' ).length && $( document.body ).triggerHandler( 'woocommerce-display-product-type-alert', select_val ) !== false ) {
window.alert( woocommerce_admin_meta_boxes.i18n_product_type_alert );
}
});
$( 'input#_downloadable, input#_virtual' ).change( function() { $( 'input#_downloadable, input#_virtual' ).change( function() {
show_and_hide_panels(); show_and_hide_panels();
}); });
@ -439,10 +433,11 @@ jQuery( function( $ ) {
}); });
var data = { var data = {
post_id: woocommerce_admin_meta_boxes.post_id, post_id : woocommerce_admin_meta_boxes.post_id,
data: $( '.product_attributes' ).find( 'input, select, textarea' ).serialize(), product_type: $( '#product-type' ).val(),
action: 'woocommerce_save_attributes', data : $( '.product_attributes' ).find( 'input, select, textarea' ).serialize(),
security: woocommerce_admin_meta_boxes.save_attributes_nonce action : 'woocommerce_save_attributes',
security : woocommerce_admin_meta_boxes.save_attributes_nonce
}; };
$.post( woocommerce_admin_meta_boxes.ajax_url, data, function() { $.post( woocommerce_admin_meta_boxes.ajax_url, data, function() {

File diff suppressed because one or more lines are too long

View File

@ -31,7 +31,7 @@
$form.on( 'reload_product_variations', { variationForm: this }, this.onReload ); $form.on( 'reload_product_variations', { variationForm: this }, this.onReload );
$form.on( 'hide_variation', { variationForm: this }, this.onHide ); $form.on( 'hide_variation', { variationForm: this }, this.onHide );
$form.on( 'show_variation', { variationForm: this }, this.onShow ); $form.on( 'show_variation', { variationForm: this }, this.onShow );
$form.on( 'click', 'single_add_to_cart_button', { variationForm: this }, this.onAddToCart ); $form.on( 'click', '.single_add_to_cart_button', { variationForm: this }, this.onAddToCart );
$form.on( 'reset_data', { variationForm: this }, this.onResetDisplayedVariation ); $form.on( 'reset_data', { variationForm: this }, this.onResetDisplayedVariation );
$form.on( 'reset_image', { variationForm: this }, this.onResetImage ); $form.on( 'reset_image', { variationForm: this }, this.onResetImage );
$form.on( 'change', '.variations select', { variationForm: this }, this.onChange ); $form.on( 'change', '.variations select', { variationForm: this }, this.onChange );
@ -124,35 +124,57 @@
* Looks for matching variations for current selected attributes. * Looks for matching variations for current selected attributes.
*/ */
VariationForm.prototype.onFindVariation = function( event ) { VariationForm.prototype.onFindVariation = function( event ) {
var form = event.data.variationForm; var form = event.data.variationForm,
attributes = form.getChosenAttributes(),
if ( form.useAjax ) { currentAttributes = attributes.data;
return;
}
form.$form.trigger( 'update_variation_values' );
var attributes = form.getChosenAttributes(),
matching_variations = form.findMatchingVariations( form.variationData, attributes.data );
if ( attributes.count === attributes.chosenCount ) { if ( attributes.count === attributes.chosenCount ) {
var variation = matching_variations.shift(); if ( form.useAjax ) {
if ( form.xhr ) {
if ( variation ) { form.xhr.abort();
form.$form.trigger( 'found_variation', [ variation ] ); }
form.$form.block( { message: null, overlayCSS: { background: '#fff', opacity: 0.6 } } );
currentAttributes.product_id = parseInt( form.$form.data( 'product_id' ), 10 );
currentAttributes.custom_data = form.$form.data( 'custom_data' );
form.xhr = $.ajax( {
url: wc_cart_fragments_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'get_variation' ),
type: 'POST',
data: currentAttributes,
success: function( variation ) {
if ( variation ) {
form.$form.trigger( 'found_variation', [ variation ] );
} else {
form.$form.trigger( 'reset_data' );
form.$form.find( '.single_variation' ).after( '<p class="wc-no-matching-variations woocommerce-info">' + wc_add_to_cart_variation_params.i18n_no_matching_variations_text + '</p>' );
form.$form.find( '.wc-no-matching-variations' ).slideDown( 200 );
}
},
complete: function() {
form.$form.unblock();
}
} );
} else { } else {
window.alert( wc_add_to_cart_variation_params.i18n_no_matching_variations_text ); form.$form.trigger( 'update_variation_values' );
form.$form.trigger( 'reset_data' );
var matching_variations = form.findMatchingVariations( form.variationData, currentAttributes ),
variation = matching_variations.shift();
if ( variation ) {
form.$form.trigger( 'found_variation', [ variation ] );
} else {
window.alert( wc_add_to_cart_variation_params.i18n_no_matching_variations_text );
form.$form.trigger( 'reset_data' );
}
} }
} else { } else {
form.$form.trigger( 'reset_data' ); form.$form.trigger( 'reset_data' );
form.$singleVariation.slideUp( 200 ).trigger( 'hide_variation' );
} }
// Show reset link.
form.toggleResetLink( attributes.chosenCount > 0 );
// added to get around variation image flicker issue // added to get around variation image flicker issue
$( '.product.has-default-attributes > .images' ).fadeTo( 200, 1 ); $( '.product.has-default-attributes > .images' ).fadeTo( 200, 1 );
form.toggleResetLink( attributes.chosenCount > 0 );
}; };
/** /**
@ -237,46 +259,13 @@
form.$form.find( '.wc-no-matching-variations' ).remove(); form.$form.find( '.wc-no-matching-variations' ).remove();
if ( form.useAjax ) { if ( form.useAjax ) {
if ( form.xhr ) { form.$form.trigger( 'check_variations' );
form.xhr.abort();
}
var attributes = form.getChosenAttributes(),
data = attributes.data;
if ( attributes.count === attributes.chosenCount ) {
form.$form.block( { message: null, overlayCSS: { background: '#fff', opacity: 0.6 } } );
data.product_id = parseInt( form.$form.data( 'product_id' ), 10 );
data.custom_data = form.$form.data( 'custom_data' );
form.xhr = $.ajax( {
url: wc_cart_fragments_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'get_variation' ),
type: 'POST',
data: data,
success: function( variation ) {
if ( variation ) {
form.$form.trigger( 'found_variation', [ variation ] );
} else {
form.$form.trigger( 'reset_data' );
form.$form.find( '.single_variation' ).after( '<p class="wc-no-matching-variations woocommerce-info">' + wc_add_to_cart_variation_params.i18n_no_matching_variations_text + '</p>' );
form.$form.find( '.wc-no-matching-variations' ).slideDown( 200 );
}
},
complete: function() {
form.$form.unblock();
}
} );
} else {
form.$form.trigger( 'reset_data' );
}
form.toggleResetLink( attributes.chosenCount > 0 );
} else { } else {
form.$form.trigger( 'woocommerce_variation_select_change' ); form.$form.trigger( 'woocommerce_variation_select_change' );
form.$form.trigger( 'check_variations' ); form.$form.trigger( 'check_variations' );
$( this ).blur(); $( this ).blur();
} }
// added to get around variation image flicker issue
$( '.product.has-default-attributes > .images' ).fadeTo( 200, 1 );
// Custom event for when variation selection has been changed // Custom event for when variation selection has been changed
form.$form.trigger( 'woocommerce_variation_has_changed' ); form.$form.trigger( 'woocommerce_variation_has_changed' );
}; };
@ -391,7 +380,7 @@
current_attr_select.html( new_attr_select.html() ); current_attr_select.html( new_attr_select.html() );
current_attr_select.find( 'option' + option_gt_filter + ':not(.enabled)' ).prop( 'disabled', true ); current_attr_select.find( 'option' + option_gt_filter + ':not(.enabled)' ).prop( 'disabled', true );
// Choose selected. // Choose selected value.
if ( selected_attr_val ) { if ( selected_attr_val ) {
// If the previously selected value is no longer available, fall back to the placeholder (it's going to be there). // If the previously selected value is no longer available, fall back to the placeholder (it's going to be there).
if ( selected_attr_val_valid ) { if ( selected_attr_val_valid ) {
@ -399,6 +388,8 @@
} else { } else {
current_attr_select.val( '' ).change(); current_attr_select.val( '' ).change();
} }
} else {
current_attr_select.val( '' ); // No change event to prevent infinite loop.
} }
}); });
@ -535,10 +526,11 @@
*/ */
$.fn.wc_variations_image_update = function( variation ) { $.fn.wc_variations_image_update = function( variation ) {
var $form = this, var $form = this,
$product = $form.closest('.product'), $product = $form.closest( '.product' ),
$gallery_img = $product.find( '.flex-control-nav li:eq(0) img' ), $gallery_img = $product.find( '.flex-control-nav li:eq(0) img' ),
$product_img_wrap = $product.find( '.woocommerce-product-gallery__wrapper .woocommerce-product-gallery__image:eq(0)' ), $gallery_wrapper = $product.find( '.woocommerce-product-gallery__wrapper ' ),
$product_img = $product.find( '.woocommerce-product-gallery__wrapper .woocommerce-product-gallery__image:eq(0) .wp-post-image' ); $product_img_wrap = $gallery_wrapper.find( '.woocommerce-product-gallery__image, .woocommerce-product-gallery__image--placeholder' ).eq( 0 ),
$product_img = $product_img_wrap.find( '.wp-post-image' );
if ( variation && variation.image && variation.image.src && variation.image.src.length > 1 ) { if ( variation && variation.image && variation.image.src && variation.image.src.length > 1 ) {
$product_img.wc_set_variation_attr( 'src', variation.image.src ); $product_img.wc_set_variation_attr( 'src', variation.image.src );

File diff suppressed because one or more lines are too long

View File

@ -411,7 +411,9 @@ abstract class WC_Abstract_Legacy_Order extends WC_Data {
return $this->get_id(); return $this->get_id();
} elseif ( 'post' === $key ) { } elseif ( 'post' === $key ) {
return get_post( $this->get_id() ); return get_post( $this->get_id() );
} elseif ( 'status' === $key || 'post_status' === $key ) { } elseif ( 'status' === $key ) {
return $this->get_status();
} elseif ( 'post_status' === $key ) {
return 'wc-' . $this->get_status(); return 'wc-' . $this->get_status();
} elseif ( 'customer_message' === $key || 'customer_note' === $key ) { } elseif ( 'customer_message' === $key || 'customer_note' === $key ) {
return $this->get_customer_note(); return $this->get_customer_note();

View File

@ -253,7 +253,7 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data {
* @return string * @return string
*/ */
public function get_price_html_from_text() { public function get_price_html_from_text() {
wc_deprecated_function( 'WC_Product::get_price_html_from_text', '2.7', 'wc_get_price_from_prefix' ); wc_deprecated_function( 'WC_Product::get_price_html_from_text', '2.7', 'wc_get_price_html_from_text' );
return wc_get_price_html_from_text(); return wc_get_price_html_from_text();
} }

View File

@ -592,7 +592,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
*/ */
public function set_shipping_tax( $value ) { public function set_shipping_tax( $value ) {
$this->set_prop( 'shipping_tax', wc_format_decimal( $value ) ); $this->set_prop( 'shipping_tax', wc_format_decimal( $value ) );
$this->set_total_tax( $this->get_cart_tax() + $this->get_shipping_tax() ); $this->set_total_tax( (float) $this->get_cart_tax() + (float) $this->get_shipping_tax() );
} }
/** /**
@ -603,7 +603,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
*/ */
public function set_cart_tax( $value ) { public function set_cart_tax( $value ) {
$this->set_prop( 'cart_tax', wc_format_decimal( $value ) ); $this->set_prop( 'cart_tax', wc_format_decimal( $value ) );
$this->set_total_tax( $this->get_cart_tax() + $this->get_shipping_tax() ); $this->set_total_tax( (float) $this->get_cart_tax() + (float) $this->get_shipping_tax() );
} }
/** /**

View File

@ -1869,7 +1869,7 @@ class WC_Product extends WC_Abstract_Legacy_Product {
} elseif ( $this->managing_stock() && $this->is_on_backorder( 1 ) ) { } elseif ( $this->managing_stock() && $this->is_on_backorder( 1 ) ) {
$availability = __( 'Available on backorder', 'woocommerce' ); $availability = __( 'Available on backorder', 'woocommerce' );
} elseif ( $this->managing_stock() ) { } elseif ( $this->managing_stock() ) {
$availability = wc_format_stock_for_display( $this->get_stock_quantity(), $this->backorders_allowed() && $this->backorders_require_notification() ); $availability = wc_format_stock_for_display( $this );
} else { } else {
$availability = ''; $availability = '';
} }

View File

@ -285,7 +285,6 @@ class WC_Admin_Assets {
'i18n_download_permission_fail' => __( 'Could not grant access - the user may already have permission for this file or billing email is not set. Ensure the billing email is set, and the order has been saved.', 'woocommerce' ), 'i18n_download_permission_fail' => __( 'Could not grant access - the user may already have permission for this file or billing email is not set. Ensure the billing email is set, and the order has been saved.', 'woocommerce' ),
'i18n_permission_revoke' => __( 'Are you sure you want to revoke access to this download?', 'woocommerce' ), 'i18n_permission_revoke' => __( 'Are you sure you want to revoke access to this download?', 'woocommerce' ),
'i18n_tax_rate_already_exists' => __( 'You cannot add the same tax rate twice!', 'woocommerce' ), 'i18n_tax_rate_already_exists' => __( 'You cannot add the same tax rate twice!', 'woocommerce' ),
'i18n_product_type_alert' => __( 'Your product has variations! Before changing the product type, it is a good idea to delete the variations to avoid errors in the stock reports.', 'woocommerce' ),
'i18n_delete_note' => __( 'Are you sure you wish to delete this note? This action cannot be undone.', 'woocommerce' ), 'i18n_delete_note' => __( 'Are you sure you wish to delete this note? This action cannot be undone.', 'woocommerce' ),
); );

View File

@ -51,7 +51,7 @@ class WC_Admin_Attributes {
} }
if ( is_wp_error( $result ) ) { if ( is_wp_error( $result ) ) {
echo '<div id="woocommerce_errors" class="error"><p>' . $result->get_error_message() . '</p></div>'; echo '<div id="woocommerce_errors" class="error"><p>' . wp_kses_post( $result->get_error_message() ) . '</p></div>';
} }
// Show admin interface // Show admin interface

View File

@ -255,7 +255,7 @@ class WC_Admin_Duplicate_Product {
$post_terms_count = sizeof( $post_terms ); $post_terms_count = sizeof( $post_terms );
for ( $i = 0; $i < $post_terms_count; $i++ ) { for ( $i = 0; $i < $post_terms_count; $i++ ) {
wp_set_object_terms( $new_id, $post_terms[ $i ]->slug, $taxonomy, true ); wp_set_object_terms( $new_id, (int) $post_terms[ $i ]->term_id, $taxonomy, true );
} }
} }
} }

View File

@ -31,6 +31,9 @@ class WC_Admin_Post_Types {
// Disable Auto Save // Disable Auto Save
add_action( 'admin_print_scripts', array( $this, 'disable_autosave' ) ); add_action( 'admin_print_scripts', array( $this, 'disable_autosave' ) );
// Extra post data.
add_action( 'edit_form_top', array( $this, 'edit_form_top' ) );
// WP List table columns. Defined here so they are always available for events such as inline editing. // WP List table columns. Defined here so they are always available for events such as inline editing.
add_filter( 'manage_product_posts_columns', array( $this, 'product_columns' ) ); add_filter( 'manage_product_posts_columns', array( $this, 'product_columns' ) );
add_filter( 'manage_shop_coupon_posts_columns', array( $this, 'shop_coupon_columns' ) ); add_filter( 'manage_shop_coupon_posts_columns', array( $this, 'shop_coupon_columns' ) );
@ -339,32 +342,32 @@ class WC_Admin_Post_Types {
/* Custom inline data for woocommerce. */ /* Custom inline data for woocommerce. */
echo ' echo '
<div class="hidden" id="woocommerce_inline_' . $post->ID . '"> <div class="hidden" id="woocommerce_inline_' . absint( $post->ID ) . '">
<div class="menu_order">' . $the_product->get_menu_order() . '</div> <div class="menu_order">' . absint( $the_product->get_menu_order() ) . '</div>
<div class="sku">' . $the_product->get_sku() . '</div> <div class="sku">' . esc_html( $the_product->get_sku() ) . '</div>
<div class="regular_price">' . $the_product->get_regular_price() . '</div> <div class="regular_price">' . esc_html( $the_product->get_regular_price() ) . '</div>
<div class="sale_price">' . $the_product->get_sale_price() . '</div> <div class="sale_price">' . esc_html( $the_product->get_sale_price() ) . '</div>
<div class="weight">' . $the_product->get_weight() . '</div> <div class="weight">' . esc_html( $the_product->get_weight() ) . '</div>
<div class="length">' . $the_product->get_length() . '</div> <div class="length">' . esc_html( $the_product->get_length() ) . '</div>
<div class="width">' . $the_product->get_width() . '</div> <div class="width">' . esc_html( $the_product->get_width() ) . '</div>
<div class="height">' . $the_product->get_height() . '</div> <div class="height">' . esc_html( $the_product->get_height() ) . '</div>
<div class="shipping_class">' . $the_product->get_shipping_class() . '</div> <div class="shipping_class">' . esc_html( $the_product->get_shipping_class() ) . '</div>
<div class="visibility">' . $the_product->get_catalog_visibility() . '</div> <div class="visibility">' . esc_html( $the_product->get_catalog_visibility() ) . '</div>
<div class="stock_status">' . $the_product->get_stock_status() . '</div> <div class="stock_status">' . esc_html( $the_product->get_stock_status() ) . '</div>
<div class="stock">' . $the_product->get_stock_quantity() . '</div> <div class="stock">' . esc_html( $the_product->get_stock_quantity() ) . '</div>
<div class="manage_stock">' . $the_product->get_manage_stock() . '</div> <div class="manage_stock">' . esc_html( $the_product->get_manage_stock() ) . '</div>
<div class="featured">' . $the_product->get_featured() . '</div> <div class="featured">' . esc_html( $the_product->get_featured() ) . '</div>
<div class="product_type">' . $the_product->get_type() . '</div> <div class="product_type">' . esc_html( $the_product->get_type() ) . '</div>
<div class="product_is_virtual">' . $the_product->get_virtual() . '</div> <div class="product_is_virtual">' . esc_html( $the_product->get_virtual() ) . '</div>
<div class="tax_status">' . $the_product->get_tax_status() . '</div> <div class="tax_status">' . esc_html( $the_product->get_tax_status() ) . '</div>
<div class="tax_class">' . $the_product->get_tax_class() . '</div> <div class="tax_class">' . esc_html( $the_product->get_tax_class() ) . '</div>
<div class="backorders">' . $the_product->get_backorders() . '</div> <div class="backorders">' . esc_html( $the_product->get_backorders() ) . '</div>
</div> </div>
'; ';
break; break;
case 'sku' : case 'sku' :
echo $the_product->get_sku() ? $the_product->get_sku() : '<span class="na">&ndash;</span>'; echo $the_product->get_sku() ? esc_html( $the_product->get_sku() ) : '<span class="na">&ndash;</span>';
break; break;
case 'product_type' : case 'product_type' :
if ( $the_product->is_type( 'grouped' ) ) { if ( $the_product->is_type( 'grouped' ) ) {
@ -439,7 +442,7 @@ class WC_Admin_Post_Types {
* @param string $column * @param string $column
*/ */
public function render_shop_coupon_columns( $column ) { public function render_shop_coupon_columns( $column ) {
global $post, $woocommerce; global $post;
switch ( $column ) { switch ( $column ) {
case 'coupon_code' : case 'coupon_code' :
@ -509,7 +512,7 @@ class WC_Admin_Post_Types {
* @param string $column * @param string $column
*/ */
public function render_shop_order_columns( $column ) { public function render_shop_order_columns( $column ) {
global $post, $woocommerce, $the_order; global $post, $the_order;
if ( empty( $the_order ) || $the_order->get_id() != $post->ID ) { if ( empty( $the_order ) || $the_order->get_id() != $post->ID ) {
$the_order = wc_get_order( $post->ID ); $the_order = wc_get_order( $post->ID );
@ -1760,6 +1763,14 @@ class WC_Admin_Post_Types {
} }
} }
/**
* Output extra data on post forms.
* @param WP_Post $post
*/
public function edit_form_top( $post ) {
echo '<input type="hidden" id="original_post_title" name="original_post_title" value="' . esc_attr( $post->post_title ) . '" />';
}
/** /**
* Change title boxes in admin. * Change title boxes in admin.
* @param string $text * @param string $text

View File

@ -87,7 +87,7 @@ class WC_Admin_Settings {
delete_transient( 'woocommerce_cache_excluded_uris' ); delete_transient( 'woocommerce_cache_excluded_uris' );
WC()->query->init_query_vars(); WC()->query->init_query_vars();
WC()->query->add_endpoints(); WC()->query->add_endpoints();
do_action( 'woocommerce_flush_rewrite_rules' ); wp_schedule_single_event( time(), 'woocommerce_flush_rewrite_rules' );
do_action( 'woocommerce_settings_saved' ); do_action( 'woocommerce_settings_saved' );
} }

View File

@ -752,7 +752,7 @@ class WC_Admin_Setup_Wizard {
<?php if ( 'unknown' === get_option( 'woocommerce_allow_tracking', 'unknown' ) ) : ?> <?php if ( 'unknown' === get_option( 'woocommerce_allow_tracking', 'unknown' ) ) : ?>
<div class="woocommerce-message woocommerce-tracker"> <div class="woocommerce-message woocommerce-tracker">
<p><?php printf( __( 'Want to help make WooCommerce even more awesome? Allow WooThemes to collect non-sensitive diagnostic data and usage information. %1$sFind out more%2$s.', 'woocommerce' ), '<a href="https://woocommerce.com/usage-tracking/" target="_blank">', '</a>' ); ?></p> <p><?php printf( __( 'Want to help make WooCommerce even more awesome? Allow WooCommerce to collect non-sensitive diagnostic data and usage information. %1$sFind out more%2$s.', 'woocommerce' ), '<a href="https://woocommerce.com/usage-tracking/" target="_blank">', '</a>' ); ?></p>
<p class="submit"> <p class="submit">
<a class="button-primary button button-large" href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'wc_tracker_optin', 'true' ), 'wc_tracker_optin', 'wc_tracker_nonce' ) ); ?>"><?php _e( 'Allow', 'woocommerce' ); ?></a> <a class="button-primary button button-large" href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'wc_tracker_optin', 'true' ), 'wc_tracker_optin', 'wc_tracker_nonce' ) ); ?>"><?php _e( 'Allow', 'woocommerce' ); ?></a>
<a class="button-secondary button button-large skip" href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'wc_tracker_optout', 'true' ), 'wc_tracker_optout', 'wc_tracker_nonce' ) ); ?>"><?php _e( 'No thanks', 'woocommerce' ); ?></a> <a class="button-secondary button button-large skip" href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'wc_tracker_optout', 'true' ), 'wc_tracker_optout', 'wc_tracker_nonce' ) ); ?>"><?php _e( 'No thanks', 'woocommerce' ); ?></a>

View File

@ -136,10 +136,11 @@ class WC_Admin_Taxonomies {
// When an image is selected, run a callback. // When an image is selected, run a callback.
file_frame.on( 'select', function() { file_frame.on( 'select', function() {
var attachment = file_frame.state().get( 'selection' ).first().toJSON(); var attachment = file_frame.state().get( 'selection' ).first().toJSON();
var attachment_thumbnail = attachment.sizes.thumbnail || attachment.sizes.full;
jQuery( '#product_cat_thumbnail_id' ).val( attachment.id ); jQuery( '#product_cat_thumbnail_id' ).val( attachment.id );
jQuery( '#product_cat_thumbnail' ).find( 'img' ).attr( 'src', attachment.sizes.thumbnail.url ); jQuery( '#product_cat_thumbnail' ).find( 'img' ).attr( 'src', attachment_thumbnail.url );
jQuery( '.remove_image_button' ).show(); jQuery( '.remove_image_button' ).show();
}); });
@ -245,10 +246,11 @@ class WC_Admin_Taxonomies {
// When an image is selected, run a callback. // When an image is selected, run a callback.
file_frame.on( 'select', function() { file_frame.on( 'select', function() {
var attachment = file_frame.state().get( 'selection' ).first().toJSON(); var attachment = file_frame.state().get( 'selection' ).first().toJSON();
var attachment_thumbnail = attachment.sizes.thumbnail || attachment.sizes.full;
jQuery( '#product_cat_thumbnail_id' ).val( attachment.id ); jQuery( '#product_cat_thumbnail_id' ).val( attachment.id );
jQuery( '#product_cat_thumbnail' ).find( 'img' ).attr( 'src', attachment.sizes.thumbnail.url ); jQuery( '#product_cat_thumbnail' ).find( 'img' ).attr( 'src', attachment_thumbnail.url );
jQuery( '.remove_image_button' ).show(); jQuery( '.remove_image_button' ).show();
}); });

View File

@ -207,22 +207,15 @@ class WC_Admin {
*/ */
public function admin_footer_text( $footer_text ) { public function admin_footer_text( $footer_text ) {
if ( ! current_user_can( 'manage_woocommerce' ) || ! function_exists( 'wc_get_screen_ids' ) ) { if ( ! current_user_can( 'manage_woocommerce' ) || ! function_exists( 'wc_get_screen_ids' ) ) {
return; return $footer_text;
} }
$current_screen = get_current_screen(); $current_screen = get_current_screen();
$wc_pages = wc_get_screen_ids(); $wc_pages = wc_get_screen_ids();
// Set only wc pages // Set only WC pages.
$wc_pages = array_flip( $wc_pages ); $wc_pages = array_diff( $wc_pages, array( 'profile', 'user-edit' ) );
if ( isset( $wc_pages['profile'] ) ) {
unset( $wc_pages['profile'] );
}
if ( isset( $wc_pages['user-edit'] ) ) {
unset( $wc_pages['user-edit'] );
}
$wc_pages = array_flip( $wc_pages );
// Check to make sure we're on a WooCommerce admin page // Check to make sure we're on a WooCommerce admin page.
if ( isset( $current_screen->id ) && apply_filters( 'woocommerce_display_admin_footer_text', in_array( $current_screen->id, $wc_pages ) ) ) { if ( isset( $current_screen->id ) && apply_filters( 'woocommerce_display_admin_footer_text', in_array( $current_screen->id, $wc_pages ) ) ) {
// Change the footer text // Change the footer text
if ( ! get_option( 'woocommerce_admin_footer_text_rated' ) ) { if ( ! get_option( 'woocommerce_admin_footer_text_rated' ) ) {

View File

@ -277,8 +277,8 @@ class WC_Meta_Box_Coupon_Data {
'amount' => wc_format_decimal( $_POST['coupon_amount'] ), 'amount' => wc_format_decimal( $_POST['coupon_amount'] ),
'date_expires' => wc_clean( $_POST['expiry_date'] ), 'date_expires' => wc_clean( $_POST['expiry_date'] ),
'individual_use' => isset( $_POST['individual_use'] ), 'individual_use' => isset( $_POST['individual_use'] ),
'product_ids' => array_filter( array_map( 'intval', (array) $_POST['product_ids'] ) ), 'product_ids' => isset( $_POST['product_ids'] ) ? array_filter( array_map( 'intval', (array) $_POST['product_ids'] ) ) : array(),
'excluded_product_ids' => array_filter( array_map( 'intval', (array) $_POST['exclude_product_ids'] ) ), 'excluded_product_ids' => isset( $_POST['exclude_product_ids'] ) ? array_filter( array_map( 'intval', (array) $_POST['exclude_product_ids'] ) ) : array(),
'usage_limit' => absint( $_POST['usage_limit'] ), 'usage_limit' => absint( $_POST['usage_limit'] ),
'usage_limit_per_user' => absint( $_POST['usage_limit_per_user'] ), 'usage_limit_per_user' => absint( $_POST['usage_limit_per_user'] ),
'limit_usage_to_x_items' => absint( $_POST['limit_usage_to_x_items'] ), 'limit_usage_to_x_items' => absint( $_POST['limit_usage_to_x_items'] ),

View File

@ -254,15 +254,10 @@ class WC_Meta_Box_Product_Data {
public static function save( $post_id, $post ) { public static function save( $post_id, $post ) {
// Process product type first so we have the correct class to run setters. // Process product type first so we have the correct class to run setters.
$product_type = empty( $_POST['product-type'] ) ? 'simple' : sanitize_title( stripslashes( $_POST['product-type'] ) ); $product_type = empty( $_POST['product-type'] ) ? 'simple' : sanitize_title( stripslashes( $_POST['product-type'] ) );
$classname = WC_Product_Factory::get_classname_from_product_type( $product_type ); $classname = WC_Product_Factory::get_product_classname( $post_id, $product_type );
$product = new $classname( $post_id );
if ( ! class_exists( $classname ) ) { $attributes = self::prepare_attributes();
$classname = 'WC_Product_Simple'; $errors = $product->set_props( array(
}
$product = new $classname( $post_id );
$attributes = self::prepare_attributes();
$errors = $product->set_props( array(
'sku' => isset( $_POST['_sku'] ) ? wc_clean( $_POST['_sku'] ) : null, 'sku' => isset( $_POST['_sku'] ) ? wc_clean( $_POST['_sku'] ) : null,
'purchase_note' => wp_kses_post( stripslashes( $_POST['_purchase_note'] ) ), 'purchase_note' => wp_kses_post( stripslashes( $_POST['_purchase_note'] ) ),
'downloadable' => isset( $_POST['_downloadable'] ), 'downloadable' => isset( $_POST['_downloadable'] ),
@ -277,8 +272,8 @@ class WC_Meta_Box_Product_Data {
'height' => wc_clean( $_POST['_height'] ), 'height' => wc_clean( $_POST['_height'] ),
'shipping_class_id' => absint( $_POST['product_shipping_class'] ), 'shipping_class_id' => absint( $_POST['product_shipping_class'] ),
'sold_individually' => ! empty( $_POST['_sold_individually'] ), 'sold_individually' => ! empty( $_POST['_sold_individually'] ),
'upsell_ids' => array_map( 'intval', (array) $_POST['upsell_ids'] ), 'upsell_ids' => isset( $_POST['upsell_ids'] ) ? array_map( 'intval', (array) $_POST['upsell_ids'] ) : array(),
'cross_sell_ids' => array_map( 'intval', (array) $_POST['crosssell_ids'] ), 'cross_sell_ids' => isset( $_POST['crosssell_ids'] ) ? array_map( 'intval', (array) $_POST['crosssell_ids'] ) : array(),
'regular_price' => wc_clean( $_POST['_regular_price'] ), 'regular_price' => wc_clean( $_POST['_regular_price'] ),
'sale_price' => wc_clean( $_POST['_sale_price'] ), 'sale_price' => wc_clean( $_POST['_sale_price'] ),
'date_on_sale_from' => wc_clean( $_POST['_sale_price_dates_from'] ), 'date_on_sale_from' => wc_clean( $_POST['_sale_price_dates_from'] ),
@ -308,6 +303,10 @@ class WC_Meta_Box_Product_Data {
$product->save(); $product->save();
if ( $product->is_type( 'variable' ) ) {
$product->get_data_store()->sync_variation_names( $product, wc_clean( $_POST['original_post_title'] ), wc_clean( $_POST['post_title'] ) );
}
do_action( 'woocommerce_process_product_meta_' . $product_type, $post_id ); do_action( 'woocommerce_process_product_meta_' . $product_type, $post_id );
} }

View File

@ -8,9 +8,16 @@ if ( ! defined( 'ABSPATH' ) ) {
<h3 class="fixed"> <h3 class="fixed">
<button type="button" data-permission_id="<?php echo esc_attr( $download->get_id() ); ?>" rel="<?php echo esc_attr( $download->get_product_id() ) . ',' . esc_attr( $download->get_download_id() ); ?>" class="revoke_access button"><?php _e( 'Revoke access', 'woocommerce' ); ?></button> <button type="button" data-permission_id="<?php echo esc_attr( $download->get_id() ); ?>" rel="<?php echo esc_attr( $download->get_product_id() ) . ',' . esc_attr( $download->get_download_id() ); ?>" class="revoke_access button"><?php _e( 'Revoke access', 'woocommerce' ); ?></button>
<div class="handlediv" aria-label="<?php esc_attr_e( 'Click to toggle', 'woocommerce' ); ?>"></div> <div class="handlediv" aria-label="<?php esc_attr_e( 'Click to toggle', 'woocommerce' ); ?>"></div>
<strong> <strong><?php
<?php echo '#' . esc_html( $product->get_id() ) . ' &mdash; ' . apply_filters( 'woocommerce_admin_download_permissions_title', $product->get_name(), $download->get_product_id(), $download->get_order_id(), $download->get_order_key(), $download->get_download_id() ) . ' &mdash; ' . esc_html( $file_count ) . ': ' . wc_get_filename_from_url( $product->get_file_download_path( $download->get_download_id() ) ) . ' &mdash; ' . sprintf( _n( 'Downloaded %s time', 'Downloaded %s times', $download->get_download_count(), 'woocommerce' ), $download->get_download_count() ); ?> printf(
</strong> '#%s &mdash; %s &mdash; %s: %s &mdash; ',
esc_html( $product->get_id() ),
esc_html( apply_filters( 'woocommerce_admin_download_permissions_title', $product->get_name(), $download->get_product_id(), $download->get_order_id(), $download->get_order_key(), $download->get_download_id() ) ),
esc_html( $file_count ),
esc_html( wc_get_filename_from_url( $product->get_file_download_path( $download->get_download_id() ) ) )
);
printf( _n( 'Downloaded %s time', 'Downloaded %s times', $download->get_download_count(), 'woocommerce' ), esc_html( $download->get_download_count() ) )
?></strong>
</h3> </h3>
<table cellpadding="0" cellspacing="0" class="wc-metabox-content"> <table cellpadding="0" cellspacing="0" class="wc-metabox-content">
<tbody> <tbody>

View File

@ -220,10 +220,9 @@ class WC_Admin_Report {
} }
if ( $filter_range ) { if ( $filter_range ) {
$query['where'] .= " $query['where'] .= "
AND posts.post_date >= '" . date( 'Y-m-d', $this->start_date ) . "' AND posts.post_date >= '" . date( 'Y-m-d H:i:s', $this->start_date ) . "'
AND posts.post_date < '" . date( 'Y-m-d', strtotime( '+1 DAY', $this->end_date ) ) . "' AND posts.post_date < '" . date( 'Y-m-d H:i:s', strtotime( '+1 DAY', $this->end_date ) ) . "'
"; ";
} }

View File

@ -188,7 +188,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
} }
} }
echo '<p>' . ' <strong>' . implode( ', ', $this->product_ids_titles ) . '</strong></p>'; echo '<p>' . ' <strong>' . esc_html( implode( ', ', $this->product_ids_titles ) ) . '</strong></p>';
echo '<p><a class="button" href="' . esc_url( remove_query_arg( 'product_ids' ) ) . '">' . __( 'Reset', 'woocommerce' ) . '</a></p>'; echo '<p><a class="button" href="' . esc_url( remove_query_arg( 'product_ids' ) ) . '">' . __( 'Reset', 'woocommerce' ) . '</a></p>';
} }

View File

@ -84,21 +84,14 @@ class WC_Report_Stock extends WP_List_Table {
case 'product' : case 'product' :
if ( $sku = $product->get_sku() ) { if ( $sku = $product->get_sku() ) {
echo $sku . ' - '; echo esc_html( $sku ) . ' - ';
} }
echo $product->get_name(); echo esc_html( $product->get_name() );
// Get variation data // Get variation data.
if ( $product->is_type( 'variation' ) ) { if ( $product->is_type( 'variation' ) ) {
$list_attributes = array(); echo '<div class="description">' . wc_get_formatted_variation( $product, true ) . '</div>';
$attributes = $product->get_variation_attributes();
foreach ( $attributes as $name => $attribute ) {
$list_attributes[] = wc_attribute_label( str_replace( 'attribute_', '', $name ) ) . ': <strong>' . $attribute . '</strong>';
}
echo '<div class="description">' . implode( ', ', $list_attributes ) . '</div>';
} }
break; break;
@ -120,7 +113,7 @@ class WC_Report_Stock extends WP_List_Table {
break; break;
case 'stock_level' : case 'stock_level' :
echo $product->get_stock_quantity(); echo esc_html( $product->get_stock_quantity() );
break; break;
case 'wc_actions' : case 'wc_actions' :

View File

@ -17,8 +17,8 @@ if ( ! defined( 'ABSPATH' ) ) {
/* translators: 1: start date 2: end date */ /* translators: 1: start date 2: end date */
printf( printf(
esc_html__( 'From %1$s to %2$s', 'woocommerce' ), esc_html__( 'From %1$s to %2$s', 'woocommerce' ),
wc_clean( $_GET['start_date'] ), esc_html( wc_clean( $_GET['start_date'] ) ),
wc_clean( $_GET['end_date'] ) esc_html( wc_clean( $_GET['end_date'] ) )
); );
?></h3> ?></h3>
<?php else : ?> <?php else : ?>

View File

@ -109,6 +109,8 @@ function woocommerce_wp_textarea_input( $field ) {
$field['value'] = isset( $field['value'] ) ? $field['value'] : get_post_meta( $thepostid, $field['id'], true ); $field['value'] = isset( $field['value'] ) ? $field['value'] : get_post_meta( $thepostid, $field['id'], true );
$field['desc_tip'] = isset( $field['desc_tip'] ) ? $field['desc_tip'] : false; $field['desc_tip'] = isset( $field['desc_tip'] ) ? $field['desc_tip'] : false;
$field['name'] = isset( $field['name'] ) ? $field['name'] : $field['id']; $field['name'] = isset( $field['name'] ) ? $field['name'] : $field['id'];
$field['rows'] = isset( $field['rows'] ) ? $field['rows'] : 2;
$field['cols'] = isset( $field['cols'] ) ? $field['cols'] : 20;
// Custom attribute handling // Custom attribute handling
$custom_attributes = array(); $custom_attributes = array();
@ -127,7 +129,7 @@ function woocommerce_wp_textarea_input( $field ) {
echo wc_help_tip( $field['description'] ); echo wc_help_tip( $field['description'] );
} }
echo '<textarea class="' . esc_attr( $field['class'] ) . '" style="' . esc_attr( $field['style'] ) . '" name="' . esc_attr( $field['name'] ) . '" id="' . esc_attr( $field['id'] ) . '" placeholder="' . esc_attr( $field['placeholder'] ) . '" rows="2" cols="20" ' . implode( ' ', $custom_attributes ) . '>' . esc_textarea( $field['value'] ) . '</textarea> '; echo '<textarea class="' . esc_attr( $field['class'] ) . '" style="' . esc_attr( $field['style'] ) . '" name="' . esc_attr( $field['name'] ) . '" id="' . esc_attr( $field['id'] ) . '" placeholder="' . esc_attr( $field['placeholder'] ) . '" rows="' . esc_attr( $field['rows'] ) . '" cols="' . esc_attr( $field['cols'] ) . '" ' . implode( ' ', $custom_attributes ) . '>' . esc_textarea( $field['value'] ) . '</textarea> ';
if ( ! empty( $field['description'] ) && false === $field['desc_tip'] ) { if ( ! empty( $field['description'] ) && false === $field['desc_tip'] ) {
echo '<span class="description">' . wp_kses_post( $field['description'] ) . '</span>'; echo '<span class="description">' . wp_kses_post( $field['description'] ) . '</span>';

View File

@ -142,8 +142,14 @@ class WC_REST_Coupons_Controller extends WC_REST_Posts_Controller {
public function prepare_item_for_response( $post, $request ) { public function prepare_item_for_response( $post, $request ) {
$coupon = new WC_Coupon( (int) $post->ID ); $coupon = new WC_Coupon( (int) $post->ID );
$data = $coupon->get_data(); $data = $coupon->get_data();
// The API returns 'expiry_date' and 'exclude_product_ids' instead of date_expires and 'excluded_product_ids'.
$data['expiry_date'] = $data['date_expires'];
$data['exclude_product_ids'] = $data['excluded_product_ids'];
unset( $data['excluded_product_ids'], $data['date_expires'] );
$format_decimal = array( 'amount', 'minimum_amount', 'maximum_amount' ); $format_decimal = array( 'amount', 'minimum_amount', 'maximum_amount' );
$format_date = array( 'date_created', 'date_modified', 'date_expires' ); $format_date = array( 'date_created', 'date_modified', 'expiry_date' );
$format_null = array( 'usage_limit', 'usage_limit_per_user', 'limit_usage_to_x_items' ); $format_null = array( 'usage_limit', 'usage_limit_per_user', 'limit_usage_to_x_items' );
// Format decimal values. // Format decimal values.
@ -415,7 +421,7 @@ class WC_REST_Coupons_Controller extends WC_REST_Posts_Controller {
'type' => 'string', 'type' => 'string',
'context' => array( 'view', 'edit' ), 'context' => array( 'view', 'edit' ),
), ),
'date_expires' => array( 'expiry_date' => array(
'description' => __( 'UTC DateTime when the coupon expires.', 'woocommerce' ), 'description' => __( 'UTC DateTime when the coupon expires.', 'woocommerce' ),
'type' => 'string', 'type' => 'string',
'context' => array( 'view', 'edit' ), 'context' => array( 'view', 'edit' ),
@ -437,7 +443,7 @@ class WC_REST_Coupons_Controller extends WC_REST_Posts_Controller {
'type' => 'array', 'type' => 'array',
'context' => array( 'view', 'edit' ), 'context' => array( 'view', 'edit' ),
), ),
'excluded_product_ids' => array( 'exclude_product_ids' => array(
'description' => __( "List of product ID's the coupon cannot be used on.", 'woocommerce' ), 'description' => __( "List of product ID's the coupon cannot be used on.", 'woocommerce' ),
'type' => 'array', 'type' => 'array',
'context' => array( 'view', 'edit' ), 'context' => array( 'view', 'edit' ),

View File

@ -823,7 +823,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller {
* @param array $images Images data. * @param array $images Images data.
* @return WC_Product * @return WC_Product
*/ */
protected function save_product_images( $product, $images ) { protected function set_product_images( $product, $images ) {
if ( is_array( $images ) ) { if ( is_array( $images ) ) {
$gallery = array(); $gallery = array();
@ -1042,7 +1042,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller {
* @param WP_REST_Request $request Request data. * @param WP_REST_Request $request Request data.
* @return WC_Product * @return WC_Product
*/ */
protected function save_product_meta( $product, $request ) { protected function set_product_meta( $product, $request ) {
global $wpdb; global $wpdb;
// Virtual. // Virtual.
@ -1321,6 +1321,8 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller {
$product = $this->save_default_attributes( $product, $request ); $product = $this->save_default_attributes( $product, $request );
} }
$product->save();
return $product; return $product;
} }
@ -1377,7 +1379,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller {
$image['position'] = 0; $image['position'] = 0;
} }
$variation = $this->save_product_images( $variation, array( $image ) ); $variation = $this->set_product_images( $variation, array( $image ) );
} }
// Virtual variation. // Virtual variation.
@ -1526,23 +1528,7 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller {
* @return bool|WP_Error * @return bool|WP_Error
*/ */
protected function add_post_meta_fields( $post, $request ) { protected function add_post_meta_fields( $post, $request ) {
$product = wc_get_product( $post ); return $this->update_post_meta_fields( $post, $request );
// Check for featured/gallery images, upload it and set it.
if ( isset( $request['images'] ) ) {
$product = $this->save_product_images( $product, $request['images'] );
}
// Save product meta fields.
$product = $this->save_product_meta( $product, $request );
$product->save();
// Save variations.
if ( isset( $request['type'] ) && 'variable' === $request['type'] && isset( $request['variations'] ) && is_array( $request['variations'] ) ) {
$this->save_variations_data( $product, $request );
}
return true;
} }
/** /**
@ -1557,23 +1543,25 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller {
// Check for featured/gallery images, upload it and set it. // Check for featured/gallery images, upload it and set it.
if ( isset( $request['images'] ) ) { if ( isset( $request['images'] ) ) {
$product = $this->save_product_images( $product, $request['images'] ); $product = $this->set_product_images( $product, $request['images'] );
} }
// Save product meta fields. // Save product meta fields.
$product = $this->save_product_meta( $product, $request ); $product = $this->set_product_meta( $product, $request );
// Save the product data.
$product->save();
// Save variations. // Save variations.
if ( $product->is_type( 'variable' ) ) { if ( $product->is_type( 'variable' ) ) {
if ( isset( $request['variations'] ) && is_array( $request['variations'] ) ) { if ( isset( $request['variations'] ) && is_array( $request['variations'] ) ) {
$this->save_variations_data( $product, $request ); $this->save_variations_data( $product, $request );
} else {
// Just sync variations.
$product = WC_Product_Variable::sync( $product, false );
} }
} }
$product->save(); // Clear caches here so in sync with any new variations/children.
wc_delete_product_transients( $product->get_id() );
wp_cache_delete( 'product-' . $product->get_id(), 'products' );
return true; return true;
} }

View File

@ -83,6 +83,8 @@ class WC_REST_System_Status_Controller extends WC_REST_Controller {
$response[ $section ] = $values; $response[ $section ] = $values;
} }
$response = $this->prepare_item_for_response( $response, $request );
return rest_ensure_response( $response ); return rest_ensure_response( $response );
} }
@ -848,4 +850,27 @@ class WC_REST_System_Status_Controller extends WC_REST_Controller {
'context' => $this->get_context_param( array( 'default' => 'view' ) ), 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
); );
} }
/**
* Prepare the system status response
*
* @param array $system_status
* @param WP_REST_Request $request Request object.
* @return WP_REST_Response $response Response data.
*/
public function prepare_item_for_response( $system_status, $request ) {
$data = $this->add_additional_fields_to_object( $system_status, $request );
$data = $this->filter_response_by_context( $data, 'view' );
$response = rest_ensure_response( $data );
/**
* Filter the system status returned from the REST API.
*
* @param WP_REST_Response $response The response object.
* @param mixed $system_status System status
* @param WP_REST_Request $request Request object.
*/
return apply_filters( 'woocommerce_rest_prepare_system_status', $response, $system_status, $request );
}
} }

View File

@ -442,7 +442,7 @@ class WC_AJAX {
if ( wc_is_order_status( 'wc-' . $status ) && $order ) { if ( wc_is_order_status( 'wc-' . $status ) && $order ) {
$order->update_status( $status, '', true ); $order->update_status( $status, '', true );
do_action( 'woocommerce_order_edit_status', $order_id, $status ); do_action( 'woocommerce_order_edit_status', $order->get_id(), $status );
} }
} }
@ -523,7 +523,7 @@ class WC_AJAX {
foreach ( $variation_ids as $variation_id ) { foreach ( $variation_ids as $variation_id ) {
if ( 'product_variation' === get_post_type( $variation_id ) ) { if ( 'product_variation' === get_post_type( $variation_id ) ) {
$variation = wc_get_product( $variation_id ); $variation = wc_get_product( $variation_id );
$variation->delete(); $variation->delete( true );
} }
} }
} }
@ -542,9 +542,12 @@ class WC_AJAX {
} }
parse_str( $_POST['data'], $data ); parse_str( $_POST['data'], $data );
$post_id = absint( $_POST['post_id'] );
$product = wc_get_product( $post_id ); $attributes = WC_Meta_Box_Product_Data::prepare_attributes( $data );
$attributes = WC_Meta_Box_Product_Data::prepare_attributes( $data ); $product_id = absint( $_POST['post_id'] );
$product_type = ! empty( $_POST['product_type'] ) ? wc_clean( $_POST['product_type'] ) : 'simple';
$classname = WC_Product_Factory::get_product_classname( $product_id, $product_type );
$product = new $classname( $product_id );
$product->set_attributes( $attributes ); $product->set_attributes( $attributes );
$product->save(); $product->save();
@ -598,43 +601,40 @@ class WC_AJAX {
$variations = array(); $variations = array();
$product = wc_get_product( $post_id ); $product = wc_get_product( $post_id );
$attributes = wc_list_pluck( array_filter( $product->get_attributes(), 'wc_attributes_array_filter_variation' ), 'get_slugs' );
if ( $product->is_type( 'variable' ) ) { if ( ! empty( $attributes ) ) {
$attributes = wc_list_pluck( array_filter( $product->get_attributes(), 'wc_attributes_array_filter_variation' ), 'get_slugs' ); // Get existing variations so we don't create duplicates.
$existing_variations = array_map( 'wc_get_product', $product->get_children() );
$existing_attributes = array();
if ( ! empty( $attributes ) ) { foreach ( $existing_variations as $existing_variation ) {
// Get existing variations so we don't create duplicates. $existing_attributes[] = $existing_variation->get_attributes();
$existing_variations = array_map( 'wc_get_product', $product->get_children() );
$existing_attributes = array();
foreach ( $existing_variations as $existing_variation ) {
$existing_attributes[] = $existing_variation->get_attributes();
}
$added = 0;
$possible_attributes = wc_array_cartesian( $attributes );
foreach ( $possible_attributes as $possible_attribute ) {
if ( in_array( $possible_attribute, $existing_attributes ) ) {
continue;
}
$variation = new WC_Product_Variation();
$variation->set_parent_id( $post_id );
$variation->set_attributes( $possible_attribute );
do_action( 'product_variation_linked', $variation->save() );
if ( ( $added ++ ) > WC_MAX_LINKED_VARIATIONS ) {
break;
}
}
echo $added;
} }
$data_store = $product->get_data_store(); $added = 0;
$data_store->sort_all_product_variations( $product->get_id() ); $possible_attributes = wc_array_cartesian( $attributes );
foreach ( $possible_attributes as $possible_attribute ) {
if ( in_array( $possible_attribute, $existing_attributes ) ) {
continue;
}
$variation = new WC_Product_Variation();
$variation->set_parent_id( $post_id );
$variation->set_attributes( $possible_attribute );
do_action( 'product_variation_linked', $variation->save() );
if ( ( $added ++ ) > WC_MAX_LINKED_VARIATIONS ) {
break;
}
}
echo $added;
} }
$data_store = $product->get_data_store();
$data_store->sort_all_product_variations( $product->get_id() );
die(); die();
} }
@ -1457,7 +1457,7 @@ class WC_AJAX {
if ( $refund_id && 'shop_order_refund' === get_post_type( $refund_id ) ) { if ( $refund_id && 'shop_order_refund' === get_post_type( $refund_id ) ) {
$refund = wc_get_order( $refund_id ); $refund = wc_get_order( $refund_id );
$order_id = $refund->get_parent_id(); $order_id = $refund->get_parent_id();
$refund->delete(); $refund->delete( true );
do_action( 'woocommerce_refund_deleted', $refund_id, $order_id ); do_action( 'woocommerce_refund_deleted', $refund_id, $order_id );
} }
} }
@ -1819,7 +1819,7 @@ class WC_AJAX {
if ( isset( $data['allowed'] ) && 'true' === $data['allowed'] ) { if ( isset( $data['allowed'] ) && 'true' === $data['allowed'] ) {
foreach ( $variations as $variation_id ) { foreach ( $variations as $variation_id ) {
$variation = wc_get_product( $variation_id ); $variation = wc_get_product( $variation_id );
$variation->delete(); $variation->delete( true );
} }
} }
} }

View File

@ -490,7 +490,7 @@ class WC_Cart {
*/ */
if ( ! $product->has_enough_stock( $product_qty_in_cart[ $product->get_stock_managed_by_id() ] ) ) { if ( ! $product->has_enough_stock( $product_qty_in_cart[ $product->get_stock_managed_by_id() ] ) ) {
/* translators: 1: product name 2: quantity in stock */ /* translators: 1: product name 2: quantity in stock */
$error->add( 'out-of-stock', sprintf( __( 'Sorry, we do not have enough "%1$s" in stock to fulfill your order (%2$s in stock). Please edit your cart and try again. We apologise for any inconvenience caused.', 'woocommerce' ), $product->get_name(), $product->get_stock_quantity() ) ); $error->add( 'out-of-stock', sprintf( __( 'Sorry, we do not have enough "%1$s" in stock to fulfill your order (%2$s in stock). Please edit your cart and try again. We apologise for any inconvenience caused.', 'woocommerce' ), $product->get_name(), wc_format_stock_quantity_for_display( $product->get_stock_quantity(), $product ) ) );
return $error; return $error;
} }
@ -924,7 +924,7 @@ class WC_Cart {
if ( ! $product_data->has_enough_stock( $quantity ) ) { if ( ! $product_data->has_enough_stock( $quantity ) ) {
/* translators: 1: product name 2: quantity in stock */ /* translators: 1: product name 2: quantity in stock */
throw new Exception( sprintf( __( 'You cannot add that amount of &quot;%1$s&quot; to the cart because there is not enough stock (%2$s remaining).', 'woocommerce' ), $product_data->get_name(), $product_data->get_stock_quantity() ) ); throw new Exception( sprintf( __( 'You cannot add that amount of &quot;%1$s&quot; to the cart because there is not enough stock (%2$s remaining).', 'woocommerce' ), $product_data->get_name(), wc_format_stock_quantity_for_display( $product_data->get_stock_quantity(), $product_data ) ) );
} }
// Stock check - this time accounting for whats already in-cart // Stock check - this time accounting for whats already in-cart
@ -936,7 +936,7 @@ class WC_Cart {
'<a href="%s" class="button wc-forward">%s</a> %s', '<a href="%s" class="button wc-forward">%s</a> %s',
wc_get_cart_url(), wc_get_cart_url(),
__( 'View Cart', 'woocommerce' ), __( 'View Cart', 'woocommerce' ),
sprintf( __( 'You cannot add that amount to the cart &mdash; we have %1$s in stock and you already have %2$s in your cart.', 'woocommerce' ), $product_data->get_stock_quantity(), $products_qty_in_cart[ $product_data->get_id() ] ) sprintf( __( 'You cannot add that amount to the cart &mdash; we have %1$s in stock and you already have %2$s in your cart.', 'woocommerce' ), wc_format_stock_quantity_for_display( $product_data->get_stock_quantity(), $product_data ), wc_format_stock_quantity_for_display( $products_qty_in_cart[ $product_data->get_id() ], $product_data ) )
) ); ) );
} }
} }

View File

@ -835,6 +835,13 @@ class WC_Checkout {
$customer->update_meta_data( $key, $value ); $customer->update_meta_data( $key, $value );
} }
} }
/**
* Action hook to adjust customer before save.
* @since 2.7.0
*/
do_action( 'woocommerce_checkout_update_customer', $customer, $data );
$customer->save(); $customer->save();
} }

View File

@ -360,7 +360,7 @@ class WC_Coupon extends WC_Legacy_Coupon {
$cart_item_qty = is_null( $cart_item ) ? 1 : $cart_item['quantity']; $cart_item_qty = is_null( $cart_item ) ? 1 : $cart_item['quantity'];
if ( $this->is_type( array( 'percent' ) ) ) { if ( $this->is_type( array( 'percent' ) ) ) {
$discount = $this->get_amount() * ( $discounting_amount / 100 ); $discount = (float) $this->get_amount() * ( $discounting_amount / 100 );
} elseif ( $this->is_type( 'fixed_cart' ) && ! is_null( $cart_item ) && WC()->cart->subtotal_ex_tax ) { } elseif ( $this->is_type( 'fixed_cart' ) && ! is_null( $cart_item ) && WC()->cart->subtotal_ex_tax ) {
/** /**
* This is the most complex discount - we need to divide the discount between rows based on their price in. * This is the most complex discount - we need to divide the discount between rows based on their price in.
@ -376,14 +376,14 @@ class WC_Coupon extends WC_Legacy_Coupon {
} else { } else {
$discount_percent = ( wc_get_price_excluding_tax( $cart_item['data'] ) * $cart_item_qty ) / WC()->cart->subtotal_ex_tax; $discount_percent = ( wc_get_price_excluding_tax( $cart_item['data'] ) * $cart_item_qty ) / WC()->cart->subtotal_ex_tax;
} }
$discount = ( $this->get_amount() * $discount_percent ) / $cart_item_qty; $discount = ( (float) $this->get_amount() * $discount_percent ) / $cart_item_qty;
} elseif ( $this->is_type( 'fixed_product' ) ) { } elseif ( $this->is_type( 'fixed_product' ) ) {
$discount = min( $this->get_amount(), $discounting_amount ); $discount = min( $this->get_amount(), $discounting_amount );
$discount = $single ? $discount : $discount * $cart_item_qty; $discount = $single ? $discount : $discount * $cart_item_qty;
} }
$discount = min( $discount, $discounting_amount ); $discount = (float) min( $discount, $discounting_amount );
// Handle the limit_usage_to_x_items option // Handle the limit_usage_to_x_items option
if ( ! $this->is_type( array( 'fixed_cart' ) ) ) { if ( ! $this->is_type( array( 'fixed_cart' ) ) ) {

View File

@ -135,7 +135,7 @@ class WC_Customer_Download extends WC_Data implements ArrayAccess {
* Get downloads_remaining. * Get downloads_remaining.
* *
* @param string $context * @param string $context
* @return integer * @return integer|string
*/ */
public function get_downloads_remaining( $context = 'view' ) { public function get_downloads_remaining( $context = 'view' ) {
return $this->get_prop( 'downloads_remaining', $context ); return $this->get_prop( 'downloads_remaining', $context );
@ -233,10 +233,10 @@ class WC_Customer_Download extends WC_Data implements ArrayAccess {
/** /**
* Get downloads_remaining. * Get downloads_remaining.
* *
* @param int $value * @param integer|string $value
*/ */
public function set_downloads_remaining( $value ) { public function set_downloads_remaining( $value ) {
$this->set_prop( 'downloads_remaining', absint( $value ) ); $this->set_prop( 'downloads_remaining', '' === $value ? '' : absint( $value ) );
} }
/** /**

View File

@ -98,7 +98,7 @@ class WC_Download_Handler {
* @access private * @access private
*/ */
private static function check_downloads_remaining( $download ) { private static function check_downloads_remaining( $download ) {
if ( 0 >= $download->get_downloads_remaining() ) { if ( '' !== $download->get_downloads_remaining() && 0 >= $download->get_downloads_remaining() ) {
self::download_error( __( 'Sorry, you have reached your download limit for this file', 'woocommerce' ), '', 403 ); self::download_error( __( 'Sorry, you have reached your download limit for this file', 'woocommerce' ), '', 403 );
} }
} }

View File

@ -128,7 +128,7 @@ class WC_Embed {
/* translators: %s: average rating */ /* translators: %s: average rating */
printf( printf(
esc_html_( 'Rated %s out of 5', 'woocommerce' ), esc_html_( 'Rated %s out of 5', 'woocommerce' ),
$_product->get_average_rating() esc_html( $_product->get_average_rating() )
); );
?> ?>
</div> </div>

View File

@ -357,17 +357,18 @@ class WC_Order_Item_Product extends WC_Order_Item {
$order = $this->get_order(); $order = $this->get_order();
if ( $product && $order && $product->is_downloadable() && $order->is_download_permitted() ) { if ( $product && $order && $product->is_downloadable() && $order->is_download_permitted() ) {
$data_store = WC_Data_Store::load( 'customer-download' ); $data_store = WC_Data_Store::load( 'customer-download' );
$download_ids = $data_store->get_downloads( array( $customer_downloads = $data_store->get_downloads( array(
'user_email' => $order->get_billing_email(), 'user_email' => $order->get_billing_email(),
'order_id' => $order->get_id(), 'order_id' => $order->get_id(),
'product_id' => $this->get_variation_id() ? $this->get_variation_id() : $this->get_product_id(), 'product_id' => $this->get_variation_id() ? $this->get_variation_id() : $this->get_product_id(),
'return' => 'ids',
) ); ) );
foreach ( $customer_downloads as $customer_download ) {
$download_id = $customer_download->get_download_id();
foreach ( $download_ids as $download_id ) {
if ( $product->has_file( $download_id ) ) { if ( $product->has_file( $download_id ) ) {
$files[ $download_id ] = $product->get_file( $download_id ); $file = $product->get_file( $download_id );
$files[ $download_id ] = $file->get_data();
$files[ $download_id ]['download_url'] = $this->get_item_download_url( $download_id ); $files[ $download_id ]['download_url'] = $this->get_item_download_url( $download_id );
} }
} }

View File

@ -35,6 +35,7 @@ class WC_Post_Data {
add_action( 'transition_post_status', array( __CLASS__, 'transition_post_status' ), 10, 3 ); add_action( 'transition_post_status', array( __CLASS__, 'transition_post_status' ), 10, 3 );
add_action( 'woocommerce_product_set_stock_status', array( __CLASS__, 'delete_product_query_transients' ) ); add_action( 'woocommerce_product_set_stock_status', array( __CLASS__, 'delete_product_query_transients' ) );
add_action( 'woocommerce_product_set_visibility', array( __CLASS__, 'delete_product_query_transients' ) ); add_action( 'woocommerce_product_set_visibility', array( __CLASS__, 'delete_product_query_transients' ) );
add_action( 'woocommerce_product_type_changed', array( __CLASS__, 'product_type_changed' ), 10, 3 );
add_action( 'edit_term', array( __CLASS__, 'edit_term' ), 10, 3 ); add_action( 'edit_term', array( __CLASS__, 'edit_term' ), 10, 3 );
add_action( 'edited_term', array( __CLASS__, 'edited_term' ), 10, 3 ); add_action( 'edited_term', array( __CLASS__, 'edited_term' ), 10, 3 );
@ -117,6 +118,22 @@ class WC_Post_Data {
} }
} }
/**
* Handle type changes.
*
* @since 2.7.0
* @param WC_Product $product
* @param string $from
* @param string $to
*/
public static function product_type_changed( $product, $from, $to ) {
if ( 'variable' === $from ) {
// If the product is no longer variable, we should ensure all variations are removed.
$data_store = WC_Data_Store::load( 'product-variable' );
$data_store->delete_variations( $product );
}
}
/** /**
* When editing a term, check for product attributes. * When editing a term, check for product attributes.
* @param id $term_id * @param id $term_id
@ -239,7 +256,6 @@ class WC_Post_Data {
break; break;
} }
} }
return $data; return $data;
} }
@ -249,56 +265,35 @@ class WC_Post_Data {
* @param mixed $id ID of post being deleted * @param mixed $id ID of post being deleted
*/ */
public static function delete_post( $id ) { public static function delete_post( $id ) {
global $woocommerce, $wpdb; if ( ! current_user_can( 'delete_posts' ) || ! $id ) {
if ( ! current_user_can( 'delete_posts' ) ) {
return; return;
} }
if ( $id > 0 ) { $post_type = get_post_type( $id );
$post_type = get_post_type( $id ); switch ( $post_type ) {
case 'product' :
switch ( $post_type ) { $data_store = WC_Data_Store::load( 'product-variable' );
case 'product' : $data_store->delete_variations( $id, true );
$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 );
}
}
$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 );
}
}
if ( $parent_id = wp_get_post_parent_id( $id ) ) {
wc_delete_product_transients( $parent_id );
}
if ( $parent_id = wp_get_post_parent_id( $id ) ) {
wc_delete_product_transients( $parent_id );
}
break; break;
case 'product_variation' : case 'product_variation' :
wc_delete_product_transients( wp_get_post_parent_id( $id ) ); wc_delete_product_transients( wp_get_post_parent_id( $id ) );
break; break;
case 'shop_order' : case 'shop_order' :
$refunds = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order_refund' AND post_parent = %d", $id ) ); global $wpdb;
if ( ! is_null( $refunds ) ) { $refunds = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order_refund' AND post_parent = %d", $id ) );
foreach ( $refunds as $refund ) {
wp_delete_post( $refund->ID, true ); if ( ! is_null( $refunds ) ) {
} foreach ( $refunds as $refund ) {
wp_delete_post( $refund->ID, true );
} }
}
break; break;
}
} }
} }
@ -308,22 +303,28 @@ class WC_Post_Data {
* @param mixed $id * @param mixed $id
*/ */
public static function trash_post( $id ) { public static function trash_post( $id ) {
global $wpdb; if ( ! $id ) {
return;
}
if ( $id > 0 ) { $post_type = get_post_type( $id );
$post_type = get_post_type( $id ); // If this is an order, trash any refunds too.
if ( in_array( $post_type, wc_get_order_types( 'order-count' ) ) ) {
global $wpdb;
if ( in_array( $post_type, wc_get_order_types( 'order-count' ) ) ) { $refunds = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order_refund' AND post_parent = %d", $id ) );
$refunds = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order_refund' AND post_parent = %d", $id ) );
foreach ( $refunds as $refund ) { foreach ( $refunds as $refund ) {
$wpdb->update( $wpdb->posts, array( 'post_status' => 'trash' ), array( 'ID' => $refund->ID ) ); $wpdb->update( $wpdb->posts, array( 'post_status' => 'trash' ), array( 'ID' => $refund->ID ) );
}
delete_transient( 'woocommerce_processing_order_count' );
wc_delete_shop_order_transients( $id );
} }
wc_delete_shop_order_transients( $id );
// If this is a product, trash children variations.
} elseif ( 'product' === $post_type ) {
$data_store = WC_Data_Store::load( 'product-variable' );
$data_store->delete_variations( $id, false );
} }
} }
@ -333,32 +334,28 @@ class WC_Post_Data {
* @param mixed $id * @param mixed $id
*/ */
public static function untrash_post( $id ) { public static function untrash_post( $id ) {
global $wpdb; if ( ! $id ) {
return;
}
if ( $id > 0 ) { $post_type = get_post_type( $id );
$post_type = get_post_type( $id ); if ( in_array( $post_type, wc_get_order_types( 'order-count' ) ) ) {
global $wpdb;
if ( in_array( $post_type, wc_get_order_types( 'order-count' ) ) ) { $refunds = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order_refund' AND post_parent = %d", $id ) );
$refunds = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order_refund' AND post_parent = %d", $id ) ); foreach ( $refunds as $refund ) {
$wpdb->update( $wpdb->posts, array( 'post_status' => 'wc-completed' ), array( 'ID' => $refund->ID ) );
foreach ( $refunds as $refund ) {
$wpdb->update( $wpdb->posts, array( 'post_status' => 'wc-completed' ), array( 'ID' => $refund->ID ) );
}
delete_transient( 'woocommerce_processing_order_count' );
wc_delete_shop_order_transients( $id );
} elseif ( 'product' === $post_type ) {
// Check if SKU is valid before untrash the product.
$sku = get_post_meta( $id, '_sku', true );
if ( ! empty( $sku ) ) {
if ( ! wc_product_has_unique_sku( $id, $sku ) ) {
update_post_meta( $id, '_sku', '' );
}
}
} }
wc_delete_shop_order_transients( $id );
} elseif ( 'product' === $post_type ) {
$data_store = WC_Data_Store::load( 'product-variable' );
$data_store->untrash_variations( $id );
wc_product_force_unique_sku( $id );
} }
} }

View File

@ -21,35 +21,33 @@ class WC_Product_Factory {
* Get a product. * Get a product.
* *
* @param mixed $product_id (default: false) * @param mixed $product_id (default: false)
* @param array $deprecated * @param array $deprecated Previously used to pass arguments to the factory, e.g. to force a type.
* @return WC_Product|bool Product object or null if the product cannot be loaded. * @return WC_Product|bool Product object or null if the product cannot be loaded.
*/ */
public function get_product( $product_id = false, $deprecated = array() ) { public function get_product( $product_id = false, $deprecated = array() ) {
$product_id = $this->get_product_id( $product_id ); if ( ! $product_id = $this->get_product_id( $product_id ) ) {
if ( ! $product_id ) {
return false; return false;
} }
$product_type = $this->get_product_type( $product_id ); $product_type = $this->get_product_type( $product_id );
$classname = $this->get_classname_from_product_type( $product_type );
// backwards compat filter // Backwards compatibility.
$post_type = 'variation' === $product_type ? 'product_variation' : 'product'; if ( ! empty( $deprecated ) ) {
$classname = apply_filters( 'woocommerce_product_class', $classname, $product_type, $post_type, $product_id ); wc_deprecated_argument( 'args', '2.7', 'Passing args to the product factory is deprecated. If you need to force a type, construct the product class directly.' );
if ( ! $classname ) { if ( isset( $deprecated['product_type'] ) ) {
return false; $product_type = $this->get_classname_from_product_type( $deprecated['product_type'] );
}
} }
if ( ! class_exists( $classname ) ) { $classname = $this->get_product_classname( $product_id, $product_type );
$classname = 'WC_Product_Simple';
}
try { try {
// Try to get from cache, otherwise create a new object, // Try to get from cache, otherwise create a new object,
$product = wp_cache_get( 'product-' . $product_id, 'products' ); $product = wp_cache_get( 'product-' . $product_id, 'products' );
if ( ! is_a( $product, 'WC_Product' ) ) { if ( ! is_a( $product, 'WC_Product' ) ) {
$product = new $classname( $product_id ); $product = new $classname( $product_id, $deprecated );
wp_cache_set( 'product-' . $product_id, $product, 'products' ); wp_cache_set( 'product-' . $product_id, $product, 'products' );
} }
@ -59,6 +57,24 @@ class WC_Product_Factory {
} }
} }
/**
* Gets a product classname and allows filtering. Returns WC_Product_Simple if the class does not exist.
*
* @since 2.7.0
* @param int $product_id
* @param string $product_type
* @return string
*/
public static function get_product_classname( $product_id, $product_type ) {
$classname = apply_filters( 'woocommerce_product_class', self::get_classname_from_product_type( $product_type ), $product_type, 'variation' === $product_type ? 'product_variation' : 'product', $product_id );
if ( ! $classname || ! class_exists( $classname ) ) {
$classname = 'WC_Product_Simple';
}
return $classname;
}
/** /**
* Get the product type for a product. * Get the product type for a product.
* *
@ -87,6 +103,7 @@ class WC_Product_Factory {
/** /**
* Create a WC coding standards compliant class name e.g. WC_Product_Type_Class instead of WC_Product_type-class. * Create a WC coding standards compliant class name e.g. WC_Product_Type_Class instead of WC_Product_type-class.
*
* @param string $product_type * @param string $product_type
* @return string|false * @return string|false
*/ */

View File

@ -16,27 +16,25 @@ if ( ! defined( 'ABSPATH' ) ) {
class WC_Product_Variable extends WC_Product { class WC_Product_Variable extends WC_Product {
/** /**
* Stores product data. * Array of children variation IDs. Determined by children.
* *
* @var array * @var array
*/ */
protected $extra_data = array( protected $children = array();
'children' => array(),
'visible_children' => array(),
'variation_prices' => array(),
'variation_prices_including_taxes' => array(),
'variation_attributes' => array(),
);
/** /**
* Merges variable product data into the parent object. * Array of visible children variation IDs. Determined by children.
* *
* @param int|WC_Product|object $product Product to init. * @var array
*/ */
public function __construct( $product = 0 ) { protected $visible_children = array();
$this->data = array_merge( $this->data, $this->extra_data );
parent::__construct( $product ); /**
} * Array of variation attributes IDs. Determined by children.
*
* @var array
*/
protected $variation_attributes = array();
/** /**
* Get internal type. * Get internal type.
@ -54,69 +52,23 @@ class WC_Product_Variable extends WC_Product {
*/ */
/** /**
* Return a products child ids. * Get the add to cart button text.
* *
* @param string $context * @return string
* @return array Children ids
*/ */
public function get_children( $context = 'view' ) { public function add_to_cart_text() {
if ( is_bool( $context ) ) { return apply_filters( 'woocommerce_product_add_to_cart_text', $this->is_purchasable() ? __( 'Select options', 'woocommerce' ) : __( 'Read more', 'woocommerce' ), $this );
wc_deprecated_argument( 'visible_only', '2.7', 'WC_Product_Variable::get_visible_children' );
return $context ? $this->get_visible_children() : $this->get_children();
}
if ( has_filter( 'woocommerce_get_children' ) ) {
wc_deprecated_function( 'The woocommerce_get_children filter', '', 'woocommerce_product_get_children or woocommerce_product_get_visible_children' );
}
return apply_filters( 'woocommerce_get_children', $this->get_prop( 'children', $context ), $this, false );
}
/**
* Return a products child ids - visible only.
*
* @since 2.7.0
* @param string $context
* @return array Children ids
*/
public function get_visible_children( $context = 'view' ) {
if ( has_filter( 'woocommerce_get_children' ) ) {
wc_deprecated_function( 'The woocommerce_get_children filter', '', 'woocommerce_product_get_children or woocommerce_product_get_visible_children' );
}
return apply_filters( 'woocommerce_get_children', $this->get_prop( 'visible_children', $context ), $this, true );
}
/**
* Return an array of attributes used for variations, as well as their possible values.
*
* @param string $context
* @return array Attributes and their available values
*/
public function get_variation_attributes( $context = 'view' ) {
return $this->get_prop( 'variation_attributes', $context );
} }
/** /**
* 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. * 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.
* *
* @param string $context * @param bool $include_taxes Should taxes be included in the prices.
* @return array() Array of RAW prices, regular prices, and sale prices with keys set to variation ID. * @return array() Array of RAW prices, regular prices, and sale prices with keys set to variation ID.
*/ */
public function get_variation_prices( $context = 'view' ) { public function get_variation_prices( $include_taxes = false ) {
if ( is_bool( $context ) ) { $data_store = $this->get_data_store();
wc_deprecated_argument( 'display', '2.7', 'Use WC_Product_Variable::get_variation_prices_including_taxes' ); return $data_store->read_price_data( $this, $include_taxes );
return $context ? $this->get_variation_prices_including_taxes() : $this->get_variation_prices();
}
return $this->get_prop( 'variation_prices', $context );
}
/**
* Get an array of all sale and regular prices from all variations, includes taxes.
*
* @since 2.7.0
* @param string $context
* @return array() Array of RAW prices, regular prices, and sale prices with keys set to variation ID.
*/
public function get_variation_prices_including_taxes( $context = 'view' ) {
return $this->get_prop( 'variation_prices_including_taxes', $context );
} }
/** /**
@ -127,7 +79,7 @@ class WC_Product_Variable extends WC_Product {
* @return string * @return string
*/ */
public function get_variation_regular_price( $min_or_max = 'min', $include_taxes = false ) { public function get_variation_regular_price( $min_or_max = 'min', $include_taxes = false ) {
$prices = $include_taxes ? $this->get_variation_prices_including_taxes() : $this->get_variation_prices(); $prices = $this->get_variation_prices( $include_taxes );
$price = 'min' === $min_or_max ? current( $prices['regular_price'] ) : end( $prices['regular_price'] ); $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, $include_taxes ); return apply_filters( 'woocommerce_get_variation_regular_price', $price, $this, $min_or_max, $include_taxes );
} }
@ -140,7 +92,7 @@ class WC_Product_Variable extends WC_Product {
* @return string * @return string
*/ */
public function get_variation_sale_price( $min_or_max = 'min', $include_taxes = false ) { public function get_variation_sale_price( $min_or_max = 'min', $include_taxes = false ) {
$prices = $include_taxes ? $this->get_variation_prices_including_taxes() : $this->get_variation_prices(); $prices = $this->get_variation_prices( $include_taxes );
$price = 'min' === $min_or_max ? current( $prices['sale_price'] ) : end( $prices['sale_price'] ); $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, $include_taxes ); return apply_filters( 'woocommerce_get_variation_sale_price', $price, $this, $min_or_max, $include_taxes );
} }
@ -153,7 +105,7 @@ class WC_Product_Variable extends WC_Product {
* @return string * @return string
*/ */
public function get_variation_price( $min_or_max = 'min', $include_taxes = false ) { public function get_variation_price( $min_or_max = 'min', $include_taxes = false ) {
$prices = $include_taxes ? $this->get_variation_prices_including_taxes() : $this->get_variation_prices(); $prices = $this->get_variation_prices( $include_taxes );
$price = 'min' === $min_or_max ? current( $prices['price'] ) : end( $prices['price'] ); $price = 'min' === $min_or_max ? current( $prices['price'] ) : end( $prices['price'] );
return apply_filters( 'woocommerce_get_variation_price', $price, $this, $min_or_max, $include_taxes ); return apply_filters( 'woocommerce_get_variation_price', $price, $this, $min_or_max, $include_taxes );
} }
@ -165,7 +117,7 @@ class WC_Product_Variable extends WC_Product {
* @return string * @return string
*/ */
public function get_price_html( $price = '' ) { public function get_price_html( $price = '' ) {
$prices = $this->get_variation_prices_including_taxes(); $prices = $this->get_variation_prices( true );
if ( empty( $prices['price'] ) ) { if ( empty( $prices['price'] ) ) {
return apply_filters( 'woocommerce_variable_empty_price_html', '', $this ); return apply_filters( 'woocommerce_variable_empty_price_html', '', $this );
@ -182,6 +134,44 @@ class WC_Product_Variable extends WC_Product {
return apply_filters( 'woocommerce_get_price_html', $price, $this ); return apply_filters( 'woocommerce_get_price_html', $price, $this );
} }
/**
* Return a products child ids.
*
* @return array Children ids
*/
public function get_children( $visible_only = '' ) {
if ( is_bool( $visible_only ) ) {
wc_deprecated_argument( 'visible_only', '2.7', 'WC_Product_Variable::get_visible_children' );
return $visible_only ? $this->get_visible_children() : $this->get_children();
}
if ( has_filter( 'woocommerce_get_children' ) ) {
wc_deprecated_function( 'The woocommerce_get_children filter', '', 'woocommerce_product_get_children or woocommerce_product_get_visible_children' );
}
return apply_filters( 'woocommerce_get_children', $this->children, $this, false );
}
/**
* Return a products child ids - visible only.
*
* @since 2.7.0
* @return array Children ids
*/
public function get_visible_children() {
if ( has_filter( 'woocommerce_get_children' ) ) {
wc_deprecated_function( 'The woocommerce_get_children filter', '', 'woocommerce_product_get_children or woocommerce_product_get_visible_children' );
}
return apply_filters( 'woocommerce_get_children', $this->visible_children, $this, true );
}
/**
* Return an array of attributes used for variations, as well as their possible values.
*
* @return array Attributes and their available values
*/
public function get_variation_attributes() {
return $this->variation_attributes;
}
/** /**
* If set, get the default attributes for a variable product. * If set, get the default attributes for a variable product.
* *
@ -278,33 +268,13 @@ class WC_Product_Variable extends WC_Product {
*/ */
/** /**
* Sets an array of variation prices. * Sets an array of variation attributes.
*
* @since 2.7.0
* @param array
*/
public function set_variation_prices( $variation_prices ) {
$this->set_prop( 'variation_prices', $variation_prices );
}
/**
* Sets an array of variation prices, including taxes.
*
* @since 2.7.0
* @param array
*/
public function set_variation_prices_including_taxes( $variation_prices_including_taxes ) {
$this->set_prop( 'variation_prices_including_taxes', $variation_prices_including_taxes );
}
/**
* Sets an array of variation attributes
* *
* @since 2.7.0 * @since 2.7.0
* @param array * @param array
*/ */
public function set_variation_attributes( $variation_attributes ) { public function set_variation_attributes( $variation_attributes ) {
$this->set_prop( 'variation_attributes', $variation_attributes ); $this->variation_attributes = $variation_attributes;
} }
/** /**
@ -314,7 +284,7 @@ class WC_Product_Variable extends WC_Product {
* @param array * @param array
*/ */
public function set_children( $children ) { public function set_children( $children ) {
$this->set_prop( 'children', array_filter( wp_parse_id_list( (array) $children ) ) ); $this->children = array_filter( wp_parse_id_list( (array) $children ) );
} }
/** /**
@ -324,7 +294,7 @@ class WC_Product_Variable extends WC_Product {
* @param array * @param array
*/ */
public function set_visible_children( $visible_children ) { public function set_visible_children( $visible_children ) {
$this->set_prop( 'visible_children', array_filter( wp_parse_id_list( (array) $visible_children ) ) ); $this->visible_children = array_filter( wp_parse_id_list( (array) $visible_children ) );
} }
/* /*
@ -368,12 +338,17 @@ class WC_Product_Variable extends WC_Product {
// Trigger action before saving to the DB. Allows you to adjust object props before save. // Trigger action before saving to the DB. Allows you to adjust object props before save.
do_action( 'woocommerce_before_' . $this->object_type . '_object_save', $this, $this->data_store ); do_action( 'woocommerce_before_' . $this->object_type . '_object_save', $this, $this->data_store );
// Get names before save.
$previous_name = $this->data['name'];
$new_name = $this->get_name( 'edit' );
if ( $this->get_id() ) { if ( $this->get_id() ) {
$this->data_store->update( $this ); $this->data_store->update( $this );
} else { } else {
$this->data_store->create( $this ); $this->data_store->create( $this );
} }
$this->data_store->sync_variation_names( $this, $previous_name, $new_name );
$this->data_store->sync_managed_variation_stock_status( $this ); $this->data_store->sync_managed_variation_stock_status( $this );
return $this->get_id(); return $this->get_id();
@ -391,8 +366,7 @@ class WC_Product_Variable extends WC_Product {
* @return bool * @return bool
*/ */
public function is_on_sale() { public function is_on_sale() {
$data_store = WC_Data_Store::load( 'product-variable' ); $prices = $this->get_variation_prices();
$prices = $data_store->read_price_data( $this );
return apply_filters( 'woocommerce_product_is_on_sale', $prices['regular_price'] !== $prices['sale_price'] && $prices['sale_price'] === $prices['price'], $this ); return apply_filters( 'woocommerce_product_is_on_sale', $prices['regular_price'] !== $prices['sale_price'] && $prices['sale_price'] === $prices['price'], $this );
} }
@ -459,21 +433,6 @@ class WC_Product_Variable extends WC_Product {
return parent::has_weight() || $this->child_has_weight(); return parent::has_weight() || $this->child_has_weight();
} }
/*
|--------------------------------------------------------------------------
| Non-CRUD Getters
|--------------------------------------------------------------------------
*/
/**
* Get the add to cart button text.
*
* @return string
*/
public function add_to_cart_text() {
return apply_filters( 'woocommerce_product_add_to_cart_text', $this->is_purchasable() ? __( 'Select options', 'woocommerce' ) : __( 'Read more', 'woocommerce' ), $this );
}
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Sync with child variations. | Sync with child variations.

View File

@ -720,7 +720,7 @@ class WC_Shortcodes {
var $variations_form = $( '[data-product-page-preselected-id="<?php echo esc_attr( $preselected_id ); ?>"]' ).find( 'form.variations_form' ); var $variations_form = $( '[data-product-page-preselected-id="<?php echo esc_attr( $preselected_id ); ?>"]' ).find( 'form.variations_form' );
<?php foreach ( $attributes as $attr => $value ) { ?> <?php foreach ( $attributes as $attr => $value ) { ?>
$variations_form.find( 'select[name="<?php echo esc_attr( $attr ); ?>"]' ).val( '<?php echo $value; ?>' ); $variations_form.find( 'select[name="<?php echo esc_attr( $attr ); ?>"]' ).val( '<?php echo esc_js( $value ); ?>' );
<?php } ?> <?php } ?>
}); });
</script> </script>

View File

@ -82,7 +82,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
public function read( &$order ) { public function read( &$order ) {
$order->set_defaults(); $order->set_defaults();
if ( ! $order->get_id() || ! ( $post_object = get_post( $order->get_id() ) ) ) { if ( ! $order->get_id() || ! ( $post_object = get_post( $order->get_id() ) ) || 'shop_order' !== $post_object->post_type ) {
throw new Exception( __( 'Invalid order.', 'woocommerce' ) ); throw new Exception( __( 'Invalid order.', 'woocommerce' ) );
} }
@ -134,11 +134,12 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
if ( $args['force_delete'] ) { if ( $args['force_delete'] ) {
wp_delete_post( $id ); wp_delete_post( $id );
$order->set_id( 0 ); $order->set_id( 0 );
do_action( 'woocommerce_delete_order', $id );
} else { } else {
wp_trash_post( $id ); wp_trash_post( $id );
$order->set_status( 'trash' ); $order->set_status( 'trash' );
do_action( 'woocommerce_trash_order', $id );
} }
do_action( 'woocommerce_delete_order', $id );
} }
/* /*

View File

@ -85,7 +85,7 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
public function read( &$coupon ) { public function read( &$coupon ) {
$coupon->set_defaults(); $coupon->set_defaults();
if ( ! $coupon->get_id() || ! ( $post_object = get_post( $coupon->get_id() ) ) ) { if ( ! $coupon->get_id() || ! ( $post_object = get_post( $coupon->get_id() ) ) || 'shop_coupon' !== $post_object->post_type ) {
throw new Exception( __( 'Invalid coupon.', 'woocommerce' ) ); throw new Exception( __( 'Invalid coupon.', 'woocommerce' ) );
} }
@ -155,10 +155,11 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
if ( $args['force_delete'] ) { if ( $args['force_delete'] ) {
wp_delete_post( $coupon->get_id() ); wp_delete_post( $coupon->get_id() );
$coupon->set_id( 0 ); $coupon->set_id( 0 );
do_action( 'woocommerce_delete_coupon', $id );
} else { } else {
wp_trash_post( $coupon->get_id() ); wp_trash_post( $coupon->get_id() );
do_action( 'woocommerce_trash_coupon', $id );
} }
do_action( 'woocommerce_delete_coupon', $id );
} }
/** /**

View File

@ -113,7 +113,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
public function read( &$product ) { public function read( &$product ) {
$product->set_defaults(); $product->set_defaults();
if ( ! $product->get_id() || ! ( $post_object = get_post( $product->get_id() ) ) ) { if ( ! $product->get_id() || ! ( $post_object = get_post( $product->get_id() ) ) || 'product' !== $post_object->post_type ) {
throw new Exception( __( 'Invalid product.', 'woocommerce' ) ); throw new Exception( __( 'Invalid product.', 'woocommerce' ) );
} }
@ -186,11 +186,12 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
if ( $args['force_delete'] ) { if ( $args['force_delete'] ) {
wp_delete_post( $product->get_id() ); wp_delete_post( $product->get_id() );
$product->set_id( 0 ); $product->set_id( 0 );
do_action( 'woocommerce_delete_' . $post_type, $id );
} else { } else {
wp_trash_post( $product->get_id() ); wp_trash_post( $product->get_id() );
$product->set_status( 'trash' ); $product->set_status( 'trash' );
do_action( 'woocommerce_trash_' . $post_type, $id );
} }
do_action( 'woocommerce_delete_' . $post_type, $id );
} }
/* /*
@ -585,8 +586,16 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
* @since 2.7.0 * @since 2.7.0
*/ */
protected function update_version_and_type( &$product ) { protected function update_version_and_type( &$product ) {
wp_set_object_terms( $product->get_id(), $product->get_type(), 'product_type' ); $old_type = WC_Product_Factory::get_product_type( $product->get_id() );
$new_type = $product->get_type();
wp_set_object_terms( $product->get_id(), $new_type, 'product_type' );
update_post_meta( $product->get_id(), '_product_version', WC_VERSION ); update_post_meta( $product->get_id(), '_product_version', WC_VERSION );
// Action for the transition.
if ( $old_type !== $new_type ) {
do_action( 'woocommerce_product_type_changed', $product, $old_type, $new_type );
}
} }
/** /**
@ -1074,6 +1083,10 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
$wp_query_args['paged'] = absint( $args['page'] ); $wp_query_args['paged'] = absint( $args['page'] );
} }
if ( ! empty( $args['include'] ) ) {
$wp_query_args['post__in'] = array_map( 'absint', $args['include'] );
}
if ( ! empty( $args['exclude'] ) ) { if ( ! empty( $args['exclude'] ) ) {
$wp_query_args['post__not_in'] = array_map( 'absint', $args['exclude'] ); $wp_query_args['post__not_in'] = array_map( 'absint', $args['exclude'] );
} }
@ -1152,8 +1165,16 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
); );
if ( is_numeric( $term ) ) { if ( is_numeric( $term ) ) {
$product_ids[] = absint( $term ); $post_id = absint( $term );
$product_ids[] = wp_get_post_parent_id( $term ); $post_type = get_post_type( $post_id );
if ( 'product_variation' === $post_type && $include_variations ) {
$product_ids[] = $post_id;
} elseif ( 'product' === $post_type ) {
$product_ids[] = $post_id;
}
$product_ids[] = wp_get_post_parent_id( $post_id );
} }
return wp_parse_id_list( $product_ids ); return wp_parse_id_list( $product_ids );

View File

@ -26,21 +26,22 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
*/ */
protected function read_product_data( &$product ) { protected function read_product_data( &$product ) {
parent::read_product_data( $product ); parent::read_product_data( $product );
$this->read_children( $product );
// Set directly since individual data needs changed at the WC_Product_Variation level -- these datasets just pull. // Set directly since individual data needs changed at the WC_Product_Variation level -- these datasets just pull.
$this->read_price_data( $product ); $children = $this->read_children( $product );
$this->read_price_data( $product, true ); $product->set_children( $children['all'] );
$this->read_variation_attributes( $product ); $product->set_visible_children( $children['visible'] );
$product->set_variation_attributes( $this->read_variation_attributes( $product ) );
} }
/** /**
* Loads variation child IDs. * Loads variation child IDs.
*
* @param WC_Product * @param WC_Product
* @param bool $force_read True to bypass the transient. * @param bool $force_read True to bypass the transient.
* @return WC_Product * @return array
*/ */
public function read_children( &$product, $force_read = false ) { private function read_children( &$product, $force_read = false ) {
$children_transient_name = 'wc_product_children_' . $product->get_id(); $children_transient_name = 'wc_product_children_' . $product->get_id();
$children = get_transient( $children_transient_name ); $children = get_transient( $children_transient_name );
@ -67,14 +68,17 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
set_transient( $children_transient_name, $children, DAY_IN_SECONDS * 30 ); set_transient( $children_transient_name, $children, DAY_IN_SECONDS * 30 );
} }
$product->set_children( wp_parse_id_list( (array) $children['all'] ) ); $children['all'] = wp_parse_id_list( (array) $children['all'] );
$product->set_visible_children( wp_parse_id_list( (array) $children['visible'] ) ); $children['visible'] = wp_parse_id_list( (array) $children['visible'] );
return $children;
} }
/** /**
* Loads an array of attributes used for variations, as well as their possible values. * Loads an array of attributes used for variations, as well as their possible values.
* *
* @param WC_Product * @param WC_Product
* @return array
*/ */
private function read_variation_attributes( &$product ) { private function read_variation_attributes( &$product ) {
global $wpdb; global $wpdb;
@ -124,7 +128,7 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
} }
} }
$product->set_variation_attributes( $variation_attributes ); return $variation_attributes;
} }
/** /**
@ -136,8 +140,9 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
* @since 2.7.0 * @since 2.7.0
* @param WC_Product * @param WC_Product
* @param bool $include_taxes If taxes should be calculated or not. * @param bool $include_taxes If taxes should be calculated or not.
* @return array of prices
*/ */
private function read_price_data( &$product, $include_taxes = false ) { public function read_price_data( &$product, $include_taxes = false ) {
global $wp_filter; global $wp_filter;
/** /**
@ -171,17 +176,7 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
* $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. * $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 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 ] ) ) { if ( empty( $this->prices_array[ $price_hash ] ) ) {
if ( $include_taxes ) {
$product->set_variation_prices_including_taxes( $this->prices_array[ $price_hash ] );
} else {
$product->set_variation_prices( $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 ) ); $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 the product version has changed since the transient was last saved, reset the transient cache.
@ -248,13 +243,8 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
* This value may differ from the transient cache. It is filtered once before storing locally. * This value may differ from the transient cache. It is filtered once before storing locally.
*/ */
$this->prices_array[ $price_hash ] = apply_filters( 'woocommerce_variation_prices', $transient_cached_prices_array[ $price_hash ], $product, $include_taxes ); $this->prices_array[ $price_hash ] = apply_filters( 'woocommerce_variation_prices', $transient_cached_prices_array[ $price_hash ], $product, $include_taxes );
if ( $include_taxes ) {
$product->set_variation_prices_including_taxes( $this->prices_array[ $price_hash ] );
} else {
$product->set_variation_prices( $this->prices_array[ $price_hash ] );
}
} }
return $this->prices_array[ $price_hash ];
} }
/** /**
@ -293,10 +283,36 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
public function child_is_in_stock( $product ) { public function child_is_in_stock( $product ) {
global $wpdb; global $wpdb;
$children = $product->get_visible_children( 'edit' ); $children = $product->get_visible_children( 'edit' );
$oufofstock_children = $wpdb->get_var( "SELECT COUNT( post_id ) FROM $wpdb->postmeta WHERE meta_key = '_stock_status' AND meta_value = 'instock' AND post_id IN ( " . implode( ',', array_map( 'absint', $children ) ) . " )" ); $oufofstock_children = $children ? $wpdb->get_var( "SELECT COUNT( post_id ) FROM $wpdb->postmeta WHERE meta_key = '_stock_status' AND meta_value = 'instock' AND post_id IN ( " . implode( ',', array_map( 'absint', $children ) ) . " )" ) : 0;
return $children > $oufofstock_children; return $children > $oufofstock_children;
} }
/**
* Syncs all variation names if the parent name is changed.
*
* @param WC_Product $product
* @param string $previous_name
* @param string $new_name
* @since 2.7.0
*/
public function sync_variation_names( &$product, $previous_name = '', $new_name = '' ) {
if ( $new_name !== $previous_name ) {
global $wpdb;
$wpdb->query( $wpdb->prepare(
"
UPDATE {$wpdb->posts}
SET post_title = REPLACE( post_title, %s, %s )
WHERE post_type = 'product_variation'
AND post_parent = %d
",
$previous_name,
$new_name,
$product->get_id()
) );
}
}
/** /**
* Stock managed at the parent level - update children being managed by this product. * Stock managed at the parent level - update children being managed by this product.
* This sync function syncs downwards (from parent to child) when the variable product is saved. * This sync function syncs downwards (from parent to child) when the variable product is saved.
@ -318,7 +334,9 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
} }
} }
if ( $changed ) { if ( $changed ) {
$this->read_children( $product, true ); $children = $this->read_children( $product, true );
$product->set_children( $children['all'] );
$product->set_visible_children( $children['visible'] );
} }
} }
} }
@ -356,4 +374,56 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
public function sync_stock_status( &$product ) { public function sync_stock_status( &$product ) {
$product->set_stock_status( $product->child_is_in_stock() ? 'instock' : 'outofstock' ); $product->set_stock_status( $product->child_is_in_stock() ? 'instock' : 'outofstock' );
} }
/**
* Delete variations of a product.
*
* @since 2.7.0
* @param int $product_id
* @param $force_delete False to trash.
*/
public function delete_variations( $product_id, $force_delete = false ) {
$variation_ids = wp_parse_id_list( get_posts( array(
'post_parent' => $product_id,
'post_type' => 'product_variation',
'fields' => 'ids',
'post_status' => 'any',
'numberposts' => -1,
) ) );
if ( ! empty( $variation_ids ) ) {
foreach ( $variation_ids as $variation_id ) {
if ( $force_delete ) {
wp_delete_post( $variation_id, true );
} else {
wp_trash_post( $variation_id );
}
}
}
delete_transient( 'wc_product_children_' . $product_id );
}
/**
* Untrash variations.
*
* @param int $product_id
*/
public function untrash_variations( $product_id ) {
$variation_ids = wp_parse_id_list( get_posts( array(
'post_parent' => $product_id,
'post_type' => 'product_variation',
'fields' => 'ids',
'post_status' => 'trash',
'numberposts' => -1,
) ) );
if ( ! empty( $variation_ids ) ) {
foreach ( $variation_ids as $variation_id ) {
wp_untrash_post( $variation_id );
}
}
delete_transient( 'wc_product_children_' . $product_id );
}
} }

View File

@ -94,7 +94,9 @@ class WC_Shipping_Zone_Data_Store extends WC_Data_Store_WP implements WC_Shippin
$wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array( 'zone_id' => $zone->get_id() ) ); $wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_locations', array( 'zone_id' => $zone->get_id() ) );
$wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zones', array( 'zone_id' => $zone->get_id() ) ); $wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zones', array( 'zone_id' => $zone->get_id() ) );
WC_Cache_Helper::incr_cache_prefix( 'shipping_zones' ); WC_Cache_Helper::incr_cache_prefix( 'shipping_zones' );
$id = $zone->get_id();
$zone->set_id( null ); $zone->set_id( null );
do_action( 'woocommerce_delete_shipping_zone', $id );
} }
} }
@ -160,6 +162,7 @@ class WC_Shipping_Zone_Data_Store extends WC_Data_Store_WP implements WC_Shippin
public function delete_method( $instance_id ) { public function delete_method( $instance_id ) {
global $wpdb; global $wpdb;
$wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_methods', array( 'instance_id' => $instance_id ) ); $wpdb->delete( $wpdb->prefix . 'woocommerce_shipping_zone_methods', array( 'instance_id' => $instance_id ) );
do_action( 'woocommerce_delete_shipping_zone_method', $instance_id );
} }
/** /**

View File

@ -37,6 +37,16 @@ interface WC_Product_Variable_Data_Store_Interface {
*/ */
public function child_is_in_stock( $product ); public function child_is_in_stock( $product );
/**
* Syncs all variation names if the parent name is changed.
*
* @param WC_Product $product
* @param string $previous_name
* @param string $new_name
* @since 2.7.0
*/
public function sync_variation_names( &$product, $previous_name = '', $new_name = '' );
/** /**
* Stock managed at the parent level - update children being managed by this product. * Stock managed at the parent level - update children being managed by this product.
* This sync function syncs downwards (from parent to child) when the variable product is saved. * This sync function syncs downwards (from parent to child) when the variable product is saved.
@ -51,4 +61,19 @@ interface WC_Product_Variable_Data_Store_Interface {
* @param WC_Product|int $product * @param WC_Product|int $product
*/ */
public function sync_price( &$product ); public function sync_price( &$product );
/**
* Delete variations of a product.
*
* @param int $product_id
* @param $force_delete False to trash.
*/
public function delete_variations( $product_id, $force_delete = false );
/**
* Untrash variations.
*
* @param int $product_id
*/
public function untrash_variations( $product_id );
} }

View File

@ -580,7 +580,7 @@ class WC_Email extends WC_Settings_API {
if ( current_user_can( 'edit_themes' ) && ! empty( $template_code ) && ! empty( $template_path ) ) { if ( current_user_can( 'edit_themes' ) && ! empty( $template_code ) && ! empty( $template_path ) ) {
$saved = false; $saved = false;
$file = get_stylesheet_directory() . '/woocommerce/' . $template_path; $file = get_stylesheet_directory() . '/woocommerce/' . $template_path;
$code = stripslashes( $template_code ); $code = wp_unslash( $template_code );
if ( is_writeable( $file ) ) { if ( is_writeable( $file ) ) {
$f = fopen( $file, 'w+' ); $f = fopen( $file, 'w+' );
@ -626,7 +626,7 @@ class WC_Email extends WC_Settings_API {
// Locate template file // Locate template file
$core_file = $this->template_base . $template; $core_file = $this->template_base . $template;
$template_file = apply_filters( 'woocommerce_locate_core_template', $core_file, $template, $this->template_base ); $template_file = apply_filters( 'woocommerce_locate_core_template', $core_file, $template, $this->template_base, $this->id );
// Copy template file // Copy template file
copy( $template_file, $theme_file ); copy( $template_file, $theme_file );
@ -755,7 +755,7 @@ class WC_Email extends WC_Settings_API {
$local_file = $this->get_theme_template_file( $template ); $local_file = $this->get_theme_template_file( $template );
$core_file = $this->template_base . $template; $core_file = $this->template_base . $template;
$template_file = apply_filters( 'woocommerce_locate_core_template', $core_file, $template, $this->template_base ); $template_file = apply_filters( 'woocommerce_locate_core_template', $core_file, $template, $this->template_base, $this->id );
$template_dir = apply_filters( 'woocommerce_template_directory', 'woocommerce', $template ); $template_dir = apply_filters( 'woocommerce_template_directory', 'woocommerce', $template );
?> ?>
<div class="template <?php echo $template_type; ?>"> <div class="template <?php echo $template_type; ?>">

View File

@ -100,7 +100,7 @@ class WC_Shipping_Flat_Rate extends WC_Shipping_Method {
'percent' => '', 'percent' => '',
'min_fee' => '', 'min_fee' => '',
'max_fee' => '', 'max_fee' => '',
), $atts ); ), $atts, 'fee' );
$calculated_fee = 0; $calculated_fee = 0;

View File

@ -130,7 +130,7 @@ class WC_Shipping_Legacy_Flat_Rate extends WC_Shipping_Method {
$atts = shortcode_atts( array( $atts = shortcode_atts( array(
'percent' => '', 'percent' => '',
'min_fee' => '', 'min_fee' => '',
), $atts ); ), $atts, 'fee' );
$calculated_fee = 0; $calculated_fee = 0;

View File

@ -53,13 +53,17 @@ class WC_Shortcode_Cart {
/** /**
* Output the cart shortcode. * Output the cart shortcode.
*
* @param array $atts
*/ */
public static function output() { public static function output( $atts ) {
// Constants // Constants
if ( ! defined( 'WOOCOMMERCE_CART' ) ) { if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
define( 'WOOCOMMERCE_CART', true ); define( 'WOOCOMMERCE_CART', true );
} }
$atts = shortcode_atts( array(), $atts, 'woocommerce_cart' );
// Update Shipping // Update Shipping
if ( ! empty( $_POST['calc_shipping'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'woocommerce-cart' ) ) { if ( ! empty( $_POST['calc_shipping'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'woocommerce-cart' ) ) {
self::calculate_shipping(); self::calculate_shipping();

View File

@ -34,6 +34,8 @@ class WC_Shortcode_Checkout {
return; return;
} }
$atts = shortcode_atts( array(), $atts, 'woocommerce_checkout' );
// Backwards compat with old pay and thanks link arguments // Backwards compat with old pay and thanks link arguments
if ( isset( $_GET['order'] ) && isset( $_GET['key'] ) ) { if ( isset( $_GET['order'] ) && isset( $_GET['key'] ) ) {
wc_deprecated_argument( __CLASS__ . '->' . __FUNCTION__, '2.1', '"order" is no longer used to pass an order ID. Use the order-pay or order-received endpoint instead.' ); wc_deprecated_argument( __CLASS__ . '->' . __FUNCTION__, '2.1', '"order" is no longer used to pass an order ID. Use the order-pay or order-received endpoint instead.' );
@ -146,7 +148,7 @@ class WC_Shortcode_Checkout {
<li class="method"> <li class="method">
<?php _e( 'Payment method:', 'woocommerce' ); ?> <?php _e( 'Payment method:', 'woocommerce' ); ?>
<strong><?php <strong><?php
echo $order->get_payment_method_title(); echo wp_kses_post( $order->get_payment_method_title() );
?></strong> ?></strong>
</li> </li>
<?php endif; ?> <?php endif; ?>

View File

@ -98,7 +98,7 @@ class WC_Shortcode_My_Account {
private static function my_account( $atts ) { private static function my_account( $atts ) {
extract( shortcode_atts( array( extract( shortcode_atts( array(
'order_count' => 15, // @deprecated 2.6.0. Keep for backward compatibility. 'order_count' => 15, // @deprecated 2.6.0. Keep for backward compatibility.
), $atts ) ); ), $atts, 'woocommerce_my_account' ) );
wc_get_template( 'myaccount/my-account.php', array( wc_get_template( 'myaccount/my-account.php', array(
'current_user' => get_user_by( 'id', get_current_user_id() ), 'current_user' => get_user_by( 'id', get_current_user_id() ),

View File

@ -33,7 +33,7 @@ class WC_Shortcode_Order_Tracking {
return; return;
} }
extract( shortcode_atts( array(), $atts ) ); extract( shortcode_atts( array(), $atts, 'woocommerce_order_tracking' ) );
global $post; global $post;

View File

@ -341,7 +341,7 @@ function get_woocommerce_currencies() {
'INR' => __( 'Indian rupee', 'woocommerce' ), 'INR' => __( 'Indian rupee', 'woocommerce' ),
'IQD' => __( 'Iraqi dinar', 'woocommerce' ), 'IQD' => __( 'Iraqi dinar', 'woocommerce' ),
'IRR' => __( 'Iranian rial', 'woocommerce' ), 'IRR' => __( 'Iranian rial', 'woocommerce' ),
'IRT' => __( 'Iranian Toman', 'woocommerce' ), 'IRT' => __( 'Iranian toman', 'woocommerce' ),
'ISK' => __( 'Icelandic kr&oacute;na', 'woocommerce' ), 'ISK' => __( 'Icelandic kr&oacute;na', 'woocommerce' ),
'JEP' => __( 'Jersey pound', 'woocommerce' ), 'JEP' => __( 'Jersey pound', 'woocommerce' ),
'JMD' => __( 'Jamaican dollar', 'woocommerce' ), 'JMD' => __( 'Jamaican dollar', 'woocommerce' ),

View File

@ -40,6 +40,7 @@ function wc_do_deprecated_action( $action, $args, $deprecated_in, $replacement )
*/ */
function wc_deprecated_function( $function, $version, $replacement = null ) { function wc_deprecated_function( $function, $version, $replacement = null ) {
if ( is_ajax() ) { if ( is_ajax() ) {
do_action( 'deprecated_function_run', $function, $replacement, $version );
$log_string = "The {$function} function is deprecated since version {$version}."; $log_string = "The {$function} function is deprecated since version {$version}.";
$log_string .= $replacement ? "Replace with {$replacement}." : ''; $log_string .= $replacement ? "Replace with {$replacement}." : '';
error_log( $log_string ); error_log( $log_string );
@ -59,6 +60,7 @@ function wc_deprecated_function( $function, $version, $replacement = null ) {
*/ */
function wc_doing_it_wrong( $function, $message, $version ) { function wc_doing_it_wrong( $function, $message, $version ) {
if ( is_ajax() ) { if ( is_ajax() ) {
do_action( 'doing_it_wrong_run', $function, $message, $version );
error_log( "{$function} was called incorrectly. {$message}. This message was added in version {$version}." ); error_log( "{$function} was called incorrectly. {$message}. This message was added in version {$version}." );
} else { } else {
_doing_it_wrong( $function, $message, $version ); _doing_it_wrong( $function, $message, $version );
@ -75,6 +77,7 @@ function wc_doing_it_wrong( $function, $message, $version ) {
*/ */
function wc_deprecated_argument( $argument, $version, $message = null ) { function wc_deprecated_argument( $argument, $version, $message = null ) {
if ( is_ajax() ) { if ( is_ajax() ) {
do_action( 'deprecated_argument_run', $function, $message, $version );
error_log( "The {$argument} argument is deprecated since version {$version}. {$message}" ); error_log( "The {$argument} argument is deprecated since version {$version}. {$message}" );
} else { } else {
_deprecated_argument( $argument, $version, $message ); _deprecated_argument( $argument, $version, $message );

View File

@ -21,7 +21,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @return bool * @return bool
*/ */
function wc_string_to_bool( $string ) { function wc_string_to_bool( $string ) {
return is_bool( $string ) ? $string : ( 'yes' === $string || 1 === $string || '1' === $string ); return is_bool( $string ) ? $string : ( 'yes' === $string || 1 === $string || 'true' === $string || '1' === $string );
} }
/** /**
@ -922,32 +922,45 @@ if ( ! function_exists( 'wc_make_numeric_postcode' ) ) {
/** /**
* Format the stock amount ready for display based on settings. * Format the stock amount ready for display based on settings.
*
* @since 2.7.0 * @since 2.7.0
* @param int $stock_amount * @param WC_Product $product Product object for which the stock you need to format.
* @param boolean $show_backorder_notification
* @return string * @return string
*/ */
function wc_format_stock_for_display( $stock_amount, $show_backorder_notification = false ) { function wc_format_stock_for_display( $product ) {
$display = __( 'In stock', 'woocommerce' ); $display = __( 'In stock', 'woocommerce' );
$stock_amount = $product->get_stock_quantity();
switch ( get_option( 'woocommerce_stock_format' ) ) { switch ( get_option( 'woocommerce_stock_format' ) ) {
case 'low_amount' : case 'low_amount' :
if ( $stock_amount <= get_option( 'woocommerce_notify_low_stock_amount' ) ) { if ( $stock_amount <= get_option( 'woocommerce_notify_low_stock_amount' ) ) {
$display = sprintf( __( 'Only %s left in stock', 'woocommerce' ), $stock_amount ); $display = sprintf( __( 'Only %s left in stock', 'woocommerce' ), wc_format_stock_quantity_for_display( $stock_amount, $product ) );
} }
break; break;
case '' : case '' :
$display = sprintf( __( '%s in stock', 'woocommerce' ), $stock_amount ); $display = sprintf( __( '%s in stock', 'woocommerce' ), wc_format_stock_quantity_for_display( $stock_amount, $product ) );
break; break;
} }
if ( $show_backorder_notification ) { if ( $product->backorders_allowed() && $product->backorders_require_notification() ) {
$display .= ' ' . __( '(can be backordered)', 'woocommerce' ); $display .= ' ' . __( '(can be backordered)', 'woocommerce' );
} }
return $display; return $display;
} }
/**
* Format the stock quantity ready for display.
*
* @since 2.7.0
* @param int $stock_quantity
* @param WC_Product $product so that we can pass through the filters.
* @return string
*/
function wc_format_stock_quantity_for_display( $stock_quantity, $product ) {
return apply_filters( 'woocommerce_format_stock_quantity', $stock_quantity, $product );
}
/** /**
* Format a sale price for display. * Format a sale price for display.
* @since 2.7.0 * @since 2.7.0

View File

@ -497,6 +497,7 @@ function wc_create_refund( $args = array() ) {
if ( 0 > $args['amount'] ) { if ( 0 > $args['amount'] ) {
$args['amount'] = 0; $args['amount'] = 0;
} }
$refund->set_currency( $order->get_currency() );
$refund->set_amount( $args['amount'] ); $refund->set_amount( $args['amount'] );
$refund->set_parent_id( absint( $args['order_id'] ) ); $refund->set_parent_id( absint( $args['order_id'] ) );
$refund->set_refunded_by( get_current_user_id() ? get_current_user_id() : 1 ); $refund->set_refunded_by( get_current_user_id() ? get_current_user_id() : 1 );
@ -544,8 +545,18 @@ function wc_create_refund( $args = array() ) {
$refund->update_taxes(); $refund->update_taxes();
$refund->calculate_totals( false ); $refund->calculate_totals( false );
$refund->set_total( $args['amount'] * -1 ); $refund->set_total( $args['amount'] * -1 );
/**
* Action hook to adjust refund before save.
* @since 2.7.0
*/
do_action( 'woocommerce_create_refund', $refund, $args );
$refund->save(); $refund->save();
// Backwards compatibility hook.
do_action( 'woocommerce_refund_created', $refund->get_id(), $args );
} catch ( Exception $e ) { } catch ( Exception $e ) {
return new WP_Error( 'error', $e->getMessage() ); return new WP_Error( 'error', $e->getMessage() );
} }

View File

@ -61,6 +61,7 @@ function wc_get_products( $args ) {
'limit' => get_option( 'posts_per_page' ), 'limit' => get_option( 'posts_per_page' ),
'offset' => null, 'offset' => null,
'page' => 1, 'page' => 1,
'include' => array(),
'exclude' => array(), 'exclude' => array(),
'orderby' => 'date', 'orderby' => 'date',
'order' => 'DESC', 'order' => 'DESC',
@ -93,7 +94,7 @@ function wc_get_products( $args ) {
* @since 2.2.0 * @since 2.2.0
* *
* @param mixed $the_product Post object or post ID of the product. * @param mixed $the_product Post object or post ID of the product.
* @param array $deprecated * @param array $deprecated Previously used to pass arguments to the factory, e.g. to force a type.
* @return WC_Product|null * @return WC_Product|null
*/ */
function wc_get_product( $the_product = false, $deprecated = array() ) { function wc_get_product( $the_product = false, $deprecated = array() ) {
@ -101,7 +102,10 @@ function wc_get_product( $the_product = false, $deprecated = array() ) {
wc_doing_it_wrong( __FUNCTION__, __( 'wc_get_product should not be called before the woocommerce_init action.', 'woocommerce' ), '2.5' ); wc_doing_it_wrong( __FUNCTION__, __( 'wc_get_product should not be called before the woocommerce_init action.', 'woocommerce' ), '2.5' );
return false; return false;
} }
return WC()->product_factory->get_product( $the_product ); if ( ! empty( $deprecated ) ) {
wc_deprecated_argument( 'args', '2.7', 'Passing args to wc_get_product is deprecated. If you need to force a type, construct the product class directly.' );
}
return WC()->product_factory->get_product( $the_product, $deprecated );
} }
/** /**
@ -531,8 +535,6 @@ function wc_get_product_types() {
* @return bool * @return bool
*/ */
function wc_product_has_unique_sku( $product_id, $sku ) { function wc_product_has_unique_sku( $product_id, $sku ) {
global $wpdb;
$data_store = WC_Data_Store::load( 'product' ); $data_store = WC_Data_Store::load( 'product' );
$sku_found = $data_store->is_existing_sku( $product_id, $sku ); $sku_found = $data_store->is_existing_sku( $product_id, $sku );
@ -543,6 +545,47 @@ function wc_product_has_unique_sku( $product_id, $sku ) {
} }
} }
/**
* Force a unique SKU.
*
* @since 2.7.0
* @param integer $product_id
*/
function wc_product_force_unique_sku( $product_id ) {
$product = wc_get_product( $product_id );
if ( $product ) {
try {
$current_sku = $product->get_sku();
$new_sku = wc_product_generate_unique_sku( $product_id, $current_sku );
if ( $current_sku !== $new_sku ) {
$product->set_sku( $new_sku );
$product->save();
}
} catch ( Exception $e ) {}
}
}
/**
* Recursively appends a suffix until a unique SKU is found.
*
* @since 2.7.0
* @param integer $product_id
* @param string $sku
* @param integer $index
* @return string
*/
function wc_product_generate_unique_sku( $product_id, $sku, $index = 0 ) {
$generated_sku = 0 < $index ? $sku . '-' . $index : $sku;
if ( ! wc_product_has_unique_sku( $product_id, $generated_sku ) ) {
$generated_sku = wc_product_generate_unique_sku( $product_id, $sku, ( $index + 1 ) );
}
return $generated_sku;
}
/** /**
* Get product ID by SKU. * Get product ID by SKU.
* *
@ -840,8 +883,8 @@ function wc_get_price_including_tax( $product, $args = array() ) {
'qty' => '', 'qty' => '',
'price' => '', 'price' => '',
) ); ) );
$price = max( 0, $args['price'] ? $args['price'] : $product->get_price() ); $price = (float) max( 0, $args['price'] ? $args['price'] : $product->get_price() );
$qty = $args['qty'] ? $args['qty'] : 1; $qty = (int) $args['qty'] ? $args['qty'] : 1;
if ( ! $product->is_taxable() ) { if ( ! $product->is_taxable() ) {
$price = $price * $qty; $price = $price * $qty;
@ -888,8 +931,8 @@ function wc_get_price_excluding_tax( $product, $args = array() ) {
'qty' => '', 'qty' => '',
'price' => '', 'price' => '',
) ); ) );
$price = max( 0, $args['price'] ? $args['price'] : $product->get_price() ); $price = (float) max( 0, $args['price'] ? $args['price'] : $product->get_price() );
$qty = $args['qty'] ? $args['qty'] : 1; $qty = (int) $args['qty'] ? $args['qty'] : 1;
if ( $product->is_taxable() && wc_prices_include_tax() ) { if ( $product->is_taxable() && wc_prices_include_tax() ) {
$tax_rates = WC_Tax::get_base_tax_rates( $product->get_tax_class( true ) ); $tax_rates = WC_Tax::get_base_tax_rates( $product->get_tax_class( true ) );

View File

@ -100,7 +100,7 @@ function wc_reduce_stock_levels( $order_id ) {
} }
if ( $new_stock < 0 ) { if ( $new_stock < 0 ) {
do_action( 'woocommerce_product_on_backorder', array( 'product' => $product, 'order_id' => $order_id, 'quantity' => $qty_ordered ) ); do_action( 'woocommerce_product_on_backorder', array( 'product' => $product, 'order_id' => $order_id, 'quantity' => $qty ) );
} }
} }
} }

View File

@ -119,7 +119,7 @@ function wc_get_product_terms( $product_id, $taxonomy, $args = array() ) {
} }
} elseif ( ! empty( $args['orderby'] ) && 'menu_order' === $args['orderby'] ) { } elseif ( ! empty( $args['orderby'] ) && 'menu_order' === $args['orderby'] ) {
// wp_get_post_terms doesn't let us use custom sort order. // wp_get_post_terms doesn't let us use custom sort order.
$args['include'] = wc_get_object_terms( $product_id, $taxonomy, 'id' ); $args['include'] = wc_get_object_terms( $product_id, $taxonomy, 'term_id' );
if ( empty( $args['include'] ) ) { if ( empty( $args['include'] ) ) {
$terms = array(); $terms = array();
@ -483,7 +483,7 @@ function wc_terms_clauses( $clauses, $taxonomies, $args ) {
return $clauses; return $clauses;
} }
// Wordpress should give us the taxonomies asked when calling the get_terms function. Only apply to categories and pa_ attributes. // WordPress should give us the taxonomies asked when calling the get_terms function. Only apply to categories and pa_ attributes.
$found = false; $found = false;
foreach ( (array) $taxonomies as $taxonomy ) { foreach ( (array) $taxonomies as $taxonomy ) {
if ( taxonomy_is_product_attribute( $taxonomy ) || in_array( $taxonomy, apply_filters( 'woocommerce_sortable_taxonomies', array( 'product_cat' ) ) ) ) { if ( taxonomy_is_product_attribute( $taxonomy ) || in_array( $taxonomy, apply_filters( 'woocommerce_sortable_taxonomies', array( 'product_cat' ) ) ) ) {
@ -503,7 +503,7 @@ function wc_terms_clauses( $clauses, $taxonomies, $args ) {
} }
// Query fields. // Query fields.
$clauses['fields'] = 'DISTINCT ' . $clauses['fields']; $clauses['fields'] = 'DISTINCT ' . $clauses['fields'] . ', tm.meta_value';
// Query join. // Query join.
if ( get_option( 'db_version' ) < 34370 ) { if ( get_option( 'db_version' ) < 34370 ) {

View File

@ -163,7 +163,7 @@ class WC_Widget_Layered_Nav_Filters extends WC_Widget {
foreach ( $rating_filter as $rating ) { foreach ( $rating_filter as $rating ) {
$link_ratings = implode( ',', array_diff( $rating_filter, array( $rating ) ) ); $link_ratings = implode( ',', array_diff( $rating_filter, array( $rating ) ) );
$link = $link_ratings ? add_query_arg( 'rating_filter', $link_ratings ) : remove_query_arg( 'rating_filter', $base_link ); $link = $link_ratings ? add_query_arg( 'rating_filter', $link_ratings ) : remove_query_arg( 'rating_filter', $base_link );
echo '<li class="chosen"><a aria-label="' . esc_attr__( 'Remove filter', 'woocommerce' ) . '" href="' . esc_url( $link ) . '">' . sprintf( __( 'Rated %s out of 5', 'woocommerce' ), $rating ) . '</a></li>'; echo '<li class="chosen"><a aria-label="' . esc_attr__( 'Remove filter', 'woocommerce' ) . '" href="' . esc_url( $link ) . '">' . sprintf( __( 'Rated %s out of 5', 'woocommerce' ), esc_html( $rating ) ) . '</a></li>';
} }
} }

View File

@ -451,7 +451,7 @@ class WC_Widget_Layered_Nav extends WC_Widget {
} }
if ( $count > 0 || $option_is_set ) { if ( $count > 0 || $option_is_set ) {
$link = esc_url( apply_filters( 'woocommerce_layered_nav_link', $link ) ); $link = esc_url( apply_filters( 'woocommerce_layered_nav_link', $link, $term, $taxonomy ) );
$term_html = '<a href="' . $link . '">' . esc_html( $term->name ) . '</a>'; $term_html = '<a href="' . $link . '">' . esc_html( $term->name ) . '</a>';
} else { } else {
$link = false; $link = false;

View File

@ -77,9 +77,7 @@ class WC_Widget_Recent_Reviews extends WC_Widget {
echo '<li><a href="' . esc_url( get_comment_link( $comment->comment_ID ) ) . '">'; echo '<li><a href="' . esc_url( get_comment_link( $comment->comment_ID ) ) . '">';
echo $_product->get_image(); echo $_product->get_image() . wp_kses_post( $_product->get_name() ) . '</a>';
echo $_product->get_name() . '</a>';
echo $rating_html; echo $rating_html;

View File

@ -61,7 +61,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<li class="woocommerce-order-overview__payment-method method"> <li class="woocommerce-order-overview__payment-method method">
<?php _e( 'Payment method:', 'woocommerce' ); ?> <?php _e( 'Payment method:', 'woocommerce' ); ?>
<strong><?php echo $order->get_payment_method_title(); ?></strong> <strong><?php echo wp_kses_post( $order->get_payment_method_title() ); ?></strong>
</li> </li>
<?php endif; ?> <?php endif; ?>

View File

@ -26,11 +26,11 @@ do_action( 'woocommerce_before_account_orders', $has_orders ); ?>
<?php if ( $has_orders ) : ?> <?php if ( $has_orders ) : ?>
<table class="woocommerce-MyAccount-orders shop_table shop_table_responsive my_account_orders account-orders-table"> <table class="woocommerce-orders-table woocommerce-MyAccount-orders shop_table shop_table_responsive my_account_orders account-orders-table">
<thead> <thead>
<tr> <tr>
<?php foreach ( wc_get_account_orders_columns() as $column_id => $column_name ) : ?> <?php foreach ( wc_get_account_orders_columns() as $column_id => $column_name ) : ?>
<th class="<?php echo esc_attr( $column_id ); ?>"><span class="nobr"><?php echo esc_html( $column_name ); ?></span></th> <th class="woocommerce-orders-table__header woocommerce-orders-table__header-<?php echo esc_attr( $column_id ); ?>"><span class="nobr"><?php echo esc_html( $column_name ); ?></span></th>
<?php endforeach; ?> <?php endforeach; ?>
</tr> </tr>
</thead> </thead>
@ -40,9 +40,9 @@ do_action( 'woocommerce_before_account_orders', $has_orders ); ?>
$order = wc_get_order( $customer_order ); $order = wc_get_order( $customer_order );
$item_count = $order->get_item_count(); $item_count = $order->get_item_count();
?> ?>
<tr class="order"> <tr class="woocommerce-orders-table__row woocommerce-orders-table__row--status-<?php echo esc_attr( $order->get_status() ); ?> order">
<?php foreach ( wc_get_account_orders_columns() as $column_id => $column_name ) : ?> <?php foreach ( wc_get_account_orders_columns() as $column_id => $column_name ) : ?>
<td class="<?php echo esc_attr( $column_id ); ?>" data-title="<?php echo esc_attr( $column_name ); ?>"> <td class="woocommerce-orders-table__cell woocommerce-orders-table__cell-<?php echo esc_attr( $column_id ); ?>" data-title="<?php echo esc_attr( $column_name ); ?>">
<?php if ( has_action( 'woocommerce_my_account_my_orders_column_' . $column_id ) ) : ?> <?php if ( has_action( 'woocommerce_my_account_my_orders_column_' . $column_id ) ) : ?>
<?php do_action( 'woocommerce_my_account_my_orders_column_' . $column_id, $order ); ?> <?php do_action( 'woocommerce_my_account_my_orders_column_' . $column_id, $order ); ?>
@ -90,7 +90,7 @@ do_action( 'woocommerce_before_account_orders', $has_orders ); ?>
if ( $actions = apply_filters( 'woocommerce_my_account_my_orders_actions', $actions, $order ) ) { if ( $actions = apply_filters( 'woocommerce_my_account_my_orders_actions', $actions, $order ) ) {
foreach ( $actions as $key => $action ) { foreach ( $actions as $key => $action ) {
echo '<a href="' . esc_url( $action['url'] ) . '" class="button ' . sanitize_html_class( $key ) . '">' . esc_html( $action['name'] ) . '</a>'; echo '<a href="' . esc_url( $action['url'] ) . '" class="woocommerce-button button ' . sanitize_html_class( $key ) . '">' . esc_html( $action['name'] ) . '</a>';
} }
} }
?> ?>
@ -105,19 +105,19 @@ do_action( 'woocommerce_before_account_orders', $has_orders ); ?>
<?php do_action( 'woocommerce_before_account_orders_pagination' ); ?> <?php do_action( 'woocommerce_before_account_orders_pagination' ); ?>
<?php if ( 1 < $customer_orders->max_num_pages ) : ?> <?php if ( 1 < $customer_orders->max_num_pages ) : ?>
<div class="woocommerce-Pagination"> <div class="woocommerce-pagination woocommerce-pagination--without-numbers woocommerce-Pagination">
<?php if ( 1 !== $current_page ) : ?> <?php if ( 1 !== $current_page ) : ?>
<a class="woocommerce-Button woocommerce-Button--previous button" href="<?php echo esc_url( wc_get_endpoint_url( 'orders', $current_page - 1 ) ); ?>"><?php _e( 'Previous', 'woocommerce' ); ?></a> <a class="woocommerce-button woocommerce-button--previous woocommerce-Button woocommerce-Button--previous button" href="<?php echo esc_url( wc_get_endpoint_url( 'orders', $current_page - 1 ) ); ?>"><?php _e( 'Previous', 'woocommerce' ); ?></a>
<?php endif; ?> <?php endif; ?>
<?php if ( intval( $customer_orders->max_num_pages ) !== $current_page ) : ?> <?php if ( intval( $customer_orders->max_num_pages ) !== $current_page ) : ?>
<a class="woocommerce-Button woocommerce-Button--next button" href="<?php echo esc_url( wc_get_endpoint_url( 'orders', $current_page + 1 ) ); ?>"><?php _e( 'Next', 'woocommerce' ); ?></a> <a class="woocommerce-button woocommerce-button--next woocommerce-Button woocommerce-Button--next button" href="<?php echo esc_url( wc_get_endpoint_url( 'orders', $current_page + 1 ) ); ?>"><?php _e( 'Next', 'woocommerce' ); ?></a>
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php endif; ?> <?php endif; ?>
<?php else : ?> <?php else : ?>
<div class="woocommerce-Message woocommerce-Message--info woocommerce-info"> <div class="woocommerce-message woocommerce-message--info woocommerce-Message woocommerce-Message--info woocommerce-info">
<a class="woocommerce-Button button" href="<?php echo esc_url( apply_filters( 'woocommerce_return_to_shop_redirect', wc_get_page_permalink( 'shop' ) ) ); ?>"> <a class="woocommerce-Button button" href="<?php echo esc_url( apply_filters( 'woocommerce_return_to_shop_redirect', wc_get_page_permalink( 'shop' ) ) ); ?>">
<?php _e( 'Go shop', 'woocommerce' ) ?> <?php _e( 'Go shop', 'woocommerce' ) ?>
</a> </a>

View File

@ -31,7 +31,7 @@ if ( ! comments_open() ) {
<h2 class="woocommerce-Reviews-title"><?php <h2 class="woocommerce-Reviews-title"><?php
if ( get_option( 'woocommerce_enable_review_rating' ) === 'yes' && ( $count = $product->get_review_count() ) ) { if ( get_option( 'woocommerce_enable_review_rating' ) === 'yes' && ( $count = $product->get_review_count() ) ) {
/* translators: 1: reviews count 2: product name */ /* translators: 1: reviews count 2: product name */
printf( esc_html( _n( '%1$s review for %2$s', '%1$s reviews for %2$s', $count, 'woocommerce' ) ), $count, '<span>' . get_the_title() . '</span>' ); printf( esc_html( _n( '%1$s review for %2$s', '%1$s reviews for %2$s', $count, 'woocommerce' ) ), esc_html( $count ), '<span>' . get_the_title() . '</span>' );
} else { } else {
_e( 'Reviews', 'woocommerce' ); _e( 'Reviews', 'woocommerce' );
} }

View File

@ -33,7 +33,7 @@ do_action( 'woocommerce_before_add_to_cart_form' ); ?>
setup_postdata( $GLOBALS['post'] =& $post_object ); setup_postdata( $GLOBALS['post'] =& $post_object );
?> ?>
<tr> <tr id="product-<?php the_ID(); ?>" <?php post_class(); ?>>
<td> <td>
<?php if ( $grouped_product->is_sold_individually() || ! $grouped_product->is_purchasable() ) : ?> <?php if ( $grouped_product->is_sold_individually() || ! $grouped_product->is_purchasable() ) : ?>
<?php woocommerce_template_loop_add_to_cart(); ?> <?php woocommerce_template_loop_add_to_cart(); ?>

View File

@ -40,7 +40,7 @@ $image_title = $thumbnail_post->post_content;
if ( has_post_thumbnail() ) { if ( has_post_thumbnail() ) {
echo apply_filters( 'woocommerce_single_product_image_thumbnail_html', '<figure data-thumb="' . get_the_post_thumbnail_url( $post->ID, 'shop_thumbnail' ) . '" class="woocommerce-product-gallery__image">' . get_the_post_thumbnail( $post->ID, 'shop_single', $attributes ) . '</figure>' ); echo apply_filters( 'woocommerce_single_product_image_thumbnail_html', '<figure data-thumb="' . get_the_post_thumbnail_url( $post->ID, 'shop_thumbnail' ) . '" class="woocommerce-product-gallery__image">' . get_the_post_thumbnail( $post->ID, 'shop_single', $attributes ) . '</figure>' );
} else { } else {
echo sprintf( '<figure class="woocommerce-product-gallery__image--placeholder"><img src="%s" alt="%s" /></figure>', esc_url( wc_placeholder_img_src() ), esc_html__( 'Awaiting product image', 'woocommerce' ) ); echo sprintf( '<figure class="woocommerce-product-gallery__image--placeholder"><img src="%s" alt="%s" class="wp-post-image" /></figure>', esc_url( wc_placeholder_img_src() ), esc_html__( 'Awaiting product image', 'woocommerce' ) );
} }
do_action( 'woocommerce_product_thumbnails' ); do_action( 'woocommerce_product_thumbnails' );

View File

@ -20,7 +20,7 @@ if ( ! defined( 'ABSPATH' ) ) {
exit; exit;
} }
global $post, $product, $woocommerce; global $post, $product;
$attachment_ids = $product->get_gallery_image_ids(); $attachment_ids = $product->get_gallery_image_ids();

View File

@ -47,12 +47,12 @@ if ( $rating_count > 0 ) : ?>
/* translators: %s: rating count */ /* translators: %s: rating count */
printf( printf(
_n( 'based on %s customer rating', 'based on %s customer ratings', $rating_count, 'woocommerce' ), _n( 'based on %s customer rating', 'based on %s customer ratings', $rating_count, 'woocommerce' ),
'<span class="rating">' . $rating_count . '</span>' '<span class="rating">' . esc_html( $rating_count ) . '</span>'
); );
?> ?>
</span> </span>
</div> </div>
<?php if ( comments_open() ) : ?><a href="#reviews" class="woocommerce-review-link" rel="nofollow">(<?php printf( _n( '%s customer review', '%s customer reviews', $review_count, 'woocommerce' ), '<span class="count">' . $review_count . '</span>' ); ?>)</a><?php endif ?> <?php if ( comments_open() ) : ?><a href="#reviews" class="woocommerce-review-link" rel="nofollow">(<?php printf( _n( '%s customer review', '%s customer reviews', $review_count, 'woocommerce' ), '<span class="count">' . esc_html( $review_count ) . '</span>' ); ?>)</a><?php endif ?>
</div> </div>
<?php endif; ?> <?php endif; ?>

View File

@ -30,7 +30,7 @@ if [ $1 == 'before' ]; then
elif [ $1 == 'during' ]; then elif [ $1 == 'during' ]; then
## Only run on 7.0 box. ## Only run on 7.0 box.
if [[ ${TRAVIS_PHP_VERSION:0:3} == "7.0" ]]; then if [ $TRAVIS_PHP_VERSION == "7.0" ]; then
# WordPress Coding Standards. # WordPress Coding Standards.
# @link https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards # @link https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards
# @link http://pear.php.net/package/PHP_CodeSniffer/ # @link http://pear.php.net/package/PHP_CodeSniffer/
@ -50,8 +50,7 @@ elif [ $1 == 'during' ]; then
elif [ $1 == 'after' ]; then elif [ $1 == 'after' ]; then
## Only run on 7.0 box. ## Only run on 7.0 box.
if [[ ${TRAVIS_PHP_VERSION:0:3} == "7.0" ]]; then if [ $TRAVIS_PHP_VERSION == "7.0" ]; then
# Get scrutinizer ocular and run it
wget https://scrutinizer-ci.com/ocular.phar wget https://scrutinizer-ci.com/ocular.phar
chmod +x ocular.phar chmod +x ocular.phar
php ocular.phar code-coverage:upload --format=php-clover ./tmp/clover.xml php ocular.phar code-coverage:upload --format=php-clover ./tmp/clover.xml

View File

@ -30,7 +30,8 @@ class WC_API_Unit_Test_Case extends WC_Unit_Test_Case {
$_SERVER['REQUEST_METHOD'] = null; $_SERVER['REQUEST_METHOD'] = null;
// mock the API server to prevent headers from being sent // mock the API server to prevent headers from being sent
$this->mock_server = $this->getMock( 'WC_API_Server', array( 'header' ), array( '/' ) ); // $this->mock_server = $this->getMock( 'WC_API_Server', array( 'header' ), array( '/' ) );
$this->mock_server = $this->getMockBuilder( 'WC_API_Server' )->setMethods( array( 'header' ) )->disableOriginalConstructor()->getMock();
WC()->api->register_resources( $this->mock_server ); WC()->api->register_resources( $this->mock_server );
} }

View File

@ -30,14 +30,20 @@ class WC_Helper_Order {
/** /**
* Create a order. * Create a order.
* *
* @since 2.4 * @since 2.4
* @version 2.7 New parameter $product.
* *
* @return WC_Order Order object. * @param int $customer_id
* @param WC_Product $product
*
* @return WC_Order
*/ */
public static function create_order( $customer_id = 1 ) { public static function create_order( $customer_id = 1, $product = null ) {
if ( ! is_a( $product, 'WC_Product' ) ) {
$product = WC_Helper_Product::create_simple_product();
}
// Create product
$product = WC_Helper_Product::create_simple_product();
WC_Helper_Shipping::create_simple_flat_rate(); WC_Helper_Shipping::create_simple_flat_rate();
$order_data = array( $order_data = array(

View File

@ -55,11 +55,11 @@ class WC_Tests_API_Coupons extends WC_REST_Unit_Test_Case {
'date_modified' => wc_rest_prepare_date_response( $post_1->post_modified_gmt ), 'date_modified' => wc_rest_prepare_date_response( $post_1->post_modified_gmt ),
'discount_type' => 'fixed_cart', 'discount_type' => 'fixed_cart',
'description' => 'This is a dummy coupon', 'description' => 'This is a dummy coupon',
'date_expires' => '', 'expiry_date' => '',
'usage_count' => 0, 'usage_count' => 0,
'individual_use' => false, 'individual_use' => false,
'product_ids' => array(), 'product_ids' => array(),
'excluded_product_ids' => array(), 'exclude_product_ids' => array(),
'usage_limit' => '', 'usage_limit' => '',
'usage_limit_per_user' => '', 'usage_limit_per_user' => '',
'limit_usage_to_x_items' => 0, 'limit_usage_to_x_items' => 0,
@ -117,11 +117,11 @@ class WC_Tests_API_Coupons extends WC_REST_Unit_Test_Case {
'date_modified' => wc_rest_prepare_date_response( $post->post_modified_gmt ), 'date_modified' => wc_rest_prepare_date_response( $post->post_modified_gmt ),
'discount_type' => 'fixed_cart', 'discount_type' => 'fixed_cart',
'description' => 'This is a dummy coupon', 'description' => 'This is a dummy coupon',
'date_expires' => null, 'expiry_date' => null,
'usage_count' => 0, 'usage_count' => 0,
'individual_use' => false, 'individual_use' => false,
'product_ids' => array(), 'product_ids' => array(),
'excluded_product_ids' => array(), 'exclude_product_ids' => array(),
'usage_limit' => null, 'usage_limit' => null,
'usage_limit_per_user' => null, 'usage_limit_per_user' => null,
'limit_usage_to_x_items' => 0, 'limit_usage_to_x_items' => 0,
@ -184,11 +184,11 @@ class WC_Tests_API_Coupons extends WC_REST_Unit_Test_Case {
'date_modified' => $data['date_modified'], 'date_modified' => $data['date_modified'],
'discount_type' => 'fixed_product', 'discount_type' => 'fixed_product',
'description' => 'Test', 'description' => 'Test',
'date_expires' => null, 'expiry_date' => null,
'usage_count' => 0, 'usage_count' => 0,
'individual_use' => false, 'individual_use' => false,
'product_ids' => array(), 'product_ids' => array(),
'excluded_product_ids' => array(), 'exclude_product_ids' => array(),
'usage_limit' => 10, 'usage_limit' => 10,
'usage_limit_per_user' => null, 'usage_limit_per_user' => null,
'limit_usage_to_x_items' => 0, 'limit_usage_to_x_items' => 0,
@ -414,11 +414,11 @@ class WC_Tests_API_Coupons extends WC_REST_Unit_Test_Case {
$this->assertArrayHasKey( 'description', $properties ); $this->assertArrayHasKey( 'description', $properties );
$this->assertArrayHasKey( 'discount_type', $properties ); $this->assertArrayHasKey( 'discount_type', $properties );
$this->assertArrayHasKey( 'amount', $properties ); $this->assertArrayHasKey( 'amount', $properties );
$this->assertArrayHasKey( 'date_expires', $properties ); $this->assertArrayHasKey( 'expiry_date', $properties );
$this->assertArrayHasKey( 'usage_count', $properties ); $this->assertArrayHasKey( 'usage_count', $properties );
$this->assertArrayHasKey( 'individual_use', $properties ); $this->assertArrayHasKey( 'individual_use', $properties );
$this->assertArrayHasKey( 'product_ids', $properties ); $this->assertArrayHasKey( 'product_ids', $properties );
$this->assertArrayHasKey( 'excluded_product_ids', $properties ); $this->assertArrayHasKey( 'exclude_product_ids', $properties );
$this->assertArrayHasKey( 'usage_limit', $properties ); $this->assertArrayHasKey( 'usage_limit', $properties );
$this->assertArrayHasKey( 'usage_limit_per_user', $properties ); $this->assertArrayHasKey( 'usage_limit_per_user', $properties );
$this->assertArrayHasKey( 'limit_usage_to_x_items', $properties ); $this->assertArrayHasKey( 'limit_usage_to_x_items', $properties );

View File

@ -373,7 +373,8 @@ class Settings extends WC_REST_Unit_Test_Case {
* @since 2.7.0 * @since 2.7.0
*/ */
public function test_get_setting_invalid_setting_type() { public function test_get_setting_invalid_setting_type() {
$controller = $this->getMock( 'WC_Rest_Settings_Options_Controller', array( 'get_group_settings', 'is_setting_type_valid' ) ); // $controller = $this->getMock( 'WC_Rest_Settings_Options_Controller', array( 'get_group_settings', 'is_setting_type_valid' ) );
$controller = $this->getMockBuilder( 'WC_Rest_Settings_Options_Controller' )->setMethods( array( 'get_group_settings', 'is_setting_type_valid' ) )->getMock();
$controller $controller
->expects( $this->any() ) ->expects( $this->any() )

View File

@ -21,7 +21,8 @@ class WC_Tests_Register_WP_Admin_Settings extends WC_Unit_Test_Case {
public function setUp() { public function setUp() {
parent::setUp(); parent::setUp();
$mock_page = $this->getMock( 'WC_Settings_General' ); // $mock_page = $this->getMock( 'WC_Settings_General' );
$mock_page = $this->getMockBuilder( 'WC_Settings_General' )->getMock();
$mock_page $mock_page
->expects( $this->any() ) ->expects( $this->any() )

View File

@ -92,7 +92,7 @@ class WC_Tests_Core_Functions extends WC_Unit_Test_Case {
'INR' => 'Indian rupee', 'INR' => 'Indian rupee',
'IQD' => 'Iraqi dinar', 'IQD' => 'Iraqi dinar',
'IRR' => 'Iranian rial', 'IRR' => 'Iranian rial',
'IRT' => 'Iranian Toman', 'IRT' => 'Iranian toman',
'ISK' => 'Icelandic kr&oacute;na', 'ISK' => 'Icelandic kr&oacute;na',
'JEP' => 'Jersey pound', 'JEP' => 'Jersey pound',
'JMD' => 'Jamaican dollar', 'JMD' => 'Jamaican dollar',

View File

@ -10,7 +10,7 @@ class WC_Tests_Install extends WC_Unit_Test_Case {
* Test check version. * Test check version.
*/ */
public function test_check_version() { public function test_check_version() {
update_option( 'woocommerce_version', WC()->version - 1 ); update_option( 'woocommerce_version', ( (float) WC()->version - 1 ) );
update_option( 'woocommerce_db_version', WC()->version ); update_option( 'woocommerce_db_version', WC()->version );
WC_Install::check_version(); WC_Install::check_version();