diff --git a/assets/js/frontend/cart.js b/assets/js/frontend/cart.js
index e6ae0396dc0..0ccf9ec6bff 100644
--- a/assets/js/frontend/cart.js
+++ b/assets/js/frontend/cart.js
@@ -6,35 +6,237 @@ jQuery( function( $ ) {
return false;
}
- // Shipping calculator
- $( document ).on( 'click', '.shipping-calculator-button', function() {
- $( '.shipping-calculator-form' ).slideToggle( 'slow' );
- return false;
- }).on( 'change', 'select.shipping_method, input[name^=shipping_method]', function() {
- var shipping_methods = [];
+ // Gets a url for a given AJAX endpoint.
+ var get_url = function( endpoint ) {
+ return wc_cart_params.wc_ajax_url.toString().replace(
+ '%%endpoint%%',
+ endpoint
+ );
+ };
- $( 'select.shipping_method, input[name^=shipping_method][type=radio]:checked, input[name^=shipping_method][type=hidden]' ).each( function() {
- shipping_methods[ $( this ).data( 'index' ) ] = $( this ).val();
- });
+ // Check if a node is blocked for processing.
+ var is_blocked = function( $node ) {
+ return $node.is( '.processing' );
+ };
- $( 'div.cart_totals' ).block({
+ // Block a node for processing.
+ var block = function( $node ) {
+ $node.addClass( 'processing' ).block( {
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
- });
+ } );
+ };
+
+ // Unblock a node after processing is complete.
+ var unblock = function( $node ) {
+ $node.removeClass( 'processing' ).unblock();
+ };
+
+ // Updates the .woocommerce div with a string of html.
+ var update_wc_div = function( html_str ) {
+ var $html = $.parseHTML( html_str );
+ var $new_div = $( 'div.woocommerce', $html );
+ $( 'div.woocommerce' ).replaceWith( $new_div );
+ };
+
+ // Shipping calculator
+ $( document ).on( 'click', '.shipping-calculator-button', function() {
+ $( '.shipping-calculator-form' ).slideToggle( 'slow' );
+ return false;
+ } ).on( 'change', 'select.shipping_method, input[name^=shipping_method]', function() {
+ var shipping_methods = [];
+
+ $( 'select.shipping_method, input[name^=shipping_method][type=radio]:checked, input[name^=shipping_method][type=hidden]' ).each( function() {
+ shipping_methods[ $( this ).data( 'index' ) ] = $( this ).val();
+ } );
+
+ block( $( 'div.cart_totals' ) );
var data = {
security: wc_cart_params.update_shipping_method_nonce,
shipping_method: shipping_methods
};
- $.post( wc_cart_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'update_shipping_method' ), data, function( response ) {
+ $.post( get_url( 'update_shipping_method' ), data, function( response ) {
$( 'div.cart_totals' ).replaceWith( response );
$( document.body ).trigger( 'updated_shipping_method' );
- });
- });
+ } );
+ } );
+
+ $( document ).on( 'submit', 'form.woocommerce-shipping-calculator', function( evt ) {
+ evt.preventDefault();
+
+ var $form = $( evt.target );
+
+ block( $form );
+
+ // Provide the submit button value because wc-form-handler expects it.
+ $( '' ).attr( 'type', 'hidden' )
+ .attr( 'name', 'calc_shipping' )
+ .attr( 'value', 'x' )
+ .appendTo( $form );
+
+ // Make call to actual form post URL.
+ $.ajax( {
+ type: $form.attr( 'method' ),
+ url: $form.attr( 'action' ),
+ data: $form.serialize(),
+ dataType: 'html',
+ success: function( response ) {
+ update_wc_div(response );
+ },
+ complete: function() {
+ unblock( $form );
+ }
+ } );
+ } );
$( '.shipping-calculator-form' ).hide();
-});
+
+ // Update the cart after something has changed.
+ var update_cart_totals = function() {
+ block( $( 'div.cart_totals' ) );
+
+ $.ajax( {
+ url: get_url( 'get_cart_totals' ),
+ dataType: 'html',
+ success: function( response ) {
+ $( 'div.cart_totals' ).replaceWith( response );
+ }
+ } );
+ };
+
+ // clears previous notices and shows new one above form.
+ var show_notice = function( html_element ) {
+ var $form = $( 'div.woocommerce > form' );
+
+ $( '.woocommerce-error, .woocommerce-message' ).remove();
+ $form.before( html_element );
+ };
+
+ // Handle form submit and route to correct logic.
+ $( document ).on( 'submit', 'div.woocommerce > form', function( evt ) {
+ evt.preventDefault();
+
+ var $form = $( evt.target );
+ var $submit = $( document.activeElement );
+
+ window.console.log( $submit );
+
+ if ( is_blocked( $form ) ) {
+ return false;
+ }
+
+ if ( $submit.is( '[name="update_cart"]' ) || $submit.is( 'input.qty' ) ) {
+ window.console.log( 'update cart' );
+ quantity_update( $form );
+
+ } else if ( $submit.is( '[name="apply_coupon"]' ) || $submit.is( '#coupon_code' ) ) {
+ window.console.log( 'apply coupon' );
+ apply_coupon( $form );
+ }
+ } );
+
+ // Coupon code
+ var apply_coupon = function( $form ) {
+ block( $form );
+
+ var $text_field = $( '#coupon_code' );
+ var coupon_code = $text_field.val();
+
+ var data = {
+ security: wc_cart_params.apply_coupon_nonce,
+ coupon_code: coupon_code
+ };
+
+ $.ajax( {
+ type: 'POST',
+ url: get_url( 'apply_coupon' ),
+ data: data,
+ dataType: 'html',
+ success: function( response ) {
+ show_notice( response );
+ },
+ complete: function() {
+ unblock( $form );
+ $text_field.val( '' );
+ update_cart_totals();
+ }
+ } );
+ };
+
+ $( document ).on( 'click', 'a.woocommerce-remove-coupon', function( evt ) {
+ evt.preventDefault();
+
+ var $tr = $( this ).parents( 'tr' );
+ var coupon = $( this ).attr( 'data-coupon' );
+
+ block( $tr.parents( 'table' ) );
+
+ var data = {
+ security: wc_cart_params.remove_coupon_nonce,
+ coupon: coupon
+ };
+
+ $.ajax( {
+ type: 'POST',
+ url: get_url( 'remove_coupon' ),
+ data: data,
+ dataType: 'html',
+ success: function( response ) {
+ show_notice( response );
+ unblock( $tr.parents( 'table' ) );
+ },
+ complete: function() {
+ update_cart_totals();
+ }
+ } );
+ } );
+
+ // Quantity Update
+ var quantity_update = function( $form ) {
+
+ // Provide the submit button value because wc-form-handler expects it.
+ $( '' ).attr( 'type', 'hidden' )
+ .attr( 'name', 'update_cart' )
+ .attr( 'value', 'Update Cart' )
+ .appendTo( $form );
+
+ block( $form );
+
+ // Make call to actual form post URL.
+ $.ajax( {
+ type: $form.attr( 'method' ),
+ url: $form.attr( 'action' ),
+ data: $form.serialize(),
+ dataType: 'html',
+ success: update_wc_div,
+ complete: function() {
+ unblock( $form );
+ }
+ } );
+ };
+
+ // Item Remove
+ $( document ).on( 'click', 'td.product-remove > a', function( evt ) {
+ evt.preventDefault();
+
+ var $a = $( evt.target );
+ var $form = $a.parents( 'form' );
+
+ block( $form );
+
+ $.ajax( {
+ type: 'GET',
+ url: $a.attr( 'href' ),
+ dataType: 'html',
+ success: update_wc_div,
+ complete: function() {
+ unblock( $form );
+ }
+ } );
+ } );
+} );
diff --git a/assets/js/frontend/cart.min.js b/assets/js/frontend/cart.min.js
index a1a3317cac9..da0aa9928e4 100644
--- a/assets/js/frontend/cart.min.js
+++ b/assets/js/frontend/cart.min.js
@@ -1 +1 @@
-jQuery(function(a){return"undefined"==typeof wc_cart_params?!1:(a(document).on("click",".shipping-calculator-button",function(){return a(".shipping-calculator-form").slideToggle("slow"),!1}).on("change","select.shipping_method, input[name^=shipping_method]",function(){var b=[];a("select.shipping_method, input[name^=shipping_method][type=radio]:checked, input[name^=shipping_method][type=hidden]").each(function(){b[a(this).data("index")]=a(this).val()}),a("div.cart_totals").block({message:null,overlayCSS:{background:"#fff",opacity:.6}});var c={security:wc_cart_params.update_shipping_method_nonce,shipping_method:b};a.post(wc_cart_params.wc_ajax_url.toString().replace("%%endpoint%%","update_shipping_method"),c,function(b){a("div.cart_totals").replaceWith(b),a(document.body).trigger("updated_shipping_method")})}),void a(".shipping-calculator-form").hide())});
\ No newline at end of file
+jQuery(function(a){if("undefined"==typeof wc_cart_params)return!1;var b=function(a){return wc_cart_params.wc_ajax_url.toString().replace("%%endpoint%%",a)},c=function(a){return a.is(".processing")},d=function(a){a.addClass("processing").block({message:null,overlayCSS:{background:"#fff",opacity:.6}})},e=function(a){a.removeClass("processing").unblock()},f=function(b){var c=a.parseHTML(b),d=a("div.woocommerce",c);a("div.woocommerce").replaceWith(d)};a(document).on("click",".shipping-calculator-button",function(){return a(".shipping-calculator-form").slideToggle("slow"),!1}).on("change","select.shipping_method, input[name^=shipping_method]",function(){var c=[];a("select.shipping_method, input[name^=shipping_method][type=radio]:checked, input[name^=shipping_method][type=hidden]").each(function(){c[a(this).data("index")]=a(this).val()}),d(a("div.cart_totals"));var e={security:wc_cart_params.update_shipping_method_nonce,shipping_method:c};a.post(b("update_shipping_method"),e,function(b){a("div.cart_totals").replaceWith(b),a(document.body).trigger("updated_shipping_method")})}),a(document).on("submit","form.woocommerce-shipping-calculator",function(b){b.preventDefault();var c=a(b.target);d(c),a("").attr("type","hidden").attr("name","calc_shipping").attr("value","x").appendTo(c),a.ajax({type:c.attr("method"),url:c.attr("action"),data:c.serialize(),dataType:"html",success:function(a){f(a)},complete:function(){e(c)}})}),a(".shipping-calculator-form").hide();var g=function(){d(a("div.cart_totals")),a.ajax({url:b("get_cart_totals"),dataType:"html",success:function(b){a("div.cart_totals").replaceWith(b)}})},h=function(b){var c=a("div.woocommerce > form");a(".woocommerce-error, .woocommerce-message").remove(),c.before(b)};a(document).on("submit","div.woocommerce > form",function(b){b.preventDefault();var d=a(b.target),e=a(document.activeElement);return window.console.log(e),c(d)?!1:void(e.is('[name="update_cart"]')||e.is("input.qty")?(window.console.log("update cart"),j(d)):(e.is('[name="apply_coupon"]')||e.is("#coupon_code"))&&(window.console.log("apply coupon"),i(d)))});var i=function(c){d(c);var f=a("#coupon_code"),i=f.val(),j={security:wc_cart_params.apply_coupon_nonce,coupon_code:i};a.ajax({type:"POST",url:b("apply_coupon"),data:j,dataType:"html",success:function(a){h(a)},complete:function(){e(c),f.val(""),g()}})};a(document).on("click","a.woocommerce-remove-coupon",function(c){c.preventDefault();var f=a(this).parents("tr"),i=a(this).attr("data-coupon");d(f.parents("table"));var j={security:wc_cart_params.remove_coupon_nonce,coupon:i};a.ajax({type:"POST",url:b("remove_coupon"),data:j,dataType:"html",success:function(a){h(a),e(f.parents("table"))},complete:function(){g()}})});var j=function(b){a("").attr("type","hidden").attr("name","update_cart").attr("value","Update Cart").appendTo(b),d(b),a.ajax({type:b.attr("method"),url:b.attr("action"),data:b.serialize(),dataType:"html",success:f,complete:function(){e(b)}})};a(document).on("click","td.product-remove > a",function(b){b.preventDefault();var c=a(b.target),g=c.parents("form");d(g),a.ajax({type:"GET",url:c.attr("href"),dataType:"html",success:f,complete:function(){e(g)}})})});
\ No newline at end of file
diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php
index bb10caa2760..b2e90ddddb1 100644
--- a/includes/class-wc-ajax.php
+++ b/includes/class-wc-ajax.php
@@ -94,6 +94,7 @@ class WC_AJAX {
'apply_coupon' => true,
'remove_coupon' => true,
'update_shipping_method' => true,
+ 'get_cart_totals' => true,
'update_order_review' => true,
'add_to_cart' => true,
'checkout' => true,
@@ -254,6 +255,22 @@ class WC_AJAX {
die();
}
+ /**
+ * AJAX receive updated cart_totals div.
+ */
+ public static function get_cart_totals() {
+
+ if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
+ define( 'WOOCOMMERCE_CART', true );
+ }
+
+ WC()->cart->calculate_totals();
+
+ woocommerce_cart_totals();
+
+ die();
+ }
+
/**
* AJAX update order review on checkout.
*/
diff --git a/includes/class-wc-checkout.php b/includes/class-wc-checkout.php
index 51fb97bdee1..7f99fef889b 100644
--- a/includes/class-wc-checkout.php
+++ b/includes/class-wc-checkout.php
@@ -765,18 +765,22 @@ class WC_Checkout {
}
// Get the billing_ and shipping_ address fields
- $address_fields = array_merge( WC()->countries->get_address_fields(), WC()->countries->get_address_fields( '', 'shipping_' ) );
+ if ( isset( $this->checkout_fields['shipping'] ) && isset( $this->checkout_fields['billing'] ) ) {
- if ( is_user_logged_in() && array_key_exists( $input, $address_fields ) ) {
- $current_user = wp_get_current_user();
+ $address_fields = array_merge( $this->checkout_fields['billing'], $this->checkout_fields['shipping'] );
- if ( $meta = get_user_meta( $current_user->ID, $input, true ) ) {
- return $meta;
+ if ( is_user_logged_in() && is_array( $address_fields ) && array_key_exists( $input, $address_fields ) ) {
+ $current_user = wp_get_current_user();
+
+ if ( $meta = get_user_meta( $current_user->ID, $input, true ) ) {
+ return $meta;
+ }
+
+ if ( $input == 'billing_email' ) {
+ return $current_user->user_email;
+ }
}
- if ( $input == 'billing_email' ) {
- return $current_user->user_email;
- }
}
switch ( $input ) {
diff --git a/includes/class-wc-frontend-scripts.php b/includes/class-wc-frontend-scripts.php
index 10ca99535db..82a23703a47 100644
--- a/includes/class-wc-frontend-scripts.php
+++ b/includes/class-wc-frontend-scripts.php
@@ -289,6 +289,8 @@ class WC_Frontend_Scripts {
'ajax_url' => WC()->ajax_url(),
'wc_ajax_url' => WC_AJAX::get_endpoint( "%%endpoint%%" ),
'update_shipping_method_nonce' => wp_create_nonce( "update-shipping-method" ),
+ 'apply_coupon_nonce' => wp_create_nonce( "apply-coupon" ),
+ 'remove_coupon_nonce' => wp_create_nonce( "remove-coupon" ),
);
break;
case 'wc-cart-fragments' :
diff --git a/includes/class-wc-product-variable.php b/includes/class-wc-product-variable.php
index c8c09b7b07d..6fc6a549ae8 100644
--- a/includes/class-wc-product-variable.php
+++ b/includes/class-wc-product-variable.php
@@ -376,68 +376,54 @@ class WC_Product_Variable extends WC_Product {
* @return array of attributes and their available values
*/
public function get_variation_attributes() {
+ global $wpdb;
+
$variation_attributes = array();
+ $attributes = $this->get_attributes();
+ $child_ids = $this->get_children( true );
- if ( ! $this->has_child() ) {
- return $variation_attributes;
- }
-
- $attributes = $this->get_attributes();
-
- foreach ( $attributes as $attribute ) {
- if ( ! $attribute['is_variation'] ) {
- continue;
- }
-
- $values = array();
- $attribute_field_name = 'attribute_' . sanitize_title( $attribute['name'] );
-
- // Get used values from children variations
- foreach ( $this->get_children() as $child_id ) {
- $variation = $this->get_child( $child_id );
-
- if ( ! empty( $variation->variation_id ) ) {
- if ( ! $variation->variation_is_visible() ) {
- continue; // Disabled or hidden
- }
-
- $child_variation_attributes = $variation->get_variation_attributes();
-
- if ( isset( $child_variation_attributes[ $attribute_field_name ] ) ) {
- $values[] = $child_variation_attributes[ $attribute_field_name ];
- }
+ if ( ! empty( $child_ids ) ) {
+ foreach ( $attributes as $attribute ) {
+ if ( empty( $attribute['is_variation'] ) ) {
+ continue;
}
- }
- // empty value indicates that all options for given attribute are available
- if ( in_array( '', $values ) ) {
- $values = $attribute['is_taxonomy'] ? wp_get_post_terms( $this->id, $attribute['name'], array( 'fields' => 'slugs' ) ) : wc_get_text_attributes( $attribute['value'] );
+ // Get possible values for this attribute, for only visible variations.
+ $values = array_unique( $wpdb->get_col( $wpdb->prepare(
+ "SELECT meta_value FROM {$wpdb->postmeta} WHERE meta_key = %s AND post_id IN (" . implode( ',', array_map( 'esc_sql', $child_ids ) ) . ")",
+ wc_variation_attribute_name( $attribute['name'] )
+ ) ) );
- // Get custom attributes (non taxonomy) as defined
- } elseif ( ! $attribute['is_taxonomy'] ) {
- $text_attributes = wc_get_text_attributes( $attribute['value'] );
- $assigned_text_attributes = $values;
- $values = array();
+ // empty value indicates that all options for given attribute are available
+ if ( in_array( '', $values ) ) {
+ $values = $attribute['is_taxonomy'] ? wp_get_post_terms( $this->id, $attribute['name'], array( 'fields' => 'slugs' ) ) : wc_get_text_attributes( $attribute['value'] );
- // Pre 2.4 handling where 'slugs' were saved instead of the full text attribute
- if ( version_compare( get_post_meta( $this->id, '_product_version', true ), '2.4.0', '<' ) ) {
- $assigned_text_attributes = array_map( 'sanitize_title', $assigned_text_attributes );
+ // Get custom attributes (non taxonomy) as defined
+ } elseif ( ! $attribute['is_taxonomy'] ) {
+ $text_attributes = wc_get_text_attributes( $attribute['value'] );
+ $assigned_text_attributes = $values;
+ $values = array();
- foreach ( $text_attributes as $text_attribute ) {
- if ( in_array( sanitize_title( $text_attribute ), $assigned_text_attributes ) ) {
- $values[] = $text_attribute;
+ // Pre 2.4 handling where 'slugs' were saved instead of the full text attribute
+ if ( version_compare( get_post_meta( $this->id, '_product_version', true ), '2.4.0', '<' ) ) {
+ $assigned_text_attributes = array_map( 'sanitize_title', $assigned_text_attributes );
+
+ foreach ( $text_attributes as $text_attribute ) {
+ if ( in_array( sanitize_title( $text_attribute ), $assigned_text_attributes ) ) {
+ $values[] = $text_attribute;
+ }
}
- }
- } else {
- foreach ( $text_attributes as $text_attribute ) {
- if ( in_array( $text_attribute, $assigned_text_attributes ) ) {
- $values[] = $text_attribute;
+ } else {
+ foreach ( $text_attributes as $text_attribute ) {
+ if ( in_array( $text_attribute, $assigned_text_attributes ) ) {
+ $values[] = $text_attribute;
+ }
}
}
}
- }
- $variation_attributes[ $attribute['name'] ] = array_unique( $values );
+ $variation_attributes[ $attribute['name'] ] = array_unique( $values );
+ }
}
return $variation_attributes;
diff --git a/includes/class-wc-shipping-zone.php b/includes/class-wc-shipping-zone.php
index f2dd568d246..11e90179696 100644
--- a/includes/class-wc-shipping-zone.php
+++ b/includes/class-wc-shipping-zone.php
@@ -249,7 +249,7 @@ class WC_Shipping_Zone implements WC_Data {
}
}
- return $methods;
+ return apply_filters( 'woocommerce_shipping_zone_shipping_methods', $methods, $raw_methods, $allowed_classes, $this );
}
/**
diff --git a/includes/shortcodes/class-wc-shortcode-checkout.php b/includes/shortcodes/class-wc-shortcode-checkout.php
index 1259c8e2d33..ac59a99ee20 100644
--- a/includes/shortcodes/class-wc-shortcode-checkout.php
+++ b/includes/shortcodes/class-wc-shortcode-checkout.php
@@ -198,8 +198,9 @@ class WC_Shortcode_Checkout {
if ( $order_id > 0 ) {
$order = wc_get_order( $order_id );
- if ( $order->order_key != $order_key )
- unset( $order );
+ if ( $order->order_key != $order_key ) {
+ $order = false;
+ }
}
// Empty awaiting payment session
diff --git a/includes/wc-attribute-functions.php b/includes/wc-attribute-functions.php
index 8e2b025d157..6fc38fa0b1e 100644
--- a/includes/wc-attribute-functions.php
+++ b/includes/wc-attribute-functions.php
@@ -49,6 +49,17 @@ function wc_attribute_taxonomy_name( $attribute_name ) {
return 'pa_' . wc_sanitize_taxonomy_name( $attribute_name );
}
+/**
+ * Get the attribute name used when storing values in post meta.
+ *
+ * @param string $attribute_name Attribute name.
+ * @since 2.6.0
+ * @return string
+ */
+function wc_variation_attribute_name( $attribute_name ) {
+ return 'attribute_' . sanitize_title( $attribute_name );
+}
+
/**
* Get a product attribute name by ID.
*
diff --git a/tests/unit-tests/util/core-functions.php b/tests/unit-tests/util/core-functions.php
index 00d1c424a0f..5567014857f 100644
--- a/tests/unit-tests/util/core-functions.php
+++ b/tests/unit-tests/util/core-functions.php
@@ -153,5 +153,17 @@ class Core_Functions extends \WC_Unit_Test_Case {
$this->assertEquals( '', $default['state'] );
}
+ /**
+ * Test wc_format_country_state_string().
+ *
+ * @since 2.6.0
+ */
+ public function test_wc_format_country_state_string() {
+ // Test with correct values.
+ $this->assertEquals( array( 'country' => 'US', 'state' => 'CA' ), wc_format_country_state_string( 'US:CA' ) );
+ // Test what happens when we pass an incorrect value.
+ $this->assertEquals( array( 'country' => 'US-CA', 'state' => '' ), wc_format_country_state_string( 'US-CA' ) );
+ }
+
}