Develop WC

The official WooCommerce development blog

WooCommerce 2.6.13 fix release notes — January 18, 2017

WooCommerce 2.6.13 fix release notes

The WooCommerce 2.6.13 fix release is now available. You can download it from WordPress.org or as an automatic update in your administration panel.

~8 commits made it into this release fixing a few issues.

The full changelog for 2.6.13 is below.

* Fix - Demo store banner styling in 2017.
* Fix - Removed default instructions from COD, BACS and Cheque gateways so displayed messages can be unset.
* Fix - Made variation options update on first load.
* Localisation - Added Romanian locale to the installer.

If you spot any further issues, please report them to us in detail on GitHub so the development team can review – comments on this post are closed.

WC 2.7 extension compatibility examples — January 17, 2017

WC 2.7 extension compatibility examples

Our team have been looking at updating some of our extensions for 2.7 compatibility and we thought it would be good to share our notes in doing so. The first bunch we reviewed were EU VAT, Shipment Tracking, and Product Add-ons. Our notes are below.

Identifying issues

To test these extensions, we first needed to install them alongside 2.7 beta 1 and ensure WP_DEBUG mode was enabled so notices were logged and visible.

In testing, we were mainly on the look out for notices, warnings, and (unlikely) fatal errors. To capture notices and view them on-site, you can use the Query Monitor plugin.

If you also enable WP_DEBUG_LOG you can also see notices as they appear via your terminal by running the command:

tail -f wp-content/debug.log

Supported versions

We chose to officially provide support for 2.6.x and 2.7.x. 2.6 has been out since June 2016 making it well over 6 months old. Testing 2.6/2.7 in our case was just a case of switching between the master and release/2.6 branches of WooCommerce core.

Updating EU VAT extension

After activating I immediately saw two warnings:

[09-Jan-2017 11:01:33 UTC] PHP Notice:  id was called incorrectly. Order properties should not be accessed directly. Please see Debugging in WordPress for more information. (This message was added in version 2.7.) in /srv/www/wordpress-default/wp-includes/functions.php on line 4091
[09-Jan-2017 11:01:33 UTC] PHP Notice:  billing_country was called incorrectly. Order properties should not be accessed directly. Please see Debugging in WordPress for more information. (This message was added in version 2.7.) in /srv/www/wordpress-default/wp-includes/functions.php on line 4091

These are caused by accessible properties directory on an object rather than going through the CRUD methods.

Query monitor shows the call stack and shows which method in the EU VAT plugin was calling these properties:

This showed that the WC_EU_VAT_Number::formatted_billing_address method needed an update and needed to switch to the get_ methods.

I also noticed this plugin uses update_post_meta calls directly to update the VAT Number. This should also be replaced by CRUD methods.

To avoid lots of conditional logic inline in this case, I decided to put my 2.7.x logic in the same class, but with dedicated methods to keep the source as clean as possible where possible. When I did do things inline, I either use a version_compare statement, or is_callable.

In general, using is_callable is preferred over method_exists calls because is_callable also checks method scope. This ensures the method can actually be called from your code, rather than if the method simply exists.

I’ve included a few examples of how I handled certain cases below.


Throughout the admin class it did checks on the billing country like this:

// We only need this box for EU orders
if ( ! in_array( $theorder->billing_country, WC_EU_VAT_Number::get_eu_countries() ) ) {
    echo wpautop( __( 'This order is out of scope for EU VAT.', 'woocommerce-eu-vat-number' ) );
    return;
}

I moved the in_array check to a new method called is_eu_order in this case.


For order data saving during checkout, the 2.6.x version uses the following hooks:

add_action( 'woocommerce_checkout_update_order_meta', array( __CLASS__, 'update_order_meta' ) );
add_action( 'woocommerce_checkout_update_user_meta', array( __CLASS__, 'update_user_meta' ) );
add_action( 'woocommerce_refund_created', array( __CLASS__, 'refund_vat_number' ) );

These update meta calls I left in place for 2.6, wrapped with a version check to run in 2.6.x. For 2.7, I used newer hooks which allow dealing with the CRUD objects directly:

