WooCommerce - Version 3.0.3

Version Description

  • 2017-04-13 =
  • Fix - Fixed an issue with variation tax-classes when set to 'parent'. This made taxes apply on top of the tax inclusive price in certain setups.
  • Fix - Escaped attribute translations in the cart.php template and bumped the template version to match.
  • Fix - Corrected the display of refund dates on the order screen.
  • Fix - Fixed the grouped product visibility check in the grouped.php template and bumped the template version to match.
  • Fix - Fixed the sale badge display for grouped products.
  • Fix - Added the itemReviewed structured data for product reviews to make it validate.
  • Fix - Made the get_attribute method work on variation objects.
  • Tweak - Turned off the deferred email sending by default which was added in 3.0. Whilst it does improve performance, there were compatibility problems on some servers. It can be enabled with a filter if desired.
  • Dev - Added backtrace information to the deprecation messages to help find problem plugins.
Download this release

Release Info

Developer claudiosanches
Plugin Icon 128x128 WooCommerce
Version 3.0.3
Comparing to
See all releases

Code changes from version 3.0.2 to 3.0.3

i18n/languages/woocommerce.pot CHANGED
@@ -4,7 +4,7 @@ msgid ""
4
  msgstr ""
5
  "Project-Id-Version: WooCommerce 3.0.2\n"
6
  "Report-Msgid-Bugs-To: https://github.com/woocommerce/woocommerce/issues\n"
7
- "POT-Creation-Date: 2017-04-12 18:59:45+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=utf-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
@@ -6712,7 +6712,7 @@ msgstr ""
6712
  #: includes/admin/class-wc-admin-menus.php:80
6713
  #: includes/admin/settings/class-wc-settings-api.php:45
6714
  #: includes/admin/views/html-admin-page-status-report.php:432
6715
- #: includes/class-wc-install.php:883
6716
  msgid "Settings"
6717
  msgstr ""
6718
 
@@ -7703,13 +7703,13 @@ msgstr ""
7703
 
7704
  #: includes/admin/class-wc-admin-profile.php:84
7705
  #: includes/admin/meta-boxes/class-wc-meta-box-order-data.php:86
7706
- #: includes/class-wc-countries.php:1111 includes/class-wc-emails.php:412
7707
  msgid "Phone"
7708
  msgstr ""
7709
 
7710
  #: includes/admin/class-wc-admin-profile.php:88
7711
  #: includes/admin/meta-boxes/class-wc-meta-box-order-data.php:83
7712
- #: includes/class-wc-countries.php:1120 includes/class-wc-emails.php:405
7713
  #: includes/class-wc-form-handler.php:197
7714
  #: templates/myaccount/form-edit-account.php:40
7715
  #: templates/myaccount/form-login.php:91
@@ -15652,7 +15652,7 @@ msgid "Sessions successfully cleared"
15652
  msgstr ""
15653
 
15654
  #: includes/api/class-wc-rest-system-status-tools-controller.php:430
15655
- msgid "All missing WooCommerce pages was installed successfully."
15656
  msgstr ""
15657
 
15658
  #: includes/api/class-wc-rest-system-status-tools-controller.php:437
@@ -17720,33 +17720,33 @@ msgstr ""
17720
  msgid "Go to shop"
17721
  msgstr ""
17722
 
17723
- #: includes/class-wc-emails.php:398
17724
  msgid "Note"
17725
  msgstr ""
17726
 
17727
- #: includes/class-wc-emails.php:458
17728
  msgid "Product low in stock"
17729
  msgstr ""
17730
 
17731
- #: includes/class-wc-emails.php:461
17732
  #. translators: 1: product name 2: items in stock
17733
  msgid "%1$s is low in stock. There are %2$d left."
17734
  msgstr ""
17735
 
17736
- #: includes/class-wc-emails.php:485
17737
  msgid "Product out of stock"
17738
  msgstr ""
17739
 
17740
- #: includes/class-wc-emails.php:487
17741
  #. translators: %s: product name
17742
  msgid "%s is out of stock."
17743
  msgstr ""
17744
 
17745
- #: includes/class-wc-emails.php:516
17746
  msgid "Product backorder"
17747
  msgstr ""
17748
 
17749
- #: includes/class-wc-emails.php:517
17750
  msgid "%1$s units of %2$s have been backordered in order #%3$s."
17751
  msgstr ""
17752
 
@@ -17936,41 +17936,41 @@ msgstr ""
17936
  msgid "Shop manager"
17937
  msgstr ""
17938
 
17939
- #: includes/class-wc-install.php:883
17940
  msgid "View WooCommerce settings"
17941
  msgstr ""
17942
 
17943
- #: includes/class-wc-install.php:899
17944
  msgid "View WooCommerce documentation"
17945
  msgstr ""
17946
 
17947
- #: includes/class-wc-install.php:899
17948
  msgid "Docs"
17949
  msgstr ""
17950
 
17951
- #: includes/class-wc-install.php:900
17952
  msgid "View WooCommerce API docs"
17953
  msgstr ""
17954
 
17955
- #: includes/class-wc-install.php:900
17956
  msgid "API docs"
17957
  msgstr ""
17958
 
17959
- #: includes/class-wc-install.php:901
17960
  msgid "Visit premium customer support"
