diff --git a/includes/admin/list-tables/class-wc-admin-list-table-orders.php b/includes/admin/list-tables/class-wc-admin-list-table-orders.php index 1b562292c2a..f9babc8825b 100644 --- a/includes/admin/list-tables/class-wc-admin-list-table-orders.php +++ b/includes/admin/list-tables/class-wc-admin-list-table-orders.php @@ -772,15 +772,22 @@ class WC_Admin_List_Table_Orders extends WC_Admin_List_Table { // Filter the orders by the posted customer. if ( ! empty( $_GET['_customer_user'] ) ) { // WPCS: input var ok. - // @codingStandardsIgnoreStart - $query_vars['meta_query'] = array( - array( - 'key' => '_customer_user', - 'value' => (int) $_GET['_customer_user'], // WPCS: input var ok, sanitization ok. - 'compare' => '=', - ), - ); - // @codingStandardsIgnoreEnd + $customer_id = (int) $_GET['_customer_user']; // WPCS: input var ok, sanitization ok. + + // On WC 3.5.0 the ID of the user that placed the order was moved from the post meta _customer_user to the post_author field in the wp_posts table. + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '>=' ) ) { + $query_vars['author'] = $customer_id; + } else { + // @codingStandardsIgnoreStart + $query_vars['meta_query'] = array( + array( + 'key' => '_customer_user', + 'value' => $customer_id, + 'compare' => '=', + ), + ); + // @codingStandardsIgnoreEnd + } } // Sorting. diff --git a/includes/admin/reports/class-wc-report-customers.php b/includes/admin/reports/class-wc-report-customers.php index 503f470992f..326ad5334f5 100644 --- a/includes/admin/reports/class-wc-report-customers.php +++ b/includes/admin/reports/class-wc-report-customers.php @@ -69,46 +69,56 @@ class WC_Report_Customers extends WC_Admin_Report { * Output customers vs guests chart. */ public function customers_vs_guests() { - - $customer_order_totals = $this->get_order_report_data( - array( - 'data' => array( - 'ID' => array( - 'type' => 'post_data', - 'function' => 'COUNT', - 'name' => 'total_orders', - ), + $customer_args = array( + 'data' => array( + 'ID' => array( + 'type' => 'post_data', + 'function' => 'COUNT', + 'name' => 'total_orders', ), - 'where_meta' => array( - array( - 'meta_key' => '_customer_user', - 'meta_value' => '0', - 'operator' => '>', - ), - ), - 'filter_range' => true, - ) + ), + 'filter_range' => true, ); + $guest_args = $customer_args; - $guest_order_totals = $this->get_order_report_data( - array( - 'data' => array( - 'ID' => array( - 'type' => 'post_data', - 'function' => 'COUNT', - 'name' => 'total_orders', - ), + // On WC 3.5.0 the ID of the user that placed the order was moved from the post meta _customer_user to the post_author field in the wp_posts table. + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '>=' ) ) { + $customer_args['where'] = array( + array( + 'key' => 'post_author', + 'value' => '0', + 'operator' => '>', ), - 'where_meta' => array( - array( - 'meta_key' => '_customer_user', - 'meta_value' => '0', - 'operator' => '=', - ), + ); + + $guest_args['where'] = array( + array( + 'key' => 'post_author', + 'value' => '0', + 'operator' => '=', ), - 'filter_range' => true, - ) - ); + ); + } else { + $customer_args['where_meta'] = array( + array( + 'meta_key' => '_customer_user', + 'meta_value' => '0', + 'operator' => '>', + ), + ); + + $guest_args['where_meta'] = array( + array( + 'meta_key' => '_customer_user', + 'meta_value' => '0', + 'operator' => '=', + ), + ); + } + + $customer_order_totals = $this->get_order_report_data( $customer_args ); + $guest_order_totals = $this->get_order_report_data( $guest_args ); + ?>
@@ -246,61 +256,63 @@ class WC_Report_Customers extends WC_Admin_Report { public function get_main_chart() { global $wp_locale; - $customer_orders = $this->get_order_report_data( - array( - 'data' => array( - 'ID' => array( - 'type' => 'post_data', - 'function' => 'COUNT', - 'name' => 'total_orders', - ), - 'post_date' => array( - 'type' => 'post_data', - 'function' => '', - 'name' => 'post_date', - ), + $customer_args = array( + 'data' => array( + 'ID' => array( + 'type' => 'post_data', + 'function' => 'COUNT', + 'name' => 'total_orders', ), - 'where_meta' => array( - array( - 'meta_key' => '_customer_user', - 'meta_value' => '0', - 'operator' => '>', - ), + 'post_date' => array( + 'type' => 'post_data', + 'function' => '', + 'name' => 'post_date', ), - 'group_by' => $this->group_by_query, - 'order_by' => 'post_date ASC', - 'query_type' => 'get_results', - 'filter_range' => true, - ) + ), + 'group_by' => $this->group_by_query, + 'order_by' => 'post_date ASC', + 'query_type' => 'get_results', + 'filter_range' => true, ); + $guest_args = $customer_args; - $guest_orders = $this->get_order_report_data( - array( - 'data' => array( - 'ID' => array( - 'type' => 'post_data', - 'function' => 'COUNT', - 'name' => 'total_orders', - ), - 'post_date' => array( - 'type' => 'post_data', - 'function' => '', - 'name' => 'post_date', - ), + // On WC 3.5.0 the ID of the user that placed the order was moved from the post meta _customer_user to the post_author field in the wp_posts table. + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '>=' ) ) { + $customer_args['where'] = array( + array( + 'key' => 'post_author', + 'value' => '0', + 'operator' => '>', ), - 'where_meta' => array( - array( - 'meta_key' => '_customer_user', - 'meta_value' => '0', - 'operator' => '=', - ), + ); + + $guest_args['where'] = array( + array( + 'key' => 'post_author', + 'value' => '0', + 'operator' => '=', ), - 'group_by' => $this->group_by_query, - 'order_by' => 'post_date ASC', - 'query_type' => 'get_results', - 'filter_range' => true, - ) - ); + ); + } else { + $customer_args['where_meta'] = array( + array( + 'meta_key' => '_customer_user', + 'meta_value' => '0', + 'operator' => '>', + ), + ); + + $guest_args['where_meta'] = array( + array( + 'meta_key' => '_customer_user', + 'meta_value' => '0', + 'operator' => '=', + ), + ); + } + + $customer_orders = $this->get_order_report_data( $customer_args ); + $guest_orders = $this->get_order_report_data( $guest_args ); $signups = $this->prepare_chart_data( $this->customers, 'user_registered', '', $this->chart_interval, $this->start_date, $this->chart_groupby ); $customer_orders = $this->prepare_chart_data( $customer_orders, 'post_date', 'total_orders', $this->chart_interval, $this->start_date, $this->chart_groupby ); diff --git a/includes/api/class-wc-rest-orders-controller.php b/includes/api/class-wc-rest-orders-controller.php index 02957242ac0..c9121bb4465 100644 --- a/includes/api/class-wc-rest-orders-controller.php +++ b/includes/api/class-wc-rest-orders-controller.php @@ -365,15 +365,20 @@ class WC_REST_Orders_Controller extends WC_REST_Legacy_Orders_Controller { } if ( isset( $request['customer'] ) ) { - if ( ! empty( $args['meta_query'] ) ) { - $args['meta_query'] = array(); // WPCS: slow query ok. - } + // On WC 3.5.0 the ID of the user that placed the order was moved from the post meta _customer_user to the post_author field in the wp_posts table. + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '>=' ) ) { + $args['author'] = $request['customer']; + } else { + if ( ! empty( $args['meta_query'] ) ) { + $args['meta_query'] = array(); // WPCS: slow query ok. + } - $args['meta_query'][] = array( - 'key' => '_customer_user', - 'value' => $request['customer'], - 'type' => 'NUMERIC', - ); + $args['meta_query'][] = array( + 'key' => '_customer_user', + 'value' => $request['customer'], + 'type' => 'NUMERIC', + ); + } } // Search by product. diff --git a/includes/api/v1/class-wc-rest-orders-controller.php b/includes/api/v1/class-wc-rest-orders-controller.php index 804e3fb571e..f617192c0bb 100644 --- a/includes/api/v1/class-wc-rest-orders-controller.php +++ b/includes/api/v1/class-wc-rest-orders-controller.php @@ -404,15 +404,20 @@ class WC_REST_Orders_V1_Controller extends WC_REST_Posts_Controller { } if ( isset( $request['customer'] ) ) { - if ( ! empty( $args['meta_query'] ) ) { - $args['meta_query'] = array(); - } + // On WC 3.5.0 the ID of the user that placed the order was moved from the post meta _customer_user to the post_author field in the wp_posts table. + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '>=' ) ) { + $args['author'] = $request['customer']; + } else { + if ( ! empty( $args['meta_query'] ) ) { + $args['meta_query'] = array(); // WPCS: slow query ok. + } - $args['meta_query'][] = array( - 'key' => '_customer_user', - 'value' => $request['customer'], - 'type' => 'NUMERIC', - ); + $args['meta_query'][] = array( + 'key' => '_customer_user', + 'value' => $request['customer'], + 'type' => 'NUMERIC', + ); + } } // Search by product. diff --git a/includes/class-wc-background-updater.php b/includes/class-wc-background-updater.php index 3f80666a767..1c28f7f26da 100644 --- a/includes/class-wc-background-updater.php +++ b/includes/class-wc-background-updater.php @@ -132,4 +132,13 @@ class WC_Background_Updater extends WC_Background_Process { WC_Install::update_db_version(); parent::complete(); } + + /** + * See if the batch limit has been exceeded. + * + * @return bool + */ + public function is_memory_exceeded() { + return $this->memory_exceeded(); + } } diff --git a/includes/class-wc-cart-totals.php b/includes/class-wc-cart-totals.php index bb2c384a113..538977363f0 100644 --- a/includes/class-wc-cart-totals.php +++ b/includes/class-wc-cart-totals.php @@ -637,7 +637,7 @@ final class WC_Cart_Totals { } if ( $this->calculate_tax && $item->product->is_taxable() ) { - $total_taxes = WC_Tax::calc_tax( $item->total, $item->tax_rates, $item->price_includes_tax ); + $total_taxes = apply_filters( 'woocommerce_calculate_item_totals_taxes', WC_Tax::calc_tax( $item->total, $item->tax_rates, $item->price_includes_tax ), $item, $this ); $item->taxes = $total_taxes; $item->total_tax = array_sum( array_map( array( $this, 'round_line_tax' ), $item->taxes ) ); diff --git a/includes/class-wc-cart.php b/includes/class-wc-cart.php index d5ea1c5eb9a..293dcf772ec 100644 --- a/includes/class-wc-cart.php +++ b/includes/class-wc-cart.php @@ -1385,28 +1385,27 @@ class WC_Cart extends WC_Legacy_Cart { * @return string price or string for the shipping total */ public function get_cart_shipping_total() { + + // Default total assumes Free shipping. + $total = __( 'Free!', 'woocommerce' ); + if ( 0 < $this->get_shipping_total() ) { if ( $this->display_prices_including_tax() ) { - $return = wc_price( $this->shipping_total + $this->shipping_tax_total ); + $total = wc_price( $this->shipping_total + $this->shipping_tax_total ); if ( $this->shipping_tax_total > 0 && ! wc_prices_include_tax() ) { - $return .= ' ' . WC()->countries->inc_tax_or_vat() . ''; + $total .= ' ' . WC()->countries->inc_tax_or_vat() . ''; } - - return $return; } else { - $return = wc_price( $this->shipping_total ); + $total = wc_price( $this->shipping_total ); if ( $this->shipping_tax_total > 0 && wc_prices_include_tax() ) { - $return .= ' ' . WC()->countries->ex_tax_or_vat() . ''; + $total .= ' ' . WC()->countries->ex_tax_or_vat() . ''; } - - return $return; } - } else { - return __( 'Free!', 'woocommerce' ); } + return apply_filters( 'woocommerce_cart_shipping_total', $total, $this ); } /** diff --git a/includes/class-wc-emails.php b/includes/class-wc-emails.php index 207b00c1d84..1ff431bc6c5 100644 --- a/includes/class-wc-emails.php +++ b/includes/class-wc-emails.php @@ -223,11 +223,6 @@ class WC_Emails { $this->emails['WC_Email_Customer_New_Account'] = include 'emails/class-wc-email-customer-new-account.php'; $this->emails = apply_filters( 'woocommerce_email_classes', $this->emails ); - - // include css inliner. - if ( ! class_exists( 'Emogrifier' ) && class_exists( 'DOMDocument' ) ) { - include_once dirname( __FILE__ ) . '/libraries/class-emogrifier.php'; - } } /** diff --git a/includes/class-wc-geolite-integration.php b/includes/class-wc-geolite-integration.php index 2d97ccd54f8..49e9fc9941f 100644 --- a/includes/class-wc-geolite-integration.php +++ b/includes/class-wc-geolite-integration.php @@ -46,7 +46,7 @@ class WC_Geolite_Integration { /** * Get country 2-letters ISO by IP address. - * Retuns empty string when not able to find any ISO code. + * Returns empty string when not able to find any ISO code. * * @param string $ip_address User IP address. * @return string @@ -55,9 +55,12 @@ class WC_Geolite_Integration { $iso_code = ''; try { - $reader = new MaxMind\Db\Reader( $this->database ); // phpcs:ignore PHPCompatibility.PHP.NewLanguageConstructs.t_ns_separatorFound - $data = $reader->get( $ip_address ); - $iso_code = $data['country']['iso_code']; + $reader = new MaxMind\Db\Reader( $this->database ); // phpcs:ignore PHPCompatibility.PHP.NewLanguageConstructs.t_ns_separatorFound + $data = $reader->get( $ip_address ); + + if ( isset( $data['country']['iso_code'] ) ) { + $iso_code = $data['country']['iso_code']; + } $reader->close(); } catch ( Exception $e ) { diff --git a/includes/class-wc-install.php b/includes/class-wc-install.php index b0bcb358020..e07100c8262 100644 --- a/includes/class-wc-install.php +++ b/includes/class-wc-install.php @@ -109,6 +109,10 @@ class WC_Install { 'wc_update_343_cleanup_foreign_keys', 'wc_update_343_db_version', ), + '3.5.0' => array( + 'wc_update_350_order_customer_id', + 'wc_update_350_db_version', + ), ); /** diff --git a/includes/class-wc-regenerate-images.php b/includes/class-wc-regenerate-images.php index ae9c371f3dd..c3cb3352817 100644 --- a/includes/class-wc-regenerate-images.php +++ b/includes/class-wc-regenerate-images.php @@ -36,11 +36,7 @@ class WC_Regenerate_Images { public static function init() { add_action( 'image_get_intermediate_size', array( __CLASS__, 'filter_image_get_intermediate_size' ), 10, 3 ); add_filter( 'wp_generate_attachment_metadata', array( __CLASS__, 'add_uncropped_metadata' ) ); - - // Resize WooCommerce images on the fly when browsing site through customizer as to showcase image setting changes in real time. - if ( is_customize_preview() ) { - add_filter( 'wp_get_attachment_image_src', array( __CLASS__, 'maybe_resize_image' ), 10, 4 ); - } + add_filter( 'wp_get_attachment_image_src', array( __CLASS__, 'maybe_resize_image' ), 10, 4 ); // Not required when Jetpack Photon is in use. if ( method_exists( 'Jetpack', 'is_module_active' ) && Jetpack::is_module_active( 'photon' ) ) { @@ -206,18 +202,32 @@ class WC_Regenerate_Images { } // Use a whitelist of sizes we want to resize. Ignore others. - if ( ! in_array( $size, apply_filters( 'woocommerce_image_sizes_to_resize', array( 'woocommerce_thumbnail', 'woocommerce_gallery_thumbnail', 'woocommerce_single', 'shop_thumbnail', 'shop_catalog', 'shop_single' ) ), true ) ) { + if ( ! $image || ! in_array( $size, apply_filters( 'woocommerce_image_sizes_to_resize', array( 'woocommerce_thumbnail', 'woocommerce_gallery_thumbnail', 'woocommerce_single', 'shop_thumbnail', 'shop_catalog', 'shop_single' ) ), true ) ) { return $image; } - // Get image metadata - we need it to proceed. - $imagemeta = wp_get_attachment_metadata( $attachment_id ); + $image_size = wc_get_image_size( $size ); + $ratio_match = false; - if ( empty( $imagemeta ) ) { - return $image; + // If '' is passed to either size, we test ratios against the original file. It's uncropped. + if ( '' === $image_size['width'] || '' === $image_size['height'] ) { + $imagedata = wp_get_attachment_metadata( $attachment_id ); + + if ( ! $imagedata ) { + return $image; + } + + if ( ! isset( $imagedata['file'] ) && isset( $imagedata['sizes']['full'] ) ) { + $imagedata['height'] = $imagedata['sizes']['full']['height']; + $imagedata['width'] = $imagedata['sizes']['full']['width']; + } + + $ratio_match = wp_image_matches_ratio( $image[1], $image[2], $imagedata['width'], $imagedata['height'] ); + } else { + $ratio_match = wp_image_matches_ratio( $image[1], $image[2], $image_size['width'], $image_size['height'] ); } - if ( ! isset( $imagemeta['sizes'], $imagemeta['sizes'][ $size ] ) || ! self::image_size_matches_settings( $imagemeta['sizes'][ $size ], $size ) ) { + if ( ! $ratio_match ) { return self::resize_and_return_image( $attachment_id, $image, $size, $icon ); } @@ -316,28 +326,30 @@ class WC_Regenerate_Images { include ABSPATH . 'wp-admin/includes/image.php'; } - $thumbnail = self::get_image( $fullsizepath, $image_size['width'], $image_size['height'], $image_size['crop'] ); + self::$regenerate_size = is_customize_preview() ? $size . '_preview' : $size; - // If the file is already there perhaps just load it. - if ( $thumbnail && file_exists( $thumbnail['filename'] ) ) { - $wp_uploads = wp_upload_dir( null, false ); - $wp_uploads_dir = $wp_uploads['basedir']; - $wp_uploads_url = $wp_uploads['baseurl']; + if ( is_customize_preview() ) { + // Make sure registered image size matches the size we're requesting. + add_image_size( self::$regenerate_size, absint( $image_size['width'] ), absint( $image_size['height'] ), $image_size['crop'] ); - return array( - 0 => str_replace( $wp_uploads_dir, $wp_uploads_url, $thumbnail['filename'] ), - 1 => $thumbnail['width'], - 2 => $thumbnail['height'], - ); + $thumbnail = self::get_image( $fullsizepath, absint( $image_size['width'] ), absint( $image_size['height'] ), $image_size['crop'] ); + + // If the file is already there perhaps just load it if we're using the customizer. No need to store in meta data. + if ( $thumbnail && file_exists( $thumbnail['filename'] ) ) { + $wp_uploads = wp_upload_dir( null, false ); + $wp_uploads_dir = $wp_uploads['basedir']; + $wp_uploads_url = $wp_uploads['baseurl']; + + return array( + 0 => str_replace( $wp_uploads_dir, $wp_uploads_url, $thumbnail['filename'] ), + 1 => $thumbnail['width'], + 2 => $thumbnail['height'], + ); + } } $metadata = wp_get_attachment_metadata( $attachment_id ); - // Make sure registered image size matches the size we're requesting. - add_image_size( $size . '_preview', $image_size['width'], $image_size['height'], $image_size['crop'] ); - - self::$regenerate_size = $size . '_preview'; - // We only want to regen a specific image size. add_filter( 'intermediate_image_sizes', array( __CLASS__, 'adjust_intermediate_image_sizes' ) ); @@ -352,14 +364,13 @@ class WC_Regenerate_Images { return $image; } - // Since this is only a preview we should not update the actual size. That will be done later by the background job. - if ( isset( $new_metadata['sizes'][ $size . '_preview' ] ) ) { - $metadata['sizes'][ $size . '_preview' ] = $new_metadata['sizes'][ $size . '_preview' ]; + if ( isset( $new_metadata['sizes'][ self::$regenerate_size ] ) ) { + $metadata['sizes'][ self::$regenerate_size ] = $new_metadata['sizes'][ self::$regenerate_size ]; wp_update_attachment_metadata( $attachment_id, $metadata ); } // Now we've done our regen, attempt to return the new size. - $new_image = image_downsize( $attachment_id, $size . '_preview' ); + $new_image = image_downsize( $attachment_id, self::$regenerate_size ); return $new_image ? $new_image : $image; } diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php index a98d4076c85..9f7e1380f85 100644 --- a/includes/class-woocommerce.php +++ b/includes/class-woocommerce.php @@ -20,7 +20,7 @@ final class WooCommerce { * * @var string */ - public $version = '3.4.0'; + public $version = '3.5.0'; /** * The single instance of the class. diff --git a/includes/data-stores/abstract-wc-order-data-store-cpt.php b/includes/data-stores/abstract-wc-order-data-store-cpt.php index 73ff2844cb0..0d6208887da 100644 --- a/includes/data-stores/abstract-wc-order-data-store-cpt.php +++ b/includes/data-stores/abstract-wc-order-data-store-cpt.php @@ -67,7 +67,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme 'post_type' => $order->get_type( 'edit' ), 'post_status' => 'wc-' . ( $order->get_status( 'edit' ) ? $order->get_status( 'edit' ) : apply_filters( 'woocommerce_default_order_status', 'pending' ) ), 'ping_status' => 'closed', - 'post_author' => 1, + 'post_author' => is_callable( array( $order, 'get_customer_id' ) ) ? $order->get_customer_id() : 0, 'post_title' => $this->get_post_title(), 'post_password' => uniqid( 'order_' ), 'post_parent' => $order->get_parent_id( 'edit' ), @@ -139,7 +139,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme $changes = $order->get_changes(); // Only update the post when the post data changes. - if ( array_intersect( array( 'date_created', 'date_modified', 'status', 'parent_id', 'post_excerpt' ), array_keys( $changes ) ) ) { + if ( array_intersect( array( 'date_created', 'date_modified', 'status', 'parent_id', 'post_excerpt', 'customer_id' ), array_keys( $changes ) ) ) { $post_data = array( 'post_date' => gmdate( 'Y-m-d H:i:s', $order->get_date_created( 'edit' )->getOffsetTimestamp() ), 'post_date_gmt' => gmdate( 'Y-m-d H:i:s', $order->get_date_created( 'edit' )->getTimestamp() ), @@ -148,6 +148,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme 'post_excerpt' => $this->get_post_excerpt( $order ), 'post_modified' => isset( $changes['date_modified'] ) ? gmdate( 'Y-m-d H:i:s', $order->get_date_modified( 'edit' )->getOffsetTimestamp() ) : current_time( 'mysql' ), 'post_modified_gmt' => isset( $changes['date_modified'] ) ? gmdate( 'Y-m-d H:i:s', $order->get_date_modified( 'edit' )->getTimestamp() ) : current_time( 'mysql', 1 ), + 'post_author' => is_callable( array( $order, 'get_customer_id' ) ) ? $order->get_customer_id() : 0, ); /** @@ -165,12 +166,27 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme wp_update_post( array_merge( array( 'ID' => $order->get_id() ), $post_data ) ); } $order->read_meta_data( true ); // Refresh internal meta data, in case things were hooked into `save_post` or another WP hook. + + // If customer changed, update any downloadable permissions. + if ( in_array( 'customer_id', $changes ) ) { + $this->update_downloadable_permissions( $order ); + } } $this->update_post_meta( $order ); $order->apply_changes(); $this->clear_caches( $order ); } + /** + * Update downloadable permissions for a given order. + * + * @param WC_Order $order Order object. + */ + protected function update_downloadable_permissions( $order ) { + $data_store = WC_Data_Store::load( 'customer-download' ); + $data_store->update_user_by_order_id( $order->get_id(), $order->get_customer_id(), $order->get_billing_email() ); + } + /** * Method to delete an order from the database. * diff --git a/includes/data-stores/class-wc-customer-data-store.php b/includes/data-stores/class-wc-customer-data-store.php index d907e61893c..8ad38340b3f 100644 --- a/includes/data-stores/class-wc-customer-data-store.php +++ b/includes/data-stores/class-wc-customer-data-store.php @@ -325,18 +325,27 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat public function get_last_order( &$customer ) { global $wpdb; - $last_order = $wpdb->get_var( - // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared - "SELECT posts.ID + // On WC 3.5.0 the ID of the user that placed the order was moved from the post meta _customer_user to the post_author field in the wp_posts table. + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '>=' ) ) { + $query = "SELECT ID + FROM $wpdb->posts + WHERE post_author = '" . esc_sql( $customer->get_id() ) . "' + AND post_type = 'shop_order' + AND post_status IN ( '" . implode( "','", array_map( 'esc_sql', array_keys( wc_get_order_statuses() ) ) ) . "' ) + ORDER BY ID DESC"; + } else { + $query = "SELECT posts.ID FROM $wpdb->posts AS posts LEFT JOIN {$wpdb->postmeta} AS meta on posts.ID = meta.post_id WHERE meta.meta_key = '_customer_user' AND meta.meta_value = '" . esc_sql( $customer->get_id() ) . "' AND posts.post_type = 'shop_order' AND posts.post_status IN ( '" . implode( "','", array_map( 'esc_sql', array_keys( wc_get_order_statuses() ) ) ) . "' ) - ORDER BY posts.ID DESC" - // phpcs:enable - ); + ORDER BY posts.ID DESC"; + } + + // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared + $last_order = $wpdb->get_var( $query ); if ( ! $last_order ) { return false; @@ -358,17 +367,25 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat if ( '' === $count ) { global $wpdb; - $count = $wpdb->get_var( - // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared - "SELECT COUNT(*) + // On WC 3.5.0 the ID of the user that placed the order was moved from the post meta _customer_user to the post_author field in the wp_posts table. + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '>=' ) ) { + $query = "SELECT COUNT(*) + FROM $wpdb->posts + WHERE post_type = 'shop_order' + AND post_status IN ( '" . implode( "','", array_map( 'esc_sql', array_keys( wc_get_order_statuses() ) ) ) . "' ) + AND post_author = " . esc_sql( $customer->get_id() ); + } else { + $query = "SELECT COUNT(*) FROM $wpdb->posts as posts LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id WHERE meta.meta_key = '_customer_user' AND posts.post_type = 'shop_order' AND posts.post_status IN ( '" . implode( "','", array_map( 'esc_sql', array_keys( wc_get_order_statuses() ) ) ) . "' ) - AND meta_value = '" . esc_sql( $customer->get_id() ) . "'" - // phpcs:enable - ); + AND meta_value = '" . esc_sql( $customer->get_id() ) . "'"; + } + + // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared + $count = $wpdb->get_var( $query ); update_user_meta( $customer->get_id(), '_order_count', $count ); } @@ -393,11 +410,18 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat global $wpdb; $statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() ); - $spent = $wpdb->get_var( - // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared - apply_filters( - 'woocommerce_customer_get_total_spent_query', - "SELECT SUM(meta2.meta_value) + + // On WC 3.5.0 the ID of the user that placed the order was moved from the post meta _customer_user to the post_author field in the wp_posts table. + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '>=' ) ) { + $query = "SELECT SUM(meta.meta_value) + FROM $wpdb->posts as posts + LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id + WHERE posts.post_author = '" . esc_sql( $customer->get_id() ) . "' + AND posts.post_type = 'shop_order' + AND posts.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' ) + AND meta.meta_key = '_order_total'"; + } else { + $query = "SELECT SUM(meta2.meta_value) FROM $wpdb->posts as posts LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id LEFT JOIN {$wpdb->postmeta} AS meta2 ON posts.ID = meta2.post_id @@ -405,11 +429,11 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat AND meta.meta_value = '" . esc_sql( $customer->get_id() ) . "' AND posts.post_type = 'shop_order' AND posts.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' ) - AND meta2.meta_key = '_order_total'", - $customer - ) - // phpcs:enable - ); + AND meta2.meta_key = '_order_total'"; + } + + // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared + $spent = $wpdb->get_var( apply_filters( 'woocommerce_customer_get_total_spent_query', $query, $customer ) ); if ( ! $spent ) { $spent = 0; diff --git a/includes/data-stores/class-wc-order-data-store-cpt.php b/includes/data-stores/class-wc-order-data-store-cpt.php index 2cc5b7e1fcd..316aa4b8373 100644 --- a/includes/data-stores/class-wc-order-data-store-cpt.php +++ b/includes/data-stores/class-wc-order-data-store-cpt.php @@ -107,10 +107,17 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement $date_paid = get_post_meta( $id, '_paid_date', true ); } + // On WC 3.5.0 the ID of the user that placed the order was moved from the post meta _customer_user to the post_author field in the wp_posts table. + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '>=' ) ) { + $customer_id = $post_object->post_author; + } else { + $customer_id = get_post_meta( $id, '_customer_user', true ); + } + $order->set_props( array( 'order_key' => get_post_meta( $id, '_order_key', true ), - 'customer_id' => get_post_meta( $id, '_customer_user', true ), + 'customer_id' => $customer_id, 'billing_first_name' => get_post_meta( $id, '_billing_first_name', true ), 'billing_last_name' => get_post_meta( $id, '_billing_last_name', true ), 'billing_company' => get_post_meta( $id, '_billing_company', true ), @@ -258,10 +265,9 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement update_post_meta( $id, '_shipping_address_index', implode( ' ', $order->get_address( 'shipping' ) ) ); } - // If customer changed, update any downloadable permissions. - if ( in_array( 'customer_id', $updated_props, true ) || in_array( 'billing_email', $updated_props, true ) ) { - $data_store = WC_Data_Store::load( 'customer-download' ); - $data_store->update_user_by_order_id( $id, $order->get_customer_id(), $order->get_billing_email() ); + // If customer email changed, update any downloadable permissions. + if ( in_array( 'billing_email', $updated_props ) ) { + $this->update_downloadable_permissions( $order ); } // Mark user account as active. @@ -645,6 +651,11 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement 'page' => 'paged', ); + // On WC 3.5.0 the ID of the user that placed the order was moved from the post meta _customer_user to the post_author field in the wp_posts table. + if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '>=' ) ) { + $key_mapping['customer_id'] = 'author'; + } + foreach ( $key_mapping as $query_key => $db_key ) { if ( isset( $query_vars[ $query_key ] ) ) { $query_vars[ $db_key ] = $query_vars[ $query_key ]; diff --git a/includes/emails/class-wc-email.php b/includes/emails/class-wc-email.php index c7d72fa3bc4..fd36a929e90 100644 --- a/includes/emails/class-wc-email.php +++ b/includes/emails/class-wc-email.php @@ -510,28 +510,45 @@ class WC_Email extends WC_Settings_API { /** * Apply inline styles to dynamic content. * + * We only inline CSS for html emails, and to do so we use Emogrifier library (if supported). + * * @param string|null $content Content that will receive inline styles. * @return string */ public function style_inline( $content ) { - // make sure we only inline CSS for html emails. - if ( in_array( $this->get_content_type(), array( 'text/html', 'multipart/alternative' ), true ) && class_exists( 'DOMDocument' ) ) { + if ( in_array( $this->get_content_type(), array( 'text/html', 'multipart/alternative' ), true ) ) { ob_start(); wc_get_template( 'emails/email-styles.php' ); $css = apply_filters( 'woocommerce_email_styles', ob_get_clean() ); - // apply CSS styles inline for picky email clients. - try { - $emogrifier = new Emogrifier( $content, $css ); - $content = $emogrifier->emogrify(); - } catch ( Exception $e ) { - $logger = wc_get_logger(); - $logger->error( $e->getMessage(), array( 'source' => 'emogrifier' ) ); + if ( $this->supports_emogrifier() ) { + if ( ! class_exists( 'Emogrifier' ) ) { + include_once dirname( dirname( __FILE__ ) ) . '/libraries/class-emogrifier.php'; + } + try { + $emogrifier = new Emogrifier( $content, $css ); + $content = $emogrifier->emogrify(); + } catch ( Exception $e ) { + $logger = wc_get_logger(); + $logger->error( $e->getMessage(), array( 'source' => 'emogrifier' ) ); + } + } else { + $content = '' . $content; } } return $content; } + /** + * Return if emogrifier library is supported. + * + * @since 3.5.0 + * @return bool + */ + protected function supports_emogrifier() { + return class_exists( 'DOMDocument' ) && version_compare( PHP_VERSION, '5.5', '>=' ); + } + /** * Get the email content in plain text format. * diff --git a/includes/libraries/class-emogrifier.php b/includes/libraries/class-emogrifier.php index 490c2586ce2..12d6c82722a 100644 --- a/includes/libraries/class-emogrifier.php +++ b/includes/libraries/class-emogrifier.php @@ -4,1552 +4,1808 @@ * * For more information, please see the README.md file. * - * @version 1.2.0 + * @version 2.0.0 * * @author Cameron Brooks * @author Jaime Prado - * @author Oliver Klee + * @author Oliver Klee * @author Roman Ožana * @author Sander Kruger + * @author Zoli Szabó */ -// @codingStandardsIgnoreFile class Emogrifier { - /** - * @var int - */ - const CACHE_KEY_CSS = 0; - - /** - * @var int - */ - const CACHE_KEY_SELECTOR = 1; - - /** - * @var int - */ - const CACHE_KEY_XPATH = 2; - - /** - * @var int - */ - const CACHE_KEY_CSS_DECLARATIONS_BLOCK = 3; - - /** - * @var int - */ - const CACHE_KEY_COMBINED_STYLES = 4; - - /** - * for calculating nth-of-type and nth-child selectors - * - * @var int - */ - const INDEX = 0; - - /** - * for calculating nth-of-type and nth-child selectors - * - * @var int - */ - const MULTIPLIER = 1; - - /** - * @var string - */ - const ID_ATTRIBUTE_MATCHER = '/(\\w+)?\\#([\\w\\-]+)/'; - - /** - * @var string - */ - const CLASS_ATTRIBUTE_MATCHER = '/(\\w+|[\\*\\]])?((\\.[\\w\\-]+)+)/'; - - /** - * @var string - */ - const CONTENT_TYPE_META_TAG = ''; - - /** - * @var string - */ - const DEFAULT_DOCUMENT_TYPE = ''; - - /** - * @var string - */ - private $html = ''; - - /** - * @var string - */ - private $css = ''; - - /** - * @var bool[] - */ - private $excludedSelectors = array(); - - /** - * @var string[] - */ - private $unprocessableHtmlTags = array( 'wbr' ); - - /** - * @var bool[] - */ - private $allowedMediaTypes = array( 'all' => true, 'screen' => true, 'print' => true ); - - /** - * @var mixed[] - */ - private $caches = array( - self::CACHE_KEY_CSS => array(), - self::CACHE_KEY_SELECTOR => array(), - self::CACHE_KEY_XPATH => array(), - self::CACHE_KEY_CSS_DECLARATIONS_BLOCK => array(), - self::CACHE_KEY_COMBINED_STYLES => array(), - ); - - /** - * the visited nodes with the XPath paths as array keys - * - * @var \DOMElement[] - */ - private $visitedNodes = array(); - - /** - * the styles to apply to the nodes with the XPath paths as array keys for the outer array - * and the attribute names/values as key/value pairs for the inner array - * - * @var string[][] - */ - private $styleAttributesForNodes = array(); - - /** - * Determines whether the "style" attributes of tags in the the HTML passed to this class should be preserved. - * If set to false, the value of the style attributes will be discarded. - * - * @var bool - */ - private $isInlineStyleAttributesParsingEnabled = true; - - /** - * Determines whether the