if ( version_compare( WC_VERSION, '2.7', '<' ) ) {
            add_action( 'woocommerce_checkout_update_order_meta', array( __CLASS__, 'update_order_meta' ) );
            add_action( 'woocommerce_checkout_update_user_meta', array( __CLASS__, 'update_user_meta' ) );
            add_action( 'woocommerce_refund_created', array( __CLASS__, 'refund_vat_number' ) );
        } else {
            add_action( 'woocommerce_checkout_create_order', array( __CLASS__, 'set_order_data' ) );
            add_action( 'woocommerce_checkout_update_customer', array( __CLASS__, 'set_customer_data' ) );
            add_action( 'woocommerce_create_refund', array( __CLASS__, 'set_refund_data' ) );
        }

The old callbacks could then be left alone. Here is an example of one of the callbacks I updated with the new hooks vs what it was originally.

    /**
     * Stores VAT Number to customer profile
     *
     * @param int $user_id
     */
    public static function update_user_meta( $user_id ) {
        if ( $user_id && self::$data['vat_number'] ) {
            update_user_meta( $user_id, 'vat_number', self::$data['vat_number'] );
        }
    }
    
    /**
     * Save VAT Number to the customer during checkout (WC 2.7.x).
     *
     * @param  WC_Customer $customer
     */
    public static function set_customer_data( $customer ) {
        $customer->update_meta_data( 'vat_number', self::$data['vat_number'] );
    }

Notice the set_customer_data method updates the object directly. WordPress passes a reference to the $customer object via the woocommerce_checkout_update_customer action. By setting the meta data here, I don’t need to save the object myself. It’s done by core 🙂


I found several calls in methods to:

WC()->customer->get_country()

This has been deprecated in favour of get_billing_country(). To replace this without lots of conditionals, I just stored it to a variable like this:

$billing_country = is_callable( array( WC()->customer, 'get_billing_country' ) ) ? WC()->customer->get_billing_country() : WC()->customer->get_country();

Finally, I noticed the admin code had lots of get_post_meta calls scattered around, and most of them dealt with the same meta keys and did the same logic. To simply this, and update it for 2.7, I moved them all into a single getter function which returns the data required.

This allows all other methods to use this method rather than using get_post_meta or CRUD objects directly.

Updating Shipment Tracking extension

The following notes are from Justin Shreve.

Before I began testing the shipment tracking extension, I activated a small debugging plugin to provide backtraces when deprecation notices and doing_it_wrong messages are generated. WooCommerce 2.7 uses both of these to log when deprecated code is used. Here is a gist that you can save, upload, and activate. The Query Monitor plugin mentioned above will also show backtraces.

I also noted that the Shipment Tracking extension has some classes that provide compatibility with some of our other extensions, like the CSV Order Exporter, the XML Order Exporter, and our Print Invoices and Packing Lists extension . I installed all of these extensions so I could make sure that I test and update all potential code paths.

I began to test the various features of the extension while keeping my debug log open. I compiled a list of issues to fix based on each notice, backtrace provided.

The notices generated in my debug log were related to accessing the ID property of the $order object. In 2.7, all properties should be accessed with getters and setters. In the case of ID, get_id should be used.

Like in the VAT extension, is_callable is used to ensure the correct method is used, so 2.6 backwards compatibility can be maintained. In future versions, where 2.6 support is no longer needed, this line can be simplified.

As an example, here is the email_display method currently used in the Shipment Tracking extension.

public function email_display( $order, $sent_to_admin, $plain_text = null ) {
	if ( $plain_text === true ) {
		wc_get_template( 'email/plain/tracking-info.php', array( 'tracking_items' => $this->get_tracking_items( $order->id, true ) ), 'woocommerce-shipment-tracking/', $this->get_plugin_path() . '/templates/' );
	} else {
		wc_get_template( 'email/tracking-info.php', array( 'tracking_items' => $this->get_tracking_items( $order->id, true ) ), 'woocommerce-shipment-tracking/', $this->get_plugin_path() . '/templates/' );
	}
}

To update the code, I defined a new variable holding the value of the order ID:

$order_id = is_callable( array( $order, 'get_id' ) ) ? $order->get_id() : $order->id;

And then I update all the $order->id calls to use it. Here is the finished function:

public function email_display( $order, $sent_to_admin, $plain_text = null ) {
	$order_id = is_callable( array( $order, 'get_id' ) ) ? $order->get_id() : $order->id;
	if ( true === $plain_text ) {
		wc_get_template( 'email/plain/tracking-info.php', array( 'tracking_items' => $this->get_tracking_items( $order_id, true ) ), 'woocommerce-shipment-tracking/', $this->get_plugin_path() . '/templates/' );
	} else {
		wc_get_template( 'email/tracking-info.php', array( 'tracking_items' => $this->get_tracking_items( $order_id, true ) ), 'woocommerce-shipment-tracking/', $this->get_plugin_path() . '/templates/' );
	}
}