17961
  msgstr ""
17962
 
17963
- #: includes/class-wc-install.php:901
17964
  msgid "Premium support"
17965
  msgstr ""
17966
 
17967
- #: includes/class-wc-install.php:1043
17968
  msgid ""
17969
  "%1$s could not be installed (%2$s). <a href=\"%3$s\">Please install it "
17970
  "manually by clicking here.</a>"
17971
  msgstr ""
17972
 
17973
- #: includes/class-wc-install.php:1069
17974
  msgid ""
17975
  "%1$s was installed but could not be activated. <a href=\"%2$s\">Please "
17976
  "activate it manually by clicking here.</a>"
4
  msgstr ""
5
  "Project-Id-Version: WooCommerce 3.0.2\n"
6
  "Report-Msgid-Bugs-To: https://github.com/woocommerce/woocommerce/issues\n"
7
+ "POT-Creation-Date: 2017-04-13 17:32:37+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=utf-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
6712
  #: includes/admin/class-wc-admin-menus.php:80
6713
  #: includes/admin/settings/class-wc-settings-api.php:45
6714
  #: includes/admin/views/html-admin-page-status-report.php:432
6715
+ #: includes/class-wc-install.php:893
6716
  msgid "Settings"
6717
  msgstr ""
6718
 
7703
 
7704
  #: includes/admin/class-wc-admin-profile.php:84
7705
  #: includes/admin/meta-boxes/class-wc-meta-box-order-data.php:86
7706
+ #: includes/class-wc-countries.php:1111 includes/class-wc-emails.php:418
7707
  msgid "Phone"
7708
  msgstr ""
7709
 
7710
  #: includes/admin/class-wc-admin-profile.php:88
7711
  #: includes/admin/meta-boxes/class-wc-meta-box-order-data.php:83
7712
+ #: includes/class-wc-countries.php:1120 includes/class-wc-emails.php:411
7713
  #: includes/class-wc-form-handler.php:197
7714
  #: templates/myaccount/form-edit-account.php:40
7715
  #: templates/myaccount/form-login.php:91
15652
  msgstr ""
15653
 
15654
  #: includes/api/class-wc-rest-system-status-tools-controller.php:430
15655
+ msgid "All missing WooCommerce pages successfully installed"
15656
  msgstr ""
15657
 
15658
  #: includes/api/class-wc-rest-system-status-tools-controller.php:437
17720
  msgid "Go to shop"
17721
  msgstr ""
17722
 
17723
+ #: includes/class-wc-emails.php:404
17724
  msgid "Note"
17725
  msgstr ""
17726
 
17727
+ #: includes/class-wc-emails.php:464
17728
  msgid "Product low in stock"
17729
  msgstr ""
17730
 
17731
+ #: includes/class-wc-emails.php:467
17732
  #. translators: 1: product name 2: items in stock
17733
  msgid "%1$s is low in stock. There are %2$d left."
17734
  msgstr ""
17735
 
17736
+ #: includes/class-wc-emails.php:491
17737
  msgid "Product out of stock"
17738
  msgstr ""
17739
 
17740
+ #: includes/class-wc-emails.php:493
17741
  #. translators: %s: product name
17742
  msgid "%s is out of stock."
17743
  msgstr ""
17744
 
17745
+ #: includes/class-wc-emails.php:522
17746
  msgid "Product backorder"
17747
  msgstr ""
17748
 
17749
+ #: includes/class-wc-emails.php:523
17750
  msgid "%1$s units of %2$s have been backordered in order #%3$s."
17751
  msgstr ""
17752
 
17936
  msgid "Shop manager"
17937
  msgstr ""
17938
 
17939
+ #: includes/class-wc-install.php:893
17940
  msgid "View WooCommerce settings"
17941
  msgstr ""
17942
 
17943
+ #: includes/class-wc-install.php:909
17944
  msgid "View WooCommerce documentation"
17945
  msgstr ""
17946
 
17947
+ #: includes/class-wc-install.php:909
17948
  msgid "Docs"
17949
  msgstr ""
17950
 
17951
+ #: includes/class-wc-install.php:910
17952
  msgid "View WooCommerce API docs"
17953
  msgstr ""
17954
 
17955
+ #: includes/class-wc-install.php:910
17956
  msgid "API docs"
17957
  msgstr ""
17958
 
17959
+ #: includes/class-wc-install.php:911
17960
  msgid "Visit premium customer support"
17961
  msgstr ""
17962
 
17963
+ #: includes/class-wc-install.php:911
17964
  msgid "Premium support"
17965
  msgstr ""
17966
 
17967
+ #: includes/class-wc-install.php:1053
17968
  msgid ""
17969
  "%1$s could not be installed (%2$s). <a href=\"%3$s\">Please install it "
17970
  "manually by clicking here.</a>"
17971
  msgstr ""
17972
 
17973
+ #: includes/class-wc-install.php:1079
17974
  msgid ""
17975
  "%1$s was installed but could not be activated. <a href=\"%2$s\">Please "
17976
  "activate it manually by clicking here.</a>"
includes/admin/meta-boxes/views/html-order-refund.php CHANGED
@@ -14,7 +14,7 @@ $who_refunded = new WP_User( $refund->get_refunded_by() );
14
  <td class="name">
15
  <?php