I made similar updates around the code base, fixing the notices.

After I was confident I fixed the deprecated code, I began looking for the the following additional problems:

  • get_post_meta and update_post_meta calls.
  • Usage of get_post or get_post_type.
  • Direct SQL queries touching the posts table.

Per our CRUD introduction post, it’s important to standardize accessing and writing data so that we can make improvements in the future, and your extensions will be ready for them. These are not surfaced via notices however, so you will need to scan your code for these.

The tracking extension stores it’s data in a piece of meta, so there were a few get_post_meta calls, and update_post_meta calls to be updated. In 2.7, these custom meta keys (pieces of data that are not properties of the main WC_Order class), can be accessed using the get_meta function available on all CRUD classes, and saved using the update_meta_data and save_meta_data functions available on all CRUD classes. Since these methods were added in 2.7, we need to do a version check here too, to provide backwards compatibility. These statements can also be simplified in future versions.

In the save_tracking_items method of the tracking extension, the following line

update_post_meta( $order_id, '_wc_shipment_tracking_items', $tracking_items );

became

if ( version_compare( WC_VERSION, '2.7', '<' ) ) { update_post_meta( $order_id, '_wc_shipment_tracking_items', $tracking_items ); } else { $order = new WC_Order( $order_id ); $order->update_meta_data( '_wc_shipment_tracking_items', $tracking_items );
    $order->save_meta_data();
}

In the example above, we can still access the post meta directly orders are still in the posts table for the time being. For 2.7 and above, we will use the new class – preparing our extension for the future.

The shipment tracking extension exposes a new set of REST API endpoints for getting, creating, and deleting tracking info for orders. Some of the REST API code contained a few uses of get_post to check if an order ID is valid before trying to return tracking numbers for it. The WC_Order class in 2.7 will thrown an exception if an order is invalid, so this can be used instead.

I wrote a helper function for the API class that would allow me to do the check easier in multiple places:

/**
 * Checks if an order ID is a valid order.
 *
 * @param int $order_id
 * @return bool
 */
public function is_valid_order_id( $order_id ) {
    if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '2.7', '<' ) ) { 
        $order = get_post( $order_id ); 
        if ( empty( $order->post_type ) || $this->post_type !== $order->post_type ) { 
            return false; 
        } 
    } else { 
        try { 
            new WC_Order( $order_id ); 
        } catch ( Exception $e ) { 
            return false; 
        } 
    } 
    return true; 
}

Using this helper function, I was able to update the multiple checks that looked like

$order = get_post( (int) $request['order_id'] ); 

if ( empty( $order->post_type ) || $this->post_type !== $order->post_type ) { 
    return new WP_Error( 'woocommerce_rest_order_invalid_id', __( 'Invalid order ID.', 'woocommerce-shipment-tracking' ), array( 'status' => 404 ) ); 
} 

to

 
$order_id = (int) $request['order_id']; 

if ( ! $this->is_valid_order_id( $order_id ) ) { 
    return new WP_Error( 'woocommerce_rest_order_invalid_id', __( 'Invalid order ID.', 'woocommerce-shipment-tracking' ), array( 'status' => 404 ) ); 
} 

I also made sure that the REST API endpoints exposed were running on WP-API like the WooCommerce endpoints as of 2.6, and not the older legacy WooCommerce REST API. They were, so no updates were needed.

The extension didn’t make any direct queries, so all compatibility updates are finished.

I retested the extension on both 2.7 AND 2.6, to make sure my changes functioned correctly in both versions.

When adding support for a new version, don’t forget to test the old version again afterwards to ensure you’ve not used any non-existant methods! This is called regression testing.

There were no issues in the debug log, and the extension still worked correctly. Success!

Updating Product Add-ons extension

The following notes are from Claudio Sanches.

Since this extension handles meta data for Products and Orders, I invested time in creating new classes to handle compatibility for WooCommerce 2.6, thereby making modifications simpler and easier in the future when 2.6 support may be removed.

So I started testing and tracking the log file on my terminal, creating a new class for the legacy support, this is how I included the classes into the main Product Addons class:

/**
 * Initializes plugin classes.
 */
public function init_classes() {
	if ( is_admin() ) {
		$this->init_admin();
	}

	include_once( dirname( __FILE__ ) . '/classes/class-product-addon-display.php' );
	include_once( dirname( __FILE__ ) . '/classes/class-product-addon-cart.php' );
	include_once( dirname( __FILE__ ) . '/classes/class-product-addon-ajax.php' );

	// Handle WooCommerce 2.7 compatibility.
	if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '2.7.0', '<' ) ) {
		include_once( dirname( __FILE__ ) . '/classes/legacy/class-product-addon-display-legacy.php' );
		include_once( dirname( __FILE__ ) . '/classes/legacy/class-product-addon-cart-legacy.php' );
		include_once( dirname( __FILE__ ) . '/classes/legacy/class-wc-addons-ajax.php' );

		$GLOBALS['Product_Addon_Display'] = new Product_Addon_Display_Legacy();
		$GLOBALS['Product_Addon_Cart']    = new Product_Addon_Cart_Legacy();
		new WC_Addons_Ajax();
	} else {
		$GLOBALS['Product_Addon_Display'] = new Product_Addon_Display();
		$GLOBALS['Product_Addon_Cart']    = new Product_Addon_Cart();
		new Product_Addon_Cart_Ajax();
	}
}

/**
 * Initializes plugin admin.
 */
protected function init_admin() {
	include_once( dirname( __FILE__ ) . '/admin/class-product-addon-admin.php' );

	// Handle WooCommerce 2.7 compatibility.
	if ( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '2.7.0', '<' ) ) {
		include_once( dirname( __FILE__ ) . '/admin/legacy/class-product-addon-admin-legacy.php' );

		$GLOBALS['Product_Addon_Admin'] = new Product_Addon_Admin_Legacy();
	} else {
		$GLOBALS['Product_Addon_Admin'] = new Product_Addon_Admin();
	}
}

This makes the task of handling backward compatibility easier because it’s all dealt with in one place , and you can do any changes that you need inside the current class. You just need to copy and paste the old methods no longer needed to the *_Legacy class.

Here an example with the Product_Addon_Cart_Legacy class:

/**
 * Product_Addon_Cart_Legacy class.
 */
class Product_Addon_Cart_Legacy extends Product_Addon_Cart {

	/**
	 * Constructor.
	 */
	function __construct() {
		parent::__construct();

		// Add meta to order.
		add_action( 'woocommerce_add_order_item_meta', array( $this, 'order_item_meta' ), 10, 2 );
	}

	/**
	 * add_cart_item function.
	 *
	 * @param array $cart_item
	 */
	public function add_cart_item( $cart_item ) {
		// Adjust price if addons are set
		if ( ! empty( $cart_item['addons'] ) && apply_filters( 'woocommerce_product_addons_adjust_price', true, $cart_item ) ) {

			$extra_cost = 0;

			foreach ( $cart_item['addons'] as $addon ) {
				if ( $addon['price'] > 0 ) {
					$extra_cost += $addon['price'];
				}
			}

			$cart_item['data']->adjust_price( $extra_cost );
		}

		return $cart_item;
	}

	/**
	 * Add meta to orders.
	 *
	 * @param int $item_id
	 * @param array $values
	 */
	public function order_item_meta( $item_id, $values ) {
		if ( ! empty( $values['addons'] ) ) {
			foreach ( $values['addons'] as $addon ) {

				$name = $addon['name'];

				if ( $addon['price'] > 0 && apply_filters( 'woocommerce_addons_add_price_to_name', true ) ) {
					$name .= ' (' . strip_tags( wc_price( get_product_addon_price_for_display ( $addon['price'], $values[ 'data' ], true ) ) ) . ')';
				}

				wc_add_order_item_meta( $item_id, $name, $addon['value'] );
			}
		}
	}

In this legacy class  constructor I first called the parent class constructor (main class), and then I used the woocommerce_add_order_item_meta hook that was deprecated in 2.7 with the Product_Addon_Cart_Legacy->order_item_meta() callback for 2.6.x.

This legacy pattern allows us to reuse as much code across the 2.6.x and 2.7.x versions of the class as possible. Reuse code where possible; not only does this make supporting multiple versions more straightforward, it also makes your code more DRY, easier to test, and easier to read.

I found that Product_Addon_Cart_Legacy->add_cart_item() was also throwing deprecated messages because it uses WC_Product->adjust_price(). To resolve this, in the Product_Addon_Cart class I modified the following code:

/**
 * Product_Addon_Cart class.
 */
class Product_Addon_Cart {