16
  /* translators: 1: refund id 2: date */
17
- printf( __( 'Refund #%1$s - %2$s', 'woocommerce' ), $refund->get_id(), wc_format_datetime( $order->get_date_created(), get_option( 'date_format' ) . ', ' . get_option( 'time_format' ) ) );
18
 
19
  if ( $who_refunded->exists() ) {
20
  echo ' ' . esc_attr_x( 'by', 'Ex: Refund - $date >by< $username', 'woocommerce' ) . ' ' . '<abbr class="refund_by" title="' . sprintf( esc_attr__( 'ID: %d', 'woocommerce' ), absint( $who_refunded->ID ) ) . '">' . esc_attr( $who_refunded->display_name ) . '</abbr>' ;
14
  <td class="name">
15
  <?php
16
  /* translators: 1: refund id 2: date */
17
+ printf( __( 'Refund #%1$s - %2$s', 'woocommerce' ), $refund->get_id(), wc_format_datetime( $refund->get_date_created(), get_option( 'date_format' ) . ', ' . get_option( 'time_format' ) ) );
18
 
19
  if ( $who_refunded->exists() ) {
20
  echo ' ' . esc_attr_x( 'by', 'Ex: Refund - $date >by< $username', 'woocommerce' ) . ' ' . '<abbr class="refund_by" title="' . sprintf( esc_attr__( 'ID: %d', 'woocommerce' ), absint( $who_refunded->ID ) ) . '">' . esc_attr( $who_refunded->display_name ) . '</abbr>' ;
includes/api/class-wc-rest-system-status-tools-controller.php CHANGED
@@ -427,7 +427,7 @@ class WC_REST_System_Status_Tools_Controller extends WC_REST_Controller {
427
  break;
428
  case 'install_pages' :
429
  WC_Install::create_pages();
430
- $message = __( 'All missing WooCommerce pages was installed successfully.', 'woocommerce' );
431
  break;
432
  case 'delete_taxes' :
433
 
427
  break;
428
  case 'install_pages' :
429
  WC_Install::create_pages();
430
+ $message = __( 'All missing WooCommerce pages successfully installed', 'woocommerce' );
431
  break;
432
  case 'delete_taxes' :
433
 
includes/class-wc-cart.php CHANGED
@@ -1138,8 +1138,8 @@ class WC_Cart {
1138
  } elseif ( $this->prices_include_tax ) {
1139
 
1140
  // Get base tax rates
1141
- if ( empty( $shop_tax_rates[ $product->get_tax_class( true ) ] ) ) {
1142
- $shop_tax_rates[ $product->get_tax_class( true ) ] = WC_Tax::get_base_tax_rates( $product->get_tax_class( true ) );
1143
  }
1144
 
1145
  // Get item tax rates
@@ -1147,7 +1147,7 @@ class WC_Cart {
1147
  $tax_rates[ $product->get_tax_class() ] = WC_Tax::get_rates( $product->get_tax_class() );
1148
  }
1149
 
1150
- $base_tax_rates = $shop_tax_rates[ $product->get_tax_class( true ) ];
1151
  $item_tax_rates = $tax_rates[ $product->get_tax_class() ];
1152
 
1153
  /**
@@ -1241,7 +1241,7 @@ class WC_Cart {
1241
  */
1242
  } elseif ( $this->prices_include_tax ) {
1243
 
1244
- $base_tax_rates = $shop_tax_rates[ $product->get_tax_class( true ) ];
1245
  $item_tax_rates = $tax_rates[ $product->get_tax_class() ];
1246
 
1247
  /**
1138
  } elseif ( $this->prices_include_tax ) {
1139
 
1140
  // Get base tax rates
1141
+ if ( empty( $shop_tax_rates[ $product->get_tax_class( 'unfiltered' ) ] ) ) {
1142
+ $shop_tax_rates[ $product->get_tax_class( 'unfiltered' ) ] = WC_Tax::get_base_tax_rates( $product->get_tax_class( 'unfiltered' ) );
1143
  }
1144
 
1145
  // Get item tax rates
1147
  $tax_rates[ $product->get_tax_class() ] = WC_Tax::get_rates( $product->get_tax_class() );
1148
  }
1149
 
1150
+ $base_tax_rates = $shop_tax_rates[ $product->get_tax_class( 'unfiltered' ) ];
1151
  $item_tax_rates = $tax_rates[ $product->get_tax_class() ];
1152
 
1153
  /**
1241
  */
1242
  } elseif ( $this->prices_include_tax ) {
1243
 
1244
+ $base_tax_rates = $shop_tax_rates[ $product->get_tax_class( 'unfiltered' ) ];
1245
  $item_tax_rates = $tax_rates[ $product->get_tax_class() ];
1246
 
1247
  /**
includes/class-wc-emails.php CHANGED
@@ -88,7 +88,7 @@ class WC_Emails {
88
  'woocommerce_created_customer',
89
  ) );
90
 
91
- if ( apply_filters( 'woocommerce_defer_transactional_emails', true ) ) {
92
  self::$background_emailer = new WC_Background_Emailer();
93
 
94
  foreach ( $email_actions as $action ) {
@@ -139,9 +139,15 @@ class WC_Emails {
139
  * @param array $args Email args (default: []).
140
  */
141
  public static function send_transactional_email( $args = array() ) {
142
- $args = func_get_args();
143
- self::instance(); // Init self so emails exist.
144
- do_action_ref_array( current_filter() . '_notification', $args );
 
 
 
 
 
 
145
  }
146
 
147
  /**
88
  'woocommerce_created_customer',
89
  ) );
90
 
91
+ if ( apply_filters( 'woocommerce_defer_transactional_emails', false ) ) {
92
  self::$background_emailer = new WC_Background_Emailer();
93
 
94
  foreach ( $email_actions as $action ) {
139
  * @param array $args Email args (default: []).
140
  */
141
  public static function send_transactional_email( $args = array() ) {
142
+ try {
143
+ $args = func_get_args();
144
+ self::instance(); // Init self so emails exist.
145
+ do_action_ref_array( current_filter() . '_notification', $args );
146
+ } catch ( Exception $e ) {
147
+ if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
148
+ trigger_error( 'Transactional email triggered fatal error for callback ' . current_filter(), E_USER_WARNING );
149
+ }
150
+ }
151
  }
152
 
153
  /**
includes/class-wc-install.php CHANGED
@@ -855,11 +855,21 @@ CREATE TABLE {$wpdb->prefix}woocommerce_termmeta (
855
  $upgrade_notice = '';
856
 
857
  if ( preg_match( $regexp, $content, $matches ) ) {
858
- $version = trim( $matches[1] );
859
  $notices = (array) preg_split( '~[\r\n]+~', trim( $matches[2] ) );
860
 
 
 
 
 
 
 
 
 
 
 
 
861
  // Check the latest stable version and ignore trunk.
862
- if ( $version === $new_version && version_compare( WC_VERSION, $version, '<' ) ) {
863
 
864
  $upgrade_notice .= '</p><p class="wc_plugin_upgrade_notice">';
865
 
855
  $upgrade_notice = '';
856
 
857
  if ( preg_match( $regexp, $content, $matches ) ) {
 
858
  $notices = (array) preg_split( '~[\r\n]+~', trim( $matches[2] ) );
859
 
860
+ // Convert the full version strings to minor versions.
861
+ $notice_version_parts = explode( '.', trim( $matches[1] ) );
862
+ $current_version_parts = explode( '.', WC_VERSION );
863
+
864
+ if ( 3 !== sizeof( $notice_version_parts ) ) {
865
+ return;
866
+ }
867
+
868
+ $notice_version = $notice_version_parts[0] . '.' . $notice_version_parts[1];
869
+ $current_version = $current_version_parts[0] . '.' . $current_version_parts[1];
870
+
871
  // Check the latest stable version and ignore trunk.
872
+ if ( version_compare( $current_version, $notice_version, '<' ) ) {
873
 
874
  $upgrade_notice .= '</p><p class="wc_plugin_upgrade_notice">';
875
 
includes/class-wc-product-grouped.php CHANGED
@@ -52,7 +52,7 @@ class WC_Product_Grouped extends WC_Product {
52
  public function is_on_sale( $context = 'view' ) {
53
  global $wpdb;
54
 
55
- $on_sale = $this->get_children() && 1 === $wpdb->get_var( "SELECT 1 FROM $wpdb->postmeta WHERE meta_key = '_sale_price' AND meta_value > 0 AND post_id IN (" . implode( ',', array_map( 'esc_sql', $this->get_children() ) ) . ");" );
56
 
57
  return 'view' === $context ? apply_filters( 'woocommerce_product_is_on_sale', $on_sale, $this ) : $on_sale;
58
  }
52
  public function is_on_sale( $context = 'view' ) {
53
  global $wpdb;
54
 
55
+ $on_sale = $this->get_children() && null !== $wpdb->get_var( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_sale_price' AND meta_value > 0 AND post_id IN (" . implode( ',', array_map( 'esc_sql', $this->get_children() ) ) . ");" );
56
 
57
  return 'view' === $context ? apply_filters( 'woocommerce_product_is_on_sale', $on_sale, $this ) : $on_sale;
58
  }
includes/class-wc-product-variation.php CHANGED
@@ -91,6 +91,30 @@ class WC_Product_Variation extends WC_Product_Simple {
91
  return $variation_attributes;
92
  }
93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  /**
95
  * Wrapper for get_permalink. Adds this variations attributes to the URL.
96
  *
@@ -135,7 +159,7 @@ class WC_Product_Variation extends WC_Product_Simple {
135
 
136
  // Inherit value from parent.
137
  if ( 'view' === $context && empty( $value ) ) {
138
- $value = $this->parent_data['sku'];
139
  }
140
  return $value;
141
  }
@@ -151,7 +175,7 @@ class WC_Product_Variation extends WC_Product_Simple {
151
 
152
  // Inherit value from parent.
153
  if ( 'view' === $context && empty( $value ) ) {
154
- $value = $this->parent_data['weight'];
155
  }
156
  return $value;
157
  }
@@ -167,7 +191,7 @@ class WC_Product_Variation extends WC_Product_Simple {
167
 
168
  // Inherit value from parent.
169
  if ( 'view' === $context && empty( $value ) ) {
170
- $value = $this->parent_data['length'];
171
  }
172
  return $value;
173
  }
@@ -183,7 +207,7 @@ class WC_Product_Variation extends WC_Product_Simple {
183
 
184
  // Inherit value from parent.
185
  if ( 'view' === $context && empty( $value ) ) {
186
- $value = $this->parent_data['width'];
187
  }
188
  return $value;
189
  }
@@ -199,7 +223,7 @@ class WC_Product_Variation extends WC_Product_Simple {
199
 
200
  // Inherit value from parent.
201
  if ( 'view' === $context && empty( $value ) ) {
202
- $value = $this->parent_data['height'];
203
  }
204
  return $value;
205
  }
@@ -207,15 +231,24 @@ class WC_Product_Variation extends WC_Product_Simple {
207
  /**
208
  * Returns the tax class.
209
  *
210
- * @param string $context
 
 
211
  * @return string
212
  */
213
  public function get_tax_class( $context = 'view' ) {
214
- $value = $this->get_prop( 'tax_class', $context );
215
 
216
- // Inherit value from parent.
217
- if ( 'view' === $context && 'parent' === $value ) {
218
- $value = $this->parent_data['tax_class'];
 
 
 
 
 
 
 
219
  }
220
  return $value;
221
  }
@@ -248,7 +281,7 @@ class WC_Product_Variation extends WC_Product_Simple {
248
 
249
  // Inherit value from parent.
250
  if ( 'view' === $context && 'parent' === $this->get_manage_stock() ) {
251
- $value = $this->parent_data['stock_quantity'];
252
  }
253
  return $value;
254
  }
@@ -265,7 +298,7 @@ class WC_Product_Variation extends WC_Product_Simple {
265
 
266
  // Inherit value from parent.
267
  if ( 'view' === $context && 'parent' === $this->get_manage_stock() ) {
268
- $value = $this->parent_data['backorders'];
269
  }
270
  return $value;
271
  }
@@ -281,7 +314,7 @@ class WC_Product_Variation extends WC_Product_Simple {
281
  $image_id = $this->get_prop( 'image_id', $context );
282
 
283
  if ( 'view' === $context && ! $image_id ) {
284
- $image_id = $this->parent_data['image_id'];
285
  }
286
 
287
  return $image_id;
@@ -298,7 +331,7 @@ class WC_Product_Variation extends WC_Product_Simple {
298
  $shipping_class_id = $this->get_prop( 'shipping_class_id', $context );
299
 
300
  if ( 'view' === $context && ! $shipping_class_id ) {
301
- $shipping_class_id = $this->parent_data['shipping_class_id'];
302
  }
303
 
304
  return $shipping_class_id;
91
  return $variation_attributes;
92
  }
93
 
94
+ /**
95
+ * Returns a single product attribute as a string.
96
+ * @param string $attribute to get.
97
+ * @return string
98
+ */
99
+ public function get_attribute( $attribute ) {
100
+ $attributes = $this->get_attributes();
101
+ $attribute = sanitize_title( $attribute );
102
+
103
+ if ( isset( $attributes[ $attribute ] ) ) {
104
+ $value = $attributes[ $attribute ];
105
+ $term = taxonomy_exists( $attribute ) ? get_term_by( 'slug', $value, $attribute ) : false;
106
+ $value = ! is_wp_error( $term ) && $term ? $term->name : $value;
107
+ } elseif ( isset( $attributes[ 'pa_' . $attribute ] ) ) {
108
+ $value = $attributes[ 'pa_' . $attribute ];
109
+ $term = taxonomy_exists( 'pa_' . $attribute ) ? get_term_by( 'slug', $value, 'pa_' . $attribute ) : false;
110
+ $value = ! is_wp_error( $term ) && $term ? $term->name : $value;
111
+ } else {
112
+ return '';
113
+ }
114
+
115
+ return $value;
116
+ }
117
+
118
  /**
119
  * Wrapper for get_permalink. Adds this variations attributes to the URL.
120
  *
159
 
160
  // Inherit value from parent.
161
  if ( 'view' === $context && empty( $value ) ) {
162
+ $value = apply_filters( $this->get_hook_prefix() . 'sku', $this->parent_data['sku'], $this );
163
  }
164
  return $value;
165
  }
175
 
176
  // Inherit value from parent.
177
  if ( 'view' === $context && empty( $value ) ) {
178
+ $value = apply_filters( $this->get_hook_prefix() . 'weight', $this->parent_data['weight'], $this );
179
  }
180
  return $value;
181
  }
191
 
192
  // Inherit value from parent.
193
  if ( 'view' === $context && empty( $value ) ) {
194
+ $value = apply_filters( $this->get_hook_prefix() . 'length', $this->parent_data['length'], $this );
195
  }
196
  return $value;
197
  }
207
 
208
  // Inherit value from parent.
209
  if ( 'view' === $context && empty( $value ) ) {
210
+ $value = apply_filters( $this->get_hook_prefix() . 'width', $this->parent_data['width'], $this );
211
  }
212
  return $value;
213
  }
223
 
224
  // Inherit value from parent.
225
  if ( 'view' === $context && empty( $value ) ) {
226
+ $value = apply_filters( $this->get_hook_prefix() . 'height', $this->parent_data['height'], $this );
227
  }
228
  return $value;
229
  }
231
  /**
232
  * Returns the tax class.
233
  *
234
+ * Does not use get_prop so it can handle 'parent' Inheritance correctly.
235
+ *
236
+ * @param string $context view, edit, or unfiltered
237
  * @return string
238
  */
239
  public function get_tax_class( $context = 'view' ) {
240
+ $value = null;
241
 
242
+ if ( array_key_exists( 'tax_class', $this->data ) ) {
243
+ $value = array_key_exists( 'tax_class', $this->changes ) ? $this->changes['tax_class'] : $this->data['tax_class'];
244
+
245
+ if ( 'edit' !== $context && 'parent' === $value ) {
246
+ $value = $this->parent_data['tax_class'];
247
+ }
248
+
249
+ if ( 'view' === $context ) {
250
+ $value = apply_filters( $this->get_hook_prefix() . 'tax_class', $value, $this );
251
+ }
252
  }
253
  return $value;
254
  }
281
 
282
  // Inherit value from parent.
283
  if ( 'view' === $context && 'parent' === $this->get_manage_stock() ) {
284
+ $value = apply_filters( $this->get_hook_prefix() . 'stock_quantity', $this->parent_data['stock_quantity'], $this );
285
  }
286
  return $value;
287
  }
298
 
299
  // Inherit value from parent.
300
  if ( 'view' === $context && 'parent' === $this->get_manage_stock() ) {
301
+ $value = apply_filters( $this->get_hook_prefix() . 'backorders', $this->parent_data['backorders'], $this );
302
  }
303
  return $value;
304
  }
314
  $image_id = $this->get_prop( 'image_id', $context );
315
 
316
  if ( 'view' === $context && ! $image_id ) {
317
+ $value = apply_filters( $this->get_hook_prefix() . 'image_id', $this->parent_data['image_id'], $this );
318
  }
319
 
320
  return $image_id;
331
  $shipping_class_id = $this->get_prop( 'shipping_class_id', $context );
332
 
333
  if ( 'view' === $context && ! $shipping_class_id ) {
334
+ $value = apply_filters( $this->get_hook_prefix() . 'shipping_class_id', $this->parent_data['shipping_class_id'], $this );
335
  }
336
 
337
  return $shipping_class_id;
includes/class-wc-structured-data.php CHANGED
@@ -254,7 +254,10 @@ class WC_Structured_Data {
254
  $markup['@id'] = get_comment_link( $comment->comment_ID );
255
  $markup['datePublished'] = get_comment_date( 'c', $comment->comment_ID );
256
  $markup['description'] = get_comment_text( $comment->comment_ID );
257
-
 
 
 
258
  if ( $rating = get_comment_meta( $comment->comment_ID, 'rating', true ) ) {
259
  $markup['reviewRating'] = array(
260
  '@type' => 'rating',
254
  $markup['@id'] = get_comment_link( $comment->comment_ID );
255
  $markup['datePublished'] = get_comment_date( 'c', $comment->comment_ID );
256
  $markup['description'] = get_comment_text( $comment->comment_ID );
257
+ $markup['itemReviewed'] = array(
258
+ '@type' => 'Product',
259
+ 'name' => get_the_title( $comment->post_ID ),
260
+ );
261
  if ( $rating = get_comment_meta( $comment->comment_ID, 'rating', true ) ) {
262
  $markup['reviewRating'] = array(
263
  '@type' => 'rating',
includes/wc-deprecated-functions.php CHANGED
@@ -59,6 +59,8 @@ function wc_deprecated_function( $function, $version, $replacement = null ) {
59
  * @param string $replacement
60
  */
61
  function wc_doing_it_wrong( $function, $message, $version ) {
 
 
62
  if ( is_ajax() ) {
63
  do_action( 'doing_it_wrong_run', $function, $message, $version );
64
  error_log( "{$function} was called incorrectly. {$message}. This message was added in version {$version}." );
59
  * @param string $replacement
60
  */
61
  function wc_doing_it_wrong( $function, $message, $version ) {
62
+ $message .= ' Backtrace: ' . wp_debug_backtrace_summary();
63
+
64
  if ( is_ajax() ) {
65
  do_action( 'doing_it_wrong_run', $function, $message, $version );
66
  error_log( "{$function} was called incorrectly. {$message}. This message was added in version {$version}." );
includes/wc-product-functions.php CHANGED
@@ -908,7 +908,7 @@ function wc_get_price_including_tax( $product, $args = array() ) {
908
  $return_price = round( $line_price + $tax_amount, wc_get_price_decimals() );
909
  } else {
910
  $tax_rates = WC_Tax::get_rates( $product->get_tax_class() );
911
- $base_tax_rates = WC_Tax::get_base_tax_rates( $product->get_tax_class( true ) );
912
 
913
  /**
914
  * If the customer is excempt from VAT, remove the taxes here.
@@ -957,7 +957,7 @@ function wc_get_price_excluding_tax( $product, $args = array() ) {
957
  }
958
 
959
  if ( $product->is_taxable() && wc_prices_include_tax() ) {
960
- $tax_rates = WC_Tax::get_base_tax_rates( $product->get_tax_class( true ) );
961
  $taxes = WC_Tax::calc_tax( $price * $qty, $tax_rates, true );
962
  $price = WC_Tax::round( $price * $qty - array_sum( $taxes ) );
963
  } else {
908
  $return_price = round( $line_price + $tax_amount, wc_get_price_decimals() );
909
  } else {
910
  $tax_rates = WC_Tax::get_rates( $product->get_tax_class() );
911
+ $base_tax_rates = WC_Tax::get_base_tax_rates( $product->get_tax_class( 'unfiltered' ) );
912
 
913
  /**
914
  * If the customer is excempt from VAT, remove the taxes here.
957
  }
958
 
959
  if ( $product->is_taxable() && wc_prices_include_tax() ) {
960
+ $tax_rates = WC_Tax::get_base_tax_rates( $product->get_tax_class( 'unfiltered' ) );
961
  $taxes = WC_Tax::calc_tax( $price * $qty, $tax_rates, true );
962
  $price = WC_Tax::round( $price * $qty - array_sum( $taxes ) );
963
  } else {
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: automattic, mikejolley, jameskoster, claudiosanches, jshreve, code
3
  Tags: ecommerce, e-commerce, store, sales, sell, shop, cart, checkout, downloadable, downloads, paypal, storefront, woo commerce
4
  Requires at least: 4.4
5
  Tested up to: 4.7
6
- Stable tag: 3.0.2
7
  License: GPLv3
8
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
9
 
@@ -161,6 +161,17 @@ Yes you can! Join in on our [GitHub repository](http://github.com/woocommerce/wo
161
 
162
  == Changelog ==
163
 
 
 
 
 
 
 
 
 
 
 
 
164
  = 3.0.2 - 2017-04-12 =
165
  * Fix - Removed required states for GP, GF, KW, LB, MQ, RE and YT countries.
166
  * Fix - Made cache in the [products] shortcode respect filters from plugins.
@@ -308,5 +319,5 @@ Yes you can! Join in on our [GitHub repository](http://github.com/woocommerce/wo
308
 
309
  == Upgrade Notice ==
310
 
311
- = 3.0.2 =
312
  3.0 is a major update. [Make a full site backup](https://docs.woocommerce.com/document/backup-wordpress-content), update your theme and extensions, and [review update best practices](https://docs.woocommerce.com/document/how-to-update-your-site) before upgrading.
3
  Tags: ecommerce, e-commerce, store, sales, sell, shop, cart, checkout, downloadable, downloads, paypal, storefront, woo commerce
4
  Requires at least: 4.4
5
  Tested up to: 4.7
6
+ Stable tag: 3.0.3
7
  License: GPLv3
8
  License URI: https://www.gnu.org/licenses/gpl-3.0.html
9
 
161
 
162
  == Changelog ==
163
 
164
+ = 3.0.3 - 2017-04-13 =
165
+ * Fix - Fixed an issue with variation tax-classes when set to 'parent'. This made taxes apply on top of the tax inclusive price in certain setups.
166
+ * Fix - Escaped attribute translations in the `cart.php` template and bumped the template version to match.
167
+ * Fix - Corrected the display of refund dates on the order screen.
168
+ * Fix - Fixed the grouped product visibility check in the grouped.php template and bumped the template version to match.
169
+ * Fix - Fixed the sale badge display for grouped products.
170
+ * Fix - Added the `itemReviewed` structured data for product reviews to make it validate.
171
+ * Fix - Made the `get_attribute` method work on variation objects.
172
+ * Tweak - Turned off the deferred email sending by default which was added in 3.0. Whilst it does improve performance, there were compatibility problems on some servers. It can be enabled with a filter if desired.
173
+ * Dev - Added backtrace information to the deprecation messages to help find problem plugins.
174
+
175
  = 3.0.2 - 2017-04-12 =
176
  * Fix - Removed required states for GP, GF, KW, LB, MQ, RE and YT countries.
177
  * Fix - Made cache in the [products] shortcode respect filters from plugins.
319
 
320
  == Upgrade Notice ==
321
 
322
+ = 3.0.3 =
323
  3.0 is a major update. [Make a full site backup](https://docs.woocommerce.com/document/backup-wordpress-content), update your theme and extensions, and [review update best practices](https://docs.woocommerce.com/document/how-to-update-your-site) before upgrading.
templates/cart/cart.php CHANGED
@@ -13,7 +13,7 @@
13
  * @see https://docs.woocommerce.com/document/template-structure/
14
  * @author WooThemes
15
  * @package WooCommerce/Templates
16
- * @version 3.0.0
17
  */
18
 
19
  if ( ! defined( 'ABSPATH' ) ) {
@@ -75,7 +75,7 @@ do_action( 'woocommerce_before_cart' ); ?>
75
  ?>
76
  </td>
77
 
78
- <td class="product-name" data-title="<?php _e( 'Product', 'woocommerce' ); ?>">
79
  <?php
80
  if ( ! $product_permalink ) {
81
  echo apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key ) . '&nbsp;';
@@ -93,13 +93,13 @@ do_action( 'woocommerce_before_cart' ); ?>
93
  ?>
94
  </td>
95
 
96
- <td class="product-price" data-title="<?php _e( 'Price', 'woocommerce' ); ?>">
97
  <?php
98
  echo apply_filters( 'woocommerce_cart_item_price', WC()->cart->get_product_price( $_product ), $cart_item, $cart_item_key );
99
  ?>
100
  </td>
101
 
102
- <td class="product-quantity" data-title="<?php _e( 'Quantity', 'woocommerce' ); ?>">
103
  <?php
104
  if ( $_product->is_sold_individually() ) {
105
  $product_quantity = sprintf( '1 <input type="hidden" name="cart[%s][qty]" value="1" />', $cart_item_key );
@@ -116,7 +116,7 @@ do_action( 'woocommerce_before_cart' ); ?>
116
  ?>
117
  </td>
118
 
119
- <td class="product-subtotal" data-title="<?php _e( 'Total', 'woocommerce' ); ?>">
120
  <?php
121
  echo apply_filters( 'woocommerce_cart_item_subtotal', WC()->cart->get_product_subtotal( $_product, $cart_item['quantity'] ), $cart_item, $cart_item_key );
122
  ?>
13
  * @see https://docs.woocommerce.com/document/template-structure/
14
  * @author WooThemes
15
  * @package WooCommerce/Templates
16
+ * @version 3.0.3
17
  */
18
 
19
  if ( ! defined( 'ABSPATH' ) ) {
75
  ?>
76
  </td>
77
 
78
+ <td class="product-name" data-title="<?php esc_attr_e( 'Product', 'woocommerce' ); ?>">
79
  <?php
80
  if ( ! $product_permalink ) {
81
  echo apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key ) . '&nbsp;';
93
  ?>
94
  </td>
95
 
96
+ <td class="product-price" data-title="<?php esc_attr_e( 'Price', 'woocommerce' ); ?>">
97
  <?php
98
  echo apply_filters( 'woocommerce_cart_item_price', WC()->cart->get_product_price( $_product ), $cart_item, $cart_item_key );
99
  ?>
100
  </td>
101
 
102
+ <td class="product-quantity" data-title="<?php esc_attr_e( 'Quantity', 'woocommerce' ); ?>">
103
  <?php
104
  if ( $_product->is_sold_individually() ) {
105
  $product_quantity = sprintf( '1 <input type="hidden" name="cart[%s][qty]" value="1" />', $cart_item_key );
116
  ?>
117
  </td>
118
 
119
+ <td class="product-subtotal" data-title="<?php esc_attr_e( 'Total', 'woocommerce' ); ?>">
120
  <?php
121
  echo apply_filters( 'woocommerce_cart_item_subtotal', WC()->cart->get_product_subtotal( $_product, $cart_item['quantity'] ), $cart_item, $cart_item_key );
122
  ?>
templates/single-product/add-to-cart/grouped.php CHANGED
@@ -13,7 +13,7 @@
13
  * @see https://docs.woocommerce.com/document/template-structure/
14
  * @author WooThemes
15
  * @package WooCommerce/Templates
16
- * @version 3.0.0
17
  */
18
  if ( ! defined( 'ABSPATH' ) ) {
19
  exit;
@@ -66,7 +66,7 @@ do_action( 'woocommerce_before_add_to_cart_form' ); ?>
66
  </td>
67
  <td class="label">
68
  <label for="product-<?php echo $grouped_product->get_id(); ?>">
69
- <?php echo $product->is_visible() ? '<a href="' . esc_url( apply_filters( 'woocommerce_grouped_product_list_link', get_permalink(), $grouped_product->get_id() ) ) . '">' . get_the_title() . '</a>' : get_the_title(); ?>
70
  </label>
71
  </td>
72
  <?php do_action( 'woocommerce_grouped_product_list_before_price', $grouped_product ); ?>
13
  * @see https://docs.woocommerce.com/document/template-structure/
14
  * @author WooThemes
15
  * @package WooCommerce/Templates
16
+ * @version 3.0.3
17
  */
18
  if ( ! defined( 'ABSPATH' ) ) {
19
  exit;
66
  </td>
67
  <td class="label">
68
  <label for="product-<?php echo $grouped_product->get_id(); ?>">
69
+ <?php echo $grouped_product->is_visible() ? '<a href="' . esc_url( apply_filters( 'woocommerce_grouped_product_list_link', get_permalink(), $grouped_product->get_id() ) ) . '">' . get_the_title() . '</a>' : get_the_title(); ?>
70
  </label>
71
  </td>
72
  <?php do_action( 'woocommerce_grouped_product_list_before_price', $grouped_product ); ?>
woocommerce.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: WooCommerce
4
  * Plugin URI: https://woocommerce.com/
5
  * Description: An e-commerce toolkit that helps you sell anything. Beautifully.
6
- * Version: 3.0.2
7
  * Author: Automattic
8
  * Author URI: https://woocommerce.com
9
  * Requires at least: 4.4
@@ -35,7 +35,7 @@ final class WooCommerce {
35
  *
36
  * @var string
37
  */
38
- public $version = '3.0.2';
39
 
40
  /**
41
  * The single instance of the class.
3
  * Plugin Name: WooCommerce
4
  * Plugin URI: https://woocommerce.com/
5
  * Description: An e-commerce toolkit that helps you sell anything. Beautifully.
6
+ * Version: 3.0.3
7
  * Author: Automattic
8
  * Author URI: https://woocommerce.com
9
  * Requires at least: 4.4
35
  *
36
  * @var string
37
  */
38
+ public $version = '3.0.3';
39
 
40
  /**
41
  * The single instance of the class.