	/**
	 * Constructor.
	 */
	function __construct() {
		// ...

		// Add meta to order.
		add_action( 'woocommerce_checkout_create_order_line_item', array( $this, 'order_line_item' ), 10, 3 );

		// ...
	}

	/**
	 * Adjust add-on proce if set on cart.
	 *
	 * @param array $cart_item Cart item data.
	 * @return array
	 */
	public function add_cart_item( $cart_item ) {
		if ( ! empty( $cart_item['addons'] ) && apply_filters( 'woocommerce_product_addons_adjust_price', true, $cart_item ) ) {
			$price = $cart_item['data']->get_price();

			foreach ( $cart_item['addons'] as $addon ) {
				if ( $addon['price'] > 0 ) {
					$price += (float) $addon['price'];
				}
			}

			$cart_item['data']->set_price( $price );
		}

		return $cart_item;
	}

	// ...

	/**
	 * Include add-ons line item meta.
	 *
	 * @param  WC_Order_Item_Product $item          Order item data.
	 * @param  string                $cart_item_key Cart item key.
	 * @param  array                 $values        Order item values.
	 */
	public function order_line_item( $item, $cart_item_key, $values ) {
		if ( ! empty( $values['addons'] ) ) {
			foreach ( $values['addons'] as $addon ) {
				$key = $addon['name'];

				if ( $addon['price'] > 0 && apply_filters( 'woocommerce_addons_add_price_to_name', true ) ) {
					$key .= ' (' . strip_tags( wc_price( get_product_addon_price_for_display( $addon['price'], $values['data'], true ) ) ) . ')';
				}

				$item->add_meta_data( $key, $addon['value'] );
			}
		}
	}

	// ...
}

In this new code it’s possible to see the use of woocommerce_checkout_create_order_line_item hook new in 2.7 and inside Product_Addon_Cart->order_line_item() using WC_Order->add_meta_data to include and save into the Orders CRUD.

For the Product_Addon_Cart_Legacy->add_cart_item() I had to change a little of the logic to be able to use the new WC_Product->set_price().

I did the same kind of thing for the rest of the classes in the plugin, then I cleaned all warnings from my debug.log, switched back to WooCommerce 2.6, and tested everything again to make sure that I had not introduced any extra bug or broken backwards compatibility.

Deprecation in WooCommerce Core —

Deprecation in WooCommerce Core

Deprecation is a method of discouraging usage of a feature, or practice, in favour of something else without breaking backwards compatibility or totally prohibiting it’s usage. To quote the Wikipedia article on Deprecation:

While a deprecated software feature remains in the software, its use may raise warning messages recommending alternative practices; deprecated status may also indicate the feature will be removed in the future. Features are deprecated rather than immediately removed, to provide backward compatibility and give programmers time to bring affected code into compliance with the new standard.

There are various reasons why a function, method, or feature may be deprecated. To name a few:

  • We may want to remove a function we do not use any longer.
  • We may decide to change or improve how a function works, but due to breaking backwards compatibility we need to deprecate the old function instead.
  • We may need to make naming conventions standardised.
  • We may find opportunities to merge similar functions to avoid reusing the same code in difference places.

In WooCommerce 2.7, we’ve done quite a bit of deprecation mostly due to the new CRUD system. We want developers to make use of the new CRUD objects where possible, but we don’t want to break the old way of accessing data so developers have some time to migrate.

We understand first hand that the process of updating extensions can be made difficult due to the need to support older versions as well as the latest version of WooCommerce, but we want to emphasise the fact that whilst notices are not ideal or attractive, they are just warnings–Store owners; Deprecation warnings do not mean your store is broken, it just serves as a reminder that code will need to be updated.

How do we deprecate functions?

When we deprecate something in WooCommerce, we take a few actions to make it clear to developers and to maintain backwards compatibility.

  1. We add a docblock to the function/method showing what version the function was deprecated. e.g. @deprecated 2.x.x
  2. We add a warning notice using our own wc_deprecated_functionfunction which shows what version, what function, and what replacement is available. More on that in a bit.
  3. We remove usage of the deprecated function throughout of codebase.

The function or method itself is not removed from the codebase. This preserves backwards compatibility until removed (usually over a year or several major releases into the future!).

I mentioned wc_deprecated_function above – this is our own wrapper for the _deprecated_function WordPress function. It works very similar except for that it forces a log entry instead of display regardless of WP_DEBUG mode during AJAX events so that AJAX requests are not broken as a result of the notice.

What happens when a deprecated function is called?

If an extension or theme uses a deprecated function, you may see a warning like the following example:

Notice: woocommerce_show_messages is deprecated since version 2.1! Use wc_print_notices instead. in /srv/www/wordpress-default/wp-includes/functions.php on line 3783

This tells you what is deprecated, when, where, and what replacement is available.

Notices and warnings are usually shown inline but there are some plugins you can use to collect them and shown nicely in the footer of your site. Personally, I Use Query Monitor.

Warnings in production (store owners read this!)

Showing PHP notices and warnings (or any error for that matter) is highly discouraged on your production stores. They can reveal information about your setup which a malicious user could use. Make sure they are hidden from public view, and optionally logged instead.

In WordPress you can do this by adding or modifying some constants in your wp-config.php file.

define( 'WP_DEBUG', false );

On some hosts, errors may still be visible due to the hosts config. To force them to not display you can add this to your wp-config.php file too:

@ini_set( 'display_errors', 0 );

To log notices instead of displaying them, use:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

The default location of the WordPress error log is wp-content/debug.log.

Dealing with deprecation as a developer

Whilst it would be nice to just support the latest versions and remove any deprecated code immediately, this often isn’t possible (unless you’re making plugins for your own personal use). In a perfect world, WordPress would have some form of versioning system whereby you could define what versions X version of your own plugin supported and WordPress could ensure a suitable version is installed. Alas, this mechanism does not exist, so approaching deprecation can be a little tricky.

Therefore, your approach to deprecation may vary depending on what type of plugin you’re offering.

A basic example could be to use function_exists and method_existscalls and run code conditionally (this is preferred over detecting versions installed).

So for example, with the CRUD update say you were getting a products ID you may have been using:

$id = $product->id;

As this will throw a warning in 2.7 you could swap this to:

if ( method_exists( $product, 'get_id' ) ) {
    $id = $product->get_id();
} else {
    $id = $product->id;
}

Or for brevity:

$id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id;

This uses the most up to date method of getting the ID when possible. If you had a plugin which did a lot of the above logic, you could use some other approaches such as:

  • Having wrapper functions in your code to return the correct value based on what is available.
  • Having different files or classes based on version so they can be removed when support is dropped easily.
  • If the plugin is not mission critical, disabling your custom functionality if the minimum version is less than 2.7 and showing a warning before upgrade.

If you do run into a scenario where you want to know the version being ran, you can use our constant and version compare:

if ( version_compare( WC_VERSION, '2.7', '<' ) ) {
    // Older than 2.7
} else {
    // 2.7+
}

We’ll be posting examples in the coming weeks showing how we’re updating our own extensions. Stay tuned!

WooCommerce 2.6.12 fix release notes — January 12, 2017

WooCommerce 2.6.12 fix release notes

The WooCommerce 2.6.12 fix release is now available. You can download it from WordPress.org or as an automatic update in your administration panel.

~12 commits made it into this release fixing a few issues.

The full changelog for 2.6.12 is below.

* 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.

If you spot any further issues, please report them to us in detail on GitHub so the development team can review – comments on this post are closed.

WooCommerce 2.6.10 fix release notes — December 22, 2016

WooCommerce 2.6.10 fix release notes

The WooCommerce 2.6.10 fix release is now available. You can download it from WordPress.org or as an automatic update in your administration panel.

~30 commits made it into this release fixing several issues.

The main change to be aware of surrounds the variation add to cart form script. A few versions back we spotted an incompatibility with the latest version of Firefox which would deselect an attribute on click. This was “fixed” in 2.6.9, however this inadvertently prevented the ‘default selected’ attributes from working correctly in other browsers.

We narrowed this down to the way WooCommerce would filter out non-relevent attributes on select box focus. Some browsers did not like the DOM being changed during the focus event. To resolve this, the DOM needed to be kept up to date and left alone during focus.

The PR which fixed this can be found here. You’ll notice it’s a fairly substantial refactor, and one we wouldn’t usually feel comfortable including in a fix release, but it was necessary to resolve the browser compatibility issues and makes the script more easily understandable. We’ve been testing this internally since merging.

The full changelog for 2.6.10 is below.

* 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.
* Updated Geo IP API services.
* 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.

If you spot any further issues, please report them to us in detail on GitHub so the development team can review – comments on this post are closed.

Happy holidays from the team 🙂