From d56edfd53aadd40d0c56b501ac49042d8d84be05 Mon Sep 17 00:00:00 2001 From: Khan M Rashedun-Naby Date: Tue, 17 Jul 2018 22:57:32 +0600 Subject: [PATCH 001/401] #20696 Cart widget Customizer selective refresh fix --- includes/widgets/class-wc-widget-cart.php | 39 +++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/includes/widgets/class-wc-widget-cart.php b/includes/widgets/class-wc-widget-cart.php index e699476d135..d9543e61332 100644 --- a/includes/widgets/class-wc-widget-cart.php +++ b/includes/widgets/class-wc-widget-cart.php @@ -36,6 +36,10 @@ class WC_Widget_Cart extends WC_Widget { ), ); + if ( is_customize_preview() ) { + $this->enqueue_ajax_script(); + } + parent::__construct(); } @@ -69,4 +73,39 @@ class WC_Widget_Cart extends WC_Widget { $this->widget_end( $args ); } + + /** + * This method provides the JS script which will execute on addition of a new widget. + * + * @return void + */ + private function enqueue_ajax_script() { + $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; + + wp_register_script( 'wc-cart-fragments', WC()->plugin_url() . '/assets/js/frontend/cart-fragments' . $suffix . '.js', array( 'jquery', 'js-cookie' ), WC_VERSION, true ); + + wp_localize_script( 'wc-cart-fragments', 'wc_cart_fragments_params', + array( + 'ajax_url' => WC()->ajax_url(), + 'wc_ajax_url' => WC_AJAX::get_endpoint( '%%endpoint%%' ), + 'cart_hash_key' => apply_filters( 'woocommerce_cart_hash_key', 'wc_cart_hash_' . md5( get_current_blog_id() . '_' . get_site_url( get_current_blog_id(), '/' ) . get_template() ) ), + 'fragment_name' => apply_filters( 'woocommerce_cart_fragment_name', 'wc_fragments_' . md5( get_current_blog_id() . '_' . get_site_url( get_current_blog_id(), '/' ) . get_template() ) ), + ) + ); + + wp_enqueue_script( 'wc-cart-fragments' ); + + ?> + + Date: Tue, 14 Aug 2018 13:03:02 +0600 Subject: [PATCH 002/401] Added @todo comment to PHPDoc --- includes/widgets/class-wc-widget-cart.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/includes/widgets/class-wc-widget-cart.php b/includes/widgets/class-wc-widget-cart.php index d9543e61332..41bab3984d9 100644 --- a/includes/widgets/class-wc-widget-cart.php +++ b/includes/widgets/class-wc-widget-cart.php @@ -77,6 +77,9 @@ class WC_Widget_Cart extends WC_Widget { /** * This method provides the JS script which will execute on addition of a new widget. * + * @todo 1. In this function there is a redundency of code, which needs to be fixed. + * 2. Also sort out a better way to fix the raw JS code block. May be using `wc_enqueue_js()`. + * * @return void */ private function enqueue_ajax_script() { From e41a6199bbf63ba19467998ea14755d31055e596 Mon Sep 17 00:00:00 2001 From: Khan M Rashedun-Naby Date: Tue, 14 Aug 2018 13:11:23 +0600 Subject: [PATCH 003/401] Updated @todo comment --- includes/widgets/class-wc-widget-cart.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/widgets/class-wc-widget-cart.php b/includes/widgets/class-wc-widget-cart.php index 41bab3984d9..d81a8e863cf 100644 --- a/includes/widgets/class-wc-widget-cart.php +++ b/includes/widgets/class-wc-widget-cart.php @@ -77,8 +77,8 @@ class WC_Widget_Cart extends WC_Widget { /** * This method provides the JS script which will execute on addition of a new widget. * - * @todo 1. In this function there is a redundency of code, which needs to be fixed. - * 2. Also sort out a better way to fix the raw JS code block. May be using `wc_enqueue_js()`. + * @todo 1. In this function there is redundency of code, which needs to be fixed. + * 2. Also sort out a better way to fix the later raw JS code block. May be do it a more WordPress way. * * @return void */ From 0f6f6814ad8ffc3db352dac2c031b32a4150010b Mon Sep 17 00:00:00 2001 From: Florian Ludwig Date: Tue, 14 Aug 2018 18:08:55 +0200 Subject: [PATCH 004/401] Moves cart hash calculation to WC_Cart class --- includes/class-wc-ajax.php | 2 +- includes/class-wc-cart-session.php | 2 +- includes/class-wc-cart.php | 11 +++++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php index d3eb96aa941..2a78d9585f8 100644 --- a/includes/class-wc-ajax.php +++ b/includes/class-wc-ajax.php @@ -178,7 +178,7 @@ class WC_AJAX { 'div.widget_shopping_cart_content' => '
' . $mini_cart . '
', ) ), - 'cart_hash' => apply_filters( 'woocommerce_add_to_cart_hash', WC()->cart->get_cart_for_session() ? md5( json_encode( WC()->cart->get_cart_for_session() ) ) : '', WC()->cart->get_cart_for_session() ), + 'cart_hash' => WC()->cart->get_cart_hash(), ); wp_send_json( $data ); diff --git a/includes/class-wc-cart-session.php b/includes/class-wc-cart-session.php index ea8023a72b2..f1cc4abd382 100644 --- a/includes/class-wc-cart-session.php +++ b/includes/class-wc-cart-session.php @@ -228,7 +228,7 @@ final class WC_Cart_Session { private function set_cart_cookies( $set = true ) { if ( $set ) { wc_setcookie( 'woocommerce_items_in_cart', 1 ); - wc_setcookie( 'woocommerce_cart_hash', md5( wp_json_encode( $this->get_cart_for_session() ) ) ); + wc_setcookie( 'woocommerce_cart_hash', WC()->cart->get_cart_hash() ); } elseif ( isset( $_COOKIE['woocommerce_items_in_cart'] ) ) { // WPCS: input var ok. wc_setcookie( 'woocommerce_items_in_cart', 0, time() - HOUR_IN_SECONDS ); wc_setcookie( 'woocommerce_cart_hash', '', time() - HOUR_IN_SECONDS ); diff --git a/includes/class-wc-cart.php b/includes/class-wc-cart.php index ae5f00e5aaf..b31bed3b7ed 100644 --- a/includes/class-wc-cart.php +++ b/includes/class-wc-cart.php @@ -1938,4 +1938,15 @@ class WC_Cart extends WC_Legacy_Cart { $this->fees_api->remove_all_fees(); do_action( 'woocommerce_cart_reset', $this, false ); } + + /** + * Returns the hash based on cart contents. + * + * @return string hash for cart content + */ + public function get_cart_hash() { + $hash = $this->session->get_cart_for_session() ? md5( json_encode( $this->session->get_cart_for_session() ) ) : ''; + + return apply_filters( 'woocommerce_add_to_cart_hash', $hash, $this->get_cart_for_session() ); + } } From 0f7b470167b899fb29d18cc7e62bdeab700be195 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Fri, 17 Aug 2018 19:13:44 +0200 Subject: [PATCH 005/401] Added test if request is a REST API request so that cart is not loaded. Code inspired by WC_REST_Authentication::is_request_to_rest_api, but included also legacy API . --- includes/class-woocommerce.php | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php index 4c142be2e1f..82343396190 100644 --- a/includes/class-woocommerce.php +++ b/includes/class-woocommerce.php @@ -227,6 +227,34 @@ final class WooCommerce { } } + /** + * Returns true if the request is a REST API request. + * + * @return bool + */ + private static function is_rest_api_request() { + $request_uri = $_SERVER['REQUEST_URI']; // @codingStandardsIgnoreLine + if ( empty( $request_uri ) ) { + return false; + } + + // Legacy API. + if ( 0 === strpos( $request_uri, '/wc-api/' ) ) { + return true; + } + + // New REST API. + $rest_prefix = trailingslashit( rest_get_url_prefix() ); + + // Check if this is WC endpoint. + $woocommerce = ( false !== strpos( $request_uri, $rest_prefix . 'wc/' ) ); + + // Allow third party plugins use our authentication methods. + $third_party = ( false !== strpos( $request_uri, $rest_prefix . 'wc-' ) ); + + return apply_filters( 'woocommerce_rest_is_request_to_rest_api', $woocommerce || $third_party ); + } + /** * What type of request is this? * @@ -242,7 +270,7 @@ final class WooCommerce { case 'cron': return defined( 'DOING_CRON' ); case 'frontend': - return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' ) && ! defined( 'REST_REQUEST' ); + return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' ) && ! self::is_rest_api_request(); } } From 3e4791fbb4e9f64bd4e446e7efa0d4a214fbee0c Mon Sep 17 00:00:00 2001 From: Florian Ludwig Date: Wed, 5 Sep 2018 10:16:01 +0200 Subject: [PATCH 006/401] Fixes wrong usage of deprecated get_cart_for_session --- includes/class-wc-cart.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-cart.php b/includes/class-wc-cart.php index b31bed3b7ed..1c4bdc139ad 100644 --- a/includes/class-wc-cart.php +++ b/includes/class-wc-cart.php @@ -1945,8 +1945,9 @@ class WC_Cart extends WC_Legacy_Cart { * @return string hash for cart content */ public function get_cart_hash() { - $hash = $this->session->get_cart_for_session() ? md5( json_encode( $this->session->get_cart_for_session() ) ) : ''; + $cart = $this->session->get_cart_for_session(); + $hash = $cart ? md5( json_encode( $cart ) ) : ''; - return apply_filters( 'woocommerce_add_to_cart_hash', $hash, $this->get_cart_for_session() ); + return apply_filters( 'woocommerce_add_to_cart_hash', $hash, $cart ); } } From 9e01b0dfe20eeb78bfbb935ba0c4ad95e403b613 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 26 Sep 2018 18:58:59 +0200 Subject: [PATCH 007/401] Update order of remember me and submit button Have the same structure and logic as wp-login.php - Seems more logic to have the checkbox of Remember me before the Login button. --- templates/myaccount/form-login.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/myaccount/form-login.php b/templates/myaccount/form-login.php index b4d4e589eba..c80463492ec 100644 --- a/templates/myaccount/form-login.php +++ b/templates/myaccount/form-login.php @@ -47,11 +47,11 @@ do_action( 'woocommerce_before_customer_login_form' ); ?>

- - + +

From e95842228a22cac80f95488dbe61f0d11c2001dc Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Mon, 3 Dec 2018 11:30:26 -0400 Subject: [PATCH 008/401] phpcs sniff fixes wc-attribute-functions.php, introduct `wc_attribute_taxonomy_name_raw()` --- includes/wc-attribute-functions.php | 37 ++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/includes/wc-attribute-functions.php b/includes/wc-attribute-functions.php index b4a3616f0c2..adb425f1342 100644 --- a/includes/wc-attribute-functions.php +++ b/includes/wc-attribute-functions.php @@ -97,7 +97,8 @@ function wc_attribute_taxonomy_name_by_id( $attribute_id ) { SELECT attribute_name FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_id = %d - ", $attribute_id + ", + $attribute_id ) ); @@ -116,7 +117,7 @@ function wc_attribute_taxonomy_name_by_id( $attribute_id ) { * @return int */ function wc_attribute_taxonomy_id_by_name( $name ) { - $name = str_replace( 'pa_', '', wc_sanitize_taxonomy_name( $name ) ); + $name = wc_attribute_taxonomy_name_raw( $name ); $taxonomies = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_id', 'attribute_name' ); return isset( $taxonomies[ $name ] ) ? (int) $taxonomies[ $name ] : 0; @@ -131,7 +132,7 @@ function wc_attribute_taxonomy_id_by_name( $name ) { */ function wc_attribute_label( $name, $product = '' ) { if ( taxonomy_is_product_attribute( $name ) ) { - $name = wc_sanitize_taxonomy_name( str_replace( 'pa_', '', $name ) ); + $name = wc_attribute_taxonomy_name_raw( $name ); $all_labels = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_label', 'attribute_name' ); $label = isset( $all_labels[ $name ] ) ? $all_labels[ $name ] : $name; } elseif ( $product ) { @@ -166,7 +167,7 @@ function wc_attribute_label( $name, $product = '' ) { function wc_attribute_orderby( $name ) { global $wc_product_attributes, $wpdb; - $name = str_replace( 'pa_', '', sanitize_title( $name ) ); + $name = wc_attribute_taxonomy_name_raw( $name ); if ( isset( $wc_product_attributes[ 'pa_' . $name ] ) ) { $orderby = $wc_product_attributes[ 'pa_' . $name ]->attribute_orderby; @@ -201,7 +202,8 @@ function wc_get_attribute_taxonomy_names() { */ function wc_get_attribute_types() { return (array) apply_filters( - 'product_attributes_type_selector', array( + 'product_attributes_type_selector', + array( 'select' => __( 'Select', 'woocommerce' ), ) ); @@ -385,7 +387,8 @@ function wc_get_attribute( $id ) { SELECT * FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_id = %d - ", $id + ", + $id ) ); @@ -543,7 +546,8 @@ function wc_create_attribute( $args ) { "SELECT post_id, meta_value FROM {$wpdb->postmeta} WHERE meta_key = '_product_attributes' AND meta_value LIKE %s", '%' . $wpdb->esc_like( $old_taxonomy_name ) . '%' ), - ARRAY_A ); + ARRAY_A + ); foreach ( $metadatas as $metadata ) { $product_id = $metadata['post_id']; $unserialized_data = maybe_unserialize( $metadata['meta_value'] ); @@ -600,7 +604,8 @@ function wc_update_attribute( $id, $args ) { SELECT attribute_name FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_id = %d - ", $args['id'] + ", + $args['id'] ) ); @@ -623,7 +628,8 @@ function wc_delete_attribute( $id ) { SELECT attribute_name FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_id = %d - ", $id + ", + $id ) ); @@ -662,3 +668,16 @@ function wc_delete_attribute( $id ) { return false; } + +/** + * Get an unprefixed product attribute name. + * + * @since 3.5.3 + * + * @param string $attribute_name Attribute name. + * @return string + */ +function wc_attribute_taxonomy_name_raw( $attribute_name ) { + $attribute_name = wc_sanitize_taxonomy_name( $attribute_name ); + return 0 === strpos( $attribute_name, 'pa_' ) ? substr( $attribute_name, 3 ) : $attribute_name; +} From e8ec6df3ff1f0be070d8f17b7d192041a09100bd Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Mon, 3 Dec 2018 10:38:35 -0400 Subject: [PATCH 009/401] phpcs sniff fixes, `wc_attribute_taxonomy_name_raw()` for abstract-wc-widget.php --- includes/abstracts/abstract-wc-widget.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/abstracts/abstract-wc-widget.php b/includes/abstracts/abstract-wc-widget.php index 22dd2be276b..21b1e9668de 100644 --- a/includes/abstracts/abstract-wc-widget.php +++ b/includes/abstracts/abstract-wc-widget.php @@ -225,7 +225,7 @@ abstract class WC_Widget extends WP_Widget { case 'text': ?>

- +

slug, $queried_object->taxonomy ); + $link = get_term_link( $queried_object->slug, $queried_object->taxonomy ); } // Min/Max. @@ -337,7 +337,7 @@ abstract class WC_Widget extends WP_Widget { // All current filters. if ( $_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes() ) { // phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found, WordPress.CodeAnalysis.AssignmentInCondition.Found foreach ( $_chosen_attributes as $name => $data ) { - $filter_name = sanitize_title( str_replace( 'pa_', '', $name ) ); + $filter_name = wc_attribute_taxonomy_name_raw( $name ); if ( ! empty( $data['terms'] ) ) { $link = add_query_arg( 'filter_' . $filter_name, implode( ',', $data['terms'] ), $link ); } From 33f25f83b5af863bed9fb05e25b06f38bd0e4d28 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Mon, 3 Dec 2018 10:45:25 -0400 Subject: [PATCH 010/401] phpcs sniff fixes, `wc_attribute_taxonomy_name_raw()` for class-wc-admin-importers.php --- includes/admin/class-wc-admin-importers.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/includes/admin/class-wc-admin-importers.php b/includes/admin/class-wc-admin-importers.php index 6a09e469809..cef42e5e9b5 100644 --- a/includes/admin/class-wc-admin-importers.php +++ b/includes/admin/class-wc-admin-importers.php @@ -82,7 +82,7 @@ class WC_Admin_Importers { */ public function admin_scripts() { $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; - wp_register_script( 'wc-product-import', WC()->plugin_url() . '/assets/js/admin/wc-product-import' . $suffix . '.js', array( 'jquery' ), WC_VERSION ); + wp_register_script( 'wc-product-import', WC()->plugin_url() . '/assets/js/admin/wc-product-import' . $suffix . '.js', array( 'jquery' ), WC_VERSION, true ); } /** @@ -159,7 +159,7 @@ class WC_Admin_Importers { foreach ( $post['terms'] as $term ) { if ( strstr( $term['domain'], 'pa_' ) ) { if ( ! taxonomy_exists( $term['domain'] ) ) { - $attribute_name = wc_sanitize_taxonomy_name( str_replace( 'pa_', '', $term['domain'] ) ); + $attribute_name = wc_attribute_taxonomy_name_raw( $term['domain'] ); // Create the taxonomy. if ( ! in_array( $attribute_name, wc_get_attribute_taxonomies(), true ) ) { @@ -179,7 +179,8 @@ class WC_Admin_Importers { $term['domain'], apply_filters( 'woocommerce_taxonomy_objects_' . $term['domain'], array( 'product' ) ), apply_filters( - 'woocommerce_taxonomy_args_' . $term['domain'], array( + 'woocommerce_taxonomy_args_' . $term['domain'], + array( 'hierarchical' => true, 'show_ui' => false, 'query_var' => true, @@ -248,16 +249,20 @@ class WC_Admin_Importers { // @codingStandardsIgnoreEnd. // Clean up orphaned data. - $wpdb->query( " + $wpdb->query( + " DELETE {$wpdb->posts}.* FROM {$wpdb->posts} LEFT JOIN {$wpdb->posts} wp ON wp.ID = {$wpdb->posts}.post_parent WHERE wp.ID IS NULL AND {$wpdb->posts}.post_type = 'product_variation' - " ); - $wpdb->query( " + " + ); + $wpdb->query( + " DELETE {$wpdb->postmeta}.* FROM {$wpdb->postmeta} LEFT JOIN {$wpdb->posts} wp ON wp.ID = {$wpdb->postmeta}.post_id WHERE wp.ID IS NULL - " ); + " + ); // @codingStandardsIgnoreStart. $wpdb->query( " DELETE tr.* FROM {$wpdb->term_relationships} tr From dc0aeb0553eb384ea95c3c0028559386bd95f8e4 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Mon, 3 Dec 2018 10:57:17 -0400 Subject: [PATCH 011/401] phpcs sniff fixes, `wc_attribute_taxonomy_name_raw()` for class-wc-rest-products-v2-controller.php --- .../class-wc-rest-products-v2-controller.php | 55 ++++++++++++++----- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/includes/api/v2/class-wc-rest-products-v2-controller.php b/includes/api/v2/class-wc-rest-products-v2-controller.php index 0a9e5ecc868..c78aca7d1ca 100644 --- a/includes/api/v2/class-wc-rest-products-v2-controller.php +++ b/includes/api/v2/class-wc-rest-products-v2-controller.php @@ -58,7 +58,9 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller */ public function register_routes() { register_rest_route( - $this->namespace, '/' . $this->rest_base, array( + $this->namespace, + '/' . $this->rest_base, + array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), @@ -76,7 +78,9 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller ); register_rest_route( - $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)', array( + $this->namespace, + '/' . $this->rest_base . '/(?P[\d]+)', + array( 'args' => array( 'id' => array( 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), @@ -118,7 +122,9 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller ); register_rest_route( - $this->namespace, '/' . $this->rest_base . '/batch', array( + $this->namespace, + '/' . $this->rest_base . '/batch', + array( array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'batch_items' ), @@ -259,7 +265,8 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller } $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok. - $args, array( + $args, + array( 'key' => '_sku', 'value' => $skus, 'compare' => 'IN', @@ -270,7 +277,8 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller // Filter by tax class. if ( ! empty( $request['tax_class'] ) ) { $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok. - $args, array( + $args, + array( 'key' => '_tax_class', 'value' => 'standard' !== $request['tax_class'] ? $request['tax_class'] : '', ) @@ -285,7 +293,8 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller // Filter product in stock or out of stock. if ( is_bool( $request['in_stock'] ) ) { $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok. - $args, array( + $args, + array( 'key' => '_stock_status', 'value' => true === $request['in_stock'] ? 'instock' : 'outofstock', ) @@ -444,7 +453,7 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller $attributes = $product->get_attributes(); if ( ! isset( $attributes[ $slug ] ) ) { - return str_replace( 'pa_', '', $slug ); + return wc_attribute_taxonomy_name_raw( $slug ); } $attribute = $attributes[ $slug ]; @@ -499,7 +508,9 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller protected function get_attribute_options( $product_id, $attribute ) { if ( isset( $attribute['is_taxonomy'] ) && $attribute['is_taxonomy'] ) { return wc_get_product_terms( - $product_id, $attribute['name'], array( + $product_id, + $attribute['name'], + array( 'fields' => 'names', ) ); @@ -697,7 +708,9 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller if ( 'variation' === $product->get_type() ) { return new WP_Error( - "woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), array( + "woocommerce_rest_invalid_{$this->post_type}_id", + __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), + array( 'status' => 404, ) ); @@ -1311,7 +1324,9 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller if ( ! $object || 0 === $object->get_id() ) { return new WP_Error( - "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( + "woocommerce_rest_{$this->post_type}_invalid_id", + __( 'Invalid ID.', 'woocommerce' ), + array( 'status' => 404, ) ); @@ -1319,7 +1334,9 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller if ( 'variation' === $object->get_type() ) { return new WP_Error( - "woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), array( + "woocommerce_rest_invalid_{$this->post_type}_id", + __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), + array( 'status' => 404, ) ); @@ -1339,8 +1356,10 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $object->get_id() ) ) { return new WP_Error( + "woocommerce_rest_user_cannot_delete_{$this->post_type}", /* translators: %s: post type */ - "woocommerce_rest_user_cannot_delete_{$this->post_type}", sprintf( __( 'Sorry, you are not allowed to delete %s.', 'woocommerce' ), $this->post_type ), array( + sprintf( __( 'Sorry, you are not allowed to delete %s.', 'woocommerce' ), $this->post_type ), + array( 'status' => rest_authorization_required_code(), ) ); @@ -1375,8 +1394,10 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller // If we don't support trashing for this type, error out. if ( ! $supports_trash ) { return new WP_Error( + 'woocommerce_rest_trash_not_supported', /* translators: %s: post type */ - 'woocommerce_rest_trash_not_supported', sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), array( + sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), + array( 'status' => 501, ) ); @@ -1386,8 +1407,10 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller if ( is_callable( array( $object, 'get_status' ) ) ) { if ( 'trash' === $object->get_status() ) { return new WP_Error( + 'woocommerce_rest_already_trashed', /* translators: %s: post type */ - 'woocommerce_rest_already_trashed', sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array( + sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), + array( 'status' => 410, ) ); @@ -1400,8 +1423,10 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller if ( ! $result ) { return new WP_Error( + 'woocommerce_rest_cannot_delete', /* translators: %s: post type */ - 'woocommerce_rest_cannot_delete', sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ), array( + sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ), + array( 'status' => 500, ) ); From c1ad273a03aa4a420d65f2cd7f34f9d897e667f5 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Mon, 3 Dec 2018 10:57:27 -0400 Subject: [PATCH 012/401] replace all remaining str_replace( 'pa_', ... with wc_attribute_taxonomy_name_raw(...) --- includes/api/legacy/v1/class-wc-api-products.php | 4 ++-- includes/api/legacy/v2/class-wc-api-orders.php | 4 ++-- includes/api/legacy/v2/class-wc-api-products.php | 4 ++-- includes/api/legacy/v3/class-wc-api-orders.php | 4 ++-- includes/api/legacy/v3/class-wc-api-products.php | 4 ++-- includes/api/v1/class-wc-rest-products-controller.php | 2 +- includes/widgets/class-wc-widget-layered-nav-filters.php | 2 +- includes/widgets/class-wc-widget-layered-nav.php | 6 +++--- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/includes/api/legacy/v1/class-wc-api-products.php b/includes/api/legacy/v1/class-wc-api-products.php index 7026333127d..47427dd6154 100644 --- a/includes/api/legacy/v1/class-wc-api-products.php +++ b/includes/api/legacy/v1/class-wc-api-products.php @@ -500,7 +500,7 @@ class WC_API_Products extends WC_API_Resource { // taxonomy-based attributes are prefixed with `pa_`, otherwise simply `attribute_` $attributes[] = array( - 'name' => ucwords( str_replace( 'attribute_', '', str_replace( 'pa_', '', $attribute_name ) ) ), + 'name' => ucwords( str_replace( 'attribute_', '', wc_attribute_taxonomy_name_raw( $attribute_name ) ) ), 'option' => $attribute, ); } @@ -508,7 +508,7 @@ class WC_API_Products extends WC_API_Resource { foreach ( $product->get_attributes() as $attribute ) { $attributes[] = array( - 'name' => ucwords( str_replace( 'pa_', '', $attribute['name'] ) ), + 'name' => ucwords( wc_attribute_taxonomy_name_raw( $attribute['name'] ) ), 'position' => $attribute['position'], 'visible' => (bool) $attribute['is_visible'], 'variation' => (bool) $attribute['is_variation'], diff --git a/includes/api/legacy/v2/class-wc-api-orders.php b/includes/api/legacy/v2/class-wc-api-orders.php index 2c3f74160e8..c3f6416afcd 100644 --- a/includes/api/legacy/v2/class-wc-api-orders.php +++ b/includes/api/legacy/v2/class-wc-api-orders.php @@ -973,7 +973,7 @@ class WC_API_Orders extends WC_API_Resource { if ( isset( $variations ) && is_array( $variations ) ) { // start by normalizing the passed variations foreach ( $variations as $key => $value ) { - $key = str_replace( 'attribute_', '', str_replace( 'pa_', '', $key ) ); // from get_attributes in class-wc-api-products.php + $key = str_replace( 'attribute_', '', wc_attribute_taxonomy_name_raw( $key ) ); // from get_attributes in class-wc-api-products.php $variations_normalized[ $key ] = strtolower( $value ); } // now search through each product child and see if our passed variations match anything @@ -981,7 +981,7 @@ class WC_API_Orders extends WC_API_Resource { $meta = array(); foreach ( get_post_meta( $variation ) as $key => $value ) { $value = $value[0]; - $key = str_replace( 'attribute_', '', str_replace( 'pa_', '', $key ) ); + $key = str_replace( 'attribute_', '', wc_attribute_taxonomy_name_raw( $key ) ); $meta[ $key ] = strtolower( $value ); } // if the variation array is a part of the $meta array, we found our match diff --git a/includes/api/legacy/v2/class-wc-api-products.php b/includes/api/legacy/v2/class-wc-api-products.php index 41ea5b9c302..01cf9db9898 100644 --- a/includes/api/legacy/v2/class-wc-api-products.php +++ b/includes/api/legacy/v2/class-wc-api-products.php @@ -1835,7 +1835,7 @@ class WC_API_Products extends WC_API_Resource { // taxonomy-based attributes are prefixed with `pa_`, otherwise simply `attribute_` $attributes[] = array( 'name' => wc_attribute_label( str_replace( 'attribute_', '', $attribute_name ) ), - 'slug' => str_replace( 'attribute_', '', str_replace( 'pa_', '', $attribute_name ) ), + 'slug' => str_replace( 'attribute_', '', wc_attribute_taxonomy_name_raw( $attribute_name ) ), 'option' => $attribute, ); } @@ -1844,7 +1844,7 @@ class WC_API_Products extends WC_API_Resource { foreach ( $product->get_attributes() as $attribute ) { $attributes[] = array( 'name' => wc_attribute_label( $attribute['name'] ), - 'slug' => str_replace( 'pa_', '', $attribute['name'] ), + 'slug' => wc_attribute_taxonomy_name_raw( $attribute['name'] ), 'position' => (int) $attribute['position'], 'visible' => (bool) $attribute['is_visible'], 'variation' => (bool) $attribute['is_variation'], diff --git a/includes/api/legacy/v3/class-wc-api-orders.php b/includes/api/legacy/v3/class-wc-api-orders.php index b9c3ff0e812..be6576c66c9 100644 --- a/includes/api/legacy/v3/class-wc-api-orders.php +++ b/includes/api/legacy/v3/class-wc-api-orders.php @@ -1018,7 +1018,7 @@ class WC_API_Orders extends WC_API_Resource { if ( isset( $variations ) && is_array( $variations ) ) { // start by normalizing the passed variations foreach ( $variations as $key => $value ) { - $key = str_replace( 'attribute_', '', str_replace( 'pa_', '', $key ) ); // from get_attributes in class-wc-api-products.php + $key = str_replace( 'attribute_', '', wc_attribute_taxonomy_name_raw( $key ) ); // from get_attributes in class-wc-api-products.php $variations_normalized[ $key ] = strtolower( $value ); } // now search through each product child and see if our passed variations match anything @@ -1026,7 +1026,7 @@ class WC_API_Orders extends WC_API_Resource { $meta = array(); foreach ( get_post_meta( $variation ) as $key => $value ) { $value = $value[0]; - $key = str_replace( 'attribute_', '', str_replace( 'pa_', '', $key ) ); + $key = str_replace( 'attribute_', '', wc_attribute_taxonomy_name_raw( $key ) ); $meta[ $key ] = strtolower( $value ); } // if the variation array is a part of the $meta array, we found our match diff --git a/includes/api/legacy/v3/class-wc-api-products.php b/includes/api/legacy/v3/class-wc-api-products.php index c2d4423aab4..73d9869f079 100644 --- a/includes/api/legacy/v3/class-wc-api-products.php +++ b/includes/api/legacy/v3/class-wc-api-products.php @@ -2393,7 +2393,7 @@ class WC_API_Products extends WC_API_Resource { // taxonomy-based attributes are prefixed with `pa_`, otherwise simply `attribute_` $attributes[] = array( 'name' => wc_attribute_label( str_replace( 'attribute_', '', $attribute_name ), $product ), - 'slug' => str_replace( 'attribute_', '', str_replace( 'pa_', '', $attribute_name ) ), + 'slug' => str_replace( 'attribute_', '', wc_attribute_taxonomy_name_raw( $attribute_name ) ), 'option' => $attribute, ); } @@ -2402,7 +2402,7 @@ class WC_API_Products extends WC_API_Resource { foreach ( $product->get_attributes() as $attribute ) { $attributes[] = array( 'name' => wc_attribute_label( $attribute['name'], $product ), - 'slug' => str_replace( 'pa_', '', $attribute['name'] ), + 'slug' => wc_attribute_taxonomy_name_raw( $attribute['name'] ), 'position' => (int) $attribute['position'], 'visible' => (bool) $attribute['is_visible'], 'variation' => (bool) $attribute['is_variation'], diff --git a/includes/api/v1/class-wc-rest-products-controller.php b/includes/api/v1/class-wc-rest-products-controller.php index 92d755da896..6a9bd6d458d 100644 --- a/includes/api/v1/class-wc-rest-products-controller.php +++ b/includes/api/v1/class-wc-rest-products-controller.php @@ -352,7 +352,7 @@ class WC_REST_Products_V1_Controller extends WC_REST_Posts_Controller { } else { $default[] = array( 'id' => 0, - 'name' => str_replace( 'pa_', '', $key ), + 'name' => wc_attribute_taxonomy_name_raw( $key ), 'option' => $value, ); } diff --git a/includes/widgets/class-wc-widget-layered-nav-filters.php b/includes/widgets/class-wc-widget-layered-nav-filters.php index 1f9d1df071d..7469cda8d50 100644 --- a/includes/widgets/class-wc-widget-layered-nav-filters.php +++ b/includes/widgets/class-wc-widget-layered-nav-filters.php @@ -65,7 +65,7 @@ class WC_Widget_Layered_Nav_Filters extends WC_Widget { continue; } - $filter_name = 'filter_' . sanitize_title( str_replace( 'pa_', '', $taxonomy ) ); + $filter_name = 'filter_' . wc_attribute_taxonomy_name_raw( $taxonomy ); $current_filter = isset( $_GET[ $filter_name ] ) ? explode( ',', wc_clean( wp_unslash( $_GET[ $filter_name ] ) ) ) : array(); // WPCS: input var ok, CSRF ok. $current_filter = array_map( 'sanitize_title', $current_filter ); $new_filter = array_diff( $current_filter, array( $term_slug ) ); diff --git a/includes/widgets/class-wc-widget-layered-nav.php b/includes/widgets/class-wc-widget-layered-nav.php index 31b73654b2e..16d67d6ae3a 100644 --- a/includes/widgets/class-wc-widget-layered-nav.php +++ b/includes/widgets/class-wc-widget-layered-nav.php @@ -223,7 +223,7 @@ class WC_Widget_Layered_Nav extends WC_Widget { if ( $taxonomy !== $this->get_current_taxonomy() ) { $term_counts = $this->get_filtered_term_product_counts( wp_list_pluck( $terms, 'term_id' ), $taxonomy, $query_type ); $_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes(); - $taxonomy_filter_name = str_replace( 'pa_', '', $taxonomy ); + $taxonomy_filter_name = wc_attribute_taxonomy_name_raw( $taxonomy ); $taxonomy_label = wc_attribute_label( $taxonomy ); /* translators: %s: taxonomy name */ @@ -423,7 +423,7 @@ class WC_Widget_Layered_Nav extends WC_Widget { continue; } - $filter_name = 'filter_' . str_replace( 'pa_', '', $taxonomy ); + $filter_name = 'filter_' . wc_attribute_taxonomy_name_raw( $taxonomy ); $current_filter = isset( $_GET[ $filter_name ] ) ? explode( ',', wc_clean( wp_unslash( $_GET[ $filter_name ] ) ) ) : array(); // WPCS: input var ok, CSRF ok. $current_filter = array_map( 'sanitize_title', $current_filter ); @@ -452,7 +452,7 @@ class WC_Widget_Layered_Nav extends WC_Widget { // Add Query type Arg to URL. if ( 'or' === $query_type && ! ( 1 === count( $current_filter ) && $option_is_set ) ) { - $link = add_query_arg( 'query_type_' . sanitize_title( str_replace( 'pa_', '', $taxonomy ) ), 'or', $link ); + $link = add_query_arg( 'query_type_' . wc_attribute_taxonomy_name_raw( $taxonomy ), 'or', $link ); } $link = str_replace( '%2C', ',', $link ); } From 329d415cfdf254b883d8770659f6a8740811edf3 Mon Sep 17 00:00:00 2001 From: Will Gorham Date: Tue, 4 Dec 2018 14:47:04 -0500 Subject: [PATCH 013/401] When selling to a single country, force user location to that country --- includes/wc-core-functions.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/includes/wc-core-functions.php b/includes/wc-core-functions.php index c2cf1c7e1be..e7ba56d9a27 100644 --- a/includes/wc-core-functions.php +++ b/includes/wc-core-functions.php @@ -1071,6 +1071,12 @@ function wc_get_customer_default_location() { $location = WC_Geolocation::geolocate_ip( '', true, false ); } + // When selling to only one country, force customer location to that country. + $countries = WC()->countries->get_allowed_countries(); + if ( 1 === count( $countries ) ) { + $location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', key( $countries ) ) ); + } + // Base fallback. if ( empty( $location['country'] ) ) { $location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', get_option( 'woocommerce_default_country' ) ) ); From d1d2c89ca9623451799c3b5c8134ba7d370c6910 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Wed, 19 Dec 2018 13:20:15 -0400 Subject: [PATCH 014/401] convert user creation GMT datestamp to local datetime in API --- includes/api/class-wc-rest-customers-controller.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/includes/api/class-wc-rest-customers-controller.php b/includes/api/class-wc-rest-customers-controller.php index 979a42e9ef0..70f4781e83d 100644 --- a/includes/api/class-wc-rest-customers-controller.php +++ b/includes/api/class-wc-rest-customers-controller.php @@ -28,8 +28,9 @@ class WC_REST_Customers_Controller extends WC_REST_Customers_V2_Controller { /** * Get formatted item data. * + * @param WC_Data $object WC_Data instance. + * * @since 3.0.0 - * @param WC_Data $object WC_Data instance. * @return array */ protected function get_formatted_item_data( $object ) { @@ -38,7 +39,7 @@ class WC_REST_Customers_Controller extends WC_REST_Customers_V2_Controller { // Format date values. foreach ( $format_date as $key ) { - $datetime = $data[ $key ]; + $datetime = 'date_created' == $key ? get_date_from_gmt( $data[ $key ] ) : $data[ $key ]; $data[ $key ] = wc_rest_prepare_date_response( $datetime, false ); $data[ $key . '_gmt' ] = wc_rest_prepare_date_response( $datetime ); } From bd65e6528575ac92b2e14dfdbd063e8b09e02478 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Thu, 27 Dec 2018 11:02:28 -0400 Subject: [PATCH 015/401] update customer unit test --- tests/unit-tests/api/customers.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/unit-tests/api/customers.php b/tests/unit-tests/api/customers.php index 54f96cae51d..5a9898fbfd0 100644 --- a/tests/unit-tests/api/customers.php +++ b/tests/unit-tests/api/customers.php @@ -44,16 +44,17 @@ class Customers extends WC_REST_Unit_Test_Case { $request->set_query_params( array( 'orderby' => 'id', ) ); - $response = $this->server->dispatch( $request ); - $customers = $response->get_data(); + $response = $this->server->dispatch( $request ); + $customers = $response->get_data(); + $date_created = get_date_from_gmt( $customer_1->get_date_created() ); $this->assertEquals( 200, $response->get_status() ); $this->assertEquals( 2, count( $customers ) ); $this->assertContains( array( 'id' => $customer_1->get_id(), - 'date_created' => wc_rest_prepare_date_response( $customer_1->get_date_created(), false ), - 'date_created_gmt' => wc_rest_prepare_date_response( $customer_1->get_date_created() ), + 'date_created' => wc_rest_prepare_date_response( $date_created, false ), + 'date_created_gmt' => wc_rest_prepare_date_response( $date_created ), 'date_modified' => wc_rest_prepare_date_response( $customer_1->get_date_modified(), false ), 'date_modified_gmt' => wc_rest_prepare_date_response( $customer_1->get_date_modified() ), 'email' => 'test@woo.local', From 5dac4075b27f33d3997ebc72e13fb26def7b69d3 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Thu, 27 Dec 2018 11:18:22 -0400 Subject: [PATCH 016/401] phpcs sniff fixes for customers.php --- tests/unit-tests/api/customers.php | 490 +++++++++++++++-------------- 1 file changed, 261 insertions(+), 229 deletions(-) diff --git a/tests/unit-tests/api/customers.php b/tests/unit-tests/api/customers.php index 5a9898fbfd0..8bc4cfe4b48 100644 --- a/tests/unit-tests/api/customers.php +++ b/tests/unit-tests/api/customers.php @@ -3,9 +3,15 @@ * Tests for the Customers REST API. * * @package WooCommerce\Tests\API - * @since 3.5.0 + * @since 3.5.0 */ +/** + * Tests for the Customers REST API. + * + * @package WooCommerce\Tests\API + * @extends WC_REST_Unit_Test_Case + */ class Customers extends WC_REST_Unit_Test_Case { /** @@ -41,9 +47,11 @@ class Customers extends WC_REST_Unit_Test_Case { WC_Helper_Customer::create_customer( 'test2', 'test2', 'test2@woo.local' ); $request = new WP_REST_Request( 'GET', '/wc/v3/customers' ); - $request->set_query_params( array( - 'orderby' => 'id', - ) ); + $request->set_query_params( + array( + 'orderby' => 'id', + ) + ); $response = $this->server->dispatch( $request ); $customers = $response->get_data(); $date_created = get_date_from_gmt( $customer_1->get_date_created() ); @@ -51,57 +59,60 @@ class Customers extends WC_REST_Unit_Test_Case { $this->assertEquals( 200, $response->get_status() ); $this->assertEquals( 2, count( $customers ) ); - $this->assertContains( array( - 'id' => $customer_1->get_id(), - 'date_created' => wc_rest_prepare_date_response( $date_created, false ), - 'date_created_gmt' => wc_rest_prepare_date_response( $date_created ), - 'date_modified' => wc_rest_prepare_date_response( $customer_1->get_date_modified(), false ), - 'date_modified_gmt' => wc_rest_prepare_date_response( $customer_1->get_date_modified() ), - 'email' => 'test@woo.local', - 'first_name' => 'Justin', - 'last_name' => '', - 'role' => 'customer', - 'username' => 'testcustomer', - 'billing' => array( - 'first_name' => '', - 'last_name' => '', - 'company' => '', - 'address_1' => '123 South Street', - 'address_2' => 'Apt 1', - 'city' => 'Philadelphia', - 'state' => 'PA', - 'postcode' => '19123', - 'country' => 'US', - 'email' => '', - 'phone' => '', - ), - 'shipping' => array( - 'first_name' => '', - 'last_name' => '', - 'company' => '', - 'address_1' => '123 South Street', - 'address_2' => 'Apt 1', - 'city' => 'Philadelphia', - 'state' => 'PA', - 'postcode' => '19123', - 'country' => 'US', - ), - 'is_paying_customer' => false, - 'avatar_url' => $customer_1->get_avatar_url(), - 'meta_data' => array(), - '_links' => array( - 'self' => array( - array( - 'href' => rest_url( '/wc/v3/customers/' . $customer_1->get_id() . '' ), - ), + $this->assertContains( + array( + 'id' => $customer_1->get_id(), + 'date_created' => wc_rest_prepare_date_response( $date_created, false ), + 'date_created_gmt' => wc_rest_prepare_date_response( $date_created ), + 'date_modified' => wc_rest_prepare_date_response( $customer_1->get_date_modified(), false ), + 'date_modified_gmt' => wc_rest_prepare_date_response( $customer_1->get_date_modified() ), + 'email' => 'test@woo.local', + 'first_name' => 'Justin', + 'last_name' => '', + 'role' => 'customer', + 'username' => 'testcustomer', + 'billing' => array( + 'first_name' => '', + 'last_name' => '', + 'company' => '', + 'address_1' => '123 South Street', + 'address_2' => 'Apt 1', + 'city' => 'Philadelphia', + 'state' => 'PA', + 'postcode' => '19123', + 'country' => 'US', + 'email' => '', + 'phone' => '', ), - 'collection' => array( - array( - 'href' => rest_url( '/wc/v3/customers' ), + 'shipping' => array( + 'first_name' => '', + 'last_name' => '', + 'company' => '', + 'address_1' => '123 South Street', + 'address_2' => 'Apt 1', + 'city' => 'Philadelphia', + 'state' => 'PA', + 'postcode' => '19123', + 'country' => 'US', + ), + 'is_paying_customer' => false, + 'avatar_url' => $customer_1->get_avatar_url(), + 'meta_data' => array(), + '_links' => array( + 'self' => array( + array( + 'href' => rest_url( '/wc/v3/customers/' . $customer_1->get_id() . '' ), + ), + ), + 'collection' => array( + array( + 'href' => rest_url( '/wc/v3/customers' ), + ), ), ), ), - ), $customers ); + $customers + ); } /** @@ -125,125 +136,137 @@ class Customers extends WC_REST_Unit_Test_Case { // Test just the basics first.. $request = new WP_REST_Request( 'POST', '/wc/v3/customers' ); - $request->set_body_params( array( - 'username' => 'create_customer_test', - 'password' => 'test123', - 'email' => 'create_customer_test@woo.local', - ) ); + $request->set_body_params( + array( + 'username' => 'create_customer_test', + 'password' => 'test123', + 'email' => 'create_customer_test@woo.local', + ) + ); $response = $this->server->dispatch( $request ); - $data = $response->get_data(); + $data = $response->get_data(); $this->assertEquals( 201, $response->get_status() ); - $this->assertEquals( array( - 'id' => $data['id'], - 'date_created' => $data['date_created'], - 'date_created_gmt' => $data['date_created_gmt'], - 'date_modified' => $data['date_modified'], - 'date_modified_gmt' => $data['date_modified_gmt'], - 'email' => 'create_customer_test@woo.local', - 'first_name' => '', - 'last_name' => '', - 'role' => 'customer', - 'username' => 'create_customer_test', - 'billing' => array( - 'first_name' => '', - 'last_name' => '', - 'company' => '', - 'address_1' => '', - 'address_2' => '', - 'city' => '', - 'state' => '', - 'postcode' => '', - 'country' => '', - 'email' => '', - 'phone' => '', + $this->assertEquals( + array( + 'id' => $data['id'], + 'date_created' => $data['date_created'], + 'date_created_gmt' => $data['date_created_gmt'], + 'date_modified' => $data['date_modified'], + 'date_modified_gmt' => $data['date_modified_gmt'], + 'email' => 'create_customer_test@woo.local', + 'first_name' => '', + 'last_name' => '', + 'role' => 'customer', + 'username' => 'create_customer_test', + 'billing' => array( + 'first_name' => '', + 'last_name' => '', + 'company' => '', + 'address_1' => '', + 'address_2' => '', + 'city' => '', + 'state' => '', + 'postcode' => '', + 'country' => '', + 'email' => '', + 'phone' => '', + ), + 'shipping' => array( + 'first_name' => '', + 'last_name' => '', + 'company' => '', + 'address_1' => '', + 'address_2' => '', + 'city' => '', + 'state' => '', + 'postcode' => '', + 'country' => '', + ), + 'is_paying_customer' => false, + 'meta_data' => array(), + 'avatar_url' => $data['avatar_url'], ), - 'shipping' => array( - 'first_name' => '', - 'last_name' => '', - 'company' => '', - 'address_1' => '', - 'address_2' => '', - 'city' => '', - 'state' => '', - 'postcode' => '', - 'country' => '', - ), - 'is_paying_customer' => false, - 'meta_data' => array(), - 'avatar_url' => $data['avatar_url'], - ), $data ); + $data + ); - // Test extra data + // Test extra data. $request = new WP_REST_Request( 'POST', '/wc/v3/customers' ); - $request->set_body_params( array( - 'username' => 'create_customer_test2', - 'password' => 'test123', - 'email' => 'create_customer_test2@woo.local', - 'first_name' => 'Test', - 'last_name' => 'McTestFace', - 'billing' => array( - 'country' => 'US', - 'state' => 'WA', - ), - 'shipping' => array( - 'state' => 'CA', - 'country' => 'US', - ), - ) ); + $request->set_body_params( + array( + 'username' => 'create_customer_test2', + 'password' => 'test123', + 'email' => 'create_customer_test2@woo.local', + 'first_name' => 'Test', + 'last_name' => 'McTestFace', + 'billing' => array( + 'country' => 'US', + 'state' => 'WA', + ), + 'shipping' => array( + 'state' => 'CA', + 'country' => 'US', + ), + ) + ); $response = $this->server->dispatch( $request ); - $data = $response->get_data(); + $data = $response->get_data(); $this->assertEquals( 201, $response->get_status() ); - $this->assertEquals( array( - 'id' => $data['id'], - 'date_created' => $data['date_created'], - 'date_created_gmt' => $data['date_created_gmt'], - 'date_modified' => $data['date_modified'], - 'date_modified_gmt' => $data['date_modified_gmt'], - 'email' => 'create_customer_test2@woo.local', - 'first_name' => 'Test', - 'last_name' => 'McTestFace', - 'role' => 'customer', - 'username' => 'create_customer_test2', - 'billing' => array( - 'first_name' => '', - 'last_name' => '', - 'company' => '', - 'address_1' => '', - 'address_2' => '', - 'city' => '', - 'state' => 'WA', - 'postcode' => '', - 'country' => 'US', - 'email' => '', - 'phone' => '', + $this->assertEquals( + array( + 'id' => $data['id'], + 'date_created' => $data['date_created'], + 'date_created_gmt' => $data['date_created_gmt'], + 'date_modified' => $data['date_modified'], + 'date_modified_gmt' => $data['date_modified_gmt'], + 'email' => 'create_customer_test2@woo.local', + 'first_name' => 'Test', + 'last_name' => 'McTestFace', + 'role' => 'customer', + 'username' => 'create_customer_test2', + 'billing' => array( + 'first_name' => '', + 'last_name' => '', + 'company' => '', + 'address_1' => '', + 'address_2' => '', + 'city' => '', + 'state' => 'WA', + 'postcode' => '', + 'country' => 'US', + 'email' => '', + 'phone' => '', + ), + 'shipping' => array( + 'first_name' => '', + 'last_name' => '', + 'company' => '', + 'address_1' => '', + 'address_2' => '', + 'city' => '', + 'state' => 'CA', + 'postcode' => '', + 'country' => 'US', + ), + 'is_paying_customer' => false, + 'meta_data' => array(), + 'avatar_url' => $data['avatar_url'], ), - 'shipping' => array( - 'first_name' => '', - 'last_name' => '', - 'company' => '', - 'address_1' => '', - 'address_2' => '', - 'city' => '', - 'state' => 'CA', - 'postcode' => '', - 'country' => 'US', - ), - 'is_paying_customer' => false, - 'meta_data' => array(), - 'avatar_url' => $data['avatar_url'], - ), $data ); + $data + ); - // Test without required field + // Test without required field. $request = new WP_REST_Request( 'POST', '/wc/v3/customers' ); - $request->set_body_params( array( - 'username' => 'create_customer_test3', - 'first_name' => 'Test', - 'last_name' => 'McTestFace', - ) ); + $request->set_body_params( + array( + 'username' => 'create_customer_test3', + 'first_name' => 'Test', + 'last_name' => 'McTestFace', + ) + ); $response = $this->server->dispatch( $request ); - $data = $response->get_data(); + $data = $response->get_data(); $this->assertEquals( 400, $response->get_status() ); } @@ -256,11 +279,13 @@ class Customers extends WC_REST_Unit_Test_Case { public function test_create_customer_without_permission() { wp_set_current_user( 0 ); $request = new WP_REST_Request( 'POST', '/wc/v3/customers' ); - $request->set_body_params( array( - 'username' => 'create_customer_test_without_permission', - 'password' => 'test123', - 'email' => 'create_customer_test_without_permission@woo.local', - ) ); + $request->set_body_params( + array( + 'username' => 'create_customer_test_without_permission', + 'password' => 'test123', + 'email' => 'create_customer_test_without_permission@woo.local', + ) + ); $response = $this->server->dispatch( $request ); $this->assertEquals( 401, $response->get_status() ); } @@ -274,47 +299,50 @@ class Customers extends WC_REST_Unit_Test_Case { wp_set_current_user( 1 ); $customer = WC_Helper_Customer::create_customer( 'get_customer_test', 'test123', 'get_customer_test@woo.local' ); $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/customers/' . $customer->get_id() ) ); - $data = $response->get_data(); + $data = $response->get_data(); - $this->assertEquals( array( - 'id' => $data['id'], - 'date_created' => $data['date_created'], - 'date_created_gmt' => $data['date_created_gmt'], - 'date_modified' => $data['date_modified'], - 'date_modified_gmt' => $data['date_modified_gmt'], - 'email' => 'get_customer_test@woo.local', - 'first_name' => 'Justin', - 'billing' => array( - 'first_name' => '', - 'last_name' => '', - 'company' => '', - 'address_1' => '123 South Street', - 'address_2' => 'Apt 1', - 'city' => 'Philadelphia', - 'state' => 'PA', - 'postcode' => '19123', - 'country' => 'US', - 'email' => '', - 'phone' => '', + $this->assertEquals( + array( + 'id' => $data['id'], + 'date_created' => $data['date_created'], + 'date_created_gmt' => $data['date_created_gmt'], + 'date_modified' => $data['date_modified'], + 'date_modified_gmt' => $data['date_modified_gmt'], + 'email' => 'get_customer_test@woo.local', + 'first_name' => 'Justin', + 'billing' => array( + 'first_name' => '', + 'last_name' => '', + 'company' => '', + 'address_1' => '123 South Street', + 'address_2' => 'Apt 1', + 'city' => 'Philadelphia', + 'state' => 'PA', + 'postcode' => '19123', + 'country' => 'US', + 'email' => '', + 'phone' => '', + ), + 'shipping' => array( + 'first_name' => '', + 'last_name' => '', + 'company' => '', + 'address_1' => '123 South Street', + 'address_2' => 'Apt 1', + 'city' => 'Philadelphia', + 'state' => 'PA', + 'postcode' => '19123', + 'country' => 'US', + ), + 'is_paying_customer' => false, + 'meta_data' => array(), + 'last_name' => '', + 'role' => 'customer', + 'username' => 'get_customer_test', + 'avatar_url' => $data['avatar_url'], ), - 'shipping' => array( - 'first_name' => '', - 'last_name' => '', - 'company' => '', - 'address_1' => '123 South Street', - 'address_2' => 'Apt 1', - 'city' => 'Philadelphia', - 'state' => 'PA', - 'postcode' => '19123', - 'country' => 'US', - ), - 'is_paying_customer' => false, - 'meta_data' => array(), - 'last_name' => '', - 'role' => 'customer', - 'username' => 'get_customer_test', - 'avatar_url' => $data['avatar_url'], - ), $data ); + $data + ); } /** @@ -355,10 +383,12 @@ class Customers extends WC_REST_Unit_Test_Case { $this->assertEquals( 'update_customer_test@woo.local', $data['email'] ); $request = new WP_REST_Request( 'PUT', '/wc/v3/customers/' . $customer->get_id() ); - $request->set_body_params( array( - 'email' => 'updated_email@woo.local', - 'first_name' => 'UpdatedTest', - ) ); + $request->set_body_params( + array( + 'email' => 'updated_email@woo.local', + 'first_name' => 'UpdatedTest', + ) + ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); @@ -411,7 +441,7 @@ class Customers extends WC_REST_Unit_Test_Case { */ public function test_delete_customer_invalid_id() { wp_set_current_user( 1 ); - $request = new WP_REST_Request( 'DELETE', '/wc/v3/customers/0' ); + $request = new WP_REST_Request( 'DELETE', '/wc/v3/customers/0' ); $request->set_param( 'force', true ); $response = $this->server->dispatch( $request ); $this->assertEquals( 400, $response->get_status() ); @@ -445,27 +475,29 @@ class Customers extends WC_REST_Unit_Test_Case { $customer_4 = WC_Helper_Customer::create_customer( 'test_batch_customer4', 'test123', 'test_batch_customer4@woo.local' ); $request = new WP_REST_Request( 'POST', '/wc/v3/customers/batch' ); - $request->set_body_params( array( - 'update' => array( - array( - 'id' => $customer_1->get_id(), - 'last_name' => 'McTest', + $request->set_body_params( + array( + 'update' => array( + array( + 'id' => $customer_1->get_id(), + 'last_name' => 'McTest', + ), ), - ), - 'delete' => array( - $customer_2->get_id(), - $customer_3->get_id(), - ), - 'create' => array( - array( - 'username' => 'newuser', - 'password' => 'test123', - 'email' => 'newuser@woo.local', + 'delete' => array( + $customer_2->get_id(), + $customer_3->get_id(), ), - ), - ) ); + 'create' => array( + array( + 'username' => 'newuser', + 'password' => 'test123', + 'email' => 'newuser@woo.local', + ), + ), + ) + ); $response = $this->server->dispatch( $request ); - $data = $response->get_data(); + $data = $response->get_data(); $this->assertEquals( 'McTest', $data['update'][0]['last_name'] ); $this->assertEquals( 'newuser', $data['create'][0]['username'] ); @@ -473,9 +505,9 @@ class Customers extends WC_REST_Unit_Test_Case { $this->assertEquals( $customer_2->get_id(), $data['delete'][0]['id'] ); $this->assertEquals( $customer_3->get_id(), $data['delete'][1]['id'] ); - $request = new WP_REST_Request( 'GET', '/wc/v3/customers' ); + $request = new WP_REST_Request( 'GET', '/wc/v3/customers' ); $response = $this->server->dispatch( $request ); - $data = $response->get_data(); + $data = $response->get_data(); $this->assertEquals( 3, count( $data ) ); } @@ -487,9 +519,9 @@ class Customers extends WC_REST_Unit_Test_Case { */ public function test_customer_schema() { wp_set_current_user( 1 ); - $request = new WP_REST_Request( 'OPTIONS', '/wc/v3/customers' ); - $response = $this->server->dispatch( $request ); - $data = $response->get_data(); + $request = new WP_REST_Request( 'OPTIONS', '/wc/v3/customers' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); $properties = $data['schema']['properties']; $this->assertEquals( 16, count( $properties ) ); From 4a0447a1509087193d612815d528bf2d022b976e Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 2 Jan 2019 17:04:51 +0000 Subject: [PATCH 017/401] Add adjust stock option to order class when adding items --- includes/abstracts/abstract-wc-order.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index 8a4a57d0ae3..88492c3bb6b 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -1139,10 +1139,11 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { * @param WC_Product $product Product object. * @param int $qty Quantity to add. * @param array $args Args for the added product. + * @param bool $adjust_stock True or false. Should the product stock also be reduced whilst adding to the order? * @return int * @throws WC_Data_Exception Exception thrown if the item cannot be added to the cart. */ - public function add_product( $product, $qty = 1, $args = array() ) { + public function add_product( $product, $qty = 1, $args = array(), $adjust_stock = false ) { if ( $product ) { $default_args = array( 'name' => $product->get_name(), @@ -1179,8 +1180,25 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { $item->set_props( $args ); $item->set_backorder_meta(); $item->set_order_id( $this->get_id() ); + + if ( $adjust_stock && $product && $product->managing_stock() ) { + $item_name = $product->get_formatted_name(); + $new_stock = wc_update_product_stock( $product, $item->get_quantity(), 'decrease' ); + + if ( is_wp_error( $new_stock ) ) { + /* translators: %s item name. */ + $this->add_order_note( sprintf( __( 'Unable to reduce stock for item %s.', 'woocommerce' ), $item_name ) ); + } else { + /* translators: %1$s: item name, %2$s stock change */ + $this->add_order_note( sprintf( __( '%1$s stock reduced %2$s.', 'woocommerce' ), $item_name, $new_stock . '→' . ( $new_stock - $item->get_quantity() ) ) ); + } + + $item->add_meta_data( '_reduced_stock', $item->get_quantity(), true ); + } + $item->save(); $this->add_item( $item ); + wc_do_deprecated_action( 'woocommerce_order_add_product', array( $this->get_id(), $item->get_id(), $product, $qty, $args ), '3.0', 'woocommerce_new_order_item action instead' ); delete_transient( 'wc_order_' . $this->get_id() . '_needs_processing' ); return $item->get_id(); From 0a5c77d93da6930be5b081f47f5e2a0bd33039e9 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 2 Jan 2019 17:09:20 +0000 Subject: [PATCH 018/401] Add adjust stock handling when deleting items --- includes/wc-order-item-functions.php | 33 ++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/includes/wc-order-item-functions.php b/includes/wc-order-item-functions.php index 9c4a4bef44a..c4e0e0bc8e6 100644 --- a/includes/wc-order-item-functions.php +++ b/includes/wc-order-item-functions.php @@ -44,10 +44,10 @@ function wc_add_order_item( $order_id, $item_array ) { /** * Update an item for an order. * - * @since 2.2 * @param int $item_id Item ID. * @param array $args Either `order_item_type` or `order_item_name`. * + * @since 2.2 * @throws Exception When `WC_Data_Store::load` validation fails. * @return bool True if successfully updated, false otherwise. */ @@ -67,12 +67,13 @@ function wc_update_order_item( $item_id, $args ) { /** * Delete an item from the order it belongs to based on item id. * - * @param int $item_id Item ID. + * @param int $item_id Item ID. + * @param bool $adjust_stock True or false. If the item had it's stock reduced, this will restore the stock and make a note. * * @throws Exception When `WC_Data_Store::load` validation fails. * @return bool */ -function wc_delete_order_item( $item_id ) { +function wc_delete_order_item( $item_id, $adjust_stock = false ) { $item_id = absint( $item_id ); if ( ! $item_id ) { @@ -83,6 +84,30 @@ function wc_delete_order_item( $item_id ) { do_action( 'woocommerce_before_delete_order_item', $item_id ); + if ( $adjust_stock ) { + $item = WC_Order_Factory::get_order_item( $item_id ); + + if ( $item->is_type( 'line_item' ) ) { + $order_id = wc_get_order_id_by_order_item_id( $item_id ); + $order = wc_get_order( $order_id ); + $product = $item->get_product(); + $item_stock_reduced = $item->get_meta( '_reduced_stock', true ); + + if ( $order && $product && $item_stock_reduced && $product->managing_stock() ) { + $item_name = $product->get_formatted_name(); + $new_stock = wc_update_product_stock( $product, $item_stock_reduced, 'increase' ); + + if ( is_wp_error( $new_stock ) ) { + /* translators: %s item name. */ + $order->add_order_note( sprintf( __( 'Unable to restore stock for item %s.', 'woocommerce' ), $item_name ) ); + } else { + /* translators: %1$s: item name, %2$s stock change */ + $order->add_order_note( sprintf( __( '%1$s stock increased %2$s.', 'woocommerce' ), $item_name, ( $new_stock - $item_stock_reduced ) . '→' . $new_stock ) ); + } + } + } + } + $data_store->delete_order_item( $item_id ); do_action( 'woocommerce_delete_order_item', $item_id ); @@ -170,7 +195,7 @@ function wc_get_order_item_meta( $item_id, $key, $single = true ) { /** * Get order ID by order item ID. * - * @param int $item_id Item ID. + * @param int $item_id Item ID. * * @throws Exception When `WC_Data_Store::load` validation fails. * @return int From 184b121eb8223501bfcfb5cd76d2eecc7dacbfdc Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 2 Jan 2019 17:09:45 +0000 Subject: [PATCH 019/401] Use adjust stock handling --- includes/class-wc-ajax.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php index 2d6e2882703..76d68876c00 100644 --- a/includes/class-wc-ajax.php +++ b/includes/class-wc-ajax.php @@ -884,7 +884,7 @@ class WC_AJAX { throw new Exception( __( 'Invalid product ID', 'woocommerce' ) . ' ' . $product_id ); } - $item_id = $order->add_product( $product, $qty ); + $item_id = $order->add_product( $product, $qty, array(), true ); $item = apply_filters( 'woocommerce_ajax_order_item', $order->get_item( $item_id ), $item_id ); $added_items[ $item_id ] = $item; @@ -1130,7 +1130,7 @@ class WC_AJAX { if ( sizeof( $order_item_ids ) > 0 ) { foreach ( $order_item_ids as $id ) { - wc_delete_order_item( absint( $id ) ); + wc_delete_order_item( absint( $id ), true ); } } From 282edab5d63c9d7b633d7359fbc36be816211ce1 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 3 Jan 2019 11:16:06 +0000 Subject: [PATCH 020/401] Revert "Add adjust stock option to order class when adding items" This reverts commit 4a0447a1509087193d612815d528bf2d022b976e. --- includes/abstracts/abstract-wc-order.php | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index 88492c3bb6b..8a4a57d0ae3 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -1139,11 +1139,10 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { * @param WC_Product $product Product object. * @param int $qty Quantity to add. * @param array $args Args for the added product. - * @param bool $adjust_stock True or false. Should the product stock also be reduced whilst adding to the order? * @return int * @throws WC_Data_Exception Exception thrown if the item cannot be added to the cart. */ - public function add_product( $product, $qty = 1, $args = array(), $adjust_stock = false ) { + public function add_product( $product, $qty = 1, $args = array() ) { if ( $product ) { $default_args = array( 'name' => $product->get_name(), @@ -1180,25 +1179,8 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { $item->set_props( $args ); $item->set_backorder_meta(); $item->set_order_id( $this->get_id() ); - - if ( $adjust_stock && $product && $product->managing_stock() ) { - $item_name = $product->get_formatted_name(); - $new_stock = wc_update_product_stock( $product, $item->get_quantity(), 'decrease' ); - - if ( is_wp_error( $new_stock ) ) { - /* translators: %s item name. */ - $this->add_order_note( sprintf( __( 'Unable to reduce stock for item %s.', 'woocommerce' ), $item_name ) ); - } else { - /* translators: %1$s: item name, %2$s stock change */ - $this->add_order_note( sprintf( __( '%1$s stock reduced %2$s.', 'woocommerce' ), $item_name, $new_stock . '→' . ( $new_stock - $item->get_quantity() ) ) ); - } - - $item->add_meta_data( '_reduced_stock', $item->get_quantity(), true ); - } - $item->save(); $this->add_item( $item ); - wc_do_deprecated_action( 'woocommerce_order_add_product', array( $this->get_id(), $item->get_id(), $product, $qty, $args ), '3.0', 'woocommerce_new_order_item action instead' ); delete_transient( 'wc_order_' . $this->get_id() . '_needs_processing' ); return $item->get_id(); From 999f744d8357cafd62919e978ccd20a785fcd1a9 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 3 Jan 2019 11:46:43 +0000 Subject: [PATCH 021/401] Allow notes to be updated after other order screen events --- .../class-wc-meta-box-order-notes.php | 53 ++++--------------- .../meta-boxes/views/html-order-notes.php | 48 +++++++++++++++++ 2 files changed, 57 insertions(+), 44 deletions(-) create mode 100644 includes/admin/meta-boxes/views/html-order-notes.php diff --git a/includes/admin/meta-boxes/class-wc-meta-box-order-notes.php b/includes/admin/meta-boxes/class-wc-meta-box-order-notes.php index d3c62f85a56..ba34495607e 100644 --- a/includes/admin/meta-boxes/class-wc-meta-box-order-notes.php +++ b/includes/admin/meta-boxes/class-wc-meta-box-order-notes.php @@ -2,14 +2,11 @@ /** * Order Notes * - * @author WooThemes - * @category Admin - * @package WooCommerce/Admin/Meta Boxes - * @version 2.1.0 + * @package WooCommerce/Admin/Meta Boxes */ if ( ! defined( 'ABSPATH' ) ) { - exit; // Exit if accessed directly + exit; } /** @@ -20,7 +17,7 @@ class WC_Meta_Box_Order_Notes { /** * Output the metabox. * - * @param WP_Post $post + * @param WP_Post $post Post object. */ public static function output( $post ) { global $post; @@ -31,52 +28,20 @@ class WC_Meta_Box_Order_Notes { $notes = wc_get_order_notes( $args ); - echo '
    '; - - if ( $notes ) { - - foreach ( $notes as $note ) { - - $note_classes = array( 'note' ); - $note_classes[] = $note->customer_note ? 'customer-note' : ''; - $note_classes[] = 'system' === $note->added_by ? 'system-note' : ''; - $note_classes = apply_filters( 'woocommerce_order_note_class', array_filter( $note_classes ), $note ); - ?> -
  • -
    - content ) ) ); ?> -
    -

    - date_created->date_i18n( wc_date_format() ), $note->date_created->date_i18n( wc_time_format() ) ); ?> - added_by ) : - /* translators: %s: note author */ - printf( ' ' . __( 'by %s', 'woocommerce' ), $note->added_by ); - endif; - ?> - -

    -
  • - ' . __( 'There are no notes yet.', 'woocommerce' ) . ''; - } - - echo '
'; + include 'views/html-order-notes.php'; ?>

- +

- + - +

+
    + customer_note ? 'customer-note' : ''; + $css_class[] = 'system' === $note->added_by ? 'system-note' : ''; + $css_class = apply_filters( 'woocommerce_order_note_class', array_filter( $css_class ), $note ); + ?> +
  • +
    + content ) ) ); // @codingStandardsIgnoreLine ?> +
    +

    + + date_created->date_i18n( wc_date_format() ), $note->date_created->date_i18n( wc_time_format() ) ) ); + ?> + + added_by ) : + /* translators: %s: note author */ + echo esc_html( sprintf( ' ' . __( 'by %s', 'woocommerce' ), $note->added_by ) ); + endif; + ?> + +

    +
  • + +
  • + +
From 5b8f95ea3c528b525de03776875ecff2e69064aa Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 3 Jan 2019 11:50:39 +0000 Subject: [PATCH 022/401] Revert "Add adjust stock handling when deleting items" This reverts commit 0a5c77d93da6930be5b081f47f5e2a0bd33039e9. --- includes/wc-order-item-functions.php | 33 ++++------------------------ 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/includes/wc-order-item-functions.php b/includes/wc-order-item-functions.php index c4e0e0bc8e6..9c4a4bef44a 100644 --- a/includes/wc-order-item-functions.php +++ b/includes/wc-order-item-functions.php @@ -44,10 +44,10 @@ function wc_add_order_item( $order_id, $item_array ) { /** * Update an item for an order. * + * @since 2.2 * @param int $item_id Item ID. * @param array $args Either `order_item_type` or `order_item_name`. * - * @since 2.2 * @throws Exception When `WC_Data_Store::load` validation fails. * @return bool True if successfully updated, false otherwise. */ @@ -67,13 +67,12 @@ function wc_update_order_item( $item_id, $args ) { /** * Delete an item from the order it belongs to based on item id. * - * @param int $item_id Item ID. - * @param bool $adjust_stock True or false. If the item had it's stock reduced, this will restore the stock and make a note. + * @param int $item_id Item ID. * * @throws Exception When `WC_Data_Store::load` validation fails. * @return bool */ -function wc_delete_order_item( $item_id, $adjust_stock = false ) { +function wc_delete_order_item( $item_id ) { $item_id = absint( $item_id ); if ( ! $item_id ) { @@ -84,30 +83,6 @@ function wc_delete_order_item( $item_id, $adjust_stock = false ) { do_action( 'woocommerce_before_delete_order_item', $item_id ); - if ( $adjust_stock ) { - $item = WC_Order_Factory::get_order_item( $item_id ); - - if ( $item->is_type( 'line_item' ) ) { - $order_id = wc_get_order_id_by_order_item_id( $item_id ); - $order = wc_get_order( $order_id ); - $product = $item->get_product(); - $item_stock_reduced = $item->get_meta( '_reduced_stock', true ); - - if ( $order && $product && $item_stock_reduced && $product->managing_stock() ) { - $item_name = $product->get_formatted_name(); - $new_stock = wc_update_product_stock( $product, $item_stock_reduced, 'increase' ); - - if ( is_wp_error( $new_stock ) ) { - /* translators: %s item name. */ - $order->add_order_note( sprintf( __( 'Unable to restore stock for item %s.', 'woocommerce' ), $item_name ) ); - } else { - /* translators: %1$s: item name, %2$s stock change */ - $order->add_order_note( sprintf( __( '%1$s stock increased %2$s.', 'woocommerce' ), $item_name, ( $new_stock - $item_stock_reduced ) . '→' . $new_stock ) ); - } - } - } - } - $data_store->delete_order_item( $item_id ); do_action( 'woocommerce_delete_order_item', $item_id ); @@ -195,7 +170,7 @@ function wc_get_order_item_meta( $item_id, $key, $single = true ) { /** * Get order ID by order item ID. * - * @param int $item_id Item ID. + * @param int $item_id Item ID. * * @throws Exception When `WC_Data_Store::load` validation fails. * @return int From 5ef45f51f645b67a5133322f0ab1a11623c8d377 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 3 Jan 2019 12:23:49 +0000 Subject: [PATCH 023/401] Stock handling in ajax class only --- assets/js/admin/meta-boxes-order.js | 16 ++++- includes/class-wc-ajax.php | 106 +++++++++++++++++++++------- 2 files changed, 96 insertions(+), 26 deletions(-) diff --git a/assets/js/admin/meta-boxes-order.js b/assets/js/admin/meta-boxes-order.js index 18bfdce16c2..a8424790cf6 100644 --- a/assets/js/admin/meta-boxes-order.js +++ b/assets/js/admin/meta-boxes-order.js @@ -560,6 +560,13 @@ jQuery( function ( $ ) { if ( response.success ) { $( '#woocommerce-order-items' ).find( '.inside' ).empty(); $( '#woocommerce-order-items' ).find( '.inside' ).append( response.data.html ); + + // Update notes. + if ( response.data.notes_html ) { + $( 'ul.order_notes' ).empty(); + $( 'ul.order_notes' ).append( $( response.data.notes_html ).find( 'li' ) ); + } + wc_meta_boxes_order_items.reloaded_items(); wc_meta_boxes_order_items.unblock(); } else { @@ -641,7 +648,7 @@ jQuery( function ( $ ) { items: $( 'table.woocommerce_order_items :input[name], .wc-order-totals-items :input[name]' ).serialize(), security: woocommerce_admin_meta_boxes.calc_totals_nonce } ); - + $( document.body ).trigger( 'order-totals-recalculate-before', data ); $.ajax({ @@ -978,6 +985,13 @@ jQuery( function ( $ ) { if ( response.success ) { $( '#woocommerce-order-items' ).find( '.inside' ).empty(); $( '#woocommerce-order-items' ).find( '.inside' ).append( response.data.html ); + + // Update notes. + if ( response.data.notes_html ) { + $( 'ul.order_notes' ).empty(); + $( 'ul.order_notes' ).append( $( response.data.notes_html ).find( 'li' ) ); + } + wc_meta_boxes_order_items.reloaded_items(); wc_meta_boxes_order_items.unblock(); } else { diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php index 76d68876c00..e10273029f2 100644 --- a/includes/class-wc-ajax.php +++ b/includes/class-wc-ajax.php @@ -838,7 +838,7 @@ class WC_AJAX { } /** - * Add order item via ajax. + * Add order item via ajax. Used on the edit order screen in WP Admim. */ public static function add_order_item() { check_ajax_referer( 'order-item', 'security' ); @@ -852,26 +852,25 @@ class WC_AJAX { throw new Exception( __( 'Invalid order', 'woocommerce' ) ); } - $order_id = absint( wp_unslash( $_POST['order_id'] ) ); // WPCS: input var ok. - $order = wc_get_order( $order_id ); + $order_id = absint( wp_unslash( $_POST['order_id'] ) ); // WPCS: input var ok. + $order = wc_get_order( $order_id ); if ( ! $order ) { throw new Exception( __( 'Invalid order', 'woocommerce' ) ); } // If we passed through items it means we need to save first before adding a new one. - $items = ( ! empty( $_POST['items'] ) ) ? $_POST['items'] : ''; - - if ( ! empty( $items ) ) { + if ( ! empty( $_POST['items'] ) ) { $save_items = array(); - parse_str( $items, $save_items ); - // Save order items. + parse_str( wp_unslash( $_POST['items'] ), $save_items ); wc_save_order_items( $order->get_id(), $save_items ); } $items_to_add = array_filter( wp_unslash( (array) $_POST['data'] ) ); // Add items to order. + $order_notes = array(); + foreach ( $items_to_add as $item ) { if ( ! isset( $item['id'], $item['qty'] ) || empty( $item['id'] ) ) { continue; @@ -884,22 +883,42 @@ class WC_AJAX { throw new Exception( __( 'Invalid product ID', 'woocommerce' ) . ' ' . $product_id ); } - $item_id = $order->add_product( $product, $qty, array(), true ); + $item_id = $order->add_product( $product, $qty ); $item = apply_filters( 'woocommerce_ajax_order_item', $order->get_item( $item_id ), $item_id ); $added_items[ $item_id ] = $item; + $order_notes[ $item_id ] = $product->get_formatted_name(); + + if ( $product->managing_stock() ) { + $new_stock = wc_update_product_stock( $product, $qty, 'decrease' ); + $order_notes[ $item_id ] = $product->get_formatted_name() . ' – ' . ( $new_stock + $qty ) . '→' . $new_stock; + $item->add_meta_data( '_reduced_stock', $qty, true ); + $item->save(); + } do_action( 'woocommerce_ajax_add_order_item_meta', $item_id, $item, $order ); } + /* translators: %s item name. */ + $order->add_order_note( sprintf( __( 'Added line items: %s', 'woocommerce' ), implode( ', ', $order_notes ) ), false, true ); + do_action( 'woocommerce_ajax_order_items_added', $added_items, $order ); $data = get_post_meta( $order_id ); + // Get HTML to return. ob_start(); include 'admin/meta-boxes/views/html-order-items.php'; + $items_html = ob_get_clean(); + + ob_start(); + $notes = wc_get_order_notes( array( 'order_id' => $order_id ) ); + include 'admin/meta-boxes/views/html-order-notes.php'; + $notes_html = ob_get_clean(); + wp_send_json_success( array( - 'html' => ob_get_clean(), + 'html' => $items_html, + 'notes_html' => $notes_html, ) ); } catch ( Exception $e ) { @@ -1106,14 +1125,23 @@ class WC_AJAX { } try { - $order_id = absint( $_POST['order_id'] ); - $order_item_ids = $_POST['order_item_ids']; - $items = ( ! empty( $_POST['items'] ) ) ? $_POST['items'] : ''; + $order_id = absint( $_POST['order_id'] ); + $order = wc_get_order( $order_id ); + + if ( ! $order ) { + throw new Exception( __( 'Invalid order', 'woocommerce' ) ); + } + + if ( ! isset( $_POST['order_item_ids'] ) ) { + throw new Exception( __( 'Invalid items', 'woocommerce' ) ); + } + + $order_item_ids = wp_unslash( $_POST['order_item_ids'] ); $calculate_tax_args = array( - 'country' => strtoupper( wc_clean( $_POST['country'] ) ), - 'state' => strtoupper( wc_clean( $_POST['state'] ) ), - 'postcode' => strtoupper( wc_clean( $_POST['postcode'] ) ), - 'city' => strtoupper( wc_clean( $_POST['city'] ) ), + 'country' => strtoupper( wc_clean( wp_unslash( $_POST['country'] ) ) ), + 'state' => strtoupper( wc_clean( wp_unslash( $_POST['state'] ) ) ), + 'postcode' => strtoupper( wc_clean( wp_unslash( $_POST['postcode'] ) ) ), + 'city' => strtoupper( wc_clean( wp_unslash( $_POST['city'] ) ) ), ); if ( ! is_array( $order_item_ids ) && is_numeric( $order_item_ids ) ) { @@ -1121,16 +1149,36 @@ class WC_AJAX { } // If we passed through items it means we need to save first before deleting. - if ( ! empty( $items ) ) { + if ( ! empty( $_POST['items'] ) ) { $save_items = array(); - parse_str( $items, $save_items ); - // Save order items - wc_save_order_items( $order_id, $save_items ); + parse_str( wp_unslash( $_POST['items'] ), $save_items ); + wc_save_order_items( $order->get_id(), $save_items ); } - if ( sizeof( $order_item_ids ) > 0 ) { - foreach ( $order_item_ids as $id ) { - wc_delete_order_item( absint( $id ), true ); + if ( ! empty( $order_item_ids ) ) { + $order_notes = array(); + + foreach ( $order_item_ids as $item_id ) { + $item_id = absint( $item_id ); + $item = $order->get_item( $item_id ); + + // Before deleting the item, adjust any stock values already reduced. + if ( $item->is_type( 'line_item' ) ) { + $product = $item->get_product(); + $item_stock_reduced = $item->get_meta( '_reduced_stock', true ); + + if ( $item_stock_reduced && $product->managing_stock() ) { + $new_stock = wc_update_product_stock( $product, $item_stock_reduced, 'increase' ); + + /* translators: %1$s: item name %2$s: stock change */ + $order->add_order_note( sprintf( __( 'Deleted %1$s and restored stock (%2$s)', 'woocommerce' ), $item->get_name(), ( ( $new_stock - $item_stock_reduced ) . '→' . $new_stock ) ), false, true ); + } else { + /* translators: %s item name. */ + $order->add_order_note( sprintf( __( 'Deleted %s', 'woocommerce' ), $item->get_name() ), false, true ); + } + } + + wc_delete_order_item( $item_id ); } } @@ -1138,12 +1186,20 @@ class WC_AJAX { $order->calculate_taxes( $calculate_tax_args ); $order->calculate_totals( false ); + // Get HTML to return. ob_start(); include 'admin/meta-boxes/views/html-order-items.php'; + $items_html = ob_get_clean(); + + ob_start(); + $notes = wc_get_order_notes( array( 'order_id' => $order_id ) ); + include 'admin/meta-boxes/views/html-order-notes.php'; + $notes_html = ob_get_clean(); wp_send_json_success( array( - 'html' => ob_get_clean(), + 'html' => $items_html, + 'notes_html' => $notes_html, ) ); } catch ( Exception $e ) { From 247fd3f610521a278636a6925965c6e231216e7c Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 3 Jan 2019 13:37:32 +0000 Subject: [PATCH 024/401] Update notes with response --- assets/js/admin/meta-boxes-order.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/assets/js/admin/meta-boxes-order.js b/assets/js/admin/meta-boxes-order.js index a8424790cf6..6cd18357129 100644 --- a/assets/js/admin/meta-boxes-order.js +++ b/assets/js/admin/meta-boxes-order.js @@ -687,10 +687,22 @@ jQuery( function ( $ ) { data: data, type: 'POST', success: function( response ) { - $( '#woocommerce-order-items' ).find( '.inside' ).empty(); - $( '#woocommerce-order-items' ).find( '.inside' ).append( response ); - wc_meta_boxes_order_items.reloaded_items(); - wc_meta_boxes_order_items.unblock(); + if ( response.success ) { + $( '#woocommerce-order-items' ).find( '.inside' ).empty(); + $( '#woocommerce-order-items' ).find( '.inside' ).append( response.data.html ); + + // Update notes. + if ( response.data.notes_html ) { + $( 'ul.order_notes' ).empty(); + $( 'ul.order_notes' ).append( $( response.data.notes_html ).find( 'li' ) ); + } + + wc_meta_boxes_order_items.reloaded_items(); + wc_meta_boxes_order_items.unblock(); + } else { + wc_meta_boxes_order_items.unblock(); + window.alert( response.data.error ); + } } }); From db0850a420d8f22f5ee1ad2d652efcecb61904be Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 3 Jan 2019 13:40:22 +0000 Subject: [PATCH 025/401] wc_maybe_adjust_line_item_product_stock helper --- includes/admin/wc-admin-functions.php | 123 ++++++++++++++++++++------ 1 file changed, 94 insertions(+), 29 deletions(-) diff --git a/includes/admin/wc-admin-functions.php b/includes/admin/wc-admin-functions.php index d56a72829c8..724ed230f65 100644 --- a/includes/admin/wc-admin-functions.php +++ b/includes/admin/wc-admin-functions.php @@ -2,14 +2,12 @@ /** * WooCommerce Admin Functions * - * @author WooThemes - * @category Core * @package WooCommerce/Admin/Functions * @version 2.4.0 */ if ( ! defined( 'ABSPATH' ) ) { - exit; // Exit if accessed directly + exit; } /** @@ -46,7 +44,9 @@ function wc_get_screen_ids() { $screen_ids[] = 'edit-' . $type; } - if ( $attributes = wc_get_attribute_taxonomies() ) { + $attributes = wc_get_attribute_taxonomies(); + + if ( $attributes ) { foreach ( $attributes as $attribute ) { $screen_ids[] = 'edit-' . wc_attribute_taxonomy_name( $attribute->attribute_name ); } @@ -58,30 +58,32 @@ function wc_get_screen_ids() { /** * Create a page and store the ID in an option. * - * @param mixed $slug Slug for the new page - * @param string $option Option name to store the page's ID - * @param string $page_title (default: '') Title for the new page - * @param string $page_content (default: '') Content for the new page - * @param int $post_parent (default: 0) Parent for the new page - * @return int page ID + * @param mixed $slug Slug for the new page. + * @param string $option Option name to store the page's ID. + * @param string $page_title (default: '') Title for the new page. + * @param string $page_content (default: '') Content for the new page. + * @param int $post_parent (default: 0) Parent for the new page. + * @return int page ID. */ function wc_create_page( $slug, $option = '', $page_title = '', $page_content = '', $post_parent = 0 ) { global $wpdb; $option_value = get_option( $option ); - if ( $option_value > 0 && ( $page_object = get_post( $option_value ) ) ) { - if ( 'page' === $page_object->post_type && ! in_array( $page_object->post_status, array( 'pending', 'trash', 'future', 'auto-draft' ) ) ) { - // Valid page is already in place + if ( $option_value > 0 ) { + $page_object = get_post( $option_value ); + + if ( $page_object && 'page' === $page_object->post_type && ! in_array( $page_object->post_status, array( 'pending', 'trash', 'future', 'auto-draft' ), true ) ) { + // Valid page is already in place. return $page_object->ID; } } if ( strlen( $page_content ) > 0 ) { - // Search for an existing page with the specified page content (typically a shortcode) + // Search for an existing page with the specified page content (typically a shortcode). $valid_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status NOT IN ( 'pending', 'trash', 'future', 'auto-draft' ) AND post_content LIKE %s LIMIT 1;", "%{$page_content}%" ) ); } else { - // Search for an existing page with the specified page slug + // Search for an existing page with the specified page slug. $valid_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status NOT IN ( 'pending', 'trash', 'future', 'auto-draft' ) AND post_name = %s LIMIT 1;", $slug ) ); } @@ -94,12 +96,12 @@ function wc_create_page( $slug, $option = '', $page_title = '', $page_content = return $valid_page_found; } - // Search for a matching valid trashed page + // Search for a matching valid trashed page. if ( strlen( $page_content ) > 0 ) { - // Search for an existing page with the specified page content (typically a shortcode) + // Search for an existing page with the specified page content (typically a shortcode). $trashed_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status = 'trash' AND post_content LIKE %s LIMIT 1;", "%{$page_content}%" ) ); } else { - // Search for an existing page with the specified page slug + // Search for an existing page with the specified page slug. $trashed_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status = 'trash' AND post_name = %s LIMIT 1;", $slug ) ); } @@ -136,7 +138,7 @@ function wc_create_page( $slug, $option = '', $page_title = '', $page_content = * * Loops though the woocommerce options array and outputs each field. * - * @param array $options Opens array to output + * @param array $options Opens array to output. */ function woocommerce_admin_fields( $options ) { @@ -150,8 +152,8 @@ function woocommerce_admin_fields( $options ) { /** * Update all settings which are passed. * - * @param array $options - * @param array $data + * @param array $options Option fields to save. + * @param array $data Passed data. */ function woocommerce_update_options( $options, $data = null ) { @@ -165,8 +167,8 @@ function woocommerce_update_options( $options, $data = null ) { /** * Get a setting from the settings API. * - * @param mixed $option_name - * @param mixed $default + * @param mixed $option_name Option name to save. + * @param mixed $default Default value to save. * @return string */ function woocommerce_settings_get_option( $option_name, $default = '' ) { @@ -178,17 +180,61 @@ function woocommerce_settings_get_option( $option_name, $default = '' ) { return WC_Admin_Settings::get_option( $option_name, $default ); } +/** + * Sees if line item stock has already reduced stock, and whether those values need adjusting e.g. after changing item qty. + * + * @since 3.6.0 + * @param WC_Order_Item $item Item object. + * @param integer $item_quantity Optional quantity to check against. Read from object if not passed. + * @return boolean|array|WP_Error Array of changes or error object when stock is updated (@see wc_update_product_stock). False if nothing changes. + */ +function wc_maybe_adjust_line_item_product_stock( $item, $item_quantity = -1 ) { + if ( 'line_item' !== $item->get_type() ) { + return; + } + + $product = $item->get_product(); + $item_quantity = wc_stock_amount( $item_quantity >= 0 ? $item_quantity : $item->get_quantity() ); + $already_reduced_stock = wc_stock_amount( $item->get_meta( '_reduced_stock', true ) ); + + if ( ! $product || ! $product->managing_stock() || ! $already_reduced_stock || $item_quantity === $already_reduced_stock ) { + return; + } + + $diff = $item_quantity - $already_reduced_stock; + + if ( $diff < 0 ) { + $new_stock = wc_update_product_stock( $product, $diff * -1, 'increase' ); + } else { + $new_stock = wc_update_product_stock( $product, $diff, 'decrease' ); + } + + if ( is_wp_error( $new_stock ) ) { + return $new_stock; + } + + $item->update_meta_data( '_reduced_stock', $item_quantity ); + $item->save(); + + return array( + 'from' => $new_stock + $diff, + 'to' => $new_stock, + ); +} + /** * Save order items. Uses the CRUD. * * @since 2.2 - * @param int $order_id Order ID - * @param array $items Order items to save + * @param int $order_id Order ID. + * @param array $items Order items to save. */ function wc_save_order_items( $order_id, $items ) { // Allow other plugins to check change in order items before they are saved. do_action( 'woocommerce_before_save_order_items', $order_id, $items ); + $qty_change_order_notes = array(); + // Line items and fees. if ( isset( $items['order_item_id'] ) ) { $data_keys = array( @@ -201,7 +247,9 @@ function wc_save_order_items( $order_id, $items ) { 'line_subtotal' => null, ); foreach ( $items['order_item_id'] as $item_id ) { - if ( ! $item = WC_Order_Factory::get_order_item( absint( $item_id ) ) ) { + $item = WC_Order_Factory::get_order_item( absint( $item_id ) ); + + if ( ! $item ) { continue; } @@ -212,6 +260,10 @@ function wc_save_order_items( $order_id, $items ) { } if ( '0' === $item_data['order_item_qty'] ) { + $changed_stock = wc_maybe_adjust_line_item_product_stock( $item, 0 ); + if ( $changed_stock && ! is_wp_error( $changed_stock ) ) { + $qty_change_order_notes[] = $item->get_name() . ' – ' . $changed_stock['from'] . '→' . $changed_stock['to']; + } $item->delete(); continue; } @@ -255,10 +307,15 @@ function wc_save_order_items( $order_id, $items ) { do_action( 'woocommerce_before_save_order_item', $item ); $item->save(); + + $changed_stock = wc_maybe_adjust_line_item_product_stock( $item ); + if ( $changed_stock && ! is_wp_error( $changed_stock ) ) { + $qty_change_order_notes[] = $item->get_name() . ' (' . $changed_stock['from'] . '→' . $changed_stock['to'] . ')'; + } } } - // Shipping Rows + // Shipping Rows. if ( isset( $items['shipping_method_id'] ) ) { $data_keys = array( 'shipping_method' => null, @@ -268,7 +325,9 @@ function wc_save_order_items( $order_id, $items ) { ); foreach ( $items['shipping_method_id'] as $item_id ) { - if ( ! $item = WC_Order_Factory::get_order_item( absint( $item_id ) ) ) { + $item = WC_Order_Factory::get_order_item( absint( $item_id ) ); + + if ( ! $item ) { continue; } @@ -310,10 +369,16 @@ function wc_save_order_items( $order_id, $items ) { } $order = wc_get_order( $order_id ); + + if ( ! empty( $qty_change_order_notes ) ) { + /* translators: %s item name. */ + $order->add_order_note( sprintf( __( 'Adjusted stock: %s', 'woocommerce' ), implode( ', ', $qty_change_order_notes ) ), false, true ); + } + $order->update_taxes(); $order->calculate_totals( false ); - // Inform other plugins that the items have been saved + // Inform other plugins that the items have been saved. do_action( 'woocommerce_saved_order_items', $order_id, $items ); } From cbee6af449c7166ff0048e9abb6fe84cc7066255 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 3 Jan 2019 13:40:48 +0000 Subject: [PATCH 026/401] Use helper and return notes. --- includes/class-wc-ajax.php | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php index e10273029f2..d8b249546ad 100644 --- a/includes/class-wc-ajax.php +++ b/includes/class-wc-ajax.php @@ -1164,14 +1164,11 @@ class WC_AJAX { // Before deleting the item, adjust any stock values already reduced. if ( $item->is_type( 'line_item' ) ) { - $product = $item->get_product(); - $item_stock_reduced = $item->get_meta( '_reduced_stock', true ); - - if ( $item_stock_reduced && $product->managing_stock() ) { - $new_stock = wc_update_product_stock( $product, $item_stock_reduced, 'increase' ); + $changed_stock = wc_maybe_adjust_line_item_product_stock( $item, 0 ); + if ( $changed_stock && ! is_wp_error( $changed_stock ) ) { /* translators: %1$s: item name %2$s: stock change */ - $order->add_order_note( sprintf( __( 'Deleted %1$s and restored stock (%2$s)', 'woocommerce' ), $item->get_name(), ( ( $new_stock - $item_stock_reduced ) . '→' . $new_stock ) ), false, true ); + $order->add_order_note( sprintf( __( 'Deleted %1$s and adjusted stock (%2$s)', 'woocommerce' ), $item->get_name(), $changed_stock['from'] . '→' . $changed_stock['to'] ), false, true ); } else { /* translators: %s item name. */ $order->add_order_note( sprintf( __( 'Deleted %s', 'woocommerce' ), $item->get_name() ), false, true ); @@ -1294,7 +1291,23 @@ class WC_AJAX { // Return HTML items $order = wc_get_order( $order_id ); + + // Get HTML to return. + ob_start(); include 'admin/meta-boxes/views/html-order-items.php'; + $items_html = ob_get_clean(); + + ob_start(); + $notes = wc_get_order_notes( array( 'order_id' => $order_id ) ); + include 'admin/meta-boxes/views/html-order-notes.php'; + $notes_html = ob_get_clean(); + + wp_send_json_success( + array( + 'html' => $items_html, + 'notes_html' => $notes_html, + ) + ); } wp_die(); } From c4e071480aa37d1ebadf1c2ce36a4f34fa57905f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 4 Jan 2019 11:04:12 +0000 Subject: [PATCH 027/401] phpcs --- .../settings/class-wc-settings-shipping.php | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/includes/admin/settings/class-wc-settings-shipping.php b/includes/admin/settings/class-wc-settings-shipping.php index 797551b0925..05ea60a8374 100644 --- a/includes/admin/settings/class-wc-settings-shipping.php +++ b/includes/admin/settings/class-wc-settings-shipping.php @@ -76,8 +76,8 @@ class WC_Settings_Shipping extends WC_Settings_Page { if ( '' === $current_section ) { $settings = apply_filters( - 'woocommerce_shipping_settings', array( - + 'woocommerce_shipping_settings', + array( array( 'title' => __( 'Shipping options', 'woocommerce' ), 'type' => 'title', @@ -229,7 +229,6 @@ class WC_Settings_Shipping extends WC_Settings_Page { wp_die( esc_html__( 'Zone does not exist!', 'woocommerce' ) ); } - $allowed_countries = WC()->countries->get_allowed_countries(); $wc_shipping = WC_Shipping::instance(); $shipping_methods = $wc_shipping->get_shipping_methods(); $continents = WC()->countries->get_continents(); @@ -247,7 +246,9 @@ class WC_Settings_Shipping extends WC_Settings_Page { } wp_localize_script( - 'wc-shipping-zone-methods', 'shippingZoneMethodsLocalizeScript', array( + 'wc-shipping-zone-methods', + 'shippingZoneMethodsLocalizeScript', + array( 'methods' => $zone->get_shipping_methods( false, 'json' ), 'zone_name' => $zone->get_zone_name(), 'zone_id' => $zone->get_id(), @@ -272,12 +273,13 @@ class WC_Settings_Shipping extends WC_Settings_Page { * Show zones */ protected function zones_screen() { - $allowed_countries = WC()->countries->get_allowed_countries(); $continents = WC()->countries->get_continents(); $method_count = wc_get_shipping_method_count(); wp_localize_script( - 'wc-shipping-zones', 'shippingZonesLocalizeScript', array( + 'wc-shipping-zones', + 'shippingZonesLocalizeScript', + array( 'zones' => WC_Shipping_Zones::get_zones( 'json' ), 'default_zone' => array( 'zone_id' => 0, @@ -336,7 +338,9 @@ class WC_Settings_Shipping extends WC_Settings_Page { protected function output_shipping_class_screen() { $wc_shipping = WC_Shipping::instance(); wp_localize_script( - 'wc-shipping-classes', 'shippingClassesLocalizeScript', array( + 'wc-shipping-classes', + 'shippingClassesLocalizeScript', + array( 'classes' => $wc_shipping->get_shipping_classes(), 'default_shipping_class' => array( 'term_id' => 0, @@ -354,7 +358,8 @@ class WC_Settings_Shipping extends WC_Settings_Page { // Extendable columns to show on the shipping classes screen. $shipping_class_columns = apply_filters( - 'woocommerce_shipping_classes_columns', array( + 'woocommerce_shipping_classes_columns', + array( 'wc-shipping-class-name' => __( 'Shipping class', 'woocommerce' ), 'wc-shipping-class-slug' => __( 'Slug', 'woocommerce' ), 'wc-shipping-class-description' => __( 'Description', 'woocommerce' ), From ff3a852b7f818fbf10dbcdab9f1866cecb7a042c Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 4 Jan 2019 11:04:17 +0000 Subject: [PATCH 028/401] Zones should use shipping countries, not allowed countries --- includes/admin/settings/class-wc-settings-shipping.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/includes/admin/settings/class-wc-settings-shipping.php b/includes/admin/settings/class-wc-settings-shipping.php index 05ea60a8374..59de3406339 100644 --- a/includes/admin/settings/class-wc-settings-shipping.php +++ b/includes/admin/settings/class-wc-settings-shipping.php @@ -229,6 +229,7 @@ class WC_Settings_Shipping extends WC_Settings_Page { wp_die( esc_html__( 'Zone does not exist!', 'woocommerce' ) ); } + $allowed_countries = WC()->countries->get_shipping_countries(); $wc_shipping = WC_Shipping::instance(); $shipping_methods = $wc_shipping->get_shipping_methods(); $continents = WC()->countries->get_continents(); @@ -273,6 +274,7 @@ class WC_Settings_Shipping extends WC_Settings_Page { * Show zones */ protected function zones_screen() { + $allowed_countries = WC()->countries->get_shipping_countries(); $continents = WC()->countries->get_continents(); $method_count = wc_get_shipping_method_count(); From da32fe3b8ae73448a33830839c3e273256029ca3 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 4 Jan 2019 11:05:29 +0000 Subject: [PATCH 029/401] Include country name alongside states so selectwoo can search properly Seems 'alt' was there in the hope select2 would search that but it's not possible without custom matchers. This instead will show state and country like this: `New York, United States.` This lets you find NY by searching for US --- .../html-admin-page-shipping-zone-methods.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/includes/admin/settings/views/html-admin-page-shipping-zone-methods.php b/includes/admin/settings/views/html-admin-page-shipping-zone-methods.php index 9524a51a17d..d376613d3f4 100644 --- a/includes/admin/settings/views/html-admin-page-shipping-zone-methods.php +++ b/includes/admin/settings/views/html-admin-page-shipping-zone-methods.php @@ -42,16 +42,18 @@ if ( ! defined( 'ABSPATH' ) ) {

attribute_name ] = $tax->attribute_name; } } + $std_attribute = current( $attribute_array ); } $this->settings = array( @@ -74,7 +76,7 @@ class WC_Widget_Layered_Nav extends WC_Widget { ), 'attribute' => array( 'type' => 'select', - 'std' => '', + 'std' => $std_attribute, 'label' => __( 'Attribute', 'woocommerce' ), 'options' => $attribute_array, ), @@ -99,6 +101,60 @@ class WC_Widget_Layered_Nav extends WC_Widget { ); } + /** + * Get this widgets title. + * + * @param array $instance Array of instance options. + * @return string + */ + protected function get_instance_title( $instance ) { + return isset( $instance['title'] ) ? $instance['title'] : __( 'Filter by', 'woocommerce' ); + } + + /** + * Get this widgets taxonomy. + * + * @param array $instance Array of instance options. + * @return string + */ + protected function get_instance_taxonomy( $instance ) { + if ( isset( $instance['attribute'] ) ) { + return wc_attribute_taxonomy_name( $instance['attribute'] ); + } + + $attribute_taxonomies = wc_get_attribute_taxonomies(); + + if ( ! empty( $attribute_taxonomies ) ) { + foreach ( $attribute_taxonomies as $tax ) { + if ( taxonomy_exists( wc_attribute_taxonomy_name( $tax->attribute_name ) ) ) { + return wc_attribute_taxonomy_name( $tax->attribute_name ); + } + } + } + + return ''; + } + + /** + * Get this widgets query type. + * + * @param array $instance Array of instance options. + * @return string + */ + protected function get_instance_query_type( $instance ) { + return isset( $instance['query_type'] ) ? $instance['query_type'] : 'and'; + } + + /** + * Get this widgets display type. + * + * @param array $instance Array of instance options. + * @return string + */ + protected function get_instance_display_type( $instance ) { + return isset( $instance['display_type'] ) ? $instance['display_type'] : 'list'; + } + /** * Output widget. * @@ -113,9 +169,9 @@ class WC_Widget_Layered_Nav extends WC_Widget { } $_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes(); - $taxonomy = isset( $instance['attribute'] ) ? wc_attribute_taxonomy_name( $instance['attribute'] ) : $this->settings['attribute']['std']; - $query_type = isset( $instance['query_type'] ) ? $instance['query_type'] : $this->settings['query_type']['std']; - $display_type = isset( $instance['display_type'] ) ? $instance['display_type'] : $this->settings['display_type']['std']; + $taxonomy = $this->get_instance_taxonomy( $instance ); + $query_type = $this->get_instance_query_type( $instance ); + $display_type = $this->get_instance_display_type( $instance ); if ( ! taxonomy_exists( $taxonomy ) ) { return; @@ -368,7 +424,7 @@ class WC_Widget_Layered_Nav extends WC_Widget { $query = implode( ' ', $query ); // We have a query - let's see if cached results of this query already exist. - $query_hash = md5( $query ); + $query_hash = md5( $query ); // Maybe store a transient of the count values. $cache = apply_filters( 'woocommerce_layered_nav_count_maybe_cache', true ); From 82365fd6d2fd231f47cf9f7fab3d48689852f0a2 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 4 Jan 2019 16:46:52 +0000 Subject: [PATCH 035/401] Tweak tax handling when exempt of VAT and woocommerce_adjust_non_base_location_prices is off --- includes/class-wc-cart-totals.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-cart-totals.php b/includes/class-wc-cart-totals.php index 538977363f0..7dda110ae97 100644 --- a/includes/class-wc-cart-totals.php +++ b/includes/class-wc-cart-totals.php @@ -421,7 +421,15 @@ final class WC_Cart_Totals { */ protected function remove_item_base_taxes( $item ) { if ( $item->price_includes_tax && $item->taxable ) { - $base_tax_rates = WC_Tax::get_base_tax_rates( $item->product->get_tax_class( 'unfiltered' ) ); + if ( apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ) { + $base_tax_rates = WC_Tax::get_base_tax_rates( $item->product->get_tax_class( 'unfiltered' ) ); + } else { + /** + * If we want all customers to pay the same price on this store, we should not remove base taxes from a VAT exempt user's price, + * but just the relevent tax rate. See issue #20911. + */ + $base_tax_rates = $item->tax_rates; + } // Work out a new base price without the shop's base tax. $taxes = WC_Tax::calc_tax( $item->price, $base_tax_rates, true ); From cb95d310662927f7bc9280c05883d3c992e69aaf Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 7 Jan 2019 15:01:46 +0000 Subject: [PATCH 036/401] Remove repetition from wc_get_customer_default_location and add validity check --- includes/wc-core-functions.php | 44 +++++++++++++++------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/includes/wc-core-functions.php b/includes/wc-core-functions.php index a63c9b39eb5..03c5e2a6ee3 100644 --- a/includes/wc-core-functions.php +++ b/includes/wc-core-functions.php @@ -1059,36 +1059,30 @@ function wc_get_base_location() { * @return array */ function wc_get_customer_default_location() { - $location = array(); + $set_default_location_to = get_option( 'woocommerce_default_customer_address', 'geolocation' ); + $default_location = '' === $set_default_location_to ? '' : get_option( 'woocommerce_default_country', '' ); + $location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', $default_location ) ); - switch ( get_option( 'woocommerce_default_customer_address' ) ) { - case 'geolocation_ajax': - case 'geolocation': - // Exclude common bots from geolocation by user agent. - $ua = strtolower( wc_get_user_agent() ); + // Geolocation takes priority if used and if geolocation is possible. + if ( 'geolocation' === $set_default_location_to || 'geolocation_ajax' === $set_default_location_to ) { + $ua = wc_get_user_agent(); - if ( ! strstr( $ua, 'bot' ) && ! strstr( $ua, 'spider' ) && ! strstr( $ua, 'crawl' ) ) { - $location = WC_Geolocation::geolocate_ip( '', true, false ); + // Exclude common bots from geolocation by user agent. + if ( ! stristr( $ua, 'bot' ) && ! stristr( $ua, 'spider' ) && ! stristr( $ua, 'crawl' ) ) { + $geolocation = WC_Geolocation::geolocate_ip( '', true, false ); + + if ( ! empty( $geolocation['country'] ) ) { + $location = $geolocation; } + } + } - // When selling to only one country, force customer location to that country. - $countries = WC()->countries->get_allowed_countries(); - if ( 1 === count( $countries ) ) { - $location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', key( $countries ) ) ); - } + // Once we have a location, ensure it's valid, otherwise fallback to a valid location. + $allowed_country_codes = WC()->countries->get_allowed_countries(); - // Base fallback. - if ( empty( $location['country'] ) ) { - $location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', get_option( 'woocommerce_default_country' ) ) ); - } - break; - case 'base': - $location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', get_option( 'woocommerce_default_country' ) ) ); - break; - default: - $countries = WC()->countries->get_allowed_countries(); - $location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', 1 === count( $countries ) ? key( $countries ) : '' ) ); - break; + if ( ! empty( $location['country'] ) && ! array_key_exists( $location['country'], $allowed_country_codes ) ) { + $location['country'] = current( $allowed_country_codes ); + $location['state'] = ''; } return apply_filters( 'woocommerce_customer_default_location_array', $location ); From ce2473e09f57e3b32e05477394eca9cd8c32d9a1 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 7 Jan 2019 16:03:23 +0000 Subject: [PATCH 037/401] Whenn loading address fields, enforce valid address --- includes/class-wc-checkout.php | 21 +++++++- .../class-wc-shortcode-my-account.php | 50 +++++++++++++------ 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/includes/class-wc-checkout.php b/includes/class-wc-checkout.php index 42a2d729731..45dffd4f8ec 100644 --- a/includes/class-wc-checkout.php +++ b/includes/class-wc-checkout.php @@ -198,13 +198,30 @@ class WC_Checkout { return $fieldset ? $this->fields[ $fieldset ] : $this->fields; } + // Fields are based on billing/shipping country. Grab those values but ensure they are valid for the store before using. + $billing_country = $this->get_value( 'billing_country' ); + $billing_country = empty( $billing_country ) ? WC()->countries->get_base_country() : $billing_country; + $allowed_countries = WC()->countries->get_allowed_countries(); + + if ( ! array_key_exists( $billing_country, $allowed_countries ) ) { + $billing_country = current( array_keys( $allowed_countries ) ); + } + + $shipping_country = $this->get_value( 'shipping_country' ); + $shipping_country = empty( $shipping_country ) ? WC()->countries->get_base_country() : $shipping_country; + $allowed_countries = WC()->countries->get_shipping_countries(); + + if ( ! array_key_exists( $shipping_country, $allowed_countries ) ) { + $shipping_country = current( array_keys( $allowed_countries ) ); + } + $this->fields = array( 'billing' => WC()->countries->get_address_fields( - $this->get_value( 'billing_country' ), + $billing_country, 'billing_' ), 'shipping' => WC()->countries->get_address_fields( - $this->get_value( 'shipping_country' ), + $shipping_country, 'shipping_' ), 'account' => array(), diff --git a/includes/shortcodes/class-wc-shortcode-my-account.php b/includes/shortcodes/class-wc-shortcode-my-account.php index 5229aa829d6..8a0825e80af 100644 --- a/includes/shortcodes/class-wc-shortcode-my-account.php +++ b/includes/shortcodes/class-wc-shortcode-my-account.php @@ -111,11 +111,14 @@ class WC_Shortcode_My_Account { $args = shortcode_atts( array( 'order_count' => 15, // @deprecated 2.6.0. Keep for backward compatibility. - ), $atts, 'woocommerce_my_account' + ), + $atts, + 'woocommerce_my_account' ); wc_get_template( - 'myaccount/my-account.php', array( + 'myaccount/my-account.php', + array( 'current_user' => get_user_by( 'id', get_current_user_id() ), 'order_count' => 'all' === $args['order_count'] ? -1 : $args['order_count'], ) @@ -141,7 +144,8 @@ class WC_Shortcode_My_Account { $status->name = wc_get_order_status_name( $order->get_status() ); wc_get_template( - 'myaccount/view-order.php', array( + 'myaccount/view-order.php', + array( 'status' => $status, // @deprecated 2.2. 'order' => wc_get_order( $order_id ), 'order_id' => $order_id, @@ -164,8 +168,29 @@ class WC_Shortcode_My_Account { public static function edit_address( $load_address = 'billing' ) { $current_user = wp_get_current_user(); $load_address = sanitize_key( $load_address ); + $country = get_user_meta( get_current_user_id(), $load_address . '_country', true ); - $address = WC()->countries->get_address_fields( get_user_meta( get_current_user_id(), $load_address . '_country', true ), $load_address . '_' ); + if ( ! $country ) { + $country = WC()->countries->get_base_country(); + } + + if ( 'billing' === $load_address ) { + $allowed_countries = WC()->countries->get_allowed_countries(); + + if ( ! array_key_exists( $country, $allowed_countries ) ) { + $country = current( array_keys( $allowed_countries ) ); + } + } + + if ( 'shipping' === $load_address ) { + $allowed_countries = WC()->countries->get_shipping_countries(); + + if ( ! array_key_exists( $country, $allowed_countries ) ) { + $country = current( array_keys( $allowed_countries ) ); + } + } + + $address = WC()->countries->get_address_fields( $country, $load_address . '_' ); // Enqueue scripts. wp_enqueue_script( 'wc-country-select' ); @@ -182,14 +207,6 @@ class WC_Shortcode_My_Account { case 'shipping_email': $value = $current_user->user_email; break; - case 'billing_country': - case 'shipping_country': - $value = WC()->countries->get_base_country(); - break; - case 'billing_state': - case 'shipping_state': - $value = WC()->countries->get_base_state(); - break; } } @@ -197,7 +214,8 @@ class WC_Shortcode_My_Account { } wc_get_template( - 'myaccount/form-edit-address.php', array( + 'myaccount/form-edit-address.php', + array( 'load_address' => $load_address, 'address' => apply_filters( 'woocommerce_address_to_edit', $address, $load_address ), ) @@ -225,7 +243,8 @@ class WC_Shortcode_My_Account { // Reset key / login is correct, display reset password form with hidden key / login values. if ( is_object( $user ) ) { return wc_get_template( - 'myaccount/form-reset-password.php', array( + 'myaccount/form-reset-password.php', + array( 'key' => $rp_key, 'login' => $rp_login, ) @@ -236,7 +255,8 @@ class WC_Shortcode_My_Account { // Show lost password form by default. wc_get_template( - 'myaccount/form-lost-password.php', array( + 'myaccount/form-lost-password.php', + array( 'form' => 'lost_password', ) ); From 04d7e54d42a1386ccec2382ded6e186fbbbf70da Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 7 Jan 2019 16:04:01 +0000 Subject: [PATCH 038/401] Set country value when loading fields rather than in template files --- includes/class-wc-countries.php | 1 + templates/checkout/form-billing.php | 27 +++++++++-------------- templates/checkout/form-shipping.php | 23 ++++++++----------- templates/myaccount/form-edit-address.php | 5 +---- 4 files changed, 21 insertions(+), 35 deletions(-) diff --git a/includes/class-wc-countries.php b/includes/class-wc-countries.php index 19230bef10b..046f152217a 100644 --- a/includes/class-wc-countries.php +++ b/includes/class-wc-countries.php @@ -1273,6 +1273,7 @@ class WC_Countries { foreach ( $fields as $key => $value ) { if ( 'state' === $key ) { $value['country_field'] = $type . 'country'; + $value['country'] = $country; } $address_fields[ $type . $key ] = $value; } diff --git a/templates/checkout/form-billing.php b/templates/checkout/form-billing.php index e09dd829d1d..731b3187c3a 100644 --- a/templates/checkout/form-billing.php +++ b/templates/checkout/form-billing.php @@ -12,24 +12,20 @@ * * @see https://docs.woocommerce.com/document/template-structure/ * @package WooCommerce/Templates - * @version 3.0.9 + * @version 3.6.0 + * @global WC_Checkout $checkout */ -if ( ! defined( 'ABSPATH' ) ) { - exit; // Exit if accessed directly -} - -/** @global WC_Checkout $checkout */ - +defined( 'ABSPATH' ) || exit; ?>
cart->needs_shipping() ) : ?> -

+

-

+

@@ -37,14 +33,11 @@ if ( ! defined( 'ABSPATH' ) ) {
get_checkout_fields( 'billing' ); + $fields = $checkout->get_checkout_fields( 'billing' ); - foreach ( $fields as $key => $field ) { - if ( isset( $field['country_field'], $fields[ $field['country_field'] ] ) ) { - $field['country'] = $checkout->get_value( $field['country_field'] ); - } - woocommerce_form_field( $key, $field, $checkout->get_value( $key ) ); - } + foreach ( $fields as $key => $field ) { + woocommerce_form_field( $key, $field, $checkout->get_value( $key ) ); + } ?>
@@ -57,7 +50,7 @@ if ( ! defined( 'ABSPATH' ) ) { diff --git a/templates/checkout/form-shipping.php b/templates/checkout/form-shipping.php index c7ae79adebe..3e2c164f07d 100644 --- a/templates/checkout/form-shipping.php +++ b/templates/checkout/form-shipping.php @@ -12,20 +12,18 @@ * * @see https://docs.woocommerce.com/document/template-structure/ * @package WooCommerce/Templates - * @version 3.0.9 + * @version 3.6.0 + * @global WC_Checkout $checkout */ -if ( ! defined( 'ABSPATH' ) ) { - exit; // Exit if accessed directly -} - +defined( 'ABSPATH' ) || exit; ?>
cart->needs_shipping_address() ) : ?>

@@ -35,14 +33,11 @@ if ( ! defined( 'ABSPATH' ) ) {
get_checkout_fields( 'shipping' ); + $fields = $checkout->get_checkout_fields( 'shipping' ); - foreach ( $fields as $key => $field ) { - if ( isset( $field['country_field'], $fields[ $field['country_field'] ] ) ) { - $field['country'] = $checkout->get_value( $field['country_field'] ); - } - woocommerce_form_field( $key, $field, $checkout->get_value( $key ) ); - } + foreach ( $fields as $key => $field ) { + woocommerce_form_field( $key, $field, $checkout->get_value( $key ) ); + } ?>
@@ -59,7 +54,7 @@ if ( ! defined( 'ABSPATH' ) ) { cart->needs_shipping() || wc_ship_to_billing_address_only() ) : ?> -

+

diff --git a/templates/myaccount/form-edit-address.php b/templates/myaccount/form-edit-address.php index 6122d2aaa17..a41ecf00d60 100644 --- a/templates/myaccount/form-edit-address.php +++ b/templates/myaccount/form-edit-address.php @@ -12,7 +12,7 @@ * * @see https://docs.woocommerce.com/document/template-structure/ * @package WooCommerce/Templates - * @version 3.4.0 + * @version 3.6.0 */ defined( 'ABSPATH' ) || exit; @@ -35,9 +35,6 @@ do_action( 'woocommerce_before_edit_account_address_form' ); ?>
$field ) { - if ( isset( $field['country_field'], $address[ $field['country_field'] ] ) ) { - $field['country'] = wc_get_post_data_by_key( $field['country_field'], $address[ $field['country_field'] ]['value'] ); - } woocommerce_form_field( $key, $field, wc_get_post_data_by_key( $key, $field['value'] ) ); } ?> From 7f71e0abe660786cd3216b9575f1a1d872e63cb0 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 7 Jan 2019 16:05:35 +0000 Subject: [PATCH 039/401] Add missing placeholder --- includes/wc-template-functions.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 82849c2b46c..827a2261398 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -910,9 +910,9 @@ if ( ! function_exists( 'woocommerce_content' ) ) { - + ' . get_the_title() . ''; + echo '

' . get_the_title() . '

'; // @codingStandardsIgnoreLine. } } if ( ! function_exists( 'woocommerce_template_loop_category_title' ) ) { @@ -2651,7 +2651,7 @@ if ( ! function_exists( 'woocommerce_form_field' ) ) { } elseif ( ! is_null( $for_country ) && is_array( $states ) ) { - $field .= ' '; foreach ( $states as $ckey => $cvalue ) { From a0aa663f29826be7638b9b10059308dd14fae9d1 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 8 Jan 2019 13:25:27 +0000 Subject: [PATCH 040/401] Attribute post data needs stripslashes --- includes/admin/meta-boxes/class-wc-meta-box-product-data.php | 2 +- includes/class-wc-ajax.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php index 1219d40fcd6..f463f6af277 100644 --- a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php +++ b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php @@ -228,7 +228,7 @@ class WC_Meta_Box_Product_Data { $attributes = array(); if ( ! $data ) { - $data = $_POST; + $data = stripslashes_deep( $_POST ); } if ( isset( $data['attribute_names'], $data['attribute_values'] ) ) { diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php index 92910a004d0..e3055f93e9d 100644 --- a/includes/class-wc-ajax.php +++ b/includes/class-wc-ajax.php @@ -615,7 +615,7 @@ class WC_AJAX { $response = array(); try { - parse_str( $_POST['data'], $data ); + parse_str( wp_unslash( $_POST['data'] ), $data ); $attributes = WC_Meta_Box_Product_Data::prepare_attributes( $data ); $product_id = absint( $_POST['post_id'] ); From e31b35597aad27a95a6c819b96ce2945bb268fc6 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 8 Jan 2019 13:28:26 +0000 Subject: [PATCH 041/401] Slash meta values to preserve slashes on save --- .../class-wc-product-data-store-cpt.php | 3 +- ...ss-wc-product-variation-data-store-cpt.php | 2 +- includes/wc-attribute-functions.php | 38 +++++++++++-------- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index 34dad6f3455..f27cc529f1c 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -764,7 +764,8 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da ); } } - update_post_meta( $product->get_id(), '_product_attributes', $meta_values ); + // Note, we use wp_slash to add extra level of escaping. See https://codex.wordpress.org/Function_Reference/update_post_meta#Workaround. + update_post_meta( $product->get_id(), '_product_attributes', wp_slash( $meta_values ) ); } } diff --git a/includes/data-stores/class-wc-product-variation-data-store-cpt.php b/includes/data-stores/class-wc-product-variation-data-store-cpt.php index eb3a99a201b..5d8cdbefe8f 100644 --- a/includes/data-stores/class-wc-product-variation-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-variation-data-store-cpt.php @@ -427,7 +427,7 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl $attributes = $product->get_attributes(); $updated_attribute_keys = array(); foreach ( $attributes as $key => $value ) { - update_post_meta( $product->get_id(), 'attribute_' . $key, $value ); + update_post_meta( $product->get_id(), 'attribute_' . $key, wp_slash( $value ) ); $updated_attribute_keys[] = 'attribute_' . $key; } diff --git a/includes/wc-attribute-functions.php b/includes/wc-attribute-functions.php index b4a3616f0c2..bae38e9651e 100644 --- a/includes/wc-attribute-functions.php +++ b/includes/wc-attribute-functions.php @@ -94,10 +94,11 @@ function wc_attribute_taxonomy_name_by_id( $attribute_id ) { $attribute_name = $wpdb->get_var( $wpdb->prepare( " - SELECT attribute_name - FROM {$wpdb->prefix}woocommerce_attribute_taxonomies - WHERE attribute_id = %d - ", $attribute_id + SELECT attribute_name + FROM {$wpdb->prefix}woocommerce_attribute_taxonomies + WHERE attribute_id = %d + ", + $attribute_id ) ); @@ -201,7 +202,8 @@ function wc_get_attribute_taxonomy_names() { */ function wc_get_attribute_types() { return (array) apply_filters( - 'product_attributes_type_selector', array( + 'product_attributes_type_selector', + array( 'select' => __( 'Select', 'woocommerce' ), ) ); @@ -382,10 +384,11 @@ function wc_get_attribute( $id ) { $data = $wpdb->get_row( $wpdb->prepare( " - SELECT * - FROM {$wpdb->prefix}woocommerce_attribute_taxonomies - WHERE attribute_id = %d - ", $id + SELECT * + FROM {$wpdb->prefix}woocommerce_attribute_taxonomies + WHERE attribute_id = %d + ", + $id ) ); @@ -543,7 +546,8 @@ function wc_create_attribute( $args ) { "SELECT post_id, meta_value FROM {$wpdb->postmeta} WHERE meta_key = '_product_attributes' AND meta_value LIKE %s", '%' . $wpdb->esc_like( $old_taxonomy_name ) . '%' ), - ARRAY_A ); + ARRAY_A + ); foreach ( $metadatas as $metadata ) { $product_id = $metadata['post_id']; $unserialized_data = maybe_unserialize( $metadata['meta_value'] ); @@ -554,7 +558,7 @@ function wc_create_attribute( $args ) { $unserialized_data[ $new_taxonomy_name ] = $unserialized_data[ $old_taxonomy_name ]; unset( $unserialized_data[ $old_taxonomy_name ] ); $unserialized_data[ $new_taxonomy_name ]['name'] = $new_taxonomy_name; - update_post_meta( $product_id, '_product_attributes', $unserialized_data ); + update_post_meta( $product_id, '_product_attributes', wp_slash( $unserialized_data ) ); } // Update variations which use this taxonomy. @@ -600,7 +604,8 @@ function wc_update_attribute( $id, $args ) { SELECT attribute_name FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_id = %d - ", $args['id'] + ", + $args['id'] ) ); @@ -620,10 +625,11 @@ function wc_delete_attribute( $id ) { $name = $wpdb->get_var( $wpdb->prepare( " - SELECT attribute_name - FROM {$wpdb->prefix}woocommerce_attribute_taxonomies - WHERE attribute_id = %d - ", $id + SELECT attribute_name + FROM {$wpdb->prefix}woocommerce_attribute_taxonomies + WHERE attribute_id = %d + ", + $id ) ); From a4925ff916eb72b8f7f4ee9b7133489c6b554280 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 8 Jan 2019 14:08:27 +0000 Subject: [PATCH 042/401] Loop to match text versions of attribute names and avoid problems with entities --- assets/js/frontend/add-to-cart-variation.js | 31 +++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/assets/js/frontend/add-to-cart-variation.js b/assets/js/frontend/add-to-cart-variation.js index 08b993123a9..919ca8d565f 100644 --- a/assets/js/frontend/add-to-cart-variation.js +++ b/assets/js/frontend/add-to-cart-variation.js @@ -352,11 +352,19 @@ } if ( attr_val ) { - // Decode entities and add slashes. + // Decode entities. attr_val = $( '
' ).html( attr_val ).text(); - // Attach. - new_attr_select.find( 'option[value="' + form.addSlashes( attr_val ) + '"]' ).addClass( 'attached ' + variation_active ); + // Attach to matching options by value. This is done to compare + // TEXT values rather than any HTML entities. + new_attr_select.find( 'option' ).each( function( index, el ) { + var option_value = $( this ).val(); + + if ( attr_val === option_value ) { + $( this ).addClass( 'attached ' + variation_active ); + return false; // break. + } + }); } else { // Attach all apart from placeholder. new_attr_select.find( 'option:gt(0)' ).addClass( 'attached ' + variation_active ); @@ -371,8 +379,21 @@ attached_options_count = new_attr_select.find( 'option.attached' ).length; // Check if current selection is in attached options. - if ( selected_attr_val && ( attached_options_count === 0 || new_attr_select.find( 'option.attached.enabled[value="' + form.addSlashes( selected_attr_val ) + '"]' ).length === 0 ) ) { - selected_attr_val_valid = false; + if ( selected_attr_val ) { + if ( 0 === attached_options_count ) { + selected_attr_val_valid = false; + } else { + selected_attr_val_valid = false; + + new_attr_select.find( 'option.attached.enabled' ).each( function( index, el ) { + var option_value = $( this ).val(); + + if ( selected_attr_val === option_value ) { + selected_attr_val_valid = true; + return false; // break. + } + }); + } } // Detach the placeholder if: From 15979d975a53e8a9e6ead571557902f79a2ba8ed Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 8 Jan 2019 16:56:23 +0000 Subject: [PATCH 043/401] phpcs --- includes/shortcodes/class-wc-shortcode-checkout.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/includes/shortcodes/class-wc-shortcode-checkout.php b/includes/shortcodes/class-wc-shortcode-checkout.php index f4646b9e3e1..e91f6ade91b 100644 --- a/includes/shortcodes/class-wc-shortcode-checkout.php +++ b/includes/shortcodes/class-wc-shortcode-checkout.php @@ -86,7 +86,7 @@ class WC_Shortcode_Checkout { try { $order_key = isset( $_GET['key'] ) ? wc_clean( wp_unslash( $_GET['key'] ) ) : ''; // WPCS: input var ok, CSRF ok. $order = wc_get_order( $order_id ); - $hold_stock_minutes = (int) get_option( 'woocommerce_hold_stock_minutes', 0 ); + $hold_stock_minutes = (int) get_option( 'woocommerce_hold_stock_minutes', 0 ); // Order or payment link is invalid. if ( ! $order || $order->get_id() !== $order_id || $order->get_order_key() !== $order_key ) { @@ -177,7 +177,8 @@ class WC_Shortcode_Checkout { } wc_get_template( - 'checkout/form-pay.php', array( + 'checkout/form-pay.php', + array( 'order' => $order, 'available_gateways' => $available_gateways, 'order_button_text' => apply_filters( 'woocommerce_pay_order_button_text', __( 'Pay for order', 'woocommerce' ) ), From 2dbdc9d356243dbb1110a71a062820ec913ffc12 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 9 Jan 2019 11:14:57 +0000 Subject: [PATCH 044/401] Load address from stored customer data for first time, not session. --- includes/class-wc-checkout.php | 61 +++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/includes/class-wc-checkout.php b/includes/class-wc-checkout.php index 42a2d729731..585b42bf80d 100644 --- a/includes/class-wc-checkout.php +++ b/includes/class-wc-checkout.php @@ -36,6 +36,13 @@ class WC_Checkout { */ protected $legacy_posted_data = array(); + /** + * Caches customer object. @see get_value. + * + * @var WC_Customer + */ + private $logged_in_customer = null; + /** * Gets the main WC_Checkout Instance. * @@ -314,13 +321,14 @@ class WC_Checkout { } $fields_prefix = array( - 'shipping' => true, - 'billing' => true, + 'shipping' => true, + 'billing' => true, ); + $shipping_fields = array( - 'shipping_method' => true, - 'shipping_total' => true, - 'shipping_tax' => true, + 'shipping_method' => true, + 'shipping_total' => true, + 'shipping_tax' => true, ); foreach ( $data as $key => $value ) { if ( is_callable( array( $order, "set_{$key}" ) ) ) { @@ -711,7 +719,7 @@ class WC_Checkout { if ( in_array( 'email', $format, true ) && '' !== $data[ $key ] ) { $email_is_valid = is_email( $data[ $key ] ); - $data[ $key ] = sanitize_email( $data[ $key ] ); + $data[ $key ] = sanitize_email( $data[ $key ] ); if ( $validate_fieldset && ! $email_is_valid ) { /* translators: %s: email address */ @@ -1106,29 +1114,52 @@ class WC_Checkout { } /** - * Gets the value either from the posted data, or from the users meta data. + * Gets the value either from POST, or from the customer object. Sets the default values in checkout fields. * - * @param string $input Input key. - * @return string + * @param string $input Name of the input we want to grab data for. e.g. billing_country. + * @return string The default value. */ public function get_value( $input ) { + // If the form was posted, get the posted value. This will only tend to happen when JavaScript is disabled client side. if ( ! empty( $_POST[ $input ] ) ) { // WPCS: input var ok, CSRF OK. return wc_clean( wp_unslash( $_POST[ $input ] ) ); // WPCS: input var ok, CSRF OK. } + // Allow 3rd parties to short circuit the logic and return their own default value. $value = apply_filters( 'woocommerce_checkout_get_value', null, $input ); - if ( null !== $value ) { + if ( ! is_null( $value ) ) { return $value; } - if ( is_callable( array( WC()->customer, "get_$input" ) ) ) { - $value = WC()->customer->{"get_$input"}(); - } elseif ( WC()->customer->meta_exists( $input ) ) { - $value = WC()->customer->get_meta( $input, true ); + /** + * For logged in customers, pull data from their account rather than the session which may contain incomplete data. + * Another reason is that WC sets shipping address to the billing address on the checkout updates unless the + * "ship to another address" box is checked. @see issue #20975. + */ + $customer_object = false; + + if ( is_user_logged_in() ) { + // Load customer object, but keep it cached to avoid reloading it multiple times. + if ( is_null( $this->logged_in_customer ) ) { + $this->logged_in_customer = new WC_Customer( get_current_user_id() ); + } + $customer_object = $this->logged_in_customer; } - $value = $value ? $value : null; // Empty value should return null. + if ( ! $customer_object ) { + $customer_object = WC()->customer; + } + + if ( is_callable( array( $customer_object, "get_$input" ) ) ) { + $value = $customer_object->{"get_$input"}(); + } elseif ( $customer_object->meta_exists( $input ) ) { + $value = $customer_object->get_meta( $input, true ); + } + + if ( '' === $value ) { + $value = null; + } return apply_filters( 'default_checkout_' . $input, $value, $input ); } From 9d381d456bd94b387d5e5fd6b7dd399ffe021c59 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 10 Jan 2019 12:01:27 +0000 Subject: [PATCH 045/401] Remove simplify commerce gateway --- .codeclimate.yml | 1 - .scrutinizer.yml | 1 - Gruntfile.js | 17 +- assets/css/admin.scss | 17 +- includes/admin/class-wc-admin-notices.php | 21 +- .../views/html-notice-simplify-commerce.php | 24 - includes/class-wc-payment-gateways.php | 15 - .../simplify-commerce/assets/images/cards.png | Bin 2787 -> 0 bytes .../simplify-commerce/assets/images/logo.png | Bin 28912 -> 0 bytes .../assets/js/simplify-commerce.js | 118 --- .../assets/js/simplify-commerce.min.js | 1 - ...ss-wc-addons-gateway-simplify-commerce.php | 519 ------------ .../class-wc-gateway-simplify-commerce.php | 777 ------------------ .../simplify-commerce/includes/Simplify.php | 88 -- .../includes/Simplify/AccessToken.php | 127 --- .../includes/Simplify/Authentication.php | 74 -- .../includes/Simplify/Authorization.php | 142 ---- .../includes/Simplify/CardToken.php | 94 --- .../includes/Simplify/Chargeback.php | 86 -- .../includes/Simplify/Constants.php | 58 -- .../includes/Simplify/Coupon.php | 151 ---- .../includes/Simplify/Customer.php | 184 ----- .../includes/Simplify/Deposit.php | 86 -- .../includes/Simplify/Event.php | 68 -- .../includes/Simplify/Exceptions.php | 294 ------- .../includes/Simplify/FraudCheck.php | 122 --- .../includes/Simplify/Http.php | 411 --------- .../includes/Simplify/Invoice.php | 228 ----- .../includes/Simplify/InvoiceItem.php | 128 --- .../includes/Simplify/Object.php | 90 -- .../includes/Simplify/Payment.php | 145 ---- .../includes/Simplify/PaymentsApi.php | 358 -------- .../includes/Simplify/Plan.php | 151 ---- .../includes/Simplify/Refund.php | 112 --- .../includes/Simplify/ResourceList.php | 48 -- .../includes/Simplify/Subscription.php | 164 ---- .../includes/Simplify/Tax.php | 124 --- .../includes/Simplify/TransactionReview.php | 141 ---- .../includes/Simplify/Webhook.php | 142 ---- phpcs.xml | 1 - phpunit.xml | 1 - tests/bin/phpcs.sh | 2 +- 42 files changed, 14 insertions(+), 5317 deletions(-) delete mode 100644 includes/admin/views/html-notice-simplify-commerce.php delete mode 100644 includes/gateways/simplify-commerce/assets/images/cards.png delete mode 100644 includes/gateways/simplify-commerce/assets/images/logo.png delete mode 100644 includes/gateways/simplify-commerce/assets/js/simplify-commerce.js delete mode 100644 includes/gateways/simplify-commerce/assets/js/simplify-commerce.min.js delete mode 100644 includes/gateways/simplify-commerce/class-wc-addons-gateway-simplify-commerce.php delete mode 100644 includes/gateways/simplify-commerce/class-wc-gateway-simplify-commerce.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/AccessToken.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Authentication.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Authorization.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/CardToken.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Chargeback.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Constants.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Coupon.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Customer.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Deposit.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Event.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Exceptions.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/FraudCheck.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Http.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Invoice.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/InvoiceItem.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Object.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Payment.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/PaymentsApi.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Plan.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Refund.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/ResourceList.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Subscription.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Tax.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/TransactionReview.php delete mode 100644 includes/gateways/simplify-commerce/includes/Simplify/Webhook.php diff --git a/.codeclimate.yml b/.codeclimate.yml index 9a63874430e..dcf25bd147f 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -24,7 +24,6 @@ exclude_paths: - "includes/api/legacy/" - "includes/libraries/" - "includes/updates/" -- "includes/gateways/simplify-commerce/" - "includes/shipping/legacy-*" - "includes/wc-deprecated-functions.php" - "assets/js/accounting/" diff --git a/.scrutinizer.yml b/.scrutinizer.yml index ddbd18e1f4d..988a1febcbe 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -27,7 +27,6 @@ filter: - sample-data/ - i18n/ - includes/api/legacy/ - - includes/gateways/simplify-commerce/includes/ - includes/legacy/ - includes/libraries/ - includes/shipping/legacy-* diff --git a/Gruntfile.js b/Gruntfile.js index df897dae4d8..6ec94817481 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -298,15 +298,14 @@ module.exports = function( grunt ) { }, dist: { src: [ - '**/*.php', // Include all files - '!apigen/**', // Exclude apigen/ - '!includes/api/legacy/**', // Exclude legacy REST API - '!includes/gateways/simplify-commerce/includes/Simplify/**', // Exclude simplify commerce SDK - '!includes/libraries/**', // Exclude libraries/ - '!node_modules/**', // Exclude node_modules/ - '!tests/cli/**', // Exclude tests/cli/ - '!tmp/**', // Exclude tmp/ - '!vendor/**' // Exclude vendor/ + '**/*.php', // Include all php files. + '!apigen/**', + '!includes/api/legacy/**', + '!includes/libraries/**', + '!node_modules/**', + '!tests/cli/**', + '!tmp/**', + '!vendor/**' ] } }, diff --git a/assets/css/admin.scss b/assets/css/admin.scss index e72585b7aa9..5d104e39bf0 100644 --- a/assets/css/admin.scss +++ b/assets/css/admin.scss @@ -583,17 +583,6 @@ color: inherit; } - .simplify-commerce-banner { - overflow: hidden; - - img { - float: right; - padding: 15px 0; - margin-left: 1em; - width: 200px; - } - } - /** * Help Tip */ @@ -3686,15 +3675,15 @@ padding: 0 15px 10px 0; } } - + .wc-shipping-zone-settings { - + td.forminp { input, textarea { width: 448px; padding: 6px 11px; } - + .select2-search input { padding: 6px; } diff --git a/includes/admin/class-wc-admin-notices.php b/includes/admin/class-wc-admin-notices.php index b190b1caede..8bec562d615 100644 --- a/includes/admin/class-wc-admin-notices.php +++ b/includes/admin/class-wc-admin-notices.php @@ -31,7 +31,6 @@ class WC_Admin_Notices { 'template_files' => 'template_file_check_notice', 'legacy_shipping' => 'legacy_shipping_notice', 'no_shipping_methods' => 'no_shipping_methods_notice', - 'simplify_commerce' => 'simplify_commerce_notice', 'regenerating_thumbnails' => 'regenerating_thumbnails_notice', 'no_secure_connection' => 'secure_connection_notice', 'wootenberg' => 'wootenberg_feature_plugin_notice', @@ -81,17 +80,9 @@ class WC_Admin_Notices { * Reset notices for themes when switched or a new version of WC is installed. */ public static function reset_admin_notices() { - $simplify_options = get_option( 'woocommerce_simplify_commerce_settings', array() ); - $location = wc_get_base_location(); - - if ( ! class_exists( 'WC_Gateway_Simplify_Commerce_Loader' ) && ! empty( $simplify_options['enabled'] ) && 'yes' === $simplify_options['enabled'] && in_array( $location['country'], apply_filters( 'woocommerce_gateway_simplify_commerce_supported_countries', array( 'US', 'IE' ) ), true ) ) { - self::add_notice( 'simplify_commerce' ); - } - if ( ! self::is_ssl() ) { self::add_notice( 'no_secure_connection' ); } - self::add_wootenberg_feature_plugin_notice(); self::add_notice( 'template_files' ); } @@ -329,18 +320,10 @@ class WC_Admin_Notices { } /** - * Simplify Commerce is being removed from core. + * Simplify Commerce is no longer in core. */ public static function simplify_commerce_notice() { - $location = wc_get_base_location(); - - if ( class_exists( 'WC_Gateway_Simplify_Commerce_Loader' ) || ! in_array( $location['country'], apply_filters( 'woocommerce_gateway_simplify_commerce_supported_countries', array( 'US', 'IE' ) ), true ) ) { - self::remove_notice( 'simplify_commerce' ); - return; - } - if ( empty( $_GET['action'] ) ) { // WPCS: input var ok, CSRF ok. - include dirname( __FILE__ ) . '/views/html-notice-simplify-commerce.php'; - } + wc_deprecated_function( 'WC_Admin_Notices::simplify_commerce_notice', '3.6.0' ); } /** diff --git a/includes/admin/views/html-notice-simplify-commerce.php b/includes/admin/views/html-notice-simplify-commerce.php deleted file mode 100644 index 8664b6b270a..00000000000 --- a/includes/admin/views/html-notice-simplify-commerce.php +++ /dev/null @@ -1,24 +0,0 @@ - -
- - -

The Simplify Commerce payment gateway is deprecated – Please install our new free Simplify Commerce plugin from WordPress.org. Simplify Commerce will be removed from WooCommerce core in a future update.', 'woocommerce' ); ?>

- -

-
diff --git a/includes/class-wc-payment-gateways.php b/includes/class-wc-payment-gateways.php index 432d1a4b876..3f347b6a3c8 100644 --- a/includes/class-wc-payment-gateways.php +++ b/includes/class-wc-payment-gateways.php @@ -81,21 +81,6 @@ class WC_Payment_Gateways { 'WC_Gateway_Paypal', ); - /** - * Simplify Commerce is @deprecated in 2.6.0. Only load when enabled. - */ - if ( ! class_exists( 'WC_Gateway_Simplify_Commerce_Loader' ) && in_array( WC()->countries->get_base_country(), apply_filters( 'woocommerce_gateway_simplify_commerce_supported_countries', array( 'US', 'IE' ) ), true ) ) { - $simplify_options = get_option( 'woocommerce_simplify_commerce_settings', array() ); - - if ( ! empty( $simplify_options['enabled'] ) && 'yes' === $simplify_options['enabled'] ) { - if ( function_exists( 'wcs_create_renewal_order' ) ) { - $load_gateways[] = 'WC_Addons_Gateway_Simplify_Commerce'; - } else { - $load_gateways[] = 'WC_Gateway_Simplify_Commerce'; - } - } - } - // Filter. $load_gateways = apply_filters( 'woocommerce_payment_gateways', $load_gateways ); diff --git a/includes/gateways/simplify-commerce/assets/images/cards.png b/includes/gateways/simplify-commerce/assets/images/cards.png deleted file mode 100644 index c4091a40060dcad0613782c68c77e1005aa8831c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2787 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'; - } - - ccForm.prepend( '
    ' + errorList + '
' ); - } - - } else { - - // Insert the token into the form so it gets submitted to the server - ccForm.append( '' ); - $form.submit(); - } - } - - $( function () { - - $( document.body ).on( 'checkout_error', function () { - $( '.simplify-token' ).remove(); - }); - - /* Checkout Form */ - $( 'form.checkout' ).on( 'checkout_place_order_simplify_commerce', function () { - return simplifyFormHandler(); - }); - - /* Pay Page Form */ - $( 'form#order_review' ).on( 'submit', function () { - return simplifyFormHandler(); - }); - - /* Pay Page Form */ - $( 'form#add_payment_method' ).on( 'submit', function () { - return simplifyFormHandler(); - }); - - /* Both Forms */ - $( 'form.checkout, form#order_review, form#add_payment_method' ).on( 'change', '#wc-simplify_commerce-cc-form input', function() { - $( '.simplify-token' ).remove(); - }); - - }); - -}( jQuery ) ); diff --git a/includes/gateways/simplify-commerce/assets/js/simplify-commerce.min.js b/includes/gateways/simplify-commerce/assets/js/simplify-commerce.min.js deleted file mode 100644 index 9491d4a152f..00000000000 --- a/includes/gateways/simplify-commerce/assets/js/simplify-commerce.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){function r(){var r=e("form.checkout, form#order_review, form#add_payment_method");if((e("#payment_method_simplify_commerce").is(":checked")&&"new"===e('input[name="wc-simplify_commerce-payment-token"]:checked').val()||"1"===e("#woocommerce_add_payment_method").val())&&0===e("input.simplify-token").length){r.block({message:null,overlayCSS:{background:"#fff",opacity:.6}});var i=e("#simplify_commerce-card-number").val(),m=e("#simplify_commerce-card-cvc").val(),c=e.payment.cardExpiryVal(e("#simplify_commerce-card-expiry").val()),n=r.find("#billing_address_1").val()||"",a=r.find("#billing_address_2").val()||"",t=r.find("#billing_country").val()||"",d=r.find("#billing_state").val()||"",l=r.find("#billing_city").val()||"",f=r.find("#billing_postcode").val()||"";return f=f.replace(/-/g,""),i=i.replace(/\s/g,""),SimplifyCommerce.generateToken({key:Simplify_commerce_params.key,card:{number:i,cvc:m,expMonth:c.month,expYear:c.year-2e3,addressLine1:n,addressLine2:a,addressCountry:t,addressState:d,addressZip:f,addressCity:l}},o),!1}return!0}function o(r){var o=e("form.checkout, form#order_review, form#add_payment_method"),i=e("#wc-simplify_commerce-cc-form");if(r.error){if(e(".woocommerce-error, .simplify-token",i).remove(),o.unblock(),"validation"===r.error.code){for(var m=r.error.fieldErrors,c=m.length,n="",a=0;a"+Simplify_commerce_params[m[a].field]+" "+Simplify_commerce_params.is_invalid+" - "+m[a].message+".";i.prepend('
    '+n+"
")}}else i.append(''),o.submit()}e(function(){e(document.body).on("checkout_error",function(){e(".simplify-token").remove()}),e("form.checkout").on("checkout_place_order_simplify_commerce",function(){return r()}),e("form#order_review").on("submit",function(){return r()}),e("form#add_payment_method").on("submit",function(){return r()}),e("form.checkout, form#order_review, form#add_payment_method").on("change","#wc-simplify_commerce-cc-form input",function(){e(".simplify-token").remove()})})}(jQuery); \ No newline at end of file diff --git a/includes/gateways/simplify-commerce/class-wc-addons-gateway-simplify-commerce.php b/includes/gateways/simplify-commerce/class-wc-addons-gateway-simplify-commerce.php deleted file mode 100644 index c7bef506404..00000000000 --- a/includes/gateways/simplify-commerce/class-wc-addons-gateway-simplify-commerce.php +++ /dev/null @@ -1,519 +0,0 @@ -id, array( $this, 'scheduled_subscription_payment' ), 10, 2 ); - add_action( 'woocommerce_subscription_failing_payment_method_updated_' . $this->id, array( $this, 'update_failing_payment_method' ), 10, 2 ); - - add_action( 'wcs_resubscribe_order_created', array( $this, 'delete_resubscribe_meta' ), 10 ); - - // Allow store managers to manually set Simplify as the payment method on a subscription - add_filter( 'woocommerce_subscription_payment_meta', array( $this, 'add_subscription_payment_meta' ), 10, 2 ); - add_filter( 'woocommerce_subscription_validate_payment_meta', array( $this, 'validate_subscription_payment_meta' ), 10, 2 ); - } - - if ( class_exists( 'WC_Pre_Orders_Order' ) ) { - add_action( 'wc_pre_orders_process_pre_order_completion_payment_' . $this->id, array( $this, 'process_pre_order_release_payment' ) ); - } - - add_filter( 'woocommerce_simplify_commerce_hosted_args', array( $this, 'hosted_payment_args' ), 10, 2 ); - add_action( 'woocommerce_api_wc_addons_gateway_simplify_commerce', array( $this, 'return_handler' ) ); - } - - /** - * Hosted payment args. - * - * @param array $args - * @param int $order_id - * @return array - */ - public function hosted_payment_args( $args, $order_id ) { - if ( ( $this->order_contains_subscription( $order_id ) ) || ( $this->order_contains_pre_order( $order_id ) && WC_Pre_Orders_Order::order_requires_payment_tokenization( $order_id ) ) ) { - $args['operation'] = 'create.token'; - } - - $args['redirect-url'] = WC()->api_request_url( 'WC_Addons_Gateway_Simplify_Commerce' ); - - return $args; - } - - /** - * Check if order contains subscriptions. - * - * @param int $order_id - * @return bool - */ - protected function order_contains_subscription( $order_id ) { - return function_exists( 'wcs_order_contains_subscription' ) && ( wcs_order_contains_subscription( $order_id ) || wcs_order_contains_renewal( $order_id ) ); - } - - /** - * Check if order contains pre-orders. - * - * @param int $order_id - * @return bool - */ - protected function order_contains_pre_order( $order_id ) { - return class_exists( 'WC_Pre_Orders_Order' ) && WC_Pre_Orders_Order::order_contains_pre_order( $order_id ); - } - - /** - * Process the subscription. - * - * @param WC_Order $order - * @param string $cart_token - * @uses Simplify_ApiException - * @uses Simplify_BadRequestException - * @return array - * @throws Exception - */ - protected function process_subscription( $order, $cart_token = '' ) { - try { - if ( empty( $cart_token ) ) { - $error_msg = __( 'Please make sure your card details have been entered correctly and that your browser supports JavaScript.', 'woocommerce' ); - - if ( 'yes' == $this->sandbox ) { - $error_msg .= ' ' . __( 'Developers: Please make sure that you are including jQuery and there are no JavaScript errors on the page.', 'woocommerce' ); - } - - throw new Simplify_ApiException( $error_msg ); - } - - // Create customer - $customer = Simplify_Customer::createCustomer( - array( - 'token' => $cart_token, - 'email' => $order->get_billing_email(), - 'name' => trim( $order->get_formatted_billing_full_name() ), - 'reference' => $order->get_id(), - ) - ); - - if ( is_object( $customer ) && '' != $customer->id ) { - $this->save_subscription_meta( $order->get_id(), $customer->id ); - } else { - $error_msg = __( 'Error creating user in Simplify Commerce.', 'woocommerce' ); - - throw new Simplify_ApiException( $error_msg ); - } - - $payment_response = $this->process_subscription_payment( $order, $order->get_total() ); - - if ( is_wp_error( $payment_response ) ) { - throw new Exception( $payment_response->get_error_message() ); - } else { - // Remove cart - WC()->cart->empty_cart(); - - // Return thank you page redirect - return array( - 'result' => 'success', - 'redirect' => $this->get_return_url( $order ), - ); - } - } catch ( Simplify_ApiException $e ) { - if ( $e instanceof Simplify_BadRequestException && $e->hasFieldErrors() && $e->getFieldErrors() ) { - foreach ( $e->getFieldErrors() as $error ) { - wc_add_notice( $error->getFieldName() . ': "' . $error->getMessage() . '" (' . $error->getErrorCode() . ')', 'error' ); - } - } else { - wc_add_notice( $e->getMessage(), 'error' ); - } - - return array( - 'result' => 'fail', - 'redirect' => '', - ); - } - } - - /** - * Store the customer and card IDs on the order and subscriptions in the order. - * - * @param int $order_id - * @param string $customer_id - */ - protected function save_subscription_meta( $order_id, $customer_id ) { - - $customer_id = wc_clean( $customer_id ); - - update_post_meta( $order_id, '_simplify_customer_id', $customer_id ); - - // Also store it on the subscriptions being purchased in the order - foreach ( wcs_get_subscriptions_for_order( $order_id ) as $subscription ) { - update_post_meta( $subscription->id, '_simplify_customer_id', $customer_id ); - } - } - - /** - * Process the pre-order. - * - * @param WC_Order $order - * @param string $cart_token - * @uses Simplify_ApiException - * @uses Simplify_BadRequestException - * @return array - */ - protected function process_pre_order( $order, $cart_token = '' ) { - if ( WC_Pre_Orders_Order::order_requires_payment_tokenization( $order->get_id() ) ) { - - try { - if ( $order->get_total() * 100 < 50 ) { - $error_msg = __( 'Sorry, the minimum allowed order total is 0.50 to use this payment method.', 'woocommerce' ); - - throw new Simplify_ApiException( $error_msg ); - } - - if ( empty( $cart_token ) ) { - $error_msg = __( 'Please make sure your card details have been entered correctly and that your browser supports JavaScript.', 'woocommerce' ); - - if ( 'yes' == $this->sandbox ) { - $error_msg .= ' ' . __( 'Developers: Please make sure that you are including jQuery and there are no JavaScript errors on the page.', 'woocommerce' ); - } - - throw new Simplify_ApiException( $error_msg ); - } - - // Create customer - $customer = Simplify_Customer::createCustomer( - array( - 'token' => $cart_token, - 'email' => $order->get_billing_email(), - 'name' => trim( $order->get_formatted_billing_full_name() ), - 'reference' => $order->get_id(), - ) - ); - - if ( is_object( $customer ) && '' != $customer->id ) { - $customer_id = wc_clean( $customer->id ); - - // Store the customer ID in the order - update_post_meta( $order->get_id(), '_simplify_customer_id', $customer_id ); - } else { - $error_msg = __( 'Error creating user in Simplify Commerce.', 'woocommerce' ); - - throw new Simplify_ApiException( $error_msg ); - } - - // Remove cart - WC()->cart->empty_cart(); - - // Is pre ordered! - WC_Pre_Orders_Order::mark_order_as_pre_ordered( $order ); - - // Return thank you page redirect - return array( - 'result' => 'success', - 'redirect' => $this->get_return_url( $order ), - ); - - } catch ( Simplify_ApiException $e ) { - if ( $e instanceof Simplify_BadRequestException && $e->hasFieldErrors() && $e->getFieldErrors() ) { - foreach ( $e->getFieldErrors() as $error ) { - wc_add_notice( $error->getFieldName() . ': "' . $error->getMessage() . '" (' . $error->getErrorCode() . ')', 'error' ); - } - } else { - wc_add_notice( $e->getMessage(), 'error' ); - } - - return array( - 'result' => 'fail', - 'redirect' => '', - ); - } - } else { - return parent::process_standard_payments( $order, $cart_token ); - } - } - - /** - * Process the payment. - * - * @param int $order_id - * @return array - */ - public function process_payment( $order_id ) { - $cart_token = isset( $_POST['simplify_token'] ) ? wc_clean( $_POST['simplify_token'] ) : ''; - $order = wc_get_order( $order_id ); - - // Processing subscription - if ( 'standard' == $this->mode && ( $this->order_contains_subscription( $order->get_id() ) || ( function_exists( 'wcs_is_subscription' ) && wcs_is_subscription( $order_id ) ) ) ) { - return $this->process_subscription( $order, $cart_token ); - - } elseif ( 'standard' == $this->mode && $this->order_contains_pre_order( $order->get_id() ) ) { - // Processing pre-order. - return $this->process_pre_order( $order, $cart_token ); - } else { - // Processing regular product. - return parent::process_payment( $order_id ); - } - } - - /** - * process_subscription_payment function. - * - * @param WC_order $order - * @param int $amount (default: 0) - * @uses Simplify_BadRequestException - * @return bool|WP_Error - */ - public function process_subscription_payment( $order, $amount = 0 ) { - if ( 0 == $amount ) { - // Payment complete - $order->payment_complete(); - - return true; - } - - if ( $amount * 100 < 50 ) { - return new WP_Error( 'simplify_error', __( 'Sorry, the minimum allowed order total is 0.50 to use this payment method.', 'woocommerce' ) ); - } - - $customer_id = get_post_meta( $order->get_id(), '_simplify_customer_id', true ); - - if ( ! $customer_id ) { - return new WP_Error( 'simplify_error', __( 'Customer not found.', 'woocommerce' ) ); - } - - try { - // Charge the customer - $payment = Simplify_Payment::createPayment( - array( - 'amount' => $amount * 100, // In cents. - 'customer' => $customer_id, - 'description' => sprintf( __( '%1$s - Order #%2$s', 'woocommerce' ), esc_html( get_bloginfo( 'name', 'display' ) ), $order->get_order_number() ), - 'currency' => strtoupper( get_woocommerce_currency() ), - 'reference' => $order->get_id(), - ) - ); - - } catch ( Exception $e ) { - - $error_message = $e->getMessage(); - - if ( $e instanceof Simplify_BadRequestException && $e->hasFieldErrors() && $e->getFieldErrors() ) { - $error_message = ''; - foreach ( $e->getFieldErrors() as $error ) { - $error_message .= ' ' . $error->getFieldName() . ': "' . $error->getMessage() . '" (' . $error->getErrorCode() . ')'; - } - } - - $order->add_order_note( sprintf( __( 'Simplify payment error: %s.', 'woocommerce' ), $error_message ) ); - - return new WP_Error( 'simplify_payment_declined', $e->getMessage(), array( 'status' => $e->getCode() ) ); - } - - if ( 'APPROVED' == $payment->paymentStatus ) { - // Payment complete - $order->payment_complete( $payment->id ); - - // Add order note - $order->add_order_note( sprintf( __( 'Simplify payment approved (ID: %1$s, Auth Code: %2$s)', 'woocommerce' ), $payment->id, $payment->authCode ) ); - - return true; - } else { - $order->add_order_note( __( 'Simplify payment declined', 'woocommerce' ) ); - - return new WP_Error( 'simplify_payment_declined', __( 'Payment was declined - please try another card.', 'woocommerce' ) ); - } - } - - /** - * scheduled_subscription_payment function. - * - * @param float $amount_to_charge The amount to charge. - * @param WC_Order $renewal_order A WC_Order object created to record the renewal payment. - */ - public function scheduled_subscription_payment( $amount_to_charge, $renewal_order ) { - $result = $this->process_subscription_payment( $renewal_order, $amount_to_charge ); - - if ( is_wp_error( $result ) ) { - $renewal_order->update_status( 'failed', sprintf( __( 'Simplify Transaction Failed (%s)', 'woocommerce' ), $result->get_error_message() ) ); - } - } - - /** - * Update the customer_id for a subscription after using Simplify to complete a payment to make up for. - * an automatic renewal payment which previously failed. - * - * @param WC_Subscription $subscription The subscription for which the failing payment method relates. - * @param WC_Order $renewal_order The order which recorded the successful payment (to make up for the failed automatic payment). - */ - public function update_failing_payment_method( $subscription, $renewal_order ) { - update_post_meta( $subscription->id, '_simplify_customer_id', get_post_meta( $renewal_order->get_id(), '_simplify_customer_id', true ) ); - } - - /** - * Include the payment meta data required to process automatic recurring payments so that store managers can. - * manually set up automatic recurring payments for a customer via the Edit Subscription screen in Subscriptions v2.0+. - * - * @since 2.4 - * @param array $payment_meta associative array of meta data required for automatic payments - * @param WC_Subscription $subscription An instance of a subscription object - * @return array - */ - public function add_subscription_payment_meta( $payment_meta, $subscription ) { - - $payment_meta[ $this->id ] = array( - 'post_meta' => array( - '_simplify_customer_id' => array( - 'value' => get_post_meta( $subscription->id, '_simplify_customer_id', true ), - 'label' => 'Simplify Customer ID', - ), - ), - ); - - return $payment_meta; - } - - /** - * Validate the payment meta data required to process automatic recurring payments so that store managers can. - * manually set up automatic recurring payments for a customer via the Edit Subscription screen in Subscriptions 2.0+. - * - * @since 2.4 - * @param string $payment_method_id The ID of the payment method to validate - * @param array $payment_meta associative array of meta data required for automatic payments - * @throws Exception - */ - public function validate_subscription_payment_meta( $payment_method_id, $payment_meta ) { - if ( $this->id === $payment_method_id ) { - if ( ! isset( $payment_meta['post_meta']['_simplify_customer_id']['value'] ) || empty( $payment_meta['post_meta']['_simplify_customer_id']['value'] ) ) { - throw new Exception( 'A "_simplify_customer_id" value is required.' ); - } - } - } - - /** - * Don't transfer customer meta to resubscribe orders. - * - * @access public - * @param int $resubscribe_order The order created for the customer to resubscribe to the old expired/cancelled subscription - * @return void - */ - public function delete_resubscribe_meta( $resubscribe_order ) { - delete_post_meta( $resubscribe_order->get_id(), '_simplify_customer_id' ); - } - - /** - * Process a pre-order payment when the pre-order is released. - * - * @param WC_Order $order - * @return WP_Error|null - */ - public function process_pre_order_release_payment( $order ) { - - try { - $order_items = $order->get_items(); - $order_item = array_shift( $order_items ); - /* translators: 1: site name 2: product name 3: order number */ - $pre_order_name = sprintf( - __( '%1$s - Pre-order for "%2$s" (Order #%3$s)', 'woocommerce' ), - esc_html( get_bloginfo( 'name', 'display' ) ), - $order_item['name'], - $order->get_order_number() - ); - - $customer_id = get_post_meta( $order->get_id(), '_simplify_customer_id', true ); - - if ( ! $customer_id ) { - return new WP_Error( 'simplify_error', __( 'Customer not found.', 'woocommerce' ) ); - } - - // Charge the customer - $payment = Simplify_Payment::createPayment( - array( - 'amount' => $order->get_total() * 100, // In cents. - 'customer' => $customer_id, - 'description' => trim( substr( $pre_order_name, 0, 1024 ) ), - 'currency' => strtoupper( get_woocommerce_currency() ), - 'reference' => $order->get_id(), - ) - ); - - if ( 'APPROVED' == $payment->paymentStatus ) { - // Payment complete - $order->payment_complete( $payment->id ); - - // Add order note - $order->add_order_note( sprintf( __( 'Simplify payment approved (ID: %1$s, Auth Code: %2$s)', 'woocommerce' ), $payment->id, $payment->authCode ) ); - } else { - return new WP_Error( 'simplify_payment_declined', __( 'Payment was declined - the customer need to try another card.', 'woocommerce' ) ); - } - } catch ( Exception $e ) { - $order_note = sprintf( __( 'Simplify Transaction Failed (%s)', 'woocommerce' ), $e->getMessage() ); - - // Mark order as failed if not already set, - // otherwise, make sure we add the order note so we can detect when someone fails to check out multiple times - if ( 'failed' != $order->get_status() ) { - $order->update_status( 'failed', $order_note ); - } else { - $order->add_order_note( $order_note ); - } - } - } - - /** - * Return handler for Hosted Payments. - */ - public function return_handler() { - if ( ! isset( $_REQUEST['cardToken'] ) ) { - parent::return_handler(); - } - - @ob_clean(); - header( 'HTTP/1.1 200 OK' ); - - $redirect_url = wc_get_page_permalink( 'cart' ); - - if ( isset( $_REQUEST['reference'] ) && isset( $_REQUEST['amount'] ) ) { - $cart_token = $_REQUEST['cardToken']; - $amount = absint( $_REQUEST['amount'] ); - $order_id = absint( $_REQUEST['reference'] ); - $order = wc_get_order( $order_id ); - $order_total = absint( $order->get_total() * 100 ); - - if ( $amount === $order_total ) { - if ( $this->order_contains_subscription( $order->get_id() ) ) { - $response = $this->process_subscription( $order, $cart_token ); - } elseif ( $this->order_contains_pre_order( $order->get_id() ) ) { - $response = $this->process_pre_order( $order, $cart_token ); - } else { - $response = parent::process_standard_payments( $order, $cart_token ); - } - - if ( 'success' == $response['result'] ) { - $redirect_url = $response['redirect']; - } else { - $order->update_status( 'failed', __( 'Payment was declined by Simplify Commerce.', 'woocommerce' ) ); - } - - wp_redirect( $redirect_url ); - exit(); - } - } - - wp_redirect( $redirect_url ); - exit(); - } -} diff --git a/includes/gateways/simplify-commerce/class-wc-gateway-simplify-commerce.php b/includes/gateways/simplify-commerce/class-wc-gateway-simplify-commerce.php deleted file mode 100644 index 0dfbd290fa7..00000000000 --- a/includes/gateways/simplify-commerce/class-wc-gateway-simplify-commerce.php +++ /dev/null @@ -1,777 +0,0 @@ -id = 'simplify_commerce'; - $this->method_title = __( 'Simplify Commerce', 'woocommerce' ); - $this->method_description = __( 'Take payments via Simplify Commerce - uses simplify.js to create card tokens and the Simplify Commerce SDK. Requires SSL when sandbox is disabled.', 'woocommerce' ); - $this->new_method_label = __( 'Use a new card', 'woocommerce' ); - $this->has_fields = true; - $this->supports = array( - 'subscriptions', - 'products', - 'subscription_cancellation', - 'subscription_reactivation', - 'subscription_suspension', - 'subscription_amount_changes', - 'subscription_payment_method_change', // Subscriptions 1.n compatibility - 'subscription_payment_method_change_customer', - 'subscription_payment_method_change_admin', - 'subscription_date_changes', - 'multiple_subscriptions', - 'default_credit_card_form', - 'tokenization', - 'refunds', - 'pre-orders', - ); - $this->view_transaction_url = 'https://www.simplify.com/commerce/app#/payment/%s'; - - // Load the form fields - $this->init_form_fields(); - - // Load the settings. - $this->init_settings(); - - // Get setting values - $this->title = $this->get_option( 'title' ); - $this->description = $this->get_option( 'description' ); - $this->enabled = $this->get_option( 'enabled' ); - $this->mode = $this->get_option( 'mode', 'standard' ); - $this->modal_color = $this->get_option( 'modal_color', '#a46497' ); - $this->sandbox = $this->get_option( 'sandbox' ); - $this->public_key = ( 'no' === $this->sandbox ) ? $this->get_option( 'public_key' ) : $this->get_option( 'sandbox_public_key' ); - $this->private_key = ( 'no' === $this->sandbox ) ? $this->get_option( 'private_key' ) : $this->get_option( 'sandbox_private_key' ); - - $this->init_simplify_sdk(); - - // Hooks - add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) ); - add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) ); - add_action( 'woocommerce_receipt_' . $this->id, array( $this, 'receipt_page' ) ); - add_action( 'woocommerce_api_wc_gateway_simplify_commerce', array( $this, 'return_handler' ) ); - } - - /** - * Init Simplify SDK. - */ - protected function init_simplify_sdk() { - // Include lib - require_once dirname( __FILE__ ) . '/includes/Simplify.php'; - - Simplify::$publicKey = $this->public_key; - Simplify::$privateKey = $this->private_key; - Simplify::$userAgent = 'WooCommerce/' . WC()->version; - } - - /** - * Admin Panel Options. - * - Options for bits like 'title' and availability on a country-by-country basis. - */ - public function admin_options() { - ?> -

- - public_key ) ) : ?> -
- -

-

- -

- -
- -

- - - checks(); ?> - - - generate_settings_html(); ?> - -
- enabled ) { - return; - } - - if ( version_compare( phpversion(), '5.3', '<' ) ) { - - // PHP Version - echo '

' . sprintf( __( 'Simplify Commerce Error: Simplify commerce requires PHP 5.3 and above. You are using version %s.', 'woocommerce' ), phpversion() ) . '

'; - - } elseif ( ! $this->public_key || ! $this->private_key ) { - - // Check required fields - echo '

' . __( 'Simplify Commerce Error: Please enter your public and private keys', 'woocommerce' ) . '

'; - - } elseif ( 'standard' == $this->mode && ! wc_checkout_is_https() ) { - - // Show message when using standard mode and no SSL on the checkout page - echo '

' . sprintf( __( 'Simplify Commerce is enabled, but the force SSL option is disabled; your checkout may not be secure! Please enable SSL and ensure your server has a valid SSL certificate - Simplify Commerce will only work in sandbox mode.', 'woocommerce' ), admin_url( 'admin.php?page=wc-settings&tab=checkout' ) ) . '

'; - - } - } - - /** - * Check if this gateway is enabled. - * - * @return bool - */ - public function is_available() { - if ( 'yes' !== $this->enabled ) { - return false; - } - - if ( 'standard' === $this->mode && 'yes' !== $this->sandbox && ! wc_checkout_is_https() ) { - return false; - } - - if ( ! $this->public_key || ! $this->private_key ) { - return false; - } - - return true; - } - - /** - * Initialise Gateway Settings Form Fields. - */ - public function init_form_fields() { - $this->form_fields = array( - 'enabled' => array( - 'title' => __( 'Enable/Disable', 'woocommerce' ), - 'label' => __( 'Enable Simplify Commerce', 'woocommerce' ), - 'type' => 'checkbox', - 'description' => '', - 'default' => 'no', - ), - 'title' => array( - 'title' => __( 'Title', 'woocommerce' ), - 'type' => 'text', - 'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ), - 'default' => __( 'Credit card', 'woocommerce' ), - 'desc_tip' => true, - ), - 'description' => array( - 'title' => __( 'Description', 'woocommerce' ), - 'type' => 'text', - 'description' => __( 'This controls the description which the user sees during checkout.', 'woocommerce' ), - 'default' => 'Pay with your credit card via Simplify Commerce by MasterCard.', - 'desc_tip' => true, - ), - 'mode' => array( - 'title' => __( 'Payment mode', 'woocommerce' ), - 'label' => __( 'Enable Hosted Payments', 'woocommerce' ), - 'type' => 'select', - 'description' => sprintf( __( 'Standard will display the credit card fields on your store (SSL required). %1$s Hosted Payments will display a Simplify Commerce modal dialog on your store (if SSL) or will redirect the customer to Simplify Commerce hosted page (if not SSL). %1$s Note: Hosted Payments need a new API Key pair with the hosted payments flag selected. %2$sFor more details check the Simplify Commerce docs%3$s.', 'woocommerce' ), '
', '', '' ), - 'default' => 'standard', - 'options' => array( - 'standard' => __( 'Standard', 'woocommerce' ), - 'hosted' => __( 'Hosted Payments', 'woocommerce' ), - ), - ), - 'modal_color' => array( - 'title' => __( 'Modal color', 'woocommerce' ), - 'type' => 'color', - 'description' => __( 'Set the color of the buttons and titles on the modal dialog.', 'woocommerce' ), - 'default' => '#a46497', - 'desc_tip' => true, - ), - 'sandbox' => array( - 'title' => __( 'Sandbox', 'woocommerce' ), - 'label' => __( 'Enable sandbox mode', 'woocommerce' ), - 'type' => 'checkbox', - 'description' => __( 'Place the payment gateway in sandbox mode using sandbox API keys (real payments will not be taken).', 'woocommerce' ), - 'default' => 'yes', - ), - 'sandbox_public_key' => array( - 'title' => __( 'Sandbox public key', 'woocommerce' ), - 'type' => 'text', - 'description' => __( 'Get your API keys from your Simplify account: Settings > API Keys.', 'woocommerce' ), - 'default' => '', - 'desc_tip' => true, - ), - 'sandbox_private_key' => array( - 'title' => __( 'Sandbox private key', 'woocommerce' ), - 'type' => 'text', - 'description' => __( 'Get your API keys from your Simplify account: Settings > API Keys.', 'woocommerce' ), - 'default' => '', - 'desc_tip' => true, - ), - 'public_key' => array( - 'title' => __( 'Public key', 'woocommerce' ), - 'type' => 'text', - 'description' => __( 'Get your API keys from your Simplify account: Settings > API Keys.', 'woocommerce' ), - 'default' => '', - 'desc_tip' => true, - ), - 'private_key' => array( - 'title' => __( 'Private key', 'woocommerce' ), - 'type' => 'text', - 'description' => __( 'Get your API keys from your Simplify account: Settings > API Keys.', 'woocommerce' ), - 'default' => '', - 'desc_tip' => true, - ), - ); - } - - /** - * Payment form on checkout page. - */ - public function payment_fields() { - $description = $this->get_description(); - - if ( 'yes' == $this->sandbox ) { - $description .= ' ' . sprintf( __( 'TEST MODE ENABLED. Use a test card: %s', 'woocommerce' ), 'https://www.simplify.com/commerce/docs/tutorial/index#testing' ); - } - - if ( $description ) { - echo wpautop( wptexturize( trim( $description ) ) ); - } - - if ( 'standard' == $this->mode ) { - parent::payment_fields(); - } - } - - /** - * Outputs scripts used for simplify payment. - */ - public function payment_scripts() { - $load_scripts = false; - - if ( is_checkout() ) { - $load_scripts = true; - } - if ( $this->is_available() ) { - $load_scripts = true; - } - - if ( false === $load_scripts ) { - return; - } - - wp_enqueue_script( 'simplify-commerce', 'https://www.simplify.com/commerce/v1/simplify.js', array( 'jquery' ), WC_VERSION, true ); - wp_enqueue_script( 'wc-simplify-commerce', WC()->plugin_url() . '/includes/gateways/simplify-commerce/assets/js/simplify-commerce.js', array( 'simplify-commerce', 'wc-credit-card-form' ), WC_VERSION, true ); - wp_localize_script( - 'wc-simplify-commerce', 'Simplify_commerce_params', array( - 'key' => $this->public_key, - 'card.number' => __( 'Card number', 'woocommerce' ), - 'card.expMonth' => __( 'Expiry month', 'woocommerce' ), - 'card.expYear' => __( 'Expiry year', 'woocommerce' ), - 'is_invalid' => __( 'is invalid', 'woocommerce' ), - 'mode' => $this->mode, - 'is_ssl' => is_ssl(), - ) - ); - } - - public function add_payment_method() { - if ( empty( $_POST['simplify_token'] ) ) { - wc_add_notice( __( 'There was a problem adding this card.', 'woocommerce' ), 'error' ); - return; - } - - $cart_token = wc_clean( $_POST['simplify_token'] ); - $customer_token = $this->get_users_token(); - $current_user = wp_get_current_user(); - $customer_info = array( - 'email' => $current_user->user_email, - 'name' => $current_user->display_name, - ); - - $token = $this->save_token( $customer_token, $cart_token, $customer_info ); - if ( is_null( $token ) ) { - wc_add_notice( __( 'There was a problem adding this card.', 'woocommerce' ), 'error' ); - return; - } - - return array( - 'result' => 'success', - 'redirect' => wc_get_endpoint_url( 'payment-methods' ), - ); - } - - /** - * Actually saves a customer token to the database. - * - * @param WC_Payment_Token $customer_token Payment Token - * @param string $cart_token CC Token - * @param array $customer_info 'email', 'name' - * - * @return null|WC_Payment_Token|WC_Payment_Token_CC - */ - public function save_token( $customer_token, $cart_token, $customer_info ) { - if ( ! is_null( $customer_token ) ) { - $customer = Simplify_Customer::findCustomer( $customer_token->get_token() ); - $updates = array( 'token' => $cart_token ); - $customer->setAll( $updates ); - $customer->updateCustomer(); - $customer = Simplify_Customer::findCustomer( $customer_token->get_token() ); // get updated customer with new set card - $token = $customer_token; - } else { - $customer = Simplify_Customer::createCustomer( - array( - 'token' => $cart_token, - 'email' => $customer_info['email'], - 'name' => $customer_info['name'], - ) - ); - $token = new WC_Payment_Token_CC(); - $token->set_token( $customer->id ); - } - - // If we were able to create an save our card, save the data on our side too - if ( is_object( $customer ) && '' != $customer->id ) { - $customer_properties = $customer->getProperties(); - $card = $customer_properties['card']; - $token->set_gateway_id( $this->id ); - $token->set_card_type( strtolower( $card->type ) ); - $token->set_last4( $card->last4 ); - $expiry_month = ( 1 === strlen( $card->expMonth ) ? '0' . $card->expMonth : $card->expMonth ); - $token->set_expiry_month( $expiry_month ); - $token->set_expiry_year( '20' . $card->expYear ); - if ( is_user_logged_in() ) { - $token->set_user_id( get_current_user_id() ); - } - $token->save(); - return $token; - } - - return null; - } - - /** - * Process customer: updating or creating a new customer/saved CC - * - * @param WC_Order $order Order object - * @param WC_Payment_Token $customer_token Payment Token - * @param string $cart_token CC Token - */ - protected function process_customer( $order, $customer_token = null, $cart_token = '' ) { - // Are we saving a new payment method? - if ( is_user_logged_in() && isset( $_POST['wc-simplify_commerce-new-payment-method'] ) && true === (bool) $_POST['wc-simplify_commerce-new-payment-method'] ) { - $customer_info = array( - 'email' => $order->get_billing_email(), - 'name' => trim( $order->get_formatted_billing_full_name() ), - ); - $token = $this->save_token( $customer_token, $cart_token, $customer_info ); - if ( ! is_null( $token ) ) { - $order->add_payment_token( $token ); - } - } - } - - /** - * Process standard payments. - * - * @param WC_Order $order - * @param string $cart_token - * @param string $customer_token - * - * @return array - * @uses Simplify_ApiException - * @uses Simplify_BadRequestException - */ - protected function process_standard_payments( $order, $cart_token = '', $customer_token = '' ) { - try { - - if ( empty( $cart_token ) && empty( $customer_token ) ) { - $error_msg = __( 'Please make sure your card details have been entered correctly and that your browser supports JavaScript.', 'woocommerce' ); - - if ( 'yes' == $this->sandbox ) { - $error_msg .= ' ' . __( 'Developers: Please make sure that you are including jQuery and there are no JavaScript errors on the page.', 'woocommerce' ); - } - - throw new Simplify_ApiException( $error_msg ); - } - - // We need to figure out if we want to charge the card token (new unsaved token, no customer, etc) - // or the customer token (just saved method, previously saved method) - $pass_tokens = array(); - - if ( ! empty( $cart_token ) ) { - $pass_tokens['token'] = $cart_token; - } - - if ( ! empty( $customer_token ) ) { - $pass_tokens['customer'] = $customer_token; - // Use the customer token only, since we already saved the (one time use) card token to the customer - if ( isset( $_POST['wc-simplify_commerce-new-payment-method'] ) && true === (bool) $_POST['wc-simplify_commerce-new-payment-method'] ) { - unset( $pass_tokens['token'] ); - } - } - - // Did we create an account and save a payment method? We might need to use the customer token instead of the card token - if ( isset( $_POST['createaccount'] ) && true === (bool) $_POST['createaccount'] && empty( $customer_token ) ) { - $user_token = $this->get_users_token(); - if ( ! is_null( $user_token ) ) { - $pass_tokens['customer'] = $user_token->get_token(); - unset( $pass_tokens['token'] ); - } - } - - $payment_response = $this->do_payment( $order, $order->get_total(), $pass_tokens ); - - if ( is_wp_error( $payment_response ) ) { - throw new Simplify_ApiException( $payment_response->get_error_message() ); - } else { - // Remove cart - WC()->cart->empty_cart(); - - // Return thank you page redirect - return array( - 'result' => 'success', - 'redirect' => $this->get_return_url( $order ), - ); - } - } catch ( Simplify_ApiException $e ) { - if ( $e instanceof Simplify_BadRequestException && $e->hasFieldErrors() && $e->getFieldErrors() ) { - foreach ( $e->getFieldErrors() as $error ) { - wc_add_notice( $error->getFieldName() . ': "' . $error->getMessage() . '" (' . $error->getErrorCode() . ')', 'error' ); - } - } else { - wc_add_notice( $e->getMessage(), 'error' ); - } - - return array( - 'result' => 'fail', - 'redirect' => '', - ); - } - } - - /** - * do payment function. - * - * @param WC_order $order - * @param int $amount (default: 0) - * @param array $token - * - * @return bool|WP_Error - * @uses Simplify_BadRequestException - */ - public function do_payment( $order, $amount = 0, $token = array() ) { - if ( $amount * 100 < 50 ) { - return new WP_Error( 'simplify_error', __( 'Sorry, the minimum allowed order total is 0.50 to use this payment method.', 'woocommerce' ) ); - } - - try { - // Charge the customer - $data = array( - 'amount' => $amount * 100, // In cents. - 'description' => sprintf( __( '%1$s - Order #%2$s', 'woocommerce' ), esc_html( get_bloginfo( 'name', 'display' ) ), $order->get_order_number() ), - 'currency' => strtoupper( get_woocommerce_currency() ), - 'reference' => $order->get_id(), - ); - - $data = array_merge( $data, $token ); - $payment = Simplify_Payment::createPayment( $data ); - - } catch ( Exception $e ) { - - $error_message = $e->getMessage(); - - if ( $e instanceof Simplify_BadRequestException && $e->hasFieldErrors() && $e->getFieldErrors() ) { - $error_message = ''; - foreach ( $e->getFieldErrors() as $error ) { - $error_message .= ' ' . $error->getFieldName() . ': "' . $error->getMessage() . '" (' . $error->getErrorCode() . ')'; - } - } - - $order->add_order_note( sprintf( __( 'Simplify payment error: %s.', 'woocommerce' ), $error_message ) ); - - return new WP_Error( 'simplify_payment_declined', $e->getMessage(), array( 'status' => $e->getCode() ) ); - } - - if ( 'APPROVED' == $payment->paymentStatus ) { - // Payment complete - $order->payment_complete( $payment->id ); - - // Add order note - $order->add_order_note( sprintf( __( 'Simplify payment approved (ID: %1$s, Auth Code: %2$s)', 'woocommerce' ), $payment->id, $payment->authCode ) ); - - return true; - } else { - $order->add_order_note( __( 'Simplify payment declined', 'woocommerce' ) ); - - return new WP_Error( 'simplify_payment_declined', __( 'Payment was declined - please try another card.', 'woocommerce' ) ); - } - } - - /** - * Process standard payments. - * - * @param WC_Order $order - * @return array - */ - protected function process_hosted_payments( $order ) { - return array( - 'result' => 'success', - 'redirect' => $order->get_checkout_payment_url( true ), - ); - } - - protected function get_users_token() { - $customer_token = null; - if ( is_user_logged_in() ) { - $tokens = WC_Payment_Tokens::get_customer_tokens( get_current_user_id() ); - foreach ( $tokens as $token ) { - if ( $token->get_gateway_id() === $this->id ) { - $customer_token = $token; - break; - } - } - } - return $customer_token; - } - - /** - * Process the payment. - * - * @param int $order_id - * - * @return array|void - */ - public function process_payment( $order_id ) { - $order = wc_get_order( $order_id ); - - // Payment/CC form is hosted on Simplify - if ( 'hosted' === $this->mode ) { - return $this->process_hosted_payments( $order ); - } - - // New CC info was entered - if ( isset( $_POST['simplify_token'] ) ) { - $cart_token = wc_clean( $_POST['simplify_token'] ); - $customer_token = $this->get_users_token(); - $customer_token_value = ( ! is_null( $customer_token ) ? $customer_token->get_token() : '' ); - $this->process_customer( $order, $customer_token, $cart_token ); - return $this->process_standard_payments( $order, $cart_token, $customer_token_value ); - } - - // Possibly Create (or update) customer/save payment token, use an existing token, and then process the payment - if ( isset( $_POST['wc-simplify_commerce-payment-token'] ) && 'new' !== $_POST['wc-simplify_commerce-payment-token'] ) { - $token_id = wc_clean( $_POST['wc-simplify_commerce-payment-token'] ); - $token = WC_Payment_Tokens::get( $token_id ); - if ( $token->get_user_id() !== get_current_user_id() ) { - wc_add_notice( __( 'Please make sure your card details have been entered correctly and that your browser supports JavaScript.', 'woocommerce' ), 'error' ); - return; - } - $this->process_customer( $order, $token ); - return $this->process_standard_payments( $order, '', $token->get_token() ); - } - } - - /** - * Hosted payment args. - * - * @param WC_Order $order - * - * @return array - */ - protected function get_hosted_payments_args( $order ) { - $args = apply_filters( - 'woocommerce_simplify_commerce_hosted_args', array( - 'sc-key' => $this->public_key, - 'amount' => $order->get_total() * 100, - 'reference' => $order->get_id(), - 'name' => esc_html( get_bloginfo( 'name', 'display' ) ), - 'description' => sprintf( __( 'Order #%s', 'woocommerce' ), $order->get_order_number() ), - 'receipt' => 'false', - 'color' => $this->modal_color, - 'redirect-url' => WC()->api_request_url( 'WC_Gateway_Simplify_Commerce' ), - 'address' => $order->get_billing_address_1() . ' ' . $order->get_billing_address_2(), - 'address-city' => $order->get_billing_city(), - 'address-state' => $order->get_billing_state(), - 'address-zip' => $order->get_billing_postcode(), - 'address-country' => $order->get_billing_country(), - 'operation' => 'create.token', - ), $order->get_id() - ); - - return $args; - } - - /** - * Receipt page. - * - * @param int $order_id - */ - public function receipt_page( $order_id ) { - $order = wc_get_order( $order_id ); - - echo '

' . __( 'Thank you for your order, please click the button below to pay with credit card using Simplify Commerce by MasterCard.', 'woocommerce' ) . '

'; - - $args = $this->get_hosted_payments_args( $order ); - $button_args = array(); - foreach ( $args as $key => $value ) { - $button_args[] = 'data-' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"'; - } - - echo ' - ' . __( 'Cancel order & restore cart', 'woocommerce' ) . ' - '; - } - - /** - * Return handler for Hosted Payments. - */ - public function return_handler() { - @ob_clean(); - header( 'HTTP/1.1 200 OK' ); - - if ( isset( $_REQUEST['reference'] ) && isset( $_REQUEST['paymentId'] ) && isset( $_REQUEST['signature'] ) ) { - $signature = strtoupper( md5( $_REQUEST['amount'] . $_REQUEST['reference'] . $_REQUEST['paymentId'] . $_REQUEST['paymentDate'] . $_REQUEST['paymentStatus'] . $this->private_key ) ); - $order_id = absint( $_REQUEST['reference'] ); - $order = wc_get_order( $order_id ); - - if ( hash_equals( $signature, $_REQUEST['signature'] ) ) { - $order_complete = $this->process_order_status( $order, $_REQUEST['paymentId'], $_REQUEST['paymentStatus'], $_REQUEST['paymentDate'] ); - - if ( ! $order_complete ) { - $order->update_status( 'failed', __( 'Payment was declined by Simplify Commerce.', 'woocommerce' ) ); - } - - wp_redirect( $this->get_return_url( $order ) ); - exit(); - } - } - - wp_redirect( wc_get_page_permalink( 'cart' ) ); - exit(); - } - - /** - * Process the order status. - * - * @param WC_Order $order - * @param string $payment_id - * @param string $status - * @param string $auth_code - * - * @return bool - */ - public function process_order_status( $order, $payment_id, $status, $auth_code ) { - if ( 'APPROVED' == $status ) { - // Payment complete - $order->payment_complete( $payment_id ); - - // Add order note - $order->add_order_note( sprintf( __( 'Simplify payment approved (ID: %1$s, Auth Code: %2$s)', 'woocommerce' ), $payment_id, $auth_code ) ); - - // Remove cart - WC()->cart->empty_cart(); - - return true; - } - - return false; - } - - /** - * Process refunds. - * WooCommerce 2.2 or later. - * - * @param int $order_id - * @param float $amount - * @param string $reason - * @uses Simplify_ApiException - * @uses Simplify_BadRequestException - * @return bool|WP_Error - */ - public function process_refund( $order_id, $amount = null, $reason = '' ) { - try { - $payment_id = get_post_meta( $order_id, '_transaction_id', true ); - - $refund = Simplify_Refund::createRefund( - array( - 'amount' => $amount * 100, // In cents. - 'payment' => $payment_id, - 'reason' => $reason, - 'reference' => $order_id, - ) - ); - - if ( 'APPROVED' == $refund->paymentStatus ) { - return true; - } else { - throw new Simplify_ApiException( __( 'Refund was declined.', 'woocommerce' ) ); - } - } catch ( Simplify_ApiException $e ) { - if ( $e instanceof Simplify_BadRequestException && $e->hasFieldErrors() && $e->getFieldErrors() ) { - foreach ( $e->getFieldErrors() as $error ) { - return new WP_Error( 'simplify_refund_error', $error->getFieldName() . ': "' . $error->getMessage() . '" (' . $error->getErrorCode() . ')' ); - } - } else { - return new WP_Error( 'simplify_refund_error', $e->getMessage() ); - } - } - - return false; - } - - /** - * Get gateway icon. - * - * @access public - * @return string - */ - public function get_icon() { - $icon = 'Visa'; - $icon .= 'MasterCard'; - $icon .= 'Discover'; - $icon .= 'Amex'; - $icon .= 'JCB'; - - return apply_filters( 'woocommerce_gateway_icon', $icon, $this->id ); - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify.php b/includes/gateways/simplify-commerce/includes/Simplify.php deleted file mode 100644 index d0c4859f5c4..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify.php +++ /dev/null @@ -1,88 +0,0 @@ -setAll($hash); - } - - /** - * Creates an OAuth access token - * @param $code - the authorisation code returned from the GET on /oauth/authorize - * @param $redirect_uri = this must be the redirect_uri set in the apps configuration - * @param $authentication - Authentication information to access the API. If not value is passed the global key Simplify::$publicKey and Simplify::$privateKey are used - * @return Simplify_AccessToken - */ - public static function create($code, $redirect_uri, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 3); - - $props = 'code='.$code.'&redirect_uri='.$redirect_uri.'&grant_type=authorization_code'; - $resp = Simplify_AccessToken::sendRequest($props, "token", $authentication); - - return new Simplify_AccessToken($resp); - } - - /** - * Refreshes the current token. The access_token and refresh_token values will be updated. - * @param $authentication - Authentication information to access the API. If not value is passed the global key Simplify::$publicKey and Simplify::$privateKey are used - * @return Simplify_AccessToken - * @throws InvalidArgumentException - */ - public function refresh($authentication = null) { - $args = func_get_args(); - - $refresh_token = $this->refresh_token; - if (empty($refresh_token)){ - throw new InvalidArgumentException('Cannot refresh access token; refresh token is invalid'); - } - - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $props = 'refresh_token='.$refresh_token.'&grant_type=refresh_token'; - $resp = Simplify_AccessToken::sendRequest($props, "token", $authentication); - - $this->setAll($resp); - - return $this; - } - - /** - *

Revokes a token from further use. - * @param $authentication - Authentication information to access the API. If not value is passed the global key Simplify::$publicKey and Simplify::$privateKey are used - * @return Simplify_AccessToken - * @throws InvalidArgumentException - */ - public function revoke($authentication = null) { - $args = func_get_args(); - - $access_token = $this->access_token; - if (empty($access_token)){ - throw new InvalidArgumentException('Cannot revoke access token; access token is invalid'); - } - - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $props = 'token='.$access_token.''; - - Simplify_AccessToken::sendRequest($props, "revoke", $authentication); - - $this->access_token = null; - $this->refresh_token = null; - $this->redirect_uri = null; - - return $this; - } - - private static function sendRequest($props, $context, $authentication){ - - $url = Simplify_Constants::OAUTH_BASE_URL.'/'.$context; - $http = new Simplify_HTTP(); - $resp = $http->oauthRequest($url, $props, $authentication); - - return $resp; - } - - /** - * @ignore - */ - static public function getClazz() { - return "AccessToken"; - } - -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Authentication.php b/includes/gateways/simplify-commerce/includes/Simplify/Authentication.php deleted file mode 100644 index c787b6c1d5f..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Authentication.php +++ /dev/null @@ -1,74 +0,0 @@ -accessToken = $accessToken; - } - - function __construct2($publicKey, $privateKey) { - $this->publicKey = $publicKey; - $this->privateKey = $privateKey; - } - - function __construct3($publicKey, $privateKey, $accessToken) { - $this->publicKey = $publicKey; - $this->privateKey = $privateKey; - $this->accessToken = $accessToken; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Authorization.php b/includes/gateways/simplify-commerce/includes/Simplify/Authorization.php deleted file mode 100644 index 69704b558f7..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Authorization.php +++ /dev/null @@ -1,142 +0,0 @@ - - *

amount
Amount of the payment (in the smallest unit of your currency). Example: 100 = $1.00USD required
- *
card.addressCity
City of the cardholder. [max length: 50, min length: 2]
- *
card.addressCountry
Country code (ISO-3166-1-alpha-2 code) of residence of the cardholder. [max length: 2, min length: 2]
- *
card.addressLine1
Address of the cardholder. [max length: 255]
- *
card.addressLine2
Address of the cardholder if needed. [max length: 255]
- *
card.addressState
State of residence of the cardholder. For the US, this is a 2-digit USPS code. [max length: 255, min length: 2]
- *
card.addressZip
Postal code of the cardholder. The postal code size is between 5 and 9 characters in length and only contains numbers or letters. [max length: 9, min length: 3]
- *
card.cvc
CVC security code of the card. This is the code on the back of the card. Example: 123
- *
card.expMonth
Expiration month of the card. Format is MM. Example: January = 01 [min value: 1, max value: 12] required
- *
card.expYear
Expiration year of the card. Format is YY. Example: 2013 = 13 [min value: 0, max value: 99] required
- *
card.name
Name as it appears on the card. [max length: 50, min length: 2]
- *
card.number
Card number as it appears on the card. [max length: 19, min length: 13] required
- *
currency
Currency code (ISO-4217) for the transaction. Must match the currency associated with your account. [default: USD] required
- *
customer
ID of customer. If specified, card on file of customer will be used.
- *
description
Free form text field to be used as a description of the payment. This field is echoed back with the payment on any find or list operations. [max length: 1024]
- *
reference
Custom reference field to be used with outside systems.
- *
replayId
An identifier that can be sent to uniquely identify a payment request to facilitate retries due to I/O related issues. This identifier must be unique for your account (sandbox or live) across all of your payments. If supplied, we will check for a payment on your account that matches this identifier, and if one is found we will attempt to return an identical response of the original request. [max length: 50, min length: 1]
- *
statementDescription.name
Merchant name required
- *
statementDescription.phoneNumber
Merchant contact phone number.
- *
token
If specified, card associated with card token will be used. [max length: 255]
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Authorization a Authorization object. - */ - static public function createAuthorization($hash, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $instance = new Simplify_Authorization(); - $instance->setAll($hash); - - $object = Simplify_PaymentsApi::createObject($instance, $authentication); - return $object; - } - - - /** - * Deletes an Simplify_Authorization object. - * - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * - * @return true - */ - public function deleteAuthorization($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $obj = Simplify_PaymentsApi::deleteObject($this, $authentication); - $this->properties = null; - return true; - } - - - /** - * Retrieve Simplify_Authorization objects. - * @param array criteria a map of parameters; valid keys are:
- *
filter
Filters to apply to the list.
- *
max
Allows up to a max of 50 list items to return. [min value: 0, max value: 50, default: 20]
- *
offset
Used in pagination of the list. This is the start offset of the page. [min value: 0, default: 0]
- *
sorting
Allows for ascending or descending sorting of the list. The value maps properties to the sort direction (either asc for ascending or desc for descending). Sortable properties are: dateCreated amount id description paymentDate.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return ResourceList a ResourceList object that holds the list of Authorization objects and the total - * number of Authorization objects available for the given criteria. - * @see ResourceList - */ - static public function listAuthorization($criteria = null, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Authorization(); - $list = Simplify_PaymentsApi::listObject($val, $criteria, $authentication); - - return $list; - } - - - /** - * Retrieve a Simplify_Authorization object from the API - * - * @param string id the id of the Authorization object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Authorization a Authorization object - */ - static public function findAuthorization($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Authorization(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - /** - * @ignore - */ - public function getClazz() { - return "Authorization"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/CardToken.php b/includes/gateways/simplify-commerce/includes/Simplify/CardToken.php deleted file mode 100644 index 0cd0f0cf44a..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/CardToken.php +++ /dev/null @@ -1,94 +0,0 @@ - - *
callback
The URL callback for the cardtoken
- *
card.addressCity
City of the cardholder. [max length: 50, min length: 2]
- *
card.addressCountry
Country code (ISO-3166-1-alpha-2 code) of residence of the cardholder. [max length: 2, min length: 2]
- *
card.addressLine1
Address of the cardholder. [max length: 255]
- *
card.addressLine2
Address of the cardholder if needed. [max length: 255]
- *
card.addressState
State of residence of the cardholder. For the US, this is a 2-digit USPS code. [max length: 255, min length: 2]
- *
card.addressZip
Postal code of the cardholder. The postal code size is between 5 and 9 in length and only contain numbers or letters. [max length: 9, min length: 3]
- *
card.cvc
CVC security code of the card. This is the code on the back of the card. Example: 123
- *
card.expMonth
Expiration month of the card. Format is MM. Example: January = 01 [min value: 1, max value: 12] required
- *
card.expYear
Expiration year of the card. Format is YY. Example: 2013 = 13 [min value: 0, max value: 99] required
- *
card.name
Name as appears on the card. [max length: 50, min length: 2]
- *
card.number
Card number as it appears on the card. [max length: 19, min length: 13] required
- *
key
Key used to create the card token.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return CardToken a CardToken object. - */ - static public function createCardToken($hash, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $instance = new Simplify_CardToken(); - $instance->setAll($hash); - - $object = Simplify_PaymentsApi::createObject($instance, $authentication); - return $object; - } - - - - /** - * Retrieve a Simplify_CardToken object from the API - * - * @param string id the id of the CardToken object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return CardToken a CardToken object - */ - static public function findCardToken($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_CardToken(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - /** - * @ignore - */ - public function getClazz() { - return "CardToken"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Chargeback.php b/includes/gateways/simplify-commerce/includes/Simplify/Chargeback.php deleted file mode 100644 index 78fca9c80af..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Chargeback.php +++ /dev/null @@ -1,86 +0,0 @@ - - *
filter
Filters to apply to the list.
- *
max
Allows up to a max of 50 list items to return. [min value: 0, max value: 50, default: 20]
- *
offset
Used in paging of the list. This is the start offset of the page. [min value: 0, default: 0]
- *
sorting
Allows for ascending or descending sorting of the list. The value maps properties to the sort direction (either asc for ascending or desc for descending). Sortable properties are: id amount description dateCreated.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return ResourceList a ResourceList object that holds the list of Chargeback objects and the total - * number of Chargeback objects available for the given criteria. - * @see ResourceList - */ - static public function listChargeback($criteria = null, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Chargeback(); - $list = Simplify_PaymentsApi::listObject($val, $criteria, $authentication); - - return $list; - } - - - /** - * Retrieve a Simplify_Chargeback object from the API - * - * @param string id the id of the Chargeback object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Chargeback a Chargeback object - */ - static public function findChargeback($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Chargeback(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - /** - * @ignore - */ - public function getClazz() { - return "Chargeback"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Constants.php b/includes/gateways/simplify-commerce/includes/Simplify/Constants.php deleted file mode 100644 index c2174da8984..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Constants.php +++ /dev/null @@ -1,58 +0,0 @@ - - *
amountOff
Amount off of the price of the product in the smallest units of the currency of the merchant. While this field is optional, you must provide either amountOff or percentOff for a coupon. Example: 100 = $1.00USD [min value: 1]
- *
couponCode
Code that identifies the coupon to be used. [min length: 2] required
- *
description
A brief section that describes the coupon.
- *
durationInMonths
DEPRECATED - Duration in months that the coupon will be applied after it has first been selected. [min value: 1, max value: 9999]
- *
endDate
Last date of the coupon in UTC millis that the coupon can be applied to a subscription. This ends at 23:59:59 of the merchant timezone.
- *
maxRedemptions
Maximum number of redemptions allowed for the coupon. A redemption is defined as when the coupon is applied to the subscription for the first time. [min value: 1]
- *
numTimesApplied
The number of times a coupon will be applied on a customer's subscription. [min value: 1, max value: 9999]
- *
percentOff
Percentage off of the price of the product. While this field is optional, you must provide either amountOff or percentOff for a coupon. The percent off is a whole number. [min value: 1, max value: 100]
- *
startDate
First date of the coupon in UTC millis that the coupon can be applied to a subscription. This starts at midnight of the merchant timezone. required
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Coupon a Coupon object. - */ - static public function createCoupon($hash, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $instance = new Simplify_Coupon(); - $instance->setAll($hash); - - $object = Simplify_PaymentsApi::createObject($instance, $authentication); - return $object; - } - - - /** - * Deletes an Simplify_Coupon object. - * - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * - * @return true - */ - public function deleteCoupon($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $obj = Simplify_PaymentsApi::deleteObject($this, $authentication); - $this->properties = null; - return true; - } - - - /** - * Retrieve Simplify_Coupon objects. - * @param array criteria a map of parameters; valid keys are:
- *
filter
Filters to apply to the list.
- *
max
Allows up to a max of 50 list items to return. [min value: 0, max value: 50, default: 20]
- *
offset
Used in paging of the list. This is the start offset of the page. [min value: 0, default: 0]
- *
sorting
Allows for ascending or descending sorting of the list. The value maps properties to the sort direction (either asc for ascending or desc for descending). Sortable properties are: dateCreated maxRedemptions timesRedeemed id startDate endDate percentOff couponCode durationInMonths numTimesApplied amountOff.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return ResourceList a ResourceList object that holds the list of Coupon objects and the total - * number of Coupon objects available for the given criteria. - * @see ResourceList - */ - static public function listCoupon($criteria = null, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Coupon(); - $list = Simplify_PaymentsApi::listObject($val, $criteria, $authentication); - - return $list; - } - - - /** - * Retrieve a Simplify_Coupon object from the API - * - * @param string id the id of the Coupon object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Coupon a Coupon object - */ - static public function findCoupon($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Coupon(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - - /** - * Updates an Simplify_Coupon object. - * - * The properties that can be updated: - *
- *
endDate
The ending date in UTC millis for the coupon. This must be after the starting date of the coupon.
- *
maxRedemptions
Maximum number of redemptions allowed for the coupon. A redemption is defined as when the coupon is applied to the subscription for the first time. [min value: 1]
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Coupon a Coupon object. - */ - public function updateCoupon($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $object = Simplify_PaymentsApi::updateObject($this, $authentication); - return $object; - } - - /** - * @ignore - */ - public function getClazz() { - return "Coupon"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Customer.php b/includes/gateways/simplify-commerce/includes/Simplify/Customer.php deleted file mode 100644 index 6bbcf524290..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Customer.php +++ /dev/null @@ -1,184 +0,0 @@ - - *
card.addressCity
City of the cardholder. required
- *
card.addressCountry
Country code (ISO-3166-1-alpha-2 code) of residence of the cardholder. required
- *
card.addressLine1
Address of the cardholder required
- *
card.addressLine2
Address of the cardholder if needed. required
- *
card.addressState
State of residence of the cardholder. For the US, this is a 2-digit USPS code. required
- *
card.addressZip
Postal code of the cardholder. The postal code size is between 5 and 9 in length and only contain numbers or letters. required
- *
card.cvc
CVC security code of the card. This is the code on the back of the card. Example: 123 required
- *
card.expMonth
Expiration month of the card. Format is MM. Example: January = 01 required
- *
card.expYear
Expiration year of the card. Format is YY. Example: 2013 = 13 required
- *
card.id
ID of card. Unused during customer create.
- *
card.name
Name as appears on the card. required
- *
card.number
Card number as it appears on the card. [max length: 19, min length: 13]
- *
email
Email address of the customer required
- *
name
Customer name [min length: 2] required
- *
reference
Reference field for external applications use.
- *
subscriptions.amount
Amount of payment in the smallest unit of your currency. Example: 100 = $1.00USD
- *
subscriptions.billingCycle
How the plan is billed to the customer. Values must be AUTO (indefinitely until the customer cancels) or FIXED (a fixed number of billing cycles). [default: AUTO]
- *
subscriptions.billingCycleLimit
The number of fixed billing cycles for a plan. Only used if the billingCycle parameter is set to FIXED. Example: 4
- *
subscriptions.coupon
Coupon associated with the subscription for the customer.
- *
subscriptions.currency
Currency code (ISO-4217). Must match the currency associated with your account. [default: USD]
- *
subscriptions.customer
The customer ID to create the subscription for. Do not supply this when creating a customer.
- *
subscriptions.frequency
Frequency of payment for the plan. Used in conjunction with frequencyPeriod. Valid values are "DAILY", "WEEKLY", "MONTHLY" and "YEARLY".
- *
subscriptions.frequencyPeriod
Period of frequency of payment for the plan. Example: if the frequency is weekly, and periodFrequency is 2, then the subscription is billed bi-weekly.
- *
subscriptions.name
Name describing subscription
- *
subscriptions.plan
The plan ID that the subscription should be created from.
- *
subscriptions.quantity
Quantity of the plan for the subscription. [min value: 1]
- *
subscriptions.renewalReminderLeadDays
If set, how many days before the next billing cycle that a renewal reminder is sent to the customer. If null, then no emails are sent. Minimum value is 7 if set.
- *
token
If specified, card associated with card token will be used
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Customer a Customer object. - */ - static public function createCustomer($hash, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $instance = new Simplify_Customer(); - $instance->setAll($hash); - - $object = Simplify_PaymentsApi::createObject($instance, $authentication); - return $object; - } - - - /** - * Deletes an Simplify_Customer object. - * - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * - * @return true - */ - public function deleteCustomer($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $obj = Simplify_PaymentsApi::deleteObject($this, $authentication); - $this->properties = null; - return true; - } - - - /** - * Retrieve Simplify_Customer objects. - * @param array criteria a map of parameters; valid keys are:
- *
filter
Filters to apply to the list.
- *
max
Allows up to a max of 50 list items to return. [min value: 0, max value: 50, default: 20]
- *
offset
Used in paging of the list. This is the start offset of the page. [min value: 0, default: 0]
- *
sorting
Allows for ascending or descending sorting of the list. The value maps properties to the sort direction (either asc for ascending or desc for descending). Sortable properties are: dateCreated id name email reference.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return ResourceList a ResourceList object that holds the list of Customer objects and the total - * number of Customer objects available for the given criteria. - * @see ResourceList - */ - static public function listCustomer($criteria = null, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Customer(); - $list = Simplify_PaymentsApi::listObject($val, $criteria, $authentication); - - return $list; - } - - - /** - * Retrieve a Simplify_Customer object from the API - * - * @param string id the id of the Customer object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Customer a Customer object - */ - static public function findCustomer($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Customer(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - - /** - * Updates an Simplify_Customer object. - * - * The properties that can be updated: - *
- *
card.addressCity
City of the cardholder. required
- *
card.addressCountry
Country code (ISO-3166-1-alpha-2 code) of residence of the cardholder. required
- *
card.addressLine1
Address of the cardholder. required
- *
card.addressLine2
Address of the cardholder if needed. required
- *
card.addressState
State of residence of the cardholder. For the US, this is a 2-digit USPS code. required
- *
card.addressZip
Postal code of the cardholder. The postal code size is between 5 and 9 in length and only contain numbers or letters. required
- *
card.cvc
CVC security code of the card. This is the code on the back of the card. Example: 123 required
- *
card.expMonth
Expiration month of the card. Format is MM. Example: January = 01 required
- *
card.expYear
Expiration year of the card. Format is YY. Example: 2013 = 13 required
- *
card.id
ID of card. If present, card details for the customer will not be updated. If not present, the customer will be updated with the supplied card details.
- *
card.name
Name as appears on the card. required
- *
card.number
Card number as it appears on the card. [max length: 19, min length: 13]
- *
email
Email address of the customer required
- *
name
Customer name [min length: 2] required
- *
reference
Reference field for external applications use.
- *
token
If specified, card associated with card token will be added to the customer
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Customer a Customer object. - */ - public function updateCustomer($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $object = Simplify_PaymentsApi::updateObject($this, $authentication); - return $object; - } - - /** - * @ignore - */ - public function getClazz() { - return "Customer"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Deposit.php b/includes/gateways/simplify-commerce/includes/Simplify/Deposit.php deleted file mode 100644 index 7c6d66add3f..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Deposit.php +++ /dev/null @@ -1,86 +0,0 @@ - - *
filter
Filters to apply to the list.
- *
max
Allows up to a max of 50 list items to return. [min value: 0, max value: 50, default: 20]
- *
offset
Used in paging of the list. This is the start offset of the page. [min value: 0, default: 0]
- *
sorting
Allows for ascending or descending sorting of the list. The value maps properties to the sort direction (either asc for ascending or desc for descending). Sortable properties are: amount dateCreated depositDate.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return ResourceList a ResourceList object that holds the list of Deposit objects and the total - * number of Deposit objects available for the given criteria. - * @see ResourceList - */ - static public function listDeposit($criteria = null, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Deposit(); - $list = Simplify_PaymentsApi::listObject($val, $criteria, $authentication); - - return $list; - } - - - /** - * Retrieve a Simplify_Deposit object from the API - * - * @param string id the id of the Deposit object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Deposit a Deposit object - */ - static public function findDeposit($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Deposit(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - /** - * @ignore - */ - public function getClazz() { - return "Deposit"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Event.php b/includes/gateways/simplify-commerce/includes/Simplify/Event.php deleted file mode 100644 index bdb682edac5..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Event.php +++ /dev/null @@ -1,68 +0,0 @@ -payload
The raw JWS payload.
required - *
url
The URL for the webhook. If present it must match the URL registered for the webhook.
- * @param $authentication Object that contains the API public and private keys. If null the values of the static - * Simplify::$publicKey and Simplify::$privateKey will be used. - * @return Payments_Event an Event object. - * @throws InvalidArgumentException - */ - static public function createEvent($hash, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $paymentsApi = new Simplify_PaymentsApi(); - - $jsonObject = $paymentsApi->jwsDecode($hash, $authentication); - - if ($jsonObject['event'] == null) { - throw new InvalidArgumentException("Incorrect data in webhook event"); - } - - return $paymentsApi->convertFromHashToObject($jsonObject['event'], self::getClazz()); - } - - /** - * @ignore - */ - static public function getClazz() { - return "Event"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Exceptions.php b/includes/gateways/simplify-commerce/includes/Simplify/Exceptions.php deleted file mode 100644 index 2e890ebd254..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Exceptions.php +++ /dev/null @@ -1,294 +0,0 @@ -status = $status; - $this->errorCode = null; - $this->reference = null; - - if ($errorData != null) { - - $this->reference = $errorData['reference']; - $this->errorData = $errorData; - - $error = $errorData['error']; - if ($error != null) { - - $m = $error['message']; - if ($m != null) { - $this->message = $m; - } - - $this->errorCode = $error['code']; - } - } - } - - /** - * Returns a map of all error data returned by the API. - * @return array a map containing API error data. - */ - function getErrorData() { - return $this->errorData; - } - - /** - * Returns the HTTP status for the request. - * @return string HTTP status code (or null if there is no status). - */ - function getStatus() { - return $this->status; - } - - /** - * Returns unique reference for the API error. - * @return string a reference (or null if there is no reference). - */ - function getReference() { - return $this->reference; - } - - /** - * Returns an code for the API error. - * @return string the error code. - */ - function getErrorCode() { - return $this->errorCode; - } - - /** - * Returns a description of the error. - * @return string Description of the error. - */ - function describe() { - return get_class($this) . ": \"" - . $this->getMessage() . "\" (status: " - . $this->getStatus() . ", error code: " - . $this->getErrorCode() . ", reference: " - . $this->getReference() . ")"; - } - -} - - -/** - * Exception raised when there are communication problems contacting the API. - */ -class Simplify_ApiConnectionException extends Simplify_ApiException { - - /** - * @ignore - */ - function __construct($message, $status = null, $errorData = null) { - parent::__construct($message, $status, $errorData); - } -} - -/** - * Exception raised where there are problems authenticating a request. - */ -class Simplify_AuthenticationException extends Simplify_ApiException { - - /** - * @ignore - */ - function __construct($message, $status = null, $errorData = null) { - parent::__construct($message, $status, $errorData); - } -} - -/** - * Exception raised when the API request contains errors. - */ -class Simplify_BadRequestException extends Simplify_ApiException { - - protected $fieldErrors; - - /** - * @ignore - */ - function __construct($message, $status = null, $errorData = null) { - parent::__construct($message, $status, $errorData); - - $fieldErrors = array(); - - if ($errorData != null) { - $error = $errorData['error']; - if ($error != null) { - $fieldErrors = $error['fieldErrors']; - if ($fieldErrors != null) { - $this->fieldErrors = array(); - foreach ($fieldErrors as $fieldError) { - array_push($this->fieldErrors, new Simplify_FieldError($fieldError)); - } - } - } - } - } - - /** - * Returns a boolean indicating whether there are any field errors. - * @return boolean true if there are field errors; false otherwise. - */ - function hasFieldErrors() { - return count($this->fieldErrors) > 0; - } - - /** - * Returns a list containing all field errors. - * @return array list of field errors. - */ - function getFieldErrors() { - return $this->fieldErrors; - } - - /** - * Returns a description of the error. - * @return string description of the error. - */ - function describe() { - $s = parent::describe(); - foreach ($this->getFieldErrors() as $fieldError) { - $s = $s . "\n" . (string) $fieldError; - } - return $s . "\n"; - } - -} - -/** - * Represents a single error in a field of a request sent to the API. - */ -class Simplify_FieldError { - - protected $field; - protected $code; - protected $message; - - /** - * @ignore - */ - function __construct($errorData) { - - $this->field = $errorData['field']; - $this->code = $errorData['code']; - $this->message = $errorData['message']; - } - - /** - * Returns the name of the field with the error. - * @return string the field name. - */ - function getFieldName() { - return $this->field; - } - - /** - * Returns the code for the error. - * @return string the error code. - */ - function getErrorCode() { - return $this->code; - } - - /** - * Returns a description of the error. - * @return string description of the error. - */ - function getMessage() { - return $this->message; - } - - - function __toString() { - return "Field error: " . $this->getFieldName() . "\"" . $this->getMessage() . "\" (" . $this->getErrorCode() . ")"; - } - -} - -/** - * Exception when a requested object cannot be found. - */ -class Simplify_ObjectNotFoundException extends Simplify_ApiException { - - /** - * @ignore - */ - function __construct($message, $status = null, $errorData = null) { - parent::__construct($message, $status, $errorData); - } -} - -/** - * Exception when a request was not allowed. - */ -class Simplify_NotAllowedException extends Simplify_ApiException { - - /** - * @ignore - */ - function __construct($message, $status = null, $errorData = null) { - parent::__construct($message, $status, $errorData); - } -} - -/** - * Exception when there was a system error processing a request. - */ -class Simplify_SystemException extends Simplify_ApiException { - - /** - * @ignore - */ - function __construct($message, $status = null, $errorData = null) { - parent::__construct($message, $status, $errorData); - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/FraudCheck.php b/includes/gateways/simplify-commerce/includes/Simplify/FraudCheck.php deleted file mode 100644 index d0fea2a5799..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/FraudCheck.php +++ /dev/null @@ -1,122 +0,0 @@ - - *
amount
Amount of the transaction to be checked for fraud (in the smallest unit of your currency). Example: 100 = $1.00USD
- *
card.addressCity
City of the cardholder. [max length: 50, min length: 2]
- *
card.addressCountry
Country code (ISO-3166-1-alpha-2 code) of residence of the cardholder. [max length: 2, min length: 2]
- *
card.addressLine1
Address of the cardholder. [max length: 255]
- *
card.addressLine2
Address of the cardholder if needed. [max length: 255]
- *
card.addressState
State of residence of the cardholder. For the US, this is a 2-digit USPS code. [max length: 255, min length: 2]
- *
card.addressZip
Postal code of the cardholder. The postal code size is between 5 and 9 characters in length and only contains numbers or letters. [max length: 9, min length: 3]
- *
card.cvc
CVC security code of the card. This is the code on the back of the card. Example: 123
- *
card.expMonth
Expiration month of the card. Format is MM. Example: January = 01 [min value: 1, max value: 12] required
- *
card.expYear
Expiration year of the card. Format is YY. Example: 2013 = 13 [min value: 0, max value: 99] required
- *
card.name
Name as it appears on the card. [max length: 50, min length: 2]
- *
card.number
Card number as it appears on the card. [max length: 19, min length: 13] required
- *
currency
Currency code (ISO-4217) for the transaction to be checked for fraud.
- *
description
- Description of the fraud check.
- *
mode
Fraud check mode. “simple” only does an AVS and CVC check; “advanced” does a complete fraud check, running the input against the set up rules. [valid values: simple, advanced, full] required
- *
sessionId
Session ID usd during data collection. [max length: 255]
- *
token
Description
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return FraudCheck a FraudCheck object. - */ - static public function createFraudCheck($hash, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $instance = new Simplify_FraudCheck(); - $instance->setAll($hash); - - $object = Simplify_PaymentsApi::createObject($instance, $authentication); - return $object; - } - - - - /** - * Retrieve Simplify_FraudCheck objects. - * @param array criteria a map of parameters; valid keys are:
- *
filter
Allows for ascending or descending sorting of the list.
- *
max
Allows up to a max of 50 list items to return. [min value: 0, max value: 50, default: 20]
- *
offset
Used in paging of the list. This is the start offset of the page. [min value: 0, default: 0]
- *
sorting
Used in paging of the list. This is the start offset of the page. The value maps properties to the sort direction (either asc for ascending or desc for descending). Sortable properties are: .
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return ResourceList a ResourceList object that holds the list of FraudCheck objects and the total - * number of FraudCheck objects available for the given criteria. - * @see ResourceList - */ - static public function listFraudCheck($criteria = null, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_FraudCheck(); - $list = Simplify_PaymentsApi::listObject($val, $criteria, $authentication); - - return $list; - } - - - /** - * Retrieve a Simplify_FraudCheck object from the API - * - * @param string id the id of the FraudCheck object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return FraudCheck a FraudCheck object - */ - static public function findFraudCheck($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_FraudCheck(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - /** - * @ignore - */ - public function getClazz() { - return "FraudCheck"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Http.php b/includes/gateways/simplify-commerce/includes/Simplify/Http.php deleted file mode 100644 index e0925a1bfa4..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Http.php +++ /dev/null @@ -1,411 +0,0 @@ - self::POST, - "put" => self::PUT, - "get" => self::GET, - "delete" => self::DELETE); - - private function request($url, $method, $authentication, $payload = '') - { - if ($authentication->publicKey == null) { - throw new InvalidArgumentException('Must have a valid public key to connect to the API'); - } - - if ($authentication->privateKey == null) { - throw new InvalidArgumentException('Must have a valid API key to connect to the API'); - } - - if (!array_key_exists(strtolower($method), self::$_validMethods)) { - throw new InvalidArgumentException('Invalid method: '.strtolower($method)); - } - - $method = self::$_validMethods[strtolower($method)]; - - $curl = curl_init(); - - $options = array(); - - $options[CURLOPT_URL] = $url; - $options[CURLOPT_CUSTOMREQUEST] = $method; - $options[CURLOPT_RETURNTRANSFER] = true; - $options[CURLOPT_FAILONERROR] = false; - - $signature = $this->jwsEncode($authentication, $url, $payload, $method == self::POST || $method == self::PUT); - - if ($method == self::POST || $method == self::PUT) { - $headers = array( - 'Content-type: application/json' - ); - $options[CURLOPT_POSTFIELDS] = $signature; - } else { - $headers = array( - 'Authorization: JWS ' . $signature - ); - } - - array_push($headers, 'Accept: application/json'); - $user_agent = 'PHP-SDK/' . Simplify_Constants::VERSION; - if (Simplify::$userAgent != null) { - $user_agent = $user_agent . ' ' . Simplify::$userAgent; - } - array_push($headers, 'User-Agent: ' . $user_agent); - - $options[CURLOPT_HTTPHEADER] = $headers; - - curl_setopt_array($curl, $options); - - $data = curl_exec($curl); - $errno = curl_errno($curl); - $status = curl_getinfo($curl, CURLINFO_HTTP_CODE); - - if ($data == false || $errno != CURLE_OK) { - throw new Simplify_ApiConnectionException(curl_error($curl)); - } - - $object = json_decode($data, true); - //'typ' => self::JWS_TYPE, - $response = array('status' => $status, 'object' => $object); - - return $response; - curl_close($curl); - } - - /** - * Handles Simplify API requests - * - * @param $url - * @param $method - * @param $authentication - * @param string $payload - * @return mixed - * @throws Simplify_AuthenticationException - * @throws Simplify_ObjectNotFoundException - * @throws Simplify_BadRequestException - * @throws Simplify_NotAllowedException - * @throws Simplify_SystemException - */ - public function apiRequest($url, $method, $authentication, $payload = ''){ - - $response = $this->request($url, $method, $authentication, $payload); - - $status = $response['status']; - $object = $response['object']; - - if ($status == self::HTTP_SUCCESS) { - return $object; - } - - if ($status == self::HTTP_REDIRECTED) { - throw new Simplify_BadRequestException("Unexpected response code returned from the API, have you got the correct URL?", $status, $object); - } else if ($status == self::HTTP_BAD_REQUEST) { - throw new Simplify_BadRequestException("Bad request", $status, $object); - } else if ($status == self::HTTP_UNAUTHORIZED) { - throw new Simplify_AuthenticationException("You are not authorized to make this request. Are you using the correct API keys?", $status, $object); - } else if ($status == self::HTTP_NOT_FOUND) { - throw new Simplify_ObjectNotFoundException("Object not found", $status, $object); - } else if ($status == self::HTTP_NOT_ALLOWED) { - throw new Simplify_NotAllowedException("Operation not allowed", $status, $object); - } else if ($status < 500) { - throw new Simplify_BadRequestException("Bad request", $status, $object); - } - throw new Simplify_SystemException("An unexpected error has been raised. Looks like there's something wrong at our end." , $status, $object); - } - - /** - * Handles Simplify OAuth requests - * - * @param $url - * @param $payload - * @param $authentication - * @return mixed - * @throws Simplify_AuthenticationException - * @throws Simplify_ObjectNotFoundException - * @throws Simplify_BadRequestException - * @throws Simplify_NotAllowedException - * @throws Simplify_SystemException - */ - public function oauthRequest($url, $payload, $authentication){ - - $response = $this->request($url, Simplify_HTTP::POST, $authentication, $payload); - - $status = $response['status']; - $object = $response['object']; - - if ($status == self::HTTP_SUCCESS) { - return $object; - } - - $error = $object['error']; - $error_description = $object['error_description']; - - if ($status == self::HTTP_REDIRECTED) { - throw new Simplify_BadRequestException("Unexpected response code returned from the API, have you got the correct URL?", $status, $object); - } else if ($status == self::HTTP_BAD_REQUEST) { - - if ( $error == 'invalid_request'){ - throw new Simplify_BadRequestException("", $status, $this->buildOauthError('Error during OAuth request', $error, $error_description)); - }else if ($error == 'unsupported_grant_type'){ - throw new Simplify_BadRequestException("", $status, $this->buildOauthError('Unsupported grant type in OAuth request', $error, $error_description)); - }else if ($error == 'invalid_scope'){ - throw new Simplify_BadRequestException("", $status, $this->buildOauthError('Invalid scope in OAuth request', $error, $error_description)); - }else{ - throw new Simplify_BadRequestException("", $status, $this->buildOauthError('Unknown OAuth error', $error, $error_description)); - } - - //TODO: build BadRequestException error JSON - - } else if ($status == self::HTTP_UNAUTHORIZED){ - - if ($error == 'access_denied'){ - throw new Simplify_AuthenticationException("", $status, $this->buildOauthError('Access denied for OAuth request', $error, $error_description)); - }else if ($error == 'invalid_client'){ - throw new Simplify_AuthenticationException("", $status, $this->buildOauthError('Invalid client ID in OAuth request', $error, $error_description)); - }else if ($error == 'unauthorized_client'){ - throw new Simplify_AuthenticationException("", $status, $this->buildOauthError('Unauthorized client in OAuth request', $error, $error_description)); - }else{ - throw new Simplify_AuthenticationException("", $status, $this->buildOauthError('Unknown authentication error', $error, $error_description)); - } - - } else if ($status < 500) { - throw new Simplify_BadRequestException("Bad request", $status, $object); - } - throw new Simplify_SystemException("An unexpected error has been raised. Looks like there's something wrong at our end." , $status, $object); - } - - public function jwsDecode($authentication, $hash) - { - if ($authentication->publicKey == null) { - throw new InvalidArgumentException('Must have a valid public key to connect to the API'); - } - - if ($authentication->privateKey == null) { - throw new InvalidArgumentException('Must have a valid API key to connect to the API'); - } - - if (!isset($hash['payload'])) { - throw new InvalidArgumentException('Event data is Missing payload'); - } - $payload = trim($hash['payload']); - - - try { - $parts = explode('.', $payload); - if (count($parts) != 3) { - $this->jwsAuthError("Incorrectly formatted JWS message"); - } - - $headerStr = $this->jwsUrlSafeDecode64($parts[0]); - $bodyStr = $this->jwsUrlSafeDecode64($parts[1]); - $sigStr = $parts[2]; - - $url = null; - if (isset($hash['url'])) { - $url = $hash['url']; - } - $this->jwsVerifyHeader($headerStr, $url, $authentication->publicKey); - - $msg = $parts[0] . "." . $parts[1]; - if (!$this->jwsVerifySignature($authentication->privateKey, $msg, $sigStr)) { - $this->jwsAuthError("JWS signature does not match"); - } - - return $bodyStr; - - } catch (ApiException $e) { - throw $e; - } catch (Exception $e) { - $this->jwsAuthError("Exception during JWS decoding: " . $e); - } - } - - private function jwsEncode($authentication, $url, $payload, $hasPayload) - { - // TODO - better seeding of RNG - $jws_hdr = array('typ' => self::JWS_TYPE, - 'alg' => self::JWS_ALGORITHM, - 'kid' => $authentication->publicKey, - self::JWS_HDR_URI => $url, - self::JWS_HDR_TIMESTAMP => sprintf("%u000", round(microtime(true))), - self::JWS_HDR_NONCE => sprintf("%u", mt_rand()), - ); - - // add oauth token if provided - if ( !empty($authentication->accessToken) ){ - $jws_hdr[self::JWS_HDR_TOKEN] = $authentication->accessToken; - } - - $header = $this->jwsUrlSafeEncode64(json_encode($jws_hdr)); - - if ($hasPayload) { - $payload = $this->jwsUrlSafeEncode64($payload); - } else { - $payload = ''; - } - - $msg = $header . "." . $payload; - return $msg . "." . $this->jwsSign($authentication->privateKey, $msg); - } - - private function jwsSign($privateKey, $msg) { - $decodedPrivateKey = $this->jwsUrlSafeDecode64($privateKey); - $sig = hash_hmac('sha256', $msg, $decodedPrivateKey, true); - - return $this->jwsUrlSafeEncode64($sig); - } - - private function jwsVerifyHeader($header, $url, $publicKey) { - - $hdr = json_decode($header, true); - - if (count($hdr) != self::JWS_NUM_HEADERS) { - $this->jwsAuthError("Incorrect number of JWS header parameters - found " . count($hdr) . " required " . self::JWS_NUM_HEADERS); - } - - if ($hdr['alg'] != self::JWS_ALGORITHM) { - $this->jwsAuthError("Incorrect algorithm - found " . $hdr['alg'] . " required " . self::WS_ALGORITHM); - } - - if ($hdr['typ'] != self::JWS_TYPE) { - $this->jwsAuthError("Incorrect type - found " . $hdr['typ'] . " required " . self::JWS_TYPE); - } - - if ($hdr['kid'] == null) { - $this->jwsAuthError("Missing Key ID"); - } - - if ($hdr['kid'] != $publicKey) { - if ($this->isLiveKey($publicKey)) { - $this->jwsAuthError("Invalid Key ID"); - } - } - - if ($hdr[self::JWS_HDR_URI] == null) { - $this->jwsAuthError("Missing URI"); - } - - if ($url != null && $hdr[self::JWS_HDR_URI] != $url) { - $this->jwsAuthError("Incorrect URL - found " . $hdr[self::JWS_HDR_URI] . " required " . $url); - } - - - if ($hdr[self::JWS_HDR_TIMESTAMP] == null) { - $this->jwsAuthError("Missing timestamp"); - } - - if (!$this->jwsVerifyTimestamp($hdr[self::JWS_HDR_TIMESTAMP])) { - $this->jwsAuthError("Invalid timestamp"); - } - - if ($hdr[self::JWS_HDR_NONCE] == null) { - $this->jwsAuthError("Missing nonce"); - } - - if ($hdr[self::JWS_HDR_UNAME] == null) { - $this->jwsAuthError("Missing username"); - } - } - - - private function jwsVerifySignature($privateKey, $msg, $expectedSig) { - return $this->jwsSign($privateKey, $msg) == $expectedSig; - } - - private function jwsAuthError($reason) { - throw new Simplify_AuthenticationException("JWS authentication failure: " . $reason); - } - - private function jwsVerifyTimestamp($ts) { - $now = round(microtime(true)); // Seconds - return abs($now - $ts / 1000) < self::JWS_MAX_TIMESTAMP_DIFF; - } - - private function isLiveKey($k) { - return strpos($k, "lvpb") === 0; - } - - private function jwsUrlSafeEncode64($s) { - return str_replace(array('+', '/', '='), - array('-', '_', ''), - base64_encode($s)); - } - - private function jwsUrlSafeDecode64($s) { - - switch (strlen($s) % 4) { - case 0: break; - case 2: $s = $s . "=="; - break; - case 3: $s = $s . "="; - break; - default: throw new InvalidArgumentException('incorrectly formatted JWS payload'); - } - return base64_decode(str_replace(array('-', '_'), array('+', '/'), $s)); - } - - private function buildOauthError($msg, $error, $error_description){ - - return array( - 'error' => array( - 'code' => 'oauth_error', - 'message' => $msg.', error code: '.$error.', description: '.$error_description.'' - ) - ); - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Invoice.php b/includes/gateways/simplify-commerce/includes/Simplify/Invoice.php deleted file mode 100644 index afdbcc59172..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Invoice.php +++ /dev/null @@ -1,228 +0,0 @@ - - *
billingAddress.city
Billing address city of the location where the goods or services were supplied. [max length: 255, min length: 2]
- *
billingAddress.country
Billing address country of the location where the goods or services were supplied. [max length: 2, min length: 2]
- *
billingAddress.line1
Billing address line 1 of the location where the goods or services were supplied. [max length: 255]
- *
billingAddress.line2
Billing address line 2 of the location where the goods or services were supplied. [max length: 255]
- *
billingAddress.name
Billing address name of the location where the goods or services were supplied. Will use the customer name if not provided. [max length: 255]
- *
billingAddress.state
Billing address state of the location where the goods or services were supplied. [max length: 255]
- *
billingAddress.zip
Billing address zip of the location where the goods or services were supplied. [max length: 32]
- *
businessAddress.city
Address city of the business that is sending the invoice. [max length: 255, min length: 2]
- *
businessAddress.country
Address country of the business that is sending the invoice. [max length: 2, min length: 2]
- *
businessAddress.line1
Address line 1 of the business that is sending the invoice. [max length: 255]
- *
businessAddress.line2
Address line 2 of the business that is sending the invoice. [max length: 255]
- *
businessAddress.name
The name of the business that is sending the invoice. [max length: 255]
- *
businessAddress.state
Address state of the business that is sending the invoice. [max length: 255]
- *
businessAddress.zip
Address zip of the business that is sending the invoice. [max length: 32]
- *
currency
Currency code (ISO-4217). Must match the currency associated with your account. [max length: 3, min length: 3, default: USD]
- *
customer
The customer ID of the customer we are invoicing. This is optional if invoiceToCopy or a name and email are provided
- *
customerTaxNo
The tax number or VAT id of the person to whom the goods or services were supplied. [max length: 255]
- *
discountRate
The discount percent as a decimal e.g. 12.5. This is used to calculate the discount amount which is subtracted from the total amount due before any tax is applied. [max length: 6]
- *
dueDate
The date invoice payment is due. If a late fee is provided this will be added to the invoice total is the due date has past.
- *
email
The email of the customer we are invoicing. This is optional if customer or invoiceToCopy is provided. A new customer will be created using the the name and email.
- *
invoiceId
User defined invoice id. If not provided the system will generate a numeric id. [max length: 255]
- *
invoiceToCopy
The id of an existing invoice to be copied. This is optional if customer or a name and email are provided
- *
items.amount
Amount of the invoice item (the smallest unit of your currency). Example: 100 = $1.00USD [min value: -9999900, max value: 9999900] required
- *
items.description
The description of the invoice item. [max length: 1024]
- *
items.invoice
The ID of the invoice this item belongs to.
- *
items.product
The product this invoice item refers to.
- *
items.quantity
Quantity of the item. This total amount of the invoice item is the amount * quantity. [min value: 1, max value: 999999, default: 1]
- *
items.reference
User defined reference field. [max length: 255]
- *
items.tax
The tax ID of the tax charge in the invoice item.
- *
lateFee
The late fee amount that will be added to the invoice total is the due date is past due. Value provided must be in the smallest unit of your currency. Example: 100 = $1.00USD [max value: 9999900]
- *
memo
A memo that is displayed to the customer on the invoice payment screen. [max length: 4000]
- *
name
The name of the customer we are invoicing. This is optional if customer or invoiceToCopy is provided. A new customer will be created using the the name and email. [max length: 50, min length: 2]
- *
note
This field can be used to store a note that is not displayed to the customer. [max length: 4000]
- *
reference
User defined reference field. [max length: 255]
- *
shippingAddress.city
Address city of the location where the goods or services were supplied. [max length: 255, min length: 2]
- *
shippingAddress.country
Address country of the location where the goods or services were supplied. [max length: 2, min length: 2]
- *
shippingAddress.line1
Address line 1 of the location where the goods or services were supplied. [max length: 255]
- *
shippingAddress.line2
Address line 2 of the location where the goods or services were supplied. [max length: 255]
- *
shippingAddress.name
Address name of the location where the goods or services were supplied. [max length: 255]
- *
shippingAddress.state
Address state of the location where the goods or services were supplied. [max length: 255]
- *
shippingAddress.zip
Address zip of the location where the goods or services were supplied. [max length: 32]
- *
suppliedDate
The date on which the goods or services were supplied.
- *
taxNo
The tax number or VAT id of the person who supplied the goods or services. [max length: 255]
- *
type
The type of invoice. One of WEB or MOBILE. [valid values: WEB, MOBILE, default: WEB]
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Invoice a Invoice object. - */ - static public function createInvoice($hash, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $instance = new Simplify_Invoice(); - $instance->setAll($hash); - - $object = Simplify_PaymentsApi::createObject($instance, $authentication); - return $object; - } - - - /** - * Deletes an Simplify_Invoice object. - * - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * - * @return true - */ - public function deleteInvoice($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $obj = Simplify_PaymentsApi::deleteObject($this, $authentication); - $this->properties = null; - return true; - } - - - /** - * Retrieve Simplify_Invoice objects. - * @param array criteria a map of parameters; valid keys are:
- *
filter
Filters to apply to the list.
- *
max
Allows up to a max of 50 list items to return. [min value: 0, max value: 50, default: 20]
- *
offset
Used in paging of the list. This is the start offset of the page. [min value: 0, default: 0]
- *
sorting
Allows for ascending or descending sorting of the list. The value maps properties to the sort direction (either asc for ascending or desc for descending). Sortable properties are: id invoiceDate dueDate datePaid customer status dateCreated.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return ResourceList a ResourceList object that holds the list of Invoice objects and the total - * number of Invoice objects available for the given criteria. - * @see ResourceList - */ - static public function listInvoice($criteria = null, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Invoice(); - $list = Simplify_PaymentsApi::listObject($val, $criteria, $authentication); - - return $list; - } - - - /** - * Retrieve a Simplify_Invoice object from the API - * - * @param string id the id of the Invoice object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Invoice a Invoice object - */ - static public function findInvoice($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Invoice(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - - /** - * Updates an Simplify_Invoice object. - * - * The properties that can be updated: - *
- *
billingAddress.city
Billing address city of the location where the goods or services were supplied. [max length: 255, min length: 2]
- *
billingAddress.country
Billing address country of the location where the goods or services were supplied. [max length: 2, min length: 2]
- *
billingAddress.line1
Billing address line 1 of the location where the goods or services were supplied. [max length: 255]
- *
billingAddress.line2
Billing address line 2 of the location where the goods or services were supplied. [max length: 255]
- *
billingAddress.name
Billing address name of the location where the goods or services were supplied. [max length: 255]
- *
billingAddress.state
Billing address state of the location where the goods or services were supplied. [max length: 255]
- *
billingAddress.zip
Billing address zip of the location where the goods or services were supplied. [max length: 32]
- *
businessAddress.city
Business address city of the business that is sending the invoice. [max length: 255, min length: 2]
- *
businessAddress.country
Business address country of the business that is sending the invoice. [max length: 2, min length: 2]
- *
businessAddress.line1
Business address line 1 of the business that is sending the invoice. [max length: 255]
- *
businessAddress.line2
Business address line 2 of the business that is sending the invoice. [max length: 255]
- *
businessAddress.name
Business address name of the business that is sending the invoice. [max length: 255]
- *
businessAddress.state
Business address state of the business that is sending the invoice. [max length: 255]
- *
businessAddress.zip
Business address zip of the business that is sending the invoice. [max length: 32]
- *
currency
Currency code (ISO-4217). Must match the currency associated with your account. [max length: 3, min length: 3]
- *
customerTaxNo
The tax number or VAT id of the person to whom the goods or services were supplied. [max length: 255]
- *
datePaid
This is the date the invoice was PAID in UTC millis.
- *
discountRate
The discount percent as a decimal e.g. 12.5. This is used to calculate the discount amount which is subtracted from the total amount due before any tax is applied. [max length: 6]
- *
dueDate
The date invoice payment is due. If a late fee is provided this will be added to the invoice total is the due date has past.
- *
email
The email of the customer we are invoicing. This is optional if customer or invoiceToCopy is provided. A new customer will be created using the the name and email.
- *
invoiceId
User defined invoice id. If not provided the system will generate a numeric id. [max length: 255]
- *
items.amount
Amount of the invoice item in the smallest unit of your currency. Example: 100 = $1.00USD [min value: -9999900, max value: 9999900] required
- *
items.description
The description of the invoice item. [max length: 1024]
- *
items.invoice
The ID of the invoice this item belongs to.
- *
items.product
The Id of the product this item refers to.
- *
items.quantity
Quantity of the item. This total amount of the invoice item is the amount * quantity. [min value: 1, max value: 999999, default: 1]
- *
items.reference
User defined reference field. [max length: 255]
- *
items.tax
The tax ID of the tax charge in the invoice item.
- *
lateFee
The late fee amount that will be added to the invoice total is the due date is past due. Value provided must be in the smallest unit of your currency. Example: 100 = $1.00USD [max value: 9999900]
- *
memo
A memo that is displayed to the customer on the invoice payment screen. [max length: 4000]
- *
name
The name of the customer we are invoicing. This is optional if customer or invoiceToCopy is provided. A new customer will be created using the the name and email. [max length: 50, min length: 2]
- *
note
This field can be used to store a note that is not displayed to the customer. [max length: 4000]
- *
payment
The ID of the payment. Use this ID to query the /payment API. [max length: 255]
- *
reference
User defined reference field. [max length: 255]
- *
shippingAddress.city
Address city of the location where the goods or services were supplied. [max length: 255, min length: 2]
- *
shippingAddress.country
Address country of the location where the goods or services were supplied. [max length: 2, min length: 2]
- *
shippingAddress.line1
Address line 1 of the location where the goods or services were supplied. [max length: 255]
- *
shippingAddress.line2
Address line 2 of the location where the goods or services were supplied. [max length: 255]
- *
shippingAddress.name
Address name of the location where the goods or services were supplied. [max length: 255]
- *
shippingAddress.state
Address state of the location where the goods or services were supplied. [max length: 255]
- *
shippingAddress.zip
Address zip of the location where the goods or services were supplied. [max length: 32]
- *
status
New status of the invoice.
- *
suppliedDate
The date on which the goods or services were supplied.
- *
taxNo
The tax number or VAT id of the person who supplied the goods or services. [max length: 255]
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Invoice a Invoice object. - */ - public function updateInvoice($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $object = Simplify_PaymentsApi::updateObject($this, $authentication); - return $object; - } - - /** - * @ignore - */ - public function getClazz() { - return "Invoice"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/InvoiceItem.php b/includes/gateways/simplify-commerce/includes/Simplify/InvoiceItem.php deleted file mode 100644 index 9c7d9b8c053..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/InvoiceItem.php +++ /dev/null @@ -1,128 +0,0 @@ - - *
amount
Amount of the invoice item in the smallest unit of your currency. Example: 100 = $1.00USD [min value: -9999900, max value: 9999900] required
- *
description
Individual items of an invoice [max length: 1024]
- *
invoice
The ID of the invoice this item belongs to.
- *
product
Product ID this item relates to.
- *
quantity
Quantity of the item. This total amount of the invoice item is the amount * quantity. [min value: 1, max value: 999999, default: 1]
- *
reference
User defined reference field. [max length: 255]
- *
tax
The tax ID of the tax charge in the invoice item.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return InvoiceItem a InvoiceItem object. - */ - static public function createInvoiceItem($hash, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $instance = new Simplify_InvoiceItem(); - $instance->setAll($hash); - - $object = Simplify_PaymentsApi::createObject($instance, $authentication); - return $object; - } - - - /** - * Deletes an Simplify_InvoiceItem object. - * - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * - * @return true - */ - public function deleteInvoiceItem($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $obj = Simplify_PaymentsApi::deleteObject($this, $authentication); - $this->properties = null; - return true; - } - - - /** - * Retrieve a Simplify_InvoiceItem object from the API - * - * @param string id the id of the InvoiceItem object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return InvoiceItem a InvoiceItem object - */ - static public function findInvoiceItem($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_InvoiceItem(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - - /** - * Updates an Simplify_InvoiceItem object. - * - * The properties that can be updated: - *
- *
amount
Amount of the invoice item in the smallest unit of your currency. Example: 100 = $1.00USD [min value: 1]
- *
description
Individual items of an invoice
- *
quantity
Quantity of the item. This total amount of the invoice item is the amount * quantity. [min value: 1, max value: 999999]
- *
reference
User defined reference field.
- *
tax
The tax ID of the tax charge in the invoice item.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return InvoiceItem a InvoiceItem object. - */ - public function updateInvoiceItem($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $object = Simplify_PaymentsApi::updateObject($this, $authentication); - return $object; - } - - /** - * @ignore - */ - public function getClazz() { - return "InvoiceItem"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Object.php b/includes/gateways/simplify-commerce/includes/Simplify/Object.php deleted file mode 100644 index f7fc4e42820..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Object.php +++ /dev/null @@ -1,90 +0,0 @@ -properties)) { - return $this->properties[$key]; - } else { - return null; - } - } - - /** - * @ignore - * - * @param string $key - * @param mixed $value - */ - public function __set($key, $value) { - $this->properties[$key] = $value; - } - - /** - * Updates the object's properties with the values in the specified map. - * @param $hash array Map of values to set. - */ - public function setAll($hash) { - foreach ($hash as $key => $value) { - $this->$key = $value; - } - } - - /** - * @ignore - */ - public function __toString() { - return json_encode($this->properties); - } - - /** - * Returns the object's properties as a map. - * @return array map of properties. - */ - public function getProperties() { - return $this->properties; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Payment.php b/includes/gateways/simplify-commerce/includes/Simplify/Payment.php deleted file mode 100644 index 44f616992b3..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Payment.php +++ /dev/null @@ -1,145 +0,0 @@ - - *
amount
Amount of the payment (in the smallest unit of your currency). Example: 100 = $1.00USD
- *
authorization
The ID of the authorization being used to capture the payment.
- *
card.addressCity
City of the cardholder. [max length: 50, min length: 2]
- *
card.addressCountry
Country code (ISO-3166-1-alpha-2 code) of residence of the cardholder. [max length: 2, min length: 2]
- *
card.addressLine1
Address of the cardholder. [max length: 255]
- *
card.addressLine2
Address of the cardholder if needed. [max length: 255]
- *
card.addressState
State of residence of the cardholder. For the US, this is a 2-digit USPS code. [max length: 255, min length: 2]
- *
card.addressZip
Postal code of the cardholder. The postal code size is between 5 and 9 in length and only contain numbers or letters. [max length: 9, min length: 3]
- *
card.cvc
CVC security code of the card. This is the code on the back of the card. Example: 123
- *
card.expMonth
Expiration month of the card. Format is MM. Example: January = 01 [min value: 1, max value: 12] required
- *
card.expYear
Expiration year of the card. Format is YY. Example: 2013 = 13 [min value: 0, max value: 99] required
- *
card.name
Name as it appears on the card. [max length: 50, min length: 2]
- *
card.number
Card number as it appears on the card. [max length: 19, min length: 13] required
- *
currency
Currency code (ISO-4217) for the transaction. Must match the currency associated with your account. [default: USD] required
- *
customer
ID of customer. If specified, card on file of customer will be used.
- *
description
Free form text field to be used as a description of the payment. This field is echoed back with the payment on any find or list operations. [max length: 1024]
- *
invoice
ID of invoice for which this payment is being made.
- *
reference
Custom reference field to be used with outside systems.
- *
replayId
An identifier that can be sent to uniquely identify a payment request to facilitate retries due to I/O related issues. This identifier must be unique for your account (sandbox or live) across all of your payments. If supplied, we will check for a payment on your account that matches this identifier. If found will attempt to return an identical response of the original request. [max length: 50, min length: 1]
- *
statementDescription.name
Merchant name. required
- *
statementDescription.phoneNumber
Merchant contact phone number.
- *
token
If specified, card associated with card token will be used. [max length: 255]
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Payment a Payment object. - */ - static public function createPayment($hash, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $instance = new Simplify_Payment(); - $instance->setAll($hash); - - $object = Simplify_PaymentsApi::createObject($instance, $authentication); - return $object; - } - - - - /** - * Retrieve Simplify_Payment objects. - * @param array criteria a map of parameters; valid keys are:
- *
filter
Filters to apply to the list.
- *
max
Allows up to a max of 50 list items to return. [min value: 0, max value: 50, default: 20]
- *
offset
Used in paging of the list. This is the start offset of the page. [min value: 0, default: 0]
- *
sorting
Allows for ascending or descending sorting of the list. The value maps properties to the sort direction (either asc for ascending or desc for descending). Sortable properties are: dateCreated createdBy amount id description paymentDate.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return ResourceList a ResourceList object that holds the list of Payment objects and the total - * number of Payment objects available for the given criteria. - * @see ResourceList - */ - static public function listPayment($criteria = null, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Payment(); - $list = Simplify_PaymentsApi::listObject($val, $criteria, $authentication); - - return $list; - } - - - /** - * Retrieve a Simplify_Payment object from the API - * - * @param string id the id of the Payment object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Payment a Payment object - */ - static public function findPayment($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Payment(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - - /** - * Updates an Simplify_Payment object. - * - * The properties that can be updated: - *
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Payment a Payment object. - */ - public function updatePayment($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $object = Simplify_PaymentsApi::updateObject($this, $authentication); - return $object; - } - - /** - * @ignore - */ - public function getClazz() { - return "Payment"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/PaymentsApi.php b/includes/gateways/simplify-commerce/includes/Simplify/PaymentsApi.php deleted file mode 100644 index 3abd7615cae..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/PaymentsApi.php +++ /dev/null @@ -1,358 +0,0 @@ - 'POST', - 'delete' => 'DELETE', - 'list' => 'GET', - 'show' => 'GET', - 'update' => 'PUT' - ); - - /** - * @ignore - * - * @param object $object - * @param object $authentication - * - * @return mixed - */ - static public function createObject($object, $authentication = null) - { - $paymentsApi = new Simplify_PaymentsApi(); - - $jsonObject = $paymentsApi->execute("create", $object, $authentication); - - $o = $paymentsApi->convertFromHashToObject($jsonObject, $object->getClazz()); - - return $o; - } - - /** - * @ignore - * - * @param object $object - * @param object $authentication - * - * @return mixed - */ - static public function findObject($object, $authentication = null) - { - $paymentsApi = new Simplify_PaymentsApi(); - - $jsonObject = $paymentsApi->execute("show", $object, $authentication); - $o = $paymentsApi->convertFromHashToObject($jsonObject, $object->getClazz()); - - return $o; - } - - /** - * @ignore - * - * @param object $object - * @param object $authentication - * - * @return mixed - */ - static public function updateObject($object, $authentication = null) { - $paymentsApi = new Simplify_PaymentsApi(); - - $jsonObject = $paymentsApi->execute("update", $object, $authentication); - $o = $paymentsApi->convertFromHashToObject($jsonObject, $object->getClazz()); - - return $o; - } - - /** - * @ignore - * - * @param object $object - * @param object $authentication - * - * @return mixed - */ - static public function deleteObject($object, $authentication = null) { - $paymentsApi = new Simplify_PaymentsApi(); - - $jsonObject = $paymentsApi->execute("delete", $object, $authentication); - - return $jsonObject; - } - - /** - * @ignore - * - * @param object $object - * @param array $criteria - * @param object $authentication - * - * @return Simplify_ResourceList - */ - static public function listObject($object, $criteria = null, $authentication = null) { - if ($criteria != null) { - if (isset($criteria['max'])) { - $object->max = $criteria['max']; - } - if (isset($criteria['offset'])) { - $object->offset = $criteria['offset']; - } - if (isset($criteria['sorting'])) { - $object->sorting = $criteria['sorting']; - } - if (isset($criteria['filter'])) { - $object->filter = $criteria['filter']; - } - } - - $paymentsApi = new Simplify_PaymentsApi(); - $jsonObject = $paymentsApi->execute("list", $object, $authentication); - - $ret = new Simplify_ResourceList(); - if (array_key_exists('list', $jsonObject) & is_array($jsonObject['list'])) { - foreach ($jsonObject['list'] as $obj) { - array_push($ret->list, $paymentsApi->convertFromHashToObject($obj, $object->getClazz())); - } - $ret->total = $jsonObject['total']; - } - - return $ret; - } - - /** - * @ignore - * - * @param array $from - * @param string $toClazz - * - * @return mixed - */ - public function convertFromHashToObject($from, $toClazz) - { - $clazz = 'stdClass'; - $toClazz = "Simplify_" . $toClazz; - if ("stdClass" != $toClazz && class_exists("{$toClazz}", false)) { - $clazz = "{$toClazz}"; - } - $object = new $clazz(); - - foreach ($from as $key => $value) { - if (is_array($value) && count(array_keys($value))) { - $newClazz = "Simplify_" . ucfirst($key); - if (!class_exists($newClazz, false)) { - $newClazz = 'stdClass'; - } - - $object->$key = $this->convertFromHashToObject($value, $newClazz); - } else { - $object->$key = $value; - } - } - - return $object; - } - - /** - * @ignore - * - * @param string $publicKey - * @param string $action - * @param object $object - * - * @return string - */ - public function getUrl($publicKey, $action, $object) - { - $url = $this->fixUrl(Simplify::$apiBaseSandboxUrl); - if ($this->isLiveKey($publicKey)) { - $url = $this->fixUrl(Simplify::$apiBaseLiveUrl); - } - $url = $this->fixUrl($url) . urlencode(lcfirst($object->getClazz())) . '/'; - - $queryParams = array(); - if ( 'show' == $action ) { - $url .= urlencode($object->id); - } elseif ( 'list' == $action ) { - $queryParams = array_merge($queryParams, array('max' => $object->max, 'offset' => $object->offset)); - if (is_array($object->filter) && count(array_keys($object->filter))) { - foreach ($object->filter as $key => $value) { - $queryParams["filter[$key]"] = $value; - } - } - if (is_array($object->sorting) && count(array_keys($object->sorting))) { - foreach ($object->sorting as $key => $value) { - $queryParams["sorting[$key]"] = $value; - } - } - $query = http_build_query($queryParams); - if ($query != '') { - if (strpos($url, '?', strlen($url)) === false) $url .= '?'; - $url .= $query; - } - - } elseif ( 'delete' == $action ) { - $url .= urlencode($object->id); - } elseif ( 'update' == $action ) { - $url .= urlencode($object->id); - } elseif ( 'create' == $action ) { - } - return $url; - } - - /** - * @ignore - * - * @param string $action - * - * @return string - */ - public function getMethod($action) - { - if (array_key_exists(strtolower($action), self::$methodMap)) { - return self::$methodMap[strtolower($action)]; - } - return 'GET'; - } - - /** - * @ignore - * - * @param string $action - * @param object $object - * @param object $authentication - * - * @return mixed - */ - private function execute($action, $object, $authentication) - { - $http = new Simplify_HTTP(); - - return $http->apiRequest($this->getUrl($authentication->publicKey, $action, $object), $this->getMethod($action), - $authentication, json_encode($object->getProperties())); - } - - /** - * @ignore - * - * @param string $hash - * @param object $authentication - * - * @return mixed - */ - public function jwsDecode($hash, $authentication) - { - $http = new Simplify_HTTP(); - - $data = $http->jwsDecode($authentication, $hash); - - return json_decode($data, true); - } - - /** - * @ignore - * - * @param string $url - * - * @return string - */ - private function fixUrl($url) - { - if ($this->endsWith($url, '/')) { - return $url; - } - return $url . '/'; - } - - /** - * @ignore - * - * @param string $k - * - * @return bool - */ - private function isLiveKey($k) { - return strpos($k, "lvpb") === 0; - } - - /** - * @ignore - * - * @param string $s - * @param string $c - * - * @return bool - */ - private function endsWith($s, $c) - { - return substr($s, -strlen($c)) == $c; - } - - /** - * Helper function to build the Authentication object for backwards compatibility. - * An array of all the arguments passed to one of the API functions is checked against what - * we expect to received. If it's greater, then we're assuming that the user is using the older way of - * passing the keys. i.e as two separate strings. We take those two string and create the Authentication object - * - * @ignore - * @param $authentication - * @param $args - * @param $expectedArgCount - * @return Simplify_Authentication - */ - static function buildAuthenticationObject($authentication = null, $args, $expectedArgCount){ - - if(sizeof($args) > $expectedArgCount) { - $authentication = new Simplify_Authentication($args[$expectedArgCount-1], $args[$expectedArgCount]); - } - - if ($authentication == null){ - $authentication = new Simplify_Authentication(); - } - - // check that the keys have been set, if not use the global keys - if ( empty($authentication->publicKey)){ - $authentication->publicKey = Simplify::$publicKey; - } - if ( empty($authentication->privateKey)){ - $authentication->privateKey = Simplify::$privateKey; - } - - return $authentication; - } - -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Plan.php b/includes/gateways/simplify-commerce/includes/Simplify/Plan.php deleted file mode 100644 index 8f11103a098..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Plan.php +++ /dev/null @@ -1,151 +0,0 @@ - - *
amount
Amount of payment for the plan in the smallest unit of your currency. Example: 100 = $1.00USD required
- *
billingCycle
How the plan is billed to the customer. Values must be AUTO (indefinitely until the customer cancels) or FIXED (a fixed number of billing cycles). [default: AUTO]
- *
billingCycleLimit
The number of fixed billing cycles for a plan. Only used if the billingCycle parameter is set to FIXED. Example: 4
- *
currency
Currency code (ISO-4217) for the plan. Must match the currency associated with your account. [default: USD] required
- *
frequency
Frequency of payment for the plan. Used in conjunction with frequencyPeriod. Valid values are "DAILY", "WEEKLY", "MONTHLY" and "YEARLY". [default: MONTHLY] required
- *
frequencyPeriod
Period of frequency of payment for the plan. Example: if the frequency is weekly, and periodFrequency is 2, then the subscription is billed bi-weekly. [min value: 1, default: 1] required
- *
name
Name of the plan [max length: 50, min length: 2] required
- *
renewalReminderLeadDays
If set, how many days before the next billing cycle that a renewal reminder is sent to the customer. If null, then no emails are sent. Minimum value is 7 if set.
- *
trialPeriod
Plan free trial period selection. Must be Days, Weeks, or Month [default: NONE] required
- *
trialPeriodQuantity
Quantity of the trial period. Must be greater than 0 and a whole number. [min value: 1]
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Plan a Plan object. - */ - static public function createPlan($hash, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $instance = new Simplify_Plan(); - $instance->setAll($hash); - - $object = Simplify_PaymentsApi::createObject($instance, $authentication); - return $object; - } - - - /** - * Deletes an Simplify_Plan object. - * - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * - * @return true - */ - public function deletePlan($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $obj = Simplify_PaymentsApi::deleteObject($this, $authentication); - $this->properties = null; - return true; - } - - - /** - * Retrieve Simplify_Plan objects. - * @param array criteria a map of parameters; valid keys are:
- *
filter
Filters to apply to the list.
- *
max
Allows up to a max of 50 list items to return. [min value: 0, max value: 50, default: 20]
- *
offset
Used in paging of the list. This is the start offset of the page. [min value: 0, default: 0]
- *
sorting
Allows for ascending or descending sorting of the list. The value maps properties to the sort direction (either asc for ascending or desc for descending). Sortable properties are: dateCreated amount frequency name id.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return ResourceList a ResourceList object that holds the list of Plan objects and the total - * number of Plan objects available for the given criteria. - * @see ResourceList - */ - static public function listPlan($criteria = null, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Plan(); - $list = Simplify_PaymentsApi::listObject($val, $criteria, $authentication); - - return $list; - } - - - /** - * Retrieve a Simplify_Plan object from the API - * - * @param string id the id of the Plan object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Plan a Plan object - */ - static public function findPlan($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Plan(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - - /** - * Updates an Simplify_Plan object. - * - * The properties that can be updated: - *
- *
name
Name of the plan. [min length: 2] required
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Plan a Plan object. - */ - public function updatePlan($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $object = Simplify_PaymentsApi::updateObject($this, $authentication); - return $object; - } - - /** - * @ignore - */ - public function getClazz() { - return "Plan"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Refund.php b/includes/gateways/simplify-commerce/includes/Simplify/Refund.php deleted file mode 100644 index 46c22ecfb87..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Refund.php +++ /dev/null @@ -1,112 +0,0 @@ - - *
amount
Amount of the refund in the smallest unit of your currency. Example: 100 = $1.00USD [min value: 1] required
- *
payment
ID of the payment for the refund required
- *
reason
Reason for the refund
- *
reference
Custom reference field to be used with outside systems.
- *
replayId
An identifier that can be sent to uniquely identify a refund request to facilitate retries due to I/O related issues. This identifier must be unique for your account (sandbox or live) across all of your refunds. If supplied, we will check for a refund on your account that matches this identifier. If found we will return an identical response to that of the original request. [max length: 50, min length: 1]
- *
statementDescription.name
Merchant name. required
- *
statementDescription.phoneNumber
Merchant contact phone number.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Refund a Refund object. - */ - static public function createRefund($hash, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $instance = new Simplify_Refund(); - $instance->setAll($hash); - - $object = Simplify_PaymentsApi::createObject($instance, $authentication); - return $object; - } - - - - /** - * Retrieve Simplify_Refund objects. - * @param array criteria a map of parameters; valid keys are:
- *
filter
Filters to apply to the list.
- *
max
Allows up to a max of 50 list items to return. [min value: 0, max value: 50, default: 20]
- *
offset
Used in paging of the list. This is the start offset of the page. [min value: 0, default: 0]
- *
sorting
Allows for ascending or descending sorting of the list. The value maps properties to the sort direction (either asc for ascending or desc for descending). Sortable properties are: id amount description dateCreated paymentDate.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return ResourceList a ResourceList object that holds the list of Refund objects and the total - * number of Refund objects available for the given criteria. - * @see ResourceList - */ - static public function listRefund($criteria = null, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Refund(); - $list = Simplify_PaymentsApi::listObject($val, $criteria, $authentication); - - return $list; - } - - - /** - * Retrieve a Simplify_Refund object from the API - * - * @param string id the id of the Refund object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Refund a Refund object - */ - static public function findRefund($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Refund(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - /** - * @ignore - */ - public function getClazz() { - return "Refund"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/ResourceList.php b/includes/gateways/simplify-commerce/includes/Simplify/ResourceList.php deleted file mode 100644 index 7c31002fe96..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/ResourceList.php +++ /dev/null @@ -1,48 +0,0 @@ -() methods. - */ -class Simplify_ResourceList { - - /** - * @var array $list the list of domain objects. - */ - public $list = array(); - - /** - * @var int $total the total number of object available. - */ - public $total = 0; -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Subscription.php b/includes/gateways/simplify-commerce/includes/Simplify/Subscription.php deleted file mode 100644 index 91d33005fa5..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Subscription.php +++ /dev/null @@ -1,164 +0,0 @@ - - *
amount
Amount of the payment in the smallest unit of your currency. Example: 100 = $1.00USD
- *
billingCycle
How the plan is billed to the customer. Values must be AUTO (indefinitely until the customer cancels) or FIXED (a fixed number of billing cycles). [default: AUTO]
- *
billingCycleLimit
The number of fixed billing cycles for a plan. Only used if the billingCycle parameter is set to FIXED. Example: 4
- *
coupon
Coupon ID associated with the subscription
- *
currency
Currency code (ISO-4217). Must match the currency associated with your account. [default: USD]
- *
customer
Customer that is enrolling in the subscription.
- *
frequency
Frequency of payment for the plan. Used in conjunction with frequencyPeriod. Valid values are "DAILY", "WEEKLY", "MONTHLY" and "YEARLY".
- *
frequencyPeriod
Period of frequency of payment for the plan. Example: if the frequency is weekly, and periodFrequency is 2, then the subscription is billed bi-weekly.
- *
name
Name describing subscription
- *
plan
The ID of the plan that should be used for the subscription.
- *
quantity
Quantity of the plan for the subscription. [min value: 1]
- *
renewalReminderLeadDays
If set, how many days before the next billing cycle that a renewal reminder is sent to the customer. If null, then no emails are sent. Minimum value is 7 if set.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Subscription a Subscription object. - */ - static public function createSubscription($hash, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $instance = new Simplify_Subscription(); - $instance->setAll($hash); - - $object = Simplify_PaymentsApi::createObject($instance, $authentication); - return $object; - } - - - /** - * Deletes an Simplify_Subscription object. - * - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * - * @return true - */ - public function deleteSubscription($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $obj = Simplify_PaymentsApi::deleteObject($this, $authentication); - $this->properties = null; - return true; - } - - - /** - * Retrieve Simplify_Subscription objects. - * @param array criteria a map of parameters; valid keys are:
- *
filter
Filters to apply to the list.
- *
max
Allows up to a max of 50 list items to return. [min value: 0, max value: 50, default: 20]
- *
offset
Used in paging of the list. This is the start offset of the page. [min value: 0, default: 0]
- *
sorting
Allows for ascending or descending sorting of the list. The value maps properties to the sort direction (either asc for ascending or desc for descending). Sortable properties are: id plan.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return ResourceList a ResourceList object that holds the list of Subscription objects and the total - * number of Subscription objects available for the given criteria. - * @see ResourceList - */ - static public function listSubscription($criteria = null, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Subscription(); - $list = Simplify_PaymentsApi::listObject($val, $criteria, $authentication); - - return $list; - } - - - /** - * Retrieve a Simplify_Subscription object from the API - * - * @param string id the id of the Subscription object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Subscription a Subscription object - */ - static public function findSubscription($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Subscription(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - - /** - * Updates an Simplify_Subscription object. - * - * The properties that can be updated: - *
- *
amount
Amount of the payment in the smallest unit of your currency. Example: 100 = $1.00USD
- *
billingCycle
How the plan is billed to the customer. Values must be AUTO (indefinitely until the customer cancels) or FIXED (a fixed number of billing cycles). [default: AUTO]
- *
billingCycleLimit
The number of fixed billing cycles for a plan. Only used if the billingCycle parameter is set to FIXED. Example: 4
- *
coupon
Coupon being assigned to this subscription
- *
currency
Currency code (ISO-4217). Must match the currency associated with your account. [default: USD]
- *
frequency
Frequency of payment for the plan. Used in conjunction with frequencyPeriod. Valid values are "DAILY", "WEEKLY", "MONTHLY" and "YEARLY".
- *
frequencyPeriod
Period of frequency of payment for the plan. Example: if the frequency is weekly, and periodFrequency is 2, then the subscription is billed bi-weekly. [min value: 1]
- *
name
Name describing subscription
- *
plan
Plan that should be used for the subscription.
- *
prorate
Whether to prorate existing subscription. [default: true] required
- *
quantity
Quantity of the plan for the subscription. [min value: 1]
- *
renewalReminderLeadDays
If set, how many days before the next billing cycle that a renewal reminder is sent to the customer. If null or 0, no emails are sent. Minimum value is 7 if set.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Subscription a Subscription object. - */ - public function updateSubscription($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $object = Simplify_PaymentsApi::updateObject($this, $authentication); - return $object; - } - - /** - * @ignore - */ - public function getClazz() { - return "Subscription"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Tax.php b/includes/gateways/simplify-commerce/includes/Simplify/Tax.php deleted file mode 100644 index 0e504afd5bf..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Tax.php +++ /dev/null @@ -1,124 +0,0 @@ - - *
label
The label of the tax object. [max length: 255] required
- *
rate
The tax rate. Decimal value up three decimal places. e.g 12.501. [max length: 6] required
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Tax a Tax object. - */ - static public function createTax($hash, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $instance = new Simplify_Tax(); - $instance->setAll($hash); - - $object = Simplify_PaymentsApi::createObject($instance, $authentication); - return $object; - } - - - /** - * Deletes an Simplify_Tax object. - * - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * - * @return true - */ - public function deleteTax($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $obj = Simplify_PaymentsApi::deleteObject($this, $authentication); - $this->properties = null; - return true; - } - - - /** - * Retrieve Simplify_Tax objects. - * @param array criteria a map of parameters; valid keys are:
- *
filter
Filters to apply to the list.
- *
max
Allows up to a max of 50 list items to return. [min value: 0, max value: 50, default: 20]
- *
offset
Used in paging of the list. This is the start offset of the page. [min value: 0, default: 0]
- *
sorting
Allows for ascending or descending sorting of the list. The value maps properties to the sort direction (either asc for ascending or desc for descending). Sortable properties are: id label.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return ResourceList a ResourceList object that holds the list of Tax objects and the total - * number of Tax objects available for the given criteria. - * @see ResourceList - */ - static public function listTax($criteria = null, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Tax(); - $list = Simplify_PaymentsApi::listObject($val, $criteria, $authentication); - - return $list; - } - - - /** - * Retrieve a Simplify_Tax object from the API - * - * @param string id the id of the Tax object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Tax a Tax object - */ - static public function findTax($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Tax(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - /** - * @ignore - */ - public function getClazz() { - return "Tax"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/TransactionReview.php b/includes/gateways/simplify-commerce/includes/Simplify/TransactionReview.php deleted file mode 100644 index 29cd2187a3d..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/TransactionReview.php +++ /dev/null @@ -1,141 +0,0 @@ - - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return TransactionReview a TransactionReview object. - */ - static public function createTransactionReview($hash, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $instance = new Simplify_TransactionReview(); - $instance->setAll($hash); - - $object = Simplify_PaymentsApi::createObject($instance, $authentication); - return $object; - } - - - /** - * Deletes an Simplify_TransactionReview object. - * - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * - * @return true - */ - public function deleteTransactionReview($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $obj = Simplify_PaymentsApi::deleteObject($this, $authentication); - $this->properties = null; - return true; - } - - - /** - * Retrieve Simplify_TransactionReview objects. - * @param array criteria a map of parameters; valid keys are:
- *
filter
Allows for ascending or descending sorting of the list.
- *
max
Allows up to a max of 50 list items to return. [min value: 0, max value: 50, default: 20]
- *
offset
Filters to apply to the list. [min value: 0, default: 0]
- *
sorting
Used in paging of the list. This is the start offset of the page. The value maps properties to the sort direction (either asc for ascending or desc for descending). Sortable properties are: dateCreated status.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return ResourceList a ResourceList object that holds the list of TransactionReview objects and the total - * number of TransactionReview objects available for the given criteria. - * @see ResourceList - */ - static public function listTransactionReview($criteria = null, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_TransactionReview(); - $list = Simplify_PaymentsApi::listObject($val, $criteria, $authentication); - - return $list; - } - - - /** - * Retrieve a Simplify_TransactionReview object from the API - * - * @param string id the id of the TransactionReview object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return TransactionReview a TransactionReview object - */ - static public function findTransactionReview($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_TransactionReview(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - - /** - * Updates an Simplify_TransactionReview object. - * - * The properties that can be updated: - *
- *
status
Status of the transaction review.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return TransactionReview a TransactionReview object. - */ - public function updateTransactionReview($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $object = Simplify_PaymentsApi::updateObject($this, $authentication); - return $object; - } - - /** - * @ignore - */ - public function getClazz() { - return "TransactionReview"; - } -} diff --git a/includes/gateways/simplify-commerce/includes/Simplify/Webhook.php b/includes/gateways/simplify-commerce/includes/Simplify/Webhook.php deleted file mode 100644 index efb80f487f8..00000000000 --- a/includes/gateways/simplify-commerce/includes/Simplify/Webhook.php +++ /dev/null @@ -1,142 +0,0 @@ - - *
url
Endpoint URL required
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Webhook a Webhook object. - */ - static public function createWebhook($hash, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $instance = new Simplify_Webhook(); - $instance->setAll($hash); - - $object = Simplify_PaymentsApi::createObject($instance, $authentication); - return $object; - } - - - /** - * Deletes an Simplify_Webhook object. - * - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * - * @return true - */ - public function deleteWebhook($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $obj = Simplify_PaymentsApi::deleteObject($this, $authentication); - $this->properties = null; - return true; - } - - - /** - * Retrieve Simplify_Webhook objects. - * @param array criteria a map of parameters; valid keys are:
- *
filter
Filters to apply to the list.
- *
max
Allows up to a max of 50 list items to return. [min value: 0, max value: 50, default: 20]
- *
offset
Used in paging of the list. This is the start offset of the page. [min value: 0, default: 0]
- *
sorting
Allows for ascending or descending sorting of the list. The value maps properties to the sort direction (either asc for ascending or desc for descending). Sortable properties are: dateCreated.
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return ResourceList a ResourceList object that holds the list of Webhook objects and the total - * number of Webhook objects available for the given criteria. - * @see ResourceList - */ - static public function listWebhook($criteria = null, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Webhook(); - $list = Simplify_PaymentsApi::listObject($val, $criteria, $authentication); - - return $list; - } - - - /** - * Retrieve a Simplify_Webhook object from the API - * - * @param string id the id of the Webhook object to retrieve - * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Webhook a Webhook object - */ - static public function findWebhook($id, $authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 2); - - $val = new Simplify_Webhook(); - $val->id = $id; - - $obj = Simplify_PaymentsApi::findObject($val, $authentication); - - return $obj; - } - - - /** - * Updates an Simplify_Webhook object. - * - * The properties that can be updated: - *
- *
url
Endpoint URL required
- * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. - * @return Webhook a Webhook object. - */ - public function updateWebhook($authentication = null) { - - $args = func_get_args(); - $authentication = Simplify_PaymentsApi::buildAuthenticationObject($authentication, $args, 1); - - $object = Simplify_PaymentsApi::updateObject($this, $authentication); - return $object; - } - - /** - * @ignore - */ - public function getClazz() { - return "Webhook"; - } -} diff --git a/phpcs.xml b/phpcs.xml index f2b1419fb0c..18262bba637 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -5,7 +5,6 @@ tests/cli/ apigen/ - includes/gateways/simplify-commerce includes/libraries/ includes/legacy/ includes/api/legacy/ diff --git a/phpunit.xml b/phpunit.xml index 7412941c4e4..6779e6115a9 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -27,7 +27,6 @@ ./includes/admin/views ./includes/api/legacy ./includes/api/v1 - ./includes/gateways/simplify-commerce/includes ./includes/legacy ./includes/libraries ./includes/shipping/legacy-flat-rate diff --git a/tests/bin/phpcs.sh b/tests/bin/phpcs.sh index d46915c7784..28df496c940 100755 --- a/tests/bin/phpcs.sh +++ b/tests/bin/phpcs.sh @@ -2,7 +2,7 @@ if [[ ${RUN_PHPCS} == 1 ]]; then CHANGED_FILES=`git diff --name-only --diff-filter=ACMR $TRAVIS_COMMIT_RANGE | grep \\\\.php | awk '{print}' ORS=' '` - IGNORE="tests/cli/,apigen/,includes/gateways/simplify-commerce/includes/,includes/libraries/,includes/api/legacy/" + IGNORE="tests/cli/,apigen/,includes/libraries/,includes/api/legacy/" if [ "$CHANGED_FILES" != "" ]; then echo "Running Code Sniffer." From 7657783409162ebe11b0c6b9fa5524ca6b32e932 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Thu, 10 Jan 2019 16:21:43 -0400 Subject: [PATCH 046/401] use DateTime function instead of implicit toString --- includes/api/class-wc-rest-customers-controller.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/includes/api/class-wc-rest-customers-controller.php b/includes/api/class-wc-rest-customers-controller.php index 70f4781e83d..0e8aff7f717 100644 --- a/includes/api/class-wc-rest-customers-controller.php +++ b/includes/api/class-wc-rest-customers-controller.php @@ -39,7 +39,8 @@ class WC_REST_Customers_Controller extends WC_REST_Customers_V2_Controller { // Format date values. foreach ( $format_date as $key ) { - $datetime = 'date_created' == $key ? get_date_from_gmt( $data[ $key ] ) : $data[ $key ]; + // Date created is stored UTC, date modified is stored WP local time. + $datetime = 'date_created' === $key ? get_date_from_gmt( gmdate( 'Y-m-d\TH:i:s', $data[ $key ]->getTimestamp() ) ) : $data[ $key ]; $data[ $key ] = wc_rest_prepare_date_response( $datetime, false ); $data[ $key . '_gmt' ] = wc_rest_prepare_date_response( $datetime ); } From 11d14b30a48baf0e566b49fd92cc62d201ae6b17 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 11 Jan 2019 12:28:52 +0000 Subject: [PATCH 047/401] PHPCS fixes --- includes/abstracts/abstract-wc-order.php | 45 +-- includes/class-wc-cart.php | 2 +- includes/class-wc-tax.php | 375 ++++++++++++----------- includes/wc-product-functions.php | 4 +- 4 files changed, 231 insertions(+), 195 deletions(-) diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index cb16296a145..d191fc25ff2 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -403,7 +403,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { $subtotal += $item->get_subtotal(); } - return apply_filters( 'woocommerce_order_get_subtotal', (double) $subtotal, $this ); + return apply_filters( 'woocommerce_order_get_subtotal', (float) $subtotal, $this ); } /** @@ -692,13 +692,16 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { * @return string */ protected function type_to_group( $type ) { - $type_to_group = apply_filters( 'woocommerce_order_type_to_group', array( - 'line_item' => 'line_items', - 'tax' => 'tax_lines', - 'shipping' => 'shipping_lines', - 'fee' => 'fee_lines', - 'coupon' => 'coupon_lines', - ) ); + $type_to_group = apply_filters( + 'woocommerce_order_type_to_group', + array( + 'line_item' => 'line_items', + 'tax' => 'tax_lines', + 'shipping' => 'shipping_lines', + 'fee' => 'fee_lines', + 'coupon' => 'coupon_lines', + ) + ); return isset( $type_to_group[ $type ] ) ? $type_to_group[ $type ] : ''; } @@ -1285,12 +1288,15 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { $tax_based_on = 'billing'; } - $args = wp_parse_args( $args, array( - 'country' => 'billing' === $tax_based_on ? $this->get_billing_country() : $this->get_shipping_country(), - 'state' => 'billing' === $tax_based_on ? $this->get_billing_state() : $this->get_shipping_state(), - 'postcode' => 'billing' === $tax_based_on ? $this->get_billing_postcode() : $this->get_shipping_postcode(), - 'city' => 'billing' === $tax_based_on ? $this->get_billing_city() : $this->get_shipping_city(), - ) ); + $args = wp_parse_args( + $args, + array( + 'country' => 'billing' === $tax_based_on ? $this->get_billing_country() : $this->get_shipping_country(), + 'state' => 'billing' === $tax_based_on ? $this->get_billing_state() : $this->get_shipping_state(), + 'postcode' => 'billing' === $tax_based_on ? $this->get_billing_postcode() : $this->get_shipping_postcode(), + 'city' => 'billing' === $tax_based_on ? $this->get_billing_city() : $this->get_shipping_city(), + ) + ); // Default to base. if ( 'base' === $tax_based_on || empty( $args['country'] ) ) { @@ -1619,10 +1625,13 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { if ( 'excl' === $tax_display ) { $ex_tax_label = $this->get_prices_include_tax() ? 1 : 0; - $subtotal = wc_price( $this->get_line_subtotal( $item ), array( - 'ex_tax_label' => $ex_tax_label, - 'currency' => $this->get_currency(), - ) ); + $subtotal = wc_price( + $this->get_line_subtotal( $item ), + array( + 'ex_tax_label' => $ex_tax_label, + 'currency' => $this->get_currency(), + ) + ); } else { $subtotal = wc_price( $this->get_line_subtotal( $item, true ), array( 'currency' => $this->get_currency() ) ); } diff --git a/includes/class-wc-cart.php b/includes/class-wc-cart.php index 9d0dde7aab4..39dfc1204ed 100644 --- a/includes/class-wc-cart.php +++ b/includes/class-wc-cart.php @@ -1425,7 +1425,7 @@ class WC_Cart extends WC_Legacy_Cart { if ( $coupon->is_valid() ) { // Get user and posted emails to compare. - $current_user = wp_get_current_user(); + $current_user = wp_get_current_user(); $billing_email = isset( $posted['billing_email'] ) ? $posted['billing_email'] : ''; $check_emails = array_unique( array_filter( diff --git a/includes/class-wc-tax.php b/includes/class-wc-tax.php index 65be6044158..da662f2ead6 100644 --- a/includes/class-wc-tax.php +++ b/includes/class-wc-tax.php @@ -1,17 +1,16 @@ '', - 'state' => '', - 'city' => '', - 'postcode' => '', - 'tax_class' => '', - ) ); + $args = wp_parse_args( + $args, + array( + 'country' => '', + 'state' => '', + 'city' => '', + 'postcode' => '', + 'tax_class' => '', + ) + ); - extract( $args, EXTR_SKIP ); + $country = $args['country']; + $state = $args['state']; + $city = $args['city']; + $postcode = wc_normalize_postcode( wc_clean( $args['postcode'] ) ); + $tax_class = $args['tax_class']; if ( ! $country ) { return array(); } - $postcode = wc_normalize_postcode( wc_clean( $postcode ) ); $cache_key = WC_Cache_Helper::get_cache_prefix( 'taxes' ) . 'wc_tax_rates_' . md5( sprintf( '%s+%s+%s+%s+%s', $country, $state, $city, $postcode, $tax_class ) ); $matched_tax_rates = wp_cache_get( $cache_key, 'taxes' ); @@ -242,7 +245,7 @@ class WC_Tax { /** * Searches for all matching country/state/postcode tax rates. * - * @param array $args + * @param array $args Args that determine the rate to find. * @return array */ public static function find_shipping_rates( $args = array() ) { @@ -262,12 +265,12 @@ class WC_Tax { /** * Does the sort comparison. Compares (in this order): - * - Priority - * - Country - * - State - * - Number of postcodes - * - Number of cities - * - ID + * - Priority + * - Country + * - State + * - Number of postcodes + * - Number of cities + * - ID * * @param object $rate1 First rate to compare. * @param object $rate2 Second rate to compare. @@ -275,7 +278,7 @@ class WC_Tax { */ private static function sort_rates_callback( $rate1, $rate2 ) { if ( $rate1->tax_rate_priority !== $rate2->tax_rate_priority ) { - return $rate1->tax_rate_priority < $rate2->tax_rate_priority ? -1 : 1; // ASC + return $rate1->tax_rate_priority < $rate2->tax_rate_priority ? -1 : 1; // ASC. } if ( $rate1->tax_rate_country !== $rate2->tax_rate_country ) { @@ -361,10 +364,10 @@ class WC_Tax { /** * Location matching criteria - ORed * Needs to match: - * - rates with no postcodes and cities - * - rates with a matching postcode and city - * - rates with matching postcode, no city - * - rates with matching city, no postcode + * - rates with no postcodes and cities + * - rates with a matching postcode and city + * - rates with matching postcode, no city + * - rates with matching city, no postcode */ $locations_criteria = array(); $locations_criteria[] = 'locations.location_type IS NULL'; @@ -387,17 +390,24 @@ class WC_Tax { AND sub.tax_rate_id = tax_rates.tax_rate_id ) "; + $criteria[] = '( ( ' . implode( ' ) OR ( ', $locations_criteria ) . ' ) )'; - $found_rates = $wpdb->get_results( " + $criteria_string = implode( ' AND ', $criteria ); + + // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared + $found_rates = $wpdb->get_results( + " SELECT tax_rates.*, COUNT( locations.location_id ) as postcode_count, COUNT( locations2.location_id ) as city_count FROM {$wpdb->prefix}woocommerce_tax_rates as tax_rates LEFT OUTER JOIN {$wpdb->prefix}woocommerce_tax_rate_locations as locations ON tax_rates.tax_rate_id = locations.tax_rate_id LEFT OUTER JOIN {$wpdb->prefix}woocommerce_tax_rate_locations as locations2 ON tax_rates.tax_rate_id = locations2.tax_rate_id - WHERE 1=1 AND " . implode( ' AND ', $criteria ) . " + WHERE 1=1 AND {$criteria_string} GROUP BY tax_rates.tax_rate_id ORDER BY tax_rates.tax_rate_priority - " ); + " + ); + // phpcs:enable $found_rates = self::sort_rates( $found_rates ); $matched_tax_rates = array(); @@ -426,14 +436,14 @@ class WC_Tax { * * Used by get_rates(), get_shipping_rates(). * - * @param $tax_class string Optional, passed to the filter for advanced tax setups. + * @param string $tax_class string Optional, passed to the filter for advanced tax setups. * @param object $customer Override the customer object to get their location. * @return array */ public static function get_tax_location( $tax_class = '', $customer = null ) { $location = array(); - if ( is_null( $customer ) && ! empty( WC()->customer ) ) { + if ( is_null( $customer ) && WC()->customer ) { $customer = WC()->customer; } @@ -463,16 +473,18 @@ class WC_Tax { $location = self::get_tax_location( $tax_class, $customer ); $matched_tax_rates = array(); - if ( sizeof( $location ) === 4 ) { + if ( count( $location ) === 4 ) { list( $country, $state, $postcode, $city ) = $location; - $matched_tax_rates = self::find_rates( array( - 'country' => $country, - 'state' => $state, - 'postcode' => $postcode, - 'city' => $city, - 'tax_class' => $tax_class, - ) ); + $matched_tax_rates = self::find_rates( + array( + 'country' => $country, + 'state' => $state, + 'postcode' => $postcode, + 'city' => $city, + 'tax_class' => $tax_class, + ) + ); } return apply_filters( 'woocommerce_matched_rates', $matched_tax_rates, $tax_class ); @@ -481,25 +493,31 @@ class WC_Tax { /** * Get's an array of matching rates for the shop's base country. * - * @param string Tax Class - * @return array + * @param string $tax_class Tax Class. + * @return array */ public static function get_base_tax_rates( $tax_class = '' ) { - return apply_filters( 'woocommerce_base_tax_rates', self::find_rates( array( - 'country' => WC()->countries->get_base_country(), - 'state' => WC()->countries->get_base_state(), - 'postcode' => WC()->countries->get_base_postcode(), - 'city' => WC()->countries->get_base_city(), - 'tax_class' => $tax_class, - ) ), $tax_class ); + return apply_filters( + 'woocommerce_base_tax_rates', + self::find_rates( + array( + 'country' => WC()->countries->get_base_country(), + 'state' => WC()->countries->get_base_state(), + 'postcode' => WC()->countries->get_base_postcode(), + 'city' => WC()->countries->get_base_city(), + 'tax_class' => $tax_class, + ) + ), + $tax_class + ); } /** * Alias for get_base_tax_rates(). * * @deprecated 2.3 - * @param string Tax Class - * @return array + * @param string $tax_class Tax Class. + * @return array */ public static function get_shop_base_rate( $tax_class = '' ) { return self::get_base_tax_rates( $tax_class ); @@ -513,7 +531,7 @@ class WC_Tax { * @return mixed */ public static function get_shipping_tax_rates( $tax_class = null, $customer = null ) { - // See if we have an explicitly set shipping tax class + // See if we have an explicitly set shipping tax class. $shipping_tax_class = get_option( 'woocommerce_shipping_tax_class' ); if ( 'inherit' !== $shipping_tax_class ) { @@ -523,22 +541,24 @@ class WC_Tax { $location = self::get_tax_location( $tax_class, $customer ); $matched_tax_rates = array(); - if ( sizeof( $location ) === 4 ) { + if ( 4 === count( $location ) ) { list( $country, $state, $postcode, $city ) = $location; if ( ! is_null( $tax_class ) ) { - // This will be per item shipping - $matched_tax_rates = self::find_shipping_rates( array( - 'country' => $country, - 'state' => $state, - 'postcode' => $postcode, - 'city' => $city, - 'tax_class' => $tax_class, - ) ); + // This will be per item shipping. + $matched_tax_rates = self::find_shipping_rates( + array( + 'country' => $country, + 'state' => $state, + 'postcode' => $postcode, + 'city' => $city, + 'tax_class' => $tax_class, + ) + ); } elseif ( WC()->cart->get_cart() ) { - // This will be per order shipping - loop through the order and find the highest tax class rate + // This will be per order shipping - loop through the order and find the highest tax class rate. $cart_tax_classes = WC()->cart->get_cart_item_tax_classes_for_shipping(); // No tax classes = no taxable items. @@ -547,42 +567,47 @@ class WC_Tax { } // If multiple classes are found, use the first one found unless a standard rate item is found. This will be the first listed in the 'additional tax class' section. - if ( sizeof( $cart_tax_classes ) > 1 && ! in_array( '', $cart_tax_classes ) ) { + if ( count( $cart_tax_classes ) > 1 && ! in_array( '', $cart_tax_classes, true ) ) { $tax_classes = self::get_tax_class_slugs(); foreach ( $tax_classes as $tax_class ) { - if ( in_array( $tax_class, $cart_tax_classes ) ) { - $matched_tax_rates = self::find_shipping_rates( array( - 'country' => $country, - 'state' => $state, - 'postcode' => $postcode, - 'city' => $city, - 'tax_class' => $tax_class, - ) ); + if ( in_array( $tax_class, $cart_tax_classes, true ) ) { + $matched_tax_rates = self::find_shipping_rates( + array( + 'country' => $country, + 'state' => $state, + 'postcode' => $postcode, + 'city' => $city, + 'tax_class' => $tax_class, + ) + ); break; } } - - // If a single tax class is found, use it - } elseif ( sizeof( $cart_tax_classes ) == 1 ) { - $matched_tax_rates = self::find_shipping_rates( array( - 'country' => $country, - 'state' => $state, - 'postcode' => $postcode, - 'city' => $city, - 'tax_class' => $cart_tax_classes[0], - ) ); + } elseif ( 1 === count( $cart_tax_classes ) ) { + // If a single tax class is found, use it. + $matched_tax_rates = self::find_shipping_rates( + array( + 'country' => $country, + 'state' => $state, + 'postcode' => $postcode, + 'city' => $city, + 'tax_class' => $cart_tax_classes[0], + ) + ); } } - // Get standard rate if no taxes were found - if ( ! sizeof( $matched_tax_rates ) ) { - $matched_tax_rates = self::find_shipping_rates( array( - 'country' => $country, - 'state' => $state, - 'postcode' => $postcode, - 'city' => $city, - ) ); + // Get standard rate if no taxes were found. + if ( ! count( $matched_tax_rates ) ) { + $matched_tax_rates = self::find_shipping_rates( + array( + 'country' => $country, + 'state' => $state, + 'postcode' => $postcode, + 'city' => $city, + ) + ); } } @@ -592,18 +617,18 @@ class WC_Tax { /** * Return true/false depending on if a rate is a compound rate. * - * @param mixed $key_or_rate Tax rate ID, or the db row itself in object format + * @param mixed $key_or_rate Tax rate ID, or the db row itself in object format. * @return bool */ public static function is_compound( $key_or_rate ) { global $wpdb; if ( is_object( $key_or_rate ) ) { - $key = $key_or_rate->tax_rate_id; - $compound = $key_or_rate->tax_rate_compound; + $key = $key_or_rate->tax_rate_id; + $compound = $key_or_rate->tax_rate_compound; } else { - $key = $key_or_rate; - $compound = (bool) $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate_compound FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $key ) ); + $key = $key_or_rate; + $compound = (bool) $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate_compound FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $key ) ); } return (bool) apply_filters( 'woocommerce_rate_compound', $compound, $key ); @@ -612,7 +637,7 @@ class WC_Tax { /** * Return a given rates label. * - * @param mixed $key_or_rate Tax rate ID, or the db row itself in object format + * @param mixed $key_or_rate Tax rate ID, or the db row itself in object format. * @return string */ public static function get_rate_label( $key_or_rate ) { @@ -636,7 +661,7 @@ class WC_Tax { /** * Return a given rates percent. * - * @param mixed $key_or_rate Tax rate ID, or the db row itself in object format + * @param mixed $key_or_rate Tax rate ID, or the db row itself in object format. * @return string */ public static function get_rate_percent( $key_or_rate ) { @@ -656,8 +681,7 @@ class WC_Tax { /** * Get a rates code. Code is made up of COUNTRY-STATE-NAME-Priority. E.g GB-VAT-1, US-AL-TAX-1. * - * @access public - * @param mixed $key_or_rate Tax rate ID, or the db row itself in object format + * @param mixed $key_or_rate Tax rate ID, or the db row itself in object format. * @return string */ public static function get_rate_code( $key_or_rate ) { @@ -674,11 +698,11 @@ class WC_Tax { $code_string = ''; if ( null !== $rate ) { - $code = array(); - $code[] = $rate->tax_rate_country; - $code[] = $rate->tax_rate_state; - $code[] = $rate->tax_rate_name ? $rate->tax_rate_name : 'TAX'; - $code[] = absint( $rate->tax_rate_priority ); + $code = array(); + $code[] = $rate->tax_rate_country; + $code[] = $rate->tax_rate_state; + $code[] = $rate->tax_rate_name ? $rate->tax_rate_name : 'TAX'; + $code[] = absint( $rate->tax_rate_priority ); $code_string = strtoupper( implode( '-', array_filter( $code ) ) ); } @@ -686,9 +710,9 @@ class WC_Tax { } /** - * Round tax lines and return the sum. + * Round tax lines and return the sum. Note this rounds to precision, not store currency decimals. * - * @param array + * @param array $taxes Array of taxes to round. * @return float */ public static function get_tax_total( $taxes ) { @@ -725,8 +749,9 @@ class WC_Tax { } /** - * format the city. - * @param string $city + * Format the city. + * + * @param string $city Value to format. * @return string */ private static function format_tax_rate_city( $city ) { @@ -734,8 +759,9 @@ class WC_Tax { } /** - * format the state. - * @param string $state + * Format the state. + * + * @param string $state Value to format. * @return string */ private static function format_tax_rate_state( $state ) { @@ -744,8 +770,9 @@ class WC_Tax { } /** - * format the country. - * @param string $country + * Format the country. + * + * @param string $country Value to format. * @return string */ private static function format_tax_rate_country( $country ) { @@ -754,8 +781,9 @@ class WC_Tax { } /** - * format the tax rate name. - * @param string $name + * Format the tax rate name. + * + * @param string $name Value to format. * @return string */ private static function format_tax_rate_name( $name ) { @@ -763,17 +791,19 @@ class WC_Tax { } /** - * format the rate. - * @param double $rate + * Format the rate. + * + * @param float $rate Value to format. * @return string */ private static function format_tax_rate( $rate ) { - return number_format( (double) $rate, 4, '.', '' ); + return number_format( (float) $rate, 4, '.', '' ); } /** - * format the priority. - * @param string $priority + * Format the priority. + * + * @param string $priority Value to format. * @return int */ private static function format_tax_rate_priority( $priority ) { @@ -781,14 +811,15 @@ class WC_Tax { } /** - * format the class. - * @param string $class + * Format the class. + * + * @param string $class Value to format. * @return string */ public static function format_tax_rate_class( $class ) { $class = sanitize_title( $class ); $classes = self::get_tax_class_slugs(); - if ( ! in_array( $class, $classes ) ) { + if ( ! in_array( $class, $classes, true ) ) { $class = ''; } return ( 'standard' === $class ) ? '' : $class; @@ -796,7 +827,8 @@ class WC_Tax { /** * Prepare and format tax rate for DB insertion. - * @param array $tax_rate + * + * @param array $tax_rate Tax rate to format. * @return array */ private static function prepare_tax_rate( $tax_rate ) { @@ -818,10 +850,8 @@ class WC_Tax { * Internal use only. * * @since 2.3.0 - * @access private - * - * @param array $tax_rate * + * @param array $tax_rate Tax rate to insert. * @return int tax rate id */ public static function _insert_tax_rate( $tax_rate ) { @@ -842,21 +872,25 @@ class WC_Tax { * Internal use only. * * @since 2.5.0 - * @access private - * - * @param int $tax_rate_id - * @param string $output_type * + * @param int $tax_rate_id Tax rate ID. + * @param string $output_type Type of output. * @return array|object */ public static function _get_tax_rate( $tax_rate_id, $output_type = ARRAY_A ) { global $wpdb; - return $wpdb->get_row( $wpdb->prepare( " - SELECT * - FROM {$wpdb->prefix}woocommerce_tax_rates - WHERE tax_rate_id = %d - ", $tax_rate_id ), $output_type ); + return $wpdb->get_row( + $wpdb->prepare( + " + SELECT * + FROM {$wpdb->prefix}woocommerce_tax_rates + WHERE tax_rate_id = %d + ", + $tax_rate_id + ), + $output_type + ); } /** @@ -865,10 +899,9 @@ class WC_Tax { * Internal use only. * * @since 2.3.0 - * @access private * - * @param int $tax_rate_id - * @param array $tax_rate + * @param int $tax_rate_id Tax rate to update. + * @param array $tax_rate Tax rate values. */ public static function _update_tax_rate( $tax_rate_id, $tax_rate ) { global $wpdb; @@ -876,7 +909,7 @@ class WC_Tax { $tax_rate_id = absint( $tax_rate_id ); $wpdb->update( - $wpdb->prefix . "woocommerce_tax_rates", + $wpdb->prefix . 'woocommerce_tax_rates', self::prepare_tax_rate( $tax_rate ), array( 'tax_rate_id' => $tax_rate_id, @@ -894,9 +927,7 @@ class WC_Tax { * Internal use only. * * @since 2.3.0 - * @access private - * - * @param int $tax_rate_id + * @param int $tax_rate_id Tax rate to delete. */ public static function _delete_tax_rate( $tax_rate_id ) { global $wpdb; @@ -915,10 +946,9 @@ class WC_Tax { * Internal use only. * * @since 2.3.0 - * @access private * - * @param int $tax_rate_id - * @param string $postcodes String of postcodes separated by ; characters + * @param int $tax_rate_id Tax rate to update. + * @param string $postcodes String of postcodes separated by ; characters. */ public static function _update_tax_rate_postcodes( $tax_rate_id, $postcodes ) { if ( ! is_array( $postcodes ) ) { @@ -928,7 +958,7 @@ class WC_Tax { foreach ( $postcodes as $key => $postcode ) { $postcodes[ $key ] = strtoupper( trim( str_replace( chr( 226 ) . chr( 128 ) . chr( 166 ), '...', $postcode ) ) ); } - self::_update_tax_rate_locations( $tax_rate_id, array_diff( array_filter( $postcodes ), array( '*' ) ), 'postcode' ); + self::update_tax_rate_locations( $tax_rate_id, array_diff( array_filter( $postcodes ), array( '*' ) ), 'postcode' ); } /** @@ -937,10 +967,9 @@ class WC_Tax { * Internal use only. * * @since 2.3.0 - * @access private * - * @param int $tax_rate_id - * @param string $cities + * @param int $tax_rate_id Tax rate to update. + * @param string $cities Cities to set. */ public static function _update_tax_rate_cities( $tax_rate_id, $cities ) { if ( ! is_array( $cities ) ) { @@ -948,7 +977,7 @@ class WC_Tax { } $cities = array_filter( array_diff( array_map( array( __CLASS__, 'format_tax_rate_city' ), $cities ), array( '*' ) ) ); - self::_update_tax_rate_locations( $tax_rate_id, $cities, 'city' ); + self::update_tax_rate_locations( $tax_rate_id, $cities, 'city' ); } /** @@ -957,30 +986,28 @@ class WC_Tax { * Internal use only. * * @since 2.3.0 - * @access private * - * @param int $tax_rate_id - * @param array $values - * @param string $type + * @param int $tax_rate_id Tax rate ID to update. + * @param array $values Values to set. + * @param string $type Location type. */ - private static function _update_tax_rate_locations( $tax_rate_id, $values, $type ) { + private static function update_tax_rate_locations( $tax_rate_id, $values, $type ) { global $wpdb; $tax_rate_id = absint( $tax_rate_id ); $wpdb->query( - $wpdb->prepare( " - DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d AND location_type = %s; - ", $tax_rate_id, $type + $wpdb->prepare( + "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE tax_rate_id = %d AND location_type = %s;", + $tax_rate_id, + $type ) ); - if ( sizeof( $values ) > 0 ) { + if ( count( $values ) > 0 ) { $sql = "( '" . implode( "', $tax_rate_id, '" . esc_sql( $type ) . "' ),( '", array_map( 'esc_sql', $values ) ) . "', $tax_rate_id, '" . esc_sql( $type ) . "' )"; - $wpdb->query( " - INSERT INTO {$wpdb->prefix}woocommerce_tax_rate_locations ( location_code, tax_rate_id, location_type ) VALUES $sql; - " ); + $wpdb->query( "INSERT INTO {$wpdb->prefix}woocommerce_tax_rate_locations ( location_code, tax_rate_id, location_type ) VALUES $sql;" ); // @codingStandardsIgnoreLine. } WC_Cache_Helper::incr_cache_prefix( 'taxes' ); @@ -989,7 +1016,7 @@ class WC_Tax { /** * Used by admin settings page. * - * @param string $tax_class + * @param string $tax_class Tax class slug. * * @return array|null|object */ diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index af010764c59..b9719ec2b58 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -972,8 +972,8 @@ function wc_get_price_including_tax( $product, $args = array() ) { if ( $product->is_taxable() ) { if ( ! wc_prices_include_tax() ) { - $tax_rates = WC_Tax::get_rates( $product->get_tax_class() ); - $taxes = WC_Tax::calc_tax( $line_price, $tax_rates, false ); + $tax_rates = WC_Tax::get_rates( $product->get_tax_class() ); + $taxes = WC_Tax::calc_tax( $line_price, $tax_rates, false ); if ( 'yes' === get_option( 'woocommerce_tax_round_at_subtotal' ) ) { $taxes_total = array_sum( $taxes ); From af3707d75a7678092e16daca17dd43c74838a145 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 11 Jan 2019 12:38:05 +0000 Subject: [PATCH 048/401] Always round to precision. Negate need to call get_tax_total to handle rounding. Instead it just sums. --- includes/class-wc-tax.php | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/includes/class-wc-tax.php b/includes/class-wc-tax.php index da662f2ead6..b7086bf217a 100644 --- a/includes/class-wc-tax.php +++ b/includes/class-wc-tax.php @@ -150,6 +150,13 @@ class WC_Tax { $taxes[ $key ] += $tax_amount; } + /** + * Round all taxes to precision (4DP) before passing them back. Note, this is not the same rounding + * as in the cart calculation class which, depending on settings, will round to 2DP when calculating + * final totals. Also unlike that class, this rounds .5 up for all cases. + */ + $taxes = array_map( array( __CLASS__, 'round' ), $taxes ); + return $taxes; } @@ -200,6 +207,13 @@ class WC_Tax { } } + /** + * Round all taxes to precision (4DP) before passing them back. Note, this is not the same rounding + * as in the cart calculation class which, depending on settings, will round to 2DP when calculating + * final totals. Also unlike that class, this rounds .5 up for all cases. + */ + $taxes = array_map( array( __CLASS__, 'round' ), $taxes ); + return $taxes; } @@ -710,13 +724,13 @@ class WC_Tax { } /** - * Round tax lines and return the sum. Note this rounds to precision, not store currency decimals. + * Sums a set of taxes to form a single total. Values are pre-rounded to precision from 3.6.0. * - * @param array $taxes Array of taxes to round. - * @return float + * @param array $taxes Array of taxes. + * @return float */ public static function get_tax_total( $taxes ) { - return array_sum( array_map( array( __CLASS__, 'round' ), $taxes ) ); + return array_sum( $taxes ); } /** From dabfe664640df4aedce6745a9ac46a63b00e472b Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 11 Jan 2019 12:41:06 +0000 Subject: [PATCH 049/401] Correct use of rounding functions in cart class --- includes/class-wc-cart.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-cart.php b/includes/class-wc-cart.php index 39dfc1204ed..1d624cc1355 100644 --- a/includes/class-wc-cart.php +++ b/includes/class-wc-cart.php @@ -869,7 +869,7 @@ class WC_Cart extends WC_Legacy_Cart { $tax_totals[ $code ]->is_compound = WC_Tax::is_compound( $key ); $tax_totals[ $code ]->label = WC_Tax::get_rate_label( $key ); $tax_totals[ $code ]->amount += wc_round_tax_total( $tax ); - $tax_totals[ $code ]->formatted_amount = wc_price( wc_round_tax_total( $tax_totals[ $code ]->amount ) ); + $tax_totals[ $code ]->formatted_amount = wc_price( $tax_totals[ $code ]->amount ); } } @@ -1916,10 +1916,10 @@ class WC_Cart extends WC_Legacy_Cart { if ( ! $compound && WC_Tax::is_compound( $key ) ) { continue; } - $total += $tax; + $total += wc_round_tax_total( $tax ); } if ( $display ) { - $total = wc_round_tax_total( $total ); + $total = wc_format_decimal( $total, wc_get_price_decimals() ); } return apply_filters( 'woocommerce_cart_taxes_total', $total, $compound, $display, $this ); } From 50af71b9bdaa5e37b567871d31c17d8529cea82e Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 11 Jan 2019 12:59:23 +0000 Subject: [PATCH 050/401] Calculated items_subtotal_tax using rounded tax subtotals. --- includes/class-wc-cart-totals.php | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/includes/class-wc-cart-totals.php b/includes/class-wc-cart-totals.php index 55bf6d5e072..5e6bc2fbebb 100644 --- a/includes/class-wc-cart-totals.php +++ b/includes/class-wc-cart-totals.php @@ -159,6 +159,7 @@ final class WC_Cart_Totals { 'price_includes_tax' => false, 'subtotal' => 0, 'subtotal_tax' => 0, + 'subtotal_taxes' => array(), 'total' => 0, 'total_tax' => 0, 'taxes' => array(), @@ -693,6 +694,8 @@ final class WC_Cart_Totals { * @since 3.2.0 */ protected function calculate_item_subtotals() { + $merged_subtotal_taxes = array(); // Taxes indexed by tax rate ID for storage later. + foreach ( $this->items as $item_key => $item ) { if ( $item->price_includes_tax ) { if ( $this->cart->get_customer()->get_is_vat_exempt() ) { @@ -703,24 +706,31 @@ final class WC_Cart_Totals { } $item->subtotal = $item->price; - $subtotal_taxes = array(); if ( $this->calculate_tax && $item->product->is_taxable() ) { - $subtotal_taxes = WC_Tax::calc_tax( $item->subtotal, $item->tax_rates, $item->price_includes_tax ); - $item->subtotal_tax = array_sum( array_map( array( $this, 'round_line_tax' ), $subtotal_taxes ) ); + $item->subtotal_taxes = WC_Tax::calc_tax( $item->subtotal, $item->tax_rates, $item->price_includes_tax ); + $item->subtotal_tax = array_sum( array_map( array( $this, 'round_line_tax' ), $item->subtotal_taxes ) ); if ( $item->price_includes_tax ) { // Use unrounded taxes so we can re-calculate from the orders screen accurately later. - $item->subtotal = $item->subtotal - array_sum( $subtotal_taxes ); + $item->subtotal = $item->subtotal - array_sum( $item->subtotal_taxes ); + } + + foreach ( $item->subtotal_taxes as $rate_id => $rate ) { + if ( ! isset( $merged_subtotal_taxes[ $rate_id ] ) ) { + $merged_subtotal_taxes[ $rate_id ] = 0; + } + $merged_subtotal_taxes[ $rate_id ] += $this->round_line_tax( $rate ); } } - $this->cart->cart_contents[ $item_key ]['line_tax_data'] = array( 'subtotal' => wc_remove_number_precision_deep( $subtotal_taxes ) ); + $this->cart->cart_contents[ $item_key ]['line_tax_data'] = array( 'subtotal' => wc_remove_number_precision_deep( $item->subtotal_taxes ) ); $this->cart->cart_contents[ $item_key ]['line_subtotal'] = wc_remove_number_precision( $item->subtotal ); $this->cart->cart_contents[ $item_key ]['line_subtotal_tax'] = wc_remove_number_precision( $item->subtotal_tax ); } + $this->set_total( 'items_subtotal', array_sum( array_map( 'round', array_values( wp_list_pluck( $this->items, 'subtotal' ) ) ) ) ); - $this->set_total( 'items_subtotal_tax', array_sum( array_values( wp_list_pluck( $this->items, 'subtotal_tax' ) ) ) ); + $this->set_total( 'items_subtotal_tax', array_sum( $this->round_merged_taxes( $merged_subtotal_taxes ) ) ); $this->cart->set_subtotal( $this->get_total( 'items_subtotal' ) ); $this->cart->set_subtotal_tax( $this->get_total( 'items_subtotal_tax' ) ); From caffa319f4d96e4390963050e67f53d85f65d30d Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 11 Jan 2019 13:06:26 +0000 Subject: [PATCH 051/401] set_item_discount_amounts should support woocommerce_tax_round_at_subtotal --- includes/abstracts/abstract-wc-order.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index d191fc25ff2..894ed04fc49 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -1079,9 +1079,14 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { // If the prices include tax, discounts should be taken off the tax inclusive prices like in the cart. if ( $this->get_prices_include_tax() && wc_tax_enabled() ) { - $amount_tax = WC_Tax::get_tax_total( WC_Tax::calc_tax( $amount, WC_Tax::get_rates( $item->get_tax_class() ), true ) ); - $amount -= $amount_tax; - $item->set_total( max( 0, $item->get_total() - $amount ) ); + $taxes = WC_Tax::calc_tax( $amount, WC_Tax::get_rates( $item->get_tax_class() ), true ); + + if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) { + $taxes = array_map( 'wc_round_tax_total', $taxes ); + } + + $amount = $amount - array_sum( $taxes ); + $item->set_total( max( 0, round( $item->get_total() - $amount, wc_get_price_decimals() ) ) ); } else { $item->set_total( max( 0, $item->get_total() - $amount ) ); } From 50ca24e5d9b6acd9c8da8a3da17b2857c1dcb54b Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 11 Jan 2019 13:09:20 +0000 Subject: [PATCH 052/401] set_coupon_discount_amounts should support woocommerce_tax_round_at_subtotal --- includes/abstracts/abstract-wc-order.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index 894ed04fc49..78b37741da0 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -1124,11 +1124,22 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { $item = $this->get_item( $item_id, false ); if ( $this->get_prices_include_tax() && wc_tax_enabled() ) { - $amount_tax = array_sum( WC_Tax::calc_tax( $item_discount_amount, WC_Tax::get_rates( $item->get_tax_class() ), true ) ); - $discount_tax += $amount_tax; - $amount = $amount - $amount_tax; + $taxes = WC_Tax::calc_tax( $item_discount_amount, WC_Tax::get_rates( $item->get_tax_class() ), true ); + + if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) { + $taxes = array_map( 'wc_round_tax_total', $taxes ); + } + + $discount_tax += array_sum( $taxes ); + $amount = $amount - array_sum( $taxes ); } else { - $discount_tax += array_sum( WC_Tax::calc_tax( $item_discount_amount, WC_Tax::get_rates( $item->get_tax_class() ) ) ); + $taxes = WC_Tax::calc_tax( $item_discount_amount, WC_Tax::get_rates( $item->get_tax_class() ) ); + + if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) { + $taxes = array_map( 'wc_round_tax_total', $taxes ); + } + + $discount_tax += array_sum( $taxes ); } } From 28ac24d87075dcf8ab450b0e7c2a3fc548bdbdbe Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 11 Jan 2019 13:12:31 +0000 Subject: [PATCH 053/401] update_taxes can safely round all values, as lines would already be rounded if applicable --- includes/abstracts/abstract-wc-order.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index 78b37741da0..e5e55e84445 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -1427,14 +1427,8 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { $this->add_item( $item ); } - if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) { - $this->set_shipping_tax( wc_round_tax_total( array_sum( array_map( 'wc_round_tax_total', $shipping_taxes ) ) ) ); - $this->set_cart_tax( wc_round_tax_total( array_sum( array_map( 'wc_round_tax_total', $cart_taxes ) ) ) ); - } else { - $this->set_shipping_tax( wc_round_tax_total( array_sum( $shipping_taxes ) ) ); - $this->set_cart_tax( wc_round_tax_total( array_sum( $cart_taxes ) ) ); - } - + $this->set_shipping_tax( wc_round_tax_total( array_sum( $shipping_taxes ) ) ); + $this->set_cart_tax( wc_round_tax_total( array_sum( $cart_taxes ) ) ); $this->save(); } From 42f77950bde33ed703c5f37fdde12459e0156d62 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 11 Jan 2019 13:17:06 +0000 Subject: [PATCH 054/401] Make total recalc use unrounded tax values to fix test WC_Tests_Order_Coupons::test_add_coupon_to_order --- includes/abstracts/abstract-wc-order.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index e5e55e84445..c5ce6ce533c 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -1483,14 +1483,21 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { $this->calculate_taxes(); } - // Sum taxes. + // Sum taxes again so we can work out how much tax was discounted. This uses original values, not those possibly rounded to 2dp. foreach ( $this->get_items() as $item ) { - $cart_subtotal_tax += $item->get_subtotal_tax(); - $cart_total_tax += $item->get_total_tax(); + $taxes = $item->get_taxes(); + + foreach ( $taxes['total'] as $tax_rate_id => $tax ) { + $cart_total_tax += (float) $tax; + } + + foreach ( $taxes['subtotal'] as $tax_rate_id => $tax ) { + $cart_subtotal_tax += (float) $tax; + } } $this->set_discount_total( $cart_subtotal - $cart_total ); - $this->set_discount_tax( $cart_subtotal_tax - $cart_total_tax ); + $this->set_discount_tax( wc_round_tax_total( $cart_subtotal_tax - $cart_total_tax ) ); $this->set_total( round( $cart_total + $fee_total + $this->get_shipping_total() + $this->get_cart_tax() + $this->get_shipping_tax(), wc_get_price_decimals() ) ); do_action( 'woocommerce_order_after_calculate_totals', $and_taxes, $this ); From 22ce8aa8b8b7ff3db21af486db9f6b8dea6eebe2 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 11 Jan 2019 13:20:57 +0000 Subject: [PATCH 055/401] Order items respect wc_round_tax_total --- includes/class-wc-order-item-fee.php | 7 ++++++- includes/class-wc-order-item-product.php | 16 ++++++++++++---- includes/class-wc-order-item-shipping.php | 7 ++++++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/includes/class-wc-order-item-fee.php b/includes/class-wc-order-item-fee.php index 46d0edc8d3d..e0703c9430d 100644 --- a/includes/class-wc-order-item-fee.php +++ b/includes/class-wc-order-item-fee.php @@ -180,7 +180,12 @@ class WC_Order_Item_Fee extends WC_Order_Item { $tax_data['total'] = array_map( 'wc_format_decimal', $raw_tax_data['total'] ); } $this->set_prop( 'taxes', $tax_data ); - $this->set_total_tax( array_sum( $tax_data['total'] ) ); + + if ( 'yes' === get_option( 'woocommerce_tax_round_at_subtotal' ) ) { + $this->set_total_tax( array_sum( $tax_data['total'] ) ); + } else { + $this->set_total_tax( array_sum( array_map( 'wc_round_tax_total', $tax_data['total'] ) ) ); + } } /* diff --git a/includes/class-wc-order-item-product.php b/includes/class-wc-order-item-product.php index bf01b92845d..fe8fdef7067 100644 --- a/includes/class-wc-order-item-product.php +++ b/includes/class-wc-order-item-product.php @@ -160,8 +160,14 @@ class WC_Order_Item_Product extends WC_Order_Item { } } $this->set_prop( 'taxes', $tax_data ); - $this->set_total_tax( array_sum( $tax_data['total'] ) ); - $this->set_subtotal_tax( array_sum( $tax_data['subtotal'] ) ); + + if ( 'yes' === get_option( 'woocommerce_tax_round_at_subtotal' ) ) { + $this->set_total_tax( array_sum( $tax_data['total'] ) ); + $this->set_subtotal_tax( array_sum( $tax_data['subtotal'] ) ); + } else { + $this->set_total_tax( array_sum( array_map( 'wc_round_tax_total', $tax_data['total'] ) ) ); + $this->set_subtotal_tax( array_sum( array_map( 'wc_round_tax_total', $tax_data['subtotal'] ) ) ); + } } /** @@ -347,7 +353,8 @@ class WC_Order_Item_Product extends WC_Order_Item { 'order' => $order->get_order_key(), 'email' => rawurlencode( $order->get_billing_email() ), 'key' => $download_id, - ), trailingslashit( home_url() ) + ), + trailingslashit( home_url() ) ) : ''; } @@ -386,7 +393,8 @@ class WC_Order_Item_Product extends WC_Order_Item { 'order' => $order->get_order_key(), 'uid' => $email_hash, 'key' => $download_id, - ), trailingslashit( home_url() ) + ), + trailingslashit( home_url() ) ); } } diff --git a/includes/class-wc-order-item-shipping.php b/includes/class-wc-order-item-shipping.php index 9e9ca8cae1f..d66036270f4 100644 --- a/includes/class-wc-order-item-shipping.php +++ b/includes/class-wc-order-item-shipping.php @@ -142,7 +142,12 @@ class WC_Order_Item_Shipping extends WC_Order_Item { $tax_data['total'] = array_map( 'wc_format_decimal', $raw_tax_data ); } $this->set_prop( 'taxes', $tax_data ); - $this->set_total_tax( array_sum( $tax_data['total'] ) ); + + if ( 'yes' === get_option( 'woocommerce_tax_round_at_subtotal' ) ) { + $this->set_total_tax( array_sum( $tax_data['total'] ) ); + } else { + $this->set_total_tax( array_sum( array_map( 'wc_round_tax_total', $tax_data['total'] ) ) ); + } } /** From 12a73d4f8d7c273e474ca5903dfbfdeca7a63b46 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 11 Jan 2019 14:43:32 +0000 Subject: [PATCH 056/401] Clarify how discount is applied --- includes/admin/class-wc-admin-assets.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/admin/class-wc-admin-assets.php b/includes/admin/class-wc-admin-assets.php index b2162002e12..13dd778c147 100644 --- a/includes/admin/class-wc-admin-assets.php +++ b/includes/admin/class-wc-admin-assets.php @@ -336,7 +336,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) : 'i18n_permission_revoke' => __( 'Are you sure you want to revoke access to this download?', 'woocommerce' ), 'i18n_tax_rate_already_exists' => __( 'You cannot add the same tax rate twice!', 'woocommerce' ), 'i18n_delete_note' => __( 'Are you sure you wish to delete this note? This action cannot be undone.', 'woocommerce' ), - 'i18n_apply_coupon' => __( 'Enter a coupon code to apply to this order.', 'woocommerce' ), + 'i18n_apply_coupon' => __( 'Enter a coupon code to apply. Discounts are applied to line totals, before taxes.', 'woocommerce' ), 'i18n_add_fee' => __( 'Enter a fixed amount or percentage to apply as a fee.', 'woocommerce' ), ); From 01d7eaf92f72974d1fc11e1c67f4940bc9a0ee4f Mon Sep 17 00:00:00 2001 From: Matthew Rochow Date: Thu, 26 Jul 2018 08:00:40 +1000 Subject: [PATCH 057/401] Update class-wc-product-data-store-cpt.php Updating find_matching_product_variation() function to be faster --- .../class-wc-product-data-store-cpt.php | 97 ++++++++++--------- 1 file changed, 50 insertions(+), 47 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index 34dad6f3455..cf7e126839d 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -1052,61 +1052,64 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da * @return int Matching variation ID or 0. */ public function find_matching_product_variation( $product, $match_attributes = array() ) { - $query_args = array( - 'post_parent' => $product->get_id(), - 'post_type' => 'product_variation', - 'orderby' => 'menu_order', - 'order' => 'ASC', - 'fields' => 'ids', - 'post_status' => 'publish', - 'numberposts' => 1, - 'meta_query' => array(), // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_query - ); + global $wpdb; - // Allow large queries in case user has many variations or attributes. - $GLOBALS['wpdb']->query( 'SET SESSION SQL_BIG_SELECTS=1' ); + $matched_variation = 0; + $sorted_meta = array(); - foreach ( $product->get_attributes() as $attribute ) { - if ( ! $attribute->get_variation() ) { - continue; + // Get any variations of the main product + $variation_ids = $wpdb->get_results(" + SELECT ID + FROM {$wpdb->prefix}posts + WHERE {$wpdb->prefix}posts.post_parent = " . $product->get_id() . " + AND {$wpdb->prefix}posts.post_status = 'publish' + AND {$wpdb->prefix}posts.post_type = 'product_variation' + ", ARRAY_A); + + if( $variation_ids ) { + foreach( $variation_ids as $ids ) { + $variations_string .= $ids['ID'] . ','; } - $attribute_field_name = 'attribute_' . sanitize_title( $attribute->get_name() ); + $variations_string = rtrim( $variations_string, ',' ); - if ( ! isset( $match_attributes[ $attribute_field_name ] ) ) { - return 0; + // Get the attributes of the variations + $attributes = $wpdb->get_results(" + SELECT * FROM {$wpdb->prefix}postmeta + WHERE post_id + IN($variations_string) + AND meta_key LIKE 'attribute%' + ", + ARRAY_A); + + if( $attributes ) { + // Sort them into a nice easy array for us to filter + foreach( $attributes as $m ) { + $sorted_meta[ $m['post_id'] ][ $m['meta_key'] ] = $m['meta_value']; + } + + // Check each variation to find the one that matches the $match_attributes + // Note: Not all meta fields will be set which is why we check existance + foreach( $sorted_meta as $post_id => $variation ) { + $match = true; + + foreach( $match_attributes as $k => $v ) { + if( array_key_exists( $k, $variation ) ) { + if( $variation[ $k ] != $v && !empty( $variation[ $k ] ) ) { + $match = false; + } + } + } + + // Bingo + if( true == $match ) { + $matched_variation = $post_id; + } + } } - - // Note not wc_clean here to prevent removal of entities. - $value = $match_attributes[ $attribute_field_name ]; - - $query_args['meta_query'][] = array( - 'relation' => 'OR', - array( - 'key' => $attribute_field_name, - 'value' => array( '', $value ), - 'compare' => 'IN', - ), - array( - 'key' => $attribute_field_name, - 'compare' => 'NOT EXISTS', - ), - ); } - $variations = get_posts( $query_args ); - - if ( $variations && ! is_wp_error( $variations ) ) { - return current( $variations ); - } elseif ( version_compare( get_post_meta( $product->get_id(), '_product_version', true ), '2.4.0', '<' ) ) { - /** - * Pre 2.4 handling where 'slugs' were saved instead of the full text attribute. - * Fallback is here because there are cases where data will be 'synced' but the product version will remain the same. - */ - return ( array_map( 'sanitize_title', $match_attributes ) === $match_attributes ) ? 0 : $this->find_matching_product_variation( $product, array_map( 'sanitize_title', $match_attributes ) ); - } - - return 0; + return $matched_variation; } /** From 07b2bcc726bcc712c0c4f2770850d191ea5f0030 Mon Sep 17 00:00:00 2001 From: Matthew Rochow Date: Thu, 26 Jul 2018 09:05:57 +1000 Subject: [PATCH 058/401] Update class-wc-product-data-store-cpt.php --- .../class-wc-product-data-store-cpt.php | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index cf7e126839d..737add3e1d9 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -9,7 +9,7 @@ if ( ! defined( 'ABSPATH' ) ) { exit; } -/** +/**a * WC Product Data Store: Stored in CPT. * * @version 3.0.0 @@ -1052,10 +1052,10 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da * @return int Matching variation ID or 0. */ public function find_matching_product_variation( $product, $match_attributes = array() ) { - global $wpdb; + global $wpdb; $matched_variation = 0; - $sorted_meta = array(); + $sorted_meta = array(); // Get any variations of the main product $variation_ids = $wpdb->get_results(" @@ -1066,8 +1066,8 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da AND {$wpdb->prefix}posts.post_type = 'product_variation' ", ARRAY_A); - if( $variation_ids ) { - foreach( $variation_ids as $ids ) { + if ( $variation_ids ) { + foreach ( $variation_ids as $ids ) { $variations_string .= $ids['ID'] . ','; } @@ -1081,28 +1081,28 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da AND meta_key LIKE 'attribute%' ", ARRAY_A); - - if( $attributes ) { + + if ( $attributes ) { // Sort them into a nice easy array for us to filter - foreach( $attributes as $m ) { + foreach ( $attributes as $m ) { $sorted_meta[ $m['post_id'] ][ $m['meta_key'] ] = $m['meta_value']; } // Check each variation to find the one that matches the $match_attributes // Note: Not all meta fields will be set which is why we check existance - foreach( $sorted_meta as $post_id => $variation ) { + foreach ( $sorted_meta as $post_id => $variation ) { $match = true; - foreach( $match_attributes as $k => $v ) { - if( array_key_exists( $k, $variation ) ) { - if( $variation[ $k ] != $v && !empty( $variation[ $k ] ) ) { + foreach ( $match_attributes as $k => $v ) { + if ( array_key_exists( $k, $variation ) ) { + if ( $variation[ $k ] != $v && ! empty( $variation[ $k ] ) ) { $match = false; } } } // Bingo - if( true == $match ) { + if ( true == $match ) { $matched_variation = $post_id; } } From 7c50a29d2d920d72fcf9fd5cc8a6474d10f6b88f Mon Sep 17 00:00:00 2001 From: Matthew Rochow Date: Thu, 26 Jul 2018 09:18:35 +1000 Subject: [PATCH 059/401] Update class-wc-product-data-store-cpt.php --- includes/data-stores/class-wc-product-data-store-cpt.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index 737add3e1d9..83265ae5ab8 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -1092,8 +1092,8 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da // Note: Not all meta fields will be set which is why we check existance foreach ( $sorted_meta as $post_id => $variation ) { $match = true; - - foreach ( $match_attributes as $k => $v ) { + + foreach ( $match_attributes as $k => $v ) { if ( array_key_exists( $k, $variation ) ) { if ( $variation[ $k ] != $v && ! empty( $variation[ $k ] ) ) { $match = false; From 4bda1c9cadecee5c218c773252eebad69b19dccf Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 11 Jan 2019 15:52:43 +0000 Subject: [PATCH 060/401] Code style improvements --- .../class-wc-product-data-store-cpt.php | 86 ++++++++++--------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index 83265ae5ab8..4c111166f66 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -9,7 +9,7 @@ if ( ! defined( 'ABSPATH' ) ) { exit; } -/**a +/** * WC Product Data Store: Stored in CPT. * * @version 3.0.0 @@ -910,12 +910,12 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da public function get_featured_product_ids() { $product_visibility_term_ids = wc_get_product_visibility_term_ids(); + // phpcs:disable return get_posts( array( 'post_type' => array( 'product', 'product_variation' ), 'posts_per_page' => -1, 'post_status' => 'publish', - // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_tax_query 'tax_query' => array( 'relation' => 'AND', array( @@ -933,6 +933,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da 'fields' => 'id=>parent', ) ); + // phpcs:enable } /** @@ -1054,62 +1055,68 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da public function find_matching_product_variation( $product, $match_attributes = array() ) { global $wpdb; - $matched_variation = 0; - $sorted_meta = array(); + $matched_variation_id = 0; - // Get any variations of the main product - $variation_ids = $wpdb->get_results(" - SELECT ID - FROM {$wpdb->prefix}posts - WHERE {$wpdb->prefix}posts.post_parent = " . $product->get_id() . " - AND {$wpdb->prefix}posts.post_status = 'publish' - AND {$wpdb->prefix}posts.post_type = 'product_variation' - ", ARRAY_A); + // Get any variations of the main product. + $variation_ids = wp_parse_id_list( + $wpdb->get_col( + $wpdb->prepare( + " + SELECT ID + FROM {$wpdb->prefix}posts + WHERE {$wpdb->prefix}posts.post_parent = %d + AND {$wpdb->prefix}posts.post_status = 'publish' + AND {$wpdb->prefix}posts.post_type = 'product_variation' + ORDER BY menu_order ASC, ID ASC + ", + $product->get_id() + ) + ) + ); if ( $variation_ids ) { - foreach ( $variation_ids as $ids ) { - $variations_string .= $ids['ID'] . ','; - } - - $variations_string = rtrim( $variations_string, ',' ); - - // Get the attributes of the variations - $attributes = $wpdb->get_results(" - SELECT * FROM {$wpdb->prefix}postmeta - WHERE post_id - IN($variations_string) - AND meta_key LIKE 'attribute%' - ", - ARRAY_A); + // Get the attributes of the variations. + $variation_ids_string = implode( ',', $variation_ids ); + $query = "SELECT * FROM {$wpdb->prefix}postmeta WHERE post_id IN($variation_ids_string) AND meta_key LIKE %s"; + $attributes = $wpdb->get_results( + $wpdb->prepare( + $query, // phpcs:ignore + $wpdb->esc_like( 'attribute_' ) . '%' + ) + ); if ( $attributes ) { - // Sort them into a nice easy array for us to filter + $sorted_meta = array(); + foreach ( $attributes as $m ) { - $sorted_meta[ $m['post_id'] ][ $m['meta_key'] ] = $m['meta_value']; + $sorted_meta[ $m->post_id ][ $m->meta_key ] = $m->meta_value; // phpcs:ignore } - // Check each variation to find the one that matches the $match_attributes - // Note: Not all meta fields will be set which is why we check existance - foreach ( $sorted_meta as $post_id => $variation ) { + /** + * Check each variation to find the one that matches the $match_attributes. + * + * Note: Not all meta fields will be set which is why we check existance. + */ + foreach ( $sorted_meta as $variation_id => $variation ) { $match = true; - foreach ( $match_attributes as $k => $v ) { - if ( array_key_exists( $k, $variation ) ) { - if ( $variation[ $k ] != $v && ! empty( $variation[ $k ] ) ) { + foreach ( $match_attributes as $attribute_key => $attribute_value ) { + if ( array_key_exists( $attribute_key, $variation ) ) { + if ( $variation[ $attribute_key ] !== $attribute_value && ! empty( $variation[ $attribute_key ] ) ) { $match = false; } } } - // Bingo - if ( true == $match ) { - $matched_variation = $post_id; + if ( true === $match ) { + $matched_variation_id = $variation_id; + break; } } } } - return $matched_variation; + return $matched_variation_id; } /** @@ -1590,8 +1597,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da $wp_query_args['date_query'] = array(); } if ( ! isset( $wp_query_args['meta_query'] ) ) { - // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_query - $wp_query_args['meta_query'] = array(); + $wp_query_args['meta_query'] = array(); // phpcs:ignore } // Handle product types. From da80c154fc32bdf1539369a576200a5119bbeefd Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 11 Jan 2019 16:48:05 +0000 Subject: [PATCH 061/401] Query tweaks --- .../class-wc-product-data-store-cpt.php | 92 ++++++++++++------- 1 file changed, 59 insertions(+), 33 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index 4c111166f66..fe8fa04457e 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -1055,7 +1055,41 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da public function find_matching_product_variation( $product, $match_attributes = array() ) { global $wpdb; - $matched_variation_id = 0; + $meta_attribute_names = array(); + + // Get attributes to match in meta. + foreach ( $product->get_attributes() as $attribute ) { + if ( ! $attribute->get_variation() ) { + continue; + } + + $attribute_field_name = 'attribute_' . sanitize_title( $attribute->get_name() ); + + if ( ! isset( $match_attributes[ $attribute_field_name ] ) ) { + return 0; + } + + $meta_attribute_names[] = $attribute_field_name; + } + + // Get the attributes of the variations. + $query = $wpdb->prepare( + " + SELECT post_id, meta_key, meta_value FROM {$wpdb->prefix}postmeta + WHERE post_id IN ( + SELECT ID FROM {$wpdb->prefix}posts + WHERE {$wpdb->prefix}posts.post_parent = %d + AND {$wpdb->prefix}posts.post_status = 'publish' + AND {$wpdb->prefix}posts.post_type = 'product_variation' + ORDER BY menu_order ASC, ID ASC + ) + ", + $product->get_id() + ); + + $query .= ' AND meta_key IN ( "' . implode( '","', array_map( 'esc_sql', $meta_attribute_names ) ) . '" );'; + + $attributes = $wpdb->get_results( $query ); // phpcs:ignore // Get any variations of the main product. $variation_ids = wp_parse_id_list( @@ -1074,46 +1108,38 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da ) ); - if ( $variation_ids ) { - // Get the attributes of the variations. - $variation_ids_string = implode( ',', $variation_ids ); - $query = "SELECT * FROM {$wpdb->prefix}postmeta WHERE post_id IN($variation_ids_string) AND meta_key LIKE %s"; - $attributes = $wpdb->get_results( - $wpdb->prepare( - $query, // phpcs:ignore - $wpdb->esc_like( 'attribute_' ) . '%' - ) - ); + if ( ! $attributes ) { + return 0; + } - if ( $attributes ) { - $sorted_meta = array(); + $sorted_meta = array(); - foreach ( $attributes as $m ) { - $sorted_meta[ $m->post_id ][ $m->meta_key ] = $m->meta_value; // phpcs:ignore - } + foreach ( $attributes as $m ) { + $sorted_meta[ $m->post_id ][ $m->meta_key ] = $m->meta_value; // phpcs:ignore + } - /** - * Check each variation to find the one that matches the $match_attributes. - * - * Note: Not all meta fields will be set which is why we check existance. - */ - foreach ( $sorted_meta as $variation_id => $variation ) { - $match = true; + /** + * Check each variation to find the one that matches the $match_attributes. + * + * Note: Not all meta fields will be set which is why we check existance. + */ + $matched_variation_id = 0; - foreach ( $match_attributes as $attribute_key => $attribute_value ) { - if ( array_key_exists( $attribute_key, $variation ) ) { - if ( $variation[ $attribute_key ] !== $attribute_value && ! empty( $variation[ $attribute_key ] ) ) { - $match = false; - } - } - } + foreach ( $sorted_meta as $variation_id => $variation ) { + $match = true; - if ( true === $match ) { - $matched_variation_id = $variation_id; - break; + foreach ( $match_attributes as $attribute_key => $attribute_value ) { + if ( array_key_exists( $attribute_key, $variation ) ) { + if ( $variation[ $attribute_key ] !== $attribute_value && ! empty( $variation[ $attribute_key ] ) ) { + $match = false; } } } + + if ( true === $match ) { + $matched_variation_id = $variation_id; + break; + } } return $matched_variation_id; From 938992cbae7f0975057af83e2d894b401dbb934a Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 11 Jan 2019 16:52:57 +0000 Subject: [PATCH 062/401] Exit early, add back fallback code --- .../data-stores/class-wc-product-data-store-cpt.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index fe8fa04457e..e6836bec33f 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -1123,8 +1123,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da * * Note: Not all meta fields will be set which is why we check existance. */ - $matched_variation_id = 0; - foreach ( $sorted_meta as $variation_id => $variation ) { $match = true; @@ -1137,12 +1135,17 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da } if ( true === $match ) { - $matched_variation_id = $variation_id; - break; + return $variation_id; } } - return $matched_variation_id; + if ( version_compare( get_post_meta( $product->get_id(), '_product_version', true ), '2.4.0', '<' ) ) { + /** + * Pre 2.4 handling where 'slugs' were saved instead of the full text attribute. + * Fallback is here because there are cases where data will be 'synced' but the product version will remain the same. + */ + return ( array_map( 'sanitize_title', $match_attributes ) === $match_attributes ) ? 0 : $this->find_matching_product_variation( $product, array_map( 'sanitize_title', $match_attributes ) ); + } } /** From 5016e4c8587ee45a8ba8ee825a9ec53773d62a24 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 11 Jan 2019 16:56:51 +0000 Subject: [PATCH 063/401] Removed unused query --- .../class-wc-product-data-store-cpt.php | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index e6836bec33f..349c1ca81fe 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -1091,23 +1091,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da $attributes = $wpdb->get_results( $query ); // phpcs:ignore - // Get any variations of the main product. - $variation_ids = wp_parse_id_list( - $wpdb->get_col( - $wpdb->prepare( - " - SELECT ID - FROM {$wpdb->prefix}posts - WHERE {$wpdb->prefix}posts.post_parent = %d - AND {$wpdb->prefix}posts.post_status = 'publish' - AND {$wpdb->prefix}posts.post_type = 'product_variation' - ORDER BY menu_order ASC, ID ASC - ", - $product->get_id() - ) - ) - ); - if ( ! $attributes ) { return 0; } From 510ed70d1e9fa16521ca75d1a7cd9f3718424db4 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 14 Jan 2019 11:32:57 +0000 Subject: [PATCH 064/401] Fix check during install to create placeholder image. --- includes/class-wc-install.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/includes/class-wc-install.php b/includes/class-wc-install.php index fb7a14b3dee..5717a539474 100644 --- a/includes/class-wc-install.php +++ b/includes/class-wc-install.php @@ -1127,12 +1127,13 @@ CREATE TABLE {$wpdb->prefix}woocommerce_termmeta ( private static function create_placeholder_image() { $placeholder_image = get_option( 'woocommerce_placeholder_image', 0 ); - if ( ! is_numeric( $placeholder_image ) ) { - return; - } - - if ( ! empty( $placeholder_image ) && is_numeric( $placeholder_image ) && wp_attachment_is_image( $placeholder_image ) ) { - return; + // Validate current setting if set. If set, return. + if ( ! empty( $placeholder_image ) ) { + if ( ! is_numeric( $placeholder_image ) ) { + return; + } elseif ( $placeholder_image && wp_attachment_is_image( $placeholder_image ) ) { + return; + } } $upload_dir = wp_upload_dir(); From 549a0f01c0fb9108b032140790205fd8fb592f15 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 14 Jan 2019 12:54:54 +0000 Subject: [PATCH 065/401] Placeholders need srcset and sizes --- includes/wc-product-functions.php | 70 ++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 91912cb78d7..bd22b667223 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -271,11 +271,10 @@ function wc_product_post_type_link( $permalink, $post ) { } add_filter( 'post_type_link', 'wc_product_post_type_link', 10, 2 ); - /** - * Get the placeholder image URL for products etc. + * Get the placeholder image URL either from media, or use the fallback image. * - * @param string $size Image size. + * @param string $size Thumbnail size to use. * @return string */ function wc_placeholder_img_src( $size = 'woocommerce_thumbnail' ) { @@ -297,6 +296,58 @@ function wc_placeholder_img_src( $size = 'woocommerce_thumbnail' ) { return apply_filters( 'woocommerce_placeholder_img_src', $src ); } +/** + * Get the placeholder attachment image src, falling back to the default if unset. + * + * @since 3.6.0 + * @param string $size Thumbnail size to use. + * @return bool|array Returns an array (url, width, height, is_intermediate), or false, if no image is available. + */ +function wc_get_placeholder_image_src( $size ) { + $placeholder_image = get_option( 'woocommerce_placeholder_image', 0 ); + + if ( ! empty( $placeholder_image ) && is_numeric( $placeholder_image ) ) { + return wp_get_attachment_image_src( $placeholder_image, $size ); + } + + // Fallback to non-attachment placeholder. + return array( wc_placeholder_img_src( $size ), $dimensions['width'], $dimensions['height'] ); +} + +/** + * Get the placeholder attachment srcset property for responsiveness. + * + * @since 3.6.0 + * @param string $size Thumbnail size to use. + * @return bool|string Image srcset for the img tag, or false if unavailable. + */ +function wc_get_placeholder_image_srcset( $size ) { + $placeholder_image = get_option( 'woocommerce_placeholder_image', 0 ); + + if ( ! empty( $placeholder_image ) && is_numeric( $placeholder_image ) && function_exists( 'wp_get_attachment_image_srcset' ) ) { + return wp_get_attachment_image_srcset( $placeholder_image, $size ); + } + + return false; +} + +/** + * Get the placeholder attachment sizes property for responsiveness. + * + * @since 3.6.0 + * @param string $size Thumbnail size to use. + * @return bool|string Image sizes for the img tag, or false if unavailable. + */ +function wc_get_placeholder_image_sizes( $size ) { + $placeholder_image = get_option( 'woocommerce_placeholder_image', 0 ); + + if ( ! empty( $placeholder_image ) && is_numeric( $placeholder_image ) && function_exists( 'wp_get_attachment_image_sizes' ) ) { + return wp_get_attachment_image_sizes( $placeholder_image, $size ); + } + + return false; +} + /** * Get the placeholder image. * @@ -304,9 +355,18 @@ function wc_placeholder_img_src( $size = 'woocommerce_thumbnail' ) { * @return string */ function wc_placeholder_img( $size = 'woocommerce_thumbnail' ) { - $dimensions = wc_get_image_size( $size ); + $dimensions = wc_get_image_size( $size ); + $image = wc_get_placeholder_image_src( $size ); + $image_srcset = wc_get_placeholder_image_srcset( $size ); + $image_sizes = wc_get_placeholder_image_sizes( $size ); - return apply_filters( 'woocommerce_placeholder_img', '' . esc_attr__( 'Placeholder', 'woocommerce' ) . '', $size, $dimensions ); + if ( $image_srcset && $image_sizes ) { + $placeholder_image_html = '' . esc_attr__( 'Placeholder', 'woocommerce' ) . ''; + } else { + $placeholder_image_html = '' . esc_attr__( 'Placeholder', 'woocommerce' ) . ''; + } + + return apply_filters( 'woocommerce_placeholder_img', $placeholder_image_html, $size, $dimensions ); } /** From 198fdce1bf05551430bac53934acd2ca980ac336 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 14 Jan 2019 13:54:01 +0000 Subject: [PATCH 066/401] Larger placeholder to allow more resizing --- assets/images/placeholder-attachment.png | Bin 0 -> 215889 bytes includes/class-wc-install.php | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 assets/images/placeholder-attachment.png diff --git a/assets/images/placeholder-attachment.png b/assets/images/placeholder-attachment.png new file mode 100644 index 0000000000000000000000000000000000000000..82c49a723167c3ea0906fca63fa4bcb954567160 GIT binary patch literal 215889 zcmeEuRaBfow`D>SAh^4`ySux)L*p*N1A#!Gad&rb+&$1V?gY2s?!kgh=dPJ`-|w1- zS+o9!e(L(F>Z`Ns)H(a?`Xba+WRVdF5Z=9ehb%8ArSb0F$GU$%c-VLE-e=SN(1-r< z(MDWZ{N20yIK*ebr+4qf-pNaeYx%rC?S{)Ymi4_5EX$)Dv9D=}ajX3@1RLyD?khjs zP5&LS?OhA~=RP3c`yCd-#H{--?IH1K5??CX&3>{|v&MvoA)&gaSuWe3E2moCe);?K z)|0VpL+a^N-F(0N5{(BOg4ln2~c(0vL_9vPvrm5WS`5;F zKFIJ%2lqH~Pe2U;rbe&i=dpxwy%C&(_)qLAEIs3L#?%urdiXZ;6qqVRPskVI|6c}K z2}0?rGcS3(K3ks1Y7Idq@0okf0C~69a5*>GJXXGuoy z7p-rTS;+Z*9Fauv&0xxj-lku49ts(Yi{6uwm0=DPOxpbUGvBg>-dRaCHz$#s=`ke7 zW1GF8taEjiXQ#9gAF;cLL-g^NW~q{~aZV)@y||5ytwS6_(oPXwDY~O)`Yn8@+^x$f zy8s55N~T6RMc7Ck%`)HU?Ch*mMouc0p6yXSLk$$UldegRek21dx+H;=WDgf)mwv|p zYTVx5g~*bAF3_q}vPjhGEyqn!*sDJfV!%WQ4fJe=DDZs3 zk5K5ly0kq#50BDQ41SAWs&dkDoU;RQRa)37N|WtH;)nC!;$naKHEI|91)n;K9WWX?YSj=!N!x1 zVEg#tu)9*vO;RmcDgf+EE!`6wY>uL`!bIRD#unhB0Yz*C99trN93Eo(okiM zAlu#rXcg(YYk;i1p?LC@=6m5Hg@`nqys(kN?3 zYhRqI&V)|`WH%-QX%ryU$=zTG1oE- z+b#ho0uw!n=H#`@#$$8~!o3f^O;zJEGYr_(1(jij_2!{g9}My38)jVAoG5Z?^&6d` z+@Avf5$DMgo|pc zaQRQ7Ir_3(5|$311H@}DI$PS@#czLBT#xB#Luw{lT(>{VhPBg@lB#6+^{bf2`Q`F?h5yU;r}s+W~^h{R8+sqs#2IEuBViDQmT!-fQhb>k)ag(T8Nt_g}~8u|=#F~InP zws-Qnv_>Yn;W?qrU;Sr z$Vre@D$U%iI)l4LW^32p;^`&5FwULZI@kiCP}Vq)K9?DWDCT4oI}ISh8&IZ_rOwsk zM|JV@1FFW+Uh&~6{`_Pnw?o{l$}c%)uwEt?$EOkS6YIAlMqs<64dMCf4NxO@+}+Z4szq2T)K zCh4P$v}AE8z*2O(h75%H`VUHd_qp@i-C~5qiZDi*Dk*W@|8mo*priZ%_KAeOVCZNWprBF^fO?StN zHuL+IaAG>;H{|kD4CtXz?iKWh<3DX55%da z<64-Zj6++WPSzO>vZod(4~4Xr9M|xV?&{D3gd2t*!!psYvtR^s-VVY5IwB?MuUrUE z(a!8gsq^rZ!z#+428|}Bu#KYA(mz&K8nW%s$6F9IQ+_0ApA@s8{qO2SCoc-9( zSDHr%vEP>Tx4uvxwjUDvod6lD>_!me@ltw(pgRHz2`S30Xyp99&VHq}$Zf(=z)s0v z@{U@V|9mW7?JMEbR`P*z%cllaTC!e^VV0~$xt3;|$g60~_|M_2>vhDCerAr7+V9?n zQ`1n=mqXdD?;lWp8QSQCxdhyt2u3;ctlM^8YnpiMOq@eqX24aHUw=hMLA18zU2?Km zCScTpsn|UoWSp6pF^q?}ufQrP*#``3MzQB(Ku;Ot3>awir7n>ra{&gms|ai@ogD|h zK7T5x>ST3XUqCCI*I+?h#3zCSNkuj0M_Q5Chas@Tvf!mI z*X*(TbWc{X;Exg@%~7b{^i*R9D}4QQ;oW!K#_p>L7nGhsb4}!lS8xZPt6S1nVE4J9^BsOI;L?dGOUIO z(cJnK?4C)oLS#}(q=){Hvc8($h~znq@OATPJ?+ph+9QlB1{Y!?M5m4-=pK(Rn{|3s zP)NjmvvEYg%#x-qtL{nbZbE= za7;O$7^+|5wLCWhB9WF)&kL4L&nwOfxdqAsL7h#ly>!Az7H)G`uvZCTqvn3d5s%|N zRilVBMND)WIxHCZ!)){%`pPrBRtgW@s3ywd%2&4VDG%39I*S}d{bjknD-7tN4FqMd z6>@lDfowouySW;9wIdlV6m2TwMYGlXKR81*K-X(ECYqjP6|ZbIdD z%U%S;gtAvNO)Or~NYYKt73_`sS5!S*h4)2Ys?e1G!TNd(;O_l{qQDYwqLfYpVGi*m z_RzU7G2i3aI~FEXk2MD~(tO!Jt&}uc9+Xu{Y^k4VkC104b(=5L(Gh zgx1T!6%~Znk-62soW=$*E)0m}*S1b!edJ;LVDfEs+BVa+7`UgDQH-cnrww{0BW3U| zW?r_+{Ji6U&R%m0vaBWV?`ZWZnHG%{>)<0OF&sD6JEbV0+*iZF66(QN)0(i%M54F% z^611nd$vV_?nq$$p}88+)&o`6N*-htX<-Zi$vKiT5RJ0Qh<{EYdE>)GbvaTEkiRVs zN0^g^smxin*63QA=r{T+uiv1Qj11v?Li8QYb;gtRg}AGWQxkqF&0Y!+;#oL#7Sd4m z=eCBE*9UbbusfmZmUs3Qs1TnCm1OHjF~qH7sj6r`@<`{yV_VhpKewqMdKJwMwJXVw z%yoz=QSPCa488wILM*==%uep5V`Sl?pp={Fq)@TL0$K0yJZS@EUKLwmIlTP!9E6ER zSuKGd#4s-`R`!Z||A|v)ZsQ!$z~{6vT$Jhw=v#gW9)+VXeU_bnnaN4O@uM5fAB$q= zZ}q9un1xA?TIJ+JRDA_BDXP<_Eh#D>uNLU-r)|f|k3?j;Y(m4}?a#DJ*@@jr3Zzn_ zDPWL5^p6;eaWa~Q8U}WhPW5G@UtB|maIg$Kv0ux}u?$XWiP@yp%(M#2HB0e^IXmeb zusLqSB(xc|b;K}Jx2e{f8o;Ro@`YKdS(OTF9C;}})Nxq9F_~oA4|dRKC(>gXh)YLF zb10fcsYlGzSr4xNkseL!ped=-M!hb>1ue;FNDvaC)sQ|;#}sz$2N7LMR6HaJVb-)= zTRH$vM+i$oK80CYK#s!Dc$`)+Ci%{&3c0Es@CQe|5V*yy)GO|46{1>{K%3(%ly>yd z%c|hx$mGf7QO6oStM3GL(GV=9Av(lJezyK>T%>SwhMLq)vuHzHroin2DmuL(+f$Sj zqgbQDfMs+AIekHVyaA4UNBusgFG{hkgvcC#8~}lpoMdACkr_Vj~rUy zXi-;Sz_yBxU1hc4!Eke*k| zSqUhxzNf)mhQd^#9@a;@h#^xQk5N_@@0*jRe8z+fw@V84K=R0+&|_TpibQVSTFN=H zo1N)d1rk_ZMq-Bd7-rE{k#s5QYtPs^+fB`oig+RB_dmYGCHU)^V3v^-Hod+V@!XEY zo=GQLwNfNS7X5n_7&>32Yq)3Pv$fh^WpZ2jbM;lH)(9UO4_=^f+9;uh5eI}grYDs! zo&k1BCra6TI{xWh3mmAShDh&y+W{pns zyK8!6bQ_TImDElpe}P0E#K@xxT%utjRwbgwSp5C-LIYpej9X+CeZN9ZCqj+*!Hw>h zrDu&^QuDrIkMrQxIZ;qQI+Ru+(!hm>GAcE2|Dj_3n^fw3{V!4q|39SE|Aby^32N@` z6$Kf0ibTi80uOXSNIxVc0n694;J>$o;MD4ulqDz;wKXG)yBgPaK1eyuk$3*>Iid_K zpdFyAtBZ$^&jZQtch4z;cXDznC@NBhZ(3$o`yJ+BGgWXTxA*f|Ku|!^&@Y--NQg)V zD9dDEBP&b&O=WeXrp17oAys@24mxsuG}AyhTP1rL)+_bLup2F5qfVOxnjKBVCnxTB(FfWL$sHb^ zs;#k5WJo^xc_16MI8K--ce+4>Ef|Z?szLxVKNdMLUKDbK-C&owo(|S_0sOES4uwoH zSE)bt8(!>o{&y*>%^IKmw2&e)8rIUuU}lqeqb0-=TH{~c<=aJc4fMs2nbFqkP0{8V z80vGsBJRMIM#+O6EX0}sum))97M)ecT&JmAtDazpK^IALFC3E-uR*v02n9M^PuvrZLah>F^h3^*b-f9gv>CJ*kJSW_Iux@*z75&`0$-@fL zZJdT(ieu_2EG+EJR>-Dmj=)5R3rNt2L~8&#h1hv1_tZepFZ-kU>TiR8 zjw}F}&xe)wJ-9X18s#8!%&y;^bS7hLsh*+7D2;}7Va9;i&0*mmNb0Gt13H2nPpzqli7A3PzYlbs%EJ^>drRRSYau?HT>KhbsGi^)X|uXX zU5cp^XTuld;|j5cglf;?J)OY^L%Q#u6U6G=_Ya6f_LE~jRMu*cXL1AdeI?mej(BDC} z$)Xc%*G4 zj7aM!5((OAAi#6rS`T~fLw)^LW>rWSV~KsxaD5z}Lt`8K9h!pMz8e}16+Yf$Vx`9& zb9AMHEL4AxS3g`(y6Ml__%nzHfLmQd3RnDlY^W(1zJX=Zxqn#N7NR$d(t)M}sQhA9 z`<4IN{-CT&KSzOgxSXv4Pm`?3kBb!>`M%s-f9BW|*=l!=BRb`DW8Hi};0}(&0LwY& zK4N`vJqg}jRYb&b_M?qaO*WR~s1&Z;&>;LpnCO}>uN+BT?mEBR2h`IRYwzkJy~??^ zN_VZX0B@xMR3Sc5q5HMS>E&gTfSc9;!L_%axy;qe#?*P9gY;XhZ`X~0xl(3;m_vnL z>qhN7wjqxT_nU*rAHPnlgqT}fz^4+I*CSx6{^5`#FDg|Xj$#P9ipbTHTS~k0dPm*! zm5oHJjO{u{s{W(AQ1b8SITaFIZ&TI(N`KoAavO)t_gVe9lyYWmu8stF?Ixa%+*Q|h z{$R43L&0B-Hto7j^2N;QiRb*tMt9&GR*(q-t~ok8`mE)c;Ovqd>3=gPvXB5J(~HI^ z6tzwDp~?R|HWypIx_*jb%kyW$^D$vQ?JWEU9-EM0I~e!KCG)G4E_h_k*y@cvb zMiA?3=Qy(tf;}fesZx$c#M494m9Hd8)i#NdUqi1_J!n;GCVHU%{`Ja~;po^mz6_Ub z*$HbIhDQfFKjwJFR&7`~N{g2n+1feG@hr#v)ly1F9YK}PXr!W8NLIxfal&`$m!Z8c zo43Cz+b6ZZ8(M177_sP@noc<(xI_KiS1r&SmxYNV z(WIw>J=izw4kJo~9QTezXELiZfI0_9bgzvO|T5#h-D`y=u7$hhbavwea(q zHG{^^e@xr_R$Up$u;^AXf^YJEgHLwZF(RhFJ2-ZH$EH;W501%rZn)91k2rK{(U4=?IAOj6!`m!?K=@)fz z3dx!Ex?{3lF=n00WMxoU{s2j-?%>y!QiXLa1n;d-Tws<<_qSH<#xL{7u2+IA$S!`X z(k=ThhMOJkWp+9}59Zq5Ya1BGTd`^HPqBg{2!9G(C8(Qp&`1{|CIR)zf=wa{($~)q zEh)IXTpo?J7D5xH0*i+g_1b1_iwdIGrgo|WI4(pUuwd+Lb|{oseICnC{dwuE5kN=bwV3xD_eL($;b`Pp zYtl_a?C&^5qA)sYza&F;vU9M}53(ABWS=v2MN z_r@7H@V1>*&w2ChRTOB`v=H^kSt=q>tQUQ!*D0G8jXW&CR@~F}Wxg%wdyMs790#~d zw&!_|rZIyT>K2y7!FI2oLkr=G`}tbtvsW~$z#1$h02^W)1anb(JtF#2JGheO8qINV z_;jwm(ls8IT=}vI-DFna-UQ$^SQfk`OH?hdJ#o#b>XgijAWdHwb!}G9SJ(D86E#fR zoNo@_euvG_-T`Q@{f)#tt zwPLT4?IB6{>FD^lZdGbfYGktE{m$5Ljl@titImb9mAyNrV*Hz~VRrV({u}2h--` z*F|tB?iEt8LhFB5lh<8rU9by@lVL{)SxgyaNb@J@OQGh z7TNR8E~q{c3y!+}qm<@GYVS7ZJ6qX$D)4|D)@ejx5>Wip>Bwphl$MS)ub`>MJbWq@Lwgea3s5gxNlk!3TKBA?($k_<6@z!$oBY`rCO0t zzW9=yI7_3X;-mGNI;>tvqhc*>;tNotur3vt7Qz7YMJZ!1k5+}BmUSD$M1E-en;{W# zBGTXw{)`FPcF?#-gkClJfX~OG`xu$lDLf0d-IojBHp@Q}_3Ean#$Zy_WD=^xpAZ=;yxv+wIH`wHE!*+J zD`M97KfGl(URr(KtX<@lL|BMt1XRp;~ ztkSnn4zndR#NYfIJ%RfvJx| z867EI@avbJZ=|`lAQsu*K3BJ2d|o7A$3Y_wvo3&41x6`GMj<#7SkRZCz@|qT+;GH`U?n(9|2+Z&#DDNN+1c(zoLq zl$MF+Sy8@JW#JGNhEw*#D>UneY8$zkZ_`@U4@c}sVh7x-6mOVRXsl`dbtmAOO@ zvjByov0$K@t)mKCQx{~AaO2S_l@H~Sj7>`57KowZoeoJ6ehv>vCef|YeU%5KQgAnk zQc@Q*qCVx>tMH${|0m1x_E&eE{2qz;y==bD$1YkBHoxitJRgjw?9b0t=kb2P*zc5T zj4&#C%@hYmM9@a(@O*%I;xALv=&m2_rJ;>?`oES3&6fJj2#FJ)pxG{A=`|m-v6q z68Vki{9l2cQosKZKX1>hZ#d_jZ?CRHi@i82s}^|;ZgcyWr;SstwU z_6CfNg$10_v(`Kf;7|7RIxwnkW>J5W-JFECu6MeK#n*Ip3mIvaZBzd%2-|cow}+>v ze#T15os}$+IJ**x6&?XeOT*pmtzB(H)mr`OSxQ$M%9p*byiO}vB!Sxzm&E~uyv|K+ zwR}$?Ha;#Q>#1CP6N5$`BqStY2E&ww@z^Z2V!!*8u#J_7gs(T%_&=VB(bTslCG8~8 z@o@(*vBDjRTkB$LD>qaGSns$6tgm<$RChD~xm8wwMt`9j)9VTf3UY-m6tMUCG|y0^ z#|{a`Nu!*-+F1!-Xp!OL#$8KP|1Ix^r3lxC3PA%OB1V4xD9CkD6Jn6hg6mdem6VWY01VEd|%07%W{uxgkliTViT2OanJ zeDrj*Hj-*;TX~U&M+>@=lm18+MXJhB2n_5wG(O8^6WLX&?6B)-?FGXthotZ7X|N~` zI9koKx7fF2!t=H#BF|Vq0t={dSUO-M(@p?-R1e^{XZ45qw-a3rq|JFksAKv{*^~8L zs*+*@U-(s1G1NOd1te{3%Fqo4DABXFbtXMy)LDXy4BN~KFm+&y z-uuylV88cNF~teL@;R_^5jgNIAPf=gF1)^8W{AFd@Lh1%e&*z|xorR+*rHfV>eaUA z)=aWsew!AD3w_}tZT7j^c7@7~taiUF=-b$#*vq7I9!#@&M(*2Tx$C*}=}DkbB6IR8 z67b)M`j{Qz>l^rD03wAVrn<20OuyQBK9mLiO{=q>P%JRrvl!;OPfh}>4-w&7Pi0p4 z(BG#bz* zXQpJ@%f6oye)B)tyF~JWI{t;pdONw{Wt+Sre@cDzcLbhI*5*Y=IP%%PgGXD}W306ck=FN@xUE~b=XnPquAPzr&_^Ow3GRxNsh&LJ$!ZZ`3fu91MH-eHX!yxC%B zmKeNiT2w)1hG5DahEtxB$udT!7{ocE@Q=e1!G#XO!$*!Elc(ubhK<0^=yQTl=-E;f zzbW5?RAH%e<&xCLZXk4khiJC)t3puzY~v~TXIM?oO5*AoS@b8nK3%1+Bh3yE_O})` zJ$4_VM^TK}H(cKu-=h)*x}?GVbW@G2zJ3B;rHX++KPb?k+k`u@;gMm7aaGQvsUdxJ z%w1o4_kiP$1d71!Ay5+*7m%m(sgy}drKN^`Z*V%qbpcnn%U0daD*ab*D1Veukz2}$ z0xy{st=EqDLbW$u%sd(`XmDGU#hU;O6;c{ya0eBeTNGse`o612$fZ>DMVAi-sktwW zoIF-|O#1p7K2`d3^S59|sHsQN=Rchxn4o@{CfDQ$QzB&RHM9_~;+fBJ5lT4sE+b9pl%ZEij#kRzj8ChDBzIR ziVHpc&pzqhMrVeR@F*$qq}~2GzG`(`Hkc~>#a-KK>gv{ZRca{pi}EW|1SE%e%Yw9X za=0IXV%DTjWp$ ze=CrELP5t_K=B!owTd>l8&Ooy+kfBW!GU;l+LJ>(!WUJ7l#R8@ZPos9mD|H-?++y#_p8C4Iwk=8V1v z3g2sHjdYkBgq7W(D~KItMV|PJWsr$MzSu@}%_;^s_!WFh%1TumLAF~f`o_EU?IN&O zUt`UT&0LNbr;T!yKX8ARInd8>ChPR)hvouNK;XAf>z>Q#X7by;;)4B@{m(yFNH4becVQ*uVSCL1>jHuss}t7Dp%3FWZw&s& znwTfpy}kFH7mFcJT|XJ~;o!n3=#ReWUriy0J1T3}{%v)@true;`w_}h%p0-cHpJUs zlGX!Gl{jS<+5OZr{K5M=CImksk(|QaXm+yZ{ z0e#n05$Q3vJ5~G1lRG8cD{*0LN(^kSsgR`ki1PvYI-@LaR&5vl(n`L|^Az;7Pnq`GM8w}+G%k2^?RO&7zIaY(A7Pr(ap-TA1D{xtrtwQpZk z2T}=VKRzD39ybCO#xu5RHQVn)LBoI2 z0*>Za8ayO+2lO;QW-uEKN1}W%ADA-UL38c5C%O|He>~57=F0XMwX-J!-@QjDRURKz zqulN@>hSAFoz@SwsAZUD!4>YmA3RSPy??zmyOEa^0FXc=U#f$6xb81(h^{CIpNs>X;pFljlcn^yO#| z03Mv!Cyh`@nx`No$Y%~#N+s~_Afvuf1W-xv=n(r6ZTY7s>H zC<~Tmq%Uaq@}Q_{4f5Po6LHB{=UKp%O%`Tm7ZV+?JSgn|+54KR#gleo_lncS48yx#;&&Iv-GzVpZT zP5qXGp6ydUDa?$eI<+Sy9;7Yjc25(ZH#iZzjcdYQd>)4fIC6F^E(e9JHS{Lz?TGU0 zx~XeT!SNW34~1V=bH(6Q0{pRo?HsZ4QAQ%G^Q&_u^3S(lSC0~1x1>AY%%>)6TnhhQ z@G4)7sHW@VYle~69{v#l#SrrBb8hm&LxTp`_q~mds-iDkZlSLObUgAk;d+Aca5$(> zn{O|uq0Y+5H*17>4F|ZoK-sZTc$EPwNyU+%V!Z9ku&Awlhu1nx(+HUseQO!(tG=Cd zveGRUi@1$w*C$$M)Ho&+lJgl}SVKs?B{_ztguKu1ZJvIQDdGF3(LJ6sPd+HfDtG$U zg|?he>hd#w`TQnkBH8!^k=5LJ{HFnj;+{^=LDut!BB*4%^#tZKQTh*bQKG8fZ)cmY z+_?`e04U73k^U0fjAtP@ntZNO7%&41HdEv_gJFiJ))F!YlWE865gVwj2Q1N7wkR`uU^2V?eTK}xq9yx!bKr+?%M<{5#QUAWEE%Qh-1r<4jv#k ze7@s3y+R-=;*it|MR_K2KX?pTqjg54M9X4p(@S+|N=wI@loG7DUmPnbf1T?-+y@wG z7?O6T1kCh5ZdJRyI1TWG3d%*fErN(eWvvEM>L&5HA&@3l^7Z~qvy@KX#&eyDD~ml# z#34otTzP6>sD)MiOJr3UwvE?$k0Pi`v- zxV=09dL}c5N58wUSQD5OY4KQczy8JFN{~$pjoNg}fVcoBFja*}o+2ZWGwXge&)_22 ztJnT}^^p00hut@X+T%S|%ph}DB)jDuZhL+-7-;#t11l9icEQ>ba;Yw+kfiaogxJ z1Q*Jt0^c5cU(Guma%@Q&xoM#~8?APlB6B(YPxI8vYa3FGH$b%gRo z6Qo1kJeu(9+=$+< z-E6f6K6&@g$dX#PP)UC`#?)6wd2w%T(w+JgaLy`PReP~k4p)5dzy3>+wHigUOr`!r zyk#K6zmX-ZFeR$DbC5j#w%3jNGJA-;?s6+^XL^FE(6(I)F#=W|lfXTJDLUd;n?@UZ z5-8};Au^7-zZV+{r`_*6y;z`k@j){uPlk)+pe-~0=VC{)7NM=l;-_$$O()w3Y+6JV z(cHjO(nDSh{KVVAxUD3}kV4WAtWIeo-gS>g^hNE97qL;mL8nW=$>inIj%pYjsR7o; z-#f&!+aXrbYdJr7A(rI5tD>6eD%tqIO*+wWdSba*ye`NWU8mQVyP8wSEc+H)-Wwe@ zdHXP^HS4Y=;{q+D5_9du52XP3w$ORDL+|W*7uhyXgG9#(sP9+5gD8GK1 z&vMmGWhU*<+E3aP1Dh_!in|DZhf+uQ20T6MV#*kyoj{0z1z*Y-;XM-a4jYH)FtQta z@k^2D-*V=Nwj&_n?RIystSv+sdi@8W=90`OLyEALubR265$m-cEjKxuJq)>4$7d!@ z$!j%IV_E3oa2etys?{aA7j9z%EmnD$5ejQ#kU#t{T5xo87 zcJ)&H3Vj3Zz;W*k$^|6k82;kXctvEi6NK|RQejaI40UuRc)u*R#rN(CG_p7-wIy59 z-u_Dfu?5xVs|2_ScK|(oHXoN4h$ic4R%A+5S|i-uDxRSD$6hM!tCP3`h2X4OjT}P) z7f{M__Xh;BA*X};J>`#l_bgC0+NXP>B&~b3#&V)#;_uzIjA0*YyPIYoleB)QUew(j zySvZxawhx82TUeK#=KdwJP-eP?6)IMoA95o{oqW^PIEZZMf^ijHaW^%BVdi>gPXEW zfN|J=@6{X`WpI%$FHF!+vZP~jii&7Hja%wqw!u-1y`jfpx`7{{hp6HL05M=2hf;2g zvL~OV_Mru6&q4ROp=pn(%(ysG%7l3QVj%0?9TTP1kDqm=QUrI@1dxJpDjo_Ysqb>z zDsx5byu%p5NJ!j)*awBQFxzAxOkI6372w8lUTR6(KUAJM4eW7)E*W^*I{sxKJR z`@zG>3^$OopTCM5LN^x<2nh>53f5+eNDoI(EJ29j6`{W{$t>#Q(QeA^+Dp^*7$J}Rdb^H3wiR}-?u)kF#5()i%*$JswE|YzH)p;R zP$Ly@jc1Y2Zd`LIgvz>t*|0-NIC{kLBp*wt5+Ukwj!nX{m&dwh{G5 z2G5rI)3dgYYuuJ}mg;0Mp{u6UxfH)?F~A%NZZ-o}SJ4Bur#rOUo;1taVD^$wq8LmK zyJ13H#Z(`~105Q~h#{pg{Ce9S`(oyv06df6Iz|pEDpL2Ko-%#)L^7rHFp{!z4(TVF zLUj!fqhVVPENFYWTA_AMDZr6~?mPdirOmhR9~gEO*b|VcIi(f~kgKk|7x|!B6T!c< z;(T+Z_Ew}4hoFo=o@iT4C;?;qFB+ye(V3o_T)j;85qXsIf%XR1#&e^kLe6K9u{GX} zFpozQIUT$0B?ane@R?Nx#LLATe?gx}pOfB@GM;X26vADe&#pBj1KhPQmPp3KxgUjW z%k(lKZin)X_YoO|y)qHGFe)W0b4gi8CGZVfGK;=?y$B46PR7uy>L(MQ**O_DhZ&Tj{J}t&UnQ2E$!7l@ZN@Rp1Rl;b=(~yP?yk9&!Xv^5OdV*A^@+DCnOp z^v2NANPm?y>~bKQj@n7dEGLRypTdR=XEkNKb8qQ!LHfrG^?fUA|3XNjNXwh; zd$?+@TWA-}836OqE%(M|iu*UnpB}aCdlxg(R;tQTN*RhpuJw#Kkx%)!H6i5W`|;^l z4ZP2774h;2esO&9*$MH(!#cky<@Tbcq$1<@;^S^@hgQ^r{lgZnYdT7KEH3H=>ZN+@ z;Gy$H4*GVG(eV6TM!qP{@o4ZXAA~`V6I6 z3G?D0hG=H{d9dx%qrj3+ec(^ab{{)g+Kzm1F`s@S}5@h-enToIwva3)?k34K@u zd)sjO5~ZmMx#`Ztt>#UF;kRwn|?IWNp`aRJUhR!#~I-*p{V;dk&>B% zxJe8H((fN_AWx;>j5CiSZy&ST#iZ6sisHUo(qh~$Kp^PhHu1L`Xo*7RQ`UgM#`Fg0 z(V*@Ae224Mr%&c4heznRPq0JGZGEGiL8dXQAv9_pNWliQ8s<*J#QdE0!2`u@n~C^H zlW5YSf_aI@OH_m6GS1i97emhAWvg;-3w#s3+7UTl;vu6_Y!0Y;#S?Mhoy>6?#NV0x zcThspJJK8D{fF0k;}imC$Dg2C6{l&MPCGq5f|P1t+*#A?@YbglKj-ls=CtHQzALqX zV%SP4Ulaxd;RG?;4ktZ>Q}Pegk(Mwc)0vbddU)67Bh`*YUxVt zK_}0Oa1O7cB-{&`KjDHf_`Atoj6hqK9Q>uuk2SGsqYB`Xx-rz)mI9MfU^M0=EW0nw z5{_P=fq|H#d8rsQthg=f$fOrJ(gW{sn={>`%aqkSqFStx;pE_hHi3{PGU`_!SAzGx z!IG{t zaK}W=t9Ep24e&QE|K4bt^f-KkaRzY1+^+9F{(QL)Z8W_?AVl~w6eSbFiP483AA{7; zw-Jn#9?rWVha5K;Y2kvL=&`Jgi2!ELF|^SB(X6*(sW>Tw<$ThYoClGP-5wAcgnwVZ zrZzT^Cf_%#AgOdN#_?={GD(EMuxi$(NCYX)Hpwsi&A7(%Fy9$i^hzp`~52Miae}>A5dpKUz3YJI3lf3wmXvpMhxh z8?Miyv3Drvlz?Hd&o{)A@9CQI-dkv-`lAHoWg!;b(`ZgTQGlHm%!MX3hhG(2iE6Qf z=@%On%d;vm*+5yJXQXncpe8sb^)<0Fk+iJaT}&R0qsLX1wS8P2vvVs zVZt(HOlHtO8*FTWen}3nxY`7qE2c8dpJ)doOWI2DHF7kSiO+;zHp`li;}y&d0v&WD zw0AwDIp&D2(#TEr|G!tbppu?1mC_6ss`{g!D-Mz zzYm|nlE!ZV_BHY+Sad>0jtQBsGC^|rx^pr-X#C?JLxpyUNo|Oe_c|x~ruc9l_y+LV zKPvO&tzIR#fTkUoDxxocTzA!FGv;M>@;Iz26R=}47OTrMqVUN2X6m$)j-a1J0PKz8 z-okWG$Y?-S|Mh?reFutITvMZhTG?8Jlf`OOLLSFDC`}0;F!L2d{YW$k&hPoL7=MrN zZ+a29z#RoKtoPK_??cFXX!2UMArIqb3iPT3^ghwk@>G8|zk7d`>FsFUX22Yb_Z^DB z;ZNiKZgEn%muuZOk>@{UqR1aQfG_u(rUMHFCufLVStrf4!mQ7)aDm=w971&>{~QRM z%@hP$;YSc7i$zC3+ogaC3Vx!~{j0q-pnE<369wQwPn}Tlqu_s114n2`GAgsQCkU`_Lh%|EW>_ zGr)hA=szg>&p!CS;VObA%^~eg6li$F#K_Q_h0@_GHu#3EcVSyw32dJBrb{cLXaZ1` zP-^vuf~rPJ4-b!!nwrTvzz&VzMu(?^UZ=C9t&0Qd(-|-IcqTt*Q>#lLG4viI;Aqw= z=wx6bon4s7V_KZXA`kHt#^8Uk_f}tVMBmnEa1z|z-QC^Yt#Q|e;2HuUxVyUtcXxMp zr-6oG!3hu`U+0W-?z!I|aG&n)sUEsURgK+i?`3n&MHs}(pt78m);oRN(GKYq+45V> zQh;*wa!6Qvj!1N`@k{^&sKPE@?dP>uVP7Cvjr)g(uo=Tv13)<9(P?@9$8b`?a<{}P zy*_LR^V;e}N=k|{wU*s_GKJmyR|RE$Vg2XJ&3fq&4)%FAN&B|F6b7wW|3*%asF*mx zm!B@Lu4CaEZ=T-2Lj>H9BVsl^aCheCU3@^#C$T9B!HhGzfnFKBo(AZL@D`|O@nHjn z5~WwXl9F2yLj3$c?z~1Y3UAnw4*j*QS`S8W%pcBHKp;4z*!C}eUj}_o-Za;XfWPqU zQBhC{d{y&BSs<`4ZsDhWDf7#$7EnPj+NY&56&(SKXH^l;+S)EPYkX8H%AIi1EV3|~ z{9f`=0u;lx`|#8%-K=R7kfPiG8(*}!@!{%wjqW?7H(Yr=b&{W7yZaBs^sx%##})OQ zMB-F1+AJEE?JO>ZlSVbKJ~v6BitZ;AJ1eVoLW9rB9pMIMGMy8h3(#f2iUGAeG8bxDt}9KmhOM;zJb%`F|(qLO9}arI2O!Et(P!f5B` z$O3ZKXjOGD1^64TObjcxL=6T*9m-c(ZNDk1WBXI^T%Oo_xU>-<`Uq&1ImmKOoy{(v z`AYvg;z-g9CKv{koZl|u|Hp7|oaW&x3_2wCg%n2g(?}fdAvQx*y;r|bomi{NV>iP& zh$SnWke)k0!3~hC78t$uPYd=?m7;NSZ06_{Mn_L(QcO`m9=sDc)fDv-NBmygN6$Ff zyxgJ9pr~5@Gn1LwShcz*5&EP(qlQFX^G-8QR`bb?ScIk7tr{4yR@NU!KkTwV!9b}m zbCZBJxl18=FxV;RiJMHm!5W*>vFs?*8hggL*x& zlqK?fiop*>TvPdWH^WKU(%`B3DT6NPSmub5zZjtuOGylYVFCQiY{&h|Y0vcli5drh>_?*F9u!O#WS^G=hYua9Qm^E7h8g%#nm*mBBp(pi8Os0i3X(~6oa{P zC|Gkz0|#%8PqIfLQRfQ(McdQ{p_ju~97)cD2o#LX`ce2uhCFz-1`gWWq6W%fT<1#_ zBB0midg5%2`2i4x8}nmH5=kuX1Xb~nbc4AD^3+j*I$VS^^Y#&dRX}Sb4LQstp$3Bh z)6>yiJ0*zz1w)qfX@88zR^tZRL*x@VbDnzwF|>RRmUc47lQ<`Co&w!5raL8z0u^dl zg{qF(9UroQo(r6N?yLwH+kb}#{aadgClCp{sxExc}{4YozPIDVgh(XC!Bn~f-L+N7W?=Xmlpbfx-v zNsz%|tS-7ccg|9~tcPt}mpT6t1;Q9BCP7G$S`KoX%UuMtKyz`*Y=m?}u{F+Nf{h0~ zmZmcMr*nF1Wm)0d$Q#chbbali#E=<0XpX9=DolmuNNK8KuoA6)cK}WOt$StgYm*t$ z{$%nJ)RFdOJ!J>i$4sfBogL%OLW&0jEK0_0^~P6la)<_N7c-pOMQsBlXyn2D>)L^a zj652qxx!lQUB``$DDGX$z0GM+YjS7XuS#J>t^V_-R45+3nvQzC{dxxGkLGyTcd5(S9<3)xt zU7ctD**5o7F4iRntS;NEkN$Yde^VQIS_#knwXXt{X80LN&&aidIfRvE*~s}kIgESw z{PMIROis#*WOXj^y$RA_iVqJ)`7skx!%D&MMo$TA`eJ{8A>u6{n)tE;1#Z{ zV>dhgQIbt@S}vQJUD}o2{9_8Ow3G>^e6_T*B444#=0TVhJ<&~`6i<2Me8Ixd|V zSe6}Xrqu#ntuap}Wa%PHd0YafUuA0fq7&*BdtlCP-hD19aCLI(N-5~Sn$Q0_uw&qW z{h&Wv9Vjv$(p}~j-IAsm23x#ku9&Y@rsx@qYqq-86Y~6e_33ncP}@AXP-K|A{|t4@ z$?Mr=D;FgHncRy{?VUTXeb(NHQ;gw5P`Zg9g9v`^uRKbv5*-R=68GSKNa@4{sjA^A zero4fc`;`NRmeSbLwK!SJ3tc_`tMx+*Gb)nlUXPjra5!#=WN~^@y;&UfBj+k`?iTK z!e|4H-1>1{$z9Chs}y{LP7`xs;+U2h@lZ@tSJh zle6IEe;HzIe`+E#rtZaxJ^Qq!&Rp?)2H@>itq-i=L#1sVPmI){ywQ<`7j8-F=Me1t zCZ%m6i0wSW*CZW^HvUYkB&OuhS;BH>4kz)G*C7^T{uBfe(;WDP*?88s3GQk{$E&mT zTKkt|5=Q@R->wQL$JYetA;anKng!l&*Sp@ea|dW5LyP#KHr*=LEGuQS&>U?@SuNdD zpnAvt%Vqzq{MQr(?+C?S<)*4!J0hZ(nIGeWf(O=?WZC%X>uQf5gSh5|xpv`V_T}^k z#ron&U-4p_zWrtpQZu50B)UA%da|5N^)a;xaZWV(_b>SV_3f*5d*h%I*S(k3T#%D< zAH9*sO(`f&s8?sxvk$x$sM2Ujmw3|fBT5*gI%Zz?m!Srh)kh6dliI{eIl~RNp%_y~ zzxy=1*dU+^9z$R>*kLhxsyE5M>tzoFS@g)g9l1=^+(8iO+vuvozMld0cJ@BMI&eUZ zQ+ry^Df$>b7lHljQeY3_%<78=!O0H?oxgkkM2}BeU%)`V_s<6tkOHVoT0OFpaX6Jo z1LUhp9;ECLc+4h>7bd$o#6$KR(VM)E;rF02`1z||gIxT`b#LA0ul=4oVJHxCY?o@? zcVE@CHJVT(i3dDaays`j@_Ls`BNGU$DjsQKDs=fDpX72KCy!H1$?iLo6MSV6=x4b= zHPnuuv&`?Ft2avkgfM;Gxz=YgyzB%EN7}7Cr<*~Pc@7^k>r2I8nsZDOOxE@5#bqG$ zCb;R(_bDtjkz7Fp8#JsZ^z;Cm%Y*Qh^V-6HUIF|4CUdNAyku1KqXjcSz<$GX0oqkNb5;ShY(W4|uh@y2L zW%Ix0K%x`{M!pI4bI2Lhz9=qks09uv{I1&l1Jy&=sh6hpI>`fTy%wjV-|@m9Zd*PI zDj2NFdC0QH*eR03jURGzR(rW0PX?#HAnhvTM`AKX`@ady_Q88OMzPT!b6#XU}a`rd$Vq|2mt@Y>DXvIiyZ7*a&R`9Njk7a`+E)#5ojwvHYj+@<9 zDjUAx@>`&OVv0nHv!X$+uf|ju5usR#K<7*BRMy-j8)d&8FOI7vNDK;Uj>`3S*KLib z>o9Sc?Z?9XZ?r1auq-Jz{@Y0%ha3_W-|@mVn)j~0vJIB>A^-ImJdF!3a?ZwxxTc5~Q&ppg#lv`_2Y#M~WO z9>tZy&5i5zRnN^UkePND@@0p}Zx2=QyawPe$#)Lil70YXq zfo{tiPQSO{ue5Gb=9jNP?h2eMLtQM|2PlN$-|JfBcF^ZhK(YmIq)Xsxq(F=$XSCg1 zhra&DY zVnSpmu67xgcLEgCIRZ`X$VAMMzEoiruJrgd@(azi8P-TyYu}sA>yF4vUlll`XL9nV zN#CF7Ue_(-rxt!Y#3RSPj0(8Ty4$4$U&(X=nW49|H7t?1Hr(@j^TR?8M@bxHS55>) zAsBxHc-k;?eEEb}0r@dzy4(pdjl^91LVy(#ryFGhsMsUI?Dp$Qb zl;w;;6Gw|C{?qok=F`KFEIGuHATE0+odlq#5t^HX%Wwmm&vpnX7ryOYkFH%&d%F8M zZ#0h7PDRL3TT2OP$Pvd$m}0=-GbHQAaC!Blt&DAnfiQVDL<_CwZhL%!Yw8SK0IvbW^9G`C{mMl*9#)c*!hRoih7`hlPy)R zL&_jA=9+H{7$YuC>+0!8oBky5oUA5VS{9vCS@&5gWI?8=60uQG{XO+sF@kLQ-|7p( z^mbj?Roh7Xa6S6^3+&Xh_zU)e(*M~q{*tnpLjpp7_kQp{N;qx|TYRV_1{k2H1l-)@ z09rW&Bd0|9=hm+QOO~Czzk9DLvsgoHxf}Lxw+L>H2nH?m?;~3(w17#Mkx^0*SHaJW zlxT%SSu>&=ImOa433Nsa{42EheBktTKr*i@X^1s4= zlA{;0b-Y9Pf&f1RVWG%KX00`5qfG0R;#>*F2N=v298zE)IzxRu&MY5lSqmtUc9$oeS#b0<% zGgMLUIv(x3?U&Wt6rZKcJ7t!l294 z3?H^;H4a-XHxeTnl**5Gq?ah}RozTWCTb}>O)O~9XaJTYm}c_IXt75!?F)j)4~xu& zgw^wv{x1hUeak$Ak4~q1j235$oKU;7nR^ihdhd#eL)oY*Hhst4cY`pHA_`rekI|0+ zmFv7CEw3EsuXpEZaH}gKzqg501y7|7PydeP=ji#Yx_&3KDw>kBIXVluc6AP!!T4&b z2fIC3$+X=yhfEpN%($AGG!FnIdyJlzU+MP45yDIABA5I0;W~q@RMvioc82G?z8X6g zQ0N=&WF-W;wlxyrKvi9@)CK5N#M5}5_T01raL#q{2qPI6(`Jk(iq(KE|6=9;wr_4! zD%^3I!kf~78{R(<#hJ60v5nq?U_)w;_F`i%FvdIW<$E^Mv$ktZ2GKGLsHlsdUWPO<{!9@tyV7@c-mpxycV$D}SZ}#$8fctXm zrDdpLy2B)H!XIIxdk&GIKj?5K`q#G`D%caX1d=+s6$cHtY;u)k*U721dft8hBb~wF zo;G8+F|W!8b7+!$+4v8AQxd&uK*GH0<@}}Jg0t#l(qdmViwV^995Zt@TgO@Vc!ZBIA4EU(_4CtXuD6cu#^RXl zPb2fz>UAlD>dLjnsQkA;G2rRBb6B)H1iIT|Rbqu)w7a{hO*sog}($0P*2{eBbzp)YL0&z*fE9MTgRexQ7=GSwmLGa@NWlIIR3DE$#40zV)zd@~kkVbebkV z#f%2tDHX2m1IdbvHi6V*S+)XsgZ1w z576U8STS|wo)+=`vMQgRV<|-TOY62`=7ErLaRW2-H(c#(@PO?_Im+Lp%{eo<6S6LG z_%IDR4P|q-eM%Xofhr4aSihH~)&pMx{}NCdjJm#UCFw{b*K&G5s@l(rzh(8>A(TU) zS@!E%ooPP3Lk#mRwcyeHQ}W#UT_8HXJ1ZfEtZI>lbz_lkl}}YtD25hT`T6G@Ze`R3 zxro)7o~uJcLxt?du=&ppi;+r~4eRvKsXb2_8iyvb>vtBTQTAkTz`(Qz8>iN5y2Lk+X07v#iHQUP8-& zc|y0S+XIrO7{_-DHPNvw!#bRs{HG&`tQyN7zPnvgYeibdsFZiKDDZ)_%-> zHws%YygI_Nq~eL6nSJ+B!EiQu;AU(wi?ft(8*&b-s2MOlK=(Ny1UwEwuNav+J3D*U zll?E_qWn}#({&s-a!6iDC48jtdFMDmiH8*TSpFS9sL+stVr`7iU^+_zjEBVwvH!~^sHB#EoK1wp8Uon%FoB>qnHsP zc62&$_GPC*O*dpEt7Y5X9!WyXolVq>Ie<+1+PoNff^QsCibsje`O3OD-wUB8H+(lw zCcGExKeQZ>(?`g^CpKH`Dm2SXl|+Qfc!VXS+@%?woYH&`)Wld}M(AEyW~Igh;7#$! z5}z`^Vwn>sJ`Isch95hpi!wU!pzdHq?MAez?g!XI7m{{*vi_z|NXj7>`4RJIq9;( zU9BP^8A_>xqZygT#Xbqi{>I{kNRAEwN7mlA`ZB!goX=g^jOEEPFG)*T<6M^@9hW~b zx@c!9U799aHynR0&mHO5+Tk_W=pUUZKk-wZwByERaC#y#dnytc@iBr56)l=nzYNXL zBdj=1P?LkURml36QMM7@-|*IH)#pJE6+pr-!uW4%l85JJMjxLNe$<$#^#atNQ`u$V zD{j`$Ey=OpH|T6biWS_lOUgl~w(h3Wd;^R=H*|mTbAE(j35<`wUNZ0MHw#Nk3IrlD z(8RfEZnrO;XSVdGvOQd{&!^P{gM{{;YvELmW*~`r%>KZL5l&f-o+UBWQ!B>5S_Jfi z@zI1{d=ri3!tysas_$uxB?GRMiNvb@aShc8=H9;}=>9~x)1LY2Ch)PN#|xQq)HIMz zu;e+hocj0MNX%5Q>& z!1$vUVS7gf8P;E(pX01h^AnIewdn>SkzF&mF)U?5LgmwUg@uiCZWK$80yY24-~wi4 z!?&@>V|uEv6CVb_8ydgxAqvhP{I{w@D%})S?F|j|&i?%87w&_2?~h8LNha31l1i}I z2*mpghDHw>d%sEjQ?3MIrPfgqLsTfu4@YBz--;uQ&g{0g3a%}UlVD3Tutqa?OXe~V$a@bR=PD&(+eicOU3goz+K zQB1Euaht{^I{3(2-o9!Eal-(s+x}AIWgSc z+f=q+_PB-RDO%hYs>G3h%HX6bLOj;{Jhd~B1sn5TSBi!~J=mUV|C^*)()%4KE*uI6U5p2z|58uq{At#G_B}pQMg$ zzjrzb8+dA1!bqshd^EXPX*u<+YLGt!zZ%q_GQ@RU=vWc_xH2RV5qW1`b41JbW=O;% z0BKR;k;6CTTeOcHs^3@!G*k~A$(h((vMUoK2cu^unl*>CkD3NfIfsPfGH-&ml%5Ia zg;*owpFF%|Z_O=8s<`^<=f~C7CU&ctoBHXg*a(Mjqu7W;$p8F77z`J4t3kYmW(9`N zhJ}3MaI{OFd4r3!2NMfk4>J5*)n59PR+i<=Zl#X}q|!<15-m_?=PbvqR@lm|a* z2=6v@h>tn(o}l;`?6Hs4lz~0Act~OEo0$JLJp`ce8-XMtKTt|i?xL;9)zY*g`mHVj zz8-PG{t(d)On7*?(H^byyTH3PP!>?s!j&RVRbORzr1AhEaJ?adzLQ$bT-JnFIR@*E z;rlb%xf2%Ojz170{|AI{Qv`lAwiUA2ibI5Ry|<>$_k&T%myLrl##?sxEqoqV5RW`H zZBpmuldFg;IP~ZGR1TvSikwo3a%To2a(qOPW8KuHRGWOHmAMc2@o*GE+Y{&UF=ZR2 zP&bB6N#hv2<*6|~4snlLyb^0Mqqth=Qx=vfMIIG#C;aT*y6@l=xqmy!e|CHVq5WM7 zbL)oAQg{&dwzh(0(7^np#gLc_Ahm47VRUB7M@0TU#CiC#=RT5ZvjJ}Rox4s2?yg#6 z21q#Td#?HX>pGYs94dap`OXETd$@0wZ32*EgvwNn6=7haHPgg(aIopCE+laRC#x&1 zBwVX~KDCDB>@h|mQn5}kDd-)_QQID2*i$8?VBnOHTQs_ry0O!Tax)u+$ewLK%{Wk4 zB92FtiU>b1yMs`i{oNs>QeHb|q&&rSL!^|c*WxR<@0vU~oKSU*q^Tll%?Lc8j3m~>G*p;ThcODQ!9OFm{(l$uO zm;hh`)kkUnihXg0uG81ZeOnbMr|ZmyCnSU|%p8!Gj&*r|RS%!8;rPn#O&=G~h(?^M%(6`&w)IH_00mW3~1;)kgLyq8ZZg)wGEh;9;wU|@^sJtMq-&lbaz_9^{@DmOVg`h!<)xLh0r z&?QaY-YF>ML^j9+ga?r4ZLPSNf1qA-{XtHQh=2Q~K!&%vx}C;x&)I7J0H(Pv-~ssQ zyxRrBE=+zi8^(Z}KcfeCo`0@>KAoX~?mz{yF6^o7q#M7B7=QDlk;c>Uo{z zSaASFJ&5Vmen%En{?0{CKkF7eZjK%ujV~xBfQ?d&5lo54@T~QK%U0dGKhQ5JIvboa znq@al{khR^D7%gLf!ENO~LPG zLZBChce;1aWC#97PP$MYEj9^F_-dw_DF>tMtOd-EC;=Ska23y6l95ROZUYuDR0vZZ zRM^%q2^Vt4@`O11k6?D(1Tcm(s{@*15p`WMQN^&CE-}WmQuFb7NaI&Yr|&runt?$Q zPBDu@a!FM&-LyJHIYz2j#>2)c=;6qzBtvf@o64dvYooE=X?T4{7ma6WhAB`Q@)xb> zbdg>kn>^g(#Zdw7dk9U``;zup{k@}SI;ZDhNC}y?_HXU2%An{;JVCs)I`-P0_Uo@o zB_3{@W}qsQmMmRg_U!o)p(YiNVQ_l}CIce$);I~<)F@Eu|;55 zJb01Gsz}dz#Ir6EV(CR6RUZ#2Nh@8!ha0UIug~E$NSwn=!YNDSNB%A89w-Jg5{HJ? z`o3N*1SWOITVt}B??#jfFsnvIec7EN3bUaj`3PtjzE=BgNHteV@ko^>hDJ}*JMiYsJ+w`!bd*04SiX{ zv+ik{uXP%qC?|wD6rhT*N% z@ZW8GQCMws#%1UXjC^>C+7V!k^Mwq7JKqX0EW?6=lZ?hf&c0zZcu{jP*K3-d*yFhu zdmGy{?K~ur)^hS9k>HEGlEKh&vl>?196YM}_^^|b5@-HSns%t)fJri1hSNnh+aDJx zp!74FxUAA7j`z~)sb)sAI#@lz5ZK;_L-W5PDQos{kN#(f(SKQ6?v~@WB#H6nm!-Q6 ze@4sUq2f`t2~Ds6OyX8CTAl}z51sXMpt~e$iK7Otnu!bI{V+1>zW!b=dQ>R}FP$^R zz^YsCA-Iaf0YGLZa0@>tkMV>tK2#yJT1IaaFa`gpn~)>@8$(#b>~Mb@P0%lONs+%Lisd&b-=|zMV74vRcUINk1lNwy(5cXPOY!LV(8_9fnpFeedG)WkO9i0pN`}wr7C#e2j*&>; zmlX)$qc%h6D+O&f_n~s_T##Eo$A_vD1}F6(o&$_gBGtOyMxZsdz-GHYDIF%XVVR9I z{bP|_*T~xO>5eoxtY}s73E?+{R%_D5*qCefxwSRmPoV!<0rW#x#S&i8aZS5R`?zqz z^4OWt309+edqIZuFXB_9W}}Q-|BsTICxe25aE_rUbP=`2*G4~)wfQGINyjA9ycUwB zIZ$^sBt&7*?n%XO9>zY*6I)+RdN$Xf2rVR$;Znk=U2IveN;Og;d3;8GK;lA^JdoKL zU$s(KzwP`6rg7ZpFda^u`!M>AalX8~*x%~dl@$YWME;kH-*zB^TaXfv)b#92gyenHE1uENPEOf%kFA)JSTHyJJW zRHR;_PdrTmifB@@)2s9OtS7L`cHd7yxL4$hUKNZB7p$+nFWYZ)iWwO+@1za=Fw`|6 zw{V=ZM#*^YbvP7mC}^Y-jyvX`=i36son(wKpo`XRfhDDBpU++a0h*CQ25pP1zFumgvpm2e(dQcnw0&sn=8 zUVcgxrg;bS3=*+S;dV#p;B}5-3&G=@0Z~ag4CqIGMnc%fzue(y*xPv@$S+{HvRt&$Ag)~7KeL}DiWv)6LCqc4& zuhnTwIX%2X%1j<^HWyw0?nBTNTh}xUHeS&Q&>v&0Xlz#U7H-fOu+7fp)!olSbwtR342ui^m4SBq9i%YG3EocxX#7A(?R`V!%_le_Y zN8Duo+fi}vkEP(C^8!+b+J}m_E_dZTuDlU-92ois{j^-9e)Q-4i8`GE0Fktzre47B z6-}Q<@m6{ z=$#9*>W+8;5?76aUY@IN!zz5jb_={MsWhm^hW1O@M6kDME}vccrVQ35d?8o>2QZxSc+nZMS`8*-V|y zq=B=JHj>Q>9v1D>bv^g-NCDIvD=KN$+5yFfWHX2t@R9S7hBu5ttn`N;Be6I#RLx*e zCV;1dwG_ebQfT)I&31Gu%BQ3tTTDbVg#THvl2GAt9nybEhen`-7F>!z+lFD|#y-dp z+vp#~ytDl;wA?=EQ@XSjKJhRWCrvpU8NJ?@7{fCHNcQfZ!HtacH64BjJ2-*czrPuv zoEp`?YM9_oY9~H~!EtNnQ_UxpMGM07U~cLd-CcC>aw4E|CUI0#BVbs0bqyC2Rj0t8 zqUr<8Pi2f0%GzS)Av4q-dXUK3E{U#VKH2M488Hwd`ns4099aIc!nzeF&?Q1rbse__ z*59ae+FwuO6`(BSkh=9}X+3$@yb{bkRouQ+)x{$l16*%HWe$+!@Cf7T^HE<0Ew$%E zPX>aN=_^l=;-41t2{im_m2q16zilz(bDO_R-+r!XISu$xwFemR;{(@~bz#C({#+%hE_QP=mfU znO0-?V7~BdRPNOaI9#+`zaE*O83~Oo9f@*gRkH*4%kKs>sHjV{IM6?c#=^{J^T%{C4p9ePRZ0gs1eW= zfUF6k+#j&l435RL{8r_;X(9c&rD%4B$AoplOq$`zZL30=%@(mWF4+FMWOv)5j6y6u z0AjO|@5akB&?7Kt!u@Uboq4Yz@krt2F8qUDy+0k)gQE~ojjcH3wF!NPzk;`_YygLa z2eavWPo3SjUnQzy>OhlXlsvJOGeWFCb@PsbEX^!GN^P&U+#6KOu#hQ{OA`yH!u0J& z!Y+`KAs`T?I-=|_%iy;T2T}~#y2iifBhczpvz1*4@cN$)QL`j-TrpK8x_cEgyM&++#1<# z;R$XVm|BEqASeqDaU+QzWy0@_8$z{ot{8Ai9cLJ!$T_q8)*!s>2z{H%e^FFmN@QU+ z!dS%8j1?WxDscd%5N!%6IbS~VoBHn~wa#;lZ}?%y`)9;!S zgI1Goinffk@fuIQpFPc2v*zW^bm}A#BP$<$F}oYbg+b;V6_+YN5pN(*^)x#!_5_)&9qvvfgJXX_j>x%tKzmrP zR#-9kZ!>}@M5W-T=byweBp06RVstySFBT*W7pQ>NW&cEqoo(`eN6mik+_K+_j$SGR zaqm2}*8Bd@sDj6iJ0^iZqD}g`uQkarMp`xC63QIUVETH8$9xSpEfb>?Jmg^EvfXUo z#Clx=?gZ8~SvOjr%+O+gcd`s<}JDzcurq{j=exu_2gZ#~j-6CO7a#-vr zLCPN(t~^E-D5-y;mwvE87CdvT;*Gm`lUnCry6tyfW5VQlt3a0qo=Q&-7d}eC&tbXz zg@zaSoOIhU_omWu5XJQR{aylr<@dQp_JhD5&-2yPyVqaA1v|K~5HCT6bHf9JsJPWm7n^qV9(-Oitty^T$2v+94LbafY7 zs2c1`D8ymBon1LPQL_KvcOz5>Jb1VAV&DV+<<;lElt5TP0Tc-^x_vJ~hS`WQJdi*w z*7JAib))Dqbbpo08c0B_Sz`H1&OkmtSp=d=H_ z+W#>U$X@yXY)jJG9KEa@K~8}LL~7_r@&CFM%aC6o%0)Z7slj3fNt0v9LoTG03GljT zLL%8Y|JQve)e8sN+gSf|mvlimVo4B0^=Xcl?th*Z*>8s-4+Cs|qW%5f&w^ZjQb60A zJ=8BXP5k#eAs2Q7*u{QAmfi`G|9azp-Ru`3P6!aWxOa5>-w%wz4dSks{m@1lI3eTp$J61`>VM4upQezMm5YVyl zx}c|I7MfJf%*kM`(yPO*+e$6*==fL~*>5gvm8-pjggfp1981KP=Csw?Q9CiA@CONx zRr~ARBm|iEy4CHgq~C03zR_SQdzx&$R5s(VmTP^v*-{4irRkaK>h<{!35+IkKi}y8 zERkeiBz#0so>Dqk90|qY^tRg8Ys|aSNU=Y!(Kx#djS@}vmWYrFgUVl!<1%<-J&{C% zik6zOv=5n%6gr3cduz33YZ5X)bcZYlYoC*cN9S|<4X!a7(o{;lh#xr7ozYo05Agzs zsQgpXo@%$3)^IWzaasWS>dPL&WJP%%W9SPyLk0;ay>5En->yEkz1QPDycG7Rq`k+> zEfywo?B_%|5*^6+m6Ap`@FXXLOh}O&hnjPKtES%kq_5`Wry=m8&}zD6oIV3wpj^jc zoRFh3vwq`x7*ZHw>&PL5X5DBrbIHlOYKV(1nC`5#iBHvDW7xJ%-*FR0{6kkGx6~|$P-pQ8E z;Qhxu7_83=fHzg-Clj5Y&_}H-dwVgr(oj3xTV${`@bP$n%^uzB?Z`sB8PZ6#rWChO z?@Xg0tZW55>g2x@&VgLmLsK<(nkhD{$SIlLzHiYUdLD+Mfa5#`@mj0yQbkQq26Rj3 zmY$n+;E!hb_KbL)H?4-^8gn_SUettds9l5dk<|zym~@GaAjopsN-vS|t$K;oH{7ce zcUVhITV>B)_gM@6EB{_@e~$kBkD~ViqtB2TzCyp+d;E{6$QbfphsOkA2rE4z#q-hS zVXD-6a-Dkg5VzgV&u%-{dTU=t1x^41E_ic}Y4;kLNt^__!t@L&x+f6@@((A_g`qi@ z*x)Z^@WqJd6--1>ajS-@=F_{LvDzecrv7a6AaAH95@GoUOOq?O3aiXlKqJF zlr0ny`(>ibAv#WGoP~{t=9lmaFQs5+op;q*kYv}#t3liHPGf0*`%+)jtgT2DO`v2I zo@Fw7ANw~8Tn9G`#S>fIP z@xS}7zJjp=uQlf02l$LB=lNi7G0gY;gtxlJ5Q)pW-dX%oO0qp>sBgyY2IFNiaO}7= zVPrxNqU7jw#TQMvIMoYeq&S(Dh(AqFwt!WpkxD}kig8o$L+{m~C#SL+I3lcU(P%Pl zqt(=jZB`T@kfgJtO0$ac8UMtPY0wdtcL|Bq=OpI*I2Dj4<0cqdQ27ZX-_R;8xKX{J z-&WvgC7+t6{o4V}&Jqhn}ww$At5IV3@rEQ36pEm2C$f5VIRT$MP{dD8Q-6Etli8 zmT{hX@1WA7Ki&Dvm5w)oq!l#xVTbnZh>hYikry)7Ir~lT8fUW$KZUZlCwTYC{7Eh5 zj{fq%vzC^uCb+=uD-Zh;21z--p5HiL^+*=%6c)#*tJ0%k3wuB6%Fq)Wyfk5$sgyHj z)jGJQp8qUOkw;x=PUffQXW-SsS|;-ZAsVvHF+$S`_?E>QL`-G|Nf9$z+ewb?Z^e9q z0GQ~JQ5YuE5HgW0xfR@#sX}+G3UkO780d&cKh;Eg3=S;uXcu#BQtMJ8O$-)ag6J)u zwacv=(EYG^i}j^3+8#qV%oYujL_e}#o3W%<1xf1;OvAo92nA1r$BEDxk#Q&Od0Kc| z+V4jCMx;zWaZgNptx4(G>PcuX>+D7?_l7H^PuWs?FP@SE+dgD5U5ggv2aT)z1N>X!ILbk(Tw_`}r zp`cob0LU9=qE)84SjgaoR0OTPggjW=4TJjuiUC{fOR$^+j2~h_lV9z`IE^iw66@=r zz{F8YPEw+~1?6gq#tAHZq7lBqgO z!r8Bb$nVdb}*&d{nqxuD2BGuy<0kvu9~0ouIGZs19x zw*4@259BBc+q2!I{_bBWFmTz6n{Y+IXNE`$%uc#*6zDC?8&)QW{ave{l1{Sz?p81l zR1nSyA;2f+nIj0ku1CfoJ(eTd$3QL?u`tu8MB8u??x7Ux}2UOc!o;9l2+sDVc5ZroMPR24a`tsZ=xl|K(kja&!xSVG8n9>_eTi!9x9po+&zJ7 zJnf}f*BUmT{Vl*ylVCb!0PE1kZ)>&DJh(M8V^_BMf(w5V$!C|vnakBBnSCccYXZC{3Y*S6BQ0RPYr ze+y8;2#Q1BbYeFzF3<5VjYlQt)@WrRuDwM>nDpS^S&6)$VkNHd;3=o-;gboVh~4o; zT_v!9(0QkMhLa#xFi7$+D3zdEySV#diN|H(UkG+E0sNqQ$CEw*tldl)xxV)kQ6^jtN(43& zT?7s@(@Csrl^F%ffPw5uS8wE}8ipd-$Pq@bQ{cZ_8J)_u6lw8qTvuiqLCp(qq0dnS z=Vg0Aq{RY@*>PMZ*;RfRHB7DM!IRElT49}N0f_W?H8kPwHOUw=1i_~3W-NL!!`Qv^ z!!THk#H+-w=kRWTCG9t{$ADY`-O)1ry@Iz z$=Bc%Mq=1LFTBW4nD^r^E9~y7(`d|L`dv;4L_f3LGu}3x6cl{Le&Ol0=mSc<0+rD( zQQ&K0QU!a*0u12sf*-jA>*bOWud)1B+iBM{S*lmVI$2<@3V*(lnQsobtB?bdeth`r zGQDO|vryjF)VSD?beWpG8$zf9je3U0GdzZM3W5IHzu{ZUos|#LU*Tlo#1j6>+t-sC z9{Op!lhbjKY}mBlg+?&&y*nMmD-*hLtG_WIt1{{m@ck}s>((9jxuxWYfL#SQ56OIH z&GE_<*V9<;skiRN!8xaZq!6;@)e z7Z6hTpKriX^|*Akc;+qsYrrf13fme!!Z-c<5Qn+xXwM5jIHcvVd96q{BKLYikp0yT z(ZLORm!Tg$_W_bI!sSiijue;qiQ%PhvAj_M(j_lZ8-0}t_X>?`EnyB5_aesdZzqcZ zAwJD*FRlqn{L&Y>;p=`>{m-yY9@2|TkYBFA zwk5FeWP@UwXUsF}^v1^>|L~*Rd7~uD*!?%-X7TLGcbIrSbKGs60;W~F!R_>vE4`Vh z%$%c+|K36|N6zh>oJi<|F@Xsk7vW<&G#GMMisX6YOCnyW*VsT!N9ssfx!!OOiJRWn zR=5_IM;7^D4F4y7D5ku+?#;98o`tSOv28Ov*BqR`q_o*bpc>`$S3F^H)KNXJbkvP= z=^De|m&m^=@kq#~=65+xmy%A}B6%1K2p(Yb&q2r6PDnhnOFUci{US@x#`@O?KGs_~ z{Dr$R*$W$w&-VE4*_-jCub@{P$I{F(Uob!kJ)6X#37z!k^0PnE5J&J}1h7H!jtDg> ztMa{L<8a6Fa4j?JHOH$*RJ_4+#v#N4mVm)<=rw$RBH6b=2GV%9KR$A;HZ96f{0u)E zSN=!U`yJxi;1r#4 ztwWeg7|&4yz(irq;a7hl3MgKmB+$sMXzuUVpg$S-#nyJ= zv@*0TC3@%(D>N_#NskQ?HzJWVx=EdV{5bK|0TYY^_lynTzf2o1Hee1vW6ddJv#46i z>w0-kDfVs5p@puHo4h0TterzZA9Sr2izs30t&A0OfyI~-q44~O<;Ksx+Z=s@@l-n4 z0|WYgz7)1To6C)d>g{&%6$vSTrVp_dS+0zj(pu zbFriU2@*LA&q=fPR@AL2Z8Be(>WZHe{p`Y$2DqO#nS;SJQO>i}7s3tc6^W2}N zsbIE$bsC2d6`o>Urx0fJH0qDNx!Ln;&W9;nobhewS?+0AH5?_Q$Wb(G#R*IW@Sr(ldmdTxumyNlz%B;l`PoWHhL&el-NE|bpK3He^>1q;kf6J?nmym%i(sYO$n&=06! zcmrpL?Z+k0Kf$!K&*zEgs&G{+5#cJZ@&^h1lkYn|_&iekv0E)077PvX(M*JELcn_3{pZmG47u!MV`rDXqbIsvEcN$|a=@ai&- z;4U)PX!s4Qc3TuOVMJG4QAsOb%72O=~R_B%F$%onT8cd;sc5+Bu&n%l4|69CiG=Iv!A zb+%@I4)lypS7i+!*;4RusUEE*En9?|dQJBU9NNo$9^xDl6oFgYyTyvWGM;_RZ9Z8a z0^BZQEyoAR)df=k(#C3aSLv#L?g|gMab+EkCzfxyt^3^Tb^IlAvLC(tMWP?u4)*MJ zd__BT=}#15vqeg`>?9L1bEF@74Ma2)R3s-Z{QlN^H=Z>gpG6>QTSR}kpGt1e*DgUz znr}Ztf&Sm8VhGfoT`X7(7j0w%xnrl3*J$2?DeT1JvhO)}vpy3CYRvJyX#;>`V__@x z@Qm}C<8xw4!btLPaX$VKiPn!UsfMfeH3^Tlu-wXwv*J(ARa3dzxK-V48XfscI+Xq| zsTOyj_@BnN(>~b0{d?@G!bb~=FC@)T95e9Ya8%PlL)EXJT&EMFWkZWbVQR-ucm&a& zIAGSF*6xO7#~y>KPdc%7pOMxI8>G=LxCjK!wph=f;n?E$Mz4@jNW}UVY~Vk^yQOVz zGK8@Qsal8xe&}(?v}jpyu;~i$G~b=1ANJgDtMl1Ye9KLLL2}_w>-&}$aScz#W}>lJ zpF4ng=~-EioV7o@ceEr!d>S+ke6o&+he9nNbZ9Xnb_a63oS}=T_k&npEi8vT zun{x*S!xG;9JJb(JP9ADnv`g9AMEV@A`dNwc>d{KM=s}<%jei1=+#xFqhz|YF0)Co z&bGt|St=Z9(kyj0}~64D1U z6Js+Q=p`}fAJsS1qmEBKz?0+Ssto<9j$K4%TPhZ=xA`;YRq+m3K?%{)@wO~LhwjHR zo)Xr+pRhH^2V*R)@SFO7vXvwv0=6zU`uWcpkZu;JR@=imJso%rL3;*A0Ny`cDf4(5 zN2ZVH$I=y7vwszA`umh>vYk4_RH9ImOM{ypXwcdl!yX4?uFO?xw6FFC$`;ecc{P!a zSgY$(>>Y^iU-z(YJCBgR{iwWcmbb6nvG=s^{EsBp{#fbhHfcp$k-Ul-%Jt5*FVNFY zxBH8zhaY^$47m;l<|uvNMD7*W4<4FLpvF&n4s&i15`>gxy=B2}A)m4%uKc_j-JM(3 zNHzuW_6H}M%RWX(lfR;t{zC}SBe zrW9r~yG6dsyla&@?lRyTvDw*#!o&TKY;#7FnL2s8`V{)!sT@7<$t;&0_%#5pbNmqb z^!FkT_g+8~Go^WNc|R(#Ty`xhRcQo^$*7g|g6D#619RnXy59*Ztt+|*0NWwu|v)}Og%a%_hyip4Ys)GV~4r(3riq=uw9 zmk&T8;Un6q>g`tibbd3_-{l4_-ky8xS=vH3#cd75$50hj$;iCjvBqfnzb}3=!5ZFj zM-4QKIAi;b`05!bOp}}4N@ehL?7q;-O#EJ%LHxpLz&k_%2s(%cx)(;l&oZJ>sBWBV zmPTf-AO@~?lGL_esA`v(ULW{!f9C+KA@sdw>tp&bO=paWO(sVd-yXTRN-^x&|WpKv>j~X zU$51vWD?8A?p21~V?<~lA4T`W+z{fDdDUY~Xu`3#*X%Dv{1ov%Hl9A&JqVI^+U}KQ zK^T%SC7*NnOPs}9Q28+<(y{)+dhO()NG9(Yi1eEq@>d})FQXeB47$RDx=*&&@w8E= z_b6vO&{NUc=yZBD zz8ktsH<0D%(*y}0_O)#n11DtFX5Y{vBsl)X%;+Pd3=Sua2nGop4K~?H&iUdX8u6G? z&JdqI#tBhj?cb=3tQyG~IsP%g(?Z83gygm|7Kg@DTY3@yo2_PKlu?=p$`g=Cr?h7x zxly*bDrC$Qr4q1_TAkPTqsaBZX0)R-nWZJE`OFs)S(#M{w;4A1sbb`UQLnZrQAYT^ z`a{(Y)yp-L(B_ALDOW&=W-3-A!D_ZdvyU7TtpvW{EB;?0g<96aI{s$9=|0z7PU6~EM>ioncSFR zE`Ra#?q>#a@Q#xLKb~fzaJe8^PCGfAIrHgiE^Nk!RA=bp(>YEW`mVz z5wmq?YB{Nf^im5!15JlUpz8C!aID?Ot1wqOXKH!+0Hu;kU#-GHe(& zn>o$+^Vn}^%Mhr$NBxmEkW^Lg0XXzx_{fk z_I`|4EzEz@pjmi4kV3b=6!Q*uP>%(s^Dt69?Lt*}3}7iL@6h`-YVWHw14E&gLt#i^ z<_T|*|KzBohEsR8%G>au#lGtt73*~1W>974MtaeHUjlO89*?cv&x4&#(nB|IH2S_^ zb#|46@<&fw)^B{5+ZwV)xG$ay(-Z?E&}~#%>U?%c>WB}r^VmRb(VH;Kf9-~?g-)( zcdw*>bHKbWzk6}>TpTu3&U|XcdFF0hd5fsfYpND_y%aWJdVnN@_ZW5C1V0>yc(_cw z9FAe3LRRmD5Nz~}r%W@jwPpu~geXFno)01;|Ky~GYAftYtn|r!=5@8dLOsCP?M7mz zdCzAa4pa~tyHV;{b3`C+9GN9W0`&K>!o0RBa#Cijo6p@2UzqhLJ1{CJ));C*=eaDz zoq^oP#q-4@LGg_oFOIVk$_1&{798)Ql9~C)@jLwL%ym=YPFVgaBh`;h=OIWd}<8ff=zwD`+ zzD~SvzhT@@Li4!@G~bQ&zQd3m86-mk@ud7K82wIa&t#Lff{e-`BLwK`bI0!4*!e(P z5e|& zJ{6i2bYFvYu^$Rm7cY8@e*eJN3Z_I8lprNRzpdS_S@aJ6&mk4C%*9AW=Vd=x&QnS* zdh=x-jVj{OoLbCI=D6YY)bOn?yHRSFspT2$yku>eKwt3;~d@TuR=D(N^9a^s-Kr+W#KnPA<1ngsb2y*KUpz!PIMrYz`~ zLUZ1Jpby19`DZ!`R*HRdxd3Z~w)NLE4lTs~btL_9e&jo9R+~g@zgPp7K`#v+$b+7B z=Eq#Zl24EG?Im7o+6uy_`@wA8S9wmnC)Be(*#bBV?g)K%mBjQ z+fX0slE`oGu)JlJ*X-)zf_FqGmRI0$Q68wF`@;5dWyrQrbX=~tM3AuStb4Hf1?w<8 z^62aZLSMn)(6Z4Dsfbw9XPSYrnnTUm)FNQ#WVX;wRWZBm^2YelUiDUc@9`P{MP3bz zU{9VK+VWPKoWwlOyr^V)}7aQYnC1!n4bX;JJiDJP$Rz`=YE$gf`u9;S+!F@ zkDEqS=dxQ!R5VNXGVebHX2s`e;a9(1VWt-g&qkrVpJ%)dOw4|Z+BHY15rdzWo={8) zw*}1Zl<%<1)oTj21g^rUI#%tfVzRV(r!s_Fr2D`6uaQC@V25Tt0x!caS}nJ3cv>gt z6tM8-0i|Fwm^_IeIc6+QTa`L&sX6zXz>{0u^+&gdi4Y+@gS$uW1Q$-zy4zd+vKghN zXFH>R8NYXI!%=Q{*mG_;1`9*ikK!g&4hQXm=CM?~f%G?VB+0S=r+$!L;9XIEj(1>KdDJzaf7)!+JKuFkOwOB9Lds#i`1T z5fSp||0)CC{+&=uJ3WP1r=ah~=+y!trX-XarknX@EH+j3gwx!|`D#!B0SkR;5E2gG zOFQw#O#n`&I+NF3%P_s+4Sfr~#+r~(mCzuVS=_h-%$Davy!iXil3KiY(+}T%1T4Pd zpvCvg$M$?nHJlVaK1ohee4C_};(~QIbXu4PU*6v&R^Cgz-EuCTl?n)a-AC!Ps7*uL zk8}9uW@$qUipb?#HxVgSwR5&A4NuAfl21^7bVm8#HK6gNdbqCew|nkR$(AE-K|oHx zfMmkW8PRqjK;~f^ZSNio3?5>lUo}YHngYI-c@~rb9+&kI3 zGAVqejTN`d{z{iGWLE*t;rZ^B>A zG@ZuTtVh*wMK7cHA{ptqBfq`;8w+o4$x1McCy}s<%_+YwXJpievrXFx3$Ej_}+RS-lJWFC1PB*4+O?;5Lul9~zT76CLM&raQ)8d|#3LG^6=ndBRuA zpSFcxWaZu}l!q)j%P}6TD)nRjWiH-OWLE&vdP7HWee^ ziLLom$Tk?Q4DUOS8hn#8$Im=@zet3xbE%fZR!3#sy z;LQOZqkpTa51Us~ z@PhqVws0w~yKtZo!IPXjB(-Yvuufl5(gy2B<|6YH1JWVPjv9ivs<_;- z8?_#k)M64cd%rOh8QyLT*Xck|NsXSx{l$RaGqw37P!=qKqn|(EPhh8asfE6c+%s0A zGV5pqvSx{PTun{xR~q8_qvJ!}F~poZXidylJ2jQSG>2KLLT9Z+%VwRY0E+27rhS`N z)qjNnXf)fs73|hkzSLz8Z);I>HuV~&Z|l#WFBS4w^KBD)Au|B4erEbyM&Kmx3*JTQ z51Y%7Phh;mveZNwqpcuBBeiVDKX7qfrj}w7)*9l*c1{T6)YcQcYrOjI7m9vMib=xH zGk@KS?~#4EFMcQjWq6^{=XDtO*p)Tijo(E*lceD_MzDuGOy|P9mMT*PGB)#mId4}^ zDoOhBJfYI5?Vftju0?Vq^YJ4yE*%DM)NxwKa0z|+cKYi_z1;B61`Y+(Q7I)95r%y~L0)xMxGF&dLWbz_-+%N(=JiCo&HcsBDpe~_Fx z=C(%t5!=?k0e~vw{$v29An;kih-(l+h-4;)`J4H$nE-%^+qOo9kpExJz|6Zzo%8Y$ zr}^rm*5H^9eL~8-#?ZuOENOF;A^We9MjP(=4`Nmqa%CdZKy{-j)+LrWod?M+XICPP z?fFPn;WPgZB3oN>Ga(C1S1&*kb{Df&Q902y|3!8nczMmu7)m3n@77Ld>1vcWJLsy- zgioizeuVlz(^1eG%w>CJ`N^YZDDo5NpRR@c2i*G+ddr+1;9 zP*T?PG;#e)S&hi5oCYQuRo)`0ggXse4)@ugFH=2n&QQC#3b^p?7HM4$j!qfD-(e%?F^La%|@6UJ<*d3n^Nm+CNM=WT& z7r^agwsrHx$5rQ;*GQzZhNc#l%GmLY9#`|3enKxeyT6obew9bVn0n!fU&(gOuK(0T z#DNBQAziX>2>}oL*ph9l?3(rmLR7sZNEm>x+T(Q43YpeM@WR}BjWcZddObeEkM{Hp z=6G)Se*d|2p9_ejWAfO+`{+Y+c1#)y9_3!uBYr9JJ=cwpzlH*0ZT?Wm#v69G%+wB@ zM<>nKeyx@kbI8v3D?A8GNu3Wo08B|Yf>j}Y=rfI@0P)p7LrM(*VwN(Ji70e<+JHT< zZL0V(7t;-ldjjD}@81`4vJ%%nHU%3>c9)^wz&w@2gSPDE3e}N(8!bLxa_PgixqG7A zcs|6DEsYtmV^m*D384x-DpQB2)|vUNN852R2%gqjCM#O&FIBcaYUU_3fXJ%km+7j4 z!-I}SMb5=f_?j|NdtJMGoBr13JfHiXd+#QMq)%auioA0(&|+;`X_kl1C*_g!# z4y-=+hD(7duVp!ZnW)9DQxYR2oBAY`R4?Saa_?4 zvcgp{eiqNDsDOmAz**8aOSAAJ&<{vkq0a_=z}%9`wH)W*+n{h^Zw^i&RmG&l5*X>D z6rxdRVyQK$KYLp5s24bz1TixEE;1*UR<`%A^AlnYPUumvlq<5GHb@_%t|s4nEPjzW zf0Sx1+-#UG;txyo?ZZlbhe5q6(&BHZVO-wjEiRw^G}_j`oG)dDb4>V z{*V)yubyvh7g{TcuDJjjVX1xIjwN+i@1qbWJ`Jo&WlLUn#`)vspG|t5@l#C$Xm&;Lqn~y@bmA+ad=GZ!_}v z@0*yUdF_dZk zd{EmBSh7A&osh)Ebn__ORf<=GhnvG=h%GZQPSE<7LdaM0OE;l7zeX@LIQ>wc1spd! zW0Gg*T5G};$`XDwet4nOdU@;vWeE5yr%li(8SQTkq`gLFrgHV;vbSJ#i36(@kQ9U) z_nMu@n)VL?y+N6iTcg82p+P)3Ar)0!5z432;jiCBVo=?G>^l_rm^%D7n|35S_A zdno+c{tmyckN>iJTP5!d&dD(LFZs9iX?F@U22Tws_=cV)<+RLrvMh#?z&nxjryAo| zczNnyKT1Rc4?h6@4%&E9#~K$_41>$dQ`QKO z2H|_gBJV4eyo!fM(u4aSGg#^w3a317I`DL&{GeXN<+OmSswyZiE5EyPwYJ4)c)e9K z%8%xa<6mO{*>XNCSl^j(G@N?7PV-vdN1B?cfyUHCUo8aJJ$m{Jea+P6I9UY_FWt&R z-;gElC#r%asV^Joi=cF$XOUuQCIa|bL?Brn?4||*aQ3Ne? zY|w=+;>%DH`nPMvH{AZj=8!9QoB`eeMZYoktYGy1rdsvmM(7uStCNGSg9R$Lghais zjotnJPZve+rp?w&>qLCsBCQz~q-Y+O&;zLwbC zL*;P3Ni2|C%#m>3Ht-6#M|O-(8-Ng(fg*g*UJCC(Of)uqi}icGf>Vck#lzO~^mt9H z{kEq&Q?<*&#-k`ZDQ7|{f|DEj(FtLK?z8mjQR@9F()}T@BYHVfT$~0j6bdsX5ciR zU#+~v5YX}UkDz08cj`oS%|FennE0%yeEA8?%Kz;JFv!)Rz^d^I*;zu?C@)TOiQWm* z2%R`Bztmy+6Rsj(bCL*WKE$39XpD<5Opp?b2jH$l;o5=4@NsMSn^Eb}M?ZXheGB35 zcp@u5*RE`Hg?(N`zQmYql6vbpLJO@E3`Ov;euVz$(FQIIs` za8)49dnqUAza*d}b-D9wB`g62xAI092(9F>NM;EI=-I1_I`(7-6%-cYB3CAU(Rzmp z-dHQrk3ToPX*0cypJNh}*vFH_oP|XEw?5eV8ceXVFXV8LM<(Srkt`}g!b&eI$DVPW z2uu(8e~yPL^I;7R>4r*mT*W^mZ2t*d^_HRZtK=K&-G}@m%gYY({2B6Lb~mDey~4%| z_v;9*k)Cvzv11YO(`WAOArPMSw)r+V6$M>>nTVE04LiDvnkM*xkFYm~RKJ>d1cz7W zg)!ryNvZW-UyCQ^q&{>87cWgxd033v63!rAjfQ9HZ0S}M+rg<{$mFUnG_@`D{%rFO z=&1iNYv|o3#PO0SnfmI7;eYC7o5ZOGINc}{3TSxj7gT?$QkT-<-=FTZDav2BeAj^g zj}8Qc$S}jSquX$3I+8dgG>42zLZ!Nmi!~}OLVMY0-%kRZ8*r$r7_4Rb8<^F~AtABV znSwS6UhfZ%q$Gg~=^B85FP1F5BQ z!BWk%@)DtV$o!ush7h$MqRlAAvFK4Q2lUcy%cE_wbptbsphtEK@ zz1%WnO7WxDdubIsy|=r~d>2>w_%VNZ@ehYOzJf`x$BKC8gugg5uUlaSNcPLADn)(Z z(9qcXY#533vWWXxEYOnq6eJZ8v$Yr|y5N9Il<$}=@G;eCsvB>L2$TzC7#b)d%jm7@ zTIg1m?41chYLceLw*KwN6`K7sqbfHO|5F@~O5{$?R>leCqp*)hts80$X)FI)mAPBw zqQIQBe8TCjwW42hQ~=UCP?`7fAP+2S@Z6oDJ!i41#d=YN(_$d9gMr0u#yEFhgI*XqTyQW zkmZr{DbO)cAVKPrkl8%aNQ7z}XE!LAk3Qk=gk#U;9m*Ta90YzlQmQ}hc29z-K=QFk zWe?Rb2!zOpPiQRsR<}>Q+13zDGGDKrR`iRG946Y0a^U+b@N%d~ujmVor@vXd;$c6EO7Ib|cLy@k zd&s#z5fRKFVC*u#L72ffZ>JGD?rvV5V~2mEYRLwO-1W4XA6TIgXFd-5D;Ki3lU;1> zk-oSlbun#F*`}`~RJJK}2dsY~MsM^zJ>6~GR;)Hb>*--0_8d*bLhbk0vy{tWwbj`_ zH^Z!z(XZZHbhFZs+@|fI7(^qV-x!Hn+)X1! zRH5t2V_QX&0>P!p(PE8Go!kTSr5GWhjhpcX2s|s_{UZ4)X;T}*_)yyM+A8$tr5KWf_5#!fPJ9r)?q`^@jI2Ko6|#ES zNajC4;(eroiHmY^{_fE2Y~W;Yw!$r%G9aFv{66vikIIP_#wX_fhl>5ka}~Ri>S;I~ zLkLW74~>=FP%C}NCQAZ|q#PAjB@4p_{V5jsPinrIX?fff`EZclU$~<177TI^of5ib z%4Ycmk!4nq0Z7$U{M4nq^-BT z%eIc2S7#{C`M@sB47}7fPm9bgUxKx%v&eUpPr9`#SC=AyO^aEz9m*B2)&IzEwy}Yn zxYR9VL%?mrxUsWW4LL9!)?N1kKJ;_9oa!0gbj)2dnG~x2=zH2NZ8kI8Yq}kdSv+wMlO{C3sBU^u56!Ti=|Nat?9qzA>v>QWjFKJ`hp!E*0>e+;%PK62V-i%bO_h}wMqi?9Zkqg!t=@@{g@BvIC`_|+i|m2Ee9eV7k(?(f#vLA z(5ZDS+7d*YLcR6tk$4LxJHSDrgmTX!q@LEsr@X_}YV1bkzA;eM?EhIMASSEQ54RhL zwmwx8y0*YvLe;Dz|92ZY!JAQE7LMRm;M$L+Gks}OD`IFpk}MXLSEOWxjzZicsf|-X zuAHz~e;Y<*5p^`pSrY0!Q2&^s3*uG7mz#a?`@KCX?=xf?Aapm;{!29e z5S@#&I2^;i$MvW8^JHqC@3TY?L%ew)BA$dLuPvU6-3Ven-rUgTeYr`mXTAQZ(J-hp za-0>5F?!v=;D)}^>FMpn&#&HR^D2w^ec~RDMH3KMGXhkf_;)yN!u;UpS&v;sDS`BK zvwc#9S``xWF0yhHUb955J`L;CC;uYDXQB9gQY$J{sMc6DRfEk6 zxWXWzb{I0HRFadJPnq`-p9{>{EQ!HKkEt_i`V!}-Vh<}U#IoA%G)O@uq**OSlGVQ< zqTpju{3i^FgcVc6fX3FRfR-fBCI(xjxg2$lr!~f~8lO7qn?KG$jK)j&Z1z7R%v+3~ ztgz~bv}T&-W!0|L^zHiW`aWuDTpKk>);iwY#>mqaI|JYH8Os%hISZQ2tp1U{PKcr1 zM9!tw9V6M+0{7j4m2)lL=bqz!$`4IhzJ+tHZMAj_*$x!pcLL=hsR+V=UpBCM{TX+B zs)F|Xrw9a`F zb+?D)HPktin5;s_wG_KAOZ)XO>#eWW+v^t&&+MA3Zt>{8Lu(bNXyYC8 z=i+GOfzDj^*&_dKaYlYsXMrd?;$noGN{bF}Q~2E}NlM{TsWh|k)$>PpN$_#7N$;5E zcY+37j1xG-7@>XJCM+Wx2W)gzxCDi*n6)eo@IgzS^gryD2jft!SR`Ew|D7OFnENB0}URPWzVAcgX$#HU*`hG{li8 z#O}}mxN*|Kj3bM*|Ci6_pvTh1Ufs-^Kx@nYlq`}=gXs-?&yUF0KbJqus9UBr2TfLU z4fdPF)l7>0k^F7ONd#(uiv>y9UTP486Al%}3m-cYYd&0NMa;pTeu@t6^syn5A^q+0 z0?FCLRYc#U?hy$C`Wpo^BAL|7Z%D4rRFa???3M96kF?Z>oYJ?#%NAc!n_v+naMs1; z+F=IU4#EptIkW*>J-ac0xdOLNp!u|4)QNX(Z?L;@vh7DNB}^TVr?V@6GK$QS9strU zdZEGnM=)CTPE+^2Rtxi*!RjDP1-^`tG){aOQ8F;a2$+i*sv*tZWg@ z0P=cK9lSXd%wH?r~jt-)%|Ifi)K58lFD{WL`Kq&Ms!kJo13kG_5%v0r=vf7 zmH<%Rl~1lqBiV=EGFCe(79%_wQ`IRZLc2_8@M|6~(|mJPZZ*64b9^g;A#1 znqX&(Tzp*_qg9L=XsU)&V{j~H9Gv^^6s5B0W~wfg8O=$Oswm?Zflihs2|SA$FmjEB zC}uE5E-jM~mz52sMj^Fk>;}onknNZX`JeQt54*7rwDdU-&0!cz8!o`6lkn17#%^{n zyWj1;S+?EWEh2*+NSjXlAG+!T3X3}bOwkgczdNyn&|2T1d>V3~M4iK+Yn#=l?k#5b zU?kl)i=JA9wE2TAU}oE=uLuOW8OReGTeQq_(|Nda@M&w^(=^m&YCgzV%CZ$s#ubtq z)m5r!KIs6%f_PfLO|xI(e3cOXa<(MX=kz(Tu3>!>Z`?Q&PvPU^yX}qd$&K9=F-4VY zseyI_d_n-;0XMx+0ut!+_GL`1OH@xQ9;Nz?gs+w0pN(yNG3xP5^cr$ovm!ax>@~eEw*&8SMawTzH`;*ecqFwgMZdW~6U(*6kz`5Ba3UTY0 z&Z;pQhKbs^Ka@598q%s_;eSqsn{%WH)BaqNN4OqidsEA)n6%hhFnDa5A*uKPb=|ha zAm=($OZg=Dy_S`i-CPW@W!j`sk3KInefgu`QEu7m_sjT+eAfv|B_)D;aB~cv%jics`R!$7Bo9rSd5qw? z_~t;8Aaab_i{w1Dk(Ijka?jB~k&0?iwYW4+<=or>SsXkrSz3nAclINiF-53%eOJaK z<@n?Pq3&AFjiuqk(Y%?JZ|?ID$y;p1s^d!(8%tU?U0_cCa%3G!1%W_l1Wr^ys!L#) zB5|?9Y9T29FH}z-{XSp|f2e+N2mMzgiA%0Q8l@IqFe;#?{>?m3jvNAz7AY0S4$1pT zA)jK=D=LbaDaJQb&Htof0S=tvQ(N--+!tI}=BnK9^zWZP3_m1I^^cTwIajF|t)ZXS zfVt_nF}85-(}LP}D*C2AKP9)BfQP#L1798$y7X$H zrfv@#X{>YX;Mv2*s|z@|Pb-3ez`4qGZAps%_2Kh<%_;Z&K9Y;j;cq-UYiDDE>&Eos z?RdWJ57{+KREb1W=5hRicFZ3rh_>p z95p?{sl5b}XVHQN3s@^hY&n4fOkWl&!=mKI1cFQ(KGTaYf~#q}Xta)No(W5)G-8_2 z2pBL6S|d@fkRk?8My5PfW**Zf@M(|wW_m9|ot2tk9(oj9!}@FZxA5$&lZ(RU*~&Ab zWT`u>d(q~=y#!2_k2>XTGV=$FKNt}JE_=*Nzar~Do>$3fJ+E7Qo7EUv=aO?Js=?)q z=6|@5G)hO>?0XPd)=jF($fhjdhUME6+5E5l;uo1eepVl;hbri@*M0UHgGx&nyKj~b zU{FG=3D5l>uFkW;^;YsiX8yNL7e=60pgD8F9+J;ncM;>@5NZWwmB8G_4Ux^$PWDrQwkuF?IIy z2UDwUTZ?OG1^2^nJO`Hv*|BJ#8kKryjfKQc>#0WmKfCBepW`@=<%!Y5#so4#0@TB? z97olF@ujnP1v+K;*z$99IveW4bfZq1_|}sUr33rCqm{+eIF-!+`C*qdzV^wa`o0f4 z#*rLjbwXfOl2givz?9(0?;i79ntZL_Y|C+k{MpMkl;JqnY37H>bdU}AK4i=t6{g=m zfVR}(uP}`54s7zfvxCI_l1QQ_Y8;X>pZbt@Hhe@3fFmZG1SEfrffR!x_8yNN^`p8b zq&PmVs7)COKcLGDxKK^2*UH1K{?ez3 zk)4H=1S591NQ>O|kga}0B7oqUo&STRVjdj%NT=;lmy{~x2W*?M6>!d>6qQQIeWsl~ z4|;0N(O7N_lnQW0pSQ!2nP+#Q+us_D zVkX}2BEPS7(*=5q= zsrWAq*yB%dJWpmtnCcYx+!(L40qX~rb&7p7eVH~q=TX5v24!$0LohYa7i|3f1>2_< z;d;wNuVS4PToNnDG3eH2vlHhCL69p;+EA?oC+ zs{svfer;yyNDH5?c^zI-7zaMZkA?kEmS@0!v*UUg`}5&}5mXMJRIZgn?rBCDMp-ed z1DKexH#gv7*Jf;e23>UO3}R3+^>ry~@+#AJQUZO31f&VoPSR5+kEnHyS#>=c87EQa zWnUhb7xCCHySVv3jT!3hL>G$*M5bm8qal$JV|`IU0C2jn6;l$r9^lkO2mmTTti|GQ z29^aS2RM3`cC$Xzal1hBey?`VIuKFav(Yc7-B%P5()wewa`tpw%~oq|cHgt=C1w(I zcuLO`?swHq%UjLO-7hFb|Kv^P$Q?lyG@XD9j%iBvca|Dg2UulD$@#I3IgWhn?CnKW5|^pxs*#o zN*p(+a3U(A3JW^507!2cm9T7FmeCc&uTi;sTrs*tkTjl-nOsk;_ zuZR*8-~C2I#0vL?C7`S^IDFYiX{AO(y4m<$ij$07;Wkdr-WEkC;m|3HbJsZR=Obf;TVx|>o&oZ~z+D-U(q+Y@yp@Ri*> z1VTp`x$?xwO#cdNx*u?Sre8+|mWUHU4b-?^T_61%Hhy|L72jx)ymp}4xY~KT8Ep}(gp(_yr z`JIi>teC3Fu16dXdUh~Pq;o4hH+?B2*)k|3sTAgScSz-tD&#ln$c9pK9oci3YLakb zopYa-T1t|n&^2K1<6_Uf|A&-|NH+Vx?W+2ZUluPV2^IFFY7I9x2c)Mp*71BrZW<~LqcIXJ5)?+c5jyOMX zn8oZ@2`p{pv*hdbpQ`ei)s=r1PY<{>e37QkQi=-uYhz>0wEPLvC-|OBb!HlwrtxE& zlTyNS@DH=0Un+c|3U+@#upS>hN-WzG=Hj!6PMZUMDS%V16prRqmocG9Tl-UEzylSMtLeGYsQ0$M!}D1 z!~Njn;?F4k^8U?n(E>0|$OVX%h0=L)^z?@P80wp8@a>Ozj5KQFYSPC{hCn0jJfe0S zM!k)o(LHAET;d-k|LwM5ve7Z)6N0pZ0@qYbH9oF=23JyH;sjOtyq9SC=%R?6I_D3 z69^P{`lbJ~_a5KAIalZ6T)cN7D}%AtZ%kWr&gXgB9tI#vGH1yz=Y+QvS)^a|A6D57 ze-$Gj!|AM0=Z@U3e4?9vQ$1YXXc?=!RwD7<*-#-&U3O>B;zgF@v5>6LWLJFC!OZEC zf|Q;eb)319f&pcrzEZ&6$BrI;GFvy91j_m<@`7S)X-6X3KjQXil481fs@^7LNB6rS z{$qZ?qY*SD0~I1p)+9ySd2_8oTk zrs=hr5DOLjL&Mi&8aXUZeEpNRvMt=7-b7T(L|qSa5cFTRynY?5 zpPHOS1tcoEUOD-d@lvjoqD?v8%rM5Y3t7$Yg)XI}0D(xn%8TrR3+d1D-ZWyxTqKEtIN z*^O6{!b)^3uY$ns(!D<9>>tkUejCFzH(!{X2Vn#eL&*&)E)-q2EPci~m1px}jKvCc zueG37;jwx-+o%16vh&RGf_&d>jz)Bj=ZY%$hFt_!sBFUciHfq>z3RC*R#2%luebi! z5Ymj!B0T)W!WPVW1#EDTbKsQ_*U2KwD_(tOA=}fJC-Z@?2toEz-&fm@YI5nRAG<>j zV|b+U*Gij$YU8xKI=mIr{+glZFPwaKGskVD*KefsH@GOIM74!`Aa5Ps?#l0f+KNo4 zxXSzb&h2I9J6?}CyZ*v%c#=`YHw{Lw#j|4k73c)5IFtUOOV&*J5kd_%~ zTK$D^#g+k6N~M*|;8R>}3Q1*##rIrt44AE5wU2~1drQr4q6}@G8|~}( z9oPI~1mJt1Iy${s?uC=iz=!OnDmO z>wsi3)cOh9;r_nMQ3r}GwDdj8OBTJ&Bg_7i+bR%N&&D>`V&K6us-&k~I;HqhmpCb! zj%Ms_Qa)=eBSbeYFMqJc{;>Nwz)-=S@_SDl> zvdQuEJi1@r@t>KuThY~H)jMDi5c=coIbt68^2m67K$poPVnIV%^{ zC^F*VOAjzPw!c2C<}qVu^AjakBRUM92JTPevK^KTDqA+H9Fqhi7f^FrQ~Uwt6Y3NS zPNignCnLkX)w#pA6~(>5!#NjAl@^6M4$E;N@@+Reny}<`N41HTIX8$?qPZi-zLM8I zXv=BM@5!nb&hHkZl0)wz`9xE~=r>L-%#GF%WZcP}w)WQ+3FD!A$3BOV0fDId{)%))up{-)m7zJL{m4+s1jsI z6K>!PzPxEiE7EO#*EP36J?~;M%)!|k=ZX~riU^UbCG5)!@i)s!a zhfBQ`XFV*r)sqW5=zjOkMSVp0vX$#G2!;)#-I$-gkhk6HC;(R96?27uDi2kD9%|NA zB7@?%8i}+{t#oP7trR;w=uIzYP&WRZwkY83HpR0Q2phYP&Nk21pIK1!6??_tIThVk zuw<-pZ!cA0FHtEm$lq)#H{W2j(3^76B2fMA3xCQ2zkWOmaPqTYxCqr{Q6hR$I~_3% zHWo7(7j!J&S0&Q?D9<2&@1xZkZM_m;7U^-LBXKto(!$a3yVK+So42iAd)k>BFEKV0 z{|Vy*Dr9rzzJC;z-(uzMM<-P9o7>~-&G4k( zJM3^ZvBbCGdjiOxLe=kAJ1?H!x;c)%ux2n{!rDiKYzHb9IL;LrMa5aMO-1_w>WE4` z8GVhFw&YxiL5`!>Z-7pEgav+(Qlj|!=q0ihkQmft-^_wz0hy^U$tR9AlVlI93qqP) zD2U?pR<#ec_z_<8jdLOyYHs@Ijryu0$dWK)@27h zg`5gc*K<1O`U-w}nS5*g?>jLTKZvxNd#$3+Acgz7;jHGmbu)mNn4f#@a+>=W&UuHvVX?hzx0HrUnAHa(b_$Bn zurc2-d!4v4x4Jdg8KA;r)=Hd}^F~@pi!5xKpS{<+JQq(Hes401l{zmq6`8&Ui*6cb z5LVoDoSS516)eV@#kb^Kpd*pT!EIB;nL0wsrtzavBRuc(MVdng?vOl8n&jO;&S(C^ z_{)=F+$Ltf`Dmrh`NFcP_2EXi5;8eax?Qy{6GJILdA%&0$Y{?LTzrnVx6G&O_)A@k zzpO<3+v$kU)&WeHryAi={Ye(+EQfENc<6)`a zkbchUDcnP>5Fe8T?>m@&>Y-0TEc=x%v}{UR9Lnu=S%F?Z!%s?pvJ0$#%=Ej&QWlv|8Y$Fpn9Kp zu_rIz#6?%~eQrxCRa%y_Ann%R-29^+_06s4Qo>9^vLa<4mD|)+ z2+xx>T$ns`P0!a4=YtgtMAk{Bo`MD^9K47iEh7<)Ffx@S>s%Dpot0OxJiTOXZzN-90A}D+BGK zY~}~~1>t3u?hWIgSU4Vt`jw~ac&~ZTZpkIG`r`5zn*PPdWv<2FQ!x2O_G6GjwCru( zcBHN%CkuY|^BPj>inPKweL*B#N`9P(Cw)OkO$ZQEgU3>~{uzJRnz&{*Dl3IJomP;p zRmi$0o4RgOR3UiO3#5Y&Fd_aXLQ3g#AW6T1F)r}rC3w!HH1QuzQbbdonHdUvt(e%3OP`*T-1UtA>@Midd!=$aZv4^>jAuIV0 zSIPt(RWP{oJn`u5d;HIZ6))!Sn`~21l#Q+H{g0a25Xw;t(wpbpTJtXbpNv!DOx=+p z)-VpO#A<0Ru%MdJy-mz^kQmT!G6X2aZ#qx0@cnV>>zHKW3G$k8XbUpXw3;+@#O1Ma z(zQ7%QwddYTfmxNM={S@>Do$8pIKFz$G-OlJFc9EK(lP;SapXrTcgJ~vJ_C3fB9Z> ze;Vpjm(luj2~TVPu+WK&YM(YxrT+NVES@LcsG{jvC`jdQQQ_JJhsLnX{} zPLi2@)$qeQ8t^48;ui4jT3%&z5F419*DZN!h_c#d8)zJeb}7vthbL8yKDTpFa1kKE zRRQupOc2Yd;=X+_mB-Z|(Ze-VHxU9lLV^*>;7aajHbISX_H`X^=<8rbou(yy?dj_ zFkrk}KfZo{V%Au);q}V++!|s2vP9+9U7vG7T;MRpL;kLxvTzsd%ZJq9gByI;&9m=I*kvxT{>0 zF8W427L$))Yst>7pcSu*9CGF5tZSYflXpfG+E~er1P_*}x+t6)2LC>;JlB;uC++4W zI8AnbN4W>B#K89$>DL7o&CU9Zj+8Ltt9Ca?cdw zAb_0qL4-hmmYyT!Y;=f&;Lr;m9~S_w0QWgtukFghL56avEopKplsC~@ckVdxrQ60Z zUw5|>Ea+^IyW@TcS`z77fQGslnpVvLU2>yN=QHO)2|3g=ZHY`wC+&3uc*fNbUhLe9 zf80JzU+#L-b)>a@OUu6EBCJ^K_T%-D$MO zEuyfd2?LflZ_opA5f;3u|oa(mvS`<{}Fw|2gl_ zQsbO5?wGb;>*H)96RF6%x`B6|bgtTXm47<8^2ZGFqxTu!YYB{NJm(EYm7%I1d zvQPqQplZ8MQm{x<$;@XrA*HbU<;k5+{GE8z0;{{1=D;eL@XZ>7N{;BUm(!mur%tFSHxHf11c zsGI7P?;S*!BrXf)tFzxyWP`-2##g*rM~!Q;`C+LQH->%YIn_?*mH^TnFm0;tOVIhO zsh%f;6x&k!VY#y>j3*u$iFs#__Ibh_Is48%3{KBON~j^lPguo~!a z&)h?NGM|0-%b=<*f6c2!p;h@}ua2~nBW%6R;RQa?Ozr~}g|WyxGgDc8IX(%miu4In&2A**CT8ZP292%oFNt5= zfD#6TbS6MH{`4F=`oW0F_(^968i4>eVC{5bD2{Il;z{#8``a&7l?vcOlxoT+ElsLx z3zRp^bZm})>4vba7Kh+PH9m84zFaesb-L61Zpq+ds0;maXBrAFHTY8>bH7YuKjy}2 zZB6uC%LZZBmmkGm8V1wxfR7@wBW3L&oMn^Uu{=P3!l#I_G+zclJZwsLbtH zgV9X#mVBnVNChqtgjCs5TKO$&LqPMs4s(4_BSf}0<)6n99ZF0;eUIAE6#gmIftQC@ zSPR=|DFMJ+-$i%3^ytaAYdzKHSFbsYTN+F~j^+L`@!PEB%h?>S%ROwZ7p0o6>u|8n zibH*DZ+3f~_NQI^E1@5gJwiOnnu;C9^Lxap7@oF-uC#ef4%as<{t)yoxVh2WjK+(M z51J}~Gp3r7JDkGjJ*Rs(`$+t3=zC<(E5?1i3l7~-u*#aMK%l!HS2x)n^i(^ShzY8@ zGF9hN`iH!|PTAcIHSg5Nzh8IHo}q3AuG5A4<2$$u`NaWzE!`kmEVfg+7b;+nr8eX~ zvG>idt^9H%i+!k9bhS^lzP+W9%_4ZW`A_G^y3AY0Mr?5xgj;KM#X5zjva^b4V<-{= zIF)J9FqB0t!v@=J5vBG0tbywnw7$#!c8#}(6|exKwz&u%wVeD^q1H!JkxqE5$&rH4B67{6 z!b>wZ3s4pvv0)7u%z19YW63+0Ef-B#3$LhOHtrLuygNL)vn}d=g0Z25?mp!7WoyZ- zN)a~P1BbT`@jR2vvC^M^@bxtvYH2qOnd|DDvl`YWKRN-EiIFHrRbAd9rD z7qny_#`D8{&WwW-3Z|FZwn`YU*mb2XvIz6ty8J2Lee%+}Yoxt`w%s5ao~&@?PuwKs8k(I&z9>WNO@D5z5R{f1u__|H2zS555SOZ$4VL#_B znhp3HfeljP5e8m^8#=b$ZcDRb(){!2ndCnhcz*qu!0V`fnK~J{|NQ;wAM~X#<$uC@ zbpP}5KfiiRC~5KX1(7G-|9lnJL-{|SG5)>$_t5^H)W64qwyA&3;a_w3H#+=(?FJUh zs+jNpM(qEJyDtd$S0{Azl{hQ1GtzrJwfBz<}f0Ne#Thi*l zz(c<&yi!GLr8W)aj$S6*(s}QWtyTZ&_{Gh`qcb9A=Ym#7WevPUaY76#SWmY>9`8?5 zIgCtoUD4gjt+6%wt<_&LHggk?hf|e5ufzBo2p!JZEjUnS8p0pO|N=cq%bqM zl?osUR?2h<%X63W8Qlo4&oxc!O%c0zPWEfi=e`X zYV*prxoB^6BUans+g50XvufTOA4FH>Upd*TS<&KYhdD9IzBA28<2aQ*%gdZ#8w7ld zWI~066W93^-gf^~8k73$*sMJy_$+kGVUI0N+5$?DMoOJ?x8e%D_#Ps)%1huk9@7pk zH>|2Q^%&^cYTAdg832O4{r6Pb79dpjuBhdjdiLi#Y<592+s%U&Xd&E(H8hfq-)+v4 znK6RnD1bpSt(>JA-tfJnVfC{bdPnsXUWS0QuAlaBhOo@1zrD58Q|eS$m*Sj>x|y%b z3*j)Tp2FqT>r~ybMgTDdBFFFC$QDgQE1hZICGR5zaEcJtW~T3G24=kmmD#44_r8UcB?}I6j%w; zp{%z~WwYSfN{A1V72*W0To^`8DlbeF#SGTDh@h2xRhBrh3F3l(eU5tT>)N;vZI+pG zVYytaX?2eBrz0T%Jy%KPc(;=i2#k-L-F!RMdQg*QidT7EbtAwP+SxZLN(Z<=Q=mdU zbUoDyMnmhI9x`}-X$BQrzCgAk6?J>Qi#mVeK0#ZK+ZjL`@hyyy0QDi+IE}Uz(Kzsw;Nv^E-`cJJRBZ9KurTtFq^&$s=Zaz zZ)rbOwQn2gKTnO?g8UniWspI>ct6?5q8^`5dp}hKI}3aQjQ0|(CgJd3hFFHwKqGah zJ|M~`V6aO{<&$Eq5{;?fuUXWSr8#{10in8_T`Di-JilDPRI z@zWy2Ad(#evlsLa_*vh1KhNP)hM!hD&}u=3P0N^!I6-;lvBj^_N^I@i9} z=;e;NHG4L&RUgX^IUCI}iAxcWBd&hjpcG5_2~63TI1NZ#ews`RAcOql4wpl0xczzP za#!BI7?FaQ3)=|&`Ux-)+LWAun(0jUuvI9eJcn34cjUP#)T3A6;adjf;+g}^U79Kn zo68_C5u=m-6-Q;zlMx-C*LqdES<6(p4laY+s)fEd+#ghf3!TeP$Z_Qo#5y)=7v1^E1-e#elqm6O$U8;`a33C)-IrKDsc7@m zlg2_Aw`cQ;bK(2L%=<}FGpRopS528>Paf9m6A0wgbNSRl&T997a%8a3Es#NgK)z8W zD6T?1*Z8g6+*0#ps;5kr%2(f0J)4cK=W#y<>s(9_7>_WlRM6v~x`>=G>iCWyf@b;q4qn;cIr^u)mBd>~ z)y>r5;4Di$srR_nWq1xkv8M}vxu^ahv90S zz~T_WX747Dwtp1geT;!E8<(`&g_AxlnGwxPV=pr7c~ zVvRcD*oULN`K?#JM9ITY(oSkJggiEOxq7n4?D!7HGZ$h2Yx(OB+ULCL*wQ;JY8yZ+ zZe;_CbdCk(dbI)g`&C45Q+1io4c#Qao|4q!qJj|x77X>k!ugPqmz_4C*Xs&X0jKYv zXQ29?;K4Y7Wy9$T6A^CN7Z_H5_jkWN&8C%j|H!N#vadZNc4?a1IaxW^+2_Uf&&^im z*W}Npi6;{da099h-fTd4%5(X#G9aTW;WKqy9Db7PNwQq*z9I&*+IOU+V*T9Q>sg91 z7(Ok_1_8++$?D-Ix8{W6i+p^Pt;7Fi0yt#eso7H$)IKvx{7mX|H74NpFDZ1_S zTaAkW)d&@KCUO`Zr4^@VHC9hBC5+WTBbZ-gy|~R^V*Q;N|I)WUS}{rtg?tI_u7;{tc2XN@R z*D}p}D7tvr2-qaMd`vKQu8FQpE-El;9mm>+2PH(_EGqz2rA8Q+RE2X?27; zX(?~%R%{i;mAiQ+yss3ha8z`uGv3pNagKvXGP{P=W!J7!?;Tp<^JWn)71MejP!M`p zL$<%#Q2={FYIYo<5`jCExGHj1RzC%If{X#ovea1;r{8R(4zsII0%w=kW;`8LDWA}< z*uEK$PjC3g1T*8cmd}iG?S8RYXSR-~<9m2~@A$k%X8Mr_EE-D#Hf1LOX_+rGH65m` zscB8In$Qh{JTXqM^PVsHW6TnbV~toMGDEaQ+mzAIdmr7HM6pxn&LVXI`;f1C7d43< z3y%baORpC{53*HJbqh!GODu+n6}!#^Pnt#(mq+do_RYhb22-x2upVJ-sG`kkL?O3| zI`valIF++Aaq5?-P;s?HQjmf-YF~rQBUtQsjLd*6hE0fe&O>X)VM}l2IAiCV@Z&jo zsHI%~pslU2$$6e<^T=3^&gAHzWT77qTf30j7DN&PmY&ODXXE{AB=WIF-~!{Ny0}y7APKLWY2j@)Mn5E^WCyw|2XUN!HnN&yW{Zjdn6sq)U}$^?7oT4THrNk zDrK+{f5E!d*uEmE%JBj|4#Q zfccV$H&(DBVY6Lg?~Duj$ke{{=7NXX`#L=dn|haKU@Znxl5alIwAcgU;F9B4Ph2VU z5kNVUSqL~&V_z)JAWhY0xwPryFNk&YFN^%bee)|F8M4_ev1HjD?${LS{(1`xwuPT; zsnnRZi@QIIC3}Q9oksQXTi`n8Mej?t)OTiYh&zwJTAi?&rev39MK2{xOctMb=gXzG z`YyBN^DUeQh_vqZKbm(ubw-fEY3KJ8ii0v)=Lh5pT|jH>eERfe*@|PxaE|@);B4!T zB6`Qbgmq!K2asN?%$%WO(_E7(t}!a#*$9hndj5OVypfq(3a)4-K5c3W)p^l{SUz;} zrh?m123#qs4vG%(Jxh>I_sU z>c=G;x1IY=*HHjSPPue?p4sN&9|^p1rO{q2mTha8%4W! zN>{GpYT#ftTrdIPbc=bxfwX4}uo<+yFTFN9o*R^5rrG?!M0n^@lMD5mt!|#kq-Wtr zdH@&5&uwZkk{v$eK`{pQN}Heq$AE)|(%Bm?vU%`XjxdmU`uu*=5cRd6U7lP@|?FV z4zLeJascYO`5{95c&}B^ixE|#2QO}9pV@oM`Vtr@?vxoeLj%ybaT@*Xxi ztI9qc`dqUTT-DTzFLk7r9HbI>@V__fGg-Ts=RX(uhW!aG4&h+*xI@lLXXP zJHV+^sTD*e=RL0)TA=J) z)wvvBfsSK&apMnD_2{4?6@YHrMV}t;+>Fk_-}eYxsUl+4r7*L2B{;R?u5ANS@62g>wm@y8>}78+ ze{ykb%7MD=9}|j0urQU>arp6jC-e(aIvQP<6)$ep4!~&o*mQ$;OVl&@>f2)_>h%gr zrR9&c0dKBph<&!^wlg2Y^(e1dL4zm4ag$g*1y$^oK}#)M9`sAzxHa1GR<*-wT- ziebD9&hc4*?c(eq6nlgVAD~FKl6vGL=xydVNO}ogehti0rJ7c=!c^A#x%&AOPHvsH zOML-(Sw)tuTzZwAR;eZZn{+Lw5^U_B{qd17xA{_UI*kCP=7e(R8Ehf>dP+p5rsC}} z^w6Gup>+(U&~p%eVAsB&-%~S+P@PqjO4hz9}p ztwl3`Uo5!B{-F;X5+pZ)dC1*mxcU1} z2#8jRqHDU7$At*ad*O=_k$NJriVhbizwtWP<~;HJB0D`+jmz4Je&lc6rC(M$KwzLX z(?EZ8!^KRw(UyYPUSnDS5fQoy`)_cb#QU(1!Z*aao)s5T7u@(x;4(S`C3*+iKXhP1 zb$K9Mt8w+JMzaJB9{kvr)yDqyATe6oxUj$RTrTxszK-p}fGz9eP$qj}K{B6E`=M~w z&wc(NR&d;58nSBtBDwsyUw+C{*Wr>(*VSh%ttsz8L4s%s3u`Z*WU*p))Fr0Y)TQPX zxq(D&0Lr$&%$#VlVc3}!#gEHc^B!{qU(8SB1|cgKvmpOEg(eY%eJ?5X(t)#%Kon<# z3=|MFffi_3pH=Vc>bOh}Tz|Cc+q(g(&98MuIpXAr1zz5S4V&=0aV*rYY(#{rU{VQw zmsXaRuNztTDW!J(VKEiplTJOv{;SXyEn`&h#@}0r^9}s&^NxhTo5J#1;kMn>n;dC? z$c?~;!t}vhO*5Jh@{eiAp2Q!I>qYO}OPL?39A@OGa$FON!J=2q<);2U#K%pwK$rPf zwC%KSya{nEY&MG3$m^Q;*-FG(iG)XBU$u`K?INfz7WEe$NhqGh5~E|PA(iWsTZ?B0 z%dKpbyWApp{=!1FFFa^3EXCz!eTRnkzFt<*y8;z46zB#=T|K=UJJ{(n>pnn~VT0Np zyqPnE13BTJvY-=AfMibQ6uMt}&MI^?3s8uh)&N)tqLW2E3f2kPNiHZu>QW_qTNl$< zkPBUmKagR7rwulk^;pi#km{{5h3#ILDL+nm8K-hT&{x{6nFaO5;?Zoq%G3;WrFXb` zqoO5cgWCrmgl3DJCcOVpg&ns&L>f+GRBf$f)MhE?@AvGf7{$>y&X7h^ueEC%heGNo z;w(LgzM&#}8Mq3UN>~@&7}a-eI0jh3c7Mu6VJ7G&c^P|r4PTvn>0*nJ_^C4@dv}y3lWh?86^ws|CwV!OMaHxsDL@xKqJn3g)9`+ZE zG}w5r4KTnDIug)AvP&{K$q{Qi!DGfM=^sTTF^{7n@%SH-NR>3!-3J_Cc|lQ_;cBGL z>_@?)x9}xjH*C9>>Y)94@dezFx#}X4M>w6=t}9i84j$J2z6YjNY14eXYu&K*pY9vi z?!E_x-a;ON&P!%>&U4zX&8WI+mxa1&$9fZW=W9P8EB_QP)}V4;PgWGyyZw2DLlbAisvh&sq)t@de>ydWqp76a%varr3`0qD zx?6dC{q+XateHMot-L96ErFYi4tbjUC#dnkGdE6wsz+ImvWwF+)GS)-(uS#_=UkEY zQ%Y}@VZD!8p(g5TP&jpcAyQxAL#BSg;?bp`b0>3v z`EhGytUr&9eAq#nCbQJircB4VmIDs}^0nz3ewgE`e}GA}FJA8^Ejy+_3^i7KZ08DR66Z%MKB?^H+83x5YC3yX{ooPo zTlL3&XZQ%%W!VFlI5O?2{mx zE?3T}PuTRQ?h~TJ&7g>$G0XkzJzqY5AZZcw42`kdG31$esw4?sJ9AmKR0h&hDnk2r zYeDKCill_HMiO)*3WBdGDioEhcP%&2njY}Vr*mKD}OF4;xw9nxk)%|eFH~ix&)yL!g+INO3uSYQ6ROkks z9dG+-cm@Uin9ZN?a?r`n8g>OPaTvrdHa=K6!CWiN_Ziukb?R(19cj1A8+1UOkFE~ynEi_!T%xt>U{)FFHu&qW^;i8&&)L-{9K6LA}($3H`m;SwBp3CJOLw(%OTY)904IMcrG!w%Y0 z`ICX60$@DjY!mgvbsDMnr`~%m{_gdHD^MYXt(x{wz1`{3HT8ed0pL9ndtEPtn|<=} zdrg#t5LNidwHH=amK*B?@;ot0+Jb{{w#`aman`4xd3N|{cE3$p_Xk3p;_{7(GW$Ko zcs*aE?;~xR_auLjFYak%n=-UxhoU)_$}^zNOK-rC`OBNcBrDxythAE_gX@S1L2sIm z8P9?aJ##rp&4S!#0!)gK1VmAds8P9~FwM3H-XklWuGpiz%BNa{|93BsumodZWeP$+ zpzMvM;&5DT0p4Rum3PFn*uH5$1e{elk%c&wQi-(l*QUU=s;4M- zW{nn=9y8jmX!80t*o`SiLt~j=Z}PTZZlXs2@ULX{JAz+&huxu$Uo(n%MEozy>c|lb z)D~?lq*QB>em9r?by``0XW`Mnul_yjbU%Z}D}D{M4@gR4I3ddPa_Voy$G59?+L%1Q z^8cZVuVpH$&Hc=_9a^YTNOi0G4+ssiH+}Y7;!gvmL+^DSw(6Vl=lZvi{f&iR5@?0U zL{i2Kmi2cwT1YBY49YE{p0AqC37}$Y{Kcs z8<@Q)(Vw&*|6rah`2C@1n$k^Alwh64IkMDwP;s-u(T zra zD91{_a?>>9@q~W-3tGw5A(}h{*a3$@f&`xX$uz1$>y-@P{mG!LSrNjn10d)TI-W1c z_xjdhW9YR|>(`Dkv@ysK_ADDo(e&sECLnov-+~9?yTQE}Wg!LeR1tLnpg?He=MekD z@dAZrYdX9%2&R|TP0MvF_|=pfY0g5cx>I}TO(k^W=Hdl`z5zuRZ++mrm%>J1W=k)myAE9Ss^=C!;27rRVI!OUvB4 zgxXx`R?$0~Ztc~2Ie+L>;`{cX8;;ajIub<2UppNBMj}+iC)^=9478*&D_R$|Ml<6b z&=yjb0@lstpR(xurTCG>g|+QhrCIa5rJ51uvmqOdz{>-+0L9bs0v&HGh@tgfjPz~w z+JlO<-0pTzT)jr$UBmdUY?fmdHRpfEaW$c)@uC~z9AWhCugdxMr+rqsw>0in=YP|v z>CQ=>zC{>&3hu>PV$rzPHPE~vSNrQjtB1`;92BDxD`p5dWXC;o)KjDKLj-{qN-1cs zsMbiF@xuF;n}`kf;9C*AG%HLW)V!WP)G*<+VFcwWE%1Xl*fqaw<-V@72sObN6CCpm z;cnpfp9jEj`mYB7v88ygjOn}&_r>5E#sxfX?;Tb1?~6=Kec~$l!0F|mas!DO=?;l0|(#L_tre~rU8Pnx|7P8w-rA2(2>MOf zzB2D&>ImaU72nlC%kQuQd{I$w<=6pi#82mI?ifhvUTI1<35I7%hgqh8ftD^KXvbs7 z|GYt<^7gTP?&DyNJEKU{iH(Z1h0BJ~*I>==J|McdJirxtERK)CX24NNid23-P}2=@ zV7`8HOWhuKcuu&^+CC63I*tyYgSvNg`@0MR#S3#>^(3trvYopcx@5bHrm_ePKyUmk{)VA`2x#o8C z;9s64@#L2J-tf9c;x^{ww*3#+pMI%MIl9~H(c6)eqlcyX_}Jm9S+r1=4sZ%ERyJB% zaS|iz`&=^^*uqqP@|Ym-M)FFHI{KRHu((?{?4Ph>dbE$UTa84blT_H3ryERB{_si{ zyY*jRPpB_whrj8iz!3_cWs4~jiVVBK^lkRzboxKZD|x&`goKQiFWUgDxi=A^Z#4pM zc;NB(R|{eU!&nu@sKv&Grkm*n*ZP}~gBfvX5NakkcnD-VVASNLWKFdBhd77005C6QvwN`Q!|>Bpjv|5qzQI z;Aa%6JPr_(mv{Z4cU29kbJouwU{J2#twxbwWFyU^HGlla6N^7f)Caf-D7mQ58q(3E z8p&v!cq+5lHN{~2buTy-@aOzhibl}-{0TJoc23Bf?QxupME~yLZLv3N{5$Uy@kc@< zca-)G*QI0pue(_*F=v0!N5F49M}?Ab`GUhcR?(T~3w_TjdfIdgH1lC6kHG{jYg;%l zn<_gW_+_P!6rJ+2YJY)lO>h$U9xfV<^sMo++jU>G-=+T1f7o{W(}vbNm(Wak2Uw%# z8Nzu~^;Wh9I;b+fWR~ugYn9lalP0jHr?%87362KKbhI4Ua9+B+Sib5NNOgNiu>%=| z91R}TJd~Sx+eAag;({Kr^b%_==oPjoaTaLkzZyW`{g(746H9`~DMbd|%@ zL4GJm9o!qzj=5>QDM>spaOK6kCRSM4xp$Qr6l7xXJ7>bN2{Uby?zTF@j2y!$V({3< z7SHb-0x)oG-+-RfQ9rzLU-)cO#j1l|d`ZS~zj3Ausm-J2EbNHhqdCUEo_BOLjO?3s zAO4XcfWNLhx|2Efin?>;+spmI`1X6|K~m_S)WhVBy<*QZ@x!?kneo7{b)~1~N*Sjl z06w+jVfW?;1&JFiIFqD>^I!gxi!q+x8~D2`uct^M%X_KJ;`Pr&kFGgcHPAvCgE)XQIvlXNp^_=OhS5UkS6PZHD)t=f_X{Oj zA-93s8mcrEm_~&IDOh)^{(FM}EuS7fQ|fB~^{_Jd*o+ODvA5K6;Z<42dQ;RA7;c`p z|0K-BwXG4&Hq~{cmyG!=mJr=@5~OnJ4d}!q>3Kd~zVlRn$Kk2YMoo}fYRZVxh`tRe zhoK=_rD}cur+S0?^BjJR%By7q5NXS5_!guM32Bus(g|qVU8rmTx~5n-wjNLg;vA(c z*Lic;iyKB7**h?@>35zvA!b7n5P(j|ZG9-S>+SCz{ry_!R#q@b@Tm)oRb(C&)0ZJU zUy|2LLV7_h?9~tMRY>oqc^`td&JEuovSVyKLxNWj1m{0n@3;Orx0TrN;~S%lZ;1|h zM{@hX_v0=Wt`JOY^tzpHlm8|2Kli+(*KsRX6@SGNQy|c``AZHbHZ_75;@vqo*H%x} z9uH&RZ(*h{T@N{UN_r|J(G6Nt>BSJgZ$G0s-aDa}xUt&m3teZnqY&;1G;SQOlIzlY zA9taUeSDRF#9H77D@wx!Y26b&FDdc>l5V@sjr$) ze5a)dwq({JtOskE$$AbK!IW{E(MCaTk+x+k^-d^O>uX3cP~zgyK8U_8iJhg zeAjob_dUNLJIUTNv(~IxQw@2uP#jgn#2=FP;I4TwOy#w@kCS5Xroj!|b<=(@1HE$g zj@2=_$LU7?`lPtCqynplzC>Ra@5sCc;w~udE*Kh=O`*M*QtUL2do7&l+|$j zhBoMiB<-#9@w9moEN$>@bhbivqND?}yQ2kv1a# zlnM-24el}Q0O;A2##@PaVcu8XUafkG?l!w1f}^vp&y|Y_Li`!imRua$o!xqO2k6z{ zgiLxSSoL{3VHf(62`&#w`h*rO)JmmtyUvT69rw`GO=HbM$M-;HmGDgp!_~OKm#d$0wF{Xn zC6)f+)kV7+v2i7(ti2F}wUfB8$- zKkJiDVKdqhT>ppnp$jUEZNse1O(7THtpla;r=yOk$v2Jn> zO*fcBMGcsZ^c>z$Dp2lD(=GO!9B~Lj>btF5uHz~tFzkur{{bq^Wgeq@ zLLe422;??IlY8|95t&n*P2Nwq^?tPjUk8ZMmqDSWd!;5^Tn);gj3|w?6A@?pBnD)l zaNGVg9@#AON#*%*cbKmogySp8b!TKvE(e7ZcXAcT*oFtY$sqE*^9u(Zrj zWiz%~V|`^8PHjs~bwk>sOZjDGPNA|PCf6^IO`LG4q--lg&e)^%E~?Q-L&Gn5p}=mo zr3J{w4FDZzbQbUV{4Qjt#8qo_N~a!ALmii_!Y=A(8el?6_J93f?uk`FW#?quuDC7( ze{X**@)r~Zi{y%}PEiNrV;)WwglJDqcwLx7R2u+lm(fh#0xV2-RH_eqKt|N5>Q> zP>htSBlXsY27t@(%zw8?BNFH^N9FIe;x;a_#}qL$@6ZNCQ3^YB*V@mWAl|(L#B_Z9qD;jl2d2I{h;^nhdrjh%0`Bz0o{`e% zOc#7xI|S##$T1Mdr+qj2oDTq(Zx9cJ<6}@f>gjq7ww$AHN@?_ywmtIoRV6Cd9nuM% z=X0*5Tg1d>Cj__1^_)7~;29aLLcQ)qIk{$RE}9FW6PLF3u>A5Uf6}26LP`&D%fLDf zEsA@BEMXLWG=G3unoJGRbAOw|H+)RtO%0NAOC=HbZdj0@pK;NJp(9&ex^wr<3y=*=e zy<#ae_G2cD^yRrpMG1%`3;bbkqS#yX>uR^L4vNLLz?E={Qo`4yF}f;tj9sBlY?elF-H zoPv2R7{zKd`rhk^)gW1Zx!S_7`Mw4cr+~!E6-e?P3WHP*8t>m~O?<^OVPxesRwp5F zzt?c}7TgU6`<*m;Nid2kDmv7`Zc6Nz-oM?+{cN-~rw8_AbczG+omt*vUwz{9S8r^+ zIpRjrBG_iTDjuS}R>qhWTfvkjaYbja&kI*%p#!Dz)M%sTr~^;aX?-tjzTtb*qPFw} zb_^lvOoedQe)6&poi9M`IOK{nLhedOnWm0Jwz3=f&a-SPWPE663mHuQTA7^^$nv?M z__0z@nT4k(<7-!wZ#{^vhlR`+A+65iuUxzg;aZ8(2^S3Qt+~xCk@gYTik*FJYJ7`# z?p7;0564Sho)_#K>1{V6r%fEdKhc32fy0|s*9+why!{P_wRcoJPbD?|6krTOtA-(p z=i_3Q)!weZud9sspWjzkjl|=KoMyFV_cpH;54kdI+NzEk1{YU5 z)jl>}^r6%^p=u4Dfys_b=?--(`LloF8b2Q_9A6By)dL*0)*9@RiLG+6jat>&>{Ok@ z`eiqP@41C;DBTZ+{HFvoAPGD=hxKFv%-wS%4{2K`rpHkinzE=ffz@%rL^yXcDoz8{ zxB3pQ%x>uKu*RZCJ3GLQd0YJgDB?0=`5W=Hc_7f4Non*e;1#VOQDW^iKJH8uvop4wz&Ee) zr8-F2c7wp3;j{286NXWBRik6>O$WiRvGmzIn#5jJMWN{R_@XIth4jxxpm=R3{%`gK zmUet8+%mxk6Os87BBZw-4$|0W*^|9RnF2ql_BoeakB?VRDSsNBo--Pn_!(Vj>pSq)B@2$JR(VE9xI&=c;tJ!&= z5PsOe*t#UvxSW3UmBNllM;UGOpz~+;K%Pr{4Xx99$b+2EJOMppSfu5X^Ys50gOERaA zCyI;jKg;r$@p3SRM;wavX+d)7?AJvF1fjL7B zB-LYYV8W?5{=aP3Hst-SzMCRkC zB|Aq4H9Zs zh9y<3Aa+5IUwm%0bmjtCe29+qMP;AdUe1Z%q-b?~EXpSTac#~rnDqNVA+N%)898dz zT&fd`w4MedGxdFxFoXDKR4|eo8jeaADVd7C2o2jeY14ovTsW#5v%sK!+wM*sSJ$u^ zldC-O>Yu?Qbt7$g@;<&@ZYNvaEvQySj_XZ`*iPt{74!N=7Ztn$n;)8qo90Gl4%}_Ydoyvl z4x{hWRc`aXD~d>^iNT1CB&H5k$Q}?v?b1Tx0fHSH9>_hn_lvIl70HuqHN+AO(Ho1i zay}9M_od{MjAt%$mV2t0TIz3<12ONIY{fYZ&*j~GcZ4O%ZpGC-pkE%6Xuw5qNayVZ ztZi4K6RAsJUJ_n#_C34WWD2+ABUE8a0NC;0rAd_qvMc(maULYmci)VKm8(R%K5Pt$ zo6lz+K0mhy%!$q^SyQBe({clqa{qx}%iCktokKTz=EUSlj`VY6584>nz*p;2v7N8B zueEJh zVv?`LY2O75KLGimlle2Ztqo6~`&29IMLY2>z!oG;Upt4L3> zm(8}e+~yPcNww$`Gn5T3zTMim?;@I9F~>#Zl&) zeo-t5lH7scRWF~C&|(MhK2w6VFGj!9P@))3FAEQT=N1npX5JP$76tRN$T9Bb`8KZj z9`ZJ`ID6e>v_l&~y9~&Us<)4@QLjfsLk)hd>r;uZtFYO|gv8FHc;v)htE%yqO%wDK z*$Ld7%L##mNZhAdZTyvSDf#N9I!4jAlWU*Hd*Dcx7g>@7d#}7yNx$TC{yUvmwWM$+ z>)mYTyOOCG&IZ?L6RMiR)bGRVB#cd0$+i#zZ<_O{B?fuvff* z{cN0x?~~s%TAmGGEexkF-14)SIm`uw2QwE~N7fC4Cy3=Ivi(Cj9~AQXI?xkix6H5S z=h%enC%E5ro@7cGWLzwt{x->~$e8)7uvY9pA=Wf~%-uUK+JbMos3>jwh3lKcsM-U& zv_??Wa9s_O?N@oOdfn^Z90oCGVW1ZJDey2PMspClvd=6u{WOeFpDZ5i{?dEJSxA2m zemXBbcY7R5paDP(!kkFi%veIignjs>XQP|;w{l<=UY)J?n}{bj_yZVtr0KpN;jo1P zT91VOk5b+d>}*ErK3?w>;`|p|^8fb&z!|*~{Z!5k9oD8ad5Ha1!h_fFp!#-Tr4Y*i zNbKZR;22L!my;TOAf(W1+o$HG_LS^(ujvE5X|F*}upY$mrG@bxFdc6%aDLV@If-~r zY;GSN@3*S+8q6Pddy(y{|G@OgGy>}ki+n3+Y@8;kYiA_&_>;P(!q@zZ=!6(47vH2} zCrttomWQLKXmHa`0L3k~BBhXbQQK~NC%G45)zJ7Frqf&5fZGbXLr>8jpTrvN<)LHQ z`dIb?dW_sd4JASv)879ZyDzNFnyGC)n96*tgZXtLHeffPn?|I@1-1qmCV_C(F=~UU zN2a`6$c^=L%dKs3zo_=TD&kN}K}o^zwMu?F*D-7tEN>-csi3RSyYESG>T@%e===og zXct8oWazm=eohe}(;L~Ge;-*8xg;6CF1qsL+qf7L);qR0IvM?PKf0QpSrEi4)B6D? z+Uz{qIrBudwIf6N)s7+iHNB0Wj15Tnk|DL{!Vm&Isdq-TbIWGA_S9!c>Ec)N#E>V8 zD_qh@m7zNSu$wBBw$N-3{v|8)>qpc+Vij=$Tb%1?X*+gw z<^WwKf~e=Lxh~PqVq@8U-xcCM=%(S*kuLCJG3MkcaOTm!FE?Sa)0$Y)t)48=I#LZb zjny@%`(_4T$b=Wek_JQz+K!mkRz`{~T0ADb61feukqvkA?%n>93|O0X;7hHbUC{RK zhn=U&!e*AkpQ&{HH-@KO5=)syKMH`C&!igP2l30^P z0z97%SXeAW71DOkaP?YB!jSrWP%gpwp#fxoJ7D|ist0_6<&q^Ax2=S#bPhM3h!L5t zq&KtrGB#id!s=9FDraBQ$SaGHL9ml8 z{@(WOlYowimQ%|#7;A3`tRfrAMY9|ZPlyLMkIT#6X7C$ z7x93CMTyj+4Np7o5KIXY3co=leCIaqc9mrOD7|po3Pp}gBFPOcE|>9Ryod3n;i=5? z2-gt-a8>S2*k=-LBD#XRxsLgL8JnKOL^#f7cJI~n2w%qezrD4&gwwl|!~_$-8c!S! zca!P3c!qL+$6+52yRRcsBG>km2y6!2HUdlAb_6Op>@UglOqW&+`R45__M%P+RY}(} zL{K`}3DR3;>J1zF?r(#@{$x=!adYdaDzME0lvg z04ba4O0DL@^LT!NKGdp@v#nY^H@R)xQ?-=54?4B<}yL5P7pst`r9@tKI`Os-?=2DgAxm7oLu8s_>_P8x&qFhxrRe)c`1skYFP->g2wn|wLd_CT#S zABGuT{-60Wz>q3N1FMnnM$itISz|I-NZDAkd&$S@;n(Yq4CFNSRkC;&LdoPV4E9T~ zqngvCda@AR!=;YWDC1>Bhr+i$U_pM6&J$BxU5@!p(f#i}$*|Uv`^Kkjkzv2Jixb#G zrLUFG{Z=RNM@Gg9vv#>YiQxAKQ+Ofh?h8D#^SZl+?<9@Kg$c#;qDM!W)FYXa@XWVWQW=y$IxPFcb3G+z3^{O&(<2tsY|0i4)ccujnPMlyQyaFfgJ* z(#29pY3|Ei9RUl^?2^+0DT{T=lh0Kp&Oj`bOW3%EY)YE+h{H zJE7%JL5WLys7B_UyH5{=@lr{x7;eA-a&hw}|KXfFljnd`Zs%=2h%2_brf8-dDQ!)N z=g{$l+ekC^s*i4|IWsM{+VUwjU_z#{9^f?S<}v$k%QSq*?u;9MOPY@24^}q26^OVa z3Cr*wXkk*`hZAMFhxQDuus6KXbg&4p^`@zRysoUjACUtcD}AFfKFi?7fu=pvglD z3caX(Z3dcl4rWRrTga~<2I18OniI;`RluMpixG@|r8bimrXAISOhfUkkB=Afy_5A2AxTD3CRWHT~qT`h5QZ!fslB|VmtFcQbMJ*=ru^U!V^ ziqfujcT>t2JKkS2{2#f*Km&iskjdHl)HU^S=f{rJXbpKivkcl870K_1h7xFR15IB| zM8$b3Ypd6(tJwc%CRCXg%A2BVn=*{p&}1xv<(LZR?ihV+B=*63a9TFG-%K2)LAovs zjBuM!bP}ii^JDF+H^p9IqZDL9(F&Pt@X$$R?ytMb#D>Uy6bv=ezaw^aNy2fdSq`>N$ChE@ulHo$*X`rprUWPr@pm6&Rn z5mZQ2Hc#dH67U!8uYtV7;M`um^HuX=?qT}2yR6dyD)TU;o3+Ds$|t>6c;nix8!1_i z_iEd+fuyd%tR)@`jJ{PmENGj&m*%YSI#%F1h3^^HVzT(;x67EaQZtOdcLxiLk8J7j86u2kSqqu(5<`Zgit~M#49WrJ z{V!218BxTaVExAA&I~5wBgf=iPhHjpPowDn&%3HU%2_MutQM=O1>#}&|JZ;X;buGoGDK< zd=OvXWsTD)(PN?yk0a68R{_2y@iz{bhUD@7@k=pLABtfoAiMvN=rRik{*d z4wA&>KEDOu_|M#QHniRNpRyrcQ_gr0uR4Gc< zLAI))0(BZ0#zWXkIOZfu9G*Ph-8hg_|7`|fh8YLs`iLPbb*td}9H1gry64}}4vKw; zEzqVp&5b*Tk9XBA*yAZcw_9BdsS!dtLfS{*iAza{2V5UDjjBrL3DeHS;i+2-QJN$| z9Se?;PA*ha=c!5YhyXl5RogZb&$)laC7_!@j`_h3xBN=#W?Oyv1QJ@DljBpk^o>Nj z97S0CO2DoqqoKTN(#^0Y`4>}zq*34gUv!u1$4A(S*Au{J0b;W7uUZTT%ONr~_n^PY zQQR9??{dwYDD4H@|CU_sVWw~&ao{VCJYf2NnSm~uC=pLUtox^3v6UzugBzMEFb7{q?lr>Lfy^qfpl z^-57;!S=kLPbaOX3PtEO~&I zH|-`JcwbuWR%J8@-QK(FO$QV8W!%(E+AkNFhe^XfIeXvG&cx8?PKjgnL;vCd_j(@9 z_7mHWkmfZZ2oto`$~Aq!S&;jwX=_1j@0MHIfcPok{frM|Mq0R;IN)}CK}Hvn{t(d> zC2dNoD^fE)HPT8GAljYYIU6e~&qPeifTCcj_{Of7SYu&GzwNLma+7S*Xl>4m|~rC^LiWZtWWAjcC5l#(P?%W6AmWPZ?R(g3Vigfs8bR?`eh`SM4-Mwz5M zeymEkmM1M&P|bIPB*G@#tz_3vp3m#Wq95y7NZ-Xxh)&ch`!=uXprpVxGIl~Z2X(Cv zN%!IYTgb9G>?qagxYJTp9H@8m&>w4?459g^Z5E4Yzci#<0~iZ7+dJ^NlA{w1od6t( zwKf$t>Xchmei^v3ZjZ|auxyxILrtmyx}EJeJWtTnu|m7Qr?~`8*NcajmvD&syP*&l zr^Uk~5sblCOT;{cyaaCr+!>00a!=+HC~4{WpMxC71;*@7Pzmb9Pq1x%DTej%prrFhZvThUs%sUF1~`t* ztht}$d5VR;Xvq^^dSF2nPSg>p)At&c66~+b@Y8MjC`fCz8z+H_`)$Cj;SF+#u8XtE z^Wjy}W2PW#2iVS3!_z%OkBm|IeHa%3FZ0B*-d4eNq3BidUB78qX>1FBlMiJO_FEE7 zAVBh%I(kVK1!`9uM(sI2-Q{8baj;4+j%|=Jd?$5YDFY>E*h$*jK(1GkL#){c{tvd9 z*yiA~+UOs0L`AS*o*auXMw{KmV^JQ(cs1Tyr`~w zvMNZagn#E0=Gp5O<(RDDL6aLJ(P$m{cUwJQ6JNsB_Ii9{1lG_*P5j3EDvTcw{7(&T zBfB+P7zgt|Z^u9YnejX?-m4OLBv7u~)1u>NvNuHVJ81pv{>slKXk}seDGgGlm~;){ ze79p<<`I2$zuWoU2R0`*)8x~1D)(chP>EY?$1P8lTjjJ22;Yqbz77h!Fm4xrkV8kG z%}+Ge|5BSuFh7F%57l09&fnJ`g+Rz%UIL&rGdUtGjS)ty!)KHs)Trq~XbyDUM!F=~ zkJjFu-c=-Wj`^Rsc}TSaU+!K~J@%W%XppGPRyRkB-IeGbY?d7VaW~LwXc+kwV1O7; zSbynyKLmP_&Pb=0{0;PFHITo%g}a8k{b&-3&U3FqqG!^%45;l85MJ{Ch>Ex})$`P_ z<-X=Sqj^QF`bggT@%!VYCL=NQ3`W_=Q3)`$u$Ew`iX})wpH<;TxZg?AcUioZ{|Nm3 zjkigcF4|q=y+X2#HP)Z>|A0%CG_~V-D{dMHdy|_KsP>8%`Un-V?p5UH({LXqrDYh+WO^a`3pv-af;Zm=SJVO38q7xh#A&<^eTS0&TJ3Tq6U9aM zA2!3moD%R@iE7cUT@SW?JZY##N8|nwx3^J z@HsR9xxw3>wlC^%+&_UW$G6B;Tz$^AnUxgsGCgF>YaRA?@`ZWAsDq63`*OHiO@kWt zbskZP=rbetr7|$q%M0^-RnlkN=CvY<=W{UgFds^9{7rq30;KjfxXHsIl%x`HrMN}8 znn>T3ZkwS z?h0|=QZ5`q4k))Q&y%+`zysk&Q;pv$O{if*cnc7LT|QJ1k^nu^<7rn_n^P6ED9Yw} znP2wAh4?nqEcnW)7Z z@>m?a-vE;666*na&cdH&*J5Ze5_O)s@Pd(7h>yTDoo7nN z1P2XZH+&b+Ba>&M(GOy4y0b7PNF-}F=;OCk$z*HDk_)zk{k6{cFs6F&4wwQ|J$Hpa zBGH)tEgr-n^OhiMPt_pvYvAoYK}785^05T!hzs3-nxr-3{|Di@3x}i z{u05>&}Fi@tUB2@_7}`~2;G)^oak!e{$ThWQ>7BGRYNYy&{E9#wi;9gfa?JAP1(v= zmzzMvI7FV6>V^us`=PodhQ84BRg3efUyg_7L*#$jq~@@`UrG*u*Hio22^fc$4W#-l zm)n$s@ccEz?e5;QY_W)LU?HW1w?+`R3`9Ak^HJidlIz?J7D^EyC$>4|VLa`0wD@JT z(ydW^!Q znhL+ngVqS|19@)$A~}vH1SH?rAqa<^#NlBnuxjU*CwB8P(M;c)p@Rrtl>`YbU%##lw7q2(hXjqZNYP2wf@H!q!_ zX-x)MeB87$rQ@!|5|9Uj52M7;U!r3&g&e-c^+fdPbtn9N_LVE0 zMkiNMPKlR4Dn@5@Mmxu9F7L}aW_p5{<(Hfnx-TWmW37fZnXhXXv#R40Y+R-Hf_B+z zMmHk>2qt6he5CV;aM-^>QK&714Dh&%@X^c75(!DdYVl&|^6^-5Y_!jJTYV>C-;U-s z1^=78=~k-3*FWy2vQ{AX$05D&rovtW5z*C7hAZP>c(w1zKp1GQcNNY+2ZTXd7R+fiT#UziX zl-?9_6Og`AKFkRgew|UyY<%>tynojLHE1zhnXVqgZ1JmSwN0`gfcoQS^B(CQdpI9@cCs->%8PY?ZEehd)9J8uZ~#9yE1e2DlTDfKS=4k3+rFaetSf0zur7u zm>{X<)}1A%gIH=i{Mpv9l&EB1$ldCJNRaRw$mHpSsRGqhYo4N^jZep&6n4AotKk0* zz57TKt(_U+fs_rX$k0IVP(wS~t^sko8pfbMkcTE9~^*KsEaERZl47>yJ7# z!Z2oT`7XCNxZ4L((b;c<47Y>a@GVk&l^pL>zOIDWF>)Vf-&UjE8sIaU)4!?k_QR~u z$}3zaBzQU29pEb+dx2-nRDnsMSuMAg+@_{9?E+uZnkJ%2WNv>R*viIn{5i!7J9 z2IOV*AV=cT9PGEl83}Vc$sr0LMTeLILYB7Qb}zxXwO8>}fKuY4Gd* zCSoBIiox+!C1L}b`4le5;|$nJ{UV5W<&v?mBh~ay!@h3JcamIa0lwZ5^xq zh;=}`X{eikA^J(gtab$R$ou)`Zn1Q^KUFZSTmEI@YqzanvO7Yv{6<0~`QZzmAbB6g zd)J|=FoKKP+UY<}%e_DNFUm^Q#MnqF)ZJy?-?py5fnlHoDgW)&2BF!vF6AY3iB=S2F^wmfc5yB92SkMh%Odc6XbY#7`2o&=0hzE@p7m|5GcZ>5%gy64cG7&i z7oQ&$<+Q|$f24TFQW*W${i>*2`6Xp)bttfOW?3d$Xp7D+PX6jm`Fsk+M<8vgdur2q zOcht>rJ5zxEK{+4<--{)^NGx)4REpx9EG8ZCAl~F@u2D$dCb7bK<8w7Ycqskx5-UA^-pk;HNae!R+vd98Q^I&kNqf<=aN@n}ZMj`A@^8;(B;i z4}rlft7D$0((A|qVIJ1ni9j@$rO(vRajv!SdYzXEP#1>&AlBnNFODs|Uy8HO5Y8Z& z?|ViKWIFzLfNp}pvFLV*oCr0?nff;N%I@K4NLw9|RI@o=<^I}qVKVPJmqk8% zFsCX=Wu?D?-qpPS!-*Q?;xBi{E&Z{%n3J&Y_8v?_;z8oImfP7JSwJ{Xhj;T%ialV| zd*r$Tu}J*AjO(!Mr9T)s#on{n?Yo!a8)bv$`TqG)4q0WYn0!OKzl5=C%F}BZ2w}Z` zGb*5>u+XPA832}@%>I7RKuuv?(~(A+!CiDu$?mPB4XF}}{+L~U7cgg<>+)KRqT`C@ zhkf7tCzxR)_v|Z!jZJ}uZ#Rmb=ImAVjsav$Xz@+0U63uxYxt7A>TTttz0rp@C%D(s zSf@#8=2m9`=CUhd!_0*hKQjWJawM< zzAJd*L-k|m8pTc&m36C*2Ytxh+e7PNs$u7z?;G-G`&Ab6=l27y=p}srEt$!Xqq^Z7rT2*#1xZj2iajDlP zM0~)}og#5@`yA-dVdevy9)fao9+W16?l_(xFpAD|ZT^4PbL0WYf{y<%ul{ZMK$;Xl z^fX=7KfC)dB{2N_oQNf%Cj<}gS~!Vde&+d#=;#3N{67<9u?o2t#Reo?+xL`d>)+nLH~Po z1e@<@v&ebTX7wGbIbyxHsW#=9&F!Db2U)l19-X#d7D0}=YwPlh9p@(Kq-%9~<99pG zKG(29-$lgJnZ)CzUJDCS^%@XzFu58jxRM8*2(O>CNuwys)Tg-^`@zGHwkH>Z=@*jm z5_ISO`0YP|rHHNyS!C_h+5($8#R>Z_bo1vUePJ=~_sw(DaFM<-+YT`u8My(zaRII7K^6rLn~!(@7`1_g-qioXOaeUn4%wErBcdQ0B=zk3biPoz#%=l7n0fXS>F}(SK-Rh4 zs{8?u<_Qz9y7jn&j4zM7`M=S#ZP9gh)8_!MISi*LC4h#Bembok1n4*w`IC>p1dc`O~qE617|4|)*|TNjbhOjebsl}U1fd={nNZW8x}tWA4BnK zv2MB>Y6gZm9hp?)jz#X;anxaW2XaA3G&sPc3WUvZuU(co2e>wX-O$8$tDnwc+4d5a zT>dXF;oBaYX_{MUXJn$H#YWt@*ZUzEU>=sJT`(chSPzo$d;T9pTF$19al${5VKNML z$;K2*$D#(mrRzg+6Mu-58Z#h4s%*x7R>daS#C+a-i=MKxYd@~L%)Wtw>3qr0<8IVD z4>t2tJJr(gY^t)u$kIbZrY}17oOZBqpr?r0&h&#rxhfVEcdPVR-INMIwb4qy?y~n7 zjrYXP;r#^T#H;!EujM z*_1QqwwiD3F)(?%WwE)-8bN2Wr%VxEp6z&^hZYX1i<$Y;K!o+otUHqf$8G?pusjwq zt+n@Uo>e2=ViW-wVtcU(_7c(nHz)YAUa9FyDTz!vh8}1fsUkwS-bGC*e?YtmNRNE3 zygc^{x5x-l2>nmT`Ne_<3-AZIqj(&z8V}bAs(m=G{HkiC60DM=@*_#N`S)6O+m;*?EZ)uGD773j_$a6G#)vA~NNl3})?aRgNmy+g+c!4fU zsJqJ<7DY^rUbwe@Ol>$v7iU14y~;3!qEE)2epQCCGaT4Q0NWfeKIE;##V1t_ItKMj zH)b{w8hXE4d*?I#_+j32kQ-stRgf+BTCjuWtJz(j$-N2t4#pKCn8)sVVNHtQ`@J5< z&>__noo{>pi*J8LxzXkJH@JSQGg zwKbZSVka({jetHKAQV^9ngLj0FhqWs2lq{bR=7zjVV-seVLZTD zXH=2IEkgN!G?(7+{@?%?s^Z(x<4eDIaxzv>^G!Yp;RtXg?RAftSW%r<*_Wa27h&YT zGIZY;4+qOpp|ms&(QdJO7Rg1pja`(mRva{IVNT*N`0wNYqaxNHR6jKSf{FddV&SLO zR|X3R^WfED-CKqi#^&niTUp%`7xko2)87>i)yDUqsd;?G?@YpV-Bf2;jII6|#0UTDw9Bh- zO0SZ)B$ivyqAi_KA^G|EA;&vyV|oTB&XIKyU^}6QA6Gwbz3@XCEEe{Ph{TZ8&(7}; zm^;N6>`Pq)R_IRM`n2#f^3~E(L+OnmiA$kFC@vq%#{g zl0$$j@0joIE3-7W|KR#K8~(Tz)90`FxHm>WFjO59Bt2pOuU)Zbi*{?@M)Z_U7;`rC zKunPCE-y=ruJwvs?B{LtW&%Grzw*tv^L$uqO#PsoMFi&Iz^{2Tu`K-$ zV&&UQ;>y`usY#4iB9}+`iT*c`#7cjd#&O}kL;$Yfs{4hjp{fvQ4N<9A0bj4bGej!v zm(HPiz%OYT*Uz|M41U*zQPDid`E=$nXSJ_OHi{)kpDsXdpHpud`nu8sWq^#}`cS<2 z|5voaU;i-z|Gfmsu8R_w3jWOdEP126skmd{(bJ6mgZ*U-<|qDQwxJ^8_&+N8M%-|e z(~+-vTKW-aA$4kT$;^LI4feaEtVxXy&21pYb)VxYTfIYld9hC|*?&AVR%#1vM_FrQb8`B!-zi3DyKkMBm|b%)beiJ;o{+JWHByJeaVyd-t9?dVy79KblcspSJD(Zg z{P8A9I;dN@{o5=S1xVJFY-L`JjcM?gG1+JTW_(97qdI4wA&7B<=+>LHy89^;I5g&A zZmIpy=+qeaAh`NJ+2K^|ghZX_Wp-wRC`=8kFaC7VY1(`rhQW<&^EJ=)iRAV6g~$D< zBE|67kA-AD(@u{E?Wab@H!Yo=%f1jU2*uOHSj1ZYy|<6_mla>VB-1rrmeos$ zgy9K;ziJt*|7_(FaK8L0MaHar(8Sw2Hc2)aW8a_f_6^|0Bw&^VL^ z1(kxMqWg^{LXw-3{5JyW`sOx>LJ~MC+w73?Pve&R8N_Oz%$elmqrUBI-`;SI) zKl~DXLkfwoL&b&Fgb4j@`s~4D#;xjXk3OCeMtc!rnFyd|iH?wx^ml?} z-WG?#J><9Uy&=F*2q@T^x(+C0?fwy@ep#jZbP2`jIKxRy1g&u-o&xv7ul9a0#Q2Ui zHntyoCC;@!$|a87l?`x8+y+T}ds4{|-S8__Ids8jT=g`F8!q4kbIy$}TNiKpDKEr@(RVE0WUfq6XXB)okqi*nFYt4looWrk z;_)!*f@YqKuVY5w5nbHRe0FQy;?!I#2};azA>g3!&cM-7!41Z6SQM$`ugE^ig(A`% zl7-0>)Z6pwV~St55%~`c?RFNVZ<_r^0AJ?6zyL= zg`vEncElPz{S6A=P3`@R@Z(=u1`p5!bf;4Pp{F;UReNpm?*xl2WG{(!h7ag94LU|U z(ojZ)G)G)T<(?tNwkJ2qfE%EOwym+%>U*RM-!N=s?Ecq=LHM06f(*DD?vXidYxLLe z*j4g|d!-`1c&bnZ+3{a)or7UzI^Xx(C~I$gQDc=vY|e)zblgjI9B`5u5<9Fth3Rnf z_z#8G<9Gt#NEKWSbW-VL0r78qB=O~P3B+5hx?7k`cT7uU8Z4mKz4Kkxl#JoiYLcU@ z>2v&TlEyPSYT#xcoc`Bnro8aLeC{9{sU+!9Kr$#3dPn@`<*UCQ0h8{Lmxey4oFeRG z`aBD1FVl^se*H=B{Tz!}X%Tb~;B`J}v@Np8qQc{bLUZM$zO7aVjBd$CrIQF=>VeMX zNJRt}+BYT{-d_+SV%X9$6;0mH8n9F0X!7oao_at2bG|Epf*;TD5(FRqig`)QjtH*h z>^g~AA;*&*uVGYUphwrGI%`PIZW9 zT9Q0Q{rX4i+Y5``uaWhe*8UPj?x}90NOpz$nG9-X%EU#IL1|UWWv|pD>1nwhGA^eq zwaSS}Gd7VD33&z0CC5|4d6GfT@|`m$2QhG7`$tPc^oVeh;k5Y3gmEW*mpBwyh4!R< z7y}hA_kH)4y##Kc3#33EAD#B2;ALT!;%Vl${hTmqo{yH%RSbN=v&QhlCPEyLX5Kb4 zOeDeCf3=az*&pJ}iPYSd=H6O}if<*4=^I}i=p6K4jp-sa(b^_2^Zxs5>YgJZu^pL1 zTyMm`0P32z)yFQLAEtaHHjLp5s}{ZfuT#}F=Cf!i0&~Mar6<$VirZ|rmtC`VpwD?p2MlRzOP8^@X#U*L$MOhL9Rb}JY zgctv4srKMq*4oPYUOoGCHJNWCw)MVavrFk*JAPxFL8YDjQ_T&RQH)v$9wygcvbFo> zLu3)g5_v0+nb`fW(3~EfOmjnwD`m=eRSUb8*hs3-`=&n{HFy(>Yh>-W59#pp>Bi}B z^Evffn%B z%LBUYQCz#hzg@*=KJG9|xd};-QZS!JbUn}^uTP6a+2-ZO77`i14%PFqWIXYRL3-9O zT~jym=Dk@=2~qEc29`#z&&jFJc6n{O{?*r@i}T`l7?N`$dpk?uKVD`M{SsvPjt6Gu z9CP>oaPGQnb@$UWo3->W= z&ANU#ucKZ#dyaNZ$ywP7eNPcg+@uP;b-1w9b}n7muwQk*pSZk%jbzQg2kLQ{Tb5Eb z3`5(|ipJ#oe>HTB2Jk#$2^#dg29dsy8!q_n-o^Ajh`@UCB&aOhH-Gjg^oniAY}URN zzLXk}xSwS&w<6F-mtTmsXJAm>PAX&Dua+iU8Q&dBjMGJ)#wH8?(oTZ3#dUls;d!<~ zPsi*i_)JRhdARcj-9?AZwf>i&PR3tSjTiriPF;iK3p}fCRhlp(flQ^!)rQg`zI4%$ z*Ax-|w|Az2P%{!wL5S?XLY{(Xiy!zw-RNV+ccOA+l~DU<d)IGAI={OCME3b_M= zsL^hL6@7SWNI`g=)N8)9mC%CW?@fMAtYXjQNqBM*JB$Z*yvxD-iMV& z-S3;TADee>POo8ywGTrt38gMuom8RbhF$4&R8_cJpWBN zAC7Wm63-e?C}+=?@x%g0p~*{H$*Cs>tTtx~RLAPP4VKZclVB}ngg4(+W5e;@995gMvR7NL**~0wcSpe?!>4h4C-tm`FAG>#zze_6HCC0}Tt5q@D zc8RMK4c)Usd5Ef5IphLyAb)BK?gn-lWL6HZ>o<7SXK0bx{J_&~ zq2>jO`{?&RdVh}W@(Azi8VMlw^g*)oTQ%$L0*+Rd_WjD)j$w{*r~Q=7|KmM7{u?|x zzlsR;2Eq}|tN)baJ&2tN0^~CD=KPV5S4Wa_7FuabNe$DW2)2;0rGUE!=hIW*Xf0T- zo<}PQGCpf;R3A=9y1R<6__-Q`)LmZVj+aj+>C2 zh$vg8@*Y8NeuPEQzLaX1KUR>)3VCLU%WW!ef4U_)$ioX%8bQ7-9i<*eBN8^7j@gS2 zC$azR?BIL1-2M=ntTg@@oLmw4z@L2E{tKd2NAUFi`8y{el-JgEeD=IiYT6l1-D&g9 z&VFqdM#cq8L5+-!8JEytsI|P95ew46qHe~0fA>Np`8Zv*V5f* z#=OUjQAQSjTV~?VEs%T?AaZm2T7UF>x?50|28k8k@eFTFx7@`+MNYN#+Avd(x~&t` zQsQ8ZB8dBUN*a#P5VC)ipPT-cBg3#BC1kD|^4p)Bh5I&=r@9EHTL0hY>7;+_c}_OM zNcDdFCEf<@)mk&5mU5Ki$j+MvH^^Mw^IFE1ZH=O+^?8a|A#aU~p8GP`o zXP3Qal(gjNE&rcP@C~c{@D<0|cDXeAC$ch{MD7&D*M-F?6#RIe_id%QW5EipVN1>n zd82EOg-RDM9M>RyMrfhp(De}W;s+2RGE}I7;c4$|Lr+^ zC3Ms?`un=V1j;=P`mNopf34Nux=U+i&b!S_xGfPwsQ6-#+D$9U;PkC{76@&pbF-?> zof%+R>DB$y23$34#_xqy&mzOE851^ZbuZl)fyJVsa+IUdv0HOH$FKh`0>rMG?A-o1 zmRSD-Gj@8D3A4R4x6xv{chm((LjCFQ{Kp-nC*p&DbmoXj`YHBRZT+L+D=;^b_7apn3X{-+!n*U;*p|f9y!mSM(exd~V&D zJH4Qb%IPmu1JE=s|!wKqEkbjkew$!bM&KV?sztP6)tVCWpbu zD{Okcba23{fruXsF4jM09$u4-5AXh)ENojSdTH4wg)!wF%?`tUAs>;A<~I0|S<7PR zJE(%Sfq0H=;TF3#n5S7Kz0UGxXj3(^D%HO~knaI^#6^}yaPP866p)383`|2R+JUhC zj;i0XqP#WII_+paxzC1p?fVxt?YZY#Vv^;3pNDDoWu%NH!Ruu(9TN7>c$Q#zKQNxS z^@4!B(b7cpsDAM11Z{f{zHX{9V9JIPNC7E=umJ4IZOrKT8_gR=iXSBxEj)DB8M*oX zO&*o{!`Jw=WHRCJi;5_4YdJv2@|<{yK?FJkXWD_shdT|a^q;~${!bX7N%!aXCr3Zo z1HjW{QjY`REdh25h|tlYYWaz-KFSTh?R<}%S5(oE_d8SM~Eezmg*c!0f z(nM>I$8`^{bZ}0y0@;=OH4}vJ)V`8I7}=h7zOxYtimzYZwR*!Sd%Zy#IpJblAl-r& zOT6V#nAgf;2>H=hyci?-^y1ww7Z5&uA4j&%1;h3_kq}`|AQtF&AK&tq-@%`ve!3hnV(Xv)RjbUWrJvhDY{8b6%*5@eCY}agS$xjy=p`{ z*=)ox7NUE2kLy2=%jD#{vGE`fel7auy%7Wni_WH38|L|85qwP1$6hTNMgeSU4)I0- zO>mt5Cb;>qJjZPhDB3#Zvv9*ZQyM&1kA>*?O= zLkiey$lsePy?q1=ZE^c&&|4f{tUpjFpuBWfRUJyK=x-0_uc{+yTgM$$8y2b}|A&q! z+|I!H*n6v(1VjF|0V!?o{TGBQi%QWud37h07wA3ls45kDOuesgCp zJ>ii1c(0K}VTH65rEa`%U(mjSY4HdNRD{Nsa$+Iu3v|HHdXbEQdW635k?kbK`D(0gfp7G=c4caN zz*{qxKC`^YE>X{ivQ4zw0KS!#-EiSX1Z}cDoKBM^@U9q1*kXnkIyEW$OpY}`yM|{N zn#+Rx|FHm=8dQ_^q&(K!W1oGr)?o>)% znSvLT%?}PvC1PKO*JA3R_nUW&omd5x2HXF}>wO_>M|nQlda zybqhR{I%F`jv<3+cTl-d7Ln;Zv~K}12~pQ9(~ui8Vy#DFGAltFBG|c>kKce+En^vv zM&&HrllfafZvfdVlheoaVfE{rW7nzGu5!kx*2xa%1olANw)Mvxd(jRYGB&$ zIX7cuLb7&ue9CG0f-(xS67lzboMMPe960f~laTqZiPKwknT~mvw4GG>q?KTqh-{B( zLH=oQ4g@Ymk9w+SZD_?t{lML)TxCBc2C%aREicvN@FZ5UdW&V=#2`D25T(MwL`y5; znP7*#v@g?2G(JEiOQO3yTsSNC_?oU(l8wnAYPg>0r+5L{rq!gWYn(ks5wwqF++G{+ zmqF6q+-mbtX11-Wuf5uVMk?;@FEIq_e(Poa>#qOl3mJKr9RL3_;G!6Nr@sN@$1E6l z!=db0-qyvn6D~Nb=CY5j6o4X+b2i;BJsR~%=q=70)ag{RnAw}HZ;oHi)~3k{k|T3t zy9u$1Xqh)Tn_&lWg%ULc09)e~xeSVR6{$i2>udS=X*hZXYrQ$ZsFCSIHF!GJOyBRyE@tdNxZ zLc-^|6sA{|^`EDZ=wICKPV|f)vv4^y5bu#UyAl!FawUt|_gL-nyu`AuO#~cVvamGE z1GSj>w)D5zD^vK@3CcFf8*=f*S-v4l>Vl_-lh*VYuHI;Pz{~sAQNbF%$W?|*0xw29 zzmGZP!2+Iod=FWLq(s2kZ!BE%Vx#VZ$+l6sVZEc^({SsNT_v)0Py5FDvv5@Q3E>Hz zw}@OUQRvZHk7Q4mFueZXCw6$ELH#_NFsht$4Fc&h6!yz;W`LKQee-gT)+#QsAPG!D zVq7ywr&D07o~PvM)2=qQi$2ooOmq|Ek2EQU;V(Vah+5d%&eSzGt~GTj&9k$kdjVA; z{Ik7(^nY|4xXM8u|Tb*y};x4uL~n4`+<@%-}f-vD!a$bU_?F7%fj*jinZ zqV1A<$Dr~Y33?ZI$49E_w7I??gJQb{>6lm$O;J0N7Pk26et~Be*=A7e`KSJT3Q)?+ z;3!=wwH7zQ$MGL03LBnQk31T5-G8D5 zaR9j>JNC;p#X}S~a61_#CG^P($86T5LfCBzSyGOM1_Gs0 ztGQ1zLFYjDVfoGhs`f$$b;XtN$IEzOBv0EVN%x*)B3*c-JqcY9kb=M|V|dx-_6i+} zYADwn@VbTZK!g#^Fup*F3Qdp>`LB88{ioF0zwvmJXxqiHlg>Sf_;<+^sE@{0Qf}pG z|KDQV*|%*&)M1eQY9atZqnu;F-1~l4pRxcS%PHftrp!jT)tCC-LeflDUYRnNMDS5T zLGhELP1&9ay~7cUK8}aTl4{U-+SfV;>Fj_OW}BRzTq>;M5&O92nV-cQ|F%&kNuXEH-C(|_0R&S;Rgmsn;^A6{${Ty=RYjCQ!mV?sv zWP?}V&lQXF&20?FXtxbTZ?{D_PkngmJX@#M-zf^n^Q74hgDcF7g;{e|I&kZhILTsU$h7Ii?xUp%S~#=(REZA7K#S3E`$b=SYZ zMFLmnhnD%*YUe&40fMV(Tv&A00CSRosBT&R<}7OpRFx%L-y2?XiRBrdZ{Ql{^lN$F zO2cxqh@5O&CJC_mYaynb^#MP3Q@Dd++8j%O zTwbg%p4HTz0muGD-|^0>wzE}&k+PjhIh1ajWGuYqIBn~1yNXcq0Q47dxiX>RzHi$= z&q#;!^!+G%_QU|Eq1nwXl4Vz#3_RF_k_d*J?+b^-gy#W&TgZ+K58UA;0IcP<#iA!p z3hR$!==C=WfTyQsZFb=t{6x)FEC^1BP|(XftLpJyqtL_Z7Jk#<{o_ia*f9m!>!G%q zI*}GfvC+gfe2?`7{bH3&J2ZeR#N0LJZF#H=pDVHBIokY3{P? zev-NQF{)Y)!6sA@L5y<@Nrpgwty-~t3>(Z%!~0$amX38A0c17XmoH^XB<|5h_6NDUj0~eVqh6x0Hzit9>a2P_e|& zvkKq&DBD)j-b!KaT8r{2fAl#9N@B8#GW?`aYp@#~10wwXbUAf#Iv*Hxo}|-{MvHG# z?aCr^ss)^5IQRvJ>{;Wyo7Rc`U8sdUpN&1-={ceP$^>pK$JZUox4#*1H1vei?n}=Gsxii@GsGOlotRZ z>S7AX7DnUAoUL^9va0}(6K$mj!VhScq{N!X>=_J-$=C35hu?;*i8|eb^X|)|#i>wY zY-WZH#0;0gjr9x7)Gz3*+&BeAB|F>@wES0dZsqGYvN7=|jK848Avn9*!8e6%H(elc zf9sT#N?!lC#j{}~IZFB!ITnW1Wr-Ud|5$JhAvZXU=go@z+JH>ZeSymfBwAm{H9g1> zg5cMZAZtKp4k#X%vvL&Nk2djruBN9DHEhGtRfq&FxNNHM9ppSFZyIw^99ja>NM{a$ z7QYvam-HLZqgWbO2>$fNwf;9Q>7?nJOgH}5EqzSWf6NG%Q0DxKQN~TfpO2+2uWX>s zuTo@E;5c2v z6rs`r@0`7O7m9cuPLtJOly6JPoYfh7AgG@k@kCynZJs&g$iV(aStf zfM*MwUki9pI|*7DZNf5Y2yvqOpOg})p_d1~`1f9V=d5zOlc@6nJo})hkSIz9Kf>2T z*13m3T8#joV;Cl?Duxj`GfEsj-d=N@8y-0@%VuUpRP4NXS+Wk^)Qinr!Xh|E)IENn zT0?B5!8OHm-iJnVKUu*k76f&@(q&AnYM#9)Cu^J{0PTu>WrPp13@u*OV$ChDX5k4R zHGa^>Xzkp?cGw7ou4iSxWDiq5$E%n!6+WNh3}}LCl+u2KUYo0n@5N4a0BOJsQz;*X z#MKvZir=e39U}$LxJ-l7%TGz7+Utpm(ddJdpl4qLBkjCY2#dg9NEcEvD; zv;0S3;W?OGt6;P5>TTwo;}=oRb@lGcT#2H+|F0y*AIiR2f58j%1lK!?Mf+r?Q|pwR z*O8Mniz>Ke0RD~E8RhPsSpG;d0ep1NqbNH@ehR2PZ(4xn?uJqY0mV7Z9fPrz zRn%||jljx}XYOQQBm)VnU+SW(1sn=bLo>>zLgNb8TsW612+9~5%*tmc60h3W@reaS zvk9MEC798`tSGm%j*PV0TWC~)>w-sc0@jdULhQf$G(ktp&aO+pXnoh0AUC6ViZm7! zB6UxbAa`9Kj=9BCkHh1%U))!KQ|q%xuQiq4g?;55=UCBjtCu*0Na(3f)3~TwJ+M!) z@1HPdATS?ys|srt8YqO!UCs*u?~}&N+2+py$>l5m&;W)akfnc*J<}DSrOB?`mZtx@(|^&BWZQHKC&IH3L!xtG9-#N|=*Gl4 zCyCf&3$gjNGs^FjzG>~(Ss{AHrG!V^K1tvOWAJDi=g7ji)g;WS?DNj9B~JyP3*z0C zZ!ohl2KGl}o{4`=lhRRDTkU?hxXr%4m6louZTMUl6AKV5#($6=3eP+IH{gMe`gdLL zu|NLG)0bFs#Mg%I4-^FpF~nZWLDQh!0vji#_sEC-kdC~spf?tA0@&Y%|0pfIGEkuP znuC)Lt=m_EhoSTY$*NbzrWmDwQUJli6!ovb2wC{#3yFm%W=&>%HD|v1Cx6CL1|6El zq?W`ID={v{O$01iOMaT6y|os)_hbGnWr$ppToq{iC#gCI93>3LwFT*&nz63R6?j7L zH?J29Z7%rDI`|IFA}}d~lldYF>8gN=j!^UAIYakVIP%U=f8#_?q;4asO@$g^F9_es@oOgsJ2!;v2vi|Ez z$e4g=#{W8nBA`-}>%I~w-f1xL6N;Xm2ICOd7O}VC#fVog~;nv&cENEBiNh}>|t;rB-O}^hu22JzFi!NkQSkR zQ1Kza)~J}-s_o~7Ya4#gBbmHp4;H+JxtF0ud*20`r1g7!M&Vap)(eX*|E3|I8#@QI zj!@C`7!uV0+Z_T~*ZeT)^L?Fum9IhN3uP{f$j+odo6ZdSn;oaNkJS7jd#0{sWju>U zq*AILu9hD04v8oaah6PqrpYBcl2FeR>BJqZ`zRs*XYkx5*L6gIw=94m4SoR)xLUhyBZ=xcxW;9HO%mP$jyVNQ!YgJI2uT4U${Wg3 z87?x$nXpS9nU{DR#{LjCG@!jS#xx-YrMLV-RKeDgt&HbxC-~a3p%xVbBSmP;$Skp= zm%;JYRva@r7N-b=!gpi7dBO1bOmfy|b-#L6-+T-Atjvi3ouvPW{_eYF8=fRV0dsrH zCBR7zP#14T8Hg8bHuJ9Lu6W{?Vi>RdSmN zNfL?1lX0m0t|ErPE z36fiUV_gL@t z`ha85#HK9+9DBG+nIiu}=t7(6pkKF2c=d54C3zG#8gTAPTHRMgGnT)Ld))zMmsPE! zKyV|Z2cZi&FVmMVnHRi!k8m7=A|mjkx`;PAoLGhov`{&FwOQfzz{qA_^6Z88rX2ZN z8n`@SvL;#&e7P~j)%NbmUS0$?G~jtNt4wCBfr7TG|NL%=7$L)MUvysA2a4?o4_Q<~ zj|G>lH9$?;kz{dKa_>_L^!Q3FZAEeyCpy_|Wbk6j#gt9B-^m}|)1(VKMYDUQ&GJ$c zT3#$bXYnPnfeF-)-xSFP<1g$Of7N5ByR^KJtvM~EykD! z!!2;0$mUcFlZy%k1!!}fo)y9Iej{UMxbv@i2~j&-;AR-So)8e)F>zFoRKu;z?BZ}0 zF$t>|#H1D{*rbZ5?I_t+^+~z+WE1!sf?o)jG0q*lEv+V5QpgD!d^2*{vCB;uBaDDi zV0c6%s2Oxj^7+^`F$OX9NC#*#qj>|!C4GQJ4MIvN%+D!bQ)%=#3- zA0d`}X>9)n9>RaG{Ht}9Sy0?|G$Bt%`TW>*%tXHvYsx#x@1YcA-c3l22=wFjq~NG0 zYj3;f43LZBA`vVQZ29<*Yytco8CnHthDai|;dz1y664S!3(Uxr`th=9)kIyNn9(Of5k=PY-2Clp0|T&;@;+RjaPbw-wbajR z*dDrO?HKS}ePGAju>;-HSTDqYz*Z>hpn7oe1CL%UD?fz^Z_|##=B~no$T?}8<0a{g z-%P^6r(TXVA^arNPNi27kyb{-+Txfi@ntqx#>*M^g51fKpCQ6EQNWYRL|;x}lrF-|(}Xh3C1IClq3?`@9ef_N_~aFLiO; z2%%~gLaT&Lr+BDFVrp>VFt_u#ohHS!l0Du_0x-d=GE^p$N22dGFmrTt*_cLu_MOOs z%gWVvr7*h=M@rMah>K$rY<%(BL24b3?%+Sw-N^FLtItRe+0$#$;}3u$5-VvCjN4*5 z)Fq^Bu*DS~kL+rRFjC{shl!DOx<*6CRbt1{aU%(1RZKEGw+@jakXc5fzkU1B5<3mI z;tQIFla(f%$ za(G?L_zFmCgj_YELP-ZMDZ~_&sV8VYtx}JAwCVMc-d(QErrOnDF({d29&@{q49V%E zqD&OhPh&Fcj|(5$EFR)@l8J^W@|AZ9W5!BSH<|5Eypg>r;6;Y|%wf8VCG(;clh4r* zY7Pd^O^2hhD>-DgO3C}H57^_3v42Vz=~S1Z{Sx$VV!NUk7-WUAe=O!;_5{^tUmIXsL?fBrcg4!>{FU>ByK@$- z|0}71Dscb9nu0|sFPB%YfGW4(z~F3`a_a^)WyD_D$vY7Mq|w!TQsCp(so}E)>ZcWr zRkQ-Sx{;U_(y0cNv*Oie>R74rr^B4~gg!#B`K=^hOhQ&(8sL21z{$nKl_CdK5bG|? zT&ziASXj8O(h9sB93-f+M1RhEf}0Qrp>&m14g!f3&e%E`aqqQCmZBzs{*#Ql)8Gzf2Mz(HF}BNw(Gr`iB&hP(WX z_dVsbh+ub($!lA#Nu4F$2oXTr*EsPd&hUn)gFoWIfwg*X=$&=-HF;3g7J26PTtRsi3+aktzhzK*S~;8Roy4zrOA`1*8GbgSF{5;)n7dL!I{BX7x#g z-tQFtGT(jw*m?215Be&cF)la*cj5f+*bK&q81~-7-3Wy8Y~Catf~O0a2%OW>{ke-u zo}~Vg^i%eWJpY8=2X1#tQ7dlnxkY<5K1p&aM~|g_GQU~>qIyp1MR3y2QwuX5%qAe& zhHce=JsX#ole+-cs^MFlaJ6^}LCTS#z@wBU9=A0E711qtp=vVKt%KamUN}~#F>&|4Y;Vdg^ty1 zeG(g2vMEtwrno*PqkBwVd&34ql2lN5Mhm{#20;3}P#b(E4_wT2`8~jxJ=o)>e5`bv|V>l8K(^*t%uryf9S~|mIwFUV-K{y zL6C3h`i||I-2u;ESPuTEKP0hWTgJ$Gm67B6bel;qRJU6WVX;ob3f&GpIG*$#;$VCK zzpRaS^d0KwjYsoY&SX4XsN8ih^x|$@PrRTj7@`F0u(>6iFC)D)nOfXm`D|GkV&6kbq|Gw z(71b$`KHwZ*e=uVIU2>B_<}rQBfn-&Min*3A*(h|Ppf=8ncF>tByFP>KXQ>aiTDp~ z(CGd2HCHCEEIn^YCFrKQ{NwGVtkAtsQKG@+j(j%Jz9R8PsXvSVqiq&2E_ax6l7pv& z#s^19cT2AOWhxK_wJ@{tfbIvEvV}hK564`h6v-5Ked)zHbJXfnAzP}f9g=jIH7SBS z=+6D`K-DHD*nqe?T!1NhwuC>>ZAra7NTMb}C_2Ex<(tmNQn&21$=M!D<6=SV%TH3l zQpC**I2o|&6D<-4XKF<920T*+1y*Y4%{|ySV!QY#nR5weNV5X8`}mtcBs>eGa23V5 zF9=9H-n{jw+0yltGe3ByUNvskpUfl}r#TNp;J82gu;NdE?^FH#4t2JPZ~a=Gp-P;f zk~Q&v@HY1SWu3Sr|M&{-?WZFN89{k5yVCbcSsE0k;td6^Wb;W854&oGUx7cNAbd6(haoi=r$;|#A$`*dUcZ77%WJbg*oa`~siZm6DlYgkqz5@778WD|I7$%m zbhqxE{1<^{uKj%YP^NVLrK!zy-TrEKe5Isz=Dw#&`?^u*oy!3nTl(X(bNuS%imOxM4m{4%jUxb{!WX!63VD64esU)I&t@Y?wF8$ZhWmGaZwddJ;thP{f!-&*|E z|2MXQg%bj_4oKIr^HGb2iAIQ~@L=eoIFT&n&hG%s#$tLwgO!Fs4Smf9P;wNc1<$Yu zV2%@FAy&2`-}llA;U^?=N~N+qvq6d=4tm8`=__Ybz>-S46vx%v+|ptP^PJynw*9P!qy%$X zV?+4f;C8c?XSP`><2G8kzie@c!FynDrVepY7D=_ynSY$Pk#DL!yxi}=W=Y7oPh>md z_B6l7uGi`xXvFW_Pnv)a9r*6bP**xs6K)@loCSiqD*uP^(0;LaiWt}TxMC~&jM(WCK$x&%#h*{%Ld6s$RJ;M=htxuM86smP?Gy~V^WAnm+o7G`UM3&fLZE9^lW{)8bn`6lp1sMFPDEK05>am@_6S4BD;|4W~05-~-E6Pz-Jz6ufbrRf< z7e^nPi7KYMM;`WqzShQzG$mo0;mlf|{4C$AvlwJwDC_kf)-Bk1Oo&Go(k7n&$Uwh! z&reTf(7}_due)8F=YL9$hYpqh1o?KpU(FcnV#8b5oDA+W3&uEwnTTZzGe^DL!_eNr zjj6x4Xf{07*+lcDKZHhJjWN!*5OxR#h=qj>$w)*atDgtPrxL_alavBY=avb$qM?(! z=OGgI0_$s?DTG`x2e6=zbYp^aCbGAi@InkrC8Uu&#TdLuaM2(Tvi3(+)YzD{Pudk* zH+{0B%hj2)CLk2ZVGN!Gon>D`BQN#eY}I6MhY_53YWca#U1B#MU|!K|-sZ`ihCC9X zEOHsI8JPy;BK~MUtnO>QcB>*|4m=DD*YFE$jIoMqHh_N@+#kqmuHnL2=Uz87Ns1!L zW?j9HfG|eO{I3xG7yIIW;AIBDwUPnPU`pgTMYA5|oEZTLjB%N`>;dvJ%0ItW+vEc- zur9XCazPe)a%$9W)Q==DrhP?h(=i3`g>fG5<>XvWU(XRM8`1%)L3(iY*uH%VXo`_8 zsMw>`J%ym)lCfC7ZN#IYjwr;LVI2EOSiGjE0(Y67LfNo_G>z`)3a@&wOJaQ`dr~(9 z3Q1F6sY%!JpgNR4^%4n)ynd0dcI}$O*gB3=tqG-*;Paeb_La-_dB9Uuh^dZhG>zWE4#KQ^*+v8`;NrqQu9RzQb4xIJ z!fXaTcMmtizQD_iF(z zJ1W_9u$~;LJ^7B3=jol)IYmk`__0%D2><2rWbr7oQ7J?V>hMH1r;OB9!W|NPx#anz zhJM#1Cn~g3s32*P7j<@uzV`a1sy}m!!inWUKwcR5HM78ko2R5 zn*|d!(CS_4Na4nUxSx;Y8dbAdM4HeW^Uznj|3zn? zm;HSFcqn&zM{-?c#pRrH5BUEEl^&8%AL5ZK?(YTtF5Te+QA)4+rVL0ID@=CyRGC{= zchL!6gs+`5AyuN#8gcB5it&)t+L-@jSs-Hwg_hQkJdPOq{bx5j`%=shrAyBcyb zcC;$Rym{Gqi4-+_DFj%qpF294!^MlhzCrvkK=%fZxN&VG_?)eK|@Lc<7hlFCf_(^6d)=ZaO7w1RZ!qOpj&dgO!F>-kmuZB0$iL!b_w`;pOVR%m1;kF=alwCc0C`VUFHUwT&2R~ zWy;6sjOOGkJQ?eJKP;_%2TW{~O2?(JY*EV%Yiq*8=dumJN(eo^oIknPYo zH#2YYf9{h1F||Aif@XcD%+sEG|9(uC`AwAZ{!;Y<_$TAYNlc~mbr8yVtJ-Vyv0;?e zc(NT0PoJLy4%wPOuKhJsDI50zyAhQ}6zoyR!Kqms`dlnA+%Ea<5zWR|KJXu3`Uht4 z?Gy)-z#Gu)ciqEMe?JMT&tn^S6*qP77nD=2@lW6?;v)lZ^^U-n^F3IZj6x=G{49E? zv@kEoT1;1{)IRWB4$RFLannz1wzx1P0s^F$4!`+L=!vepniS!QI|7_*FhEh8PsQxE+$JYVR(TsAT7JG&z z-MXM9ql%QK@JBhr{rh7#I&`)Mr3KDx1>>$KP&#D$T=9ZFSN;XOlgEK1Om)Vcu!4AZ zJN*xvgywC|7xJuD5#T+3b*;c*M#Q&=3O3&B(x{aVBwB)#iQ1N|&`xIGn=FhW@67Rz zM?V2;Ct4`af+im3Lg$6r7s^sl>Yvd4c3zYDy4=eVrR^2w?_4O0`$f-7J+fJ`SJ6qK z&A3iia(W&LM(xhckDh(^6V4H2o5vQT$vt5jqp=H>{wc7<*P_#9gSO>g!ofU98}I!z z4!rZH4a?82itrR6!5TI-L5aooYbSDTF+wkW>v1!K?? zyJOB&y=A06QQ-T7fv;tnX#=r*vpQ6hd|5wsVPncNY(Qg}P@hR$o>A4~kYG?+RNg=` z|D!f&K)9O-Q$@kq!-}Z`XCE&poqZ&GEOg)#$Hj(DhufXjFh;~9c{^UwJ%OlQC*dov z;}wX+#_}H6YDdJy(A3!-?*Kat7@q9$*QF_^lV8zO&g0YUGFfbpD8o}X_|D>x?vbI` z(ES;m^6+vvjES)quM}5r-d9LX7@jQlB`%K}3W!trCk9vvY3#5$hZo8q+SHjX8 zfJm(uXieXA%zrY>(;qe1EQha{Gc1 zS8plcSD3%{4f&^o=JZ-C6gj$>l@1rX!XK3GOY@pM#s#8m8HQY9w+&i?QIxNGtnOK{ z_b4Ij=|(x^8ju`%i3O4K_KQ2W5zGR)ga`l7AXfimknD5e?w)LPvP}0amj!oPO)VEN*e*B=a*mIm{vfNnKbec?N=r@4_S(t z!+et3xn<%HQHMT)TsGZw6|T{eMQ3r=3!dBy{F0#2sV8dxknlXFY!cr&Q(rC_DPYop zKQ{4qiJf*u{@4`eXo4VQ1>C`;%<$Mf_}w`P^z`Lfsz#h~#rFn@G-9IKcFz!blj5d)Z?X&)uvPDB<$`R^lKJx#rG({b$OigcvM1>&_dc`8yTBF!JEsbT5|->+xR07 zJXi&-ikL2ebtVkDeT^t@Y38p{KS77a3M$PK6v1Z%F|K0ZZWyal%DfPGe~x5Q69Mpg z<(S(e$`U81}!}q}zhKHkfw&GK$5L-1F zbxf`Vf+5>S6tqszaMnmpOtM@tp0H%Au|7A!P~rKj#Z~nVe8;A^IR>B52Mf6bWretm zmNdm&R4^ROBdF<1U(zU0MWkw+mRniIwy+eJe-_fW)QeF2jbo78c<2}&C%u+3PdN8! zAamOcPzh)pnN`H!!b5OTSbV4=jtDjc)j}~zL0G@XfQ@d2RHUl6o49S$&ky~_y9-2w%>7o%sk zaTwdJWa5l^-fg|)nr+0UvM?$vfQ3z_*Az1aU6%h8g+h-ZwrwSfe z)f9O?t;U6sn*aMcm!RD|ufI_S9|ELz-qF1__eom2K=2w@SISH;8S(K@r%1!kKv>2y zmBR&P3`d%*v5>7=3u&6(W@<0duvXTCQu*ModLzu5eYN+vO?FpyQ=8)r51xB4j$ywb zNudeLeRM&~SGM9NV)>JNmz#8GGnshySA$7}!+*Bfb%+V%$bZ3nk!GN-)qhz@ylao% zig%#pmmTU97T;mH_V_ATr^g-F(YB`A5uWHh=l&&(_Qe*f6pIkcfpcey(irUCRhWzM z7{_n!U?a945sTjfM~Jm2GsB5qK77RAcD5t`SIKbPjH`xERK@-Z8*a3dM7)z^hmLp; zn$KGo11S-UQjG-o=8-C`cYi@ZH=t_}meB@4>XBK>?D; znynbUnI7U(4KB)+&hQ1NXecSLFGcW0)r~4QC)5SQRqKoPFkx7X;ZdM5zt7lI$C=<< z#sGACb$Wo|oJs3!0e!KWBsq=XG8x~=RK`8^kwi?_QM<&kS#)Dx?4aYx@HY3l(ff=j z?=DpfA~#3i)v8979!kUBEOiaHJVgKWe}P({XzM>uX4?{qg z+hmj$Ti@g0qz|(p%-+sOnohtm-skkEO2ak+WI{e{E%%DO3Oxib8Xuhh4}0$w*3`EB zfhvjx6$=6)O+k87dM{gw)JP2^KomqeNC`clvK8slmEL>r9Sa@lgg|IgLJJ872#|!p zjqZK!J^O$5`+e~x-?vJ}T63&1NBzw)%VA^2KtXnctRPb*7ueBZsQJ>Ll9#c|-su1o zejM@Ce=YWdyYPYUl`=%r68*q{*9!l<8Kw3M-K_1XD$^*-@K@U@L7Y!IlwyD%tm{*xc^Kmts*D;$LKWyX9>ocx*K7 zZ0?z2reqvXAx7}{0;@MiahKpth z*lx^|@Urv<YSdV8hu3_KY-ZCly;i`O5II`Gj^3ShyQu7%BPUfr4^W>|lxa_S6TK z{ot=p=lA}ZhX2CxLKUK*RLBF48}WI^aq-=~v5uzh54q}Ko+Uja^q)0=gqwZCno*b4 zL?`sl%AJ9UMB|>xjcXQ$Ni9=X3Vya=FU)!WLQ&uY)aws8D!aSoF`dE{CV}hqZ!BWp zLbtyN$ke>x6XHF(Jhk+W(l%ZH9>%+;#5wp}(1o)Vk0c7n4vJ&q)VW0$`tQ8TvE0eq zP}zMvQhFVt-YX373gGujbn~V;MH9xWo*w>ex;oV4MT+&7lLsM@eX#z5&ySJMsLsB) zfvn*3Nt_xYmU`*Zb+g4H`rmUl-k09xe^n|pH~UiLc9~q@T_EF`hlLN!JDET)YWLZ* z$$q70sD-5~Ld-hpOvY_3#N$r~s~bMjT9IDpiAsFGE_3f>oq~^@!v8c-zg%5vwePM+ zk;9gO7{u}P&ijyn{+-GYg>TfOpU<365~yhISDWPOi4taS!f{6)$US8n`6}EL@JBXJ z436WKD3)q4>4t^%g+OBbR_3kVxEa1#+0(KZem<{i>UQ|v%~Qe$nZeHkVxELJ2xhJV zmhL%H83yZFqsQ)pURSnp#R*#~>$&MS@4Y({)CSqDzv?38(?99C_&`AR?u{?iH1zN9 z1OqON7xS3hjN@4H)qQ_o*g(DVP*hzQZ=$^i z28u!ZyfkKF=DX$O(Mz3#UHy{chma_4S+lD`8DeX1TuNPBx207W&1Ss4tDPbSIyj;f zHXouL995ube0Df$-!^JO+u|k~UkirVHa`Z|>N3hKcbqSjZOCq;xCUN5Nr8PS+aEk_ zC(404P>0ht5ffgEm~a_a$Ji`J<0$BI zTn@sE3>>r{1SQNgxF#u)jt>Tw4|fIp-SjMY|xKN z^ER(GC)KmqmA*KX`2^Gi?75VI|a2ZEwLyRTcacBTVeyu^mPxir+PkZdt$WCMrcZO+T9`Zo#efMH}5g&mS+uKq&^@OF2dxv^gW!C6!foS;V&sE!O4Qd zz1R~gI(GHAci(-waj8@F$9MZDp%S-bsYFbbgU$nj9M*sxe#4AbN{5Tl{dhEDIiCPH z`Z}UCm!nJmA@8vokz-9voVYr+eEp)2K(-CDTU9Ni(uC(|CVL_X*?@UIK@Nu;jp>J{ zy?eZVK0nYD?fPRQBrEVrUU*w{PTN8D)rRd^+|{0C+(%2IdBs3QhvmHQ8m78FFuRpf z40p@fD;Bwocf6kf?3hU0baLL*2qZ0*44lG@1MjVM0ajdnVb@U^BjK0cPED6T5Wfb` zyi4d~zjQ4{cspY*ycPR!Pjea^WZpWN#J>LVI-A-J%67_}!ot-^YT&+@u-?SN`kQX) zJ&8?y#RXS-(k!F5AZfW83I^~OEH8@c>uI-q8gH-19&g^=oS+7jX!uq;SxVbmkcUpM z-U4!H6<@0F!3lJ>xUpwi%JElJ1s>seP&v_x{22j9Zsy_(0eE}YN2(c_fo`u`GA+3euW&1{aDv~;YTx9aUrA80HrppgC`^ z^R`pZn@6*2bc|9%%w-!uld$fMFBB{@sr}_(Gsn^m##`Rl`Or0sHH4|>gB-;}E1s5B z;taKh%gPk|?QYRk{A*Pk_;F03HYuu%WtX~Nt2GV~^JO3j5*;3mci%|Rwoyn&EWHL2 z_TWe_R{aCLh&4*L0fm4=Ug1Q=(Dn74otk&gEZh9*X4^a(QWZU#1D2T~(T~(?TplV*|SPbSU@Ls0EC>@2c$9pf3%?Q&i+eBT~un4~XybD_n z=8rZq`(1>hnuwW?)`Q>0Z-}rSF`lfjCj=1zC)x*UjC;hdNWlw&EvFbw2aai;99;-F zeOgT|gwR4ALvHAw@9*0i|Bo4mgm0%VaGr!1h6B*OEA zFL(~eaEKG>;{s2SI0S^H*%ZSd&fPUwW+DG}F`YTwiw3kYuEInVd_lac_ zZlj?6u+bf*6<~slbjKhi1WH(iHiIlnONmPxThle8$6AEB4wCmWqNQgUNZE=9xA`rN zeGhnf>oKW3+(O3H$;lJTG|2g*0Zg$49dkV)^LJl;?d z0o0A3lDDgDp8b7hM3eFyy&vM?zb>Dm+NGz^Xzxjk`J09^&uQUG+0WyGKM7Lq`39ui zPhbP9zy13UX=fWhZJq_x{7vsZgmV9?zY`PF-+%DD@tvxKPJWQ%_Y3^uKvrBe_&>b> zUVi%hIev5CHwS)m;5P?;bKo}zeskbA2Yz$le>w1dWZYD?PWL}P_Fta#-~RUn9hx7^ zUo<&H|Mu|zbNbie-pxBN;f5LY*OgD5K6~w-Kit$8p6K3)IsHEut|@00oH|{aPOOdm z2eH2%aGy{4lw<6>%m0Ze{`(dM3Q7D#f%E@;6y?dUG*45R#;HF0=R5rMO6wGoB?XGk zoc|oyujlE|T>F%KkK@eW(ZqibQW-!Yserd<{+AIvF?e}u^4@i(|1;)ahkg{2n?-<` ze;Yv@)!9n!(B6M}kvk)XFHe~OU`X}unH7i;4Md>;Btt{;gkr+q(bt4t~2S zzulDo&;0)OF#p%+{tgcQ#pwPH4*tdHUi%%_{fnsl4$1#TRDOr#|Kgwh&Zhi}fA%|@ z^1o@T-)_qPPj1Tor7ZPOaB8=E)mVi_WmCkkNj4DPlmU&vK<{sxx?p_G*9eu9T>e+;LF z)epb`55hIx|3GSBya1rp`mA5CHIP=#QV5VCFI3UyRm2j$1FV1l@c9(sj(R^q&^yG+kjvTRbX8`+GSs(`cNoVD z&Q?>%P=tPg`QK0}NSk9tf`}AP#C}t47ImdxJqP`fo`0W*@bnWkq zdv*`@LfleA@UKsB#7z~lWW`nSIxx;)MeS!6$<*&vTU*->V-W5nzT@!Vr2S}5>DbtJ z?@<2(OIRq@PbO$FfDo*7I6M$Uf}8*!ELwOI!_OI#Fv5)IuWaVFl1(Wvx8u@!FcZoi zLZdp{>O1f%Hk=W%|IC8!P;wgh^P()gfON1e_pZ9FElnlzLg3}_*XQs(<*!fC>=Z?8 z8oytDT$`u1K{MIAOIc$t;OOY6htO33>sJsfufKYmQ20=(({;X)6xxum>PVN_g9(oJCOY?~pQi=s}?dNWqg z;3aR{&C;02mR04D*T%?#s*-K}A_V;-`{i%5PuWzi(Ix^S$7@M6ESZ ze+IIt9H2n2SL-W13AK%9z>LLK&`~>wk#d#=A$SQkr*(X<0I8_UcWx`I+E0*p^K)7b z%|zX~94fr>U`e}|dEFdRKe>Lc!BqeKBT~BRsm8X|TG;BhkB72o<;-)Lr_wi#*rJuF zG?IiZ_<5E;q(`={aVYzsC)0X6Kziyg^i(R^E-(~2ff~})mPfVCh*P~HJH2WZPpR4Q z$rIU%{KLWL4|1?zNlyr;?##iGM4okdHPO#}nA*9t_+gLXjF3=t#U^C7E5Ej0{EYLO z>CA)3WoEK{MYJ2t*}|r_T;ARI6f_7FRZy1|fW|e;GE|7bu9`Q#$Vyf4@BVDaI@7Qa zC`Xmw!-;Y7MI~tC@ePIJ2>|)RE7X9Ft!&)-trbUKoGg(8Uxcw0Dy}h83~WUy7Y!{F z1Ia&v1yIx`2lk~8?Ec@BovW6w-WIOweqZT2#3JFxICEx454nDRC zq3Q`)hUVWXq{^;n-O(bnove5ds2{QDTf#bmFpjQ-#`UA@fWJg}K!8$+@r_pC)SyUl zd+p2o*r1)D=t#YY?ONfO%A36jps&Q0W*4=+8Lb$VdcKK%)El zt}nFVHcKkH?~$JQ?hZ=H^C{Ke@b2wg(V(-`^;B&3D+|~gHFiY{v*i8GFOs%xKnEP6l78L(w7noBflXcxCPP}J@A*vV}Ov>)@lN2RQhrV;0g zYPt5RVVSvy^Hu{r#-&^J$W8CN_{DNm0swE@Hp)~-=UgJB`*=7*Y0Zr-k_eCD_S$Ti z2@8nM)=I5 zJ2my<$HU>|4;2-z@o0NR!V=T5__rSzv%ntYF z-LbT&rmZVLxZLYbqM{0Ck#G(QLj#W)a_#X&!^Pu0c~L?Z&tk0|d=mf!k0PPXA}-$$ zb+O{RlC>DGRwZ_p%JAjF&2Cy+e!nNe;_>f4kJJJ|5F?dyQ=W*A;NjfM&0TZ$#k-GK z=-P(N0W2bs-f}~HAkTwze_vko1kUiX`~yc_TK0>n3fF^t`OCBf)ivkuzvdBj^@pjW8T4 z|I3#ad~<3WV;c7L+Z{^r^A#gcre+t6x+rib?!yy#zLRzKldl($=07_1yydpK5Dh_V z-u(^jcxOO!eE!A7!4#>1v>Ad|VF@s(ej9sFR}kVyK>Y;P7cfPPstO_Xn*)~vi4z+a znmbu zv{RHJy^o+o%|a~88~53dz*koj!nbLFYjRzp^@|Vq?xWu6)z(P2)2#5m%suV`D@Utd z6QGj2DoKA!X6sJNJa-XFXVxB1WUgo_N6HkCvS>|&M54Nmq0yg5`i;mrss7AYXFdVJ1H)`XCNDYUF}3Ni7GcS}~!>~8B| zt(klPg;k}u_4l}uwe;(>#=|lad@>8A9ASP2bHue$P&9mZZ5V;s3g20uqRb~2yw^ey zi6_XQ{ls=bM2TF|5p@UUa`sNg5v#MyyYoNG!mlb)DaGE3*}FMkDV~V3(rMiflFLFi zz$LvR0rtRK88aTutpPX#9V+&23w7e+4 zOJ#Ye_b2jC0Xlr4eI&OjXxC3@WYb_nr&tF326;W)K!Wo&w;_@(ajHaFEndGWv1b^7 z7W8=poabG4M#!!Jp=G5=gM{1L;{`3WR&dJ+6N%}eV>^R<_lMJ^^JKR)+pDvyd8kAARGlKy<}^e-Uz~ zJC*F*q%mAByS@2h+N5BdeC^}1jOENuZ=QmPgRva{_WmaJ>5N3D;+%zH2x|ETfpx`5 znG?)Z44h8%HI3ef^60U(JTkujjk?B}G3p*_%tGG*fGI5Yk~eprW4mpfKKaVWYyQy- z)*RMy?MP|Ycw87wZw0lUanp%FLDD|R&~MLas;Lk_j_`!1gm92psTq1i(6_r|QrMz8 z>T)upUq$On!~z3Q2N3Kq0(#S{9n|+D_W@iD7OJ*z&ZQGAQT2S>)N=?Nkuy4RS>N=P zdovLN1C?l%`(l==%BN0hiH$1SeNa?(uAOyp~f*#=tyx)AL!P*(i3E*7l#oZmEQGCg*$9aD~0xK7P#zIeu&R%#T1Ydk!Ns+ZryBF=gWVM827utYq-G44E zb%h5~wsC#jd`8(d)X`hUjy5+m3hT1-j7O3z$pi4?(Dk;Qz+QHxcKbtbr#9JxuR><+ zeZ`;`@^XV-uz9O)p!GxV*M(P|Md|s{ZxxBIMXrs`YW0iotk?FI$;^di4++2Vv+6rJ?HGib7xQ(z)L`95qNe7=@F``wO zqrDsOJ}#sg4>on1k)*6$GiZ+Xh}S-4^8ctZ4{J@C8yW(8zT|J5WTrdY=)zm`GO z=t0`Tgu2B<&FcF!Cw~st()xCOxMECp3vLgCY~PNyo2`PVur{FFr{DG4y~6_U5zN8N z20!%W;6pxru`!}-0HE3*onh@~^qS>wTPjO|&0dmXHDQmBweBptjTS=CBN zQe3Qm?YOHx+8$l9kJiR-4iIW$1I}3G+PgKgyU%EDrqIB=^qWayH$wxWa)kS9GoME( zu{n~;oY1b5)pm(nFxENTNtPaqS$pHOHR?7jLZLG5FY_pD5rVxfr*I|92Dv-H$1XlugOYJ>4Y zztq{W+VXnzt-4`%nsoakXLGG+yceYz93TJ3;h2-}OVxGOAuyTK>lS%iZG7Y$-khlRspxe}&fB-B_wHMI7r4H0hixuU#lg&y;>R zn5zjG3U}wo({Q#89E`KlzIZpduU%ehu|Q(0{~fXD;%TKS zcYyT`dM0^&qtBoYucn3KVOIDv0WqIH7|`jT`1jXm|_Ijpm18O<+aQ%fhtNLmN`xY(Ygh=uOF9g&6ywUVwKi7T}r?XtTBdeA@ zw7GnCkt~W+MCmUq3#HKB?yhgnEe*Uqo4~hoc(~S{HH4)2n9Ls=>dK1UkE*i0;3lP; zy2h4Eea@3?zDF)RUOrCx-AqQp9?#EPDx7dtM>z(O!JNQ6>d?yc^TjIGa0 z`lM8mSddv>XLC}rNlAZhaz}z;3u)deiHp3hSVz>@a1aoiz|~>g(wi+k1b3o$>(khZ z&(R5BKG@IA@PlSbpjdx{V^F~mYk9TH+T0)by7A(u;GC*xh@r5!%O456C+%#2-`W_zbYcWEmH#x^{oq{E!B~tVB(9 z^p?smBS4c)>%seL(tD|{0M_DA`i89iR83=BIpU=TM$6{Ml=XFOJBtYd*FVXki+z7p z15DW{P`&|=QYJo1`T56}v+h(k=%O1rOLXHIR0MRNZJt%9i}9Uyn24GD^nY}j#=|7ITj7Hl=~y$cXi=($}k zAYmOP2_A2bg;!4|h{;OELt$FPZ~cW4yJ+c#0Eq@Fsm<_c3vgs&e12ZNSSz_&Z_gO1 zW(1jt33?@T!x1(;e56wsz#PjT-u1@)-owWNv1H$eU#r}@eIBC-NTbj#H!r~kjxJU> z8_HPC&(7Bxe@QSnwAeA&Alk^-84f?hUkmjMH@fE&@!&AP-_+mc&`!pm0g==F-uwEh zV}Hw=M0ih}dY+|in(zipFFcLVmf+kk)O@YWc#}z6Xnx;L$E9J?wODp&xac#?jgVE| zTO?Yi+mNP=^>-8)xcp|H>m-u7z7VyS8&x+%khxJ%>`a(@{EV&M*yIi8T_h9sD+?iV zp}t949bOhSjw#WjG*S@Q9*ICg>9hv64Y%dugBHcTHT))$oL6cPHI(Fts}D88rJ>rd zob`ukZQf3X8An;&GVJ!(nw_Fn*|G;4S;Ed1q&=C?<{T~WJtE`XvZiE(D;x`1t;;$s z^hd3pIMj~&*~&nC34~AqsBiZD1-Sgb62hk7w>8{6mSKjdK1>zR&18tU5t z!6jUP1~YL?J-`>69U|-7_g&THnfpqptO6u#Hu(bFAerw9nm;C^ie8|qR@1c1#LR;! zp-(=B^VqRXw>o~*`Pq4g&uXA^TpUftiYvYJa5&Xjt-fQ2%Q;j~_^U*u+;);StMxul zy`yE9Np+aciwtmD%nI(X{tZaoxURWwGmkAAr#^s263)$F zWBiJ;YakoY$TDQr0^5?!jV!*hSu}@7wXc6DObs@Vs`LTJnMowVy!jf+0`qMh75CK? z^K9vGg<*Aer>Yu{%|m-w5=b-kM)!`VKb?7zRWFr}E>3rxcPiai=wJ6(+z|_9@I~5o z2`QEow%PE{du_gd*UG?tSy-g4tUufwl;M!Pe>hTd(RSc;evJRNDE~Iu`KZo3v7)I> zWPtaiGGffx2;GZQC+!!(SZG*H?QPa>0=zo{aNGWT26^hu?TTWeRZyCuHRA4(TLr;XnAz^K3iT-bs^`ZQAmet zO|64bAbA`Dn;4Fe9D3wc=gcy+76`Am?s#pHG2cpWEx*(fh|j|Qog3r1e>7%Ccsum& z7xKA1;^lDwp+L%|+k9QqcjJpTskryzJ8u4%(uxl_yV%&cEK|eeShQ`SAh+s+He4sK zhXd@;;Ft)gVL9oZPp})KBj6V{s&3#L-21b+7W8uVre1g{`LyX~#+TdJ@kphv!s?v3 z3(F@LSTeZHX-qfDIjDi2-mlJ~TOwKk8!Q zrARTP5UA~Bn)tL!=@n?99$pRFq^QsWiLoRtYFaz_vJ`*Cuk6M(T7MzHlZcEHvr#*$ za<^Scpot1`JkP*$QVe7+s(lNCY1VVrkUV&3t=aP}2~`8=&v6!Vsf)_O*D#?hZO31^ zcL^t5ksB-g?y2rge+5vmN7TOL$Y zFqxi?H=gNhW`GfnniJM`ci^4~*6DqC*>YyC!$>;#A#(pjKBzW8mci0{QE(xO)V`Js z=rITf7umSyJbKL(62QG7S)rx8bk;{$umJNgMi_~# zDQp0BDRxL2#V|+}nt!~nH*|TLy+!qpnIQ4krVB*51y2hl(sYZE!0@knf%d&mtftzY zl}!9U4HL&%HiZDwa*GyFR9ShVq3zIRI(#XZ?t!jit#JYC1G72ch^Is!buC)6+7<$8MPMckf_gA zeKd?ckKa`9O&JsLAj2*_Z`(OAFz-f_0RnmK;{t7P=LWDQ`_|zIvM+@)7(LNcWMf zG$4=>#>UIW7hdazcWzUV%VDd|lBbnr<7dAbnATZ?Mg&6yN1ywTHWm`^V^!vgQ3}tg z=d{j#UM6kLY8MPFYpl&BzHa%C#-U{62~jefKdn`|wzBsV*3bsGL+9WJx`eH>sx=I{ zT6+YpGov#EvH)Na-?O!do|il&gVB60|i9Usad z&q^Wiji$51CinaO-KCOen<2RGYrK=#2>le|TBzOJ_H>H7x-aU$VNNebtp(%=wK@I} zUuOpyTn+SZJI4y*;Sd7$^xQ#uj_Aa-JYw(8JkDL>5Q?4c$uFs{8(q}AI34CqoT~K` z+x;-hS9in4+1V!j+d{FIv6(W{oYp7*gZ9+x*ZP)oEShWs%+qf7(FR7*Z0g-#a9gS^ zU8rk;6c_2ftkFdjwKi?ki^uPSe$*fj4juB7A$25)%%!?x$)~`R+LaMeAXh&tTioUh z;;54bvDLaa&K~~`CX5LUK_`B&>?rA;o?2ioAv`5?p7wgdG~=dC>AI#+@`YXSZ7Oc?}fL z^DDgmc!{#Eg>d@0&(GK?oM8mQYHnucb_%^d%9;LWt>(vq%;d^-Kyp`Xbc`DM>oYaF zp04wc<*d^ruZ(B#exTd2)e+5M11xexqgkc0{~Q(7p>jl4VqQHE%n$Ad&7z8Rook?)O~@sV(SxT8BPJ{F z@QKGO84${Xwby117^(p?Gq zjnje(GbF(tWMgDpufy*Bz_A<_E$U+0BZDBDUwuisMgVUd2}FeJu@qNG!Bd=z(RD`h z4dR%SRy6`Rl7GXkalB6TY;ARSbJ|B1%3gEKHE{~_-^ye&RPUF-Euz1TC@lyT@N>2h z-{-|&8))2yFPsA*j(zMbKW28Cvnb6#W-8(Bfb%&f-2C?T`feo-yamTw5RT}vS*ggQ zukK?1=><^A$_W*8=WnSld~D&`!$C?SGiJXwB~Is%{qYqG#kVf~1m=F)`59%WTMs_^ z6=ic|Hoc<)Heb@Bqj7dLi1oNS_{b_$bq@DkT*0nP&&z=p@P}y0zP(W5=%0nUE^jXu zIhOcN6eigLz3=oQ?E5>CmMGx8bUR0>5WxrWvFs^Sl66W!p9(W2)i3xh3KSVU{ zI`*1)67|iSpCHZeIw|GZ(^RZo)+sa5&Fji+C{R$^WXFYuA_VhuSr#5^Oe9!TAgj^_ zZGz$}-nec?T>!7NJMkmL6l0-v0hl_8*^Skxrn;%Tm)TUo*ypf!SE8NuJ0$USy=(e) zqjsG8Hh~Jrub7qKs1ywb`pX24obCiKGZXrzNRZ@=(MP`?&1d6*YbMjZ1Dtjr{gv{GlzIvo2(aV z4I*t%5+2q*UK0_% z@dDv@;C0ZCrgR$8OOrJYJl=);!g1VQwo&;7rT9k}KTDPTXsgq;UE=zXW|viQfsFb$ zd-qWKwwBdOZjod@TDe*|v7!J{iCOwh-}B~C}|LQN0K#k_@g`ZR>rJoWt@$pCK* z{CFq3J4>@*XP{5vhu4)>RV~M!VUpa^^MrWV&Bx4UiH2zatEHLlRjQm2K}K>!K+`iN z#u=@_7`q$A7mLmWYkpVgk1116!HGDIf#OSFMK~3#MqJjbkV$ApbkmcnTR|3rS_A3u zy{Sr*c#mVBDLN~`q7Uf#ohgtrX4-67Wo8rz2VR*#)TcKsDK>s%*wDfSvnQ0c`c0~M zx-OBj;fR1baQJ55*hUU>gpsAaULTDkiISA9it37N;VP)p#X!Fcdhumu*7s%Ch?ApQ&EJ_BzlKW z+5H}syyC$HuI{!q$~Lvx?dw<1*mW>`WC{O1I6oh^bN{t^G`kW%IGSDKz3njBWDa1p zkfU*RV6Me`Ng!CwFLG;ZE1q=yR`GI3AQNYJf7fXn~RVc#r3Fxn=sGl|2(9qCU zb_9y*Vtr^koF*iou{(5^Os$CWRtkQ|9jIM9xw7Fts97-F3N|Keu{l#-fY--E>9s|) zwGPOWMNj?Z)*)^gmF7Jkr96ys-&j&R@2{6G%XGSWm+k8y*n6PtLSdaULho$7_$pnG zlv8WH-?udz>TpUoJ**$Q`W+pDu(>p|vrFRnloT7D*DnIW&lMhR&U{vb?T2&h*qw;& z)*@U4G#jA>+z*mR4mIPBIOhvABj(lfZH1h_AnFxH^_$oD%`D>*XCdeaXTU@a z4Pu;I)!99sF2A>c)-JRF2i{^Jp!IC@(|zpb44Kn)G(cCRq@#P*{5(YSZ_^5w9S3jl zr#N?hu+l$q%I|3&UdFeZ?^?)JxqkSh9;8?i2;4j$DK}%9%W7UC**#QMqQeZSkAc>< zqtd+U%*%pi8JQf62)Bkme#h*O>_1P}fs!ZRv307p)vdK3{y;Yxi6M%h(_OJ8X8N#i zsZH8lsWr`!E#j?;=GgGY;vk9jOwnSw*H_My-usG=v6Phn_jv_Gs2y4-s#Y9@#^ZYk z@k$<7P%9g^msK47~;|S`qlxv>U)mY48&GBYU>GaX9^BX`cuxQ_e4A|)x)dU zg@ZXo@C>+ocPLFxEoe(ckHc&q#Xn1vV^k@*@ab%(B>-8`9HllxgKZ5YxX)!w z4hF!i+fplRZ1VCj17#IMIZ+#Rj)?xqL+vW|fFDl@eURgxoj#t76KE>U@B(u%=1Hs6TP^wrx4WZj&tuUzLt*725;b5Qqwa zkq5xfep}Gui*8(<9bK{ZITOv}0DfkO<&v2%z1=vgGtDzawI+BC{~Jy~ZVTC3v2GzMrvNS#ZJtL8nG!^}M!&mP}w*8#n6W#6*$TaG)1D-Q851s1+1ZW67W^t~irv-y#X zFJ%`Ax8*VKh4L!sAi`cdn z8!@VLGdNj8W~`%l#ixjV0p;9JFNyiqC8u6-Z_v)GrcsJdCw^@j(aZR@&F=}sqVG|` znpGMVsMPO0bAQN6RzbpH*2c^BpLrKf--Pl-Te^ZKuHwxWz|H=epYKp#y24EljFY6Duk6Q#M>zJAfyNT_)(f8UfzE@tb`0_$t)rt>K?^bbIyihhUY?k) z@Ij~KD@mm8>McwhNdxW?1Gc3xdqp0PQ$0zjJZnn>z! z1Fn&ln&16(zMPTbu{8F>JKF}P&vd@UicBovQC9A%8xW9^@6MkKlDSWoMy)lLhY}+j zb5?z1u5X|n(d{{_ouO0S_mq4fsGBeQ?}Sp_dN6;OWF78Tht*KH*GeoG5qdaM-Hyh#y9Fjxyb_?czj=R*m7R9KaIkk}(lKSTA~>>c ze>Ma5_>NkltU7291ZP#D5yf;TMfDV9SRQ~oZ?YJCH+1bXATzr;>Ai((ojtABHB&y6 zA-CP)J=E!l_MC0=T-}oil8hnFV|5IecP!j8(LZdt;hWXng^Kp(&Ec9b9oK~CDw!EC zEkl~+Fs`6-%QlmF*uh#Lc&R9^049|*Hg%)^3H=8CcqO=nj0WA zybw0Wl#}^MBoL>KASfM=DhZr?XFqy%aNzfNMD_C(-SV4v9LUGccnw*av30Q^wr*Pz zuU$^VTbMi9uj#lC-#BhHe)`s+BwjLG4#%^x-NpJxbNHhkbziu58*HG5i%OVAuZYcA zK)N8bq&u(S0&94Kt^Fvg-0g$7!<`we98qV1$2m_jiFwtc`qu#LUrJxTr!>X1P;+bL zgtSXM@=n?C2-~UYIN~=@S~6V&W385M&k&PT)T!Tok=xY2rs;jINNA49lSAk}od0xFM zyShdL19VH7a@U^jets!Xpl_imM#K9IQU1m-$g>-qg|;-7 z<#RnQ{}s`>UwlDdXWo-C`m;gaFxPtzx;E7MqiN}OHYjSv`g-B$3RnIe=gD&2g7vI+ zHIXrSO?x@UBm25anDuc4^WdwSjq7c^$~hI5;f?gI zGB6ON;H^vR?$FFvS%F_h=K4sJX+~uJu0P2jdH!{2X5(vQvp>Jy0x5BEO=Ye?bm)eS z=OjA?oN~a&&)`_Bc*g%!X%n_GO=sz1GdpSx7e z!RmA&hs@^5H4%xkKU`-dwnx&Nm4ePJNOHjBKjV(LHpf}{2};|Qp^~j-7bCVC&BXI8 zn(5YwcxF^@zr$=<4RGH7gsgUM^4HQ2%fgpKB-s;$Skrc?!KwZ=@3exMi$}Yl$ld+Q z^?TSnc>Z|X*wUJova8mH{={XX`^wdi#laNA8zrbB>I?q>URZ)LSa75 z$cePmf9ZM=)6Dc0e3NhU8Lc4YyTOk~hhA7l$yM!v!*HT!>u7T|(qX=^a##8~8V-${ z9*PLV1@&83+VPVqZU>kGHFn`)N*76rYB2w)8qhvspr5LtiF$7lne1@6gE|_|(0V&P z!DM`fC4bOa{4Or{-u~4ZcHMWa#=$^JM#e%Y(CA8B{!|4q0h~d|y+TGwGyF<+F$z>N zcGQkQcI6q?Ovz8rwav|RC>IV&XQIS>?nNv=rqQ{gbcz8sM!{|-MT?;$(zTCS^xx~u z&_vX&9feo8wiDi-^nQ(GmTC?83rmN(Zu*TT(e_)!&7ZvdE^}5mRfz(ESG1sbkZYe3 zxS5X&G&LvuU$kdyzWZu8U-XtyrTFs0BAqf$NB3<|n3Vd}tA@|ioL!b^t2antIdM5d8I zpY;_*LEm!3YFyjVSJt4ll!ZOpJC;rEHoaXmzY;e8ghg;?%B4>tCYPmf0>?s){F9w( z9SQ8Z8sZ&&pg8&0R!n72Z&yV zUxarr%FhO@?_!C>=HZQFuGe}CnAvjp;o9#0tMTTb&zT9EL<2PQSOzrVkGC1UK?T!F|?itv!8hw33elS}PXM0C(`_^UGJ z2RJ*rwHI{;3f5ru{5PEI#Q|?QieQDUJ2k%m zxbht@u})-MbdW(*x93QBZs zGiqOitc*7)?5aDEIobR!S)Z!~g_nu{Rf`$8LrJkSMT!2>HkOsjDawt=2o;&aT+Gw4 z>tgihtB}uZj!FZZgLDku7CpM+)im!j^6dG7XmO72>nJvbP0gWxnv7RocN}nPCNqly!NMly>xvl{HpuH%*=qMPTKnko&tOKoFd&w zsU}@r)weH=21*rT_;?-;)t@XrCSN&fpGXyNdSwbff5{PMZ4HA6Oa9V*>45LU$6F^+ zr;Pa)$XIV!`u?;O+fmCo*JorBm2M6pIPpif+FM!$@Ay z4+bGTRXd6f)Kfmmmql z)R?<4(&DJc*RhRQfs@OzN7sUU=)mw$d*)-9jL4v!x zCqM}99-QET;I4x+xI4k!0}M_G?(Q~72=4Cg1h+FJ|F_RR=W_a@=XtBES5>WA-R<&p zpD}Ye+|JVxGkO`3MIOZqVch5xZQh^J8*_1y;~C zz5xkMi}|LQNZR@6jwI@-+ieYRn{pA`lD3b&vv2o?VQ&Yb3tQ{15&lE=x3A~VS~4Da z1FvRrg<@veJ4ROFN0-m_ddxXTUp99@D!V5hMs}uGHHH~G$8>v7d# zxvuhq%GrlZ^dozZ^xIN9>%<6rW-!2EnHbuv4leWG1{M3%LF0zCYxpu*qX0giuJ2bp zBsoPH`g&RSB<53KHSTC)Cybwghu)sRHPN=$R3`NtbyeqZKNZ%T@UI7C`YN%ElmfB7 zN-ReyEAj0XtsKKk-vd6Rg}OOlhh+{#X0jtBv8Ozlatd3}pX)G~yV^GCoP}&OJbQY2Vru;2V#AO5w}+IfY}+ znvu;9rv=B8l7jsW2FboYjk-x=G6G-#sV(I2pzTtt;FACJicBi*Md;sL00sIEAdqaB zABp*QL7HGW;G4X6ZRb}OZ0&z&WtmZJ&P%@s6c>`O2#@4^mRlUTYA#q^Ygls5%(L;n z4tvk4SK;eCc9`eUS|cCr5GeIGAz-G@ZonunE$wB7&%9^G++jx8SSss~CfGH3`mlL+ zds)6e!mc~i@xIgHv~GLd|~ZpgUj|Tbn@uYPUWv9Og(n1)I}r4ty0*8 zzcsGqxe__h)GQqTj3#8!C79y78_N^hs>>gumG-a>^7$AU>+7?T|IfB+23A%jkjSe6 zAs)%Q@}}3B)Y}SJgx3T|#;P)RpUvMXUWqw!{=s?J#K+J|RR4Z}j!>~nR>+^djqUk< zCnhY7*GF#;trGHraQ|plM*CR^{|7R~*Pwxg2HXA zA9hExZh~^(7UE=2*lm)ggu_Dw2eqAvvu*4X;L**&$eB&6xzmXcMNNw7bkk|Zp00Kc z8GP=!Br&61$|7&)H2(Rk{@PGd%+T_!b(gvxq`VTQ$Jp+i)_a~7sf+bRRNLM1(2Kro z7@5|>$u2H*&6>4}Uef1Iq`lnM)n@vc1to?W$NJ3Qh%3zP6-}Px%tiKh(qu`T%q(l6S3jZJX#SZZZlLz2%vD5ipzKV}Wuwo0HoG?ynp)pMZsfwRESTfg z@&=3#=Yi-#ksZ>FdMj?5QEC~l6!jbbcbIEvrSEvhP_SP^HzxFbgm0SMo(+wsq7fUW zEV6hVKwjoT-^QLJL{m<|XqC>J1VyFV)%{wTU1H7hi||Y#MrTn@c83CqUpE6~doqgWD_wHhNH#=FMHtWO8r$6i5am=C; zBK2ggq$De>|L90{u?y0Z8RfSl2k+|ZzWkAF@_wj4MOvKGARnsvXW{=ZyMRzyRt`3y zD!DMB*?E85u$fj@izovM*b>hl(f=y*5c24pJ2_7;C?f1$-VGTVI~xU!Nwp0H7Q^rtLm^h{n)@DlT;d_*`eR&}GO+ zZt`lS$$SXcFN*WL>rT*wjSrfHt{L{~h=hGlR@$_MBKdn(B3mjHGI+*i&R%08MJ3H^ z(X;i5UGDX7BoM5HJaEXD%3DBx4BOA~FyuLc?;_PWEG%Rd41zyLUW3K+-{#ex3_D-s z>YqL^F%%ri%I4P%MlCAGcYOVozC_KjXM<{F_=j*Z`WOxJZz2j0n@g5f4S4XS#M@!D%|d@5O8X=q6`k;Ls?#JUWems+;+|5%qD7Dp@5q>r7Y-Ef*XS*XxF(>De9nyCtl zaVvwM)8bOrdh`4s;d`e*zENg(Fl}6Nj<3j^sLrJOk-1M_NYMFUO5NzAjiqHGK z%vai)@+RRq|ZygTJ^W5$@0odw;=#2Wt{jb?vCn0*=Gf;hLOLeJNnws?ZOc~4-`aXgIy7)u0?bkCe{VOSN4m7_4vbJmo<)KabKE?)NP*_(Fn$_h> zd;ylc?M74n(iPrF^-~onQYJeczPS3Vs-uNpirWxAQ-J4diftl02aPz7sA=nP`n&Be zYnRt?<@AZkFfIu1#C50Tl%E|i!pnX+%$<;N`J2DONr2Pi<&RZgS;O-C5JTFG&_nJ& z*)Td0-u6E(WkM=r%oBQw+%kt2%KLQC3X5kkN@g4j1ofwiN;gc=UKI918Gq)Iy!>8k ziZJevt^yUxb#HF0vsg)+e!!H5#qt-==ERVOSF?(Ayk`45a2)W-Q@e$1K97ytsIf$b zklSSMYXEkHsfc>+oKSvX9G23XU3^j<6AX5XOqvj4=`U+&OksHT3n~JwpcYsSfVOse zRIh?RO4ui=vyPa~T#1OIe&M^Krws^upvO}zxn5@PxN~IddDK)$dQsX_ij4O2i*DXl z&<}QxHwuXD=g|rlBi?);8+CvZZae-V=7sMHpAV+dH-yZ+)43L4ekEI>uG^CEanDrf z17`JHL5#Z1Qzt#?2XNUj3T4zW0^NFNk9pR+AlDz=C^0bpQEob39N^ICt}DUYaZzK; z2xi<~g%5Z@(wMrXS!)N6a8_C3>glx6x68OHInOp@9hiw*F1Qcq>na^kB@7# zZJSh?F}@3W;=!`fKtsMO7NM31`cC*sjrjb?4)TizdNHUI55y@E5(iI2k@;g?WRpEI zdC0E_>&wK_8zL??b0Q+sficVR=(23}Xy!4)ROM(u?h%`DG32a+U5|lfIi=&PxlAZK zd7BNoL?4z}rVLSRXkS3Z=} z9&7Ly_XUQfo))Yx-3_vM{GqFgB3riMKjP>j=Y|EvR2_5p#LH)~${u3eNk$z6^laii zEZeLKpOGEJ=Ynk>UOnXUIMdo--Lw2&ERv3h$HP+|YI6Ag4( z;3vLHEkJ2a;okwsGm9J?%~c5-U6^xkDH&isSeOUs)ud(9??K#G+@WLg7BB9=e-(?#8OhiIb>2egirRjzubo$+ZD4Ky!ND1#!8x6G_9|b4u z%3pJDlPG%wlWvWioq8d{c=7VdtqKPHA(P9dx$QBjB1okKwS|*=;c8abD(iU|;2gaw z&NHLHw=eEVHsU@s7TGC%V&2!Mt05@Z)|qiTtiM_^HRb57HC zcIioFUw)eXQ0X8i!iedcuhHSs#@MH6kx(etW!p?2KdAlJlfph(QO2T`&Jdj*+5G2P z2k_+G#(czSDDvvkveYP_xs{zdnazjBh3xUFRpI2eBg5GU8CZ|_IcJOTQMY-xym7&*|V zn*JUD{7d%_sNgRpVuAI8*cPmo{3-JMj8?0 zt**37SC2`L+iQ|Hp=j|W(edHvgXKoVM$PwzG}LeH<>8~cKuKdc0v2?oTYVD)4`;lX zrI7WvnXfhl*GxSh%}X6D)l$WM`Jcg%Uk!#wbaZ6-&tQ>nP|yi~EHSQgg`tQ)BPfbh z#eS8ad&kDtGXM)StaWyu_dYQ$PK({)K$0Pf_=05&_6L)K?7kT$pbuv_>9m@}Ge&}du3`-QkPWN2Bp_%D*vA@9xtiT`0^NKwjGq87{ z^zDR#U+97-M=5;tXgPRTZJ2F45#T?|hB+#;bFV=QVLs4iBSM6yajjz1Z^znHuY@(z zI`n5IXWnYG>oTyS83KOI3lOItC*TQHdgBv~_@Wc7^G))-k9)Q-I8lz8rP9e`r3sh= zzRnIrvmRs}RG|F_VW|Z$3>|O(w@YJ)p)ys(9^&APScMHV#tfWVAPi#d+nk?jGpM+k zdJ;Vnd?sBuWRepRjRI<4dHU9zn@Owy*;^2YPL(nN`6jQ?2ITLoddl(h+a>1;uO=|Zp;rgF!5T>v~=vEQ-7JpUa0wpl3%v|CSgbpJr#|{M`KC3{11&J3w|V~ z&CPr^a;^;rr`5s((q?R7@u`(} z#8JrX$Fgc)SXYxSZz+%KdgzANbjHeEJ_j8K; zLhG`0AyD%g-d`?UBDtq{>_bW zF-$kYOV+)zvQjP;x_IuU=}9~)c@a3jJ-o)(d8daLgn4GEKEjyH3f!RLc^ZE~E}jnJ zXFc@axQZ|x8|0*)Dd20t55bn_t&5B4uL9}x?5!CM*9Aqp1*f!b@p$h4(ktgCcs}-I z9$!Rm%XdB@VOxh$ax?GPBtGPng3l>E&+Kjfy|Lix=LBbu?-BRsRP@R_lvCJ;;Jj7A z(G_=0@MiV5D{V#i93{(6^k z#+%@%#bPfkh|jIXVY(r-5Ue_NX&<+&K4rEYZC6xIj`B?M@$+#M9=0T=;>9`YToUmS zUaIW44^M)LzLek#eAq3+?Y(wR23Ob#8>qbursWAUEk2rbDi2!__W+FWGMW&9z!ujl!F{|fC=AD6F4Fm%^nz& zv^mr=MsxCxr+pZQEE#wHhaoNF9wmuKu|`ieXI6~cMWk50siSYEGeNBBUugMM@*2PX zuUsmk4U$*5gFtiZD_&cBx(=seIwJ~1M7WH~}l zx83SJ_h@38F_Rj@nl*UCLW^~`*RWO=RCW+vO+@}#-nl;wqc|17cY*WCfVfdjyhVDG zI&y)2hy%oqFkCZAy?AR*Numi{VNtwGY2U!*0yHciXFZ>$94}Z^VEZyG?adwTS=(vl zcpq6(AUiH0eRiAeEVAc{^4H+u(nkFK^`v`qGqU}mgnWeIsig+DiRRtExP;;hJK@jy z*w5pc`WJXz#QcI`P!oB2+iax=@DQPI%^L^iprRy=2;#?6LkRh{dL#tD?0}V}{g^;x z@XpK4s5}N9*#x|Iqgre6_7Fp0G-9=spEMdnjWqL3T+ajaLpu%qEVlS7ycB{RMMvH% z9AWVY8%NKOihr~n;`XEw)v+JG;)~;0mDmO_PrDyj)G;_owZsDW^K>Ulyz<~O^YX9# zM=+i?_&AMVN4nz!BVurxRU&lyv)rg$On%qKc>OWHKGc6n*$TcaBafCB%KY6=&q>x< ziiyC-^o(hT7t29|Fe^G4iqjfW*@Q(OE>dd$6K&;oN0tZISxvM2k~4_y`|GEFlBJA& zfv%kb0sw!(b&dS*KMay{3EStW~1IGj+_lmn=)u5Cz9O- zyeJDw09a>7AGHxwCb;}0Q;HpST-YpvUKQ71p1SfZoI#42N8eP16sQDI-DHcD?Gcbx z%Xc#X#-pD*Gj$+807w#6R=ry z3R^`{PZ9g01mzRwOb~|p+T+hTC8$62;sWBIdP>-KjRi^4i`yw5zstYYrxch3&yY}T zbsJ-U&GN584Sr*TDBSxU?%Oc(z8O;_9=Emm+SphQeN)mK|22TcQ<;f2yMj)PmBZ$g zRUvy>N5`lKw@r21^^!3I?mK$O9QDC(TxYzt!z(rfa(p}otNIsyj^}MDj%_;5ZHr0; zm8SuR=PlKYWgw>-M|G8ea7k{O(Y!2#N2rReDqATV=D`-F5tnp-TGf z1I&eq_^PfG1XSfnmWWBXAO|DL-*e2qR_o{eS=pD@&*xds^IVXmB!NutGl=^eskwk% zc%wP$&KwG2HyIDTSrxdI^~b$3?)Q71>3s~_38GbYG{TTT{C;O3@Xapi^?q5XJoNX^ z|4d>|2-zglY+@kNzcw;h!~`y~MPeTWYJTAsiv3X~Lj67xgNSx(yssL|Joc-9OS2_^ z3mtLr`@4gEjLnlbWGt;lcgn{&bG|Q6ADq9xrfyPFYXISgre%j;WiAvWts92h8S2#M zp?ILd;ob~nc!e#olF?nZ5W=L05r*K@%X}+FjHVp~5ub=wY%NwA6-5c}X+A2n4tLa2 z*c2xoQ0tN)n4*y9(sWm;@30iTB_snWJK+kwO=CTpKdPYzU3lrF9FNL+t5p1qXRAj<{&clsAn>MQ09??v;s&UulV5^{0yV+mz zf&$ZA{?!s`o0)A2uclc;r2Lc~07mr%*=fYs<_##swTZIBWwGdlLx~c_+XC_U`=f>@ z5?M54>Z`vE$m%|;Z!LG893S_&b97Ycx_mcGpp{1(jC>6&tQd_a_rzz(O)j2YmstQ1 z=ou_BqDX2P-9#`#TwJun#(tIEv=7^^Wa!sYRw)~-fZicMI`R?h5kY5E27h5NjqB0Y zuGE+iLdXOnsncvNH*WV28^ zp6R@YS-+dO#>yN!R;laqD_}AR?H9ynB_J73B=Ij^r1pS3q zY{~4lJkprstIZ4b?}r&U-=kPaEf|y?S>-G;2i$P$2pIZoafn@Y$L%?L-3v0qSlrT|`a2eDVo+9uU^*oO)tkr`d^SgN=jD7Cn{nI(Iou=r3WZv9m&tf7JEXkYj zp&z}FYS&ALhuOjp@P2%JtyTow*eHq8SykX3F?eJpKCY#WZj|}UU5zc%W#xy9y?nOy zfYe82#=NV%dOz`pqUubu)$!Btn#3Z7u{vzjn5l8fp63j&39rM0gIeDnLZcoVsWCsW zJ@U$@xts=8+l48s!0f718y@shR69!?wcfWWD=Qiq-a4gua&x${Vm%l=1t@my(eT2T zkCkMo;)E#shKS%Gw6XSfpJ=_;k9qYlJaGzA4Mv{5`R%_0nRL)zb&p4yHP*Oj3i^p!DpQ}$_Yl?3gZ5sQo|GJi88)>#5#C@PQ#>3LEYhE2Or?p8wv!cHAd8x?x6{J78ovlIW(f?KF_QfY|0w;?!Fey=Y-vo@wzhE>q3dStHT9Dz{{Jvx6wv#fmR%yF zOn>XjN=AgE+DQeh)u6!1x8Hu%#_$LDh48f4HDRr!x9*l0$EKPKg zOkYjkNS2oM$Z}@??tp;PqxQmG7Gn2)$u+R>k_j=FbtSK(OM|TdmD+FZ&e-yxbi`U9p8Q$$kkmtY)f$nj61|^bJkfFX z+D5IwIXJHD6@;;TEf1gyzZL&C64b@1MF6wMIP%r)yV358{^CPULGQ^UWY!IZLgz!S zwJO1r=I#>)ohxpv=&|n>y`!upo6@(n2{NpX@7^2&b>7l%1u(P+hIftDkHvxe36_hJ z0S2mbrd0oYN9@&ixYp*E|N2h-TPPv|UC{Qd9brfx(zmzR^x)Q|vwZsq68AoROMu$v z4)tql?6ndU0xd~Q>3%6m_5mB7G1o3q*MXmDY`mi#NU3ZJuw=;o5)mhPu3bOm)8B`5 z_T2I^8jnyVQ_HZ=?3Uaj$M6Tneu+y_sijYJmj`XdXqLI_0ivDOQ_>8AyEX7dxg(tX zvrN$$cJs_P)`!-^)6(GL7Zj^pD-oP*FP0dt-9t#h`)5vkd9ZKIdnZP795_g+`gwrwQ)Yaql)=J z4Xb7-nUYITHCZB(L`CZA$ACjdy!T^{EGTY08~^0c6U<);XKEl1;!CoA1vUs)uiZJ{ ztai-t0sSClJT~tgO&2e2%6oDWgw!=3f9f5bVa-)<+$7FhX+%IgiA=^o0a~)XPYX8D z+xD-qk7u3Dgq^2PfY#wWNF-5)e_CV%pY^6aIllUBzIH&R{S zH}P9^b$dqgD>pqef;GK*-j(V!O>k{`vbj&t98Dc!nh87<`*e2Pf3P0n@$0Obrg+0$ z0D}>YZIR2LFK;mzU*N@!;MsTTK%)I@-W6ddHT)mvkoL+s{NLETMmXvQN0z={n*=l% z)F|Q@A3Gye<`8nyONzu0CJ(XJb~E>6RS+#@;|UpyQ&sx-Kuf1kI6er1_EI4j-5GkF zHzw-M?#>&sz71Z5rR<>6jJJuAP10n+Kq{6pHV;f0b01{Vp>4T~)Yf+;@489ls;K*a zX`JJI#Jb|^ro?roV{#5i7(zzm8&ddL{JD7SkfN+P90hR&4X^p@pGo|8W3nB_QAVI> zadYFV6LI5Gwt=&cKg~kO?lRjAuL9AF&mC`4BS0EY2ZcA&ImlmwuPeJ7in1TqFlswf z@1#@as>)+cbG?L7c8hJ|E{jR6+D@O5z7hV9qN>Gu#g>Bck@okmwLv8*#@?R!zHy>ORgKN{_RZ6ApqfJ@kz#dw+eZxj)9N#w##%kxRhM`N-7R%~M&hYL%Vpye10!$QV1zRue!S^=q#4e()5 zsYwRKe?Zd3X&vAm@y$((12+fymp=%mgd!(>y>c<($N_2dfY{VygZi@xFt=hFQAKU^X8 zcM_mVb^8%!c()HJU>U2OWqyhntHZz*|fn%ek={h@z4uo2ltHjE0>>pV`z|IoOEua(`v#R zj(O)3RUz3&c#wK_pT^+CuY}d$yPGXGJ0DuFgIvE!zxDuLeVZ}(l~J(ntZDg&B@EDB zNxbqgP$Whq;B39Z=*ZnWSw8f~s0{1m9ZM@oIYUR|I5_HZ1U5PeN0?BFe47;kgI_7k z4bvs$wQV;p#sVe~gndng4OUv)dV#Cn5v4Kw^ql>BrBF@7tm9js*94dxt64cY=X zQ`jM&X>y=fexP4Er%jZLJl>Ym1(T6m_0FlP7Ko{}ln-nHNaF#+sjNyI-7{snvs3xQ z|Fx;mUN;b^Y9#aD2GYeK;J1y8>b4D3b#pRf_W@OeV6q$-HM~Wo@WyyhKmo=FM}W&B z36N${zK^|w`N?MTsP0C{lfQm&bW|x%%ni}mSwrX-%4GXjz77qRk57uwRk>WF3i{F< zyU@U%?^gSkLnKhmkp{RD-Za%!y2FlR-frbXU3OU{8qvSL^~0Q}ETxUKS0G_2wI`7xgtAr=OcJ) zXZn3V=Y)gK`nX6LqR?HS)M4O^U)keq!qw02YA)b7ojcX1r@VCzJ1d52>qIw=1ONN^ z))3vbZ4c^aDrx{C@8GbXk!vU zNxA7vw=(t0d@*CTOP-!Ox=4&x2B63Yx@5|7(4XCaZ3e%rNO{>efD@aw~FdWi4kxNIt<3g zw114?OLzLGnxK@(fr$@<9BjIu3NVS_uH6 z@AKu6A55kNWkp+Jeh!g(Byt}f9@bU(9wT|~<%ApsVeh_Hh^8v<6{niToqH(9x(2JVVIo>8 zsS@MPKjv(QWK&dr1gmXI=X0ukU}{)<$2~zAVLiZe;k8NadQsGQ<=27CZ^wTQCvP;$ zI{06%Eb@RdQIf)wp9GQh8J!eO8%&4)K7A0#DAL|Xgv`Ga(Zv@MffAt zv6-0SA3V{cVTOUPZdj^tQTkn4#z+o?lu2<>1QCkw-kxLGTc<-;=bouy$%LcRABZ)R zGn1`(q`t;~NYx``8!}E>DQ8NHk#STh&-_kjwrM+-BWib@?*m?4Z}$& zG?Z2zlU<^NG)})rGmv~m7DSndxXo-i<>DK6S#b~F>$!6|ZHqXvvGXkiIbB%HF*-)* zUgx0CS&iOVJv`OT&b!<3tey<3525d_`zk-oEHB&G@x4SX@ZR+?d##XZ)hxX4q!inI zgDywj%b6{<TkSyMzRv7>Ahq?B!R0q#RH_f|Uc%C`@R60MW`M7i6f z=*8Rv`t`iK>CbyWDN399DI!w;fbiPyP_{ zy1BRBGDx?vSj=@SUCQ|sf)2vz^Y1PCT&RZ2^GlFpCKyQ3q?7oor^_^N*wt0$mQQ&< ze=|%S_9r@gq7HFg?i#XG0}10tW;RPl8=D+5zOq)I)vLjVt#h%3H)J_m#!eA<%U1e# z7s8umUdG28u5F8Ea#@ynQmzsa!MaD$C73`0=ehe#y{=6_BqqM_;>SlNAu~U}9&s8= zOW%IgRV|dMv;MEt7SC3F_2rUCr)&1VzI+_n zS*po?ydn`{x;L23C>gB2IYP%inhZM2LBd=_= z5^5xE{pwnT2vDL{QT1}WuXsFks$;=_(wGNwp3$7M;F|PUm)2-;X=B-HV&T}>qj*f` z&a`7lW^SnPxkB>%Z8cJ5F==ex|^MTtDfB5o&l^lMpr4#Lg+z4Z9(j5~#JMG7dlX5y$hl60_aGG&mq>)8lNQ|F z$xPmz?a$K{3#lip4pgVjhfe8@lM3eDu^y_EXU*g85M4&gB4Y#7l(8pQH7ne@_yz$s za?Y2Cw?_VAFK8H39tJ{SRxsC4V3N$>CQL5tJLJ!0e!iV?uLD++78C{`!*DaE%Uug#kV z%}zg5Djm)`MlfXbPW_j9X|?auPQwvEUEd~wRTRZg zeq?lMm9c9qbwuhTEPzngW3XT*Ov+aw%{RAg>s|kwew47zO)&#Ll58*WI!Mb9qIM~{ z&iN%|eGD`rah|$7I-}0iMRV^Tp$UY+9f7GJ>eYXtdWFnE4acL zfQ*=q=c@|qM~E17i8}+}cz!)?Z`>aWK|i#C!$%QxHVXoWPC^`8@=R_Zte zq)2P3QEp>M(o=mC3+e$8Pt>Axk6rr5(Scq?5-pWxjpFdpM$a=CMCeCl)Kc{ZnjPxkw$N1;Z3rKmHd zagfC+77p0RCfr+t9zryka~81s@-{=Qf1dmqnCE?bxLV2VyN%>IjvP~ecd6J(t=Mx~ znpwE}6LQV)@1~~%^{gVR@mz`PGCO^X6g`iycB@ytmfl@TMK4+zU~WjY#C@V+hu4+C z!q&JwdX2iH?zD_da4Yu>8QcpQd(^4JusgG|yM%H1m@YWNT|ILn13M?&xknbuH2+YC z;Q~jm{QXced&O2Igs*c=OkZ(mFz6z^F7tg--`{2$*cI3vU28m;A+f48ASir;!FqJO zY@$*q-@u7prkc4)j1ToLdRSQbQ&j+Z44fY{K{A^prHlkouVg;!7IwsaqP=9*L9bTI ze}R(|^Hnk#?Rm}jzrdMYR;F9QzDt~Q`x6lrqd|%Yj2MNwP|6`%OC4xI$Pjf<;$=EK z0QMKz7hrRtL+=4BH#@tG3f{~ahLBq7&YAwHMc(s7A8GV3*jIi)%8-SK{}PTHR-awQ zB~HQRMG8la3)XE7*6-EBZoe>anNUtUrGJwq4saCjn{jRo$llU>8@-h%BYHB+(rM#`h&Y(s%R+VGS7r%+b)t$_{!qPmq-K+L~ZsV&058XaU2uu|!6 zLl=M=VO;q#^8zP(xs;;R33gBktYU(4qCsilj4-sCEf5I|Tnn^^O^5dlVeVN`*0%QY zi3YIHdtX*R9{VKKIJzo9k++Y_7#(|*Jw^ori@JoERk-h)=OYr_149dgAUF&MMIr~oklqM zRu&iUey;hhed*HwMep-?UQxXnEvTljGB#;>o=C4!N~&KHvw2Q)J@DcS(za{s5qg*(UAVwyIJ(`8>Hn^3H$@%b4cQUUR-eGNKvD9= zG2wvz=jsX+wnxl_kQOZjEJH$(h<1bbOGv!P+t#z@B`@zg_`%K#eG-;C74e2mTQ`hfwk$9%@YV_G*rk-Of~H?xNM{^XBabF1lTvbJqqjQ0^XpEW4D#QSDh zF`7XrEf1vJ=iY}Imw-foNq+yZ_mfI;KrIz)oVo-jQq95uUZGgQqAgJ#o7|&@cMp$_ z_uXMfuY^Gtkgo2y?HfHetK(jPX!ec+xIuPaH73F0Yhe_f4Q4_wccYWI7fXzH=`Zcz z^5%mOm+hq0abD|D_Yp_uA5+ptKex(z{QZ9mO`i)U2PSu$lI`(F6Yq+sU@+eSS)-(* zZH5U?gpRr*(ABy=*FmFg7N83lKnR%bx&;VJCI!`cYmfnv3<* zWjJeXZM$B;j^SCg79{rg<5p8KD!w3AIx8eekW-B&!E2l}jUc2flzFZ#Sw=uTIi}x^ zGm*=QL4T}N*5^Dg_aAi)_sYCQ*^lV`W8RiA*NpAF1VSahWm#=1eBc%}Y&LL>9mZTq zWm5R4Y!SF^>oFIaqv@dN>iug`{ze#! z&5rBBxDMwx{S+G~N}D7TceN{@8e)^%5AW_%k@*oNB)mrS{V+BWQ*7R41Y6_zdO8${*xc~v9TOIz z?H<=?1|=5X0IAy$?>CK5b~rK*lmxk6T1m0%oLavC=!*>pG9V?RE>2*y2oL2=|43zE zC4<#^_uK2cQQvW)tNqc1bKff&NC{%RJ*Dh>9$`uG07y46UXS< zWta4_{GQ!p86G~^nO%sIP>a<6m`<0@pg!YMG#4i2O9 zb@kFHemS5|Eg`hKxX(*x`1d(h^6_Qr#l&)ZB)sa&Uzr6P2}pxtG2CG2uf4}|5m3e& zbJj(ABnZwF!~(%+AZ;fh)(8QRZnX%|7)6apIG%zTISx^N?To@Mg?67Loz*)yGR3N* zO(BNz?c$3|E25prLocoHJDA6#n$ddA8rMrwb+^a&F4|R32Q>G~ZaT4N6fqI3s3M{w zkpboHNnU>vDr4iwg_1Q1{lAF-;0NgvKakVzjg=$zI*}>sf$3XA1Kp@eqNcyI1LpZx z+~efGbF4t#)O)U2(YsyR73$ZEaIBu&fm9LK;(q)EZW25fp*_^!&tKS}y~1E!?JPw$ z7ILl-HOFqxlfGfPV>rrtBw{EyK6_#wwcCn>^2w|v!z?q##p-E826oR>kRulyToTxf-ada%{e4C zD@NF~^1TWXye@|}4TP?C0=V76zIO~R_k@7W^Fqz*=NsS79}4NV#GYImOAI7q(aT0o zD19Rw?csWM6Gkt~LwpL`|~YejgKI9F2Cbsk08G(TUS zp$rAhdaHb<`loWwlLmb92jzzWDWOuweg&azr&hsp@+-5Rof#7*$VvxOL+cba3Vl6H zBAL_Rzg;K#dZS^#4#=(IKR6_tS-akg|9VeR{VIL+vEh}2s&^YyZuv7Id%PxwR!^2O zDi)HY?;}LOSrcRXhf)lf=&E(pu8fQ%Xg;8p6JJ4>qhgl*GZ9#SReCLBPmYeN0p#wfF3^k`jj>ZN=RnAzkGh2Q z#^#Hb)f!~x*0G%|9{rGcbZfF&sN=@&a)vcba2ofRXU=mMoyn?6?_hR>kCZx5aAvxv z0=l+RFsPIMwmQ|FbrN%%zbc4i&&X60YJ$~l=J1{G5o`i>L`&6>0xMrNPjDO1tnCQb z*P;Sa8y>57JTvn@2PGV_zW|$7u6N3Qc|tx3g%%caic7z2yxlFaC7G!FP)7*~H9|#G zuO1D`%~)`&8QX%lbf{Ym!lT~*ihsZSahH!17m#@lA0X5!PV;&~;}zi#DPsZucj|8j z>lPeOCYHbs2GR7L&^0ehj}v}yN<90B3%g4BRRiN2X76S-)mUeaX741($;ShZY-Yi{V!Qm zb;WLrEmTxHtY-K}if^{flY@+0iTHt|RR+mm9lEP#4LnKmE4bu*M4P2Kro^-0F&4h> zD(TtkBC4XsQ-Q6-DSG&mQ~d5jCIU?EKjhO`R-QM7eT)EY_ndCTTW+3{cXpMKOyrmI zwjgA$WzHa2cN44NFIA11ZG7x-AxUljCaeUEW0F;xqDi^Pz+c;8GhT2b1dFK8(%gXt6ejgE1COen&|UnpFpe+~ADBg!Os5x#Z9 z`x08O1Rv=1xe6x~{2WCRmc}~s%G7z^2#-a3?#mvhy*x*??Y5;XtV8PR-HvlqHnw-M zuSnyjMb()CJVWUi8BsXuCMv?0Fn901S1Kdi`#y6mwP$E7c)?kqRTF$MJ4;d?qH~7h z#Z9lK&S;_|+jMDpkw19Pon2T+OThkDWC*btC9LI3jctw|K>csM36Yc{Lh(uLb;k3; z^yF6ahN~^m=*@%@KiWcns|l+1`N;#)y#)*&lg{E;90XEBBJy7Ga^FeOg`yDg_CwV&9tqgZQHhuv~4?W+qP{pZQHhX+dMnD@8@~H zx4t^3){m~*|9aK6<{Wd*F~*EqZSQzkSOs79_SQr=)avo+|Go0t^+#T0Anx;y>3tL$ z$F3;T%DHMafbnvOkGpW+MK9CPftn877Q?pzy&t+qCX<}^J9d~hkHivlSAIH5w0d=v z2m(f(XnVKUcQ{vTYx6-2HBOSUaiA8k_6Iy=UCFHRO55fgb$U$0*hF7sIk4Q)dcIBs zJ}wy}{NC8mPB^6ciwOj?8xV$ofu3n&dG((_kMTJb)59a`0NfG=NKIKl z@PUAag~P-(z?WeaGRPL4$Y7*5GHBZ(CL%_biysN|>SN4(gc4&l``$R-vD^ys-ulrg zv0;s6qx$+INz)9d2$*%10bL5HZ`s@=Z%%2eiatcK)Qej_veBj+90Xg2fBcoKKj@#K zH=`+9PYl&ZGcB*|hK&=IY=!~dHREwsIO10$&XJ+kGODV0Vtv_MBgTa-g10H|Ghhei zG*lzwcZ9s}s^xb4<37UL1GHJoj3m>gz2h2%Mn&PDyP^SW?wlpt{1Sw?_-W$CEQ3GO z$GJd!BSf2cV}vZAz?X9@h;m1jVLb-L?q|dD>fZH7iyd%^n#8#NYzK0o02$b>Pfcqc{3r89r8q)nt4SfR7 z67#dSI6YMOC?>H#ENKNumMHLR^T`aq%)|TWn_%6wNMo_KZoAA4D+i65zP+`$y{UA2 zsoo8RgnCJXa835CpT=S1L|BI}TAcXI?lL8s0UpRbmGUD9akhC%Dn8DSY>;Jf!`S(*m=i;_AXpeOWS0Bix+DiywoB! zDKSD|C6XpyDz$Oh`8SHw;K$N$)I>t24FgQjN>Qj3Q{4lOQs&=kJ?c6BCl_4qy9?)=i*~C?c~gY zwXbaps)(aui^aF-_kh?=rqpS~tZ+}q{(xVCp^1a z#ws6k)Gjv~mR)c64PQ5Ig#mi@maZ~Dv|U6GB7y6|d!yMVvMft&boZ-8Cnm6xYi|`g z0<#{etTKqvtNB3DK`wq)eH8g*w+mF${is>5wTtsWHRQkJlOKP^ClWCBPXCI)gLqjb zj_PedMGD1AekH=jT|p7R;vn1}*6j7~66REq%I*b*VKG~6HRseh0o*jIx1>+I(`wk+ zIZg;=Sl6RKk3ZfOvTA@39?p8+y?_SNMpGuNT;C6B*clL zSZK@MT5&kRsJBYU81dzx2H!4ogr2fJ{8JhA7OEwFp;B={NfmB>7i)npH5^9H2E?M) zo^02mj|YJehhf7EE0sQ`B_S#i08Kqn=9|UKJxiBq7559a`xPBc zZQTUBK(i3X-^5ph3*rH~4WtU*1l$DwBaYc>!>Gd_rNfITsgaas+H??F$BXn# zaV|Fx$z~e9A2LQ?{$d_#&I|9)P`zP!*c}rxaDmYM4~1kwpxYbMs>AEw4kR#RjPzfS`S;8XZov%c|5Fky~7?M>eTRWqbz~YiCc9gsXWE6>d|9|BT2YO|cjPX=#xIEiFTPv1zqNUEghqB- zaCdiQecdHQq@>AR(z$r}pRgvF9q=b1P%$no&;OGU&_FoCafp=Y4Wglq;Q&b5+e^`0 z@|0dn1kk33}2^KG(>zldjZJ30%`{CsDStcMLo7bu-O7wx}r~jPD%-%>8M;YTuJOS4i!N{B-OD?i8{;QdkL@-<>sI zC~w%*a_?A3S*|mZqx?^-3nP$y-#4w%Hg^6Of@IQyFjC_7VW`$q_3F|orobSju-5wa zNfZh1F5VLofP$O$CG_N$kGk@aQ3S&2Tusy^uB9;69}G@>Tqkwa&Pwx0L_v3=8EZK@ zJ8SSvC~XswwHIdrS;e0L9^WTkhFS3GyAyx+5AKKVLc{bCP+u39H2aK3v=9=pT_?i3 z4qsP)&2Rl3MG37avLCP|W=2$U)|>04=>xC7zigB!7xPur@aaoa-M^GzX1alI7Z55#1wDC8AZuJqxU`E|GbE zm4V?+FJR`EjAzIjN)5bj@DFm6aG17xAYQVr;JwC(NGk2Xs>=;q93`nvkLnS(Y}^d` zlPM6Zi*V>*Pe(G`T@uFpXEsk2#CzUt)+_=o&14onT=&CO;y(AYnAg?bS455?#7yEyiK3WOatKS73#D!-&h#GjQ-M za7MQ~p7sI&#d{52cfa6ry6Q*co19qiTkN+VRN`?sjp0KVTk#1>CkGTT1j125 zu=y>yXnrrb(C%)N*fJ(ebldzYRnUWg5k-L$fc?}huve@Dd)2xO0p8n@zzCGd+}f3~ zRpKu=#znc}aO(_EUp{cpL6p|h5gu2aC<`d|$t|w_zU;`h>TI~okCUl^1ai-;yB=m11Rp<|DdojS=vFs}+s91fk}h zcp(KGuoJLdn@rmnRd8uugx#b|m-+B@?$3`tf%euG{AsLP`xBYdSCVN*Km2{t-+X}6 z0Oy|#|07}JhMJnDjpeZTsaU@o>jyRvj7742O`0fza>e5QF-2ZtkwjX=CuknvDx7cw zot{r*W_?lZdULS+e0K^cOtbP@%Xpfv^0F+-bV`&;r`@1#I}>2G-4RT8Z1^Hw;{|BG z{=HHI8WsiH`%7<&P5K>`uU?HK1fg!4aVZ-9G|aP#Uu>q^eCoLO%Xw%W+4dvTEpID% z66M{MpO)fUe?bP~oEWoqUr_0iq^vovMns&k-a4UY55mnf7>^Q@28{YEE%I$-Ge6E2NrgPCL9%-Em! z`9TVjUgYWdrVbxdz5p%lQHf&Ma1{VXRd!tiH*YGSUde>Zc%nO%$_MVKR{o|bAR1X$ z-5gej#$o6xOyE3jho<~L*#=+bV*%^t?4Mr2YWT0L8?c++k9--3FA*8J83RGsRQ<99 zG#)hcss>;vMaWNtLt^|H{mD7A!iPD$$z4MXV;hz%85s`ij*F``gPqN+g8TyZl3ZD@ zdquXt9v$ylyl)`V7>u1cI~ORlICsq$@UR52+bZ|k^*E-DoV61iE_LH4UdiiJD##7Qj*t_N>~>Wz_wjZRwG&w#;?r~~Ti${?^YDxDc;I?it%Kv3 zd4DZFT$S=}o~CYn)VkmDGe0}(a`DOVxc0ze7?{)uGy7Yhk)VC9Y|*!!An-2Ej^p8H z>Z(aJIT%cp)$k_Y5RF$gXMcjOs-iO7->ocD{e)bM)z*V^cBWHmwgQy`NQWxV}UAaf8-t#gS(1af$+U1#& zrB$p(gRIT29upd_-*Hq=X;Zq2g|F~K3QHGy5 ztuLe&78aWOLN+HcBpxADHLA=Wkkmx5atvF}DvtW-1KjZ$?-I;>0gt|feXQ?mxux%w zTbeyHJ0&I+nQ%gC_pOBd3(i{qzULZ9$FEo|!yi8(fjd2bQdahsUHx<18w=oodf@YO z8sMa?Y5)snvJu$y)<@H{=>()l!F+B9c1J&8K-TtG7FQ3g2`QD7Pd_bEPO+zB%0wC4Rd^l|bPVYqjA`{}{ihSSAi(=U zW8Go(Uj)?%?lYYyd`t;+k;NT=9LI2$ZJzZF4B-xTbYZizB(gzZFVA$tpijsXw9Hjf zg!!Jz3a$HuzX{}TYd`kpIZizvwO@m89EI0(_wS(~` z9AQqJhZPSr$tW<>V;)54!3tvf8^ogxGNaxR?L*LT%Il!-90*G5^-7K@_vK`;d+}T^ z7*22FokkLvn@vn172R+mGqQ*dr1^2LCq(>KR#pN$MIiOCXT}&7BH0em+iu_Rs)3E^ zRjiFm;dC9R6kC`-lZ@`Tuuq4Kc(Zx4!2JtR`tO73M%t!*qz+D%3>s?YinyFYwnzGj zre2smDh!pn9*UklzTl73y509n`c{5F6#lYHfsFZpKMAj7dt&0Bxs)fuR^E>cS}ROA z4BWF(5s0TYDA?5N7bpNai*?`V3K1Sv&Xqu1K&wHwi?F;El25k?5bUAm`cQ!~yN)pY z>^n1MS+gO6yX)wIV?W|8IUU@k#(SL#0ssevb+a-&zn!u;78fofLZS;m138)v@N3|} zn(EeSb|P^%5vK%=C6+`RvBAYrQ|+Yj1&EL_%R=<-s?d!c*{Yfd)C-n^p`?!a{q0zA z{ov`|mw3+e<$tB$_`~=YO9blW%oD{Un&~Q5A#p}E+DDR;qIs6+Q+`q3pNTj;xA;vk$btauvZb4B|XhyI-g1(l5%gZIsGlo|k63`_6$}{|66Lzaeg= z_S|*2-;!2jW!TW0Tj_seh;-FfPC0;xzbi;lwW}$#ArSe=m3b-ZCbskJpS&MVHjq&Z zU!kVI>$LRQ7(?2@r~NOE02)ML|Cu@c53m40`6K@Jb8y97I2fN2gHNPP-C^Y8#%2m_ zQl@$`&7K>1e8koIxgzDysHMCANHOV6)$s-*>_#+#^*qpb1Kk1TAJsS1H5G8wMyQ{B!Z71Dgep>8i>b=S4M zPksm4eoQAmc+n_gB{0$>@d#~KsjZO87h#KNOS0eh#L8TT2Ka)|B-%P@A338A=Vqmg zg4bVMyNtt~-0vq2^cUJ)p$_*-kWME57rpP#G=ywEI{O1jjT$|a zv9Is!JX_Buk5lL|SKDx?8037$a_B771REoA{A$p@hjP->iHA;-?O9WMyO)2l$VHsv z8qZc^9sa4ffWnx7`^gWFfOW@*b*I-aH;$?t&iX=HoJB&<#c04?7p2$o)_Ia(NkQQp zRit4z+Lr`6HNOokZAD^M#5Kz0Cooq0#eCZ4HHG=5_cB5Yy5^96^hJNZK2IhO z71d8U^6-y#OE3(X5f@~~)IJH0WNcz0&-;FQ<>4seYk4@q49*mQDNc$`OBl3*b*ChW z!q9f=GbSpcQJ)9@E<7@t5|GSs;gr=DWYVr^LDs4UabxW#0+?C2c-`j|@p ztIYTeMhof6jlcO=)DoN#O%%GDrI2t_Go16mS{7Z73SNP0Xlnw+a=^79Mf-9BqW%5( z<;7zDWd4slebsgl2a`WV{2wN(3E7|2C;vMm;~$;*3jy!pbgd!9-jIojBMH0|{1f3C zh^dQ$N&FNhUo;Ywrk(p#EkOn6b0PiAE2QfpO8X8&2f^v>tW47=%}2V{eZQt1a`YUNx7~uYX|^eUgzNE z0f9vhA?P#Vqsd7FvWf3hOt37&oaGq>Bn=mWTKW#*2n`tTqafT~!n}0O?f@q1OhD7e zJF}Z&ke1^-{8_n5*8s6}x=B%nTP=>)qwaVqP|N{T)P4OdJv+Xnyb4q`HSB7#j|+^i z6PSM!Z-(KtQ1@eIe7x?t%Y2>kV^WuZ`XWlI^#!G}mf4PKIk>UB$Ey16VN=E2yFWGL ze-QHjQgh*f)EQ6KrdhRux7oaI*{oo0UuOI8Bl)wmF#XIZrYw)=r_tC z4uZ$Qvr*aV-5^xVl84I{-Cj)|3U+6nzLZJqWm8S_uaD~z2J-QOQbKz(ZT2TxR6Eg{;z4TM=XGP1J*2vf~^W0`=lp;gBn6D(I{m$zY#@Io=VIYu%&J zht7zO18X&9?I)r?OXCkoOXnjk9`UO<(oJlyT}n3cL|lVbR4b@b)-*0ylTGJ`)xTII zI)12$juC~Ch~`ESDgbL`D+Vyhv!W>>_2btzILXCzf)*f^=3FXhZ6VZ7OvVZa*B-gE zPRtd~zyhlMpmOG1@|d`|u&%Ud-8bDg!!C@vwBB1Lx6jHMEP+dVhA-O}w_|2aBgFqY z3~7jbiVHn0bP|KLle%XbT8vR;z*pCJZDfvWs%&qxl7%9`R!VwJ8(46 zXmZMUw1fLv`=>Vqbj+&#qsTA};^z1xZx88iB$(P*umZ~Kf@ZTtf-HiVo5YSRa`(3@ zp*DHI#mBe4910F8MPh#ojZjPxkJpD-9O84PjqwI^f(-T)YzxQY`4~P`9!FNlIDfVC z3kwy`guXkp%-apO+is7wQ<9bM-^#@`E8(B+X@5v2Lm1XU7pr>K*^|o%O7+N@%T@O_ zQzR~kVwaUc$~<8hU0-EDkL0)>V zy*FEd#Z^Cx`RSRRFoaxj{b&6-VQN#_N|>kH*)@ z`eOy>MxEDd6{qR`i|IU(!_M~xr|!>_;VXa3Y62*m^Gk3XkJa*tZ++>QUvibXD!-&x zrOwe&2w~&~oPIt^F1lla#GnSFBt88+`|~?PRj?VOv}2}Ee~Xq5c{<0%ztFA1|Bq70 zw7#zD?++FtV5l3eB)E$w(?ss)q-23m)0L46qDEEuZ_}K5IY(ehJ64^<~4x zK}Loj-K*xsea=mfN|{2~>7?AGlXdtdU&VA=>(o$i1YJ2*@vJ&fiHytANxGcvTKR+< zSr%P(icmyuHH7TaPSa33HM{2gY`o^fbd9`#}r%mrwYg-v0F)XZw^#2UNZgM zXs8w5*JdIKZIANE0m^qpZ8RkXRoRm~UaDbAf%O>T*Qtd<_8~!>25wfouLuT<*BoHX zUYnuDHtQ>}_k&DdFMEBKSt$SvE`HlM_bJ}Zn$n93GHhR>uyJ><|2Dg!KX<0D!fyB9HEs)DhhWdDox&Ux7kU&78zvZHB+&J_;p?u+>TpG$Su^%)3J2;*BvMZ2 zbpshNrFBOCkah*L`w_Z7?*HRs0F+CygS>gZ(F-22#x8>p2~41|!W%?(Ul*sd=@!qG zBGOz>2z(I>@vA3R8(FL*jT#&h-howPbXaLt;BB+n;s^CXJKiaB7s4pLu2+m5$cEVw zW)Q@6bFbgA=@Tm~F4#CSF9<`$psqpYz77UDpf%AyChKyj70Nz;Tjp1Cy(KXTUe>V= zLMl;dorjTOIU$G5)ttky%uU^iRvhDWd4$TMRaQT8%P4@A8mDyR{x)HTwcm|qja$Al za34SVqIi9V4NBMmZmEb#=Nx-Unbmf#Z2-u~1K1~h_<3F~Q|mc3WViZXS1J|=aV zEjEC)EiaVhvTH3+n>;h%5Y?-}CGzB|NIA_&aVnJ3oxV!fMU)t;%AhwMsv6LM6;Pa{ zkZ8|AuXn|tX+N0Ji9{kjRgnA7(+A?+?|<;_uP( zh(;0VqO{@$Y1u0_4%OfeON<;OqA(1i3*VM;QB@)R(n)O8=k*sa0NH+JD|Eb|Vt~s= z!B#QX3Nz>YUkKyD&1P8=wcHRt?L`_dSirInRY$MOqPU*tVThPHh+? zFuO%97MJUlnoV4nnPEJc0l79bjC{g-aeV2iKOAc_xLNlN;uPrInkO6qZTDY|bMT*M zZ!=Xl`p>h62WxF@^$3bv1Fz?|*_NYUCT;Go#GdabMBS>|CJa&bkL$f*)H=!!9vtb_ zCD4tRi!$-Bs5lbx|IY1@-!r`1R`r_2dSLoX$bHQu*WX{0s&1S)E|fx@?7>Z|52KyO zD!FCih-9>1hRFH|r8gX0m0)gm8n-k;;%tai1~QJroK(sw7DZJDOfRJV8HdjFE-dwg zers1>5W0a`2ob1JZ5%{Y(zo%+`LJaV5!E|2~`3;jego=kbTY2ZyYrkDN7X$ z8MPZ39GL|YlERhX3kYnrS8AXyY45wsDM;ICX1~+iS8bDz+bErSpq^&gv?$B)qWMx! zqur(E1Zb2x6$%xO!vWD&+4%EeM|4#;$o5zE0rG(+Ia120&jIy;Q3JL8gfKqMP3#q; z5Sv1SM#GUQDW$!t{l?uTY%VY*p7|8OF9A2s&bMghaPR($#1;hbC%er$=qCS}9ad{0 zM3)fk`rn6W5L&G^=Vh1XqmxW_AedYE0mN}efcP>dWLFH01M{;Gy=^_pE zEPg7KwA2&O!eM>vvXpE((R5ktaN6<-TA~}o*6(^6w?3n?_6!0v9O43z;r*CTSQ? zp{I0`s(~zmy_fx=!DJwAhiPJD!+*N&6%l`$5P+-6jlb3Mr9UD%dN;)cV&mmc9dzPb zl&KWnB4NW_WXy<68-pRUl6mppBrwT=*$ltn$o*D=Z0l@6Yiicp6-iU7p$HS- zYEYDvu*^%BnVn5{fn8Xwvjq+7x*%NjvZphK zcDk?>mCxsN&LOae%3LOqe%%E^XougCe#{-ZPBAU^h-js!twe-z;t~AUJY?k${Aw8F zanJe-zmSBxoj1aNb#=qSu6$=?C0_+|k~AEl?Lhe*m@tZvE8EhGo?Ax^b{ATDv-5-CBzXj&7hJHITDqS!84}AT0WnLd2Y@FNfG zeo3HDQDM)lR+5=D6~&BFHiV1ap_TpOqFAItPb$9*;U$7lOh2YNCx6zVGVR{u=oLI zr_*WCq8%w$5tnZByvu2jTZ5`>AJ%LV(E}zhW-82GK_q!1kjoekcGvP-UFkBgyOC0( zQPTV!*L?zo>c86Tus@;K`K<2npU{g0!l-Pb=@4;}kKu;^ho^FCoDTd(G3=(2F=7kp z6yUE8*x1VO2RN%fpV!iDh7chJE5SwSVWTLBog~BMjehBGQHCL6$rWD@FEtPotXeYi z#003|8KmX-zmc(%_0~1=`yEm4Dm{-k7F$gXp?S)>LJ3PAd@FSm_lz`&=jPX35!x`R zTU1C_HA^%%>&+u|e${l|A6gLyHp|~^$z(9Oq2uL48aO&RExBQ1dya+a=%KYgjo;6fc~>&j zdcEm%a)o9WL~mc*oDzXShGJE<@xx+?Yz); zaOsMSC3Q~D0D$>z01$0`^WOL?Q3Lvpq5To)oG%$q{_UwR5raJ%k+N3{N1gckOcxD8 zmC&YxGD;jF2eVwY4E9LH+h7ZVo(w>n()*iiOhM&}ws4+0BpyR|Mwhvy;7{N6>yt93 z)=Pw58yY8_mRyP348aN~EQWCB7N3w{?GtMVDRhZ_l?b<;kDRG2oz<4P4Pa}G@wpTH z*#e?JM6W`sEknDYx2@?i?c&tEs(KPxqw7voeeRa&phSh?d|ynZv`-*5U>76k^>)Rv zWgYr`c~g9ta8U?zzg;^YONxw<#N|f+jC6MGmm4*l=fjn+(Ite}9b?(OFp-|dRWL!< zY->*j6Z(I=0HzLNmSP$Zqu|eJtsvdkPbV5~HXVH44kx{J2eWKyJ)cHZjx=#ghBOFi z#}Ja4%oE3x>BfPKZ=XAhwc-s^INQo+gWCqM9}Q$-Fb7Pd4cf@EtRH5NCUAt4$)}(8 zJJL@T^eeK54#q)KjX8tL)a})x|07lV@kgpA0q5@YuT)LsCo;AQ3{Z<-dwxEh;P;D> zMJ$-q!?uT9y17adEVC{j-fS z9pE)SZ#qA1Pwb~!vI2c#FO-1i*o&IZh^X)@=V>UcP+_uV;7JcLzVQxkipUex17`w2SD7}msC~Y`R%8US z@lcm}ziT~&gw~Qc4>yKDwH==)BKg-EYHtFS)%u1L74XsvkNoYJKYH{35GSe&REAQLIC{MOham~s&8Gp1g5!EV2lx{hy{$Hp$x)Y?+hDcV<;gZ&ptG>OxDaQQg+5`Sv`&W1`r$5}yKaBWakS&{?TUMt*K8OQh zV~9;km?!J}B``Rs{(MMwZh1K27~Iq|AuKW|t0EqI30)v=2A6#kWU1V*6v9)ywz4&_ zuS2dsFq}0*qS~nomBFf3{KaMGisKS?^!mDMO9@1ucYSkf^P#6%SoV08J>_Vggb=#y z+UG&`^D6whmt5Ce^XV@`g)y80W6q;&L93pDs}Pb_)J(7;wX#QQ6t8o5MkMs-xaT!a zk}BmrFBkL|(BLTaDzlAPGbZ@1ivuja(?ARu7}LdxO0>>>ja|uQSnlqbOr+We2i*#R zf5H^!V!6cy>^sY)LPLz*?5L?(2Aj(A9I{O|JZQO<;J8fn{E;#?-b}dX!FAx4GTel8 z&!YIeJ*=YHn@%Nm`xG1(vmMz>x=sjAtbW@|p8>NhtYbakJWS^A8)EIht1aK*ONFCk zuN0MlM4kTE1HznWYGY;nUl0KlTFZews-okl=C!gnXaocX!gywW1}6$U5Ur8Kv}MIH zX`eESA<5OPo)SGiIq4;$NGk{W9)@SbVam?!vj^3qUNeRpd@Y&CtvyQMD=>p5!0dAp zd_ypbW#Q`VHK?!3PkScR7HgI~0_!NHc@|T5h;5B0!TSai!|Rjk@0Ov6PKuv;Sn~yn z1CPI%So@YyJYTE!M?vu0H~7bSk)p)5CabT62{DLhH_#j1Mj54O>=J3vv9RxiBixK9 zexaB~4)nE>Xs3QKuW^wmx3NuTpd-0#b7C9hQ4XNy@7#x;;dGz3Kl1=?ULkUb9d`w}2WMoFHVz5cBJJ~)d=@!*%ydw4>-C+5X z&fYI?omXr@)A9c-C2 zH7~B$l$S{8bNSlK43}Fx7JEM0;OG4{L%tw>d*IY{fhvP3P}m(`pmjxUPB*hTO3V;&BXnL0wKg?QtlBoD_-K$je8w84W@cV`=OPu;o|y zgpG0Di-i_N)6xEW) zTGJW9G;Ys8Dz)}xhT^fW`SSv_P0Du$U9Drv^)`sobGmdRo7F3mG2^M^=> z#pwp|`|Qx3>O{%T;4`uR9gzNUDXL&QUq1a?u{2_Q=1Fd2#W&IDZqC-dnE3o-O*b0^ zlZa8uq;IY~TJNlx2uM9fsf8hqC0(@vmO~ko*-&g|>gFdM+^Bw}e!<@+vKOY;y1PS(>B@J zclJebd1>F6S(q_W!ZW@94B@(($jo_O-~V{uo78z*;DlXyTGh6pZ9j2p3-!v14dNXM z09wmV0R?O>ZunEn%ce*q;Z2Z-_6L_-=CYWA@nvF~-TH z%G5EOl;m(+OC`ibZ=NJunA;_NdJYYBhe-cz6#xHHX?9na421iKQMwF2g}=9MyP&!O zCJnh0F0JxJ!jjex^8oesaI;I5z1JF~lgiup14#H5tw2sId?DS^137t zIN+L#C5UT+V((zJ1LPRK?1XtiIUn_I`ZuzodOT8iulSNoVYvbg?o#<8o6yEY?pRcS zyt_vmzbQhQZo!1WU1_dZ;G3zx=-@eJqHI@`9GRh@kXaaGYy~tqKMrM8lg#chRj~_E zkH|H8sfb{VvgHcUs|?YvDQK8H-l(K%ni-i~hfXfCtu41>qf<_{O7ypdS8C|``rV1Zk$7@ZBkCXc?yi&w?`Q; z@jJ-m1JDsjBtdt+9(3==Fk3WF*Ra3B+;S=Rjs&yyc_R@U69?d(pYY*u9YNSlVdS{O zi7|x6+`m@)lpGb|dtl`XpzP9S2R6=QEt0BaOnWg0MMTL;xV`X_GG38kS@To6h;GbY z3&jN0&u}M-f`7|RYW6{Tp1X8&l?Vn`m{Tw>L$i9*o>w@H+8xsR#v9BohiY38fKnsn zWv)o#bzVqbyGP1ST2`Z+a^V6kLgai^vTQ)e!;MJpI#*Of0d5PSk}=u8^uZrM9r&!O zVn`Y6h1SXLbfPzffwnQ#?<59J{v*Pg`n$5huCpEX!O$0yOcNXzww`&OH?kR$)gBwG z4EqV45AXX8u$9Gf%8S<%ILESV^w64mN>V9~Pdf--A*X_Kzl?1CDu$7+$Q4cu#iWxs zWBr~{K&4W(f1v+u+8TQH-Hm?lv&xv$~ zSb$i#(*8hs8|nUYgp9LgsIQjh$WSP?`s$YFDon?m&Fl$K8{9&@)ve=c&!xh=SWp^} zgphL!Ix={iH(pSRZg-sb?#B`R`NSB!DELZkLklwKULvB%#-If=4wbDxA^ei4nSX5@ zPoGuE@e8sz-6Blm2q@d|5+ACJavctl04;lyiPH^F>f|Z$zl^Gj>yZ=Fyhk^9b zQQydRIH_kisBdGk&OuFytW_SlX*WjVStMw$*{s2~Ro9Eh)@-x=dLJU=bq&;oyN=wy zRJpI;=T?0b0>YVny6(+&nz`<4qAZK&t6K7;}m2+Hhh<4le3>Mu?{!9${GkrOUC zY|O~4UlQ3Cd*S_yj8gOXM{YYleKq>hBuxQ>GrVetzA+Td)8iYc$6Qj#1&3z2;d1sayY%YGc;ZAy3!a z#)Vgs&f|O2`@$yU{ojw;V5>0AIuR@B@fN@9=1H^~!+Xcd3-SeNENJ%O<;;5ho;YGt z;m4pz-#CBjEw-MM_iq+~G-D5^@Ak??UMdY)3Z$}I)xpM#e1S(^Ro zaD3D0En-zE$|FDQfm+{9;n(vXZbcayIRWMkZV-K?g@xQ|^Mq-NSf@;DeXkWwUc8FC zBk~8aJ9Y8*HVS6N1f!+RJB8qRY;VSTFm8YzG#QdK&98NDdhWSMvNYZe!JgC;zEh>o z#yGELnh_jbehVAYb{3J*-q!IhyJ7rw%r5C1h3<+S%Dl^H4x=BptiODg;JLvKE#HIJ zce78-vVxkD7oE4kMqG^MUZX8OVIV`eW~#qHxQR?BuI+YNHhj^QqKlYS*VCw!lp;RY zMbb7PYPZ&uR-X`I#!Fj?YGwD74sF!F<2Tc5KK+(4G;6f5{~VS!>9$bEf;;RvPxGDz z2=aaux&8K7WIgG=VefHLP@~*Mf0;S)>ERg2gc<-YG^uLd`TXuVIb0m)2tCtPw};$S z+kQMT=oeLX8|hUpiTfFOQOh>8k4O442)mk#`?NM_y{yu$k5|<%!RMc|o8t3|bbl4@ zDbzt<{rA}5(erMPLyR9Q6lqdR=zF>8AmY+IkhrFj*yEHRMr*kW7SV&=1Xlxib%UaH zQIhO0h0yBLjpaR{Z*rF7uHLJhVa`SkqD&`k9$*Y7a@NXRdJ-1fbSHYP4uG=-i_tws zYRTa1D(FmD4y*R*^%xMrvDF=8fp{v|>z=tD0x?fuGFW?NqFCoynWJAIPhO67`uYOU z8B-!6F7EMkiQGAmvHW}YsZqyz<^`e)63#JF=Z78nS7th1eTK02_-6)VY3r;fwX7D; z)6JKA0BXj_^Xnm}_scY%C&tg>@<#eg+)3WeBJ=}poGg8)S!1KAC!CU1COj0cDu8p< z;LO$GVwdVx$V%4!v3){Rl$^y)*8Rv%oUUP;|2{h|Qi0X6|O!?o_-i9$$I5Dj7 z8s=*0_KA;xKj*r#S1KS(X(NO%06s!5B<3r@bo=zr$Pgt2Df~I^bpO^S;Wx%vt)Is{ z5hwz6wXO{kM)svGKNV{Zq^NzKv3}e~WcZQ9XV&#g_pCiJ z;{Xvp__v4$mG_IbQ1AHrQKunM8M9Pd^x+jLGo;I8l>61k0Ex%WJr&K9P9x8AT$9D* z``V!u&C695y7@|UnfD??cWTD0`!)5PMEAv9Tl_?(R#aXG1;!rD$9irTugeRd>GXc$ zLW5h!n1coLMBfUxQ%>NN!ARW@Rse}|H`67 z{{K7Ngz`auLkpNAx1d(kPvs9x_0p^VA@?^j6CwU{0iKd4~m(*KIC(-#e=P@DT zD>PN=*K$nbM8a~*lufHi?j>O~?Kczr_g`&jR$~#2crbe*ws@H{C;f4l zZ8!f;60~o=59p5E^TA`gr@dwiSMlzNix~WxYYuwqzkmQ-e7&2ps*v@3yh`)hOVJQ> z_q1B%b+WmZ`SE&arLy+ngjhAF5hQEa+$mY!+%r*uA)9Cj9V0kjI&OQyjhD}gE(R2%IF zcT-9W8c7Nb5!I=pnj#bubDLqY1u;4kq|?2163>;$ms_>TXKZVux3#WEv{f|E=_Cso z_m$a3nx`EagteGHu(zt)gdci&Cjgf9_VsMo5|)l*sa12Gn-zd#*GDS=wZjHzWi*j> zSQTrwD}aS-JdqAGQdd|_P~jHDqE&g!$NIwV_&n!y_oCg=QRO9le7h<(ep7qd)&!~J zy6^PyvbmY1BHeGeu4>%zAhTrMbquJre{rfid%Ny>NdQ24Z56OjJ34XXNmUGT9tboV zbG{t|BiEWvh-34sSJF!+r}Mk!y0wR%!=@?0H8)$}Q~IOVEfog$+!LUd|PUl=e8&#lz<&?lFOrNEKtciKd+FA=($__xv&EG7XGP>FG5_w3@|qeAG& z_-PLex~m6=0}h_!OReQF=@A!GAUk|a(<-T67xldi0bWiCHMNrIm( zj92aIkt{L=T#hNs7b@Sd4vA>tL91>+!lL7D?amoq*f9Mze>(T6ah1SsC0yCu>&RWT zE{m&i%|T~-gbzt~DMQ|f=ZEUS8yhctJ6~T?OM>-%%~7HXxmL4=CbGbhyz5nnz>~|t zSi$N|=7&cX-1Ak^+tU5T`+C=A=PS5Qqvv(p$AHCt)!1V?bEwy$vzM_uz}(yY1@B|m zaW(68ILiVk%erIZQ!*d;Qfyx3`2hgs-U4lmYCUfS5Uk`A`n+`#FSw>7&+9kt)h}iW zKH=Kyn6sFyJkE1D`@m{vG0qF&Rzc`su|og;Fj`4Bm;<#9rnL0EbSOAZB$ zSG?KBZssHkK_WJ&EiU;g7QSU6RYig=^VIu#9CewQ?K`Ud2T3OWMUr;QDpUV_2oGGK zP3ul&U!9<_F|pK;SDz~juZ6&aixdeYBkKz<7a=Yh06(cE;SJ7(iljC{$0PkuDVmWY zY-hDZhof20EM!d}YYA6A)sz;Wj3YU$=qM_l-47G@iG*j?(f6-Qhk7N6yw@}wVkailwB}|*vQ`kEsI@hi} zXrSLU6bZCU)s}CAMy_Zm=g69!-{M;_7I-wuseCO%$z2b61@kux^Wsuf09}VcU_>m7 z^01(RNOxJhI^VWgCB&BOukkb$*jXVB2ZdyeoVWQtM3STJJV?~=ocnn_aNLA%xqiH` zm7Q;vZOW#GcLV0H@@?fmxc{ z$$i2;tB&jBZDWw#8-v<<(LF)?Jv3|m{kQj{Oy~vm;H|05TRk9EglDDY!0?o_R^-eg zI&0BrK4S%a=Rs;Am^8umdYC<$-cAg4J*-S>5cj2E8&TL~$W1;eu_4n0vWPuJ0#E07 zS@1?UHDSUvFDOpjb?vjHE*PF$o+*xm}jG+w3Ayv!Ixrl zmD?giP$ENDhsIL*Jthk}Wm|0)^P(90Cf9UtApr5Vk?AtJz(>Q4VC_a+l6vwCv)HU) z^A-VIL|2%>!reC|H|X?KZrvzlB42AQ2(_fhNjSBY)#Ayir&rS-)aWIUCbMqWkePG% zsJA6(yCDP>l?0XU-m&kl=o6S^_Ecp&t>X@hjhJCBvn<-8@ZP&3RYmB zs=8XO$Km=YW#g`u&Pj*DuX?Nh%_JT)3UU`%>FiR;UnjApJuxuG4T&tIN9F&i{;WP?kWM=*H9YQ5B zB1~P{U+c1jz{1upL_s&|DSFUq^C`pI`=vnz7>eR}p5j*FKQ7^|RdhV>?h)~>Vpa#e zU;htZXBidO)^+IwPw)W2-QC@#kl?|CySqEVT@&2h-KB7M3GVJrA%!&e-dDcv9^Iq< z)bDf7*lVvjpEXxPBl2?>^5qlGZpWzuNKVJ7N)MehhPa4Xjo=}O3`e<+jMEo}PxU;O zD5PFo55Z>Ly~zG1V`>-uWt35{j$epz=zyTP>F%THe-DU+pYI2R*SrUy_3s1X@*wf! z=TEQKNxGF~SK6*OJ}K>uWd1N+2_X7iM^q(QsJP zZ2aFDyB5uuqPgYn4QMpY`1g=BEJ5=m)W8A@DQm^lC0h6)Ij5R?L8j zbxXZB;$P_z5?jt%j?7EGHPysx@~y*g@0-t7YS^k}#p)u|;tZOevp8FqeX{*cS7)%j zCId$8%01%_krIVp33ib%-VT58AV^jm6*3dbNJSiQJ5f&eyMtzuPc(&}d^k8b%(dWY zp)jshs!w#9NWQPL>7%&wev#GwBa?{w5X5>|orAdo65C-z6+{Oi&$nd*&jGpzu3{ah zgFa4R`L`=U8{G%MyR3Gnl;sd=I`!tu@bK8_yK!&(uC`UJ3p@k}x`9)x9#hZsk$nR> zD;7iB9t$<`MxmoR>s`be&}gA|AMgsy||`zT-uW}EK=DDXt!2jf9! z^~)9E%QWa%t<%GESX#$`cR~AJktgGB^(^(itrgGx;&6!D{c_)YZ}`gN<4_0MSe37> zL6p|w%kKT=J=`tXbhA!}PLB}ukU`R6OrR5kJ1llq$$#5I$G`MGdz}{y@V~@W6F>i8 zLOE%(Sbwn$cp!rr6=K8zgg8KYBxQ}tS$csEo!ziK0x!Djgd=J?jSE!dZ9dmH-aZH! z-W|B$^pq%g*+3a0{xL;`ztIfSinS9!@ApU{!_6Ju7=abm%xgNH>v#I#vQadSxy0Ow zj^#RS9{)gU+FPVR=Sk)Z{)A<(3G@U@)G+P_0A}(J$Z@m61z3`x)O9gxXNPQ3IK!ZR zHW#`Fc|H>eNdCD(p{HpKnC{q9WZIVuPLkz}x@-Qyc`&HAOT&0OF{gGgndU~cBnM+PX#4?YPU)JFeJam$Oh_337rM=QR(N-Eg-MYm=hV1Ee@G+QwD^7_=SndC zI~{KG%HgW*!UQr1EG0No$Ect%$Ot^Pmmek4#I^r&Q}|~6bQ1J1yuUVkyYN}?q)t$i z(W_IIaqq2ZWufyB*ZQKBQme^(-qdFv^tvGM?IP3T;z9TB#qk`0OI2HZv_qpk08!I{ zHLH|tn$1+2zr|^A3Q_ZvQD=%Yp#AL_dBe?jRzBm$Mc23Bd1qt2_uuA&$!5n+XZRrJ z8}0Q!#ia8Www-;U!A^cS`?}(cY=Pv+mvCO+76M265E;zm%7^3yYPd;l$?vCd@;jvL zvNGtC>N$~?3*X%gBfC>zW-gYGGe%^#uSlT4>!49A=0fKzEZGgI=pw_$cDD*kT>6#f z#T@4^SUg35FKsh>>aH85Fm%pVeiwH=1}AYL`a+PC((|_jWm)qio~+Oe8}Yr~d4r|S zDQ91ehArno_Mz14kwMH-N#9_Uf*0$wo;Ky35J&5nMG(;n^W=#;;G^tUi3I7>pJNSA z?VbV$N<rgr#?L75eCn)O-zR$E0yuW#K@C;hF zzblr|u6x-;en#V$YqGZWURh~$HS48FQ-zY2lQScY=2{~9ju&k?*|ft>8S_1t-65My zQro>)D^@gnj_XetU7DBqm_gpn#1qcs?)TfzVdv}+$SLLc_tY^nIRT0u^F$YW@#%1{ zqj87Hwo)etuEzVt0i!$dly{g2f@iZ?K<@!l9}uX>_A%j1rRu5wtp9zz!RzvtXw&3N z`{atiuBd<*bNXVX$8PV-0`j!Cd;@DzW=+=1AWyaQd;GMcoVzp<#sZuyWPjqICF)uG zxuL_33Agg}r;QWw^IK6Lo4K}`+Q;{G!k*e{|2Aqg{yIyTH7_yF|2a#tBw(ln+)qP# z$lh9!O2gqTzVL)6RI-P=Ah*p2Y2>2Q(a-I)Tr!&kOf<|)g@eiyYKs+(%@#tqK38dP z>jg@9=J3^Yb!SWefj8+v`l9soY7S zIPZ+H85(DQ<>o4Z;?DIiq*Jnq1wq>1Q|-bBWAHpgcl~dMJ`n&xL}cpSE}3T{VQxF45!q2VxAkhXj0JDE zMf|cwUYRu=Aoi*y;Jq&x_*aauaqSNj@Tlicp?)lNxN~_~}PcUzmRn27r%<{x?013*sW$W>G_IjmUo=5WAo-~EqFVVbc}4AJ`J-_Wc%K-6rEHtWvAITeTJGXmKk3ZX{kxZKE=EvLb|~Fyk5*Qw-(yZk*N4eZRVLmpEqYRH z?qVTi*!zYdGF6&v39Nbg&XY;PIZ14BZcxoE))U~mOqK*84UN_+0mTbp*pp_*KMxR1#Gl;ECS(d5Ad%ZwYY_X2eY`)^5qm?bML4&HHeNu=7go%?D(vQ{#P= z{p1YBKa)Pg$Ktt4K64F z&qH>o28d@LlBdm|B~$)T%hBMI=-f4}f8O_5o40d!G%SHVar?}+)1BA#eDz&J6*%Pg zc{*ies(| zlq19Qi9k@aH1}sr_6Qk3WS~q@u|2VJ5LzF)z>HAgUL>k0H~#!i&(9e7XCWG`sPM1c za)V>ZqU$_~jjhHw5t1Xwd1j~SOw;l=x%fAc^rA5`tZS?hSrZujndBrGN(DI#1x8Hy z?D`nd)Z-s5zs^K*t|>4!M-oMCg3h7Rp~GsaajP zRsAirBGNjz#_9EJMmlqklD=x$w$o~Esf5IqD*wk#*<$Lpt_oML1sN3=y8TA=o)Uqb zY`V8IQhHrWo6DIThO)D3$ycv?(+}BJXC%omQ40*gzHx<*i=7iAyOh{2&qNMq4Z&yU z*$ofuN&D7Wj2MRrygX?P;kzYX!StQRv?9E>CtS(wSm94o-ni4=sCzlTcwGnm@deX1 z#XO(*dV9X$uGg=5tem}$dQJ4Alftm^FP2+upoy78qb@-mH>YFCSz`xE(dphMg&WK0`#9QUJHHe3w?n(9`&!`9xdbb5#CRCa zi0KrwOi8JS470p-;r!(QF0|(=Pvj)QG&3}D5|0Jn$svrd2q&l4J)a;gKVypY*MBPs6an68f5U z9Z14#JaP^J7}a`@jQX_9Q2TCb<~HGrZa|_zdu8iaMHCh`RDIq2?slJ@By?h!5dH_( zSd?2pFDhgPg}LARG(wBxkbig>Qfx_W8uMv5fZv2;Ns4Yh%0ufdYM>ExGg@lCXENYyOIX91vsofMOQNqCIEcYr;L6#QB+`fjI?Uc$1 zwB@#XN>oraqoMXFY?fY`r){4<^vof9q`&0GH9Q&&31VpJO_Mkbxtx z9YGl$Sb;6!sRD_t8Qn?Xcp62gY)D-;W}aEDBvfJO8Hi%Fl4&6JdOgaxB5>fC+2MVD z;Ij_G;n#UkdOO`)x%auaKSHg5wIO^&B?Nk3D+!o;o*J$<+#A1i6Sn(+8*Sev&V*-P zfEQpL{$-aV&_3r|nA~{!{RJFf-CN&o)&w)bh=jLif`r56@4zpTA)TId+2jldKc&5W zddxGO#scV5hqbyyB-9|q%82xZux9l&cY$h(XB=Z`dc;u=P+@rzA6`5~t}f4A({6H; z5I^4S7yKdL^o6QMFs}}lpNhj=hj!kt9c^L+Ut>}=x!QC-Wxeiz&PKqGHwR5IK0RoS zlh0$O$7`m9i-ziqH~?F2lh^Lrk+g!nvD&1uuHmM9kQ+ybr! z``Duxm5FOJmUpMZl&K2O09K6*Kc*~b4Ezo`VyH`Fk2-fbyv+3PK<23%C)Mx;=ae~+ zFW2trkp`9XYV!Qk{To|c!fyV4z9WAA%oT-_;5F7`v{B&Wv}%_ukSt#dtgOpsw0zpf0U8EZUd;7us; z8NKb}>{*oUbHHD&NJ!(!hD!%$y?`_>Y1UIo*7jsuP~oU7=SKTGRP=1vDSx8*UR-XY zD>R!lZO7XxpIeI2jJU1yXL-o9b@t*6p~XekEch>540>Ic-3Boe=)yde2R)F)&7yU^ zP@Xbx)qe$FMrm@${5tKz^U+sEEGTKvX5xjZ^2A1vqh?FAMRzYmmeJVzIbtGM44{nr7I_Qp$0qoz%ZF*rHq@$jq+cn$3v zeZi%q@C1Ti$$*7D>Z{!{DFk4d@?5Ks4H@2iMeqqXm6Qe7ELS_zr_6dBEKL1g{h!v& zyHDv>^%m~)PZ`T)d0!l?JH=}~afm9SLosGv&ah17M1`jPOK~wdj>Q<=M`f<1*|eMx zGh+sqt+X*dZ~jwHvE`}jtcg!G8RwDZ7hL7K*9mapsr=7+Z$D3&$tGNsr(>+@S%C4q zs6y>e`b&R5u3NxLiwRteEDbhYFlT}lYYb^iGv(KO8qP(A??t2iCJ*HRxg!~Ubxhy} zC+Xe*3)5v_lyt$b0MY9xLy?C71k9TjCuOD?@k$zb$vdzgj^L9)eQIEJgrIn!4Q^hY z)k6(ywTmM7tRcb2fJl57QZ*K)SW_g2T0X<~Q+3$hb9PYeHhG#M8ge=5D|5lmjeD_! z`FbLCdVkPN?+lGhzBLP9RouR_XJD~qpA{{o7THvbXz@cEhC3B-n1EukL-FZ-$BmWe zkZ9#RTYUC5-bP&Bty-H& z-*w0O6mOrl$3)j(j8=_DzpoeLYn~6=)n%;+GlB7c#FR6CXXZQPsSs(19xQ$c3_;jX zz@N%?w^6rL&lQA^{Rw)FFn0Q+%rT~CvHnUt1H4=02pbd-&+;6&Ph)U~B|UeWk&cM+ zDZ#gvi)t`I@(1TS%Gam@-;8%S_;cOA-=9f%&pEFoWIm8p^dqhaz8wg*c~4{d9)ZYp zuK_DXzROCc)e$&Zop$Rd;_VapHX;Z9y3iI#0E8@uF*<}wDw!G{+TSaF#H3mWv6<%k zULdTjKf|oAX+6G%|FMMMVIy4i6M}!3BXw-ICf2SaS~;b3J6{q;$AyUiOB@x0JewRi zR1eIn)Iux+A#5>wr5?%FF6QoU3^r`v?RVqDwuI9xNEf7__j(5Tyt=;zTcO?kN<33a zizxajcnLjap|^y#^Ig3ZF)t;UjTR$gJH~kvTPtowBDFp`(PsPKHP3%(^Kc|6NO#jEa?sy=Isby@D__As!ynt6 z9HizKU?Zs;jA%;S81~T#9ArI7b5SjJnaaYZ`#L%u9Uy=R<{$OeD>qpYmr9HfI3pE+ zc4XR05`itv1h1xP;(#-=8Hc0iQxB)a#;z@qq;5~Teu)iuZi9IrfZBn)>StsF1f_vmf^?3laSGQJkt&WtWHZuq<`yk(tH z`dk;?-b~gH-^HH2tGL4}w$B5$CXkwGEVga0;>3qBL=MN_?z0>TB~A(T7nL4a0qUq<($&_RmKZWCRowe|vlNZzZ{h4l&xp zon#`u7@&#*QACS;Ibi}2Cuz@p1y^)&)$e8Ya{sw0;SK(EZ`x@-rrrOi@PU&*%6(Mu zT6NorVz9cVk_z~aSBjOBt5_4yz=p{IaK&dG7>iZ;D5^LQ{r;*YD>s5Lgp9gkbSlXs zqT#6^P3SZ*F9do>p5nw&jk8v&AEs7=@ue0sxdyr|3?et<%G85UU>Mpyl&@BsL&H83 ze`4J3AV#Hym5g3_{C)XRB4HOxUG~;63p>$h=i-(}QQyaSCfkh4M5WkOx2FX8Ob7+? zgUfpss?w^*kDA@R`!IbVXvZ4W5>Y=11sx~I-XPW+bxYLptsgk8 zb+Iz`n<9J8l;?ZMS3C2aA=9858<(fHrAepA_EBR9^p6 z_3&7(2X7NXTG$uv@XyR)%vYO^BlmOWFNVYFLizvYA1D5Ht=;d`PXBSOF;hN+hP6k{VIG&Qu#|okZ_s)ylE39D=e}^_;fHoX2QOBl%UO%+c8{#TY@oUxM+g zdZ@;5O;V&a6`In;EOzv#u@SA_r1LIF80-K`s&wtqG1o7x8S%{oXnUt|G%i|~!N?uJ zuA1IO^jQiEgyI9#+7}mM)ex6me{>mj=HPC<0NoG6K}Hrji4gED&_7dLaIztBvlJ;| zP;bY%(!Tc%zl5i!UM%(0BkMc0ca4LXrh=MeQ;-#KYIOu{!QxKXstN4s zuekc&3p#mm0$)tB=3A8*zfMvF$6gJCGd{{nPMys!j`a|dIZzI87*-*Fl+4*d5I0)> z(}$+DeJ`#GO>GfK`7FGmAvyq*S~zY1f&lN)&Er2D=n)PjR=WAALmcFx-1__&I1pyA zNG$TWu!)@vV(>Wp(JVXSf;@H?hqjq*oaC%b?{vyxwXXcm-cOyN6=~h4fR%gSw|j7v zyuj6tfC;bT{@O}k>rOQ9*+~{~kA8)HL2E|>i!$bH=Ti37>13m5|IfvXj`YktPmZxM zmM25({)V%#`%JX0Z8*p4@qc7S)4vgAt$U2SeAdQ7KuEU1DdV7uLXBfnZNhD?xn z++A0%FUDHOJA;|iG7BY$McFxpDq;}RA%7!=>Ejf8%w?B?#uSj}ZV0g`P>%%EAH?ta z$T^PnP_;i!bT{QyEP1Pxlzu(H?%TZ8x5lrrx^Ozr)N8!L_#^n3<2*WP%ftT z3uxqhpOLk}OTt-_&YhsO?q!f}v-W$ahBf8gdC%yCRw_HhYexM$=8#W-#^0dr+k2@^oq2bY-Y?)gzZW z`N!+DYyV3pioSoUI{fELVxWPvQcV~1wP~|zTTN3#C-+Y6^P>^_o_EXMAx#iY_euEE z?_2pbzS&}$}TpXuEV`DTC7Y*43qy7yRF+2INWXPbJBV#nqxd|y2_EfI7rxuimW6eT zag?J^eKLR8CDPwCiv{4s3FF=Ae4nNQrThCPT0i5=y8=j+W5YFGMi$Q^JNQ zC7U~d$X6g={;~YC|t=ibZ5S8SU z#l4xrHL%+qjo`{ehvSB0;Bk-R(RXGjI~|ff>G_E&@fj#dgpv9&0^%|=AbQPS4xyKw zV%zGRX#oqL3WN7`n@FJUqmvxu{Bg?UbL1Jd{Nt~)jn|p7Tdcz-;(G6N0@ml}$+!0R zJkpK98&{W>fTUu0%8TqC56`T1cP15a-l12$rB0%o4OpXT*GvTo0KR2xfl0WjMNqT~wpp?s;>Wn?n9lRs>s)kcySuCp+=%lza)6e(05{m#D zamK5cir@>n89HwsY)u47DO9ns?>MYAXUo3jH0Kh5cS7jmYwlRIcA8 zddgWztuR{W+alGqoH;>xdGRq%AiRbPOH`HCM zq*L2M^#gdu4RIxu7r$mr&T+~nXEZ9cBdW+c{GQlT%cB34-0PZ-0EH_s`HTsTIV;bs zuS%osc}8H1d>O%+mRAzGNsjuH@0%8>`Mar1y-eY9=P2CC`QZJck@Zr3)-vgxDX;W0 zC;z$&YS6vUJ(H66*navKQbRUzHJR4AixrqDTil8vw9rPlZRHw#8<_pc zV$@qVY5Bs(#`^m^OBCl^ghMC2mcAH9_+vn~Q5a0-^rSLNYrd-4bnBOAUE(10A0iP% z;P*Y~gB8>T)d5r~L`wk*r<%nesot^tG82TmZZuV9{w63gT$ZYv8E+LtXT`eE&a=`PY^gQ3_ANgC3qv) zW#AY#w>j!i`cp9o+fmm|4cPt2m%Pa)s~@A7@JAwsZ8n^r`nmPA3k)$oIOqgwK373ha^mV?Rue>aL?%cQRba{8Gbm}^`F{%~?-nb_>1a??6YxwWEC*uf)%-^BPK~RXT z$Bj$TdI>whO}Cu>p5{EwwGRV zrt9)%g0`c;V&Ar4{}ic*NX+ky%l%M*_cDa1_p!e$icj_(GM`=Y4MT3?lILrD((Sik zgRHECfmFN8KQ#mLd>7&$$1s5o1;#IVJ%+NypgbWRc%xVwnG^Lo?(?{B(pT%P6nX2q zR_9mbe4||n!W*7M4b%a@BL`jx%6?idHDaIWP~57+I-FElMY72vcqbg};^@YbCLL-@ z@KF?M5D1NHuo{gi30CP_3oLt`sDEA4>&@F@7knr#14r_c^O6cqgbdYBq&Na5oLYkh zdk(pedIqd*yKYTFZW@cEhK4&KKcZurksHdh6TAj`UM$RKG>7^{U+xADe6EkQ{)JrFO-fXjlumIb8um5Ogd@4#f6XDXMz9I^mYwK%M6cZFwJ~>* zHJ{XTS%H27COO}U2y?4ThKeu%Pj+-@sE zwJgRV0JOkNav}K!0p?V?;lW-_it3-}4Y8?!2lTYH)?fT^p;ZyJ>kF7Hv!<+hr?l#) z?~gto01D-fC77P~m_ic6%u>r#%k8Y;#z4%;&EbS&%RWxucPSF(vJm&;$NR#m#kZ@B zRo?L&0#jJDUOOsC2fd7M;)96enZ?!4x#?waO;v@`)r5*phaCOO=M^=}Y68(%NIjKw|f)zfy1ZJ>@Sp3|K|RDJ}7o&wuhj?KmTfpI7)( z^}+y)-hS^TW~_cLN$f=%fSXG7AtTg3<{5Th>;l*}+7F#5%q`Xwm?rd7PKICHkss7w zo9-v94AlOjc;OvatyXD?075nU2HV+15%b*h>;MhsIsIXC22JwY8~x02BH?O<_coxd zLl9}B@UzI))EJifL=q?2^4IH))*fHHPk7gBycc)gMa$<90UyuDQC|-*@5bf!U(l%s zc!m)?b9Vm&scT?}sc2H`%X0FZfkU)e(`~Mkc$JpM`Ety%@YkQyR${BRd2wqmC}kb~3*R>T@EV zk0x4F|I>yv@AlwFht)jL-+Ox+P_mClspCiVHKL|Fn>o#%ZRm=zHQ=z@r`z;RsK=&j zL>3`5H=0moW}Pkv>s~7jn}MRCu-HFOpV$bAWZWU1gaR1b)VaSLDWSI*1?BfEs;GHB z7M}-fKz)VAS#PCaOyHXbZ)??Y-_MQ({=b~A# ziIYhMqa=_@ZgJC7C9iRZE;IR?>3`i$PBK(9YdNP(#%ZN3MxrUFD0NLg1DC7s)KoUO zSe+tulI>if5|)P}PIn)qwXL00qGoB+x**Hu?^2c%Pr;;`YoJT7d&zr$`aVv+KVBay zAKR#UPqYgWhZm#0?j5$SI1tR`-p$Yf92DfpCSG>W0P^}SKxg#u9d)WpRtntT2>7m^H-9lv)@krB{$dDW)dzg`KButOs(lAkJAi^M zkg^SiOJM2im&PEbcKUv~+G~VK!-S&jh^^$*O42rU<%J@aJF_(ijoLrEJ|?Qm$0>$P z7CqF6XH-%TEHGKG{F)f3-T7%Hv3XN60}+wM6{kdc>5^iF z*@(cGRbTCW6geK}@PVJJ41xcWvma0$!AwG z-QM6c?_PSrhc46p%%}>Qc5*ek$6mSA(?9#mnTtc|i@T22I!izXwwi+p+%5s@(*W{11%A zZkCJ+LHbTj7(sV*#Guxr#y{l5J4@kas%Nc-e{rY#uX#-^WtjD+`6g@5HK$pZHHnuy zMYtB1oJK`QRKVHyAXH$*?8$RKNacwzf;r% zN3l@UnqU8^SN8a7^JeOe)yRJ2y5;8gY5lV4RQ9pP3alNZV;2;o)pDTpSw?L22vrXk zo;fR$iP~upGB9GVZ*NA;rAsnJ6K3u%Pu5>aR4C1aMkjAr{Hs$cbYBbut2h9Vy`R&( zd0V|7UB{_JN08BTV}gP8&{7A86?}ydbU$m*7lq6l!^gdnOuCBPVo&9SZ`Mc6|Lr4B ztby4|DQqE7S2IsC;VzyYEGQ~)4CW)*3D*h>oKHA6{Rv@hMJri(!LA%X_?_bYncu5D z08Mkz|Y>u}ZQm$FdwjUzcMlARDzA1^b!hr&i*97D)F`=9^x zuwsPBIiYgOX0ZNHf(%ir4IB5gCYN70F^E5{H6nojkV!gsu-Mtz&s1T=MeNc!%DC}h z+4MiIrvDoep(O^RtbGW6!nIi?09{HnZ(^~_Vwf^)e{OM?X-m#Ak^ZQ>M-DB0Lb6~r zpnYW)Ig#wzZQ^Q?1Tb8orTbLWETUFXZv1^gbota8N{cE_(+6)v!epvCBK;{{5@k@p zu9ZpvNrMT$1lFZJqH{NfPH4aEhyM)}_U>fH!f5kd=y(ci(lT4mbeO8;eQGls2m&s+ z6@?W;Q-_T(SKfH70@FD|FF`VMM7{638;>{~eqD!%(5+R91~W>TRtLLr=a1gxHT`_hw9=t7{1-vf*UVb>m=RnT6Y9;*}VAx6i3t+%g^X~r_z>vji7xtR^ zCxaoJ%I;dAoWYAKevZloOPLsb+ooAw+F2jc6rHoUgemo4-m3f}V8nU@} zVbG#&7d>X?Xth7IL*8)AO@yuaSFQ~$sVV4VFjZ-#ww@*4m|MifRn{FULJWbbV(K%N zL(y+R=C)A~X;DR+CpW;B&{J+B$WG3XD~Z{39gC$-epiztO#d?$P7M3LeYd)}>$hQ~ zprsQ0Au<&hJEXV-udF`q@WnVH7eB}?ZL2dm8Oy?-YK7QQtGKwSgPePEwK}Xz{^A6Y zhPgkHuA$J*rcpA}egmjl9qH*5mLyMHDRQ>&E`OYUkl3nb^D%X+9nIZHqd+n;mCfth zC4yy_19|H?1hJW=HeEy)SSR7}P;B}3hva2&2)wU9B!_CB`8lzV3IyNha}7hY;M~L5 z>aPe!8CKKFzY0UF!bIvA@`Nhh3325xmC2evkZkk%>@w>f@yxeU_6^tsOnd;qTu3+hmn+4?V2&#PNRDni-@zXKb&guy44q4L9zFFS!C(}i zuC`m;PF3oGFx$Gc3rCduC0X+k@hH2RrX}yXZVZ7nMxy-FXCvPWAEo+noMw+elnS_R zTEUook)b>Q)QS0!$O?5^6n8X%>!imQ&YAgzE<Y2geJs6RDZA^w$RlAaGgz5gMV}o;UxIv8tgTp)%_Y6yBZ}4? zlLT>$tY}6RnVy;i&o*UUbh|Y*Bz4e0SZB%8;fz(B;~}=@q}ykf&i%y}3L9FsL8o`D z&=O1>k$K`YS57?wWi{6#*`wATxbeBJ1nyZ}}I|AafXD zie+}dA1|d*k}5V_e+&c{D^XG1yj@h(Ny|RpvLrW*WL{{|0KANB;t9P2L$l z?Y|quhnt};cm7&&W=COYSn|*a?9W(TN(qZHm~H4pkq3#b;RX~u=OtaY8BxD9TEnri zAqwErv~z{Oi!EiA2}+%$CHz2G&f0?aS$aHN!rtdna%xisR2ws^j6KghdiC?WlnVU-lS(O>Z+mTRxgUqF1-~7CKJp-|;M(JPIpZ6~zCn_MwxGv0RLY z-OVA4qRoBTG|&l=KD8e)0J;&_pFZjg`~oVA59%Bd^?L>waBCa+(F^LA0d%wAFL**x zOvi5u`b_ChcP=&);AKs*Z?e?_l3ohuap7RY11z+)$!s1#Z7=og_E5_$1Y}FK{8kZ@ z%Lh7o{45hV#MMn2Ff_nq6J!$8)t<}NB##r8{w!39D{oTtDyarSlAyjLs0QE06fJh0 z`|4`fDUV|#a-UCIk(zzdk{qWwg<VeelatM#i3pu^skEFcv z8PUr3D1dF?t6wQhhw~I#3}Tg1Pv_gqh3VK81HgDyN zEIjR(ID?c{wH|f9{E|$Ep?Q9?KLCHuVVZ;6%ScPPpn556+WAQ730;8g4Vs@rZ!WSd zcd{J|f=awi0*OG@JG@-xOlymyii>n0lMuuOZ6ZxMV$i$_K*zCnVGlbL6q?+Nf7pp5a0-fIw3@qI7zvF_ciqwc5yCuA zYTR42O}HjR?Z?MZ`00AW+p9Y0N7l!niSB%_GFjdOnb#%VQB>n=BmO~3qsN(;HvsG= zKSeNg-8~(#KI#?(q96-EZDA%{z5Ns})gVIKAwB8wmZy@>RA}&(UL{K}$`Gj){cQ9LHJxR%mEw6Is&w8{}Q13P%Hv z$K8ihOehnHE}Z&ZR}h0K!es_pFWJ6!zM_jREzF4|!@Ofu)Y^tB5|BgerR4*k>j$;e zs#c-{m09uXg??O(?W|a&d$Rb)Xe1*@by`C-1k1?F0vFCssGzF!CBoBq!IuZ0S`JVU zbQIyRTOAztz_J&TO2YQ+tQY>y)Uw>ea0ER{k5fLFc~7YUxyitW1!=zu4E5;#aK`3h$`Ixv@xPZ3F!>DX=XV-vwkwGKu>NTEj?K+9550$(nl{NB zs?G{Eje1S)%u;ac&6v9Yt4f2tY6QbP5#T(>n zjO3gEfpd1ueS;14n7coL2h61kcm43K;ED2WS;Zt&$C)P$jaUvGXR;6t{X~@}huP#K z=I_Oc&1V@2#X7}+1iEBJ|GBy2UHo0pJ6>u3MWaTHbm8i}4ZQPj3>%fL$26E0m1_l1{NI@^GM>ysMbp_ zA?oZ=(+0;N&lMSri>M(dVZl5weM;{PlePWPJtc2o7BO7&TY-S%V?~MCAwnYe z6=9lJ0%Ym|fp93lymL+HaDoVP0HpR0hE4cRv9mhwrocVFV{h8qd-ix6Z0T~p9|hPe z&<^?}{!brq5sHoLmO$3Kep-D^IDNWiU3vqUFo0RU*Dw_08=<520r_%K6-)eqwNovl zRHynF^I@|tiVV@&{EObWm}(S9$luKTf@^fTG6%w1wk~B&T338!%*;4tr!lDKWA(W>0+Qx+F}rEfd1F&;B3DaMdZDAtDhg99=Wf?EgE>oBr2rJzdSWANUV$ z9EY8K*nB%P%M;{00{GM)B7Z64#$eW3nByv(3sLepuf^_Y85Tc0r=?+a6;@Tv*RjN+ zXsp`SK1^*7;eF9n#yUV-jLt9%V7qG20-48L6O^J+ zxpOu+A6&UhGD*a9CGjOSrlx2ZGm~S@1s_eBeL%tsc}J)6_edPBShxv-D9nOOUtLMC z1uqGSaeAoc_J;+Cct=}(Z3=x$bZ-uS#IPlQPK0EqN?ZhEJmlOP8#7&*$%4B>^0J66 zb~D|Vg&l-`oJ60yPn=F-Uz{}t##=duF?1V>IP7AZ@L5N6^$p2cVR~!G?ejtOPS)$7SmL8KMj1>wYsr z;0q8t^yz{(PU3>FK+%B)JnDuo-&pp;4ywl;61zDnIBTau9UOPd`Hap^XFC?JTd&uJ z+`ceott1vH;U&gNWj*W&lzKfRE>~Fmx(j{AlN!((^&~<{J58yYL`_r5)F__E0_;B> zu}5x@3=!e#=wL?91};E-oA8FukKi*7g4{yU`m z?lt;;`rpw7^smpO4+TZ?5*rPX;n>zFS1JXO5t&OLpudM_&;(QIp!ND z)csU>#DeXoIrYoY=*yjv4cg5eYE%*1@~q#I?zE0bbtti4R-gPBBV!dZahI(Z0h{QY zqah&X(KO8?QG?U)2pK)-4z+j<`t9F>j)&RU%FS?1ilegCHrD_^tDvA0q` zJ3Z&V11+-+feAk^AD_D!M74EO{byy(G7($DuDnY=-k3NP{62P6ElZZIQP~!dsVS$a11ZTo9rj%Fe5BD=z2bOLg!Em?nIjR4hWnL#s6@Zy7&Ij1v!|6-vuX# zTgm)9swaVj@OBaQ9ub?HR3U>2jPJkGj36ZrKf0a`C634C(HmHxm9 zDZj4xQ)KntEVuKxGEi1AXmM@Cm;EEis$+8%{*B2wZK*B(3zJLx$q77)X%h6hA+$Z1 z%y6A{iWdoG#3ZCv?+PA>6bYM<6+J{}um66jz2M7Io*ITQIuE6LO7mxf zozFutbBYS)UTPUbES279aFy@5KhRXZ3mX-8HcZAOBHP);%mMq*_wn19UAq%V8!G0QU zN4E23yrx|R`AQTr!k6UV-Dn|QpSmyBQqsS{Y?EOTtdu*4#yQK1nWG= zV)phf9qb#YpruzhpMD9w>&t_S>BI7j617E_YpK={?X@Xmy{AaTmbvZsXp|`rDq~iX zZ-(rFWH4k-jgbLCVEt|h!&bRT6{L`W5tW8q$w`{@+TwZUPA5H{@SWvZ7{jH7qfn>2 zj|c%5SS{dVc~t27a@djGuc0@SS=XiK=lwKn#k)WFQE$HxI`5E3&egCemrrpdav?j7 zgBz44A?Ur~%m=El8`6#3&4dY2wV5}&=#s8IbGIRmYlo#GUj%J@NGS5I+A0B#n)Ua1 z*_!1^spqUj`vmy?=(*0VnG8z` zr8Zuj+GPh@_rlboO2;#v=9!^iZ6IsD*L_xuMB_3*aabYn091FVGFSzSv2EoY><_w2 zc14@Rb9_M^98ie~+nX1o9YfUj1! zwmP(Gw`T3yn^ue3wKug#j1qfxsoJd-D^a`lh#+RoAjB5LsFff>1&NrqUZ3lGpL73k z{|WDN9_RgfjOXL|r0rZj{dO~XRwLTtJ~B%CONjeLTI%KMD=)8bZjQ?Yx_;ar{BC=k zp7qgOo7DJ3%=sl__KhazsjECeI{-y3hj@-9Dl&1ZnHOBGRuJg-90G=mbOSPy`%9o?~yjXm!SNW>5!g;6*0IYc6)fvYVz4a6IvB8sMaUr%|3!&2`(pTEU=_-;34vUmpWPSzf3;iQxCM z)D%x!`J%=2WxJoR=H?UDwPcUIAH68aJCm0#eOZU}FDN8Lt)K99l0l}?( zLcniM5r5i$hOh`ZTgieXJ{L%(hz|y+#!JE%o?pM0V6)B2s>*BLQLE zCA~>-76xwF)jrAESA1{cZ4BdhV@hZK?)J#wHfc}w2ioWVYOrYzf1?}dW$VR?e=WXQ z(9JiRa+^6cUO8P&u4%hTg%_P;GYynSHFdz_Fav6%Hvg-a9os*bvT1QuZ;T0!JEv7F zZH}wSt2jfNEDTI;UH3soZty21x}d%8y6{;V5SrQ0j#1H~;MH1NEc9M`iO z^mVZCYRSNNTntE%-0~|XbtqfWbH)J#_L6Gu#C|?aOcT{7eSME^1^$~FMNt$lh7-CJ-j!t%tvfxWl@BA)64B|bko{Vkf%(Z?Jopf8dK<(OKBw%Ih#Af!GX#v0xzwh3? zzst6xGQ{9( zPmI})7910<_GchnqrP+j_Z<`G+2+#JT~x&ub?w>lx5%uO+FVVGntdcj2i(6+diUQ# zkMm?t%JDBe5Qf&m|8sz6BX7=b6Gdmb8oiIU+d387{cT<@s>|zNj_RvWvE@lo>-qXC zSKm`tO8m#W-8+RU^Z=7#mgF+)gV>6IhpcwjzfSVr#hI~Ws}qdlKP!<60U54HaFvV+_F&_}oUH@Co}sD(@4WmBhOED(X1% zS;h(e?`%`$7LD!w!=b102uHZf?0i`*i%LWK)hqGW3~SxWx*^N8!YdlDs4!IuLBW8) zAdHX^4ge~+L2W#SbD^ohiRl+`R^Z26;lMPn}@#o&$w3Qc9;!Y=dwpKgE zg#ehYUWlDwGs8=2H{IemqE#EUkfya^!F>CmVqg>n=o)G5f%xV`%>F?+EnHovVXmy= zS7e=KW7#6l;Z63MiC?OBnra1Zd|U7cspt1wI7WExIIYy4=3LmHC11{uzw3RR_Z%a~ zW9q#3|IaVZEdzo2`Hi?}V9@uYbqyhL74>f|*D#Xzx{P*S0IP|p8(q8)dCb^A9Yxf& z1`<>^-q|}6d@5KDe~d?r_AA@z@x#AGM}*2fuXWqr#A%4O&9dbOZvGSkJ^}QV*A@O7aZ4hG6;# z-obQuTPZqJM~uq!xZtQBCcZpC@q zCS5j3%}ebr-aEgqDh4#|ZcY;hJx}w__ZC$MW6yV(|5xN1`qwjpZ}DyXSL90mfJ9|^ zwJ);0QoeHediND(JCJJLTVDOI!w2XPuu889QDNc}+qU}$^$T`lzh`54@eb)*R{&ZiqqsK4ZRcwrVbF+Xx^M-%7ifz%9j?I?@DynNy zG)=TFt2F!7lA~$&l^Irsbl9teH zcT*#W0i*FlE;_b-`TeR$#SqyX<{d_lX0P9s?f(}Y?flCwA^6tZ|JbDty;KosXTG50 zQ`^mn?eN?`Q!xxB32!WJYF(Z>XW?OOHx;^XOvGozG|(0)-^s$}9^Dn_ERCJHrBYU* zvdGpNlNTk6RSk5X(cQdp^%Qi|;s!stVz|&NJMH^(w?+78iiCh!xuX@v?M+mz_BUVO z+l)!YRPSPYr5+@Otz<&5FIXR)k8}CQE9tl-}W-d2Gl=XDx-c50xooaZqX|h@46?GTbJeVc_q%{&KU8l z%hntle79vOz3sX1yT1r2^5~u@W5de$|aHd~4MIxknp8;(B@Azdm7= z$h)k6yD+;Ol~ez9VMM5!4L~0w$jl>CpNZ5tmgR4%-hlIUKmYj@LT6X}qUpKUpzR`U z5l`;c->?(^txA&U;K^0 zhV-aT5OdUs0}&D`Xkax=O;>-(F9&?(7F z-wabzW~)PL7Gd}0!+opX-JF@1@C)&lJ17#$I3F`g)^PPK(XV*Jn_G0g6&>iw)aKCPgAQCndNM$q2N z1FlkOs1>Sa6h&@Ilm{j?DmLpRtlWEq6YkTr*a@v0Q5fPbi4{6Y!&=ej$S6koVKU#c zYF(I>FKP-EiD0Ed!rQr6zJpfc1{JbRI$3|^n%w{NTq`NIi~XwdGF*CVlFqK~6GXiI zCz$?q7wRgvbc~3YRSH&7qP?&v`AFWo{ep&8wkqPPqqLo-iNMpUo1E>xidf4D(m~T= zD%XS99k$Z~wPlrJbQutU<|YH^9Pe88DyCO@C6s zZM^eO{uF!WuuattuDX5F;s2yT!@t}Qonr0v|8l>q;ih=)Y=!cVO5G}k4=Pqmi0`o- zPamk=$fyCa6|y{`jbrM_4jmNu6Es@(b(fbZw7zJb?)R(Jc^$0>ZqINOyB`G>sCJS&uCBfDr~S0qdwY=WFp?mTVEerY2Ahy8MXwwI-83=>z_5=pZA=ALVSZNib!1OjGzUu%_?Zfwu$Eh z*0!*lqq2)1bL#t4w5?@4oj0g(!%c1L>UA_em9mnzf*pw|PY$jzN>YJ|mv2%Yr9=aX zGl}6*b?7uVxlX>AX#-a;aTW-^EU?$V9P@O8MO3B|=_#;hEw0rcv~z%s%M2W?&sZzv z7JU5Ag=^1$Gu->}!S|LVnDga77w;T2UxI`kqKG4^v)%$@^+g9!N{nOLGRa9FuG|HQ~WN5U%QR`?~YK`ZfRxno$L>*@l zZ1&2wRtJUSKs)Ymg15y}*B{BtW5T14Dg*XSJ*;%r50Xg}{bgIA9dkFP7v#^rq#e0r z&&WE%bra)AA6a#jq*&Nm8+LsGKwKPR+{4rohEn&FO)D)fT60FW)*<`)(8B%7sg1XB z>(!>=rLT%Jy!!j2)=f?bXhYFJUk_ke1A9*28|>X6k23as!)z@pA4-E9B*mBqNjSH^Q1*v;AZ|5i-dJ1_0{iP1}0T_&2T3{9^zny##;(zg3nYTCKXAyJeKsfrc} z%5;c^^!onA2mWo@8G|+_Q-Bq>@|N>!%tRY(;^BhA!bKTCY}3-nKQi7rdoGm_{JpZ) z0wZ#7p;X{dXqAi)Qz_wMxtofDN3IN2=6Yk~vOm&h3|O`%)3fFY!dL+W`Q58zjix=q zQq(*xGwBwKz@vK)aVMdpUj80JLcw7nf!-BlIsT^EHs87vce{mPw%HC<6*gzWjF7KBxd6jRa@hVRdL?*(uEvIOpIZh zpoFPM94T7`a`_aJC&V2wF!&tUS{n`YnJHbJR~?Fs1lD|c7RwC-T7**MNC zlxZz5VN-7Z3`qY<=0eFq^z4JF(`RWdn;QX@O-=E*N#A(=D@OV%(Rx!j=*D*Fly(~@Y3-M8A?0Ayuf+0`m8#az_1+FX(9I%QGeLpqBio72xyDY| zGP-Oc(_c|QbPOB@-ray8`EJ3@{~w=#m=^5~t7 zEtuDVql~u@C1^BhchmMs@$m{zDbWc=yFhv*3GoaUCPeoJ>2s_HHFN$}W-pA+Gg&VP zkyU42&9n1y1x%m=LEDq`FL4Gj7Myf+O|vB8+v;Dt#O!Km{DRn1e*Spb7LsvLDX0O( z5ZkMjZ}qa6eAJt}`VAAX&CTbczrEh=H0<+JXS}MqdCWN>Q_*R&lhlLVRx8vSJsdUI z$;TZ!pZQq;1lv7$mItVNPZ|U|8xQM{TW?MEwge={h#SpiGGt8!I-cl$bQs?vhl7N| zWYJC0(U@-1A{}cDQg?${M^nb5ySYTR-$ACaw6dYRo@!emFWOQNeREk6Re>ptCg~J7P;HZNVcYq~0v$i=BzyjQ^l|(w7_-;i> zmF`;|wA>5c!q0%_J=v4LpG=(SqqtZTa(`R&Kg+dHU7-sF=nIAUOZn|-ga(+_hM~Y{ zyWQphF6gq%mriz1$)T&w;WZ(veBtgC^x2`cfM@GI*R8TQOP4R+dH-vl<2e;RXiK*A z%Q+VQz^}=LlGj%!2f0DPtf9#ro#Digz>aL?0`ALtX;Ur893BqKr>2FVL-oa4p|mnn zJ6O3M!OU9qMoqbaX5hxV@3PUH58SQn&_epZ0@Lb&?g=YD?=B9duS=fa*@~z2-7#6F z?T|N?_WRBS9vGB?J#U-iqTT}tG&uT#nPhRq*~)U9Ze+EW)=Whw{EbDnD`0FbZo?5g zU5bD7F-1-G8{u87#d3N*=HztBc4E+;020#BPwf{bE7_m0aYM7Mj6x1sTs(H@xr4rU zY&wmk-P|-6Wyk=GV_rIx!MD-}Wtiu7B-|ONM6{QpL-GUuw7 z-w%!*=9WnwdC4A@?((yu)oRF}fsBIP)_Lu|3+Ei*pNnYFKex*8P2@7Bzlg9-{{^xN zb`PYa7^kwH9H>$>6KUxzAHyZQUnFL5T~6pUF;R}SCSNwYQADdgne@TkEnL~CV0*JZ^oOpGrbZ{4;^S zv?|~n7N>q7uXH+UGWMaLc4;$?8^P;t2L{pRV4hvJ&+13PyX4SDu1<|2i2crN*+Rt? zMTxMcV#O)>Bb!n)bUxSetG!bB#=fM`^#Ew(QA^E>oWuyi?xh6r(DJ@nDhQq%-~$h* zHdn5_uXy5=1V?PRCru;WP>nISi_?Z7sn`B*7eJ_DpCf}=wbpHt{w1Mux1XMvuBa1} z;ZIg=bhQ;OV1bsHM-Ljr{kSWIKd56hu>wE#KkMgO2WgL~6Q}&bmPc(l?YFA;kon9> zQ4*yrojTcs_L=HOtmSpp-`>)Vjop8O?sOwMd!RgtZdYM<@O?H;)-^jW)-4rkMUa`A z!PV*^8$}>}y%QHgS{4DH4BWE8HIbXz;)_-$$!1Nr&$q3Qg%QqvKbnsx>}$>HrNp(( z)XiG-L_>Ut=sWV?lHedB^r3krB0JRBYM!ztWI1nYu1WHdX-pL40F>$IG=SgrAO zv7+v_(yNjRS08P!q#G)nsr!Da#fHKg9X)=6pC&K*_+Y{*fD=g5(^UKHTwX)H4r9$e zAxHpy`0_&3+Skg)pGq1qLr7O&nbr7e+HCVf@B0S}U=%!dNqb8J zi8w1mT4kMIeGRbbDb2JRI_Kd|>~#SyOqp2B7OY~zcn?pf7Pv1rqL&UDE#Pqr-cxx)3WSq~kyc$>t!0_7 ziDhGfFzxK5zO|uQ!F*V!rVMoc*+{@(@FoTo)SctT?)M{K_q>XWFDo6HMns3>I384b z9?5k|@v6s*!(mn@&C?#ZCLmNucHsWk6!o5Yp~#wE+}cWRIC@P+0LUuIa!erjf_WV9kg=&)az;U79>VD{j~- zr6{YBZZt=aC*K#Ua4*!AP&VT)+1M#4+ZR%P!~MYKK`H+JQ+e@->HC7c=;lq17n~)) z2MLYyP2;~D&Ead}sYwUEv%*y_GUoRvhw7~qxeCKcUHLj$Fq1QQa#$$_IG~I`4fh|a zv$#tMj}8A^-}wQ(R6^O$5U*6OTEZ@7P4d^F96&=J?x{zW24Nc}=tbDXAy<8u_ZGOw z5a;M6m*D<=HeWK9TV-#Kqa^B2U;&+WtbF;OM2~~H4pP_vhj@IdR_pOVd8I1ijtO^!e_#;z! z`gMS!KvDo0k*c2Mst7oXEco${HH5K$LcCdC6q!UJv>fk+ zW8Id&=&{!6xN%Pc_6^24V+e&7#*o^rN}0*Pqw*J|Mt8P#$3kG5?*PRZep&{%Ne)Ep zWgNFC%7A*w=scj|0(do1Uut!&~1sJd{0YH7;*QnU5UYWD}v< z#7CQ3alH!UsUX7UB$vZ&Df3Tt9&nS>z}VrT!pZHk=OUUxAMc;r1sASah+f2; zF~S|Go#6lKg+G*Q$Aq?%raz0oMM z1^v~>uG&QbSP#?euQRz(1a?Ngk11r;$ojSK;aB#y9p}H8DqbcloJzHNmiGCaot{jL z9zkg6hN82cw;n1FK9jsumKQGRF&Aw)iH^zQ<~5FNeI0~?=I_^46L1`G^p$RZzyuH0 z!TI6tC1X;VxCn++IBc`g@zL6ATwk%jd!0ZI^R-w|Z}9)|kAQar zD61flQ0O{r6`^?eUEwPJl$d|xcZqdjKza~XU3f|vWkxr6@HjJ5XMWtAcDu+VdmZ*v zgRC}6t8AD$Z3r^$D=6Zah`lPGZmS+%C&<>4{}28jl6AOlS+t;5U3wg$=s_^78m76R zz5ZpBAn6yj)I|ym>C+Vunj-{KQ47&z7EyjcV|tkkK^i~2=-!JjqXSYM#U+hYBnHOx z37tWW7Pyn~PrF+Gv}_*T*?~Q%&s&1(PlY)D&eb2h14`v2-`7b=+An%cf+Z}1xglb8 zgYO1LgFFS0Ss{js9k=LY{}@|sRv@|u6re1T30o*c>ZoQND<3Jev~-76WCz-$dXLnh zyT?5gIEAE?+po^!Me1cHsc-oba&SP)<oEX+&+H&{5+weaw2~awJFvO>wQ?LJJ4KZAQ z#=){k(IGkL@iCp}THg;#7e>eQtW;vHsL8@fd z)RS;^G=*9>ejJ+jc^`yyp&9q_-I9QI(Io-CBw`^0%`GN;(`ASBm=C+K`GYDd$-5$k zK#6h{*Y0BT3WEBak+gT)Ik3r@yuAjuHMDTR*a z3NYqRRZ#p&x&Q;_;mnyDb zU@$@3=b3W-b)AJW-p91by4tp!%?Be@S;K1OLUA5+_JVSS?@N?`(VMi-v^kV@oN6bO z4f{3k7OY$JPtyho^iJuhP1oMwrYnj!jqsK(*|E?WNqN`ws86XQ5P%8j!%XsCppY;o z8zwPtZza9JwIwJLO9)wMRBOmac{l$ZbH1p~yPi&r>wIySpnqbmM>(p-NPLSKnf6{x z6E$cDtexl8)+$0jx;qc@?%jR9xMUJ@_ajVq!=SH4ZOeiOwraI4Z=d7&=#wQ zKhx=o2Jqc=^W?)n;h8}m*36(HsR<4ELZ2T9A9`1)zX-SCBmCMz7Pz_BGU;Nu#s~0| zyGqg!8hXjvXUDxOBhh0t?2p~AX+jCq#3M&$$rEWK`2;uY<51DTmWNhg&GC%YBFLV` zW4JE++bvKnu&gpvD82d13-KRWYX{Sc;58}JPh%VPaK|AxJWoq3_) zeOWhU&3b)(1|T@LIoRdyBwa3q{b{!_936zh7W-3L1Q2oA|Uph=*7-4YDIOyTQpCc&tydG6sFqjRGP4f<6GSk|C5XKizc^n^#^ zYd`&nw3fp(N;T|pcqnQEyxoXkFH~)(ykO-)@3sIvt<`ibx6NE=M7EBIh;`3R!kI7t1rgHc)V8KpwX&k225`W1H>M z+Zu}Ti$AEhfIEh9sq_Jcg@YDhF?j6AJSynaTj>RNYLsE zAR5o|*OFKNs3=CP23|Jbnh$jhJX|jVwH?H?#eSvxYfs&2fLwBZYdI9^H1YSx(t78j z^-0NTX+v~OeHg{)7=Efo!^WN>7x-D3ijGs-_ldJIb&1Y>!g-FVSZA|dWHTNO7#VNX zdmE@7CfTxM1SxsHv816N==e0=4Ww7G2_$rQ?=w&WGJUmPOvYrcn@Hzcs;W`}z4bZb z7yvRuSxl;y_cJ826f z&@gm1DuQ{EmwdTGOAd8$sWVX7_0wiBHk8>Pvip2Nev(IZNE7o$MD3U}d{XUG;jI={ zgz7q~)Hhz?@o|SAuEejk*<21_?jK%%1-Bxu#hc=^z#}42esF&*tLf;;)I13o=-iCvr5KbYXEu5pY66=@pn z{!GqjD&)=G%)@<48H$B|$=3sDa(THxTATn7BcG3?u^rb(4|UDy7NT`opjy}lQ4uA{ zp*&u03O+AmKV;pIg>*0TnBORj?j{YfcIbCH{PqTnK!ZrH*X42?3SJPMUr1|FL4p?t zGofq5G=5blNbcx()!dSLpI6q~%5dyGWBh=gduAwK(P`+xl7`SLUx+iKEb4#hMa#AG z^up?B(DmQ+BIi;V*)!BXaJ2xGx*dLYgxo$mW#zBm%fTlY1~i-{GH}A8qrZismFdTm zqwYNMT8+^YLF#FGb8FGK$e!n&DhUid`TeWkQ*51Hy7; zTF47WkEM?UBL0iFLjT5Ft-(Y6{{qD0;*jFmF){m$?rfTPe$e%7&Hb~YY1mPHcyQq6 z#XNix?Tcb1j^V)2U{h7?on^MyV;;m!YR*zkFELz6!qU@5&Uy$xXWdDJ$Dlzq*JZrs zpc&89sA?>}B!;ZDdEETN%-}{FN?I$yFKvnr)l>z|lS}#b)GPCcU;WT@A7xG7(#Yz8 zwSE-eOZ-7TaJH7P@~wH(H3cMjw9~df>>z)V)AmQa_1qV0Cb?4AmZI8uq$Ih2_#S2n zwbX`UeZj}-K@`_|%u6NREZ9I$y#*gmem)(k*|AYFF1Wm@3ZDPEX-CY0qIJDpOwXVp zW(ucn%PT&0*?j^>4p;Xd_-3w732@cKA{!ZuJUXs@-$p3Nk;3GRB9}EHmw!3Ay25j(+o+I?vx@~$Z7VR*>t!aPlP6C-&eOc4fD+S8s-CrOo zVH0D{qkSM>hh@3YNq=*`o|J_oQR!#}{JTvP0DY3G#-vJX_^DqnvMSLMT@tg;Ql%ud z+Mzj6HE9V*eD#9BMi-dqb5B#k^sDq>b(4n5xG*o~AhGMnre5625dNPBrt(+PVBV!t z|0iku&rl1dr)vqP0hAHz({7?{+pes4rAVjYm(p9TJkEu^BU0WinqH}9J*#t%+%6kO zv*Y`$#i{xiLs;U#({jotf@Nlw-+9+p^}F>>YYYWT8?;^Q&o59`^2Bc1g*#g~j9Wbd zrRYfd%K=)-(}3ETAn#QW%1a*ehG^PEkZ%oo<&iVFwBEI&oRHKUqm=3N#HdxS@PNf7 z`ZXS5ClEsXsvWiYPScF69t#mlZAqwM_DD*Y$g=GyXYm#G3UZCcOy?0rdmN-%5rh3f z8oq^-6M-d4aeCERA)8}>cde(A2r&i6W(Z<2OMV0oZ9!KL0mtQxbtrpfZjKcp8l7>B z8v74Gs6O<(XZUa{D#2h_ag$~Yk}^H7C4dPz-Z^Oq*3(hW2yWQ0Wia01yp>(5oc_AH zFN^AMIAlvww%DeJ{;3|x_*-r{XrJH^KP_GFLj2}4$f?ltjQHwjnrm3T%l9h~HyKvN zbeMv8a<(uuLwssnx;w(k)ggiNj{|8EcnG#t>_hfbn8#vDBcvJ0ncgchfvIN?J%Eks z+IUFd4rMeJor$&;>mfS1qR6@-!3mhBl9tobErZ4sO@({tG0RGVJ-%O z@X15l8Mhgm5KWKi1<qC=k_=_&Z?BUcOfDz5F|E1$Dnb#X$=#bAaertE=bix04Oy zvy3K!w|Uz_IpV+MEa7i*R*x%t^fx)9Q~xa)zMF{z4#^h{wI4dj!|rUGe;18-<4Q*- za;g`RsKf`0sSvA!(4ggmxN<5Df6t8<@GZuZa7DX<_tffm>BDpIK{{Dqmwdfj99&Y1PDiYW;%zk*IzPr=MTn_^?v!-Xp~pVBg0foU4n0+0I(R1+u9(=es{z!& zS=94a(*n^$Q41sG+0)T~q;$&roQUb&QeQK417}p}z#u3R;*&*9dSZspaD4Nz$0*QI z%c{ty`8`t#nSIr4+C*xO!%~}F1JuL4{oDE?cupP2Rl*HkwO# zFd1mnYP#z>OOh!+YN;sNW9~pO7-tx77sS0-=c=1JSm-y=Pd$QGLWa#9Jm-SEDJ$O@ zYP1L8z`q>z{Vy2jd6GWkAx|CoH%aflb1({Ot!+NsC`Fokwx-wf&o%n;MSO{Y)4o!W zH9%|d80rm}8Wntt)CZ3nJWw#aW+-eISK9q1-O@-L5GqQ?j8>D%mM)Ykh3coPfn_UO zJdA8uO$W8>jp!y;aK++vSF%eQf<`aTlt_J7vSFqLGobe8d(f!~Za%hmza_nH(v-2B z+9GDKiI_oqX1-pxLJrO3K2FtttkIpj#lTTiXQ}@Utc9*u)Qatu%gfu1YLVbQ&8K?_ zMj!Q+uzM(WzHL!7iqP|8*?$G;q=gb?SC|T^*ED-Q{L8YBpdizsK-g(k(yX=+zB;}w z>ou#dbd>rOXlEI4U1h7fhUDeRWZlG3*R!AeDzJA+eXX6)ya9dvmAg8()T8C$m zsR(zpPb&KD$lJz2Py^lo{aH0@Z2SX5mj(0VfX$W| z(u#e$nR`%UpD@-5K4qv)N@Q|d583k!ky5uu>7E^@TTUEQG|RWK_>HShqkpyH(oA4j zMr81I_z%=-XV*_Rl_b&s`R9`7S!)F*9%}Vh%UD0VKxY@eTzQ5fs-BTqh0ZgTL*k%k znCN9JEys~JQY3r`N3cgl#8BrN2=H21R`IHWUWfVGB){a=2#Fn%#suv3tE=ZR#eAw> zU4GmyF!jJ;`R9pdyJO~0OdWo}uS48mhj+5f3nXIvH zn@npPMd_P-nF$Grfvm>^U%C8v;=>@rdcSBP9wO`3KC*F}p4k1-zyo{msBax`PNhFk z3C7X*FlZCo$FwtMrzoA&EY=eENULx5moqrvD`d>IN!OjcHzNe#?4_Z$f7zY#oA zRMA;f&^f}j6?ahn*)*P!F?!iVq2{V=JtwJ|%i$XjwR;&QOkeONfQ19C>FU&0(omTz1i~wn1R@W|2`Kwe zxx~TB7g(Pw#z`K{Z8s@$yUi!2W%&xBL$M*QrORrpIAefEn+$=zXHq{UY2P zDfN5W!rgcBV&#F3<`@U^o|~R3kpQ4xu83N}iEgd7VWWK=>MF)=)mZt54u==4YkF7u zqvG{~fAe{%-5z-}HTm#Y)FNwy?+|S`6c)aa>5$~M37|vV6>DJ`sqTHTYnUWaT{&Hk z%MSJn3^=oDkPkR`3&UOfp98}B*8%YyG8O*U0m=HcFlxx6Ye)eIW@Wl(w6x<>@yx@jG%vF~ME1K9cO0aLHwPGif$HNcd)$Q zmldkVk4!X2TuEx4ESAHjwPc12Jh>GFhAMj0Y=uqcIUViPHxh!M`zh3*%If{xNiu#C z{#n?OD>iN_R)sIebEvKT<$!;1C4r~dINrOXt+l_b70%1bto;)DL6q1|8DpnTl+vJ~ z^8zagXN-@Dp`xe@FNBg$R!>`Kfamj3W!s`kY=1|v{`p{S_jfoWP7+k`;_q-qg7R@XCwZQ_Q9k+4TS7vy6$(&}1fl$!#kS~f^%TB@iF|dNyd{qA*c&OdHoQMV z@Fj~x&R?0W+{u;aSj4M8hpaj&Ffyfs+D(@m(XJ50std`)vu8Uj7&XB(J&sIF zt3stfbwktCwf=pBPTucozuM^w^ad@R50tMk8TS#{3~UKR)9b!#E|*Y^#&&}V3x*PK zvuC@a#p-f+uPyf7X^H!7xXF)_rxL5xvzW?bl2SUYtX*;V(Ia0P&oHuayiLpwV^2Hw zvf9>>o;67~thxShdSu5$=SI5IV0sH9Yr2`WNbNI^y~NH|UgLC*y71PzDZQEw^$NnI zemB2$*nWezCaT{BX=iRu@tKwJuFD}m*$Ebye8gFdTFr8Av5L{H*dI0S3&IKjXFOVy z!Hi6uFNkHL`r-@A$8Kgd^X&b)j_Iwo`mU>Ee)Gsd&q`|v)Z5VDyf_~YMO+1CmMS5{ zZ`mSe73mh5JT$YMDd~xIVLuz7@U&!lxH{3SXxSCvib@Jyw!9!Ku9U8sEjTgzb&wi= z;J~)S)ye_iFkAS3zJjOgD+P_Vp1_pnII$0epI>0!w>rItdUi8l|4(~Y9+hPN?wgvX z#c6CNTimkI+$-7)(S(+1vxwAOaVaewQ*j}8l(EK~GPPWDgUn1#(OeQy5p!2mrg0C2 zOaT=YK>?BNQq4WT`Q7vT`<`>}@%`(2Ufu)eJUs8``8>O_{>)8I$}mIEOZqfM;5YnN zF(hcaA1O5vyooG)Bgfz0@D*7ApOGb$;|ThKERUOz6bK0=BD=ZD<|r~$_!177Q6I{A z{pU3Pg5e|bQCr|cdzcIK^yIysHgZc({fKbl%yfH#LtE+A3Zug|&{6w47$cN#hIV(O z+0{q2L3VYBUDfikf}ctx(?R-km@{+C`@OiiJ+>0Vt7n48c-6!O)V#DTyyjJhtYME< z7nIyh?LJ!RAA6~wz74~_9b`J~%sv<$XbN~gT3agU?Rq+IENR#Cx=^lh(pf25+4ry5 zUms|qQ7WuTVS}>Hr6r>cMCVIy1tTe0S!2od8-H|bxc*o%zfP5k3EH*W2SX&hz(?>T zgP@0Z{Fu(7f(tKBtEpKAD5~~Wuvmi}9~Orl-!i$YHz%$IG~3;RG@Q<7XKXt^Q*95` z4f;L1^j2DF6s6&;jq@apJ`Vdm$ zIMduB@;V%9pDN_sHaq7hv$W@hu?V4`9g-xSSOw~C8U9B_z5pn=1bhoH<9lg z#4p=x=hE+Q;;X_iOPq`7^c%OVA2+%h)%}5=A2a5T_g*Ch2dTmPZjc^0lSkN>7V7E< z%(vXG2%i)qt1a$nP2AZGjfV$aDCQ)dE7H~T87JCa#Sr*n6_@H1*`}0iGJC2L#%>hT z#Ejz~qPn(x)dbDYnt&iX9{!>Un5L8n_m8AiqC7}M6E>10i<{7mD0Vi9Fc!ITxGkQY zpkiQ4K!q%-{EA=}eQRlT+c+V}rT2|d&w=r+zDcOsYewVbQ|W|-pxej&=?`OE?_5nO z-hOZ}STTIEIxij&A1p1RFC2rJ_kg|$;ikkP2S`RvnzV4+@Qjk$kUm|g4td89yXGIU z2d>vAnI7YEwveNiK}YV+i$KX|?;2s9yPr=5(=cIsO2GPIE^ejgf?&89NWnr^_s_+@ zgc}FM!&C=R=B8%7*2(1W6IFDxgOd=tgf71mW{q}Po;55+0}>$ejRoJ)@^MCZXe8QI zlzHXmVF#*YkC8VEz@B|Agud=it|1|Qkx}$oLpNpq0Mz%bQa2h~9;{KVrWlabfcZh9 z74~|khWhC3b0sKTnseeHd{zv-KuVQa^wmQBt^_tKzy5!R!fyj5Epp+XlJ zouHR9oNw2^gFM{X4z@!JtZoc$ufk>7)P8Y{WL8^I4p*?X`-*DEwo zKg>)iYjh&`$B) z6zxB~{_E0}zXUS07lm>ex=NBFXD6kjWfa*cPbi~l9dMKVm5~uCl)S4?vO!&mImWQx zdECg(xiai|^RHlfs|V25nd-pQIp++)O$E+^+^TVasM^NY+TPzK7vEb#vxIR#YM+yB zD`C3ig<-gNUyV$Xmjxh3h*RcQ{eThbPm)w3k3PHi=4Avfemd3Ne5lE$<N&G%28;&0|unL{dkJJL-{pn7wVG{&K1_`APElc9i zDQ>XfIha;IW*KEK4`?;V!X*R%x z`IdwnZYG0laKMCxJth2wscK?w4U2VZT@zD3a%Nf%Z!!;6!% zE-7!+9ycc|I+&^*SeUK-UKnCY0IaB!0D$kjV>IHd3q}!!<-z^ex56r#dfM{_QC z+0DJ+Dr(m!6@{Tw`nUx;`pHP&jJliL5}Y(WQ^3QjfoGa(^n^aL?_ie<$@n`_Xu+Y z9*Nz!28TNkFbc!A)OL(GPfw#yi%!b%y5R#l#=G#dBk2Zaf{NY#hc3@_`#|wAyVB5R z%57?)A=c!hsdt9lwpU`NGP}G5zATi%wGK`6)uT}A@t;fK!j5R-?$VHOn9eQz4~GCp z&bx}sw@@@SorYf{j{_Y2vE_E#%5^Vbe=@sS)BcL3l#+G=PA)H_fO=Lf{LfPZz3aPm z`}*yPyS=w{&3$rxqu&c>dMJ>K@+%bulU#hs(T`!e{s9xIswd%qSlDf$OteJtao_(c zdSQR)Sw8{oh{XKRQ5P}z4*+P%l#RCURx-zA`P8RH13=fYE?XzO{N9<3r z9LjiQ*P(6^x!j;kT_MZWCkiJesY6wilSIjcmTZETuO$r=xye}aONw31K+8<~uZhEu z&xynOgI@1GCyQj0I|2gJwa@3-Y$$f(t|S0y&NRhpjkgZS_n(?Oq54`jD~`ip$uGk& z`8>%bT2O^O$RUbpyt+Qb6-U`boL0fZsm;;mPvXJipl_B_WlFMmK*MwWBZz{FD3&11jt{ZtzJ#(Dhu+3pBK zr-CU;eWt!~De#D9h|hyG&O|jiU|+?8Scqut8l4iB?k^QS8SaO z8))u!i_17X8N}Jl{#~{9IsZp`;GeQodrg;h_{;*Iy*v{UEO6|6Aaz08GHgqGNHAx# zD-YmnFKTU4x=4dRb`IAyTXy=+ku}A7JD3t%|IFJr%=sJ%N5qHUE6hoS5c@VgNP@7h zF6<0ayU<2O%1*Ft{rRdsr0Jqk_9~rQcktjNSDQekUUVGy1d*o9{8hic1)>aZtba(U zJs^JMA2Qg(4>Q+$K>2}%Jw|US8B1_Wx*g;V0`DKRAEm}H?${APQ?Js5zQewi7~!9y zu!A(=1@Ge)-Ci!layn4B9N7W;J*udES>0da9?r($mtT$mkkK5X@F)^r*XgY{IlpBaBTf!sYK^XXj}0oeR?~KvxmWC>*d^xo zh}zna-f5jRf@z)bxATJB0T5>xna z>{`fuex?_ucklS0)Yh@zyQpmgr??ik;97_W*L4cCO*fGn65VZimR^`!rM2FBMBWz( zuk{>q=Rw;LP}9Uo+CWMo*qlB_aldV`6mYg9aK5ECh|{8&#)Oy51)KcIiK>>YbU++w z1C;b^F5=Aij!lu;nfRJRJufi@YQ>Y4`3y+#Faf7CeLd>K8T2`2!I@#g&4BpFP zd2>nj7&>hd6Rq4J?W13Z9>@6yQGWO%4x3CY zd9^~m6AiKlcDkXsx)$5m+@-GfBnavjy0UZCF?7Q`xty4+_X&U&)eLcONl2o0buwk} z`RY5YJU}$@Ia?Kvl{Pq#DfT2@8bcV(;T)oi7D6-7T#CGnN93Ts4+KzEA| z&5j#H+Aon-+SCoFa2#eA%KQkst~R(z+u!T2kvQy=Y|mjq>v@x35aV^YGjb5ob~){& z#qJGl(6gtQWho_H&)Xl#pxn6}l{YH4ZiUlUbu*U&`kUg)=lAt)GdZ`nL=jnrY+OQM zu8FVJ@Rwok?Uv|cHf@b0KS*pO>SaJVGrdD5nBa@39#wA>Is};w1(VFDQbJxTzDZYl z*ZfmU&X{R{j!p!RQAHYGUKCzN7&WX~Vk87zDK!FKhHYF@j0^2QP^?!#w4=3kf^#kp zJtstMASdjl3{t7BXB5k2cVudMFXSPIZ__N9Id|s^lPbYtyRsqfO3cXWmJVSsX+;)f zGn#z^qcsDLU}yLjJpH8i)I+f!ejz^zeC2GJ`>q`<&iQAAXI;pM?rG6(48>IK5u+uc zY_U|mhL_qS%)%;`Q6(5a{4$Bj3Bw=t^02-r>)vz%5c?B7WAJX|=_8WVNkzYQ_MK*8 z18UyK&BlXOF!c!~vw>Xcg`E@~_K=S?h?wwXi;MXSe5_PElK=5thpJfEJXe4Q3}7fb z>}1l9h2Cth=19f^;S$B79yO^>?_@bfvElRqc(X!l51bDF(kTATX35sifvAldufEI@ z_#BH;*16VOzRCOB+Ud}$*(D9{*R|Zd^$}Yqyf-uEQzCfNQ5K43_$>28)~Y z?gKOeKB8%2izpdgG}yKY_(ZaQ*d`QX>Cx%$hHPm=Z=@ijmC6X<{0AAr9;`7CW;h6U zFS&W5_cD4{HblR#t_H6~y?ShJQuaNlDl~sq)uN5n0{d@t6VO0eG~D8lYGJd ze>@hmK_TTIp6|Q&Ir#UF`(KZy$_huGHL7&<@BidWs_9=(n(kBNoYrmC{?FgMv|Zu; zVTk*7{O6C?e)tQ{{kPQDM7^e5Yp>HkAt+`IK>i(**Rc9?rNckW&zg1m9|BnekTn2V z<6QrsvHxcwlQDR9Xx+N4zL$PEeSM8%|I33|BL)Ame_vyHYkt%kDOhvT|BV-}(V#UN pv_^yeKhU5x>hr%veHP^#G=eY!A6G}Kb&BiKxvRgFow;%EzW}^fIBEa@ literal 0 HcmV?d00001 diff --git a/includes/class-wc-install.php b/includes/class-wc-install.php index 5717a539474..a6bb5d2b328 100644 --- a/includes/class-wc-install.php +++ b/includes/class-wc-install.php @@ -1137,7 +1137,7 @@ CREATE TABLE {$wpdb->prefix}woocommerce_termmeta ( } $upload_dir = wp_upload_dir(); - $source = WC()->plugin_path() . '/assets/images/placeholder.png'; + $source = WC()->plugin_path() . '/assets/images/placeholder-attachment.png'; $filename = $upload_dir['basedir'] . '/woocommerce-placeholder.png'; if ( ! file_exists( $filename ) ) { From 1e563aea2962bc2b4f3a316ae9f6918c4903e0c9 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 14 Jan 2019 13:55:12 +0000 Subject: [PATCH 067/401] Use wp_get_attachment_image if we have an attachment based placeholder --- includes/wc-product-functions.php | 77 +++++++------------------------ 1 file changed, 17 insertions(+), 60 deletions(-) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index bd22b667223..3f33637b2ac 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -296,77 +296,34 @@ function wc_placeholder_img_src( $size = 'woocommerce_thumbnail' ) { return apply_filters( 'woocommerce_placeholder_img_src', $src ); } -/** - * Get the placeholder attachment image src, falling back to the default if unset. - * - * @since 3.6.0 - * @param string $size Thumbnail size to use. - * @return bool|array Returns an array (url, width, height, is_intermediate), or false, if no image is available. - */ -function wc_get_placeholder_image_src( $size ) { - $placeholder_image = get_option( 'woocommerce_placeholder_image', 0 ); - - if ( ! empty( $placeholder_image ) && is_numeric( $placeholder_image ) ) { - return wp_get_attachment_image_src( $placeholder_image, $size ); - } - - // Fallback to non-attachment placeholder. - return array( wc_placeholder_img_src( $size ), $dimensions['width'], $dimensions['height'] ); -} - -/** - * Get the placeholder attachment srcset property for responsiveness. - * - * @since 3.6.0 - * @param string $size Thumbnail size to use. - * @return bool|string Image srcset for the img tag, or false if unavailable. - */ -function wc_get_placeholder_image_srcset( $size ) { - $placeholder_image = get_option( 'woocommerce_placeholder_image', 0 ); - - if ( ! empty( $placeholder_image ) && is_numeric( $placeholder_image ) && function_exists( 'wp_get_attachment_image_srcset' ) ) { - return wp_get_attachment_image_srcset( $placeholder_image, $size ); - } - - return false; -} - -/** - * Get the placeholder attachment sizes property for responsiveness. - * - * @since 3.6.0 - * @param string $size Thumbnail size to use. - * @return bool|string Image sizes for the img tag, or false if unavailable. - */ -function wc_get_placeholder_image_sizes( $size ) { - $placeholder_image = get_option( 'woocommerce_placeholder_image', 0 ); - - if ( ! empty( $placeholder_image ) && is_numeric( $placeholder_image ) && function_exists( 'wp_get_attachment_image_sizes' ) ) { - return wp_get_attachment_image_sizes( $placeholder_image, $size ); - } - - return false; -} - /** * Get the placeholder image. * + * Uses wp_get_attachment_image if using an attachment ID @since 3.6.0 to handle responsiveness. + * * @param string $size Image size. * @return string */ function wc_placeholder_img( $size = 'woocommerce_thumbnail' ) { - $dimensions = wc_get_image_size( $size ); - $image = wc_get_placeholder_image_src( $size ); - $image_srcset = wc_get_placeholder_image_srcset( $size ); - $image_sizes = wc_get_placeholder_image_sizes( $size ); + $dimensions = wc_get_image_size( $size ); + $placeholder_image = get_option( 'woocommerce_placeholder_image', 0 ); - if ( $image_srcset && $image_sizes ) { - $placeholder_image_html = '' . esc_attr__( 'Placeholder', 'woocommerce' ) . ''; + if ( ! empty( $placeholder_image ) && is_numeric( $placeholder_image ) ) { + $image_html = wp_get_attachment_image( + $placeholder_image, + $size, + false, + array( + 'alt' => __( 'Placeholder', 'woocommerce' ), + 'class' => 'woocommerce-placeholder wp-post-image', + ) + ); } else { - $placeholder_image_html = '' . esc_attr__( 'Placeholder', 'woocommerce' ) . ''; + $image = wc_placeholder_img_src( $size ); + $image_html = '' . esc_attr__( 'Placeholder', 'woocommerce' ) . ''; } - return apply_filters( 'woocommerce_placeholder_img', $placeholder_image_html, $size, $dimensions ); + return apply_filters( 'woocommerce_placeholder_img', $image_html, $size, $dimensions ); } /** From 5f2bc29e3db05191ee0da8c3a0829d9fd46237dd Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Tue, 15 Jan 2019 10:17:31 -0400 Subject: [PATCH 068/401] include taxes in subtotal when coupon price includes taxes --- includes/class-wc-discounts.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/includes/class-wc-discounts.php b/includes/class-wc-discounts.php index fea4eead963..c4e931f6d7f 100644 --- a/includes/class-wc-discounts.php +++ b/includes/class-wc-discounts.php @@ -664,6 +664,11 @@ class WC_Discounts { */ protected function validate_coupon_minimum_amount( $coupon ) { $subtotal = wc_remove_number_precision( $this->get_object_subtotal() ); + + if ( $this->object->get_prices_include_tax() ) { + $subtotal += round( $this->object->get_total_tax(), wc_get_price_decimals() ); + } + if ( $coupon->get_minimum_amount() > 0 && apply_filters( 'woocommerce_coupon_validate_minimum_amount', $coupon->get_minimum_amount() > $subtotal, $coupon, $subtotal ) ) { /* translators: %s: coupon minimum amount */ throw new Exception( sprintf( __( 'The minimum spend for this coupon is %s.', 'woocommerce' ), wc_price( $coupon->get_minimum_amount() ) ), 108 ); From acf46fa28dbc5a1d3250db13575f3f2a33a1cd90 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Tue, 15 Jan 2019 10:21:21 -0400 Subject: [PATCH 069/401] phpcs sniff fixes for class-wc-discounts.php --- includes/class-wc-discounts.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-discounts.php b/includes/class-wc-discounts.php index c4e931f6d7f..94866123fe0 100644 --- a/includes/class-wc-discounts.php +++ b/includes/class-wc-discounts.php @@ -968,9 +968,13 @@ class WC_Discounts { */ $message = apply_filters( 'woocommerce_coupon_error', is_numeric( $e->getMessage() ) ? $coupon->get_coupon_error( $e->getMessage() ) : $e->getMessage(), $e->getCode(), $coupon ); - return new WP_Error( 'invalid_coupon', $message, array( - 'status' => 400, - ) ); + return new WP_Error( + 'invalid_coupon', + $message, + array( + 'status' => 400, + ) + ); } return true; } From d654107cb012de2d2625f9f9dcb33feb1f89a134 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Tue, 15 Jan 2019 10:33:57 -0400 Subject: [PATCH 070/401] include taxes in subtotal when validating tax inclusive coupon maximum as well --- includes/class-wc-discounts.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/includes/class-wc-discounts.php b/includes/class-wc-discounts.php index 94866123fe0..a00499dc875 100644 --- a/includes/class-wc-discounts.php +++ b/includes/class-wc-discounts.php @@ -687,6 +687,11 @@ class WC_Discounts { */ protected function validate_coupon_maximum_amount( $coupon ) { $subtotal = wc_remove_number_precision( $this->get_object_subtotal() ); + + if ( $this->object->get_prices_include_tax() ) { + $subtotal += round( $this->object->get_total_tax(), wc_get_price_decimals() ); + } + if ( $coupon->get_maximum_amount() > 0 && apply_filters( 'woocommerce_coupon_validate_maximum_amount', $coupon->get_maximum_amount() < $subtotal, $coupon ) ) { /* translators: %s: coupon maximum amount */ throw new Exception( sprintf( __( 'The maximum spend for this coupon is %s.', 'woocommerce' ), wc_price( $coupon->get_maximum_amount() ) ), 112 ); From 0f94bed1ec53d7668a560c9088db639fa066a354 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Tue, 15 Jan 2019 11:33:44 -0400 Subject: [PATCH 071/401] only add tax when comparing coupon against order --- includes/class-wc-discounts.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-discounts.php b/includes/class-wc-discounts.php index a00499dc875..8b39a8d992e 100644 --- a/includes/class-wc-discounts.php +++ b/includes/class-wc-discounts.php @@ -665,7 +665,7 @@ class WC_Discounts { protected function validate_coupon_minimum_amount( $coupon ) { $subtotal = wc_remove_number_precision( $this->get_object_subtotal() ); - if ( $this->object->get_prices_include_tax() ) { + if ( is_a( $this->object, 'WC_Order' ) && $this->object->get_prices_include_tax() ) { $subtotal += round( $this->object->get_total_tax(), wc_get_price_decimals() ); } @@ -688,7 +688,7 @@ class WC_Discounts { protected function validate_coupon_maximum_amount( $coupon ) { $subtotal = wc_remove_number_precision( $this->get_object_subtotal() ); - if ( $this->object->get_prices_include_tax() ) { + if ( is_a( $this->object, 'WC_Order' ) && $this->object->get_prices_include_tax() ) { $subtotal += round( $this->object->get_total_tax(), wc_get_price_decimals() ); } From f68a547f5355ab1de7d72b19f5bde9ec56fb8608 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 15 Jan 2019 16:49:01 +0000 Subject: [PATCH 072/401] Don't mix session and customer data --- includes/class-wc-customer.php | 35 ++++++++++++------- .../class-wc-customer-data-store-session.php | 2 +- .../shortcodes/class-wc-shortcode-cart.php | 2 +- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/includes/class-wc-customer.php b/includes/class-wc-customer.php index a3dfd8b08f6..85c04df2dd9 100644 --- a/includes/class-wc-customer.php +++ b/includes/class-wc-customer.php @@ -137,7 +137,8 @@ class WC_Customer extends WC_Legacy_Customer { public function delete_and_reassign( $reassign = null ) { if ( $this->data_store ) { $this->data_store->delete( - $this, array( + $this, + array( 'force_delete' => true, 'reassign' => $reassign, ) @@ -817,12 +818,16 @@ class WC_Customer extends WC_Legacy_Customer { * @param string $city City. */ public function set_billing_location( $country, $state = '', $postcode = '', $city = '' ) { - $billing = $this->get_prop( 'billing', 'edit' ); - $billing['country'] = $country; - $billing['state'] = $state; - $billing['postcode'] = $postcode; - $billing['city'] = $city; - $this->set_prop( 'billing', $billing ); + $address_data = $this->get_prop( 'billing', 'edit' ); + + $address_data['address_1'] = ''; + $address_data['address_2'] = ''; + $address_data['city'] = $city; + $address_data['state'] = $state; + $address_data['postcode'] = $postcode; + $address_data['country'] = $country; + + $this->set_prop( 'billing', $address_data ); } /** @@ -834,12 +839,16 @@ class WC_Customer extends WC_Legacy_Customer { * @param string $city City. */ public function set_shipping_location( $country, $state = '', $postcode = '', $city = '' ) { - $shipping = $this->get_prop( 'shipping', 'edit' ); - $shipping['country'] = $country; - $shipping['state'] = $state; - $shipping['postcode'] = $postcode; - $shipping['city'] = $city; - $this->set_prop( 'shipping', $shipping ); + $address_data = $this->get_prop( 'shipping', 'edit' ); + + $address_data['address_1'] = ''; + $address_data['address_2'] = ''; + $address_data['city'] = $city; + $address_data['state'] = $state; + $address_data['postcode'] = $postcode; + $address_data['country'] = $country; + + $this->set_prop( 'shipping', $address_data ); } /** diff --git a/includes/data-stores/class-wc-customer-data-store-session.php b/includes/data-stores/class-wc-customer-data-store-session.php index adaa2aca2e9..4a2585d20ca 100644 --- a/includes/data-stores/class-wc-customer-data-store-session.php +++ b/includes/data-stores/class-wc-customer-data-store-session.php @@ -109,7 +109,7 @@ class WC_Customer_Data_Store_Session extends WC_Data_Store_WP implements WC_Cust if ( 'billing_' === substr( $session_key, 0, 8 ) ) { $session_key = str_replace( 'billing_', '', $session_key ); } - if ( ! empty( $data[ $session_key ] ) && is_callable( array( $customer, "set_{$function_key}" ) ) ) { + if ( isset( $data[ $session_key ] ) && is_callable( array( $customer, "set_{$function_key}" ) ) ) { $customer->{"set_{$function_key}"}( wp_unslash( $data[ $session_key ] ) ); } } diff --git a/includes/shortcodes/class-wc-shortcode-cart.php b/includes/shortcodes/class-wc-shortcode-cart.php index a42527206d2..2f3b8d5d88b 100644 --- a/includes/shortcodes/class-wc-shortcode-cart.php +++ b/includes/shortcodes/class-wc-shortcode-cart.php @@ -40,7 +40,7 @@ class WC_Shortcode_Cart { } if ( $address['country'] ) { - WC()->customer->set_location( $address['country'], $address['state'], $address['postcode'], $address['city'] ); + WC()->customer->set_billing_location( $address['country'], $address['state'], $address['postcode'], $address['city'] ); WC()->customer->set_shipping_location( $address['country'], $address['state'], $address['postcode'], $address['city'] ); } else { WC()->customer->set_billing_address_to_base(); From 2ea7e4fb6acdfbdee50affbf11ed69e4ddb19076 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 16 Jan 2019 12:25:19 +0000 Subject: [PATCH 073/401] woocommerce_display_product_attributes filter --- assets/js/frontend/add-to-cart-variation.js | 8 +- includes/wc-template-functions.php | 80 ++++++++++++++++--- .../single-product/product-attributes.php | 61 +++----------- 3 files changed, 86 insertions(+), 63 deletions(-) diff --git a/assets/js/frontend/add-to-cart-variation.js b/assets/js/frontend/add-to-cart-variation.js index 08b993123a9..e4ee1241ad3 100644 --- a/assets/js/frontend/add-to-cart-variation.js +++ b/assets/js/frontend/add-to-cart-variation.js @@ -111,8 +111,8 @@ VariationForm.prototype.onResetDisplayedVariation = function( event ) { var form = event.data.variationForm; form.$product.find( '.product_meta' ).find( '.sku' ).wc_reset_content(); - form.$product.find( '.product_weight' ).wc_reset_content(); - form.$product.find( '.product_dimensions' ).wc_reset_content(); + form.$product.find( '.product_weight, .woocommerce-product-attributes-item--weight .woocommerce-product-attributes-item__value' ).wc_reset_content(); + form.$product.find( '.product_dimensions, .woocommerce-product-attributes-item--dimensions .woocommerce-product-attributes-item__value' ).wc_reset_content(); form.$form.trigger( 'reset_image' ); form.$singleVariation.slideUp( 200 ).trigger( 'hide_variation' ); }; @@ -194,8 +194,8 @@ VariationForm.prototype.onFoundVariation = function( event, variation ) { var form = event.data.variationForm, $sku = form.$product.find( '.product_meta' ).find( '.sku' ), - $weight = form.$product.find( '.product_weight' ), - $dimensions = form.$product.find( '.product_dimensions' ), + $weight = form.$product.find( '.product_weight, .woocommerce-product-attributes-item--weight .woocommerce-product-attributes-item__value' ), + $dimensions = form.$product.find( '.product_dimensions, .woocommerce-product-attributes-item--dimensions .woocommerce-product-attributes-item__value' ), $qty = form.$singleVariationWrap.find( '.quantity' ), purchasable = true, variation_id = '', diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index ff71246af43..fb0e391277c 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -906,13 +906,10 @@ if ( ! function_exists( 'woocommerce_content' ) ) { - - - - - ' . get_the_title() . ''; + echo '

' . get_the_title() . '

'; // phpcs:ignore } } if ( ! function_exists( 'woocommerce_template_loop_category_title' ) ) { @@ -1206,7 +1203,7 @@ if ( ! function_exists( 'woocommerce_template_loop_add_to_cart' ) ) { $args = apply_filters( 'woocommerce_loop_add_to_cart_args', wp_parse_args( $args, $defaults ), $product ); if ( isset( $args['attributes']['aria-label'] ) ) { - $args['attributes']['aria-label'] = strip_tags( $args['attributes']['aria-label'] ); + $args['attributes']['aria-label'] = wp_strip_all_tags( $args['attributes']['aria-label'] ); } wc_get_template( 'loop/add-to-cart.php', $args ); @@ -3239,12 +3236,75 @@ if ( ! function_exists( 'woocommerce_photoswipe' ) ) { * @param WC_Product $product Product Object. */ function wc_display_product_attributes( $product ) { + $product_attributes = array(); + + // Display weight and dimensions before attribute list. + $display_dimensions = apply_filters( 'wc_product_enable_dimensions_display', $product->has_weight() || $product->has_dimensions() ); + + if ( $display_dimensions && $product->has_weight() ) { + $product_attributes['weight'] = array( + 'label' => __( 'Weight', 'woocommerce' ), + 'value' => wc_format_weight( $product->get_weight() ), + ); + } + + if ( $display_dimensions && $product->has_dimensions() ) { + $product_attributes['dimensions'] = array( + 'label' => __( 'Dimensions', 'woocommerce' ), + 'value' => wc_format_dimensions( $product->get_dimensions( false ) ), + ); + } + + // Add product attributes to list. + $attributes = array_filter( $product->get_attributes(), 'wc_attributes_array_filter_visible' ); + + foreach ( $attributes as $attribute ) { + $values = array(); + + if ( $attribute->is_taxonomy() ) { + $attribute_taxonomy = $attribute->get_taxonomy_object(); + $attribute_values = wc_get_product_terms( $product->get_id(), $attribute->get_name(), array( 'fields' => 'all' ) ); + + foreach ( $attribute_values as $attribute_value ) { + $value_name = esc_html( $attribute_value->name ); + + if ( $attribute_taxonomy->attribute_public ) { + $values[] = '
'; + } else { + $values[] = $value_name; + } + } + } else { + $values = $attribute->get_options(); + + foreach ( $values as &$value ) { + $value = make_clickable( esc_html( $value ) ); + } + } + + $product_attributes[ 'attribute_' . sanitize_title_with_dashes( $attribute->get_name() ) ] = array( + 'label' => wc_attribute_label( $attribute->get_name() ), + 'value' => apply_filters( 'woocommerce_attribute', wpautop( wptexturize( implode( ', ', $values ) ) ), $attribute, $values ), + ); + } + + /** + * Hook: woocommerce_display_product_attributes. + * + * @since 3.6.0. + * @param array $product_attributes Array of atributes to display; label, value. + * @param WC_Product $product Showing attributes for this product. + */ + $product_attributes = apply_filters( 'woocommerce_display_product_attributes', $product_attributes, $product ); + wc_get_template( 'single-product/product-attributes.php', array( + 'product_attributes' => $product_attributes, + // Legacy params. 'product' => $product, - 'attributes' => array_filter( $product->get_attributes(), 'wc_attributes_array_filter_visible' ), - 'display_dimensions' => apply_filters( 'wc_product_enable_dimensions_display', $product->has_weight() || $product->has_dimensions() ), + 'attributes' => $attributes, + 'display_dimensions' => $display_dimensions, ) ); } diff --git a/templates/single-product/product-attributes.php b/templates/single-product/product-attributes.php index a42b6c451d9..cc0f236ba06 100644 --- a/templates/single-product/product-attributes.php +++ b/templates/single-product/product-attributes.php @@ -12,59 +12,22 @@ * happen. When this occurs the version of the template file will be bumped and * the readme will list any important changes. * - * @see https://docs.woocommerce.com/document/template-structure/ - * @package WooCommerce/Templates - * @version 3.1.0 + * @see https://docs.woocommerce.com/document/template-structure/ + * @package WooCommerce/Templates + * @version 3.6.0 */ -if ( ! defined( 'ABSPATH' ) ) { - exit; +defined( 'ABSPATH' ) || exit; + +if ( ! $product_attributes ) { + return; } ?> - - has_weight() ) : ?> - - - - - - - has_dimensions() ) : ?> - - - - - - - - - - +
get_weight() ) ); ?>
get_dimensions( false ) ) ); ?>
get_name() ); ?>is_taxonomy() ) { - $attribute_taxonomy = $attribute->get_taxonomy_object(); - $attribute_values = wc_get_product_terms( $product->get_id(), $attribute->get_name(), array( 'fields' => 'all' ) ); - - foreach ( $attribute_values as $attribute_value ) { - $value_name = esc_html( $attribute_value->name ); - - if ( $attribute_taxonomy->attribute_public ) { - $values[] = ''; - } else { - $values[] = $value_name; - } - } - } else { - $values = $attribute->get_options(); - - foreach ( $values as &$value ) { - $value = make_clickable( esc_html( $value ) ); - } - } - - echo apply_filters( 'woocommerce_attribute', wpautop( wptexturize( implode( ', ', $values ) ) ), $attribute, $values ); - ?>
+ $product_attribute ) : ?> + + +
From 8ee3e8a6e9acd27a24055b36f4abe36ab78b5bab Mon Sep 17 00:00:00 2001 From: Brent Shepherd Date: Mon, 24 Sep 2018 12:14:07 +1000 Subject: [PATCH 074/401] Add WC_Tests_Webhook_Functions::create_webhook() To make it reusable. --- tests/unit-tests/webhooks/functions.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/unit-tests/webhooks/functions.php b/tests/unit-tests/webhooks/functions.php index d9f2d2423fb..080f6118fe3 100644 --- a/tests/unit-tests/webhooks/functions.php +++ b/tests/unit-tests/webhooks/functions.php @@ -103,6 +103,14 @@ class WC_Tests_Webhook_Functions extends WC_Unit_Test_Case { * @since 3.2.0 */ public function test_wc_load_webhooks() { + $webhook = $this->create_webhook(); + $this->assertTrue( wc_load_webhooks() ); + $webhook->delete( true ); + $this->assertFalse( wc_load_webhooks() ); + } + + protected function create_webhook( $topic = 'action.woocommerce_some_action' ) { + $webhook = new WC_Webhook(); $webhook->set_props( array( @@ -111,15 +119,12 @@ class WC_Tests_Webhook_Functions extends WC_Unit_Test_Case { 'user_id' => 0, 'delivery_url' => 'https://requestb.in/17jajv31', 'secret' => 'secret', - 'topic' => 'action.woocommerce_some_action', + 'topic' => $topic, 'api_version' => 2, ) ); $webhook->save(); - $this->assertTrue( wc_load_webhooks() ); - - $webhook->delete( true ); - $this->assertFalse( wc_load_webhooks() ); + return $webhook; } } From 67bf101aaf4ce0123bf0cf8d9c97212480e157b0 Mon Sep 17 00:00:00 2001 From: Brent Shepherd Date: Mon, 24 Sep 2018 13:22:16 +1000 Subject: [PATCH 075/401] Add $status param to wc_load_webhooks() And the corresponding data stores. Defaults to '', meaning do not load only webhooks with a specific status. This maintains backward compatibility. However, the call to wc_load_webhooks() within WooCommerce::load_webhooks() can now only load active webhooks, as they are the only ones that should be enqueued. --- .../class-wc-webhook-data-store.php | 71 +++++++++++++++++-- ...class-wc-webhooks-data-store-interface.php | 4 +- includes/wc-webhook-functions.php | 5 +- tests/unit-tests/webhooks/functions.php | 50 ++++++++++++- 4 files changed, 119 insertions(+), 11 deletions(-) diff --git a/includes/data-stores/class-wc-webhook-data-store.php b/includes/data-stores/class-wc-webhook-data-store.php index fd3a53bfed9..22f3725f7c5 100644 --- a/includes/data-stores/class-wc-webhook-data-store.php +++ b/includes/data-stores/class-wc-webhook-data-store.php @@ -59,7 +59,7 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { $webhook->set_id( $webhook_id ); $webhook->apply_changes(); - delete_transient( 'woocommerce_webhook_ids' ); + $this->delete_transients( $webhook->get_status( 'edit' ) ); WC_Cache_Helper::incr_cache_prefix( 'webhooks' ); do_action( 'woocommerce_new_webhook', $webhook_id ); } @@ -180,7 +180,7 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { array( '%d' ) ); // WPCS: cache ok, DB call ok. - delete_transient( 'woocommerce_webhook_ids' ); + $this->delete_transients( 'all' ); WC_Cache_Helper::incr_cache_prefix( 'webhooks' ); do_action( 'woocommerce_webhook_deleted', $webhook->get_id(), $webhook ); } @@ -200,18 +200,31 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { * Get all webhooks IDs. * * @since 3.3.0 + * @throws InvalidArgumentException If a $status value is passed in that is not in the known wc_get_webhook_statuses() keys. + * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.5.0. * @return int[] */ - public function get_webhooks_ids() { + public function get_webhooks_ids( $status = '' ) { global $wpdb; - $ids = get_transient( 'woocommerce_webhook_ids' ); + if ( ! empty( $status ) ) { + $this->validate_status( $status ); + } + + $ids = get_transient( $this->get_transient_key( $status ) ); if ( false === $ids ) { - $results = $wpdb->get_results( "SELECT webhook_id FROM {$wpdb->prefix}wc_webhooks" ); // WPCS: cache ok, DB call ok. + + $query = "SELECT webhook_id FROM {$wpdb->prefix}wc_webhooks"; + + if ( ! empty( $status ) ) { + $query .= $wpdb->prepare( " AND status = %s", $status ); + } + + $results = $wpdb->get_results( $query ); // WPCS: cache ok, DB call ok. $ids = array_map( 'intval', wp_list_pluck( $results, 'webhook_id' ) ); - set_transient( 'woocommerce_webhook_ids', $ids ); + set_transient( $this->get_transient_key( $status ), $ids ); } return $ids; @@ -349,4 +362,50 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { return $counts; } + + /** + * Check if a given string is in known statuses, based on return value of @see wc_get_webhook_statuses(). + * + * @since 3.5.0 + * @throws InvalidArgumentException If $status is not empty and not in the known wc_get_webhook_statuses() keys. + * @param string $status Status to check. + */ + private function validate_status( $status ) { + if ( ! array_key_exists( $status, wc_get_webhook_statuses() ) ) { + throw new InvalidArgumentException( sprintf( 'Invalid status given: %s. Status must be one of: %s.', $status, implode( ', ', array_keys( wc_get_webhook_statuses() ) ) ) ); + } + } + + /** + * Get the transient key used to cache a set of webhook IDs, optionally filtered by status. + * + * @since 3.5.0 + * @param string $status Optional - status of cache key. + * @return string + */ + private function get_transient_key( $status = '' ) { + return empty( $status ) ? 'woocommerce_webhook_ids' : sprintf( 'woocommerce_webhook_ids_status_%s', $status ); + } + + /** + * Delete the transients used to cache a set of webhook IDs, optionally filtered by status. + * + * @since 3.5.0 + * @param string $status Optional - status of cache to delete, or 'all' to delete all caches. + */ + private function delete_transients( $status = '' ) { + + // Always delete the non-filtered cache. + delete_transient( $this->get_transient_key( '' ) ); + + if ( ! empty( $status ) ) { + if ( 'all' === $status ) { + foreach ( wc_get_webhook_statuses() as $status_key => $status_string ) { + delete_transient( $this->get_transient_key( $status_key ) ); + } + } else { + delete_transient( $this->get_transient_key( $status ) ); + } + } + } } diff --git a/includes/interfaces/class-wc-webhooks-data-store-interface.php b/includes/interfaces/class-wc-webhooks-data-store-interface.php index 1478e3e7845..58afeebe6f2 100644 --- a/includes/interfaces/class-wc-webhooks-data-store-interface.php +++ b/includes/interfaces/class-wc-webhooks-data-store-interface.php @@ -28,7 +28,9 @@ interface WC_Webhook_Data_Store_Interface { * Get all webhooks IDs. * * @since 3.2.0 + * @throws InvalidArgumentException If a $status value is passed in that is not in the known wc_get_webhook_statuses() keys. + * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.5.0. * @return int[] */ - public function get_webhooks_ids(); + public function get_webhooks_ids( $status = '' ); } diff --git a/includes/wc-webhook-functions.php b/includes/wc-webhook-functions.php index cb28a25613c..d7feb86bab1 100644 --- a/includes/wc-webhook-functions.php +++ b/includes/wc-webhook-functions.php @@ -128,11 +128,12 @@ function wc_get_webhook_statuses() { * * @since 3.3.0 * @throws Exception If webhook cannot be read/found and $data parameter of WC_Webhook class constructor is set. + * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.5.0. * @return bool */ -function wc_load_webhooks() { +function wc_load_webhooks( $status = '' ) { $data_store = WC_Data_Store::load( 'webhook' ); - $webhooks = $data_store->get_webhooks_ids(); + $webhooks = $data_store->get_webhooks_ids( $status ); $loaded = false; foreach ( $webhooks as $webhook_id ) { diff --git a/tests/unit-tests/webhooks/functions.php b/tests/unit-tests/webhooks/functions.php index 080f6118fe3..5a2f490bacd 100644 --- a/tests/unit-tests/webhooks/functions.php +++ b/tests/unit-tests/webhooks/functions.php @@ -109,12 +109,58 @@ class WC_Tests_Webhook_Functions extends WC_Unit_Test_Case { $this->assertFalse( wc_load_webhooks() ); } - protected function create_webhook( $topic = 'action.woocommerce_some_action' ) { + /** + * Provide webhook statuses for tests. + * + * @since 3.5.0 + */ + public function provider_webhook_statuses() { + + $webhook_statuses = array(); + + foreach ( wc_get_webhook_statuses() as $status_key => $status_string ) { + $webhook_statuses[] = array( $status_key ); + } + + return $webhook_statuses; + } + + /** + * Test the $status param on wc_load_webhooks(). + * + * @dataProvider provider_webhook_statuses + * @param string $status The status of the webhook to test. + * @since 3.5.0 + */ + public function test_wc_load_webhooks_status( $status ) { + + $webhook = $this->create_webhook( 'action.woocommerce_some_action', $status ); + + $this->assertTrue( wc_load_webhooks( '' ) ); + $this->assertTrue( wc_load_webhooks( $status ) ); + + // Find a different, but still valid status. + $other_status = ( 'active' === $status ) ? 'disabled' : 'active'; + + $this->assertFalse( wc_load_webhooks( $other_status ) ); + + $webhook->delete( true ); + $this->assertFalse( wc_load_webhooks( $status ) ); + } + + /** + * @expectedException InvalidArgumentException + */ + public function test_wc_load_webhooks_status_invalid() { + wc_load_webhooks( 'invalid_status' ); + } + + protected function create_webhook( $topic = 'action.woocommerce_some_action', $status = 'active' ) { $webhook = new WC_Webhook(); $webhook->set_props( array( - 'status' => 'active', + 'status' => $status, 'name' => 'Testing webhook', 'user_id' => 0, 'delivery_url' => 'https://requestb.in/17jajv31', From f86b738db3ccd60d40031ca54bb8ea64c58d76eb Mon Sep 17 00:00:00 2001 From: Brent Shepherd Date: Mon, 24 Sep 2018 14:22:01 +1000 Subject: [PATCH 076/401] Use search_webhooks() To avoid duplicate SQL --- .../data-stores/class-wc-webhook-data-store.php | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/includes/data-stores/class-wc-webhook-data-store.php b/includes/data-stores/class-wc-webhook-data-store.php index 22f3725f7c5..6e121f4c793 100644 --- a/includes/data-stores/class-wc-webhook-data-store.php +++ b/includes/data-stores/class-wc-webhook-data-store.php @@ -214,16 +214,13 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { $ids = get_transient( $this->get_transient_key( $status ) ); if ( false === $ids ) { - - $query = "SELECT webhook_id FROM {$wpdb->prefix}wc_webhooks"; - - if ( ! empty( $status ) ) { - $query .= $wpdb->prepare( " AND status = %s", $status ); - } - - $results = $wpdb->get_results( $query ); // WPCS: cache ok, DB call ok. - $ids = array_map( 'intval', wp_list_pluck( $results, 'webhook_id' ) ); - + $ids = $this->search_webhooks( + array( + 'limit' => -1, + 'status' => $status, + ) + ); + $ids = array_map( 'intval', $ids ); set_transient( $this->get_transient_key( $status ), $ids ); } From e7a5a2ab2f6b093735d0c921a138e95cbcade982 Mon Sep 17 00:00:00 2001 From: Brent Shepherd Date: Mon, 24 Sep 2018 13:28:39 +1000 Subject: [PATCH 077/401] Only load active webhooks on each request To avoid slowing down page loads on sites with a large number of disabled or paused webhooks, which do not need to be loaded or enqueued. --- includes/class-woocommerce.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php index f50f5923bf5..0586af51473 100644 --- a/includes/class-woocommerce.php +++ b/includes/class-woocommerce.php @@ -644,7 +644,7 @@ final class WooCommerce { return; } - wc_load_webhooks(); + wc_load_webhooks( 'active' ); } /** From 9fdbb124ae26ef469c3ae73ef94ec9f2d3fe4df8 Mon Sep 17 00:00:00 2001 From: Brent Shepherd Date: Mon, 24 Sep 2018 12:14:32 +1000 Subject: [PATCH 078/401] Add $limit param to wc_load_webhooks() And the corresponding data stores. Defaults to null, meaning do not limit, for backward compatibility. --- .../class-wc-webhook-data-store.php | 7 +- ...class-wc-webhooks-data-store-interface.php | 3 +- includes/wc-webhook-functions.php | 5 +- tests/unit-tests/webhooks/functions.php | 67 +++++++++++++++++++ 4 files changed, 78 insertions(+), 4 deletions(-) diff --git a/includes/data-stores/class-wc-webhook-data-store.php b/includes/data-stores/class-wc-webhook-data-store.php index 6e121f4c793..521c905b574 100644 --- a/includes/data-stores/class-wc-webhook-data-store.php +++ b/includes/data-stores/class-wc-webhook-data-store.php @@ -202,9 +202,10 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { * @since 3.3.0 * @throws InvalidArgumentException If a $status value is passed in that is not in the known wc_get_webhook_statuses() keys. * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.5.0. + * @param null|int $limit Limit returned results. @since 3.5.0. * @return int[] */ - public function get_webhooks_ids( $status = '' ) { + public function get_webhooks_ids( $status = '', $limit = null ) { global $wpdb; if ( ! empty( $status ) ) { @@ -224,6 +225,10 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { set_transient( $this->get_transient_key( $status ), $ids ); } + if ( null !== $limit && $limit > 0 ) { + $ids = array_slice( $ids, 0, absint( $limit ) ); + } + return $ids; } diff --git a/includes/interfaces/class-wc-webhooks-data-store-interface.php b/includes/interfaces/class-wc-webhooks-data-store-interface.php index 58afeebe6f2..7ffdd5fd980 100644 --- a/includes/interfaces/class-wc-webhooks-data-store-interface.php +++ b/includes/interfaces/class-wc-webhooks-data-store-interface.php @@ -30,7 +30,8 @@ interface WC_Webhook_Data_Store_Interface { * @since 3.2.0 * @throws InvalidArgumentException If a $status value is passed in that is not in the known wc_get_webhook_statuses() keys. * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.5.0. + * @param null|int $limit Limit returned results. @since 3.5.0. * @return int[] */ - public function get_webhooks_ids( $status = '' ); + public function get_webhooks_ids( $status = '', $limit = null ); } diff --git a/includes/wc-webhook-functions.php b/includes/wc-webhook-functions.php index d7feb86bab1..e3f37e5d974 100644 --- a/includes/wc-webhook-functions.php +++ b/includes/wc-webhook-functions.php @@ -129,11 +129,12 @@ function wc_get_webhook_statuses() { * @since 3.3.0 * @throws Exception If webhook cannot be read/found and $data parameter of WC_Webhook class constructor is set. * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.5.0. + * @param null|int $limit Limit number of webhooks loaded. @since 3.5.0. * @return bool */ -function wc_load_webhooks( $status = '' ) { +function wc_load_webhooks( $status = '', $limit = null ) { $data_store = WC_Data_Store::load( 'webhook' ); - $webhooks = $data_store->get_webhooks_ids( $status ); + $webhooks = $data_store->get_webhooks_ids( $status, $limit ); $loaded = false; foreach ( $webhooks as $webhook_id ) { diff --git a/tests/unit-tests/webhooks/functions.php b/tests/unit-tests/webhooks/functions.php index 5a2f490bacd..213a80d2a5c 100644 --- a/tests/unit-tests/webhooks/functions.php +++ b/tests/unit-tests/webhooks/functions.php @@ -155,6 +155,73 @@ class WC_Tests_Webhook_Functions extends WC_Unit_Test_Case { wc_load_webhooks( 'invalid_status' ); } + /** + * Test the $limit param on wc_load_webhooks(). + * + * @since 3.5.0 + */ + public function test_wc_load_webhooks_limit() { + global $wp_filter; + + $webhook_one = $this->create_webhook( 'action.woocommerce_one_test' ); + $webhook_two = $this->create_webhook( 'action.woocommerce_two_test' ); + + $this->assertTrue( wc_load_webhooks( '', 1 ) ); + $this->assertFalse( isset( $wp_filter['woocommerce_one_test'] ) ); + $this->assertTrue( isset( $wp_filter['woocommerce_two_test'] ) ); + + $webhook_two->delete( true ); + + $this->assertTrue( wc_load_webhooks( '', 1 ) ); + $this->assertTrue( isset( $wp_filter['woocommerce_one_test'] ) ); + + $webhook_one->delete( true ); + + $this->assertFalse( wc_load_webhooks( '', 1 ) ); + } + + /** + * Test the $status and $limit param on wc_load_webhooks(). + * + * @dataProvider provider_webhook_statuses + * @param string $status The status of the webhook to test. + */ + public function test_wc_load_webhooks_status_and_limit( $status ) { + global $wp_filter; + + $webhook_one = $this->create_webhook( 'action.woocommerce_one_test', $status ); + $webhook_two = $this->create_webhook( 'action.woocommerce_two_test', $status ); + + $this->assertTrue( wc_load_webhooks( $status, 1 ) ); + $this->assertFalse( isset( $wp_filter['woocommerce_one_test'] ) ); + + // Only active webhooks are loaded. + if ( 'active' === $status ) { + $this->assertTrue( isset( $wp_filter['woocommerce_two_test'] ) ); + } else { + $this->assertFalse( isset( $wp_filter['woocommerce_two_test'] ) ); + } + + $webhook_two->delete( true ); + + $this->assertTrue( wc_load_webhooks( $status, 1 ) ); + + if ( 'active' === $status ) { + $this->assertTrue( isset( $wp_filter['woocommerce_one_test'] ) ); + } else { + $this->assertFalse( isset( $wp_filter['woocommerce_one_test'] ) ); + } + + $webhook_one->delete( true ); + $this->assertFalse( wc_load_webhooks( $status, 1 ) ); + } + + /** + * Create and save a webhook for testing. + * + * @param string $topic The webhook topic for the test. + * @param string $status The status of the webhook to be tested. + */ protected function create_webhook( $topic = 'action.woocommerce_some_action', $status = 'active' ) { $webhook = new WC_Webhook(); From 0708c738b96d53a1a02046c1f5bb2ade5f33b762 Mon Sep 17 00:00:00 2001 From: Brent Shepherd Date: Mon, 24 Sep 2018 12:15:22 +1000 Subject: [PATCH 079/401] Only load 100 webhooks per request To avoid slowing down page loads on sites with a large numbers of webhooks. --- includes/class-woocommerce.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php index 0586af51473..259f2094b23 100644 --- a/includes/class-woocommerce.php +++ b/includes/class-woocommerce.php @@ -644,7 +644,9 @@ final class WooCommerce { return; } - wc_load_webhooks( 'active' ); + $limit = apply_filters( 'woocommerce_load_webhooks_limit', 100 ); + + wc_load_webhooks( 'active', $limit ); } /** From 21d724c65daa4e432284ee75a65fbed64e7774b1 Mon Sep 17 00:00:00 2001 From: Brent Shepherd Date: Mon, 24 Sep 2018 15:31:57 +1000 Subject: [PATCH 080/401] PHPCS fixes Add fixes required for Travis to pass the PR, but which aren't related to the PR diff. --- includes/data-stores/class-wc-webhook-data-store.php | 3 ++- includes/wc-webhook-functions.php | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/includes/data-stores/class-wc-webhook-data-store.php b/includes/data-stores/class-wc-webhook-data-store.php index 521c905b574..f83bddf80aa 100644 --- a/includes/data-stores/class-wc-webhook-data-store.php +++ b/includes/data-stores/class-wc-webhook-data-store.php @@ -242,7 +242,8 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { global $wpdb; $args = wp_parse_args( - $args, array( + $args, + array( 'limit' => 10, 'offset' => 0, 'order' => 'DESC', diff --git a/includes/wc-webhook-functions.php b/includes/wc-webhook-functions.php index e3f37e5d974..aab46c13a9d 100644 --- a/includes/wc-webhook-functions.php +++ b/includes/wc-webhook-functions.php @@ -20,6 +20,7 @@ function wc_webhook_process_delivery( $webhook, $arg ) { // so as to avoid delays or failures in delivery from affecting the // user who triggered it. if ( apply_filters( 'woocommerce_webhook_deliver_async', true, $webhook, $arg ) ) { + $queue_args = array( 'webhook_id' => $webhook->get_id(), 'arg' => $arg, From ed55a3976ad8945975c2d832683519dba961d022 Mon Sep 17 00:00:00 2001 From: Brent Shepherd Date: Mon, 24 Sep 2018 22:47:11 +1000 Subject: [PATCH 081/401] Test against $wp_filter instead of has_filter() Because we don't have the same WC_Webhook instance as used in wc_load_webhooks(), so it's impossible to check if the same object's process() method is attached as a callback. --- tests/unit-tests/webhooks/functions.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/unit-tests/webhooks/functions.php b/tests/unit-tests/webhooks/functions.php index 213a80d2a5c..867ef99c9b3 100644 --- a/tests/unit-tests/webhooks/functions.php +++ b/tests/unit-tests/webhooks/functions.php @@ -189,17 +189,19 @@ class WC_Tests_Webhook_Functions extends WC_Unit_Test_Case { public function test_wc_load_webhooks_status_and_limit( $status ) { global $wp_filter; - $webhook_one = $this->create_webhook( 'action.woocommerce_one_test', $status ); - $webhook_two = $this->create_webhook( 'action.woocommerce_two_test', $status ); + $action_one = 'woocommerce_one_test_status_' . $status; + $webhook_one = $this->create_webhook( 'action.' . $action_one, $status ); + $action_two = 'woocommerce_two_test_status_' . $status; + $webhook_two = $this->create_webhook( 'action.' . $action_two, $status ); $this->assertTrue( wc_load_webhooks( $status, 1 ) ); - $this->assertFalse( isset( $wp_filter['woocommerce_one_test'] ) ); + $this->assertFalse( isset( $wp_filter[ $action_one ] ) ); // Only active webhooks are loaded. if ( 'active' === $status ) { - $this->assertTrue( isset( $wp_filter['woocommerce_two_test'] ) ); + $this->assertTrue( isset( $wp_filter[ $action_two ] ) ); } else { - $this->assertFalse( isset( $wp_filter['woocommerce_two_test'] ) ); + $this->assertFalse( isset( $wp_filter[ $action_two ] ) ); } $webhook_two->delete( true ); @@ -207,9 +209,9 @@ class WC_Tests_Webhook_Functions extends WC_Unit_Test_Case { $this->assertTrue( wc_load_webhooks( $status, 1 ) ); if ( 'active' === $status ) { - $this->assertTrue( isset( $wp_filter['woocommerce_one_test'] ) ); + $this->assertTrue( isset( $wp_filter[ $action_one ] ) ); } else { - $this->assertFalse( isset( $wp_filter['woocommerce_one_test'] ) ); + $this->assertFalse( isset( $wp_filter[ $action_one ] ) ); } $webhook_one->delete( true ); From 739af008c630e04b5a05c4ab46117148058924be Mon Sep 17 00:00:00 2001 From: Brent Shepherd Date: Mon, 24 Sep 2018 22:51:14 +1000 Subject: [PATCH 082/401] Fix test_wc_load_webhooks_status_and_limit() As the status is being explicitly passed to wc_load_webhooks(), it will load webhooks with that status, not just active. --- tests/unit-tests/webhooks/functions.php | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/tests/unit-tests/webhooks/functions.php b/tests/unit-tests/webhooks/functions.php index 867ef99c9b3..d654675d9cd 100644 --- a/tests/unit-tests/webhooks/functions.php +++ b/tests/unit-tests/webhooks/functions.php @@ -196,23 +196,12 @@ class WC_Tests_Webhook_Functions extends WC_Unit_Test_Case { $this->assertTrue( wc_load_webhooks( $status, 1 ) ); $this->assertFalse( isset( $wp_filter[ $action_one ] ) ); - - // Only active webhooks are loaded. - if ( 'active' === $status ) { - $this->assertTrue( isset( $wp_filter[ $action_two ] ) ); - } else { - $this->assertFalse( isset( $wp_filter[ $action_two ] ) ); - } + $this->assertTrue( isset( $wp_filter[ $action_two ] ) ); $webhook_two->delete( true ); $this->assertTrue( wc_load_webhooks( $status, 1 ) ); - - if ( 'active' === $status ) { - $this->assertTrue( isset( $wp_filter[ $action_one ] ) ); - } else { - $this->assertFalse( isset( $wp_filter[ $action_one ] ) ); - } + $this->assertTrue( isset( $wp_filter[ $action_one ] ) ); $webhook_one->delete( true ); $this->assertFalse( wc_load_webhooks( $status, 1 ) ); From 28ca9f1ec4a2cdf6e33e3f10caddbd5b457b1a00 Mon Sep 17 00:00:00 2001 From: Brent Shepherd Date: Thu, 17 Jan 2019 15:17:15 +1000 Subject: [PATCH 083/401] Remove unused $wpdb --- includes/data-stores/class-wc-webhook-data-store.php | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/data-stores/class-wc-webhook-data-store.php b/includes/data-stores/class-wc-webhook-data-store.php index f83bddf80aa..56032e69f26 100644 --- a/includes/data-stores/class-wc-webhook-data-store.php +++ b/includes/data-stores/class-wc-webhook-data-store.php @@ -206,7 +206,6 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { * @return int[] */ public function get_webhooks_ids( $status = '', $limit = null ) { - global $wpdb; if ( ! empty( $status ) ) { $this->validate_status( $status ); From 2ed2572afeafbc108a491c21cc6a6daa2b676be3 Mon Sep 17 00:00:00 2001 From: Brent Shepherd Date: Thu, 17 Jan 2019 15:55:54 +1000 Subject: [PATCH 084/401] Clear webhook transients when status changes Background discussion: https://github.com/woocommerce/woocommerce/pull/21427#discussion_r246881785 --- includes/data-stores/class-wc-webhook-data-store.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/includes/data-stores/class-wc-webhook-data-store.php b/includes/data-stores/class-wc-webhook-data-store.php index 56032e69f26..2a98cd37958 100644 --- a/includes/data-stores/class-wc-webhook-data-store.php +++ b/includes/data-stores/class-wc-webhook-data-store.php @@ -152,6 +152,10 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { $webhook->apply_changes(); + if ( isset( $changes['status'] ) ) { + // We need to delete all transients, because we can't be sure of the old status. + $this->delete_transients( 'all' ); + } wp_cache_delete( $webhook->get_id(), 'webhooks' ); WC_Cache_Helper::incr_cache_prefix( 'webhooks' ); From 5280ceb8d9c2d5f77bae3b0a2491c76c4749e367 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 21 Jan 2019 11:49:44 +0000 Subject: [PATCH 085/401] Improve inline docs in calculate_shipping_for_package --- includes/class-wc-shipping.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/includes/class-wc-shipping.php b/includes/class-wc-shipping.php index 9ea7ca2bd45..15c88a95349 100644 --- a/includes/class-wc-shipping.php +++ b/includes/class-wc-shipping.php @@ -226,7 +226,8 @@ class WC_Shipping { public function get_shipping_classes() { if ( empty( $this->shipping_classes ) ) { $classes = get_terms( - 'product_shipping_class', array( + 'product_shipping_class', + array( 'hide_empty' => '0', 'orderby' => 'name', ) @@ -317,9 +318,12 @@ class WC_Shipping { unset( $package_to_hash['contents'][ $item_id ]['data'] ); } + // Get rates stored in the WC session data for this package. + $wc_session_key = 'shipping_for_package_' . $package_key; + $stored_rates = WC()->session->get( $wc_session_key ); + + // Calculate the hash for this package so we can tell if it's changed since last calculation. $package_hash = 'wc_ship_' . md5( wp_json_encode( $package_to_hash ) . WC_Cache_Helper::get_transient_version( 'shipping' ) ); - $session_key = 'shipping_for_package_' . $package_key; - $stored_rates = WC()->session->get( $session_key ); if ( ! is_array( $stored_rates ) || $package_hash !== $stored_rates['package_hash'] || 'yes' === get_option( 'woocommerce_shipping_debug_mode', 'no' ) ) { foreach ( $this->load_shipping_methods( $package ) as $shipping_method ) { @@ -333,7 +337,8 @@ class WC_Shipping { // Store in session to avoid recalculation. WC()->session->set( - $session_key, array( + $wc_session_key, + array( 'package_hash' => $package_hash, 'rates' => $package['rates'], ) From 0cf82b89372a14ce1374e51bd77ccedc3277e6a4 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 21 Jan 2019 12:05:58 +0000 Subject: [PATCH 086/401] Use fixed transient naming in wc_get_shipping_method_count --- includes/wc-core-functions.php | 44 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/includes/wc-core-functions.php b/includes/wc-core-functions.php index 141f7e810c8..6913cee34fe 100644 --- a/includes/wc-core-functions.php +++ b/includes/wc-core-functions.php @@ -1451,27 +1451,35 @@ function wc_postcode_location_matcher( $postcode, $objects, $object_id_key, $obj function wc_get_shipping_method_count( $include_legacy = false ) { global $wpdb; - $transient_name = 'wc_shipping_method_count_' . ( $include_legacy ? 1 : 0 ) . '_' . WC_Cache_Helper::get_transient_version( 'shipping' ); - $method_count = get_transient( $transient_name ); + $transient_name = $include_legacy ? 'wc_shipping_method_count_legacy' : 'wc_shipping_method_count'; + $transient_version = WC_Cache_Helper::get_transient_version( 'shipping' ); + $transient_value = get_transient( $transient_name ); - if ( false === $method_count ) { - $method_count = absint( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_shipping_zone_methods" ) ); - - if ( $include_legacy ) { - // Count activated methods that don't support shipping zones. - $methods = WC()->shipping()->get_shipping_methods(); - - foreach ( $methods as $method ) { - if ( isset( $method->enabled ) && 'yes' === $method->enabled && ! $method->supports( 'shipping-zones' ) ) { - $method_count++; - } - } - } - - set_transient( $transient_name, $method_count, DAY_IN_SECONDS * 30 ); + if ( isset( $transient_value['value'], $transient_value['version'] ) && $transient_value['version'] === $transient_version ) { + return absint( $transient_value['value'] ); } - return absint( $method_count ); + $method_count = absint( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_shipping_zone_methods" ) ); + + if ( $include_legacy ) { + // Count activated methods that don't support shipping zones. + $methods = WC()->shipping()->get_shipping_methods(); + + foreach ( $methods as $method ) { + if ( isset( $method->enabled ) && 'yes' === $method->enabled && ! $method->supports( 'shipping-zones' ) ) { + $method_count++; + } + } + } + + $transient_value = array( + 'version' => $transient_version, + 'value' => $method_count, + ); + + set_transient( $transient_name, $transient_value, DAY_IN_SECONDS * 30 ); + + return $method_count; } /** From 452bb5b5e7f58e3b9fb1bdfe3ca7c9047a36f7ff Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 21 Jan 2019 12:31:48 +0000 Subject: [PATCH 087/401] Include version within transient --- .../class-wc-shortcode-products.php | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/includes/shortcodes/class-wc-shortcode-products.php b/includes/shortcodes/class-wc-shortcode-products.php index b4545504a45..369eb346def 100644 --- a/includes/shortcodes/class-wc-shortcode-products.php +++ b/includes/shortcodes/class-wc-shortcode-products.php @@ -130,7 +130,9 @@ class WC_Shortcode_Products { 'page' => 1, // Page for pagination. 'paginate' => false, // Should results be paginated. 'cache' => true, // Should shortcode output be cached. - ), $attributes, $this->type + ), + $attributes, + $this->type ); if ( ! absint( $attributes['columns'] ) ) { @@ -534,7 +536,7 @@ class WC_Shortcode_Products { * @return string */ protected function get_transient_name() { - $transient_name = 'wc_product_loop' . substr( md5( wp_json_encode( $this->query_args ) . $this->type ), 28 ); + $transient_name = 'wc_product_loop_' . md5( wp_json_encode( $this->query_args ) . $this->type ); if ( 'rand' === $this->query_args['orderby'] ) { // When using rand, we'll cache a number of random queries and pull those to avoid querying rand on each page load. @@ -542,8 +544,6 @@ class WC_Shortcode_Products { $transient_name .= $rand_index; } - $transient_name .= WC_Cache_Helper::get_transient_version( 'product_query' ); - return $transient_name; } @@ -554,11 +554,14 @@ class WC_Shortcode_Products { * @return object Object with the following props; ids, per_page, found_posts, max_num_pages, current_page */ protected function get_query_results() { - $transient_name = $this->get_transient_name(); - $cache = wc_string_to_bool( $this->attributes['cache'] ) === true; - $results = $cache ? get_transient( $transient_name ) : false; + $transient_name = $this->get_transient_name(); + $transient_version = WC_Cache_Helper::get_transient_version( 'product_query' ); + $cache = wc_string_to_bool( $this->attributes['cache'] ) === true; + $transient_value = $cache ? get_transient( $transient_name ) : false; - if ( false === $results ) { + if ( isset( $transient_value['value'], $transient_value['version'] ) && $transient_value['version'] === $transient_version ) { + $results = $transient_value['value']; + } else { if ( 'top_rated_products' === $this->type ) { add_filter( 'posts_clauses', array( __CLASS__, 'order_by_rating_post_clauses' ) ); $query = new WP_Query( $this->query_args ); @@ -578,11 +581,17 @@ class WC_Shortcode_Products { ); if ( $cache ) { - set_transient( $transient_name, $results, DAY_IN_SECONDS * 30 ); + $transient_value = array( + 'version' => $transient_version, + 'value' => $results, + ); + set_transient( $transient_name, $transient_value, DAY_IN_SECONDS * 30 ); } } + // Remove ordering query arguments which may have been added by get_catalog_ordering_args. WC()->query->remove_ordering_args(); + return $results; } From eabc30be5e9b82dcb7b47f42ff27db2545aafe0e Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 21 Jan 2019 12:38:40 +0000 Subject: [PATCH 088/401] wc_customer_bought_product - move version within transient --- includes/wc-user-functions.php | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/includes/wc-user-functions.php b/includes/wc-user-functions.php index dc7f3ea616b..810b4304e74 100644 --- a/includes/wc-user-functions.php +++ b/includes/wc-user-functions.php @@ -95,7 +95,8 @@ if ( ! function_exists( 'wc_create_new_customer' ) ) { } $new_customer_data = apply_filters( - 'woocommerce_new_customer_data', array( + 'woocommerce_new_customer_data', + array( 'user_login' => $username, 'user_pass' => $password, 'user_email' => $email, @@ -215,10 +216,13 @@ function wc_customer_bought_product( $customer_email, $user_id, $product_id ) { return $result; } - $transient_name = 'wc_cbp_' . md5( $customer_email . $user_id . WC_Cache_Helper::get_transient_version( 'orders' ) ); - $result = get_transient( $transient_name ); + $transient_name = 'wc_customer_bought_product_' . md5( $customer_email . $user_id ); + $transient_version = WC_Cache_Helper::get_transient_version( 'orders' ); + $transient_value = get_transient( $transient_name ); - if ( false === $result ) { + if ( isset( $transient_value['value'], $transient_value['version'] ) && $transient_value['version'] === $transient_version ) { + $result = $transient_value['value']; + } else { $customer_data = array( $user_id ); if ( $user_id ) { @@ -255,7 +259,12 @@ function wc_customer_bought_product( $customer_email, $user_id, $product_id ) { ); // WPCS: unprepared SQL ok. $result = array_map( 'absint', $result ); - set_transient( $transient_name, $result, DAY_IN_SECONDS * 30 ); + $transient_value = array( + 'version' => $transient_version, + 'value' => $result, + ); + + set_transient( $transient_name, $transient_value, DAY_IN_SECONDS * 30 ); } return in_array( absint( $product_id ), $result, true ); } @@ -554,7 +563,11 @@ function wc_reset_order_customer_id_on_deleted_user( $user_id ) { global $wpdb; $wpdb->update( - $wpdb->postmeta, array( 'meta_value' => 0 ), array( + $wpdb->postmeta, + array( + 'meta_value' => 0, + ), + array( 'meta_key' => '_customer_user', 'meta_value' => $user_id, ) From 08d7e319b6690e4b7d41215ad91fea731cdb1bdb Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 21 Jan 2019 12:56:55 +0000 Subject: [PATCH 089/401] Version handling for var prices --- ...ass-wc-product-variable-data-store-cpt.php | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/includes/data-stores/class-wc-product-variable-data-store-cpt.php b/includes/data-stores/class-wc-product-variable-data-store-cpt.php index be3ea1db7b1..54f9dd3325f 100644 --- a/includes/data-stores/class-wc-product-variable-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-variable-data-store-cpt.php @@ -244,9 +244,16 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple * * @since 2.5.0 a single transient is used per product for all prices, rather than many transients per product. */ - $transient_name = 'wc_var_prices_' . $product->get_id(); + $transient_name = 'wc_var_prices_' . $product->get_id(); + $transient_version = WC_Cache_Helper::get_transient_version( 'product' ); + $price_hash = $this->get_price_hash( $product, $for_display ); - $price_hash = $this->get_price_hash( $product, $for_display ); + // Check if prices array is stale. + if ( ! isset( $this->prices_array['version'] ) || $this->prices_array['version'] !== $transient_version ) { + $this->prices_array = array( + 'version' => $transient_version, + ); + } /** * $this->prices_array is an array of values which may have been modified from what is stored in transients - this may not match $transient_cached_prices_array. @@ -256,8 +263,10 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple $transient_cached_prices_array = array_filter( (array) json_decode( strval( get_transient( $transient_name ) ), true ) ); // If the product version has changed since the transient was last saved, reset the transient cache. - if ( empty( $transient_cached_prices_array['version'] ) || WC_Cache_Helper::get_transient_version( 'product' ) !== $transient_cached_prices_array['version'] ) { - $transient_cached_prices_array = array( 'version' => WC_Cache_Helper::get_transient_version( 'product' ) ); + if ( ! isset( $transient_cached_prices_array['version'] ) || $transient_version !== $transient_cached_prices_array['version'] ) { + $transient_cached_prices_array = array( + 'version' => $transient_version, + ); } // If the prices are not stored for this hash, generate them and add to the transient. @@ -267,7 +276,7 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple 'regular_price' => array(), 'sale_price' => array(), ); - $variation_ids = $product->get_visible_children(); + $variation_ids = $product->get_visible_children(); foreach ( $variation_ids as $variation_id ) { $variation = wc_get_product( $variation_id ); @@ -335,18 +344,19 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple } } - $prices_array['price'][ $variation_id ] = wc_format_decimal( $price, wc_get_price_decimals() ); + $prices_array['price'][ $variation_id ] = wc_format_decimal( $price, wc_get_price_decimals() ); $prices_array['regular_price'][ $variation_id ] = wc_format_decimal( $regular_price, wc_get_price_decimals() ); $prices_array['sale_price'][ $variation_id ] = wc_format_decimal( $sale_price . '.00', wc_get_price_decimals() ); + $prices_array = apply_filters( 'woocommerce_variation_prices_array', $prices_array, $variation, $for_display ); } - } + } // Add all pricing data to the transient array. - foreach( $prices_array as $key => $values ) { + foreach ( $prices_array as $key => $values ) { $transient_cached_prices_array[ $price_hash ][ $key ] = $values; } - + set_transient( $transient_name, wp_json_encode( $transient_cached_prices_array ), DAY_IN_SECONDS * 30 ); } @@ -384,14 +394,7 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple } } - $price_hash[] = WC_Cache_Helper::get_transient_version( 'product' ); - $price_hash = md5( - wp_json_encode( - apply_filters( 'woocommerce_get_variation_prices_hash', $price_hash, $product, $for_display ) - ) - ); - - return $price_hash; + return md5( wp_json_encode( apply_filters( 'woocommerce_get_variation_prices_hash', $price_hash, $product, $for_display ) ) ); } /** From eea4810c4905238f6b66f84c9181937b4805f6a8 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Wed, 5 Sep 2018 11:46:38 -0300 Subject: [PATCH 090/401] Revert change to defer transient cleanup This commit reverts commits 2f8a3eae49949c9957d3408213b1339f12019498 and 17e97c2580a17af4fd70633f7fad98ea0d264142 that were created to defer transient cleanup (see #20537) and avoid deadlocks on the wp_options table (see #20528 and #17632). The problem is that deferring transient cleanup to a cron job created an issue when creating or importing multiple products at once (see #21100 and https://github.com/woocommerce/wc-smooth-generator/issues/14#issuecomment-413342136) and has the potential to impact the checkout as well if we start using more versioned transients for orders. This problem is happening because when importing or creating multiple products at once, for each product that is created or imported, WooCommerce core enqueues a few 'delete_version_transients' cron events. Events are enqueued faster than they are executed and after a few hundred products are generated, the size of the cron queue, which is stored in a single wp_options entry, starts to impact WordPress performance in general. To reduce the chance of deadlocks happening again after this change, I already created another PR to optimize the query used to delete transients (#21274) by avoiding an unnecessary filesort, and I'm planning, on a subsequent commit, to improve it further by prefixing the transient name with its version instead of suffixing it as it is currently done. But the ultimate solution for high traffic stores is to use a persistent cache plugin. --- includes/class-wc-cache-helper.php | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/includes/class-wc-cache-helper.php b/includes/class-wc-cache-helper.php index a57b85066ef..5873e0e952b 100644 --- a/includes/class-wc-cache-helper.php +++ b/includes/class-wc-cache-helper.php @@ -134,33 +134,16 @@ class WC_Cache_Helper { public static function get_transient_version( $group, $refresh = false ) { $transient_name = $group . '-transient-version'; $transient_value = get_transient( $transient_name ); - $transient_value = strval( $transient_value ? $transient_value : '' ); - if ( '' === $transient_value || true === $refresh ) { - $old_transient_value = $transient_value; - $transient_value = (string) time(); + if ( false === $transient_value || true === $refresh ) { + self::delete_version_transients( $transient_value ); - if ( $old_transient_value === $transient_value ) { - // Time did not change but transient needs flushing now. - self::delete_version_transients( $transient_value ); - } else { - self::queue_delete_version_transients( $transient_value ); - } + $transient_value = (string) time(); set_transient( $transient_name, $transient_value ); } - return $transient_value; - } - /** - * Queues a cleanup event for version transients. - * - * @param string $version Version of the transient to remove. - */ - protected static function queue_delete_version_transients( $version = '' ) { - if ( ! wp_using_ext_object_cache() && ! empty( $version ) ) { - wp_schedule_single_event( time() + 30, 'delete_version_transients', array( $version ) ); - } + return $transient_value; } /** @@ -185,7 +168,7 @@ class WC_Cache_Helper { // If affected rows is equal to limit, there are more rows to delete. Delete in 30 secs. if ( $affected === $limit ) { - self::queue_delete_version_transients( $version ); + wp_schedule_single_event( time() + 30, 'delete_version_transients', array( $version ) ); } } } From c5da2dbcde6aaaa65e3740b93bbaa414ff2b11a0 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 18 Jan 2019 22:38:35 +0000 Subject: [PATCH 091/401] Remove unrelated transient wc_count_comments is completely unrelated to products. This can be moved to the transient cleanup tool, because code already exists to clear this cache when needed in WC_Comments. --- ...rest-system-status-tools-v2-controller.php | 23 +++++++++++++------ includes/wc-product-functions.php | 1 - 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/includes/api/v2/class-wc-rest-system-status-tools-v2-controller.php b/includes/api/v2/class-wc-rest-system-status-tools-v2-controller.php index 7d321a9904a..7aecce7faad 100644 --- a/includes/api/v2/class-wc-rest-system-status-tools-v2-controller.php +++ b/includes/api/v2/class-wc-rest-system-status-tools-v2-controller.php @@ -37,7 +37,9 @@ class WC_REST_System_Status_Tools_V2_Controller extends WC_REST_Controller { */ public function register_routes() { register_rest_route( - $this->namespace, '/' . $this->rest_base, array( + $this->namespace, + '/' . $this->rest_base, + array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), @@ -49,7 +51,9 @@ class WC_REST_System_Status_Tools_V2_Controller extends WC_REST_Controller { ); register_rest_route( - $this->namespace, '/' . $this->rest_base . '/(?P[\w-]+)', array( + $this->namespace, + '/' . $this->rest_base . '/(?P[\w-]+)', + array( 'args' => array( 'id' => array( 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), @@ -199,7 +203,7 @@ class WC_REST_System_Status_Tools_V2_Controller extends WC_REST_Controller { __( 'Note:', 'woocommerce' ), __( 'This tool will update your WooCommerce database to the latest version. Please ensure you make sufficient backups before proceeding.', 'woocommerce' ) ), - ) + ), ); // Jetpack does the image resizing heavy lifting so you don't have to. @@ -226,7 +230,8 @@ class WC_REST_System_Status_Tools_V2_Controller extends WC_REST_Controller { 'name' => $tool['name'], 'action' => $tool['button'], 'description' => $tool['desc'], - ), $request + ), + $request ) ); } @@ -254,7 +259,8 @@ class WC_REST_System_Status_Tools_V2_Controller extends WC_REST_Controller { 'name' => $tool['name'], 'action' => $tool['button'], 'description' => $tool['desc'], - ), $request + ), + $request ) ); } @@ -418,6 +424,7 @@ class WC_REST_System_Status_Tools_V2_Controller extends WC_REST_Controller { case 'clear_transients': wc_delete_product_transients(); wc_delete_shop_order_transients(); + delete_transient( 'wc_count_comments' ); $attribute_taxonomies = wc_get_attribute_taxonomies(); @@ -494,14 +501,16 @@ class WC_REST_System_Status_Tools_V2_Controller extends WC_REST_Controller { case 'recount_terms': $product_cats = get_terms( - 'product_cat', array( + 'product_cat', + array( 'hide_empty' => false, 'fields' => 'id=>parent', ) ); _wc_term_recount( $product_cats, get_taxonomy( 'product_cat' ), true, false ); $product_tags = get_terms( - 'product_tag', array( + 'product_tag', + array( 'hide_empty' => false, 'fields' => 'id=>parent', ) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 4fe4ecdd4e4..f10f3e657ed 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -103,7 +103,6 @@ function wc_delete_product_transients( $post_id = 0 ) { 'wc_featured_products', 'wc_outofstock_count', 'wc_low_stock_count', - 'wc_count_comments', ); // Transient names that include an ID. From 8ede6bcb74c0e8f4f8d2e202292d56b4a9051e67 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 18 Jan 2019 23:12:46 +0000 Subject: [PATCH 092/401] Added helpers to clean transients during shutdown and clean layered nav counts --- includes/class-wc-cache-helper.php | 48 +++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-cache-helper.php b/includes/class-wc-cache-helper.php index 5873e0e952b..56721a8abf3 100644 --- a/includes/class-wc-cache-helper.php +++ b/includes/class-wc-cache-helper.php @@ -12,18 +12,64 @@ defined( 'ABSPATH' ) || exit; */ class WC_Cache_Helper { + /** + * Transients to delete on shutdown. + * + * @var array Array of transient keys. + */ + private static $delete_transients = array(); + /** * Hook in methods. */ public static function init() { + add_action( 'shutdown', array( __CLASS__, 'delete_transients_on_shutdown' ), 10 ); add_action( 'template_redirect', array( __CLASS__, 'geolocation_ajax_redirect' ) ); add_action( 'admin_notices', array( __CLASS__, 'notices' ) ); - add_action( 'delete_version_transients', array( __CLASS__, 'delete_version_transients' ) ); + add_action( 'delete_version_transients', array( __CLASS__, 'delete_version_transients' ), 10, 2 ); add_action( 'wp', array( __CLASS__, 'prevent_caching' ) ); add_action( 'clean_term_cache', array( __CLASS__, 'clean_term_cache' ), 10, 2 ); add_action( 'edit_terms', array( __CLASS__, 'clean_term_cache' ), 10, 2 ); } + /** + * Add a transient to delete on shutdown. + * + * @since 3.6.0 + * @param string|array $keys Transient key or keys. + */ + public static function queue_delete_transient( $keys ) { + self::$delete_transients = array_unique( array_merge( is_array( $keys ) ? $keys : array( $keys ), self::$delete_transients ) ); + } + + /** + * Transients that don't need to be cleaned right away can be deleted on shutdown to avoid repetition. + * + * @since 3.6.0 + */ + public static function delete_transients_on_shutdown() { + if ( self::$delete_transients ) { + foreach ( self::$delete_transients as $key ) { + delete_transient( $key ); + } + self::$delete_transients = array(); + } + } + + /** + * Used to clear layered nav counts based on passed attribute names. + * + * @since 3.6.0 + * @param array $attribute_keys Attribute keys. + */ + public static function invalidate_attribute_count( $attribute_keys ) { + if ( $attribute_keys ) { + foreach ( $attribute_keys as $attribute_key ) { + self::queue_delete_transient( 'wc_layered_nav_counts_' . $attribute_key ); + } + } + } + /** * Get prefix for use with wp_cache_set. Allows all cache in a group to be invalidated at once. * From 774487932613ec810aa68e0e03f7fc9fce24a883 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 18 Jan 2019 23:24:43 +0000 Subject: [PATCH 093/401] wc_delete_product_transients for parent --- includes/data-stores/class-wc-product-data-store-cpt.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index c3b0b6c6b59..5ec8c23b895 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -829,6 +829,10 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da */ protected function clear_caches( &$product ) { wc_delete_product_transients( $product->get_id() ); + if ( $product->get_parent_id( 'edit' ) ) { + wc_delete_product_transients( $product->get_parent_id( 'edit' ) ); + } + WC_Cache_Helper::invalidate_attribute_count( array_keys( $product->get_attributes() ) ); WC_Cache_Helper::incr_cache_prefix( 'product_' . $product->get_id() ); } From d3ac50d47f009498fa5311f9619c01b0dac6f98d Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 18 Jan 2019 23:12:04 +0000 Subject: [PATCH 094/401] Remove heavy queries from transient cleanup function Remove heavy queries from transient cleanup function Removes unneccessary loops and heavy queries. There is a parent update and cache clear which was added in #17141. This cleanup is handled in `update_attributes` so doing it again here should be unneccessary. Finally, the layered nav cleanup here can be moved. #22029 notes that this was needed in case of a stock change affecting product visibility. If we move to the data store, we can avoid getting the product here which is slower. --- .../class-wc-product-data-store-cpt.php | 9 +-- includes/wc-product-functions.php | 71 +++++++------------ 2 files changed, 26 insertions(+), 54 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index 5ec8c23b895..e0efe4a51c6 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -128,12 +128,11 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da $this->update_attributes( $product, true ); $this->update_version_and_type( $product ); $this->handle_updated_props( $product ); + $this->clear_caches( $product ); $product->save_meta_data(); $product->apply_changes(); - $this->clear_caches( $product ); - do_action( 'woocommerce_new_product', $id ); } } @@ -245,11 +244,10 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da $this->update_attributes( $product ); $this->update_version_and_type( $product ); $this->handle_updated_props( $product ); + $this->clear_caches( $product ); $product->apply_changes(); - $this->clear_caches( $product ); - do_action( 'woocommerce_update_product', $product->get_id() ); } @@ -714,7 +712,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da } if ( ! is_wp_error( wp_set_post_terms( $product->get_id(), $terms, 'product_visibility', false ) ) ) { - delete_transient( 'wc_featured_products' ); do_action( 'woocommerce_product_set_visibility', $product->get_id(), $product->get_catalog_visibility() ); } } @@ -738,8 +735,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da foreach ( $attributes as $attribute_key => $attribute ) { $value = ''; - delete_transient( 'wc_layered_nav_counts_' . $attribute_key ); - if ( is_null( $attribute ) ) { if ( taxonomy_exists( $attribute_key ) ) { // Handle attributes that have been unset. diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index f10f3e657ed..b970a3d06a1 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -92,12 +92,12 @@ function wc_product_dimensions_enabled() { } /** - * Clear all transients cache for product data. + * Clear transient cache for product data. * - * @param int $post_id (default: 0). + * @param int $post_id (default: 0) The product ID. */ function wc_delete_product_transients( $post_id = 0 ) { - // Core transients. + // Transient data to clear with a fixed name which may be stale after product updates. $transients_to_clear = array( 'wc_products_onsale', 'wc_featured_products', @@ -105,48 +105,21 @@ function wc_delete_product_transients( $post_id = 0 ) { 'wc_low_stock_count', ); - // Transient names that include an ID. - $post_transient_names = array( - 'wc_product_children_', - 'wc_var_prices_', - 'wc_related_', - 'wc_child_has_weight_', - 'wc_child_has_dimensions_', - ); + WC_Cache_Helper::queue_delete_transient( $transients_to_clear ); if ( $post_id > 0 ) { + // Transient names that include an ID - since they are dynamic they cannot be cleaned in bulk without the ID. + $post_transient_names = array( + 'wc_product_children_', + 'wc_var_prices_', + 'wc_related_', + 'wc_child_has_weight_', + 'wc_child_has_dimensions_', + ); + foreach ( $post_transient_names as $transient ) { - $transients_to_clear[] = $transient . $post_id; + delete_transient( $transient . $post_id ); } - - // Does this product have a parent? - $product = wc_get_product( $post_id ); - - if ( $product ) { - if ( $product->get_parent_id() > 0 ) { - wc_delete_product_transients( $product->get_parent_id() ); - } - - if ( 'variable' === $product->get_type() ) { - wp_cache_delete( - WC_Cache_Helper::get_cache_prefix( 'products' ) . 'product_variation_attributes_' . $product->get_id(), - 'products' - ); - } - - $attributes = $product->get_attributes(); - - if ( $attributes ) { - foreach ( $attributes as $attribute_key => $attribute ) { - $transients_to_clear[] = 'wc_layered_nav_counts_' . $attribute_key; - } - } - } - } - - // Delete transients. - foreach ( $transients_to_clear as $transient ) { - delete_transient( $transient ); } // Increments the transient version to invalidate cache. @@ -817,12 +790,16 @@ function wc_get_min_max_price_meta_query( $args ) { $max = $class_max; } - return apply_filters( 'woocommerce_get_min_max_price_meta_query', array( - 'key' => '_price', - 'value' => array( $min, $max ), - 'compare' => 'BETWEEN', - 'type' => 'DECIMAL(10,' . wc_get_price_decimals() . ')', - ), $args ); + return apply_filters( + 'woocommerce_get_min_max_price_meta_query', + array( + 'key' => '_price', + 'value' => array( $min, $max ), + 'compare' => 'BETWEEN', + 'type' => 'DECIMAL(10,' . wc_get_price_decimals() . ')', + ), + $args + ); } /** From dc01d1de76eaec73a02d2e8f00ec3da086b5401c Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 21 Jan 2019 13:08:01 +0000 Subject: [PATCH 095/401] Deprecate delete_version_transients --- includes/class-wc-cache-helper.php | 57 +++++++++++++++--------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/includes/class-wc-cache-helper.php b/includes/class-wc-cache-helper.php index 56721a8abf3..8cc42643589 100644 --- a/includes/class-wc-cache-helper.php +++ b/includes/class-wc-cache-helper.php @@ -182,8 +182,6 @@ class WC_Cache_Helper { $transient_value = get_transient( $transient_name ); if ( false === $transient_value || true === $refresh ) { - self::delete_version_transients( $transient_value ); - $transient_value = (string) time(); set_transient( $transient_name, $transient_value ); @@ -192,33 +190,6 @@ class WC_Cache_Helper { return $transient_value; } - /** - * When the transient version increases, this is used to remove all past transients to avoid filling the DB. - * - * Note; this only works on transients appended with the transient version, and when object caching is not being used. - * - * @since 2.3.10 - * @param string $version Version of the transient to remove. - */ - public static function delete_version_transients( $version = '' ) { - if ( ! wp_using_ext_object_cache() && ! empty( $version ) ) { - global $wpdb; - - $limit = apply_filters( 'woocommerce_delete_version_transients_limit', 1000 ); - - if ( ! $limit ) { - return; - } - - $affected = $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s LIMIT %d;", '\_transient\_%' . $version, $limit ) ); // WPCS: cache ok, db call ok. - - // If affected rows is equal to limit, there are more rows to delete. Delete in 30 secs. - if ( $affected === $limit ) { - wp_schedule_single_event( time() + 30, 'delete_version_transients', array( $version ) ); - } - } - } - /** * Set constants to prevent caching by some plugins. * @@ -283,6 +254,34 @@ class WC_Cache_Helper { } } } + + /** + * When the transient version increases, this is used to remove all past transients to avoid filling the DB. + * + * Note; this only works on transients appended with the transient version, and when object caching is not being used. + * + * @deprecated 3.6.0 Adjusted transient usage to include versions within the transient values, making this cleanup obsolete. + * @since 2.3.10 + * @param string $version Version of the transient to remove. + */ + public static function delete_version_transients( $version = '' ) { + if ( ! wp_using_ext_object_cache() && ! empty( $version ) ) { + global $wpdb; + + $limit = apply_filters( 'woocommerce_delete_version_transients_limit', 1000 ); + + if ( ! $limit ) { + return; + } + + $affected = $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s LIMIT %d;", '\_transient\_%' . $version, $limit ) ); // WPCS: cache ok, db call ok. + + // If affected rows is equal to limit, there are more rows to delete. Delete in 30 secs. + if ( $affected === $limit ) { + wp_schedule_single_event( time() + 30, 'delete_version_transients', array( $version ) ); + } + } + } } WC_Cache_Helper::init(); From a713e63502ce02ef047fb16ce5936b1da71e26f1 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Tue, 22 Jan 2019 15:04:43 -0200 Subject: [PATCH 096/401] Fix eslint errors --- assets/js/admin/meta-boxes-product.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/assets/js/admin/meta-boxes-product.js b/assets/js/admin/meta-boxes-product.js index da726237dc6..4171d1da1df 100644 --- a/assets/js/admin/meta-boxes-product.js +++ b/assets/js/admin/meta-boxes-product.js @@ -1,7 +1,8 @@ /*global woocommerce_admin_meta_boxes */ jQuery( function( $ ) { - // Scroll to first checked category - https://github.com/scribu/wp-category-checklist-tree/blob/d1c3c1f449e1144542efa17dde84a9f52ade1739/category-checklist-tree.php + // Scroll to first checked category + // https://github.com/scribu/wp-category-checklist-tree/blob/d1c3c1f449e1144542efa17dde84a9f52ade1739/category-checklist-tree.php $( function() { $( '[id$="-all"] > ul.categorychecklist' ).each( function() { var $list = $( this ); @@ -422,7 +423,8 @@ jQuery( function( $ ) { window.alert( response.error ); } else if ( response.slug ) { // Success. - $wrapper.find( 'select.attribute_values' ).append( '' ); + $wrapper.find( 'select.attribute_values' ) + .append( '' ); $wrapper.find( 'select.attribute_values' ).change(); } @@ -468,7 +470,8 @@ jQuery( function( $ ) { var nr_elements = original_data.length / 6; for ( var i = 0; i < nr_elements; i++ ) { if ( typeof( original_data ) !== 'undefined' && original_data[ i * 6 + 2 ].value === '' ) { - $( 'select.attribute_taxonomy' ).find( 'option[value="' + original_data[ i * 6 ].value + '"]' ).removeAttr( 'disabled' ); + $( 'select.attribute_taxonomy' ) + .find( 'option[value="' + original_data[ i * 6 ].value + '"]' ).removeAttr( 'disabled' ); } } @@ -607,7 +610,11 @@ jQuery( function( $ ) { attachment_ids = attachment_ids ? attachment_ids + ',' + attachment.id : attachment.id; var attachment_image = attachment.sizes && attachment.sizes.thumbnail ? attachment.sizes.thumbnail.url : attachment.url; - $product_images.append( '
  • ' ); + $product_images.append( + '
  • ' + ); } }); From d08745e0413d7e7c4a67dc61bdcee997a6a49452 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Tue, 22 Jan 2019 15:05:05 -0200 Subject: [PATCH 097/401] 'Used for variations' checkbox should only appear for variable products When editing product attributes, the checkbox 'Used for variations' should only be displayed when editing a variable product. The code has checks in place to control the display of this checkbox when adding an attribute or when loading the product attributes, but it was missing a check when saving product attributes which is added in this commit. --- assets/js/admin/meta-boxes-product.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assets/js/admin/meta-boxes-product.js b/assets/js/admin/meta-boxes-product.js index 4171d1da1df..ad0e8389eb0 100644 --- a/assets/js/admin/meta-boxes-product.js +++ b/assets/js/admin/meta-boxes-product.js @@ -466,6 +466,9 @@ jQuery( function( $ ) { $( '.product_attributes' ).html( response.data.html ); $( '.product_attributes' ).unblock(); + // Hide the 'Used for variations' checkbox if not viewing a variable product + show_and_hide_panels(); + // Make sure the dropdown is not disabled for empty value attributes. var nr_elements = original_data.length / 6; for ( var i = 0; i < nr_elements; i++ ) { From 343a2179393271b0ee838fd029a039f4b9a31d6e Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Tue, 22 Jan 2019 10:46:49 -0400 Subject: [PATCH 098/401] move add order tax to get_object_subtotal() --- includes/class-wc-discounts.php | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/includes/class-wc-discounts.php b/includes/class-wc-discounts.php index 8b39a8d992e..6f8502612a4 100644 --- a/includes/class-wc-discounts.php +++ b/includes/class-wc-discounts.php @@ -665,10 +665,6 @@ class WC_Discounts { protected function validate_coupon_minimum_amount( $coupon ) { $subtotal = wc_remove_number_precision( $this->get_object_subtotal() ); - if ( is_a( $this->object, 'WC_Order' ) && $this->object->get_prices_include_tax() ) { - $subtotal += round( $this->object->get_total_tax(), wc_get_price_decimals() ); - } - if ( $coupon->get_minimum_amount() > 0 && apply_filters( 'woocommerce_coupon_validate_minimum_amount', $coupon->get_minimum_amount() > $subtotal, $coupon, $subtotal ) ) { /* translators: %s: coupon minimum amount */ throw new Exception( sprintf( __( 'The minimum spend for this coupon is %s.', 'woocommerce' ), wc_price( $coupon->get_minimum_amount() ) ), 108 ); @@ -688,10 +684,6 @@ class WC_Discounts { protected function validate_coupon_maximum_amount( $coupon ) { $subtotal = wc_remove_number_precision( $this->get_object_subtotal() ); - if ( is_a( $this->object, 'WC_Order' ) && $this->object->get_prices_include_tax() ) { - $subtotal += round( $this->object->get_total_tax(), wc_get_price_decimals() ); - } - if ( $coupon->get_maximum_amount() > 0 && apply_filters( 'woocommerce_coupon_validate_maximum_amount', $coupon->get_maximum_amount() < $subtotal, $coupon ) ) { /* translators: %s: coupon maximum amount */ throw new Exception( sprintf( __( 'The maximum spend for this coupon is %s.', 'woocommerce' ), wc_price( $coupon->get_maximum_amount() ) ), 112 ); @@ -916,7 +908,8 @@ class WC_Discounts { if ( is_a( $this->object, 'WC_Cart' ) ) { return wc_add_number_precision( $this->object->get_displayed_subtotal() ); } elseif ( is_a( $this->object, 'WC_Order' ) ) { - return wc_add_number_precision( $this->object->get_subtotal() ); + $subtotal = wc_add_number_precision( $this->object->get_subtotal() ); + return $this->object->get_prices_include_tax() ? $subtotal + round( $this->object->get_total_tax(), wc_get_price_decimals() ) : $subtotal; } else { return array_sum( wp_list_pluck( $this->items, 'price' ) ); } From 7cd20021e083d1914bbf1fbbad092d2df0b75860 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 22 Jan 2019 19:06:58 +0000 Subject: [PATCH 099/401] Sort terms by parent and work back from bottommost term Fixes the issue described in #21299 by sorting terms by parent ID. Remove the extra get_term call because we already have a term object. Since we support 4.7+, also removed function exists for wp_list_sort function. --- includes/wc-product-functions.php | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 4fe4ecdd4e4..be603f3f2b5 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -228,13 +228,14 @@ function wc_product_post_type_link( $permalink, $post ) { $terms = get_the_terms( $post->ID, 'product_cat' ); if ( ! empty( $terms ) ) { - if ( function_exists( 'wp_list_sort' ) ) { - $terms = wp_list_sort( $terms, 'term_id', 'ASC' ); - } else { - usort( $terms, '_usort_terms_by_ID' ); - } + $terms = wp_list_sort( + $terms, + array( + 'parent' => 'DESC', + 'term_id' => 'ASC', + ) + ); $category_object = apply_filters( 'wc_product_post_type_link_product_cat', $terms[0], $terms, $post ); - $category_object = get_term( $category_object, 'product_cat' ); $product_cat = $category_object->slug; if ( $category_object->parent ) { @@ -818,12 +819,16 @@ function wc_get_min_max_price_meta_query( $args ) { $max = $class_max; } - return apply_filters( 'woocommerce_get_min_max_price_meta_query', array( - 'key' => '_price', - 'value' => array( $min, $max ), - 'compare' => 'BETWEEN', - 'type' => 'DECIMAL(10,' . wc_get_price_decimals() . ')', - ), $args ); + return apply_filters( + 'woocommerce_get_min_max_price_meta_query', + array( + 'key' => '_price', + 'value' => array( $min, $max ), + 'compare' => 'BETWEEN', + 'type' => 'DECIMAL(10,' . wc_get_price_decimals() . ')', + ), + $args + ); } /** From 8e12f9607d6e51bf4250213fc14932402b62e842 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Tue, 22 Jan 2019 15:47:56 -0400 Subject: [PATCH 100/401] use WP standard datetime format for installs where timezone_string setting is not set --- includes/api/class-wc-rest-customers-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/api/class-wc-rest-customers-controller.php b/includes/api/class-wc-rest-customers-controller.php index 0e8aff7f717..05c45a9196c 100644 --- a/includes/api/class-wc-rest-customers-controller.php +++ b/includes/api/class-wc-rest-customers-controller.php @@ -40,7 +40,7 @@ class WC_REST_Customers_Controller extends WC_REST_Customers_V2_Controller { // Format date values. foreach ( $format_date as $key ) { // Date created is stored UTC, date modified is stored WP local time. - $datetime = 'date_created' === $key ? get_date_from_gmt( gmdate( 'Y-m-d\TH:i:s', $data[ $key ]->getTimestamp() ) ) : $data[ $key ]; + $datetime = 'date_created' === $key ? get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $data[ $key ]->getTimestamp() ) ) : $data[ $key ]; $data[ $key ] = wc_rest_prepare_date_response( $datetime, false ); $data[ $key . '_gmt' ] = wc_rest_prepare_date_response( $datetime ); } From 89ab2748dac7fe6f5450153c47f2cd22667cf50c Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Tue, 22 Jan 2019 16:58:05 -0400 Subject: [PATCH 101/401] update unit test to work when timezone_string is not set, add timezone assertions --- tests/unit-tests/api/customers.php | 73 +++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/tests/unit-tests/api/customers.php b/tests/unit-tests/api/customers.php index 8bc4cfe4b48..5995045bb85 100644 --- a/tests/unit-tests/api/customers.php +++ b/tests/unit-tests/api/customers.php @@ -54,7 +54,7 @@ class Customers extends WC_REST_Unit_Test_Case { ); $response = $this->server->dispatch( $request ); $customers = $response->get_data(); - $date_created = get_date_from_gmt( $customer_1->get_date_created() ); + $date_created = get_date_from_gmt( date( 'Y-m-d H:i:s', strtotime( $customer_1->get_date_created() ) ) ); $this->assertEquals( 200, $response->get_status() ); $this->assertEquals( 2, count( $customers ) ); @@ -113,6 +113,77 @@ class Customers extends WC_REST_Unit_Test_Case { ), $customers ); + + update_option( 'timezone_tring', 'America/New York' ); + $customer_3 = WC_Helper_Customer::create_customer( 'timezonetest', 'timezonetest', 'timezonetest@woo.local' ); + + $request = new WP_REST_Request( 'GET', '/wc/v3/customers' ); + $request->set_query_params( + array( + 'orderby' => 'id', + ) + ); + $response = $this->server->dispatch( $request ); + $customers = $response->get_data(); + $date_created = get_date_from_gmt( date( 'Y-m-d H:i:s', strtotime( $customer_3->get_date_created() ) ) ); + + $this->assertEquals( 200, $response->get_status() ); + + $this->assertContains( + array( + 'id' => $customer_3->get_id(), + 'date_created' => wc_rest_prepare_date_response( $date_created, false ), + 'date_created_gmt' => wc_rest_prepare_date_response( $date_created ), + 'date_modified' => wc_rest_prepare_date_response( $customer_3->get_date_modified(), false ), + 'date_modified_gmt' => wc_rest_prepare_date_response( $customer_3->get_date_modified() ), + 'email' => 'timezonetest@woo.local', + 'first_name' => 'Justin', + 'last_name' => '', + 'role' => 'customer', + 'username' => 'timezonetest', + 'billing' => array( + 'first_name' => '', + 'last_name' => '', + 'company' => '', + 'address_1' => '123 South Street', + 'address_2' => 'Apt 1', + 'city' => 'Philadelphia', + 'state' => 'PA', + 'postcode' => '19123', + 'country' => 'US', + 'email' => '', + 'phone' => '', + ), + 'shipping' => array( + 'first_name' => '', + 'last_name' => '', + 'company' => '', + 'address_1' => '123 South Street', + 'address_2' => 'Apt 1', + 'city' => 'Philadelphia', + 'state' => 'PA', + 'postcode' => '19123', + 'country' => 'US', + ), + 'is_paying_customer' => false, + 'avatar_url' => $customer_3->get_avatar_url(), + 'meta_data' => array(), + '_links' => array( + 'self' => array( + array( + 'href' => rest_url( '/wc/v3/customers/' . $customer_3->get_id() . '' ), + ), + ), + 'collection' => array( + array( + 'href' => rest_url( '/wc/v3/customers' ), + ), + ), + ), + ), + $customers + ); + } /** From 767abb3a8ac7f19661e5d1d011883845838c43f1 Mon Sep 17 00:00:00 2001 From: James Allan Date: Wed, 23 Jan 2019 16:47:52 +1000 Subject: [PATCH 102/401] Keep count of the number of times custom coupons apply --- includes/class-wc-discounts.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-discounts.php b/includes/class-wc-discounts.php index fea4eead963..cefd9aea611 100644 --- a/includes/class-wc-discounts.php +++ b/includes/class-wc-discounts.php @@ -519,8 +519,9 @@ class WC_Discounts { $apply_quantity = max( 0, apply_filters( 'woocommerce_coupon_get_apply_quantity', $apply_quantity, $item, $coupon, $this ) ); // Run coupon calculations. - $discount = wc_add_number_precision( $coupon->get_discount_amount( $price_to_discount / $item->quantity, $item->object, true ) ) * $apply_quantity; - $discount = wc_round_discount( min( $discounted_price, $discount ), 0 ); + $discount = wc_add_number_precision( $coupon->get_discount_amount( $price_to_discount / $item->quantity, $item->object, true ) ) * $apply_quantity; + $discount = wc_round_discount( min( $discounted_price, $discount ), 0 ); + $applied_count = $applied_count + $apply_quantity; // Store code and discount amount per item. $this->discounts[ $coupon->get_code() ][ $item->key ] += $discount; From 8f0f22c11b33f653e597f42607911da940dd35ac Mon Sep 17 00:00:00 2001 From: Refael Iliaguyev Date: Wed, 23 Jan 2019 14:49:21 +0200 Subject: [PATCH 103/401] Fix ssl check in case shop page does not exists. --- includes/admin/class-wc-admin-notices.php | 2 +- includes/api/v2/class-wc-rest-system-status-v2-controller.php | 2 +- tests/unit-tests/api/system-status.php | 2 +- tests/unit-tests/api/v2/system-status.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/admin/class-wc-admin-notices.php b/includes/admin/class-wc-admin-notices.php index b190b1caede..9828ba32732 100644 --- a/includes/admin/class-wc-admin-notices.php +++ b/includes/admin/class-wc-admin-notices.php @@ -404,7 +404,7 @@ class WC_Admin_Notices { * @since 3.5.1 */ protected static function is_ssl() { - $shop_page = 0 < wc_get_page_id( 'shop' ) ? get_permalink( wc_get_page_id( 'shop' ) ) : get_home_url(); + $shop_page = wc_get_page_permalink( 'shop' ); return ( is_ssl() && 'https' === substr( $shop_page, 0, 5 ) ); } diff --git a/includes/api/v2/class-wc-rest-system-status-v2-controller.php b/includes/api/v2/class-wc-rest-system-status-v2-controller.php index 3a9a71444d5..2ff978ce92a 100644 --- a/includes/api/v2/class-wc-rest-system-status-v2-controller.php +++ b/includes/api/v2/class-wc-rest-system-status-v2-controller.php @@ -989,7 +989,7 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller { * @return array */ public function get_security_info() { - $check_page = 0 < wc_get_page_id( 'shop' ) ? get_permalink( wc_get_page_id( 'shop' ) ) : get_home_url(); + $check_page = wc_get_page_permalink( 'shop' ); return array( 'secure_connection' => 'https' === substr( $check_page, 0, 5 ), 'hide_errors' => ! ( defined( 'WP_DEBUG' ) && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG && WP_DEBUG_DISPLAY ) || 0 === intval( ini_get( 'display_errors' ) ), diff --git a/tests/unit-tests/api/system-status.php b/tests/unit-tests/api/system-status.php index d0940bb02c8..b277c4260e5 100644 --- a/tests/unit-tests/api/system-status.php +++ b/tests/unit-tests/api/system-status.php @@ -183,7 +183,7 @@ class WC_Tests_REST_System_Status extends WC_REST_Unit_Test_Case { $settings = (array) $data['security']; $this->assertEquals( 2, count( $settings ) ); - $this->assertEquals( 'https' === substr( get_permalink( wc_get_page_id( 'shop' ) ), 0, 5 ), $settings['secure_connection'] ); + $this->assertEquals( 'https' === substr( wc_get_page_permalink( 'shop' ), 0, 5 ), $settings['secure_connection'] ); $this->assertEquals( ! ( defined( 'WP_DEBUG' ) && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG && WP_DEBUG_DISPLAY ) || 0 === intval( ini_get( 'display_errors' ) ), $settings['hide_errors'] ); } diff --git a/tests/unit-tests/api/v2/system-status.php b/tests/unit-tests/api/v2/system-status.php index 7416a80af6b..dfcb16c9df6 100644 --- a/tests/unit-tests/api/v2/system-status.php +++ b/tests/unit-tests/api/v2/system-status.php @@ -180,7 +180,7 @@ class WC_Tests_REST_System_Status_V2 extends WC_REST_Unit_Test_Case { $settings = (array) $data['security']; $this->assertEquals( 2, count( $settings ) ); - $this->assertEquals( 'https' === substr( get_permalink( wc_get_page_id( 'shop' ) ), 0, 5 ), $settings['secure_connection'] ); + $this->assertEquals( 'https' === substr( wc_get_page_permalink( 'shop' ), 0, 5 ), $settings['secure_connection'] ); $this->assertEquals( ! ( defined( 'WP_DEBUG' ) && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG && WP_DEBUG_DISPLAY ) || 0 === intval( ini_get( 'display_errors' ) ), $settings['hide_errors'] ); } From 87093519942062800de89f60ff175ffec0678de6 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Wed, 23 Jan 2019 11:11:27 -0400 Subject: [PATCH 104/401] update function name to `wc_attribute_taxonomy_slug` --- includes/abstracts/abstract-wc-widget.php | 2 +- includes/admin/class-wc-admin-importers.php | 2 +- includes/api/legacy/v1/class-wc-api-products.php | 4 ++-- includes/api/legacy/v2/class-wc-api-orders.php | 4 ++-- includes/api/legacy/v2/class-wc-api-products.php | 4 ++-- includes/api/legacy/v3/class-wc-api-orders.php | 4 ++-- includes/api/legacy/v3/class-wc-api-products.php | 4 ++-- includes/api/v1/class-wc-rest-products-controller.php | 2 +- .../api/v2/class-wc-rest-products-v2-controller.php | 2 +- includes/wc-attribute-functions.php | 10 +++++----- .../widgets/class-wc-widget-layered-nav-filters.php | 2 +- includes/widgets/class-wc-widget-layered-nav.php | 6 +++--- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/includes/abstracts/abstract-wc-widget.php b/includes/abstracts/abstract-wc-widget.php index 415c3b4f098..eb91c48d081 100644 --- a/includes/abstracts/abstract-wc-widget.php +++ b/includes/abstracts/abstract-wc-widget.php @@ -337,7 +337,7 @@ abstract class WC_Widget extends WP_Widget { // All current filters. if ( $_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes() ) { // phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found, WordPress.CodeAnalysis.AssignmentInCondition.Found foreach ( $_chosen_attributes as $name => $data ) { - $filter_name = wc_attribute_taxonomy_name_raw( $name ); + $filter_name = wc_attribute_taxonomy_slug( $name ); if ( ! empty( $data['terms'] ) ) { $link = add_query_arg( 'filter_' . $filter_name, implode( ',', $data['terms'] ), $link ); } diff --git a/includes/admin/class-wc-admin-importers.php b/includes/admin/class-wc-admin-importers.php index cef42e5e9b5..26155622d32 100644 --- a/includes/admin/class-wc-admin-importers.php +++ b/includes/admin/class-wc-admin-importers.php @@ -159,7 +159,7 @@ class WC_Admin_Importers { foreach ( $post['terms'] as $term ) { if ( strstr( $term['domain'], 'pa_' ) ) { if ( ! taxonomy_exists( $term['domain'] ) ) { - $attribute_name = wc_attribute_taxonomy_name_raw( $term['domain'] ); + $attribute_name = wc_attribute_taxonomy_slug( $term['domain'] ); // Create the taxonomy. if ( ! in_array( $attribute_name, wc_get_attribute_taxonomies(), true ) ) { diff --git a/includes/api/legacy/v1/class-wc-api-products.php b/includes/api/legacy/v1/class-wc-api-products.php index 47427dd6154..b608a3f326c 100644 --- a/includes/api/legacy/v1/class-wc-api-products.php +++ b/includes/api/legacy/v1/class-wc-api-products.php @@ -500,7 +500,7 @@ class WC_API_Products extends WC_API_Resource { // taxonomy-based attributes are prefixed with `pa_`, otherwise simply `attribute_` $attributes[] = array( - 'name' => ucwords( str_replace( 'attribute_', '', wc_attribute_taxonomy_name_raw( $attribute_name ) ) ), + 'name' => ucwords( str_replace( 'attribute_', '', wc_attribute_taxonomy_slug( $attribute_name ) ) ), 'option' => $attribute, ); } @@ -508,7 +508,7 @@ class WC_API_Products extends WC_API_Resource { foreach ( $product->get_attributes() as $attribute ) { $attributes[] = array( - 'name' => ucwords( wc_attribute_taxonomy_name_raw( $attribute['name'] ) ), + 'name' => ucwords( wc_attribute_taxonomy_slug( $attribute['name'] ) ), 'position' => $attribute['position'], 'visible' => (bool) $attribute['is_visible'], 'variation' => (bool) $attribute['is_variation'], diff --git a/includes/api/legacy/v2/class-wc-api-orders.php b/includes/api/legacy/v2/class-wc-api-orders.php index c3f6416afcd..67fc745d364 100644 --- a/includes/api/legacy/v2/class-wc-api-orders.php +++ b/includes/api/legacy/v2/class-wc-api-orders.php @@ -973,7 +973,7 @@ class WC_API_Orders extends WC_API_Resource { if ( isset( $variations ) && is_array( $variations ) ) { // start by normalizing the passed variations foreach ( $variations as $key => $value ) { - $key = str_replace( 'attribute_', '', wc_attribute_taxonomy_name_raw( $key ) ); // from get_attributes in class-wc-api-products.php + $key = str_replace( 'attribute_', '', wc_attribute_taxonomy_slug( $key ) ); // from get_attributes in class-wc-api-products.php $variations_normalized[ $key ] = strtolower( $value ); } // now search through each product child and see if our passed variations match anything @@ -981,7 +981,7 @@ class WC_API_Orders extends WC_API_Resource { $meta = array(); foreach ( get_post_meta( $variation ) as $key => $value ) { $value = $value[0]; - $key = str_replace( 'attribute_', '', wc_attribute_taxonomy_name_raw( $key ) ); + $key = str_replace( 'attribute_', '', wc_attribute_taxonomy_slug( $key ) ); $meta[ $key ] = strtolower( $value ); } // if the variation array is a part of the $meta array, we found our match diff --git a/includes/api/legacy/v2/class-wc-api-products.php b/includes/api/legacy/v2/class-wc-api-products.php index f3bf80f7d38..5804489715a 100644 --- a/includes/api/legacy/v2/class-wc-api-products.php +++ b/includes/api/legacy/v2/class-wc-api-products.php @@ -1778,7 +1778,7 @@ class WC_API_Products extends WC_API_Resource { // taxonomy-based attributes are prefixed with `pa_`, otherwise simply `attribute_` $attributes[] = array( 'name' => wc_attribute_label( str_replace( 'attribute_', '', $attribute_name ) ), - 'slug' => str_replace( 'attribute_', '', wc_attribute_taxonomy_name_raw( $attribute_name ) ), + 'slug' => str_replace( 'attribute_', '', wc_attribute_taxonomy_slug( $attribute_name ) ), 'option' => $attribute, ); } @@ -1787,7 +1787,7 @@ class WC_API_Products extends WC_API_Resource { foreach ( $product->get_attributes() as $attribute ) { $attributes[] = array( 'name' => wc_attribute_label( $attribute['name'] ), - 'slug' => wc_attribute_taxonomy_name_raw( $attribute['name'] ), + 'slug' => wc_attribute_taxonomy_slug( $attribute['name'] ), 'position' => (int) $attribute['position'], 'visible' => (bool) $attribute['is_visible'], 'variation' => (bool) $attribute['is_variation'], diff --git a/includes/api/legacy/v3/class-wc-api-orders.php b/includes/api/legacy/v3/class-wc-api-orders.php index be6576c66c9..aa2f69219f6 100644 --- a/includes/api/legacy/v3/class-wc-api-orders.php +++ b/includes/api/legacy/v3/class-wc-api-orders.php @@ -1018,7 +1018,7 @@ class WC_API_Orders extends WC_API_Resource { if ( isset( $variations ) && is_array( $variations ) ) { // start by normalizing the passed variations foreach ( $variations as $key => $value ) { - $key = str_replace( 'attribute_', '', wc_attribute_taxonomy_name_raw( $key ) ); // from get_attributes in class-wc-api-products.php + $key = str_replace( 'attribute_', '', wc_attribute_taxonomy_slug( $key ) ); // from get_attributes in class-wc-api-products.php $variations_normalized[ $key ] = strtolower( $value ); } // now search through each product child and see if our passed variations match anything @@ -1026,7 +1026,7 @@ class WC_API_Orders extends WC_API_Resource { $meta = array(); foreach ( get_post_meta( $variation ) as $key => $value ) { $value = $value[0]; - $key = str_replace( 'attribute_', '', wc_attribute_taxonomy_name_raw( $key ) ); + $key = str_replace( 'attribute_', '', wc_attribute_taxonomy_slug( $key ) ); $meta[ $key ] = strtolower( $value ); } // if the variation array is a part of the $meta array, we found our match diff --git a/includes/api/legacy/v3/class-wc-api-products.php b/includes/api/legacy/v3/class-wc-api-products.php index 825827af1c2..e8a8c3a9ede 100644 --- a/includes/api/legacy/v3/class-wc-api-products.php +++ b/includes/api/legacy/v3/class-wc-api-products.php @@ -2336,7 +2336,7 @@ class WC_API_Products extends WC_API_Resource { // taxonomy-based attributes are prefixed with `pa_`, otherwise simply `attribute_` $attributes[] = array( 'name' => wc_attribute_label( str_replace( 'attribute_', '', $attribute_name ), $product ), - 'slug' => str_replace( 'attribute_', '', wc_attribute_taxonomy_name_raw( $attribute_name ) ), + 'slug' => str_replace( 'attribute_', '', wc_attribute_taxonomy_slug( $attribute_name ) ), 'option' => $attribute, ); } @@ -2345,7 +2345,7 @@ class WC_API_Products extends WC_API_Resource { foreach ( $product->get_attributes() as $attribute ) { $attributes[] = array( 'name' => wc_attribute_label( $attribute['name'], $product ), - 'slug' => wc_attribute_taxonomy_name_raw( $attribute['name'] ), + 'slug' => wc_attribute_taxonomy_slug( $attribute['name'] ), 'position' => (int) $attribute['position'], 'visible' => (bool) $attribute['is_visible'], 'variation' => (bool) $attribute['is_variation'], diff --git a/includes/api/v1/class-wc-rest-products-controller.php b/includes/api/v1/class-wc-rest-products-controller.php index 6a9bd6d458d..dcf4d740c0b 100644 --- a/includes/api/v1/class-wc-rest-products-controller.php +++ b/includes/api/v1/class-wc-rest-products-controller.php @@ -352,7 +352,7 @@ class WC_REST_Products_V1_Controller extends WC_REST_Posts_Controller { } else { $default[] = array( 'id' => 0, - 'name' => wc_attribute_taxonomy_name_raw( $key ), + 'name' => wc_attribute_taxonomy_slug( $key ), 'option' => $value, ); } diff --git a/includes/api/v2/class-wc-rest-products-v2-controller.php b/includes/api/v2/class-wc-rest-products-v2-controller.php index 16e815ba385..a5d53a096fd 100644 --- a/includes/api/v2/class-wc-rest-products-v2-controller.php +++ b/includes/api/v2/class-wc-rest-products-v2-controller.php @@ -460,7 +460,7 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller $attributes = $product->get_attributes(); if ( ! isset( $attributes[ $slug ] ) ) { - return wc_attribute_taxonomy_name_raw( $slug ); + return wc_attribute_taxonomy_slug( $slug ); } $attribute = $attributes[ $slug ]; diff --git a/includes/wc-attribute-functions.php b/includes/wc-attribute-functions.php index adb425f1342..6a02dc278aa 100644 --- a/includes/wc-attribute-functions.php +++ b/includes/wc-attribute-functions.php @@ -117,7 +117,7 @@ function wc_attribute_taxonomy_name_by_id( $attribute_id ) { * @return int */ function wc_attribute_taxonomy_id_by_name( $name ) { - $name = wc_attribute_taxonomy_name_raw( $name ); + $name = wc_attribute_taxonomy_slug( $name ); $taxonomies = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_id', 'attribute_name' ); return isset( $taxonomies[ $name ] ) ? (int) $taxonomies[ $name ] : 0; @@ -132,7 +132,7 @@ function wc_attribute_taxonomy_id_by_name( $name ) { */ function wc_attribute_label( $name, $product = '' ) { if ( taxonomy_is_product_attribute( $name ) ) { - $name = wc_attribute_taxonomy_name_raw( $name ); + $name = wc_attribute_taxonomy_slug( $name ); $all_labels = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_label', 'attribute_name' ); $label = isset( $all_labels[ $name ] ) ? $all_labels[ $name ] : $name; } elseif ( $product ) { @@ -167,7 +167,7 @@ function wc_attribute_label( $name, $product = '' ) { function wc_attribute_orderby( $name ) { global $wc_product_attributes, $wpdb; - $name = wc_attribute_taxonomy_name_raw( $name ); + $name = wc_attribute_taxonomy_slug( $name ); if ( isset( $wc_product_attributes[ 'pa_' . $name ] ) ) { $orderby = $wc_product_attributes[ 'pa_' . $name ]->attribute_orderby; @@ -672,12 +672,12 @@ function wc_delete_attribute( $id ) { /** * Get an unprefixed product attribute name. * - * @since 3.5.3 + * @since 3.6.0 * * @param string $attribute_name Attribute name. * @return string */ -function wc_attribute_taxonomy_name_raw( $attribute_name ) { +function wc_attribute_taxonomy_slug( $attribute_name ) { $attribute_name = wc_sanitize_taxonomy_name( $attribute_name ); return 0 === strpos( $attribute_name, 'pa_' ) ? substr( $attribute_name, 3 ) : $attribute_name; } diff --git a/includes/widgets/class-wc-widget-layered-nav-filters.php b/includes/widgets/class-wc-widget-layered-nav-filters.php index 7469cda8d50..ce4ba713732 100644 --- a/includes/widgets/class-wc-widget-layered-nav-filters.php +++ b/includes/widgets/class-wc-widget-layered-nav-filters.php @@ -65,7 +65,7 @@ class WC_Widget_Layered_Nav_Filters extends WC_Widget { continue; } - $filter_name = 'filter_' . wc_attribute_taxonomy_name_raw( $taxonomy ); + $filter_name = 'filter_' . wc_attribute_taxonomy_slug( $taxonomy ); $current_filter = isset( $_GET[ $filter_name ] ) ? explode( ',', wc_clean( wp_unslash( $_GET[ $filter_name ] ) ) ) : array(); // WPCS: input var ok, CSRF ok. $current_filter = array_map( 'sanitize_title', $current_filter ); $new_filter = array_diff( $current_filter, array( $term_slug ) ); diff --git a/includes/widgets/class-wc-widget-layered-nav.php b/includes/widgets/class-wc-widget-layered-nav.php index 0503d925df7..66b9246a334 100644 --- a/includes/widgets/class-wc-widget-layered-nav.php +++ b/includes/widgets/class-wc-widget-layered-nav.php @@ -223,7 +223,7 @@ class WC_Widget_Layered_Nav extends WC_Widget { if ( $taxonomy !== $this->get_current_taxonomy() ) { $term_counts = $this->get_filtered_term_product_counts( wp_list_pluck( $terms, 'term_id' ), $taxonomy, $query_type ); $_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes(); - $taxonomy_filter_name = wc_attribute_taxonomy_name_raw( $taxonomy ); + $taxonomy_filter_name = wc_attribute_taxonomy_slug( $taxonomy ); $taxonomy_label = wc_attribute_label( $taxonomy ); /* translators: %s: taxonomy name */ @@ -423,7 +423,7 @@ class WC_Widget_Layered_Nav extends WC_Widget { continue; } - $filter_name = 'filter_' . wc_attribute_taxonomy_name_raw( $taxonomy ); + $filter_name = 'filter_' . wc_attribute_taxonomy_slug( $taxonomy ); $current_filter = isset( $_GET[ $filter_name ] ) ? explode( ',', wc_clean( wp_unslash( $_GET[ $filter_name ] ) ) ) : array(); // WPCS: input var ok, CSRF ok. $current_filter = array_map( 'sanitize_title', $current_filter ); @@ -452,7 +452,7 @@ class WC_Widget_Layered_Nav extends WC_Widget { // Add Query type Arg to URL. if ( 'or' === $query_type && ! ( 1 === count( $current_filter ) && $option_is_set ) ) { - $link = add_query_arg( 'query_type_' . wc_attribute_taxonomy_name_raw( $taxonomy ), 'or', $link ); + $link = add_query_arg( 'query_type_' . wc_attribute_taxonomy_slug( $taxonomy ), 'or', $link ); } $link = str_replace( '%2C', ',', $link ); } From a6b1c4574014cfa842fc419bcfca739b77bbb6fb Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Wed, 23 Jan 2019 11:13:25 -0400 Subject: [PATCH 105/401] phpcs sniff fixes for class-wc-rest-products-v2-controller.php --- includes/api/v2/class-wc-rest-products-v2-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/api/v2/class-wc-rest-products-v2-controller.php b/includes/api/v2/class-wc-rest-products-v2-controller.php index a5d53a096fd..d9f3be54f55 100644 --- a/includes/api/v2/class-wc-rest-products-v2-controller.php +++ b/includes/api/v2/class-wc-rest-products-v2-controller.php @@ -1209,7 +1209,7 @@ class WC_REST_Products_V2_Controller extends WC_REST_Legacy_Products_Controller * * @param WC_Product $product Product instance. * @param array $downloads Downloads data. - * @param int $deprecated Deprecated since 3.0 + * @param int $deprecated Deprecated since 3.0. * * @return WC_Product */ From 398bbe3754551f1f03b2c1f80d8526f4e66a7c91 Mon Sep 17 00:00:00 2001 From: Refael Iliaguyev Date: Wed, 23 Jan 2019 17:26:17 +0200 Subject: [PATCH 106/401] sniff fixes --- tests/unit-tests/api/system-status.php | 4 ++-- tests/unit-tests/api/v2/system-status.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit-tests/api/system-status.php b/tests/unit-tests/api/system-status.php index b277c4260e5..03c5170ef28 100644 --- a/tests/unit-tests/api/system-status.php +++ b/tests/unit-tests/api/system-status.php @@ -81,7 +81,7 @@ class WC_Tests_REST_System_Status extends WC_REST_Unit_Test_Case { $data = $response->get_data(); $environment = (array) $data['environment']; - // Make sure all expected data is present + // Make sure all expected data is present. $this->assertEquals( 32, count( $environment ) ); // Test some responses to make sure they match up. @@ -468,7 +468,7 @@ class WC_Tests_REST_System_Status extends WC_REST_Unit_Test_Case { * * This function is called by WP_HTTP_TestCase::http_request_listner(). * - * @param array $request Request arguments. + * @param array $request Request arguments. * @param string $url URL of the request. * * @return array|false mocked response or false to let WP perform a regular request. diff --git a/tests/unit-tests/api/v2/system-status.php b/tests/unit-tests/api/v2/system-status.php index dfcb16c9df6..6fe65d3e2df 100644 --- a/tests/unit-tests/api/v2/system-status.php +++ b/tests/unit-tests/api/v2/system-status.php @@ -78,7 +78,7 @@ class WC_Tests_REST_System_Status_V2 extends WC_REST_Unit_Test_Case { $data = $response->get_data(); $environment = (array) $data['environment']; - // Make sure all expected data is present + // Make sure all expected data is present. $this->assertEquals( 32, count( $environment ) ); // Test some responses to make sure they match up. From 613a095362e884fa22e2d95d4876691f09e04d6f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 23 Jan 2019 15:31:26 +0000 Subject: [PATCH 107/401] Exclude paged from price slider --- includes/widgets/class-wc-widget-price-filter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/widgets/class-wc-widget-price-filter.php b/includes/widgets/class-wc-widget-price-filter.php index 0949c7518dc..6014dad2890 100644 --- a/includes/widgets/class-wc-widget-price-filter.php +++ b/includes/widgets/class-wc-widget-price-filter.php @@ -106,7 +106,7 @@ class WC_Widget_Price_Filter extends WC_Widget { - ' . wc_query_string_form_fields( null, array( 'min_price', 'max_price' ), '', true ) . ' + ' . wc_query_string_form_fields( null, array( 'min_price', 'max_price', 'paged' ), '', true ) . '
    From a2f77ba7ec5b7cc64a29410aab687f732d14be8f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 23 Jan 2019 16:19:40 +0000 Subject: [PATCH 108/401] phpcs --- woocommerce.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/woocommerce.php b/woocommerce.php index 12fb2f89902..e172a3b0c94 100644 --- a/woocommerce.php +++ b/woocommerce.php @@ -12,9 +12,7 @@ * @package WooCommerce */ -if ( ! defined( 'ABSPATH' ) ) { - exit; // Exit if accessed directly. -} +defined( 'ABSPATH' ) || exit; // Define WC_PLUGIN_FILE. if ( ! defined( 'WC_PLUGIN_FILE' ) ) { @@ -27,14 +25,12 @@ if ( ! class_exists( 'WooCommerce' ) ) { } /** - * Main instance of WooCommerce. - * - * Returns the main instance of WC to prevent the need to use globals. + * Returns the main instance of WC. * * @since 2.1 * @return WooCommerce */ -function WC() { +function WC() { // phpcs:ignore return WooCommerce::instance(); } From 59c97539b3891fb225025f1adf5fb1071a754182 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 23 Jan 2019 16:20:26 +0000 Subject: [PATCH 109/401] Trigger loaded hook once plugins are loaded. --- includes/class-woocommerce.php | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php index f50f5923bf5..dcdef8fe296 100644 --- a/includes/class-woocommerce.php +++ b/includes/class-woocommerce.php @@ -154,7 +154,18 @@ final class WooCommerce { $this->define_constants(); $this->includes(); $this->init_hooks(); + } + /** + * When WP has loaded all plugins, trigger the `woocommerce_loaded` hook. + * + * This ensures `woocommerce_loaded` is called only after all other plugins + * are loaded, to avoid issues caused by plugin directory naming changing + * the load order. See #21524 for details. + * + * @since 3.6.0 + */ + public function on_plugins_loaded() { do_action( 'woocommerce_loaded' ); } @@ -166,6 +177,8 @@ final class WooCommerce { private function init_hooks() { register_activation_hook( WC_PLUGIN_FILE, array( 'WC_Install', 'install' ) ); register_shutdown_function( array( $this, 'log_errors' ) ); + + add_action( 'plugins_loaded', array( $this, 'on_plugins_loaded' ), -1 ); add_action( 'after_setup_theme', array( $this, 'setup_environment' ) ); add_action( 'after_setup_theme', array( $this, 'include_template_functions' ), 11 ); add_action( 'init', array( $this, 'init' ), 0 ); @@ -183,7 +196,7 @@ final class WooCommerce { */ public function log_errors() { $error = error_get_last(); - if ( in_array( $error['type'], array( E_ERROR, E_PARSE, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR ) ) ) { + if ( in_array( $error['type'], array( E_ERROR, E_PARSE, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR ), true ) ) { $logger = wc_get_logger(); $logger->critical( /* translators: 1: error message 2: file name and path 3: line number */ @@ -525,7 +538,11 @@ final class WooCommerce { * Ensure theme and server variable compatibility and setup image sizes. */ public function setup_environment() { - /* @deprecated 2.2 Use WC()->template_path() instead. */ + /** + * WC_TEMPLATE_PATH constant. + * + * @deprecated 2.2 Use WC()->template_path() instead. + */ $this->define( 'WC_TEMPLATE_PATH', $this->template_path() ); $this->add_thumbnail_support(); @@ -564,7 +581,11 @@ final class WooCommerce { add_image_size( 'woocommerce_single', $single['width'], $single['height'], $single['crop'] ); add_image_size( 'woocommerce_gallery_thumbnail', $gallery_thumbnail['width'], $gallery_thumbnail['height'], $gallery_thumbnail['crop'] ); - // Registered for bw compat. @todo remove in 4.0. + /** + * Legacy image sizes. + * + * @deprecated These sizes will be removed in 4.0. + */ add_image_size( 'shop_catalog', $thumbnail['width'], $thumbnail['height'], $thumbnail['crop'] ); add_image_size( 'shop_single', $single['width'], $single['height'], $single['crop'] ); add_image_size( 'shop_thumbnail', $gallery_thumbnail['width'], $gallery_thumbnail['height'], $gallery_thumbnail['crop'] ); From 6709049cbcf8fe4472e35879efb27190aca4cefe Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Wed, 23 Jan 2019 14:04:04 -0400 Subject: [PATCH 110/401] round variation percentage price adjustments to decimal setting --- includes/class-wc-ajax.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php index 8b20ab0d13d..d0ea535f9f1 100644 --- a/includes/class-wc-ajax.php +++ b/includes/class-wc-ajax.php @@ -2242,7 +2242,7 @@ class WC_AJAX { if ( '%' === substr( $value, -1 ) ) { $percent = wc_format_decimal( substr( $value, 0, -1 ) ); - $field_value += ( ( $field_value / 100 ) * $percent ) * "{$operator}1"; + $field_value += round( ( $field_value / 100 ) * $percent, wc_get_price_decimals() ) * "{$operator}1"; } else { $field_value += $value * "{$operator}1"; } From 9b655c0ba7bbd6bb83cd9936b142678699bf87d1 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 23 Jan 2019 21:29:55 +0000 Subject: [PATCH 111/401] Use hasSelectiveRefresh --- assets/js/frontend/cart-fragments.js | 14 ++++++++ includes/widgets/class-wc-widget-cart.php | 44 +++-------------------- 2 files changed, 19 insertions(+), 39 deletions(-) diff --git a/assets/js/frontend/cart-fragments.js b/assets/js/frontend/cart-fragments.js index 4f04b77b05e..563840edea9 100644 --- a/assets/js/frontend/cart-fragments.js +++ b/assets/js/frontend/cart-fragments.js @@ -165,4 +165,18 @@ jQuery( function( $ ) { $( document.body ).on( 'adding_to_cart', function() { $( '.hide_cart_widget_if_empty' ).closest( '.widget_shopping_cart' ).show(); }); + + // Customiser support. + var hasSelectiveRefresh = ( + 'undefined' !== typeof wp && + wp.customize && + wp.customize.selectiveRefresh && + wp.customize.widgetsPreview && + wp.customize.widgetsPreview.WidgetPartial + ); + if ( hasSelectiveRefresh ) { + wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function() { + refresh_cart_fragment(); + } ); + } }); diff --git a/includes/widgets/class-wc-widget-cart.php b/includes/widgets/class-wc-widget-cart.php index d81a8e863cf..28aa4403654 100644 --- a/includes/widgets/class-wc-widget-cart.php +++ b/includes/widgets/class-wc-widget-cart.php @@ -37,7 +37,7 @@ class WC_Widget_Cart extends WC_Widget { ); if ( is_customize_preview() ) { - $this->enqueue_ajax_script(); + wp_enqueue_script( 'wc-cart-fragments' ); } parent::__construct(); @@ -58,6 +58,10 @@ class WC_Widget_Cart extends WC_Widget { $hide_if_empty = empty( $instance['hide_if_empty'] ) ? 0 : 1; + if ( empty( $instance['title'] ) ) { + $instance['title'] = __( 'Cart', 'woocommerce' ); + } + $this->widget_start( $args, $instance ); if ( $hide_if_empty ) { @@ -73,42 +77,4 @@ class WC_Widget_Cart extends WC_Widget { $this->widget_end( $args ); } - - /** - * This method provides the JS script which will execute on addition of a new widget. - * - * @todo 1. In this function there is redundency of code, which needs to be fixed. - * 2. Also sort out a better way to fix the later raw JS code block. May be do it a more WordPress way. - * - * @return void - */ - private function enqueue_ajax_script() { - $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; - - wp_register_script( 'wc-cart-fragments', WC()->plugin_url() . '/assets/js/frontend/cart-fragments' . $suffix . '.js', array( 'jquery', 'js-cookie' ), WC_VERSION, true ); - - wp_localize_script( 'wc-cart-fragments', 'wc_cart_fragments_params', - array( - 'ajax_url' => WC()->ajax_url(), - 'wc_ajax_url' => WC_AJAX::get_endpoint( '%%endpoint%%' ), - 'cart_hash_key' => apply_filters( 'woocommerce_cart_hash_key', 'wc_cart_hash_' . md5( get_current_blog_id() . '_' . get_site_url( get_current_blog_id(), '/' ) . get_template() ) ), - 'fragment_name' => apply_filters( 'woocommerce_cart_fragment_name', 'wc_fragments_' . md5( get_current_blog_id() . '_' . get_site_url( get_current_blog_id(), '/' ) . get_template() ) ), - ) - ); - - wp_enqueue_script( 'wc-cart-fragments' ); - - ?> - - Date: Thu, 24 Jan 2019 12:06:47 +0100 Subject: [PATCH 112/401] Updated check to only cover non-legacy REST API requests. Updated filter name. --- includes/class-woocommerce.php | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php index 4becd8f2ca8..ce9ba563f7b 100644 --- a/includes/class-woocommerce.php +++ b/includes/class-woocommerce.php @@ -228,31 +228,26 @@ final class WooCommerce { } /** - * Returns true if the request is a REST API request. + * Returns true if the request is a non-legacy REST API request. + * + * Legacy REST requests should run extra code for backwards compatibility. * * @return bool */ - private static function is_rest_api_request() { + private static function is_nonlegacy_rest_api_request() { + // TODO: replace this function once core WP function is available: https://core.trac.wordpress.org/ticket/42061. $request_uri = $_SERVER['REQUEST_URI']; // @codingStandardsIgnoreLine if ( empty( $request_uri ) ) { return false; } - // Legacy API. - if ( 0 === strpos( $request_uri, '/wc-api/' ) ) { - return true; - } - - // New REST API. + // Non-legacy REST API prefix. $rest_prefix = trailingslashit( rest_get_url_prefix() ); - // Check if this is WC endpoint. - $woocommerce = ( false !== strpos( $request_uri, $rest_prefix . 'wc/' ) ); + // Check if this is a WC endpoint. + $is_woocommerce_endpoint = ( false !== strpos( $request_uri, $rest_prefix . 'wc/' ) ); - // Allow third party plugins use our authentication methods. - $third_party = ( false !== strpos( $request_uri, $rest_prefix . 'wc-' ) ); - - return apply_filters( 'woocommerce_rest_is_request_to_rest_api', $woocommerce || $third_party ); + return apply_filters( 'woocommerce_is_request_to_nonlegacy_rest_api', $is_woocommerce_endpoint ); } /** @@ -270,7 +265,7 @@ final class WooCommerce { case 'cron': return defined( 'DOING_CRON' ); case 'frontend': - return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' ) && ! self::is_rest_api_request(); + return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' ) && ! self::is_nonlegacy_rest_api_request(); } } From 78e4ef5f38a322ebd62353e68e0fc79c8c7df6a5 Mon Sep 17 00:00:00 2001 From: Prince Ahmed <36558734+princeahmed@users.noreply.github.com> Date: Thu, 24 Jan 2019 21:59:09 +0600 Subject: [PATCH 113/401] Fix test get request During test Remote GET Request, in the error checking area used $post_response instead of using $get_response. --- includes/api/v2/class-wc-rest-system-status-v2-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/api/v2/class-wc-rest-system-status-v2-controller.php b/includes/api/v2/class-wc-rest-system-status-v2-controller.php index 2ff978ce92a..8126d0062a9 100644 --- a/includes/api/v2/class-wc-rest-system-status-v2-controller.php +++ b/includes/api/v2/class-wc-rest-system-status-v2-controller.php @@ -602,7 +602,7 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller { // Test GET requests. $get_response = wp_safe_remote_get( 'https://woocommerce.com/wc-api/product-key-api?request=ping&network=' . ( is_multisite() ? '1' : '0' ) ); $get_response_successful = false; - if ( ! is_wp_error( $post_response ) && $post_response['response']['code'] >= 200 && $post_response['response']['code'] < 300 ) { + if ( ! is_wp_error( $get_response ) && $get_response['response']['code'] >= 200 && $get_response['response']['code'] < 300 ) { $get_response_successful = true; } From 8078e6bfc2c27cfd43c3f973f92556092cee05a1 Mon Sep 17 00:00:00 2001 From: claudiulodro Date: Thu, 24 Jan 2019 10:47:05 -0800 Subject: [PATCH 114/401] Simplified title check that works on all wc widgets --- includes/abstracts/abstract-wc-widget.php | 10 +++++++++- includes/widgets/class-wc-widget-layered-nav.php | 10 ---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/includes/abstracts/abstract-wc-widget.php b/includes/abstracts/abstract-wc-widget.php index 34f5e756ea4..d4218f1944d 100644 --- a/includes/abstracts/abstract-wc-widget.php +++ b/includes/abstracts/abstract-wc-widget.php @@ -129,7 +129,15 @@ abstract class WC_Widget extends WP_Widget { * @return string */ protected function get_instance_title( $instance ) { - return isset( $instance['title'] ) ? $instance['title'] : ''; + if ( isset( $instance['title'] ) ) { + return $instance['title']; + } + + if ( isset( $this->settings, $this->settings['title'], $this->settings['title']['std'] ) ) { + return $this->settings['title']['std']; + } + + return ''; } /** diff --git a/includes/widgets/class-wc-widget-layered-nav.php b/includes/widgets/class-wc-widget-layered-nav.php index 07764297282..17771126f45 100644 --- a/includes/widgets/class-wc-widget-layered-nav.php +++ b/includes/widgets/class-wc-widget-layered-nav.php @@ -101,16 +101,6 @@ class WC_Widget_Layered_Nav extends WC_Widget { ); } - /** - * Get this widgets title. - * - * @param array $instance Array of instance options. - * @return string - */ - protected function get_instance_title( $instance ) { - return isset( $instance['title'] ) ? $instance['title'] : __( 'Filter by', 'woocommerce' ); - } - /** * Get this widgets taxonomy. * From e73eabf679bef7fb65b9983e202c07aa7d0e9b41 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Thu, 24 Jan 2019 15:38:45 -0400 Subject: [PATCH 115/401] use `name` for POST/PUT category image name for consistency in api V3 --- ...-wc-rest-product-categories-controller.php | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/includes/api/class-wc-rest-product-categories-controller.php b/includes/api/class-wc-rest-product-categories-controller.php index 329c87cd400..7065c82995e 100644 --- a/includes/api/class-wc-rest-product-categories-controller.php +++ b/includes/api/class-wc-rest-product-categories-controller.php @@ -209,4 +209,61 @@ class WC_REST_Product_Categories_Controller extends WC_REST_Product_Categories_V return $this->add_additional_fields_schema( $schema ); } + + /** + * Update term meta fields. + * + * @param WP_Term $term Term object. + * @param WP_REST_Request $request Request instance. + * @return bool|WP_Error + * + * @since 3.5.5 + */ + protected function update_term_meta_fields( $term, $request ) { + $id = (int) $term->term_id; + + if ( isset( $request['display'] ) ) { + update_woocommerce_term_meta( $id, 'display_type', 'default' === $request['display'] ? '' : $request['display'] ); + } + + if ( isset( $request['menu_order'] ) ) { + update_woocommerce_term_meta( $id, 'order', $request['menu_order'] ); + } + + if ( isset( $request['image'] ) ) { + if ( empty( $request['image']['id'] ) && ! empty( $request['image']['src'] ) ) { + $upload = wc_rest_upload_image_from_url( esc_url_raw( $request['image']['src'] ) ); + + if ( is_wp_error( $upload ) ) { + return $upload; + } + + $image_id = wc_rest_set_uploaded_image_as_attachment( $upload ); + } else { + $image_id = isset( $request['image']['id'] ) ? absint( $request['image']['id'] ) : 0; + } + + // Check if image_id is a valid image attachment before updating the term meta. + if ( $image_id && wp_attachment_is_image( $image_id ) ) { + update_woocommerce_term_meta( $id, 'thumbnail_id', $image_id ); + + // Set the image alt. + if ( ! empty( $request['image']['alt'] ) ) { + update_post_meta( $image_id, '_wp_attachment_image_alt', wc_clean( $request['image']['alt'] ) ); + } + + // Set the image title. + if ( ! empty( $request['image']['name'] ) ) { + wp_update_post( array( + 'ID' => $image_id, + 'post_title' => wc_clean( $request['image']['name'] ), + ) ); + } + } else { + delete_woocommerce_term_meta( $id, 'thumbnail_id' ); + } + } + + return true; + } } From 78e5189334caf4e611af2d5ff1a69ee54fbc1956 Mon Sep 17 00:00:00 2001 From: claudiulodro Date: Thu, 24 Jan 2019 11:49:44 -0800 Subject: [PATCH 116/401] Better @id generation for product structured data --- includes/class-wc-structured-data.php | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/includes/class-wc-structured-data.php b/includes/class-wc-structured-data.php index ac743c68462..13f188efc57 100644 --- a/includes/class-wc-structured-data.php +++ b/includes/class-wc-structured-data.php @@ -195,15 +195,16 @@ class WC_Structured_Data { $shop_name = get_bloginfo( 'name' ); $shop_url = home_url(); $currency = get_woocommerce_currency(); + $permalink = get_permalink( $product->get_id() ); $markup = array( '@type' => 'Product', - '@id' => get_permalink( $product->get_id() ), + '@id' => $permalink . '#product', // Append '#product' to differentiate between this @id and the @id generated for the Breadcrumblist. 'name' => $product->get_name(), ); if ( apply_filters( 'woocommerce_structured_data_product_limit', is_product_taxonomy() || is_shop() ) ) { - $markup['url'] = $markup['@id']; + $markup['url'] = $permalink; $this->set_data( apply_filters( 'woocommerce_structured_data_product_limited', $markup, $product ) ); return; @@ -250,7 +251,7 @@ class WC_Structured_Data { $markup_offer += array( 'priceCurrency' => $currency, 'availability' => 'https://schema.org/' . ( $product->is_in_stock() ? 'InStock' : 'OutOfStock' ), - 'url' => $markup['@id'], + 'url' => $permalink, 'seller' => array( '@type' => 'Organization', 'name' => $shop_name, @@ -329,12 +330,6 @@ class WC_Structured_Data { $markup['itemListElement'] = array(); foreach ( $crumbs as $key => $crumb ) { - // Don't add the current page to the breadcrumb list on product pages, - // otherwise Google will not recognize both the BreadcrumbList and Product structured data. - if ( is_product() && count( $crumbs ) - 1 === $key ) { - continue; - } - $markup['itemListElement'][ $key ] = array( '@type' => 'ListItem', 'position' => $key + 1, From 33584e767fefa8291519aaa94427a185d7bfa8d2 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Fri, 25 Jan 2019 10:07:32 +0100 Subject: [PATCH 117/401] Renamed function As legacy REST API will be removed soon anyway. --- includes/class-woocommerce.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php index ce9ba563f7b..1894173fceb 100644 --- a/includes/class-woocommerce.php +++ b/includes/class-woocommerce.php @@ -230,18 +230,18 @@ final class WooCommerce { /** * Returns true if the request is a non-legacy REST API request. * - * Legacy REST requests should run extra code for backwards compatibility. + * Legacy REST requests should still run some extra code for backwards compatibility. * * @return bool */ - private static function is_nonlegacy_rest_api_request() { + private static function is_rest_api_request() { // TODO: replace this function once core WP function is available: https://core.trac.wordpress.org/ticket/42061. $request_uri = $_SERVER['REQUEST_URI']; // @codingStandardsIgnoreLine if ( empty( $request_uri ) ) { return false; } - // Non-legacy REST API prefix. + // REST API prefix. $rest_prefix = trailingslashit( rest_get_url_prefix() ); // Check if this is a WC endpoint. @@ -265,7 +265,7 @@ final class WooCommerce { case 'cron': return defined( 'DOING_CRON' ); case 'frontend': - return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' ) && ! self::is_nonlegacy_rest_api_request(); + return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' ) && ! self::is_rest_api_request(); } } From 60a6895eee20aa07357ff86b1b9c3611ffd164c2 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Fri, 25 Jan 2019 09:53:37 -0400 Subject: [PATCH 118/401] php sniff fixes for class-wc-rest-product-categories-controller.php --- .../class-wc-rest-product-categories-controller.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/includes/api/class-wc-rest-product-categories-controller.php b/includes/api/class-wc-rest-product-categories-controller.php index 7065c82995e..58cbdbae51b 100644 --- a/includes/api/class-wc-rest-product-categories-controller.php +++ b/includes/api/class-wc-rest-product-categories-controller.php @@ -181,7 +181,7 @@ class WC_REST_Product_Categories_Controller extends WC_REST_Product_Categories_V 'format' => 'uri', 'context' => array( 'view', 'edit' ), ), - 'name' => array( + 'name' => array( 'description' => __( 'Image name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), @@ -254,10 +254,12 @@ class WC_REST_Product_Categories_Controller extends WC_REST_Product_Categories_V // Set the image title. if ( ! empty( $request['image']['name'] ) ) { - wp_update_post( array( - 'ID' => $image_id, - 'post_title' => wc_clean( $request['image']['name'] ), - ) ); + wp_update_post( + array( + 'ID' => $image_id, + 'post_title' => wc_clean( $request['image']['name'] ), + ) + ); } } else { delete_woocommerce_term_meta( $id, 'thumbnail_id' ); From d7e32e1c779679ed094f0bd02cf1ce400379a3ec Mon Sep 17 00:00:00 2001 From: Ian Jenkins Date: Fri, 25 Jan 2019 15:11:58 +0000 Subject: [PATCH 119/401] Remove possibe superfluous wp_attachment_is_image() filter. The reason to remove this is that this function uses get_post() under the hood which always assumes the attachment is on the same site, where as if you're using a plugin such as https://github.com/humanmade/network-media-library it might not be. I'm not sure if there's any adverse affects of not doing this filtering, from my testing, it still seems to work in the same way. --- includes/abstracts/abstract-wc-product.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index d4441652c8e..45862854302 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -1257,10 +1257,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { public function set_gallery_image_ids( $image_ids ) { $image_ids = wp_parse_id_list( $image_ids ); - if ( $this->get_object_read() ) { - $image_ids = array_filter( $image_ids, 'wp_attachment_is_image' ); - } - $this->set_prop( 'gallery_image_ids', $image_ids ); } From 65bb805cbb6d685763358ccf114212a36fd4ad1a Mon Sep 17 00:00:00 2001 From: Galen Wright-Watson Date: Fri, 25 Jan 2019 18:08:00 -0800 Subject: [PATCH 120/401] Fix: #22572-can't access settings ("The link you followed has expired"). Cause: if anything adds to `$_POST`, this triggers WooCommerce to try to save settings; if there's no nonce, authorization fails, resulting in the message. Soln: check for specific element ('save') in `$_POST` to determine whether to save elements, rather than testing that `$_POST` is non-empty. --- includes/admin/class-wc-admin-menus.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/admin/class-wc-admin-menus.php b/includes/admin/class-wc-admin-menus.php index cfe052da251..ddbfe05aba0 100644 --- a/includes/admin/class-wc-admin-menus.php +++ b/includes/admin/class-wc-admin-menus.php @@ -98,9 +98,9 @@ class WC_Admin_Menus { $current_section = empty( $_REQUEST['section'] ) ? '' : sanitize_title( wp_unslash( $_REQUEST['section'] ) ); // WPCS: input var okay, CSRF ok. // Save settings if data has been posted. - if ( '' !== $current_section && apply_filters( "woocommerce_save_settings_{$current_tab}_{$current_section}", ! empty( $_POST ) ) ) { // WPCS: input var okay, CSRF ok. + if ( '' !== $current_section && apply_filters( "woocommerce_save_settings_{$current_tab}_{$current_section}", ! empty( $_POST['save'] ) ) ) { // WPCS: input var okay, CSRF ok. WC_Admin_Settings::save(); - } elseif ( '' === $current_section && apply_filters( "woocommerce_save_settings_{$current_tab}", ! empty( $_POST ) ) ) { // WPCS: input var okay, CSRF ok. + } elseif ( '' === $current_section && apply_filters( "woocommerce_save_settings_{$current_tab}", ! empty( $_POST['save'] ) ) ) { // WPCS: input var okay, CSRF ok. WC_Admin_Settings::save(); } From d32f189f276362d71c79a4cf20a6308c711544e5 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Sat, 26 Jan 2019 23:57:32 -0400 Subject: [PATCH 121/401] recalculate coupons after adding to order --- includes/abstracts/abstract-wc-order.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index cb16296a145..c44f8ec09eb 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -952,10 +952,10 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { } $this->set_coupon_discount_amounts( $discounts ); - $this->set_item_discount_amounts( $discounts ); + $this->save(); // Recalculate totals and taxes. - $this->calculate_totals( true ); + $this->recalculate_coupons(); // Record usage so counts and validation is correct. $used_by = $this->get_user_id(); From baeccdc5477a175fbcf29cd36bd516ef5b2521d8 Mon Sep 17 00:00:00 2001 From: Galen Wright-Watson Date: Sat, 26 Jan 2019 23:07:43 -0800 Subject: [PATCH 122/401] Fix: #22577-"bad flag in substitute command" if password has a '/' character. Cause: unescaped special characters (forward slash) in variable get interpreted as part of sed script. Soln: escape forward slashes when interpolating into sed script. --- tests/bin/install.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/bin/install.sh b/tests/bin/install.sh index 88667caacf0..941400efbcf 100755 --- a/tests/bin/install.sh +++ b/tests/bin/install.sh @@ -91,7 +91,9 @@ install_test_suite() { sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php - sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php + # escape the regex delim if not already escaped (i.e. if preceded by an even number of backslashes) + E_DB_PASS=$(echo $DB_PASS | sed -E -e 's%((^|[^\\])(\\\\)*)/%\1\\/%') + sed $ioption "s/yourpasswordhere/${E_DB_PASS}/" "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php fi From 3f24ad8d4c264d6739f11a188f0424176a0117a7 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Sun, 27 Jan 2019 11:38:08 -0400 Subject: [PATCH 123/401] phpcs sniff fixes for abstract-wc-order.php --- includes/abstracts/abstract-wc-order.php | 45 ++++++++++++++---------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index c44f8ec09eb..3d9c1ab2bf4 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -403,7 +403,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { $subtotal += $item->get_subtotal(); } - return apply_filters( 'woocommerce_order_get_subtotal', (double) $subtotal, $this ); + return apply_filters( 'woocommerce_order_get_subtotal', (float) $subtotal, $this ); } /** @@ -692,13 +692,16 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { * @return string */ protected function type_to_group( $type ) { - $type_to_group = apply_filters( 'woocommerce_order_type_to_group', array( - 'line_item' => 'line_items', - 'tax' => 'tax_lines', - 'shipping' => 'shipping_lines', - 'fee' => 'fee_lines', - 'coupon' => 'coupon_lines', - ) ); + $type_to_group = apply_filters( + 'woocommerce_order_type_to_group', + array( + 'line_item' => 'line_items', + 'tax' => 'tax_lines', + 'shipping' => 'shipping_lines', + 'fee' => 'fee_lines', + 'coupon' => 'coupon_lines', + ) + ); return isset( $type_to_group[ $type ] ) ? $type_to_group[ $type ] : ''; } @@ -1285,12 +1288,15 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { $tax_based_on = 'billing'; } - $args = wp_parse_args( $args, array( - 'country' => 'billing' === $tax_based_on ? $this->get_billing_country() : $this->get_shipping_country(), - 'state' => 'billing' === $tax_based_on ? $this->get_billing_state() : $this->get_shipping_state(), - 'postcode' => 'billing' === $tax_based_on ? $this->get_billing_postcode() : $this->get_shipping_postcode(), - 'city' => 'billing' === $tax_based_on ? $this->get_billing_city() : $this->get_shipping_city(), - ) ); + $args = wp_parse_args( + $args, + array( + 'country' => 'billing' === $tax_based_on ? $this->get_billing_country() : $this->get_shipping_country(), + 'state' => 'billing' === $tax_based_on ? $this->get_billing_state() : $this->get_shipping_state(), + 'postcode' => 'billing' === $tax_based_on ? $this->get_billing_postcode() : $this->get_shipping_postcode(), + 'city' => 'billing' === $tax_based_on ? $this->get_billing_city() : $this->get_shipping_city(), + ) + ); // Default to base. if ( 'base' === $tax_based_on || empty( $args['country'] ) ) { @@ -1619,10 +1625,13 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { if ( 'excl' === $tax_display ) { $ex_tax_label = $this->get_prices_include_tax() ? 1 : 0; - $subtotal = wc_price( $this->get_line_subtotal( $item ), array( - 'ex_tax_label' => $ex_tax_label, - 'currency' => $this->get_currency(), - ) ); + $subtotal = wc_price( + $this->get_line_subtotal( $item ), + array( + 'ex_tax_label' => $ex_tax_label, + 'currency' => $this->get_currency(), + ) + ); } else { $subtotal = wc_price( $this->get_line_subtotal( $item, true ), array( 'currency' => $this->get_currency() ) ); } From f42c42066b30c09f296d5c3f0952bcc11f3f288b Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Sun, 27 Jan 2019 18:30:38 -0400 Subject: [PATCH 124/401] include refunded orders in top sellers, earners in sales by product report --- includes/admin/reports/class-wc-report-sales-by-product.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/includes/admin/reports/class-wc-report-sales-by-product.php b/includes/admin/reports/class-wc-report-sales-by-product.php index ddd2303b9c6..b96223cdcb6 100644 --- a/includes/admin/reports/class-wc-report-sales-by-product.php +++ b/includes/admin/reports/class-wc-report-sales-by-product.php @@ -254,6 +254,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { 'limit' => 12, 'query_type' => 'get_results', 'filter_range' => true, + 'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ), ) ); @@ -350,6 +351,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { 'limit' => 12, 'query_type' => 'get_results', 'filter_range' => true, + 'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ), ) ); From a93418b826db66db9c1c74fe68054fdfb83182e2 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Sun, 27 Jan 2019 19:27:30 -0400 Subject: [PATCH 125/401] phpcs sniff fixes for class-wc-report-sales-by-product.php --- .../class-wc-report-sales-by-product.php | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/includes/admin/reports/class-wc-report-sales-by-product.php b/includes/admin/reports/class-wc-report-sales-by-product.php index b96223cdcb6..ad80e5ac338 100644 --- a/includes/admin/reports/class-wc-report-sales-by-product.php +++ b/includes/admin/reports/class-wc-report-sales-by-product.php @@ -12,8 +12,6 @@ if ( ! defined( 'ABSPATH' ) ) { /** * WC_Report_Sales_By_Product * - * @author WooThemes - * @category Admin * @package WooCommerce/Admin/Reports * @version 2.1.0 */ @@ -44,11 +42,13 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { * Constructor. */ public function __construct() { + // @codingStandardsIgnoreStart if ( isset( $_GET['product_ids'] ) && is_array( $_GET['product_ids'] ) ) { $this->product_ids = array_filter( array_map( 'absint', $_GET['product_ids'] ) ); } elseif ( isset( $_GET['product_ids'] ) ) { $this->product_ids = array_filter( array( absint( $_GET['product_ids'] ) ) ); } + // @codingStandardsIgnoreEnd } /** @@ -78,8 +78,8 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { 'relation' => 'OR', array( 'type' => 'order_item_meta', - 'meta_key' => array( '_product_id', '_variation_id' ), - 'meta_value' => $this->product_ids, + 'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_key + 'meta_value' => $this->product_ids, // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_value 'operator' => 'IN', ), ), @@ -103,8 +103,8 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { 'relation' => 'OR', array( 'type' => 'order_item_meta', - 'meta_key' => array( '_product_id', '_variation_id' ), - 'meta_value' => $this->product_ids, + 'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_key + 'meta_value' => $this->product_ids, // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_value 'operator' => 'IN', ), ), @@ -148,9 +148,9 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { 'item_count' => '#d4d9dc', ); - $current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; + $current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; //phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification - if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ) ) ) { + if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ), true ) ) { $current_range = '7day'; } @@ -297,8 +297,8 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { 'where_meta' => array( array( 'type' => 'order_item_meta', - 'meta_key' => '_line_subtotal', - 'meta_value' => '0', + 'meta_key' => '_line_subtotal', // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_key + 'meta_value' => '0', // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_value 'operator' => '=', ), ), @@ -398,7 +398,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { */ public function get_export_button() { - $current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; + $current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; //phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification ?> 'OR', array( 'type' => 'order_item_meta', - 'meta_key' => array( '_product_id', '_variation_id' ), - 'meta_value' => $this->product_ids, + 'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_key + 'meta_value' => $this->product_ids, // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_value 'operator' => 'IN', ), ), @@ -489,8 +489,8 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { 'relation' => 'OR', array( 'type' => 'order_item_meta', - 'meta_key' => array( '_product_id', '_variation_id' ), - 'meta_value' => $this->product_ids, + 'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_key + 'meta_value' => $this->product_ids, // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_value 'operator' => 'IN', ), ), @@ -506,7 +506,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { $order_item_amounts = $this->prepare_chart_data( $order_item_amounts, 'post_date', 'order_item_amount', $this->chart_interval, $this->start_date, $this->chart_groupby ); // Encode in json format. - $chart_data = json_encode( + $chart_data = wp_json_encode( array( 'order_item_counts' => array_values( $order_item_counts ), 'order_item_amounts' => array_values( $order_item_amounts ), From d888f456f8796f202879502c6e315ca0c703b68c Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Sun, 27 Jan 2019 20:49:02 -0400 Subject: [PATCH 126/401] remove line item subtotal adjustment from api coupon unit test --- tests/unit-tests/api/orders.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/unit-tests/api/orders.php b/tests/unit-tests/api/orders.php index 70edbc27572..88b787a0b44 100644 --- a/tests/unit-tests/api/orders.php +++ b/tests/unit-tests/api/orders.php @@ -426,13 +426,6 @@ class WC_Tests_API_Orders extends WC_REST_Unit_Test_Case { $request = new WP_REST_Request( 'PUT', '/wc/v3/orders/' . $order->get_id() ); $request->set_body_params( array( - 'line_items' => array( - array( - 'id' => $order_item->get_id(), - 'product_id' => $order_item->get_product_id(), - 'subtotal' => '35.00', - ), - ), 'coupon_lines' => array( array( 'code' => 'fake-coupon', From 28710a522eff0309d6b03483b4bab145a48ac1c7 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Sun, 27 Jan 2019 20:57:58 -0400 Subject: [PATCH 127/401] phpcs sniff fixes for orders.php --- tests/unit-tests/api/orders.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/unit-tests/api/orders.php b/tests/unit-tests/api/orders.php index 88b787a0b44..d34a97bb80d 100644 --- a/tests/unit-tests/api/orders.php +++ b/tests/unit-tests/api/orders.php @@ -5,6 +5,10 @@ * @package WooCommerce\Tests\API * @since 3.5.0 */ + +/** + * Class WC_Tests_API_Orders + */ class WC_Tests_API_Orders extends WC_REST_Unit_Test_Case { /** @@ -121,9 +125,11 @@ class WC_Tests_API_Orders extends WC_REST_Unit_Test_Case { public function test_get_item_refund_id() { wp_set_current_user( $this->user ); $order = WC_Helper_Order::create_order(); - $refund = wc_create_refund( array( - 'order_id' => $order->get_id(), - ) ); + $refund = wc_create_refund( + array( + 'order_id' => $order->get_id(), + ) + ); $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/orders/' . $refund->get_id() ) ); $this->assertEquals( 404, $response->get_status() ); } @@ -273,7 +279,7 @@ class WC_Tests_API_Orders extends WC_REST_Unit_Test_Case { $request->set_body_params( array( 'payment_method' => 'bacs', - 'payment_method_title' => '

    Sanitize this too

    ' + 'payment_method_title' => '

    Sanitize this too

    ', ) ); $response = $this->server->dispatch( $request ); @@ -292,7 +298,7 @@ class WC_Tests_API_Orders extends WC_REST_Unit_Test_Case { wp_set_current_user( $this->user ); $product = WC_Helper_Product::create_simple_product(); - // non-existent customer + // Non-existent customer. $request = new WP_REST_Request( 'POST', '/wc/v3/orders' ); $request->set_body_params( array( From b0aa198d4c67ef3a88a91b64ba9719bff2d68276 Mon Sep 17 00:00:00 2001 From: Job Date: Mon, 28 Jan 2019 12:24:57 +0100 Subject: [PATCH 128/401] Updated system status report Two changes: * Followed the WP core order of mentioning wordpress URL and site URL * Followed the WP core naming of these two URLs - confusingly the home URL in core is described be "Site Address ()" ![http://cld.wthms.co/xo5xtL](http://cld.wthms.co/xo5xtL+) Link to image here: http://cld.wthms.co/xo5xtL. --- .../admin/views/html-admin-page-status-report.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/includes/admin/views/html-admin-page-status-report.php b/includes/admin/views/html-admin-page-status-report.php index a1ed0b386a5..be0a9b40e29 100644 --- a/includes/admin/views/html-admin-page-status-report.php +++ b/includes/admin/views/html-admin-page-status-report.php @@ -55,15 +55,15 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'minor - : - - - - - : + : + + : + + + : From 2174937f4fb8659bea24d40c4d23d6c0c89808ab Mon Sep 17 00:00:00 2001 From: Job Date: Mon, 28 Jan 2019 12:28:48 +0100 Subject: [PATCH 129/401] Translatable --- includes/admin/views/html-admin-page-status-report.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/admin/views/html-admin-page-status-report.php b/includes/admin/views/html-admin-page-status-report.php index be0a9b40e29..da1cfcaf849 100644 --- a/includes/admin/views/html-admin-page-status-report.php +++ b/includes/admin/views/html-admin-page-status-report.php @@ -55,12 +55,12 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'minor - : + : - : + : From dda4cab70c6b6259d8cf3c34b6c4224f5e68fb4f Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 28 Jan 2019 13:13:12 +0100 Subject: [PATCH 130/401] Updated todo to use the common format. --- includes/class-woocommerce.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php index 1894173fceb..209babc249b 100644 --- a/includes/class-woocommerce.php +++ b/includes/class-woocommerce.php @@ -232,10 +232,11 @@ final class WooCommerce { * * Legacy REST requests should still run some extra code for backwards compatibility. * + * @todo: replace this function once core WP function is available: https://core.trac.wordpress.org/ticket/42061. + * * @return bool */ private static function is_rest_api_request() { - // TODO: replace this function once core WP function is available: https://core.trac.wordpress.org/ticket/42061. $request_uri = $_SERVER['REQUEST_URI']; // @codingStandardsIgnoreLine if ( empty( $request_uri ) ) { return false; From b73d5de79f09e751da64519b6e6993a607b0aa39 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 28 Jan 2019 14:58:30 +0000 Subject: [PATCH 131/401] Update dependency lint-staged to v8.1.1 --- package-lock.json | 124 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 62 insertions(+), 64 deletions(-) diff --git a/package-lock.json b/package-lock.json index 772d026f196..900128afc16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -174,6 +174,23 @@ "integrity": "sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA==", "dev": true }, + "@babel/runtime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz", + "integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.12.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", + "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==", + "dev": true + } + } + }, "@babel/template": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", @@ -3846,6 +3863,12 @@ "integrity": "sha1-9uvK5h/6GH5CCZnUDOCoAfObJjU=", "dev": true }, + "fn-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fn-name/-/fn-name-2.0.1.tgz", + "integrity": "sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc=", + "dev": true + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -5796,24 +5819,6 @@ "detect-newline": "^2.1.0" } }, - "jest-get-type": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", - "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", - "dev": true - }, - "jest-validate": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-23.6.0.tgz", - "integrity": "sha512-OFKapYxe72yz7agrDAWi8v2WL8GIfVqcbKRCLbRG9PAxtzF9b1SEDdTpytNDN12z2fJynoBwpMpvj2R39plI2A==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-get-type": "^22.1.0", - "leven": "^2.1.0", - "pretty-format": "^23.6.0" - } - }, "js-base64": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.0.tgz", @@ -6017,15 +6022,15 @@ } }, "lint-staged": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.1.0.tgz", - "integrity": "sha512-yfSkyJy7EuVsaoxtUSEhrD81spdJOe/gMTGea3XaV7HyoRhTb9Gdlp6/JppRZERvKSEYXP9bjcmq6CA5oL2lYQ==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.1.1.tgz", + "integrity": "sha512-6C9tmmCedjDYQMzHydT5mXRtmEgpGUQDoIl+Ser8cfI/n9grsRUsuG2jd1BWqGf62OV+BV+6n/Drt82uYTCgJg==", "dev": true, "requires": { "@iamstarkov/listr-update-renderer": "0.4.1", "chalk": "^2.3.1", "commander": "^2.14.1", - "cosmiconfig": "5.0.6", + "cosmiconfig": "^5.0.2", "debug": "^3.1.0", "dedent": "^0.7.0", "del": "^3.0.0", @@ -6034,7 +6039,6 @@ "g-status": "^2.0.2", "is-glob": "^4.0.0", "is-windows": "^1.0.2", - "jest-validate": "^23.5.0", "listr": "^0.14.2", "lodash": "^4.17.5", "log-symbols": "^2.2.0", @@ -6046,7 +6050,8 @@ "please-upgrade-node": "^3.0.2", "staged-git-files": "1.1.2", "string-argv": "^0.0.2", - "stringify-object": "^3.2.2" + "stringify-object": "^3.2.2", + "yup": "^0.26.10" }, "dependencies": { "arr-diff": { @@ -6090,17 +6095,6 @@ } } }, - "cosmiconfig": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.0.6.tgz", - "integrity": "sha512-6DWfizHriCrFWURP1/qyhsiFvYdlJzbCzmtFWh744+KyWsJo5+kPzUZZaMRSSItoYc0pxFX7gEO7ZC1/gN/7AQ==", - "dev": true, - "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.9.0", - "parse-json": "^4.0.0" - } - }, "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", @@ -6378,16 +6372,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } } } }, @@ -8109,24 +8093,6 @@ "number-is-nan": "^1.0.0" } }, - "pretty-format": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", - "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0", - "ansi-styles": "^3.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - } - } - }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -8145,6 +8111,12 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "property-expr": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-1.5.1.tgz", + "integrity": "sha512-CGuc0VUTGthpJXL36ydB6jnbyOf/rAHFvmVrJlH+Rg0DqqLFQGAP6hIaxD/G0OAmBJPhXDHuEJigrp0e0wFV6g==", + "dev": true + }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -10359,6 +10331,12 @@ "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", "dev": true }, + "synchronous-promise": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/synchronous-promise/-/synchronous-promise-2.0.6.tgz", + "integrity": "sha512-TyOuWLwkmtPL49LHCX1caIwHjRzcVd62+GF6h8W/jHOeZUFHpnd2XJDVuUlaTaLPH1nuu2M69mfHr5XbQJnf/g==", + "dev": true + }, "table": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/table/-/table-5.2.1.tgz", @@ -10565,6 +10543,12 @@ } } }, + "toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=", + "dev": true + }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", @@ -11214,6 +11198,20 @@ "requires": { "fd-slicer": "~1.0.1" } + }, + "yup": { + "version": "0.26.10", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.26.10.tgz", + "integrity": "sha512-keuNEbNSnsOTOuGCt3UJW69jDE3O4P+UHAakO7vSeFMnjaitcmlbij/a3oNb9g1Y1KvSKH/7O1R2PQ4m4TRylw==", + "dev": true, + "requires": { + "@babel/runtime": "7.0.0", + "fn-name": "~2.0.1", + "lodash": "^4.17.10", + "property-expr": "^1.5.0", + "synchronous-promise": "^2.0.5", + "toposort": "^2.0.2" + } } } } diff --git a/package.json b/package.json index faf59c65ad4..4df44737135 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "grunt-wp-i18n": "1.0.3", "husky": "1.3.1", "istanbul": "1.0.0-alpha.2", - "lint-staged": "8.1.0", + "lint-staged": "8.1.1", "mocha": "5.2.0", "prettier": "github:automattic/calypso-prettier#c56b4251", "stylelint": "9.10.1", From 7c0273fa122f39f6b0d343847f0185e65a8a01e6 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 28 Jan 2019 15:00:48 +0000 Subject: [PATCH 132/401] Prime caches when reading variations --- ...class-wc-product-variable-data-store-cpt.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/includes/data-stores/class-wc-product-variable-data-store-cpt.php b/includes/data-stores/class-wc-product-variable-data-store-cpt.php index d9f32199f3c..342395c43ca 100644 --- a/includes/data-stores/class-wc-product-variable-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-variable-data-store-cpt.php @@ -276,7 +276,13 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple 'regular_price' => array(), 'sale_price' => array(), ); - $variation_ids = $product->get_visible_children(); + + $variation_ids = $product->get_visible_children(); + + if ( is_callable( '_prime_post_caches' ) ) { + _prime_post_caches( $variation_ids ); + } + foreach ( $variation_ids as $variation_id ) { $variation = wc_get_product( $variation_id ); @@ -344,18 +350,19 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple } } - $prices_array['price'][ $variation_id ] = wc_format_decimal( $price, wc_get_price_decimals() ); + $prices_array['price'][ $variation_id ] = wc_format_decimal( $price, wc_get_price_decimals() ); $prices_array['regular_price'][ $variation_id ] = wc_format_decimal( $regular_price, wc_get_price_decimals() ); $prices_array['sale_price'][ $variation_id ] = wc_format_decimal( $sale_price . '.00', wc_get_price_decimals() ); + $prices_array = apply_filters( 'woocommerce_variation_prices_array', $prices_array, $variation, $for_display ); } - } + } // Add all pricing data to the transient array. - foreach( $prices_array as $key => $values ) { + foreach ( $prices_array as $key => $values ) { $transient_cached_prices_array[ $price_hash ][ $key ] = $values; } - + set_transient( $transient_name, wp_json_encode( $transient_cached_prices_array ), DAY_IN_SECONDS * 30 ); } From db2bc48849c2a74822a5837afd4ac7af977c81f5 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Mon, 28 Jan 2019 11:23:25 -0400 Subject: [PATCH 133/401] don't force shop loop display mode to products when query page >1 --- includes/class-wc-breadcrumb.php | 2 +- includes/wc-template-functions.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-breadcrumb.php b/includes/class-wc-breadcrumb.php index 142aa5a7fcd..3745c3247e6 100644 --- a/includes/class-wc-breadcrumb.php +++ b/includes/class-wc-breadcrumb.php @@ -368,7 +368,7 @@ class WC_Breadcrumb { * Add a breadcrumb for pagination. */ private function paged_trail() { - if ( get_query_var( 'paged' ) ) { + if ( get_query_var( 'paged' ) && 'subcategories' !== woocommerce_get_loop_display_mode() ) { /* translators: %d: page number */ $this->add_crumb( sprintf( __( 'Page %d', 'woocommerce' ), get_query_var( 'paged' ) ) ); } diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index e2e08da9684..d2c263983bf 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -2211,7 +2211,7 @@ if ( ! function_exists( 'woocommerce_get_loop_display_mode' ) ) { */ function woocommerce_get_loop_display_mode() { // Only return products when filtering things. - if ( 1 < wc_get_loop_prop( 'current_page' ) || wc_get_loop_prop( 'is_search' ) || wc_get_loop_prop( 'is_filtered' ) ) { + if ( wc_get_loop_prop( 'is_search' ) || wc_get_loop_prop( 'is_filtered' ) ) { return 'products'; } From 475c35ed7a8d599a67d83ac646e2e9a879409c4e Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Mon, 28 Jan 2019 11:25:45 -0400 Subject: [PATCH 134/401] phpcs sniff fixes for wc-template-functions.php --- includes/wc-template-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index d2c263983bf..d9808740ec0 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -1206,7 +1206,7 @@ if ( ! function_exists( 'woocommerce_template_loop_add_to_cart' ) ) { $args = apply_filters( 'woocommerce_loop_add_to_cart_args', wp_parse_args( $args, $defaults ), $product ); if ( isset( $args['attributes']['aria-label'] ) ) { - $args['attributes']['aria-label'] = strip_tags( $args['attributes']['aria-label'] ); + $args['attributes']['aria-label'] = wp_strip_all_tags( $args['attributes']['aria-label'] ); } wc_get_template( 'loop/add-to-cart.php', $args ); From 8e18d05634a36c499957f63fa77806df5a898dac Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Mon, 28 Jan 2019 11:30:11 -0400 Subject: [PATCH 135/401] phpcs sniff fixes for class-wc-breadcrumb.php --- includes/class-wc-breadcrumb.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-breadcrumb.php b/includes/class-wc-breadcrumb.php index 3745c3247e6..9c352c4f702 100644 --- a/includes/class-wc-breadcrumb.php +++ b/includes/class-wc-breadcrumb.php @@ -28,7 +28,7 @@ class WC_Breadcrumb { */ public function add_crumb( $name, $link = '' ) { $this->crumbs[] = array( - strip_tags( $name ), + wp_strip_all_tags( $name ), $link, ); } @@ -148,8 +148,11 @@ class WC_Breadcrumb { $this->prepend_shop_page(); $terms = wc_get_product_terms( - $post->ID, 'product_cat', apply_filters( - 'woocommerce_breadcrumb_product_terms_args', array( + $post->ID, + 'product_cat', + apply_filters( + 'woocommerce_breadcrumb_product_terms_args', + array( 'orderby' => 'parent', 'order' => 'DESC', ) From ad06f626691e589222f2190ff29622bf7687e8bb Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 28 Jan 2019 19:32:21 +0100 Subject: [PATCH 136/401] Added more types of input to styling in admin/variations. --- assets/css/admin.scss | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/assets/css/admin.scss b/assets/css/admin.scss index e72585b7aa9..86bc4e7e3c6 100644 --- a/assets/css/admin.scss +++ b/assets/css/admin.scss @@ -4797,8 +4797,20 @@ float: right; } - input[type=text], - input[type=number], + input[type="text"], + input[type="number"], + input[type="password"], + input[type="color"], + input[type="date"], + input[type="datetime"], + input[type="datetime-local"], + input[type="email"], + input[type="month"], + input[type="search"], + input[type="tel"], + input[type="time"], + input[type="url"], + input[type="week"], select, textarea { width: 100%; From a663f56306ea018e9a2570e10d545b0e05e444d3 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Mon, 28 Jan 2019 14:36:39 -0400 Subject: [PATCH 137/401] two more sniffs in wc-template-functions.php --- includes/wc-template-functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index d9808740ec0..87be414b295 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -910,7 +910,7 @@ if ( ! function_exists( 'woocommerce_content' ) ) { - ' . get_the_title() . ''; + echo '

    ' . get_the_title() . '

    '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } if ( ! function_exists( 'woocommerce_template_loop_category_title' ) ) { From 9361cc7c0c5440986025d0041e2af82bd25ef857 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 28 Jan 2019 20:25:50 +0000 Subject: [PATCH 138/401] Update dependency autoprefixer to v9.4.7 --- package-lock.json | 41 +++++++++++++++++++++++++++-------------- package.json | 2 +- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 772d026f196..0646a3fe639 100644 --- a/package-lock.json +++ b/package-lock.json @@ -606,17 +606,30 @@ "dev": true }, "autoprefixer": { - "version": "9.4.6", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.6.tgz", - "integrity": "sha512-Yp51mevbOEdxDUy5WjiKtpQaecqYq9OqZSL04rSoCiry7Tc5I9FEyo3bfxiTJc1DfHeKwSFCUYbBAiOQ2VGfiw==", + "version": "9.4.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.7.tgz", + "integrity": "sha512-qS5wW6aXHkm53Y4z73tFGsUhmZu4aMPV9iHXYlF0c/wxjknXNHuj/1cIQb+6YH692DbJGGWcckAXX+VxKvahMA==", "dev": true, "requires": { "browserslist": "^4.4.1", - "caniuse-lite": "^1.0.30000929", + "caniuse-lite": "^1.0.30000932", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", - "postcss": "^7.0.13", + "postcss": "^7.0.14", "postcss-value-parser": "^3.3.1" + }, + "dependencies": { + "postcss": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", + "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + } } }, "aws-sign2": { @@ -2109,9 +2122,9 @@ } }, "caniuse-lite": { - "version": "1.0.30000929", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000929.tgz", - "integrity": "sha512-n2w1gPQSsYyorSVYqPMqbSaz1w7o9ZC8VhOEGI9T5MfGDzp7sbopQxG6GaQmYsaq13Xfx/mkxJUWC1Dz3oZfzw==", + "version": "1.0.30000932", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000932.tgz", + "integrity": "sha512-4bghJFItvzz8m0T3lLZbacmEY9X1Z2AtIzTr7s7byqZIOumASfr4ynDx7rtm0J85nDmx8vsgR6vnaSoeU8Oh0A==", "dev": true }, "caseless": { @@ -2937,9 +2950,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.103", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.103.tgz", - "integrity": "sha512-tObPqGmY9X8MUM8i3MEimYmbnLLf05/QV5gPlkR8MQ3Uj8G8B2govE1U4cQcBYtv3ymck9Y8cIOu4waoiykMZQ==", + "version": "1.3.108", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.108.tgz", + "integrity": "sha512-/QI4hMpAh48a1Sea6PALGv+kuVne9A2EWGd8HrWHMdYhIzGtbhVVHh6heL5fAzGaDnZuPyrlWJRl8WPm4RyiQQ==", "dev": true }, "elegant-spinner": { @@ -7063,9 +7076,9 @@ } }, "node-releases": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.3.tgz", - "integrity": "sha512-6VrvH7z6jqqNFY200kdB6HdzkgM96Oaj9v3dqGfgp6mF+cHmU4wyQKZ2/WPDRVoR0Jz9KqbamaBN0ZhdUaysUQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.5.tgz", + "integrity": "sha512-6C2K0x1QlYTz9wCueMN/DVZFcBVg/qsj2k9iV5gV/+OvG4KNrl7Nu7TWbWFQ3/Z2V10qVFQWtj5Xa+VBodcI6g==", "dev": true, "requires": { "semver": "^5.3.0" diff --git a/package.json b/package.json index faf59c65ad4..2200b0aa803 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "git:update-hooks": "rm -r .git/hooks && mkdir -p .git/hooks && node ./node_modules/husky/husky.js install" }, "devDependencies": { - "autoprefixer": "9.4.6", + "autoprefixer": "9.4.7", "babel": "6.23.0", "babel-cli": "6.26.0", "babel-eslint": "10.0.1", From 5317be3206824b5880a7308bffa8883da75d818a Mon Sep 17 00:00:00 2001 From: Gerhard Date: Tue, 29 Jan 2019 12:18:05 +0200 Subject: [PATCH 139/401] Do not escape output of wc_get_rating_html. --- templates/content-widget-product.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/content-widget-product.php b/templates/content-widget-product.php index 35e12f3540c..0d77ca9d89b 100644 --- a/templates/content-widget-product.php +++ b/templates/content-widget-product.php @@ -34,7 +34,7 @@ if ( ! is_a( $product, 'WC_Product' ) ) {
    - get_average_rating() ) ); ?> + get_average_rating() ); // PHPCS:Ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> get_price_html(); ?> From 47d1a02ff6311fd94c1535fb5a84b47970be1016 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Tue, 29 Jan 2019 12:18:55 +0200 Subject: [PATCH 140/401] PHPCS Fixes. --- templates/content-widget-product.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/content-widget-product.php b/templates/content-widget-product.php index 0d77ca9d89b..fec2e193030 100644 --- a/templates/content-widget-product.php +++ b/templates/content-widget-product.php @@ -29,7 +29,7 @@ if ( ! is_a( $product, 'WC_Product' ) ) { - get_image(); ?> + get_image(); // PHPCS:Ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> get_name() ); ?> @@ -37,7 +37,7 @@ if ( ! is_a( $product, 'WC_Product' ) ) { get_average_rating() ); // PHPCS:Ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> - get_price_html(); ?> + get_price_html(); // PHPCS:Ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> From b8dc401f86e5c9497bcd708f7ef0d3e1201ff52f Mon Sep 17 00:00:00 2001 From: Gerhard Date: Tue, 29 Jan 2019 12:19:10 +0200 Subject: [PATCH 141/401] Bump template version --- templates/content-widget-product.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/content-widget-product.php b/templates/content-widget-product.php index fec2e193030..9d8a47e6348 100644 --- a/templates/content-widget-product.php +++ b/templates/content-widget-product.php @@ -11,7 +11,7 @@ * * @see https://docs.woocommerce.com/document/template-structure/ * @package WooCommerce/Templates - * @version 3.5.2 + * @version 3.5.5 */ if ( ! defined( 'ABSPATH' ) ) { From 25e562400edf1a03e96ca80b05d2f7e2dfa9c809 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Tue, 29 Jan 2019 13:05:02 +0200 Subject: [PATCH 142/401] Put reload checkout in variable before unsetting it and then calling it again after it was unset. --- includes/class-wc-ajax.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php index d0ea535f9f1..c7c6b205a05 100644 --- a/includes/class-wc-ajax.php +++ b/includes/class-wc-ajax.php @@ -335,18 +335,19 @@ class WC_AJAX { WC()->customer->save(); WC()->cart->calculate_totals(); - // Get order review fragment + // Get order review fragment. ob_start(); woocommerce_order_review(); $woocommerce_order_review = ob_get_clean(); - // Get checkout payment fragment + // Get checkout payment fragment. ob_start(); woocommerce_checkout_payment(); $woocommerce_checkout_payment = ob_get_clean(); - // Get messages if reload checkout is not true - if ( ! isset( WC()->session->reload_checkout ) ) { + // Get messages if reload checkout is not true. + $reload_checkout = isset( WC()->session->reload_checkout ) ? true : false; + if ( ! $reload_checkout ) { $messages = wc_print_notices( true ); } else { $messages = ''; @@ -358,9 +359,10 @@ class WC_AJAX { array( 'result' => empty( $messages ) ? 'success' : 'failure', 'messages' => $messages, - 'reload' => isset( WC()->session->reload_checkout ) ? 'true' : 'false', + 'reload' => $reload_checkout ? 'true' : 'false', 'fragments' => apply_filters( - 'woocommerce_update_order_review_fragments', array( + 'woocommerce_update_order_review_fragments', + array( '.woocommerce-checkout-review-order-table' => $woocommerce_order_review, '.woocommerce-checkout-payment' => $woocommerce_checkout_payment, ) From e74f0acc19f522a2d1bf5dd7ffff13b98fc524db Mon Sep 17 00:00:00 2001 From: Gerhard Date: Tue, 29 Jan 2019 13:34:04 +0200 Subject: [PATCH 143/401] Add a screen reader label to the star rating div to avoid screen readers trying to read the text of the star font. --- includes/wc-template-functions.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index e2e08da9684..5ce09d294a3 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -3292,7 +3292,8 @@ function wc_get_stock_html( $product ) { * @return string */ function wc_get_rating_html( $rating, $count = 0 ) { - $html = 0 < $rating ? '
    ' . wc_get_star_rating_html( $rating, $count ) . '
    ' : ''; + /* translators: screen reader rating value */ + $html = 0 < $rating ? '
    ' . wc_get_star_rating_html( $rating, $count ) . '
    ' : ''; return apply_filters( 'woocommerce_product_get_rating_html', $html, $rating, $count ); } From 94f64f385114f6f62003e995b396a27469256243 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Tue, 29 Jan 2019 13:36:16 +0200 Subject: [PATCH 144/401] PHPCS fixes --- includes/wc-template-functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 5ce09d294a3..0efd5b76cc1 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -910,7 +910,7 @@ if ( ! function_exists( 'woocommerce_content' ) ) { - ' . get_the_title() . ''; + echo '

    ' . esc_html( get_the_title() ) . '

    '; } } if ( ! function_exists( 'woocommerce_template_loop_category_title' ) ) { From 69baf175c1b18034db70e12f0bf8204ff41a0387 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Tue, 29 Jan 2019 13:48:47 +0200 Subject: [PATCH 145/401] After some testing using area-hidden seems to deliver the best results for not producing the hissing sound on reviews. --- includes/wc-template-functions.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 0efd5b76cc1..8df4e80f289 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -3292,8 +3292,7 @@ function wc_get_stock_html( $product ) { * @return string */ function wc_get_rating_html( $rating, $count = 0 ) { - /* translators: screen reader rating value */ - $html = 0 < $rating ? '
    ' . wc_get_star_rating_html( $rating, $count ) . '
    ' : ''; + $html = 0 < $rating ? '' : ''; return apply_filters( 'woocommerce_product_get_rating_html', $html, $rating, $count ); } From d9c971ee20d1aae92d7f7f40b30ad69961e7279b Mon Sep 17 00:00:00 2001 From: Boost Date: Tue, 29 Jan 2019 13:27:05 +0000 Subject: [PATCH 146/401] Update class-wc-structured-data.php. Line 214, changed wpautop to wp_filter_nohtml_kses --- includes/class-wc-structured-data.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-structured-data.php b/includes/class-wc-structured-data.php index ac743c68462..25af41c8983 100644 --- a/includes/class-wc-structured-data.php +++ b/includes/class-wc-structured-data.php @@ -210,7 +210,7 @@ class WC_Structured_Data { } $markup['image'] = wp_get_attachment_url( $product->get_image_id() ); - $markup['description'] = wpautop( do_shortcode( $product->get_short_description() ? $product->get_short_description() : $product->get_description() ) ); + $markup['description'] = wp_filter_nohtml_kses( do_shortcode( $product->get_short_description() ? $product->get_short_description() : $product->get_description() ) ); $markup['sku'] = $product->get_sku(); if ( '' !== $product->get_price() ) { From c45dc0968747e9102e438bfaa02190acc8ad4d1e Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Tue, 29 Jan 2019 14:24:08 -0400 Subject: [PATCH 147/401] only set shipping cost required when method is enabled --- assets/js/admin/wc-setup.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/js/admin/wc-setup.js b/assets/js/admin/wc-setup.js index 5815f5df1df..95c72dfcff1 100644 --- a/assets/js/admin/wc-setup.js +++ b/assets/js/admin/wc-setup.js @@ -95,6 +95,7 @@ jQuery( function( $ ) { description.find( '.shipping-method-description' ).addClass( 'hide' ); description.find( '.' + selectedMethod ).removeClass( 'hide' ); + var $checkbox = zone.parent().find( 'input[type="checkbox"]' ); var settings = zone.find( '.shipping-method-settings' ); settings .find( '.shipping-method-setting' ) @@ -105,7 +106,7 @@ jQuery( function( $ ) { .find( '.' + selectedMethod ) .removeClass( 'hide' ) .find( '.shipping-method-required-field' ) - .prop( 'required', true ); + .prop( 'required', $checkbox.prop( 'checked' ) ); } ).find( '.wc-wizard-shipping-method-select .method' ).change(); $( '.wc-wizard-services' ).on( 'change', '.wc-wizard-shipping-method-enable', function() { From 185041da874c76d130eca804d9815fc9aa46dcf1 Mon Sep 17 00:00:00 2001 From: Job Date: Tue, 29 Jan 2019 20:40:59 +0100 Subject: [PATCH 148/401] Removed title case As requested here by https://github.com/woocommerce/woocommerce/pull/22584#pullrequestreview-197707971 --- includes/admin/views/html-admin-page-status-report.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/admin/views/html-admin-page-status-report.php b/includes/admin/views/html-admin-page-status-report.php index da1cfcaf849..201b4e9db27 100644 --- a/includes/admin/views/html-admin-page-status-report.php +++ b/includes/admin/views/html-admin-page-status-report.php @@ -55,12 +55,12 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, 'minor - : + : - : + : From 7fa1bd78c9d85f97d59c4ea229926d7dcd977d75 Mon Sep 17 00:00:00 2001 From: Toby Robles <10byrobles@gmail.com> Date: Tue, 29 Jan 2019 20:01:04 -0500 Subject: [PATCH 149/401] Updating Peruvian currency Since 2015, the government ordered that the "Nuevo Sol (S /.)" be renamed simply "Sol (S/)", also suppressing the use of the point (S/.) in the monetary sign (S/) --- includes/wc-core-functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/wc-core-functions.php b/includes/wc-core-functions.php index b45a15fa6dc..a607e68115b 100644 --- a/includes/wc-core-functions.php +++ b/includes/wc-core-functions.php @@ -410,7 +410,7 @@ function get_woocommerce_currencies() { 'NZD' => __( 'New Zealand dollar', 'woocommerce' ), 'OMR' => __( 'Omani rial', 'woocommerce' ), 'PAB' => __( 'Panamanian balboa', 'woocommerce' ), - 'PEN' => __( 'Peruvian nuevo sol', 'woocommerce' ), + 'PEN' => __( 'Sol', 'woocommerce' ), 'PGK' => __( 'Papua New Guinean kina', 'woocommerce' ), 'PHP' => __( 'Philippine peso', 'woocommerce' ), 'PKR' => __( 'Pakistani rupee', 'woocommerce' ), @@ -596,7 +596,7 @@ function get_woocommerce_currency_symbol( $currency = '' ) { 'NZD' => '$', 'OMR' => 'ر.ع.', 'PAB' => 'B/.', - 'PEN' => 'S/.', + 'PEN' => 'S/', 'PGK' => 'K', 'PHP' => '₱', 'PKR' => '₨', From efd72f32223ae688f3e71d8d91a5702fd7e742b7 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Wed, 30 Jan 2019 10:12:08 +0200 Subject: [PATCH 150/401] Update Gruntfile to make use of node-sass when calling grunt-sass. Introduce new minimum node and npm versions Make es6 default language for eslintrc Fix a couple of es6 linting errors due to too long lines. --- .eslintrc | 6 +++++- Gruntfile.js | 17 +++++++++++++---- package.json | 5 +++-- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.eslintrc b/.eslintrc index fa832ed3d28..8f9f0f536ec 100644 --- a/.eslintrc +++ b/.eslintrc @@ -7,12 +7,16 @@ "globals": { "wp": true, "wpApiSettings": true, - "wcSettings": true + "wcSettings": true, + "es6": true }, "rules": { "camelcase": 0, "indent": 0, "max-len": [ 2, { "code": 140 } ], "no-console": 1 + }, + "parserOptions": { + "ecmaVersion": 6 } } diff --git a/Gruntfile.js b/Gruntfile.js index df897dae4d8..f8aaedb136e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,6 +1,7 @@ /* jshint node:true */ module.exports = function( grunt ) { 'use strict'; + const sass = require( 'node-sass' ); grunt.initConfig({ @@ -75,9 +76,13 @@ module.exports = function( grunt ) { '<%= dirs.js %>/jquery-flot/jquery.flot.time.min.js': ['<%= dirs.js %>/jquery-flot/jquery.flot.time.js'], '<%= dirs.js %>/jquery-payment/jquery.payment.min.js': ['<%= dirs.js %>/jquery-payment/jquery.payment.js'], '<%= dirs.js %>/jquery-qrcode/jquery.qrcode.min.js': ['<%= dirs.js %>/jquery-qrcode/jquery.qrcode.js'], - '<%= dirs.js %>/jquery-serializejson/jquery.serializejson.min.js': ['<%= dirs.js %>/jquery-serializejson/jquery.serializejson.js'], + '<%= dirs.js %>/jquery-serializejson/jquery.serializejson.min.js': [ + '<%= dirs.js %>/jquery-serializejson/jquery.serializejson.js' + ], '<%= dirs.js %>/jquery-tiptip/jquery.tipTip.min.js': ['<%= dirs.js %>/jquery-tiptip/jquery.tipTip.js'], - '<%= dirs.js %>/jquery-ui-touch-punch/jquery-ui-touch-punch.min.js': ['<%= dirs.js %>/jquery-ui-touch-punch/jquery-ui-touch-punch.js'], + '<%= dirs.js %>/jquery-ui-touch-punch/jquery-ui-touch-punch.min.js': [ + '<%= dirs.js %>/jquery-ui-touch-punch/jquery-ui-touch-punch.js' + ], '<%= dirs.js %>/prettyPhoto/jquery.prettyPhoto.init.min.js': ['<%= dirs.js %>/prettyPhoto/jquery.prettyPhoto.init.js'], '<%= dirs.js %>/prettyPhoto/jquery.prettyPhoto.min.js': ['<%= dirs.js %>/prettyPhoto/jquery.prettyPhoto.js'], '<%= dirs.js %>/flexslider/jquery.flexslider.min.js': ['<%= dirs.js %>/flexslider/jquery.flexslider.js'], @@ -112,6 +117,7 @@ module.exports = function( grunt ) { sass: { compile: { options: { + implementation: sass, sourceMap: 'none' }, files: [{ @@ -260,7 +266,9 @@ module.exports = function( grunt ) { contributors: { command: [ 'echo "Generating contributor list since <%= fromDate %>"', - './node_modules/.bin/githubcontrib --owner woocommerce --repo woocommerce --fromDate <%= fromDate %> --authToken <%= authToken %> --cols 6 --sortBy contributions --format md --sortOrder desc --showlogin true > contributors.md' + './node_modules/.bin/githubcontrib --owner woocommerce --repo woocommerce --fromDate <%= fromDate %>' + + ' --authToken <%= authToken %> --cols 6 --sortBy contributions --format md --sortOrder desc' + + ' --showlogin true > contributors.md' ].join( '&&' ) } }, @@ -277,7 +285,8 @@ module.exports = function( grunt ) { { config: 'authToken', type: 'input', - message: '(optional) Provide a personal access token. This will allow 5000 requests per hour rather than 60 - use if nothing is generated.' + message: '(optional) Provide a personal access token.' + + ' This will allow 5000 requests per hour rather than 60 - use if nothing is generated.' } ] } diff --git a/package.json b/package.json index faf59c65ad4..15c84dd630b 100644 --- a/package.json +++ b/package.json @@ -54,14 +54,15 @@ "istanbul": "1.0.0-alpha.2", "lint-staged": "8.1.0", "mocha": "5.2.0", + "node-sass": "4.11.0", "prettier": "github:automattic/calypso-prettier#c56b4251", "stylelint": "9.10.1", "stylelint-config-wordpress": "13.1.0", "wc-e2e-page-objects": "0.10.0" }, "engines": { - "node": ">=8.9.3", - "npm": ">=5.5.1" + "node": ">=10.15.0", + "npm": ">=6.4.1" }, "dependencies": { "github-contributors-list": "https://github.com/woocommerce/github-contributors-list/tarball/master" From 429f515704e091dd6664f4a4bf0daed3aac7c984 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Wed, 30 Jan 2019 11:19:34 +0200 Subject: [PATCH 151/401] Introduce woocommerce_admin_report_customers_user_query_args and woocommerce_admin_report_customer_list_user_query_args filters to altering the queries running when retrieving users for reports. --- .../admin/reports/class-wc-report-customer-list.php | 11 +++++++---- includes/admin/reports/class-wc-report-customers.php | 9 ++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/includes/admin/reports/class-wc-report-customer-list.php b/includes/admin/reports/class-wc-report-customer-list.php index 2e70569ceda..a3bb9d7fa99 100644 --- a/includes/admin/reports/class-wc-report-customer-list.php +++ b/includes/admin/reports/class-wc-report-customer-list.php @@ -278,10 +278,13 @@ class WC_Report_Customer_List extends WP_List_Table { ); $query = new WP_User_Query( - array( - 'exclude' => array_merge( $admin_users->get_results(), $manager_users->get_results() ), - 'number' => $per_page, - 'offset' => ( $current_page - 1 ) * $per_page, + apply_filters( + 'woocommerce_admin_report_customer_list_user_query_args', + array( + 'exclude' => array_merge( $admin_users->get_results(), $manager_users->get_results() ), + 'number' => $per_page, + 'offset' => ( $current_page - 1 ) * $per_page, + ) ) ); diff --git a/includes/admin/reports/class-wc-report-customers.php b/includes/admin/reports/class-wc-report-customers.php index 503f470992f..511a734890e 100644 --- a/includes/admin/reports/class-wc-report-customers.php +++ b/includes/admin/reports/class-wc-report-customers.php @@ -203,9 +203,12 @@ class WC_Report_Customers extends WC_Admin_Report { ); $users_query = new WP_User_Query( - array( - 'fields' => array( 'user_registered' ), - 'exclude' => array_merge( $admin_users->get_results(), $manager_users->get_results() ), + apply_filters( + 'woocommerce_admin_report_customers_user_query_args', + array( + 'fields' => array( 'user_registered' ), + 'exclude' => array_merge( $admin_users->get_results(), $manager_users->get_results() ), + ) ) ); From a2e4ca70b65941eef3933750b756c44b5bbd9235 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Wed, 30 Jan 2019 11:33:51 +0200 Subject: [PATCH 152/401] PHPCS fixes --- .../reports/class-wc-report-customer-list.php | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/includes/admin/reports/class-wc-report-customer-list.php b/includes/admin/reports/class-wc-report-customer-list.php index a3bb9d7fa99..36f47c9b24d 100644 --- a/includes/admin/reports/class-wc-report-customer-list.php +++ b/includes/admin/reports/class-wc-report-customer-list.php @@ -1,7 +1,12 @@ '; - if ( ! empty( $_GET['link_orders'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'link_orders' ) ) { + if ( ! empty( $_GET['link_orders'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'link_orders' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput $linked = wc_update_new_customer_past_orders( absint( $_GET['link_orders'] ) ); - - echo '

    ' . sprintf( _n( '%s previous order linked', '%s previous orders linked', $linked, 'woocommerce' ), $linked ) . '

    '; + /* translators: single or plural number of orders */ + echo '

    ' . sprintf( esc_html( _n( '%s previous order linked', '%s previous orders linked', $linked, 'woocommerce' ), $linked ) ) . '

    '; } - if ( ! empty( $_GET['refresh'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'refresh' ) ) { + if ( ! empty( $_GET['refresh'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'refresh' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput $user_id = absint( $_GET['refresh'] ); $user = get_user_by( 'id', $user_id ); delete_user_meta( $user_id, '_money_spent' ); delete_user_meta( $user_id, '_order_count' ); - - echo '

    ' . sprintf( __( 'Refreshed stats for %s', 'woocommerce' ), $user->display_name ) . '

    '; + /* translators: User display name */ + echo '

    ' . sprintf( esc_html__( 'Refreshed stats for %s', 'woocommerce' ), esc_html( $user->display_name ) ) . '

    '; } echo '
    '; @@ -75,8 +78,8 @@ class WC_Report_Customer_List extends WP_List_Table { /** * Get column value. * - * @param WP_User $user - * @param string $column_name + * @param WP_User $user WP User object. + * @param string $column_name Column name. * @return string */ public function column_default( $user, $column_name ) { @@ -224,14 +227,13 @@ class WC_Report_Customer_List extends WP_List_Table { /** * Order users by name. * - * @param WP_User_Query $query - * + * @param WP_User_Query $query Query that gets passed through. * @return WP_User_Query */ public function order_by_last_name( $query ) { global $wpdb; - $s = ! empty( $_REQUEST['s'] ) ? stripslashes( $_REQUEST['s'] ) : ''; + $s = ! empty( $_REQUEST['s'] ) ? wp_unslash( $_REQUEST['s'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $query->query_from .= " LEFT JOIN {$wpdb->usermeta} as meta2 ON ({$wpdb->users}.ID = meta2.user_id) "; $query->query_where .= " AND meta2.meta_key = 'last_name' "; From 59d4a81f1a7daa6beffcc6f4ed7e34c17135c691 Mon Sep 17 00:00:00 2001 From: Toby Robles <10byrobles@gmail.com> Date: Wed, 30 Jan 2019 10:43:52 -0500 Subject: [PATCH 153/401] Peruvian currency updated --- tests/unit-tests/util/class-wc-tests-core-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit-tests/util/class-wc-tests-core-functions.php b/tests/unit-tests/util/class-wc-tests-core-functions.php index 68354a799c8..ed60277656e 100644 --- a/tests/unit-tests/util/class-wc-tests-core-functions.php +++ b/tests/unit-tests/util/class-wc-tests-core-functions.php @@ -141,7 +141,7 @@ class WC_Tests_Core_Functions extends WC_Unit_Test_Case { 'NZD' => 'New Zealand dollar', 'OMR' => 'Omani rial', 'PAB' => 'Panamanian balboa', - 'PEN' => 'Peruvian nuevo sol', + 'PEN' => 'Sol', 'PGK' => 'Papua New Guinean kina', 'PHP' => 'Philippine peso', 'PKR' => 'Pakistani rupee', From 1ba0194685db964eb4367d06981380210d9ad687 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 29 Jan 2019 11:42:51 +0000 Subject: [PATCH 154/401] Remove external lookup of ips on localhost --- includes/class-wc-geolocation.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/includes/class-wc-geolocation.php b/includes/class-wc-geolocation.php index 93fccadf788..eb23aeb7af0 100644 --- a/includes/class-wc-geolocation.php +++ b/includes/class-wc-geolocation.php @@ -148,7 +148,7 @@ class WC_Geolocation { /** * Get user IP Address using an external service. - * This is used mainly as a fallback for users on localhost where + * This can be used as a fallback for users on localhost where * get_ip_address() will be a local IP and non-geolocatable. * * @return string @@ -216,11 +216,6 @@ class WC_Geolocation { } else { $country_code = ''; } - - if ( ! $country_code && $fallback ) { - // May be a local environment - find external IP. - return self::geolocate_ip( self::get_external_ip_address(), false, $api_fallback ); - } } } From 59e0867afec7315fbd9d3a0c79075072639b9b8e Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 29 Jan 2019 16:04:00 +0000 Subject: [PATCH 155/401] Add missing post_password prop --- includes/abstracts/abstract-wc-product.php | 22 +++++++++++++++++++ .../class-wc-product-data-store-cpt.php | 3 +++ 2 files changed, 25 insertions(+) diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index d4441652c8e..40675df17da 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -88,6 +88,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'attributes' => array(), 'default_attributes' => array(), 'menu_order' => 0, + 'post_password' => '', 'virtual' => false, 'downloadable' => false, 'category_ids' => array(), @@ -549,6 +550,17 @@ class WC_Product extends WC_Abstract_Legacy_Product { return $this->get_prop( 'menu_order', $context ); } + /** + * Get post password. + * + * @since 3.6.0 + * @param string $context What the value is for. Valid values are view and edit. + * @return int + */ + public function get_post_password( $context = 'view' ) { + return $this->get_prop( 'post_password', $context ); + } + /** * Get category ids. * @@ -1124,6 +1136,16 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->set_prop( 'menu_order', intval( $menu_order ) ); } + /** + * Set post password. + * + * @since 3.6.0 + * @param int $post_password Post password. + */ + public function set_post_password( $post_password ) { + $this->set_prop( 'post_password', $post_password ); + } + /** * Set the product categories. * diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index c3b0b6c6b59..f97979c3346 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -111,6 +111,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da 'comment_status' => $product->get_reviews_allowed() ? 'open' : 'closed', 'ping_status' => 'closed', 'menu_order' => $product->get_menu_order(), + 'post_password' => $product->get_post_password( 'edit' ), 'post_date' => gmdate( 'Y-m-d H:i:s', $product->get_date_created( 'edit' )->getOffsetTimestamp() ), 'post_date_gmt' => gmdate( 'Y-m-d H:i:s', $product->get_date_created( 'edit' )->getTimestamp() ), 'post_name' => $product->get_slug( 'edit' ), @@ -163,6 +164,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da 'short_description' => $post_object->post_excerpt, 'parent_id' => $post_object->post_parent, 'menu_order' => $post_object->menu_order, + 'post_password' => $post_object->post_password, 'reviews_allowed' => 'open' === $post_object->comment_status, ) ); @@ -194,6 +196,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da 'comment_status' => $product->get_reviews_allowed( 'edit' ) ? 'open' : 'closed', 'post_status' => $product->get_status( 'edit' ) ? $product->get_status( 'edit' ) : 'publish', 'menu_order' => $product->get_menu_order( 'edit' ), + 'post_password' => $product->get_post_password( 'edit' ), 'post_name' => $product->get_slug( 'edit' ), 'post_type' => 'product', ); From bd076d42da29a269a5e7a09a04561c679b56946b Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 29 Jan 2019 16:04:27 +0000 Subject: [PATCH 156/401] Refactor wc_get_product_class to avoid $post object --- includes/wc-template-functions.php | 83 +++++++++--------------------- 1 file changed, 24 insertions(+), 59 deletions(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index e2e08da9684..9943451c613 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -557,72 +557,42 @@ function wc_get_product_class( $class = '', $product_id = null ) { if ( is_a( $product_id, 'WC_Product' ) ) { $product = $product_id; $product_id = $product_id->get_id(); - $post = get_post( $product_id ); } else { - $post = get_post( $product_id ); $product = wc_get_product( $post->ID ); } - $classes = array(); - - if ( $class ) { - if ( ! is_array( $class ) ) { - $class = preg_split( '#\s+#', $class ); - } - $classes = array_map( 'esc_attr', $class ); + if ( ! is_array( $class ) ) { + $classes = preg_split( '#\s+#', $class ); } else { - // Ensure that we always coerce class to being an array. - $class = array(); + $classes = $class; } - if ( ! $post || ! $product ) { - return $classes; + if ( ! $product ) { + return array_map( 'esc_attr', $classes ); } - $classes[] = 'post-' . $post->ID; - if ( ! is_admin() ) { - $classes[] = $post->post_type; - } - $classes[] = 'type-' . $post->post_type; - $classes[] = 'status-' . $post->post_status; + $classes = array_merge( + $classes, + array( + 'product', + 'type-product', + 'hentry', + 'post-' . $product->get_id(), + 'status-' . $product->get_status(), + ), + wc_get_product_taxonomy_class( $product->get_category_ids(), 'product_cat' ), + wc_get_product_taxonomy_class( $product->get_tag_ids(), 'product_tag' ) + ); - // Post format. - if ( post_type_supports( $post->post_type, 'post-formats' ) ) { - $post_format = get_post_format( $post->ID ); - - if ( $post_format && ! is_wp_error( $post_format ) ) { - $classes[] = 'format-' . sanitize_html_class( $post_format ); - } else { - $classes[] = 'format-standard'; - } - } - - // Post requires password. - $post_password_required = post_password_required( $post->ID ); - if ( $post_password_required ) { - $classes[] = 'post-password-required'; - } elseif ( ! empty( $post->post_password ) ) { - $classes[] = 'post-password-protected'; - } - - // Post thumbnails. - if ( current_theme_supports( 'post-thumbnails' ) && $product->get_image_id() && ! is_attachment( $post ) && ! $post_password_required ) { + if ( $product->get_image_id() ) { $classes[] = 'has-post-thumbnail'; } - // Sticky for Sticky Posts. - if ( is_sticky( $post->ID ) ) { - if ( is_home() && ! is_paged() ) { - $classes[] = 'sticky'; - } elseif ( is_admin() ) { - $classes[] = 'status-sticky'; - } + if ( $product->get_post_password() ) { + $classes[] = post_password_required( $product->get_id() ) ? 'post-password-required' : 'post-password-protected'; } - // Hentry for hAtom compliance. - $classes[] = 'hentry'; - - // Include attributes and any extra taxonomy. + // Include attributes and any extra taxonomies. if ( apply_filters( 'woocommerce_get_product_class_include_taxonomies', false ) ) { $taxonomies = get_taxonomies( array( 'public' => true ) ); foreach ( (array) $taxonomies as $taxonomy ) { @@ -631,13 +601,8 @@ function wc_get_product_class( $class = '', $product_id = null ) { } } } - // Categories. - $classes = array_merge( $classes, wc_get_product_taxonomy_class( $product->get_category_ids(), 'product_cat' ) ); - // Tags. - $classes = array_merge( $classes, wc_get_product_taxonomy_class( $product->get_tag_ids(), 'product_tag' ) ); - - return array_filter( array_unique( apply_filters( 'post_class', $classes, $class, $post->ID ) ) ); + return array_filter( array_map( 'esc_attr', array_unique( apply_filters( 'post_class', $classes, $class, $product->get_id() ) ) ) ); } /** @@ -910,7 +875,7 @@ if ( ! function_exists( 'woocommerce_content' ) ) { - ' . get_the_title() . ''; + echo '

    ' . get_the_title() . '

    '; // phpcs:ignore } } if ( ! function_exists( 'woocommerce_template_loop_category_title' ) ) { From c0b9491b1ee2703374e103254835700d47edb360 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 29 Jan 2019 16:06:03 +0000 Subject: [PATCH 157/401] Pass product objects to wc_get_product_class to avoid reloading the product object --- templates/content-product.php | 4 ++-- templates/content-single-product.php | 6 ++++-- templates/single-product/add-to-cart/grouped.php | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/templates/content-product.php b/templates/content-product.php index 36ffa701aa0..f69bc53b95e 100644 --- a/templates/content-product.php +++ b/templates/content-product.php @@ -12,7 +12,7 @@ * * @see https://docs.woocommerce.com/document/template-structure/ * @package WooCommerce/Templates - * @version 3.4.0 + * @version 3.6.0 */ defined( 'ABSPATH' ) || exit; @@ -24,7 +24,7 @@ if ( empty( $product ) || ! $product->is_visible() ) { return; } ?> -
  • > +
  • > -
    > +
    > $post = $post_object; // WPCS: override ok. setup_postdata( $post ); - echo ''; + echo ''; // Output columns for each product. foreach ( $grouped_product_columns as $column_id ) { From 0dd7cd2b8b2dbb992f7f349ce99f3cd4fa06547e Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 29 Jan 2019 16:31:20 +0000 Subject: [PATCH 158/401] Improved post_class generation to avoid loading more product objects --- includes/wc-template-functions.php | 63 +++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 9943451c613..b8abadd2084 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -494,10 +494,8 @@ function wc_product_post_class( $classes, $class = '', $post_id = 0 ) { if ( $product->get_type() ) { $classes[] = 'product-type-' . $product->get_type(); } - if ( $product->is_type( 'variable' ) ) { - if ( ! $product->get_default_attributes() ) { - $classes[] = 'has-default-attributes'; - } + if ( $product->is_type( 'variable' ) && $product->get_default_attributes() ) { + $classes[] = 'has-default-attributes'; } } @@ -546,7 +544,11 @@ function wc_get_product_taxonomy_class( $term_ids, $taxonomy ) { /** * Retrieves the classes for the post div as an array. * - * This method is clone from WordPress's get_post_class(), allowing removing taxonomies. + * This method was modified from WordPress's get_post_class() to allow the removal of taxonomies + * (for performance reasons). + * + * Previously wc_product_post_class was hooked into post_class. That still happens, but this function + * negates the need for it and thus unhooks it when running the post_class hook. @since 3.6.0 * * @since 3.4.0 * @param string|array $class One or more classes to add to the class list. @@ -576,9 +578,10 @@ function wc_get_product_class( $class = '', $product_id = null ) { array( 'product', 'type-product', - 'hentry', 'post-' . $product->get_id(), 'status-' . $product->get_status(), + wc_get_loop_class(), + $product->get_stock_status(), ), wc_get_product_taxonomy_class( $product->get_category_ids(), 'product_cat' ), wc_get_product_taxonomy_class( $product->get_tag_ids(), 'product_tag' ) @@ -587,12 +590,41 @@ function wc_get_product_class( $class = '', $product_id = null ) { if ( $product->get_image_id() ) { $classes[] = 'has-post-thumbnail'; } - if ( $product->get_post_password() ) { $classes[] = post_password_required( $product->get_id() ) ? 'post-password-required' : 'post-password-protected'; } + if ( $product->is_on_sale() ) { + $classes[] = 'sale'; + } + if ( $product->is_featured() ) { + $classes[] = 'featured'; + } + if ( $product->is_downloadable() ) { + $classes[] = 'downloadable'; + } + if ( $product->is_virtual() ) { + $classes[] = 'virtual'; + } + if ( $product->is_sold_individually() ) { + $classes[] = 'sold-individually'; + } + if ( $product->is_taxable() ) { + $classes[] = 'taxable'; + } + if ( $product->is_shipping_taxable() ) { + $classes[] = 'shipping-taxable'; + } + if ( $product->is_purchasable() ) { + $classes[] = 'purchasable'; + } + if ( $product->get_type() ) { + $classes[] = 'product-type-' . $product->get_type(); + } + if ( $product->is_type( 'variable' ) && $product->get_default_attributes() ) { + $classes[] = 'has-default-attributes'; + } - // Include attributes and any extra taxonomies. + // Include attributes and any extra taxonomies only if enabled via the hook - this is a performance issue. if ( apply_filters( 'woocommerce_get_product_class_include_taxonomies', false ) ) { $taxonomies = get_taxonomies( array( 'public' => true ) ); foreach ( (array) $taxonomies as $taxonomy ) { @@ -602,7 +634,20 @@ function wc_get_product_class( $class = '', $product_id = null ) { } } - return array_filter( array_map( 'esc_attr', array_unique( apply_filters( 'post_class', $classes, $class, $product->get_id() ) ) ) ); + // If using `wc_get_product_class` instead of `get_post_class`, we don't need to hook `wc_product_post_class` function. + $filtered = has_filter( 'post_class', 'wc_product_post_class' ); + + if ( $filtered ) { + remove_filter( 'post_class', 'wc_product_post_class', 20, 3 ); + } + + $classes = apply_filters( 'post_class', $classes, $class, $product->get_id() ); + + if ( $filtered ) { + add_filter( 'post_class', 'wc_product_post_class', 20, 3 ); + } + + return array_filter( array_map( 'esc_attr', array_unique( $classes ) ) ); } /** From 4eb0080605347255669a3ab87a67b0beb06dfc7b Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 29 Jan 2019 17:06:37 +0000 Subject: [PATCH 159/401] apply_filters_deprecated to avoid conflict with wp profile --- includes/class-wc-deprecated-filter-hooks.php | 17 ++--------------- includes/class-wc-frontend-scripts.php | 2 ++ 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/includes/class-wc-deprecated-filter-hooks.php b/includes/class-wc-deprecated-filter-hooks.php index 0c49887dfbe..ddfcb75215f 100644 --- a/includes/class-wc-deprecated-filter-hooks.php +++ b/includes/class-wc-deprecated-filter-hooks.php @@ -56,21 +56,8 @@ class WC_Deprecated_Filter_Hooks extends WC_Deprecated_Hooks { 'default_checkout_billing_postcode' => 'default_checkout_postcode', 'woocommerce_system_status_environment_rows' => 'woocommerce_debug_posting', 'woocommerce_credit_card_type_labels' => 'wocommerce_credit_card_type_labels', - 'woocommerce_get_script_data' => array( - 'woocommerce_params', - 'wc_geolocation_params', - 'wc_single_product_params', - 'wc_checkout_params', - 'wc_address_i18n_params', - 'wc_cart_params', - 'wc_cart_fragments_params', - 'wc_add_to_cart_params', - 'wc_add_to_cart_variation_params', - 'wc_country_select_params', - 'wc_password_strength_meter_params', - ), - 'woocommerce_settings_tabs_advanced' => 'woocommerce_settings_tabs_api', - 'woocommerce_settings_advanced' => 'woocommerce_settings_api', + 'woocommerce_settings_tabs_advanced' => 'woocommerce_settings_tabs_api', + 'woocommerce_settings_advanced' => 'woocommerce_settings_api', ); /** diff --git a/includes/class-wc-frontend-scripts.php b/includes/class-wc-frontend-scripts.php index 6e3c5a60404..a227709eb97 100644 --- a/includes/class-wc-frontend-scripts.php +++ b/includes/class-wc-frontend-scripts.php @@ -591,6 +591,8 @@ class WC_Frontend_Scripts { $params = false; } + $params = apply_filters_deprecated( $handle . '_params', array( $params ), '3.0.0', 'woocommerce_get_script_data' ); + return apply_filters( 'woocommerce_get_script_data', $params, $handle ); } From 35819ccb6e3fb5798fc1b26f6935b16342b2e40d Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 10:53:48 +0000 Subject: [PATCH 160/401] Small refactor for clarity --- includes/wc-core-functions.php | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/includes/wc-core-functions.php b/includes/wc-core-functions.php index b45a15fa6dc..60c3488c1cc 100644 --- a/includes/wc-core-functions.php +++ b/includes/wc-core-functions.php @@ -159,19 +159,28 @@ function wc_update_order( $args ) { function wc_get_template_part( $slug, $name = '' ) { $template = ''; - // Look in yourtheme/slug-name.php and yourtheme/woocommerce/slug-name.php. - if ( $name && ! WC_TEMPLATE_DEBUG_MODE ) { - $template = locate_template( array( "{$slug}-{$name}.php", WC()->template_path() . "{$slug}-{$name}.php" ) ); + if ( $name ) { + $template = WC_TEMPLATE_DEBUG_MODE ? '' : locate_template( + array( + "{$slug}-{$name}.php", + WC()->template_path() . "{$slug}-{$name}.php", + ) + ); + + if ( ! $template ) { + $fallback = WC()->plugin_path() . "/templates/{$slug}-{$name}.php"; + $template = file_exists( $fallback ) ? $fallback : ''; + } } - // Get default slug-name.php. - if ( ! $template && $name && file_exists( WC()->plugin_path() . "/templates/{$slug}-{$name}.php" ) ) { - $template = WC()->plugin_path() . "/templates/{$slug}-{$name}.php"; - } - - // If template file doesn't exist, look in yourtheme/slug.php and yourtheme/woocommerce/slug.php. - if ( ! $template && ! WC_TEMPLATE_DEBUG_MODE ) { - $template = locate_template( array( "{$slug}.php", WC()->template_path() . "{$slug}.php" ) ); + if ( ! $template ) { + // If template file doesn't exist, look in yourtheme/slug.php and yourtheme/woocommerce/slug.php. + $template = WC_TEMPLATE_DEBUG_MODE ? '' : locate_template( + array( + "{$slug}.php", + WC()->template_path() . "{$slug}.php", + ) + ); } // Allow 3rd party plugins to filter template file from their plugin. From 6fa7995f3a9658225f47c346da2b2d4f9f814b79 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 10:54:02 +0000 Subject: [PATCH 161/401] Only check file exists if filtered --- includes/wc-core-functions.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/includes/wc-core-functions.php b/includes/wc-core-functions.php index 60c3488c1cc..234811c019e 100644 --- a/includes/wc-core-functions.php +++ b/includes/wc-core-functions.php @@ -203,12 +203,15 @@ function wc_get_template( $template_name, $args = array(), $template_path = '', $located = wc_locate_template( $template_name, $template_path, $default_path ); // Allow 3rd party plugin filter template file from their plugin. - $located = apply_filters( 'wc_get_template', $located, $template_name, $args, $template_path, $default_path ); + $filter_located = apply_filters( 'wc_get_template', $located, $template_name, $args, $template_path, $default_path ); - if ( ! file_exists( $located ) ) { - /* translators: %s template */ - wc_doing_it_wrong( __FUNCTION__, sprintf( __( '%s does not exist.', 'woocommerce' ), '' . $located . '' ), '2.1' ); - return; + if ( $filter_located !== $located ) { + if ( ! file_exists( $filter_located ) ) { + /* translators: %s template */ + wc_doing_it_wrong( __FUNCTION__, sprintf( __( '%s does not exist.', 'woocommerce' ), '' . $located . '' ), '2.1' ); + return; + } + $located = $filter_located; } $action_args = array( From d167cb0a9593b8038f510435aa1bd706b7d762cd Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 12:55:59 +0000 Subject: [PATCH 162/401] Add caching for template names to avoid multiple lookups --- includes/wc-core-functions.php | 71 +++++++++++++++++------------- includes/wc-template-functions.php | 2 + 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/includes/wc-core-functions.php b/includes/wc-core-functions.php index 234811c019e..3460d67dd63 100644 --- a/includes/wc-core-functions.php +++ b/includes/wc-core-functions.php @@ -157,30 +157,35 @@ function wc_update_order( $args ) { * @param string $name Template name (default: ''). */ function wc_get_template_part( $slug, $name = '' ) { - $template = ''; - - if ( $name ) { - $template = WC_TEMPLATE_DEBUG_MODE ? '' : locate_template( - array( - "{$slug}-{$name}.php", - WC()->template_path() . "{$slug}-{$name}.php", - ) - ); - - if ( ! $template ) { - $fallback = WC()->plugin_path() . "/templates/{$slug}-{$name}.php"; - $template = file_exists( $fallback ) ? $fallback : ''; - } - } + $cache_key = sanitize_key( implode( '-', array( 'template-part', $slug, $name ) ) ); + $template = (string) wp_cache_get( $cache_key, 'woocommerce' ); if ( ! $template ) { - // If template file doesn't exist, look in yourtheme/slug.php and yourtheme/woocommerce/slug.php. - $template = WC_TEMPLATE_DEBUG_MODE ? '' : locate_template( - array( - "{$slug}.php", - WC()->template_path() . "{$slug}.php", - ) - ); + if ( $name ) { + $template = WC_TEMPLATE_DEBUG_MODE ? '' : locate_template( + array( + "{$slug}-{$name}.php", + WC()->template_path() . "{$slug}-{$name}.php", + ) + ); + + if ( ! $template ) { + $fallback = WC()->plugin_path() . "/templates/{$slug}-{$name}.php"; + $template = file_exists( $fallback ) ? $fallback : ''; + } + } + + if ( ! $template ) { + // If template file doesn't exist, look in yourtheme/slug.php and yourtheme/woocommerce/slug.php. + $template = WC_TEMPLATE_DEBUG_MODE ? '' : locate_template( + array( + "{$slug}.php", + WC()->template_path() . "{$slug}.php", + ) + ); + } + + wp_cache_set( $cache_key, $template, 'woocommerce' ); } // Allow 3rd party plugins to filter template file from their plugin. @@ -200,24 +205,30 @@ function wc_get_template_part( $slug, $name = '' ) { * @param string $default_path Default path. (default: ''). */ function wc_get_template( $template_name, $args = array(), $template_path = '', $default_path = '' ) { - $located = wc_locate_template( $template_name, $template_path, $default_path ); + $cache_key = sanitize_key( implode( '-', array( 'template', $template_name, $template_path, $default_path ) ) ); + $template = (string) wp_cache_get( $cache_key, 'woocommerce' ); + + if ( ! $template ) { + $template = wc_locate_template( $template_name, $template_path, $default_path ); + wp_cache_set( $cache_key, $template, 'woocommerce' ); + } // Allow 3rd party plugin filter template file from their plugin. - $filter_located = apply_filters( 'wc_get_template', $located, $template_name, $args, $template_path, $default_path ); + $filter_template = apply_filters( 'wc_get_template', $template, $template_name, $args, $template_path, $default_path ); - if ( $filter_located !== $located ) { - if ( ! file_exists( $filter_located ) ) { + if ( $filter_template !== $template ) { + if ( ! file_exists( $filter_template ) ) { /* translators: %s template */ - wc_doing_it_wrong( __FUNCTION__, sprintf( __( '%s does not exist.', 'woocommerce' ), '' . $located . '' ), '2.1' ); + wc_doing_it_wrong( __FUNCTION__, sprintf( __( '%s does not exist.', 'woocommerce' ), '' . $template . '' ), '2.1' ); return; } - $located = $filter_located; + $template = $filter_template; } $action_args = array( 'template_name' => $template_name, 'template_path' => $template_path, - 'located' => $located, + 'located' => $template, 'args' => $args, ); @@ -227,7 +238,7 @@ function wc_get_template( $template_name, $args = array(), $template_path = '', do_action( 'woocommerce_before_template_part', $action_args['template_name'], $action_args['template_path'], $action_args['located'], $action_args['args'] ); - include $located; + include $template; do_action( 'woocommerce_after_template_part', $action_args['template_name'], $action_args['template_path'], $action_args['located'], $action_args['args'] ); } diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index e2e08da9684..204991b9db3 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -398,6 +398,8 @@ function wc_reset_product_grid_settings() { if ( ! empty( $product_grid['default_columns'] ) ) { update_option( 'woocommerce_catalog_columns', absint( $product_grid['default_columns'] ) ); } + + wp_cache_flush(); // Flush any caches which could impact settings or templates. } add_action( 'after_switch_theme', 'wc_reset_product_grid_settings' ); From 536d71a5f68ba4013240e2d57338dd02faa81c74 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 13:11:09 +0000 Subject: [PATCH 163/401] cache get_current_page_url --- includes/abstracts/abstract-wc-widget.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/includes/abstracts/abstract-wc-widget.php b/includes/abstracts/abstract-wc-widget.php index eb91c48d081..50961c40d4c 100644 --- a/includes/abstracts/abstract-wc-widget.php +++ b/includes/abstracts/abstract-wc-widget.php @@ -289,6 +289,13 @@ abstract class WC_Widget extends WP_Widget { * @since 3.3.0 */ protected function get_current_page_url() { + $cache_key = 'woocommerce_widget_get_current_page_url'; + $cached_value = (string) wp_cache_get( $cache_key, 'woocommerce' ); + + if ( $cached_value ) { + return apply_filters( 'woocommerce_widget_get_current_page_url', $cached_value, $this ); + } + if ( defined( 'SHOP_IS_ON_FRONT' ) ) { $link = home_url(); } elseif ( is_shop() ) { From fa58f3bb9fb9b36c466d6072e0102ae31d2c2434 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 13:21:11 +0000 Subject: [PATCH 164/401] Lax the kses here - link and name is already escaped. --- includes/widgets/class-wc-widget-layered-nav.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/widgets/class-wc-widget-layered-nav.php b/includes/widgets/class-wc-widget-layered-nav.php index 66b9246a334..b51d1871fc7 100644 --- a/includes/widgets/class-wc-widget-layered-nav.php +++ b/includes/widgets/class-wc-widget-layered-nav.php @@ -458,8 +458,8 @@ class WC_Widget_Layered_Nav extends WC_Widget { } if ( $count > 0 || $option_is_set ) { - $link = esc_url( apply_filters( 'woocommerce_layered_nav_link', $link, $term, $taxonomy ) ); - $term_html = '' . esc_html( $term->name ) . ''; + $link = apply_filters( 'woocommerce_layered_nav_link', $link, $term, $taxonomy ); + $term_html = '' . esc_html( $term->name ) . ''; } else { $link = false; $term_html = '' . esc_html( $term->name ) . ''; @@ -468,7 +468,7 @@ class WC_Widget_Layered_Nav extends WC_Widget { $term_html .= ' ' . apply_filters( 'woocommerce_layered_nav_count', '(' . absint( $count ) . ')', $count, $term ); echo '
  • '; - echo wp_kses_post( apply_filters( 'woocommerce_layered_nav_term_html', $term_html, $term, $link, $count ) ); + echo apply_filters( 'woocommerce_layered_nav_term_html', $term_html, $term, $link, $count ); // WPCS: XSS ok. echo '
  • '; } From a5bc236c66c9f55fdbdc6bf777f185e11be37200 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 13:23:49 +0000 Subject: [PATCH 165/401] get_current_page_url cache set --- includes/abstracts/abstract-wc-widget.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/includes/abstracts/abstract-wc-widget.php b/includes/abstracts/abstract-wc-widget.php index 50961c40d4c..74adb9e18f3 100644 --- a/includes/abstracts/abstract-wc-widget.php +++ b/includes/abstracts/abstract-wc-widget.php @@ -354,6 +354,8 @@ abstract class WC_Widget extends WP_Widget { } } + wp_cache_set( $cache_key, $link, 'woocommerce' ); + return apply_filters( 'woocommerce_widget_get_current_page_url', $link, $this ); } From 4622af890ade56a41028ac026c619a85c2908f25 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 13:27:48 +0000 Subject: [PATCH 166/401] wc_attribute_taxonomy_slug cache --- includes/wc-attribute-functions.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/includes/wc-attribute-functions.php b/includes/wc-attribute-functions.php index 6a02dc278aa..c2450682510 100644 --- a/includes/wc-attribute-functions.php +++ b/includes/wc-attribute-functions.php @@ -678,6 +678,16 @@ function wc_delete_attribute( $id ) { * @return string */ function wc_attribute_taxonomy_slug( $attribute_name ) { + $cache_key = 'slug-' . $attribute_name; + $cache_value = wp_cache_get( $cache_key, 'woocommerce-attributes' ); + + if ( $cache_value ) { + return $cache_value; + } + $attribute_name = wc_sanitize_taxonomy_name( $attribute_name ); - return 0 === strpos( $attribute_name, 'pa_' ) ? substr( $attribute_name, 3 ) : $attribute_name; + $attribute_slug = 0 === strpos( $attribute_name, 'pa_' ) ? substr( $attribute_name, 3 ) : $attribute_name; + wp_cache_set( $cache_key, $attribute_slug, 'woocommerce-attributes' ); + + return $attribute_slug; } From 874f4830a6800978987505e83c9f1234b7c0a026 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 13:38:14 +0000 Subject: [PATCH 167/401] cache_value cleanup --- includes/abstracts/abstract-wc-widget.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/abstracts/abstract-wc-widget.php b/includes/abstracts/abstract-wc-widget.php index 74adb9e18f3..4ffc2e2aebb 100644 --- a/includes/abstracts/abstract-wc-widget.php +++ b/includes/abstracts/abstract-wc-widget.php @@ -289,11 +289,11 @@ abstract class WC_Widget extends WP_Widget { * @since 3.3.0 */ protected function get_current_page_url() { - $cache_key = 'woocommerce_widget_get_current_page_url'; - $cached_value = (string) wp_cache_get( $cache_key, 'woocommerce' ); + $cache_key = 'widget_current_page_url'; + $cache_value = wp_cache_get( $cache_key, 'woocommerce' ); - if ( $cached_value ) { - return apply_filters( 'woocommerce_widget_get_current_page_url', $cached_value, $this ); + if ( $cache_value ) { + return apply_filters( 'woocommerce_widget_get_current_page_url', $cache_value, $this ); } if ( defined( 'SHOP_IS_ON_FRONT' ) ) { From cb1c5792624953dc7a43c61c7aa4b4050a2de1cc Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 14:04:21 +0000 Subject: [PATCH 168/401] Cache tax class slugs --- includes/class-wc-tax.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-tax.php b/includes/class-wc-tax.php index 65be6044158..bc1c0c6fc6b 100644 --- a/includes/class-wc-tax.php +++ b/includes/class-wc-tax.php @@ -721,7 +721,16 @@ class WC_Tax { * @return array Array of class slugs ("reduced-rate", "zero-rate", etc). */ public static function get_tax_class_slugs() { - return array_filter( array_map( 'sanitize_title', self::get_tax_classes() ) ); + $cache_key = 'tax_class_slugs'; + $slugs = wp_cache_get( $cache_key, 'woocommerce' ); + + if ( ! $slugs ) { + $slugs = array_filter( array_map( 'sanitize_title', self::get_tax_classes() ) ); + + wp_cache_set( $cache_key, $slugs, 'woocommerce' ); + } + + return $slugs; } /** From 57ccde66437ade8e91d12890245d9d4c5e5e1892 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 14:13:34 +0000 Subject: [PATCH 169/401] get_product_type cache --- .../class-wc-product-data-store-cpt.php | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index c3b0b6c6b59..e4a5a78ddb7 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -1503,15 +1503,27 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da * @return bool|string */ public function get_product_type( $product_id ) { - $post_type = get_post_type( $product_id ); - if ( 'product_variation' === $post_type ) { - return 'variation'; - } elseif ( 'product' === $post_type ) { - $terms = get_the_terms( $product_id, 'product_type' ); - return ! empty( $terms ) ? sanitize_title( current( $terms )->name ) : 'simple'; - } else { - return false; + $cache_key = WC_Cache_Helper::get_cache_prefix( 'product_' . $product->get_id() ) . '_type_' . $product_id; + $product_type = wp_cache_get( $cache_key, 'products' ); + + if ( $product_type ) { + return $product_type; } + + $post_type = get_post_type( $product_id ); + + if ( 'product_variation' === $post_type ) { + $product_type = 'variation'; + } elseif ( 'product' === $post_type ) { + $terms = get_the_terms( $product_id, 'product_type' ); + $product_type = ! empty( $terms ) ? sanitize_title( current( $terms )->name ) : 'simple'; + } else { + $product_type = false; + } + + wp_cache_set( $cache_key, $product_type, 'products' ); + + return $product_type; } /** From 8fc7e2be995d932cc536b7d98dfa8071aa6ba9b1 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 14:13:53 +0000 Subject: [PATCH 170/401] prefix tax cache key --- includes/class-wc-tax.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-tax.php b/includes/class-wc-tax.php index bc1c0c6fc6b..813bf9f2d55 100644 --- a/includes/class-wc-tax.php +++ b/includes/class-wc-tax.php @@ -721,13 +721,13 @@ class WC_Tax { * @return array Array of class slugs ("reduced-rate", "zero-rate", etc). */ public static function get_tax_class_slugs() { - $cache_key = 'tax_class_slugs'; - $slugs = wp_cache_get( $cache_key, 'woocommerce' ); + $cache_key = WC_Cache_Helper::get_cache_prefix( 'taxes' ) . '-slugs'; + $slugs = wp_cache_get( $cache_key, 'taxes' ); if ( ! $slugs ) { $slugs = array_filter( array_map( 'sanitize_title', self::get_tax_classes() ) ); - wp_cache_set( $cache_key, $slugs, 'woocommerce' ); + wp_cache_set( $cache_key, $slugs, 'taxes' ); } return $slugs; From 23b0fdbc1f7327b9de3463900440f02ba7a33918 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 14:57:20 +0000 Subject: [PATCH 171/401] Fix id usage --- includes/data-stores/class-wc-product-data-store-cpt.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index e4a5a78ddb7..396a5750fec 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -1503,7 +1503,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da * @return bool|string */ public function get_product_type( $product_id ) { - $cache_key = WC_Cache_Helper::get_cache_prefix( 'product_' . $product->get_id() ) . '_type_' . $product_id; + $cache_key = WC_Cache_Helper::get_cache_prefix( 'product_' . $product_id ) . '_type_' . $product_id; $product_type = wp_cache_get( $cache_key, 'products' ); if ( $product_type ) { From e129d18c008dfb25b815f03bd832a7bc16d124af Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 16:18:56 +0000 Subject: [PATCH 172/401] get_current_page_url should not persist --- includes/abstracts/abstract-wc-widget.php | 9 --------- includes/widgets/class-wc-widget-rating-filter.php | 3 ++- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/includes/abstracts/abstract-wc-widget.php b/includes/abstracts/abstract-wc-widget.php index 4ffc2e2aebb..eb91c48d081 100644 --- a/includes/abstracts/abstract-wc-widget.php +++ b/includes/abstracts/abstract-wc-widget.php @@ -289,13 +289,6 @@ abstract class WC_Widget extends WP_Widget { * @since 3.3.0 */ protected function get_current_page_url() { - $cache_key = 'widget_current_page_url'; - $cache_value = wp_cache_get( $cache_key, 'woocommerce' ); - - if ( $cache_value ) { - return apply_filters( 'woocommerce_widget_get_current_page_url', $cache_value, $this ); - } - if ( defined( 'SHOP_IS_ON_FRONT' ) ) { $link = home_url(); } elseif ( is_shop() ) { @@ -354,8 +347,6 @@ abstract class WC_Widget extends WP_Widget { } } - wp_cache_set( $cache_key, $link, 'woocommerce' ); - return apply_filters( 'woocommerce_widget_get_current_page_url', $link, $this ); } diff --git a/includes/widgets/class-wc-widget-rating-filter.php b/includes/widgets/class-wc-widget-rating-filter.php index 3a64f15e404..ea5580de554 100644 --- a/includes/widgets/class-wc-widget-rating-filter.php +++ b/includes/widgets/class-wc-widget-rating-filter.php @@ -99,6 +99,7 @@ class WC_Widget_Rating_Filter extends WC_Widget { $found = false; $rating_filter = isset( $_GET['rating_filter'] ) ? array_filter( array_map( 'absint', explode( ',', wp_unslash( $_GET['rating_filter'] ) ) ) ) : array(); // WPCS: input var ok, CSRF ok, sanitization ok. + $base_link = $this->get_current_page_url(); $this->widget_start( $args, $instance ); @@ -110,7 +111,7 @@ class WC_Widget_Rating_Filter extends WC_Widget { continue; } $found = true; - $link = $this->get_current_page_url(); + $link = $base_link; if ( in_array( $rating, $rating_filter, true ) ) { $link_ratings = implode( ',', array_diff( $rating_filter, array( $rating ) ) ); From 8e51ab2fcdf1d3891453831bdbd1220345e8394f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 14:59:58 +0000 Subject: [PATCH 173/401] Set props using one get_post_meta call --- .../class-wc-product-data-store-cpt.php | 121 +++++++++--------- 1 file changed, 63 insertions(+), 58 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index c3b0b6c6b59..e67587a4647 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -299,66 +299,71 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da * @since 3.0.0 */ protected function read_product_data( &$product ) { - $id = $product->get_id(); - $review_count = get_post_meta( $id, '_wc_review_count', true ); - $rating_counts = get_post_meta( $id, '_wc_rating_count', true ); - $average_rating = get_post_meta( $id, '_wc_average_rating', true ); - - if ( '' === $review_count ) { - WC_Comments::get_review_count_for_product( $product ); - } else { - $product->set_review_count( $review_count ); - } - - if ( '' === $rating_counts ) { - WC_Comments::get_rating_counts_for_product( $product ); - } else { - $product->set_rating_counts( $rating_counts ); - } - - if ( '' === $average_rating ) { - WC_Comments::get_average_rating_for_product( $product ); - } else { - $product->set_average_rating( $average_rating ); - } - - $product->set_props( - array( - 'sku' => get_post_meta( $id, '_sku', true ), - 'regular_price' => get_post_meta( $id, '_regular_price', true ), - 'sale_price' => get_post_meta( $id, '_sale_price', true ), - 'price' => get_post_meta( $id, '_price', true ), - 'date_on_sale_from' => get_post_meta( $id, '_sale_price_dates_from', true ), - 'date_on_sale_to' => get_post_meta( $id, '_sale_price_dates_to', true ), - 'total_sales' => get_post_meta( $id, 'total_sales', true ), - 'tax_status' => get_post_meta( $id, '_tax_status', true ), - 'tax_class' => get_post_meta( $id, '_tax_class', true ), - 'manage_stock' => get_post_meta( $id, '_manage_stock', true ), - 'stock_quantity' => get_post_meta( $id, '_stock', true ), - 'stock_status' => get_post_meta( $id, '_stock_status', true ), - 'backorders' => get_post_meta( $id, '_backorders', true ), - 'low_stock_amount' => get_post_meta( $id, '_low_stock_amount', true ), - 'sold_individually' => get_post_meta( $id, '_sold_individually', true ), - 'weight' => get_post_meta( $id, '_weight', true ), - 'length' => get_post_meta( $id, '_length', true ), - 'width' => get_post_meta( $id, '_width', true ), - 'height' => get_post_meta( $id, '_height', true ), - 'upsell_ids' => get_post_meta( $id, '_upsell_ids', true ), - 'cross_sell_ids' => get_post_meta( $id, '_crosssell_ids', true ), - 'purchase_note' => get_post_meta( $id, '_purchase_note', true ), - 'default_attributes' => get_post_meta( $id, '_default_attributes', true ), - 'category_ids' => $this->get_term_ids( $product, 'product_cat' ), - 'tag_ids' => $this->get_term_ids( $product, 'product_tag' ), - 'shipping_class_id' => current( $this->get_term_ids( $product, 'product_shipping_class' ) ), - 'virtual' => get_post_meta( $id, '_virtual', true ), - 'downloadable' => get_post_meta( $id, '_downloadable', true ), - 'gallery_image_ids' => array_filter( explode( ',', get_post_meta( $id, '_product_image_gallery', true ) ) ), - 'download_limit' => get_post_meta( $id, '_download_limit', true ), - 'download_expiry' => get_post_meta( $id, '_download_expiry', true ), - 'image_id' => get_post_thumbnail_id( $id ), - ) + $id = $product->get_id(); + $post_meta_values = get_post_meta( $id ); + $meta_key_to_props = array( + '_sku' => 'sku', + '_regular_price' => 'regular_price', + '_sale_price' => 'sale_price', + '_sale_price_dates_from' => 'date_on_sale_from', + '_sale_price_dates_to' => 'date_on_sale_to', + 'total_sales' => 'total_sales', + '_tax_status' => 'tax_status', + '_tax_class' => 'tax_class', + '_manage_stock' => 'manage_stock', + '_backorders' => 'backorders', + '_low_stock_amount' => 'low_stock_amount', + '_sold_individually' => 'sold_individually', + '_weight' => 'weight', + '_length' => 'length', + '_width' => 'width', + '_height' => 'height', + '_upsell_ids' => 'upsell_ids', + '_crosssell_ids' => 'cross_sell_ids', + '_purchase_note' => 'purchase_note', + '_default_attributes' => 'default_attributes', + '_virtual' => 'virtual', + '_downloadable' => 'downloadable', + '_download_limit' => 'download_limit', + '_download_expiry' => 'download_expiry', + '_thumbnail_id' => 'image_id', + '_stock' => 'stock_quantity', + '_stock_status' => 'stock_status', + '_wc_average_rating' => 'average_rating', + '_wc_rating_count' => 'rating_counts', + '_wc_review_count' => 'review_count', + '_product_image_gallery' => 'gallery_image_ids', ); + $set_props = array(); + + foreach ( $meta_key_to_props as $meta_key => $prop ) { + $meta_value = isset( $post_meta_values[ $meta_key ][0] ) ? $post_meta_values[ $meta_key ][0] : ''; + $set_props[ $prop ] = maybe_unserialize( $meta_value ); // get_post_meta only unserializes single values. + } + + $set_props['category_ids'] = $this->get_term_ids( $product, 'product_cat' ); + $set_props['tag_ids'] = $this->get_term_ids( $product, 'product_tag' ); + $set_props['shipping_class_id'] = current( $this->get_term_ids( $product, 'product_shipping_class' ) ); + $set_props['gallery_image_ids'] = array_filter( explode( ',', $set_props['gallery_image_ids'] ) ); + + if ( '' === $set_props['review_count'] ) { + unset( $set_props['review_count'] ); + WC_Comments::get_review_count_for_product( $product ); + } + + if ( '' === $set_props['rating_counts'] ) { + unset( $set_props['rating_counts'] ); + WC_Comments::get_rating_counts_for_product( $product ); + } + + if ( '' === $set_props['average_rating'] ) { + unset( $set_props['average_rating'] ); + WC_Comments::get_average_rating_for_product( $product ); + } + + $product->set_props( $set_props ); + // Handle sale dates on the fly in case of missed cron schedule. if ( $product->is_type( 'simple' ) && $product->is_on_sale( 'edit' ) && $product->get_sale_price( 'edit' ) !== $product->get_price( 'edit' ) ) { update_post_meta( $product->get_id(), '_price', $product->get_sale_price( 'edit' ) ); From a16c677baf2377d8e04218fcb55ef271e4786997 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 15:12:32 +0000 Subject: [PATCH 174/401] Missing _price --- includes/data-stores/class-wc-product-data-store-cpt.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index e67587a4647..6a927ae1a05 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -305,6 +305,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da '_sku' => 'sku', '_regular_price' => 'regular_price', '_sale_price' => 'sale_price', + '_price' => 'price', '_sale_price_dates_from' => 'date_on_sale_from', '_sale_price_dates_to' => 'date_on_sale_to', 'total_sales' => 'total_sales', From 7eda1bb412c776b984ad0f621f5bde2a5aab8fa0 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 15:23:02 +0000 Subject: [PATCH 175/401] Only use WP Error when needed --- includes/abstracts/abstract-wc-data.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/includes/abstracts/abstract-wc-data.php b/includes/abstracts/abstract-wc-data.php index 7001af7b609..540b8834a60 100644 --- a/includes/abstracts/abstract-wc-data.php +++ b/includes/abstracts/abstract-wc-data.php @@ -630,7 +630,7 @@ abstract class WC_Data { * @return bool|WP_Error */ public function set_props( $props, $context = 'set' ) { - $errors = new WP_Error(); + $errors = false; foreach ( $props as $prop => $value ) { try { @@ -646,11 +646,14 @@ abstract class WC_Data { } } } catch ( WC_Data_Exception $e ) { + if ( ! $errors ) { + $errors = new WP_Error(); + } $errors->add( $e->getErrorCode(), $e->getMessage() ); } } - return count( $errors->get_error_codes() ) ? $errors : true; + return $errors && count( $errors->get_error_codes() ) ? $errors : true; } /** From 9a8d11e9f60ce905facd2235cf81400067b31bd9 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 15:25:41 +0000 Subject: [PATCH 176/401] phpcs --- includes/abstracts/abstract-wc-data.php | 62 ++++++++++++++----------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/includes/abstracts/abstract-wc-data.php b/includes/abstracts/abstract-wc-data.php index 540b8834a60..4721caa4893 100644 --- a/includes/abstracts/abstract-wc-data.php +++ b/includes/abstracts/abstract-wc-data.php @@ -220,7 +220,7 @@ abstract class WC_Data { * @return string Data in JSON format. */ public function __toString() { - return json_encode( $this->get_data() ); + return wp_json_encode( $this->get_data() ); } /** @@ -283,7 +283,7 @@ abstract class WC_Data { * @return bool true if it's an internal key, false otherwise */ protected function is_internal_meta_key( $key ) { - $internal_meta_key = ! empty( $key ) && $this->data_store && in_array( $key, $this->data_store->get_internal_meta_keys() ); + $internal_meta_key = ! empty( $key ) && $this->data_store && in_array( $key, $this->data_store->get_internal_meta_keys(), true ); if ( ! $internal_meta_key ) { return false; @@ -320,7 +320,7 @@ abstract class WC_Data { $this->maybe_read_meta_data(); $meta_data = $this->get_meta_data(); - $array_keys = array_keys( wp_list_pluck( $meta_data, 'key' ), $key ); + $array_keys = array_keys( wp_list_pluck( $meta_data, 'key' ), $key, true ); $value = $single ? '' : array(); if ( ! empty( $array_keys ) ) { @@ -349,7 +349,7 @@ abstract class WC_Data { public function meta_exists( $key = '' ) { $this->maybe_read_meta_data(); $array_keys = wp_list_pluck( $this->get_meta_data(), 'key' ); - return in_array( $key, $array_keys ); + return in_array( $key, $array_keys, true ); } /** @@ -364,11 +364,13 @@ abstract class WC_Data { foreach ( $data as $meta ) { $meta = (array) $meta; if ( isset( $meta['key'], $meta['value'], $meta['id'] ) ) { - $this->meta_data[] = new WC_Meta_Data( array( - 'id' => $meta['id'], - 'key' => $meta['key'], - 'value' => $meta['value'], - ) ); + $this->meta_data[] = new WC_Meta_Data( + array( + 'id' => $meta['id'], + 'key' => $meta['key'], + 'value' => $meta['value'], + ) + ); } } } @@ -379,9 +381,9 @@ abstract class WC_Data { * * @since 2.6.0 * - * @param string $key Meta key. - * @param string|array $value Meta value. - * @param bool $unique Should this be a unique key?. + * @param string $key Meta key. + * @param string|array $value Meta value. + * @param bool $unique Should this be a unique key?. */ public function add_meta_data( $key, $value, $unique = false ) { if ( $this->is_internal_meta_key( $key ) ) { @@ -396,10 +398,12 @@ abstract class WC_Data { if ( $unique ) { $this->delete_meta_data( $key ); } - $this->meta_data[] = new WC_Meta_Data( array( - 'key' => $key, - 'value' => $value, - ) ); + $this->meta_data[] = new WC_Meta_Data( + array( + 'key' => $key, + 'value' => $value, + ) + ); } /** @@ -462,7 +466,7 @@ abstract class WC_Data { */ public function delete_meta_data( $key ) { $this->maybe_read_meta_data(); - $array_keys = array_keys( wp_list_pluck( $this->meta_data, 'key' ), $key ); + $array_keys = array_keys( wp_list_pluck( $this->meta_data, 'key' ), $key, true ); if ( $array_keys ) { foreach ( $array_keys as $array_key ) { @@ -479,7 +483,7 @@ abstract class WC_Data { */ public function delete_meta_data_by_mid( $mid ) { $this->maybe_read_meta_data(); - $array_keys = array_keys( wp_list_pluck( $this->meta_data, 'id' ), $mid ); + $array_keys = array_keys( wp_list_pluck( $this->meta_data, 'id' ), (int) $mid, true ); if ( $array_keys ) { foreach ( $array_keys as $array_key ) { @@ -507,8 +511,8 @@ abstract class WC_Data { * @param bool $force_read True to force a new DB read (and update cache). */ public function read_meta_data( $force_read = false ) { - $this->meta_data = array(); - $cache_loaded = false; + $this->meta_data = array(); + $cache_loaded = false; if ( ! $this->get_id() ) { return; @@ -533,11 +537,13 @@ abstract class WC_Data { $raw_meta_data = $cache_loaded ? $cached_meta : $this->data_store->read_meta( $this ); if ( $raw_meta_data ) { foreach ( $raw_meta_data as $meta ) { - $this->meta_data[] = new WC_Meta_Data( array( - 'id' => (int) $meta->meta_id, - 'key' => $meta->meta_key, - 'value' => maybe_unserialize( $meta->meta_value ), - ) ); + $this->meta_data[] = new WC_Meta_Data( + array( + 'id' => (int) $meta->meta_id, + 'key' => $meta->meta_key, + 'value' => maybe_unserialize( $meta->meta_value ), + ) + ); } if ( ! $cache_loaded && ! empty( $this->cache_group ) ) { @@ -593,8 +599,8 @@ abstract class WC_Data { * @since 3.0.0 */ public function set_defaults() { - $this->data = $this->default_data; - $this->changes = array(); + $this->data = $this->default_data; + $this->changes = array(); $this->set_object_read( false ); } @@ -760,7 +766,7 @@ abstract class WC_Data { } else { $timestamp = wc_string_to_timestamp( get_gmt_from_date( gmdate( 'Y-m-d H:i:s', wc_string_to_timestamp( $value ) ) ) ); } - $datetime = new WC_DateTime( "@{$timestamp}", new DateTimeZone( 'UTC' ) ); + $datetime = new WC_DateTime( "@{$timestamp}", new DateTimeZone( 'UTC' ) ); } // Set local timezone or offset. From 77eba8681899a42ecf31f266a81a65267b636a43 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 15:30:39 +0000 Subject: [PATCH 177/401] Get rid of reflection method to speed up set_props --- includes/abstracts/abstract-wc-data.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/includes/abstracts/abstract-wc-data.php b/includes/abstracts/abstract-wc-data.php index 4721caa4893..443de6f1e00 100644 --- a/includes/abstracts/abstract-wc-data.php +++ b/includes/abstracts/abstract-wc-data.php @@ -640,16 +640,16 @@ abstract class WC_Data { foreach ( $props as $prop => $value ) { try { - if ( 'meta_data' === $prop ) { + /** + * Checks if the prop being set is allowed, and the value is not null. + */ + if ( is_null( $value ) || in_array( $prop, array( 'prop', 'date_prop', 'meta_data' ), true ) ) { continue; } $setter = "set_$prop"; - if ( ! is_null( $value ) && is_callable( array( $this, $setter ) ) ) { - $reflection = new ReflectionMethod( $this, $setter ); - if ( $reflection->isPublic() ) { - $this->{$setter}( $value ); - } + if ( is_callable( array( $this, $setter ) ) ) { + $this->{$setter}( $value ); } } catch ( WC_Data_Exception $e ) { if ( ! $errors ) { From 5b57cb535653bcba8c7ae901c6fb70580a4a7279 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 16:29:01 +0000 Subject: [PATCH 178/401] Layered nav should use base link --- includes/widgets/class-wc-widget-layered-nav.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/includes/widgets/class-wc-widget-layered-nav.php b/includes/widgets/class-wc-widget-layered-nav.php index b51d1871fc7..be045fea349 100644 --- a/includes/widgets/class-wc-widget-layered-nav.php +++ b/includes/widgets/class-wc-widget-layered-nav.php @@ -405,6 +405,7 @@ class WC_Widget_Layered_Nav extends WC_Widget { $term_counts = $this->get_filtered_term_product_counts( wp_list_pluck( $terms, 'term_id' ), $taxonomy, $query_type ); $_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes(); $found = false; + $base_link = $this->get_current_page_url(); foreach ( $terms as $term ) { $current_values = isset( $_chosen_attributes[ $taxonomy ]['terms'] ) ? $_chosen_attributes[ $taxonomy ]['terms'] : array(); @@ -431,7 +432,7 @@ class WC_Widget_Layered_Nav extends WC_Widget { $current_filter[] = $term->slug; } - $link = remove_query_arg( $filter_name, $this->get_current_page_url() ); + $link = remove_query_arg( $filter_name, $base_link ); // Add current filters to URL. foreach ( $current_filter as $key => $value ) { From cd4039e07885b76d55dbccd4ab72edbe67c87628 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 17:38:41 +0000 Subject: [PATCH 179/401] Only do includes on rest requests --- includes/class-wc-api.php | 65 ++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/includes/class-wc-api.php b/includes/class-wc-api.php index a63e6aed598..157d0259ae6 100644 --- a/includes/class-wc-api.php +++ b/includes/class-wc-api.php @@ -1,8 +1,11 @@ wc_api_init(); $this->rest_api_init(); } + /** + * Init the WC API by adding endpoints for those requests. + */ + private function wc_api_init() { + add_filter( 'query_vars', array( $this, 'add_query_vars' ), 0 ); + add_action( 'init', array( $this, 'add_endpoint' ), 0 ); + add_action( 'parse_request', array( $this, 'handle_api_requests' ), 0 ); + } + + /** + * Init WP REST API by hooking into `rest_api_init`. + * + * @since 2.6.0 + */ + private function rest_api_init() { + add_action( 'rest_api_init', array( $this, 'rest_api_includes' ), 5 ); + add_action( 'rest_api_init', array( $this, 'register_rest_routes' ), 10 ); + } + /** * Add new query vars. * @@ -87,6 +97,9 @@ class WC_API extends WC_Legacy_API { // Clean the API request. $api_request = strtolower( wc_clean( $wp->query_vars['wc-api'] ) ); + // Make sure gateways are available for request. + WC()->payment_gateways(); + // Trigger generic action before request hook. do_action( 'woocommerce_api_request', $api_request ); @@ -102,29 +115,12 @@ class WC_API extends WC_Legacy_API { } } - /** - * Init WP REST API. - * - * @since 2.6.0 - */ - private function rest_api_init() { - // REST API was included starting WordPress 4.4. - if ( ! class_exists( 'WP_REST_Server' ) ) { - return; - } - - $this->rest_api_includes(); - - // Init REST API routes. - add_action( 'rest_api_init', array( $this, 'register_rest_routes' ), 10 ); - } - /** * Include REST API classes. * * @since 2.6.0 */ - private function rest_api_includes() { + public function rest_api_includes() { // Exception handler. include_once dirname( __FILE__ ) . '/api/class-wc-rest-exception.php'; @@ -370,5 +366,4 @@ class WC_API extends WC_Legacy_API { new WC_Register_WP_Admin_Settings( $email, 'email' ); } } - } From dd981be6154e6a50df1566c1ca5319d2015fe1d6 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 30 Jan 2019 18:12:33 +0000 Subject: [PATCH 180/401] We need auth early --- includes/class-wc-api.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-api.php b/includes/class-wc-api.php index 157d0259ae6..d4a4fbe62e3 100644 --- a/includes/class-wc-api.php +++ b/includes/class-wc-api.php @@ -45,6 +45,9 @@ class WC_API extends WC_Legacy_API { * @since 2.6.0 */ private function rest_api_init() { + // Authentication needs to run early to handle basic auth. + include_once dirname( __FILE__ ) . '/api/class-wc-rest-authentication.php'; + add_action( 'rest_api_init', array( $this, 'rest_api_includes' ), 5 ); add_action( 'rest_api_init', array( $this, 'register_rest_routes' ), 10 ); } @@ -124,9 +127,6 @@ class WC_API extends WC_Legacy_API { // Exception handler. include_once dirname( __FILE__ ) . '/api/class-wc-rest-exception.php'; - // Authentication. - include_once dirname( __FILE__ ) . '/api/class-wc-rest-authentication.php'; - // Abstract controllers. include_once dirname( __FILE__ ) . '/abstracts/abstract-wc-rest-controller.php'; include_once dirname( __FILE__ ) . '/abstracts/abstract-wc-rest-posts-controller.php'; From 17a59c5004148be3aa1c5f230acd92bc1e1821da Mon Sep 17 00:00:00 2001 From: Gregory Karpinsky <1696330+tivnet@users.noreply.github.com> Date: Wed, 30 Jan 2019 23:31:39 -0500 Subject: [PATCH 181/401] Typo in class-wc-log-levels.php --- includes/class-wc-log-levels.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-log-levels.php b/includes/class-wc-log-levels.php index 6f58bb8f4f5..0f635de92bd 100644 --- a/includes/class-wc-log-levels.php +++ b/includes/class-wc-log-levels.php @@ -95,7 +95,7 @@ abstract class WC_Log_Levels { /** * Translate severity integer to level string. * - * @param int $severity Serevity level. + * @param int $severity Severity level. * @return bool|string False if not recognized. Otherwise string representation of level. */ public static function get_severity_level( $severity ) { From 1f21fd86c55855fe467917b7f4bc74162f9ec819 Mon Sep 17 00:00:00 2001 From: Shubham Mathur Date: Thu, 31 Jan 2019 18:31:59 +0530 Subject: [PATCH 182/401] Fix for images with no metadata (#22562) * Fix for images with no metadata Fix for error when images have no metadata or their metadata is removed. * Fix for images with no metadata Fix for error when images have no metadata or their metadata is removed. * Fix for images with no metadata Fix for error when images have no metadata or their metadata is removed. * Fix for images with no metadata Fix for error when images have no metadata or their metadata is removed. --- includes/class-wc-regenerate-images.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/includes/class-wc-regenerate-images.php b/includes/class-wc-regenerate-images.php index 8ea7bed9ef9..4be79d91bf8 100644 --- a/includes/class-wc-regenerate-images.php +++ b/includes/class-wc-regenerate-images.php @@ -347,6 +347,11 @@ class WC_Regenerate_Images { $metadata = wp_get_attachment_metadata( $attachment_id ); + // Fix for images with no metadata. + if ( ! is_array( $metadata ) ) { + $metadata = array(); + } + // We only want to regen a specific image size. add_filter( 'intermediate_image_sizes', array( __CLASS__, 'adjust_intermediate_image_sizes' ) ); From 64c76ee8b9644ebdd1de8f261c3394d3e65f42c4 Mon Sep 17 00:00:00 2001 From: Galen Wright-Watson Date: Fri, 25 Jan 2019 15:40:41 -0800 Subject: [PATCH 183/401] Adds filter for product categories displayed by `product_categories` shortcode. --- includes/class-wc-shortcodes.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-shortcodes.php b/includes/class-wc-shortcodes.php index 3efbb4b68d8..42d2ca5902a 100644 --- a/includes/class-wc-shortcodes.php +++ b/includes/class-wc-shortcodes.php @@ -174,7 +174,10 @@ class WC_Shortcodes { 'child_of' => $atts['parent'], ); - $product_categories = get_terms( 'product_cat', $args ); + $product_categories = apply_filters( + 'woocommerce_product_categories', + get_terms( 'product_cat', $args ) + ); if ( '' !== $atts['parent'] ) { $product_categories = wp_list_filter( $product_categories, array( From f5f020e6f489162b1c0fe6a5dc67d5a55fad6ab7 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Thu, 31 Jan 2019 11:15:14 -0400 Subject: [PATCH 184/401] add logic for $main_query parameter in get_meta_query --- includes/class-wc-query.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-query.php b/includes/class-wc-query.php index e60979b2d98..353877554bf 100644 --- a/includes/class-wc-query.php +++ b/includes/class-wc-query.php @@ -598,10 +598,14 @@ class WC_Query { * @return array */ public function get_meta_query( $meta_query = array(), $main_query = false ) { + global $wp_query; + if ( ! is_array( $meta_query ) ) { $meta_query = array(); } - $meta_query['price_filter'] = $this->price_filter_meta_query(); + if ( $main_query || $wp_query === self::$product_query ) { + $meta_query['price_filter'] = $this->price_filter_meta_query(); + } return array_filter( apply_filters( 'woocommerce_product_query_meta_query', $meta_query, $this ) ); } From 9855a18036a446e4f9382a27668abacda8f05255 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Thu, 31 Jan 2019 11:18:19 -0400 Subject: [PATCH 185/401] phpcs sniff fixes for class-wc-query.php --- includes/class-wc-query.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-query.php b/includes/class-wc-query.php index 353877554bf..b42a754673d 100644 --- a/includes/class-wc-query.php +++ b/includes/class-wc-query.php @@ -515,7 +515,7 @@ class WC_Query { global $wpdb, $wp_query; if ( isset( $wp_query->queried_object, $wp_query->queried_object->term_taxonomy_id, $wp_query->queried_object->taxonomy ) && is_a( $wp_query->queried_object, 'WP_Term' ) ) { - $search_within_terms = get_terms( + $search_within_terms = get_terms( array( 'taxonomy' => $wp_query->queried_object->taxonomy, 'child_of' => $wp_query->queried_object->term_id, @@ -550,7 +550,7 @@ class WC_Query { global $wpdb, $wp_query; if ( isset( $wp_query->queried_object, $wp_query->queried_object->term_taxonomy_id, $wp_query->queried_object->taxonomy ) && is_a( $wp_query->queried_object, 'WP_Term' ) ) { - $search_within_terms = get_terms( + $search_within_terms = get_terms( array( 'taxonomy' => $wp_query->queried_object->taxonomy, 'child_of' => $wp_query->queried_object->term_id, From 25ebc45fbcb207f8901cf481c23571b4116afff4 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 31 Jan 2019 15:46:26 +0000 Subject: [PATCH 186/401] Make consistent with checkout and rename filter --- includes/class-wc-cart.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-cart.php b/includes/class-wc-cart.php index 1c4bdc139ad..9f717fbeb04 100644 --- a/includes/class-wc-cart.php +++ b/includes/class-wc-cart.php @@ -1942,12 +1942,14 @@ class WC_Cart extends WC_Legacy_Cart { /** * Returns the hash based on cart contents. * + * @since 3.6.0 * @return string hash for cart content */ public function get_cart_hash() { - $cart = $this->session->get_cart_for_session(); - $hash = $cart ? md5( json_encode( $cart ) ) : ''; + $cart_session = $this->session->get_cart_for_session(); + $hash = $cart_session ? md5( wp_json_encode( $cart_session ) . $this->get_total( 'edit' ) ) : ''; + $hash = apply_filters_deprecated( 'woocommerce_add_to_cart_hash', array( $hash, $cart_session ), '3.6.0', 'woocommerce_cart_hash' ); - return apply_filters( 'woocommerce_add_to_cart_hash', $hash, $cart ); + return apply_filters( 'woocommerce_cart_hash', $hash, $cart_session ); } } From a68ed39e6348f794c6dbb3b36c9606261bd8a9b5 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 31 Jan 2019 15:46:41 +0000 Subject: [PATCH 187/401] use cart hash method on checkout --- includes/class-wc-checkout.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-checkout.php b/includes/class-wc-checkout.php index a86e76495da..37ed3f792e8 100644 --- a/includes/class-wc-checkout.php +++ b/includes/class-wc-checkout.php @@ -289,7 +289,7 @@ class WC_Checkout { try { $order_id = absint( WC()->session->get( 'order_awaiting_payment' ) ); - $cart_hash = md5( wp_json_encode( wc_clean( WC()->cart->get_cart_for_session() ) ) . WC()->cart->total ); + $cart_hash = WC()->cart->get_cart_hash(); $available_gateways = WC()->payment_gateways->get_available_payment_gateways(); $order = $order_id ? wc_get_order( $order_id ) : null; From 4952fbc475f11b28ab70c087c865fab54841f3db Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 31 Jan 2019 16:28:32 +0000 Subject: [PATCH 188/401] Adjust both login templates --- templates/global/form-login.php | 10 +++++----- templates/myaccount/form-login.php | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/templates/global/form-login.php b/templates/global/form-login.php index 3dc991879e8..846e8d84771 100644 --- a/templates/global/form-login.php +++ b/templates/global/form-login.php @@ -13,7 +13,7 @@ * @see https://docs.woocommerce.com/document/template-structure/ * @author WooThemes * @package WooCommerce/Templates - * @version 3.3.0 + * @version 3.6.0 */ if ( ! defined( 'ABSPATH' ) ) { @@ -44,12 +44,12 @@ if ( is_user_logged_in() ) {

    - - - -

    diff --git a/templates/myaccount/form-login.php b/templates/myaccount/form-login.php index c80463492ec..001f3423988 100644 --- a/templates/myaccount/form-login.php +++ b/templates/myaccount/form-login.php @@ -12,7 +12,7 @@ * * @see https://docs.woocommerce.com/document/template-structure/ * @package WooCommerce/Templates - * @version 3.5.0 + * @version 3.6.0 */ if ( ! defined( 'ABSPATH' ) ) { @@ -47,11 +47,11 @@ do_action( 'woocommerce_before_customer_login_form' ); ?>

    -

    From e299da75b0b6c1769978aec95facb9fd52bedaf9 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 31 Jan 2019 16:28:54 +0000 Subject: [PATCH 189/401] Update styling for remember me --- assets/css/twenty-seventeen.scss | 11 +++++++++++ assets/css/woocommerce.scss | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/assets/css/twenty-seventeen.scss b/assets/css/twenty-seventeen.scss index 776db2d71cf..6181d04469d 100644 --- a/assets/css/twenty-seventeen.scss +++ b/assets/css/twenty-seventeen.scss @@ -80,6 +80,17 @@ visibility: visible; } } + + .woocommerce-form-login { + .woocommerce-form-login__submit { + float: left; + margin-right: 1em; + } + .woocommerce-form-login__rememberme { + display: inline-block; + line-height: 3em; + } + } } .woocommerce-breadcrumb { diff --git a/assets/css/woocommerce.scss b/assets/css/woocommerce.scss index 680ba0c1123..556920251db 100644 --- a/assets/css/woocommerce.scss +++ b/assets/css/woocommerce.scss @@ -1670,6 +1670,16 @@ p.demo_store, color: $red; } } + + .woocommerce-form-login { + .woocommerce-form-login__submit { + float: left; + margin-right: 1em; + } + .woocommerce-form-login__rememberme { + display: inline-block; + } + } } .woocommerce-no-js { From 8627fc39b97beb361345098db882d220a0cc0802 Mon Sep 17 00:00:00 2001 From: Galen Wright-Watson Date: Mon, 28 Jan 2019 18:51:11 -0800 Subject: [PATCH 190/401] Update: install script-escape forward & backward slashes and ampersand in supplied password. --- tests/bin/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bin/install.sh b/tests/bin/install.sh index 941400efbcf..77d081b9505 100755 --- a/tests/bin/install.sh +++ b/tests/bin/install.sh @@ -92,7 +92,7 @@ install_test_suite() { sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php # escape the regex delim if not already escaped (i.e. if preceded by an even number of backslashes) - E_DB_PASS=$(echo $DB_PASS | sed -E -e 's%((^|[^\\])(\\\\)*)/%\1\\/%') + E_DB_PASS=$(echo $DB_PASS | sed -E -e 's%([/&\\])%\\\1%g') sed $ioption "s/yourpasswordhere/${E_DB_PASS}/" "$WP_TESTS_DIR"/wp-tests-config.php sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php fi From a7bee0325d27e9383312e1d81ca8643ee422bbd2 Mon Sep 17 00:00:00 2001 From: Gregory Karpinsky <1696330+tivnet@users.noreply.github.com> Date: Thu, 31 Jan 2019 21:23:22 -0500 Subject: [PATCH 191/401] WC_Log_Handler_File::remove - fix for MS Windows WC_LOG_DIR is defined with Unix slashes at the end. The `realpath` has Windows slashes and therefore `stripos` never works. Consider also fixing slashes here: `$this->define( 'WC_LOG_DIR', $upload_dir['basedir'] . '/wc-logs/' );` --- includes/log-handlers/class-wc-log-handler-file.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/log-handlers/class-wc-log-handler-file.php b/includes/log-handlers/class-wc-log-handler-file.php index 85514dbbfd9..74079f0a7a1 100644 --- a/includes/log-handlers/class-wc-log-handler-file.php +++ b/includes/log-handlers/class-wc-log-handler-file.php @@ -253,7 +253,7 @@ class WC_Log_Handler_File extends WC_Log_Handler { if ( isset( $logs[ $handle ] ) && $logs[ $handle ] ) { $file = realpath( trailingslashit( WC_LOG_DIR ) . $logs[ $handle ] ); - if ( 0 === stripos( $file, trailingslashit( WC_LOG_DIR ) ) && is_file( $file ) && is_writable( $file ) ) { // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_is_writable + if ( 0 === stripos( $file, realpath( trailingslashit( WC_LOG_DIR ) ) ) && is_file( $file ) && is_writable( $file ) ) { // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_is_writable $this->close( $file ); // Close first to be certain no processes keep it alive after it is unlinked. $removed = unlink( $file ); // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_unlink } From f609f713db8e2c8ec21eed21b24ffbbf4b3df0a6 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 1 Feb 2019 05:57:51 +0000 Subject: [PATCH 192/401] Update dependency phpunit/phpunit to v6.5.14 --- composer.json | 2 +- composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 6b96d22c33d..83359209593 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "require-dev": { "apigen/apigen": "4.1.2", "nette/utils": "2.5.3", - "phpunit/phpunit": "6.5.13", + "phpunit/phpunit": "6.5.14", "woocommerce/woocommerce-sniffs": "0.0.5" }, "scripts": { diff --git a/composer.lock b/composer.lock index 08825d6cc6e..a140bb4df0c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "291b6e5b4c860eeb4cf1caa6529a21e7", + "content-hash": "67b8066152baf2f08393562b576da266", "packages": [ { "name": "composer/installers", @@ -2666,16 +2666,16 @@ }, { "name": "phpunit/phpunit", - "version": "6.5.13", + "version": "6.5.14", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "0973426fb012359b2f18d3bd1e90ef1172839693" + "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0973426fb012359b2f18d3bd1e90ef1172839693", - "reference": "0973426fb012359b2f18d3bd1e90ef1172839693", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bac23fe7ff13dbdb461481f706f0e9fe746334b7", + "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7", "shasum": "" }, "require": { @@ -2746,7 +2746,7 @@ "testing", "xunit" ], - "time": "2018-09-08T15:10:43+00:00" + "time": "2019-02-01T05:22:47+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -3731,7 +3731,7 @@ }, { "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "email": "backendtea@gmail.com" } ], "description": "Symfony polyfill for ctype functions", From e1213f8432bfe44d1cc8c8378fd2d06f1eab388f Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Thu, 31 Jan 2019 13:29:38 -0400 Subject: [PATCH 193/401] remove the WP main query check --- includes/class-wc-query.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/includes/class-wc-query.php b/includes/class-wc-query.php index b42a754673d..3c88200ef9e 100644 --- a/includes/class-wc-query.php +++ b/includes/class-wc-query.php @@ -598,12 +598,10 @@ class WC_Query { * @return array */ public function get_meta_query( $meta_query = array(), $main_query = false ) { - global $wp_query; - if ( ! is_array( $meta_query ) ) { $meta_query = array(); } - if ( $main_query || $wp_query === self::$product_query ) { + if ( $main_query ) { $meta_query['price_filter'] = $this->price_filter_meta_query(); } return array_filter( apply_filters( 'woocommerce_product_query_meta_query', $meta_query, $this ) ); From 8b7414cfb5f528396f502be56a43773a769cea19 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 1 Feb 2019 14:13:37 +0000 Subject: [PATCH 194/401] Apply fix to rating filter --- includes/abstracts/abstract-wc-widget.php | 5 +++++ includes/widgets/class-wc-widget-rating-filter.php | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/includes/abstracts/abstract-wc-widget.php b/includes/abstracts/abstract-wc-widget.php index a07ff1b060c..fe66613aee0 100644 --- a/includes/abstracts/abstract-wc-widget.php +++ b/includes/abstracts/abstract-wc-widget.php @@ -327,6 +327,11 @@ abstract class WC_Widget extends WP_Widget { // Post Type Arg. if ( isset( $_GET['post_type'] ) ) { $link = add_query_arg( 'post_type', wc_clean( wp_unslash( $_GET['post_type'] ) ), $link ); + + // Prevent post type and page id when pretty permalinks are disabled. + if ( is_shop() ) { + $link = remove_query_arg( 'page_id', $link ); + } } // Min Rating Arg. diff --git a/includes/widgets/class-wc-widget-rating-filter.php b/includes/widgets/class-wc-widget-rating-filter.php index 3a64f15e404..f4974dacc8a 100644 --- a/includes/widgets/class-wc-widget-rating-filter.php +++ b/includes/widgets/class-wc-widget-rating-filter.php @@ -110,7 +110,7 @@ class WC_Widget_Rating_Filter extends WC_Widget { continue; } $found = true; - $link = $this->get_current_page_url(); + $link = remove_query_arg( 'paged', $this->get_current_page_url() ); if ( in_array( $rating, $rating_filter, true ) ) { $link_ratings = implode( ',', array_diff( $rating_filter, array( $rating ) ) ); @@ -119,7 +119,7 @@ class WC_Widget_Rating_Filter extends WC_Widget { } $class = in_array( $rating, $rating_filter, true ) ? 'wc-layered-nav-rating chosen' : 'wc-layered-nav-rating'; - $link = apply_filters( 'woocommerce_rating_filter_link', $link_ratings ? add_query_arg( 'rating_filter', $link_ratings ) : remove_query_arg( 'rating_filter' ) ); + $link = apply_filters( 'woocommerce_rating_filter_link', $link_ratings ? add_query_arg( 'rating_filter', $link_ratings, $link ) : remove_query_arg( 'rating_filter' ) ); $rating_html = wc_get_star_rating_html( $rating ); $count_html = wp_kses( apply_filters( 'woocommerce_rating_filter_count', "({$count})", $count, $rating ), From 8fde07153a270eec6f11df8e2689b68c4a1ac865 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 1 Feb 2019 14:30:42 +0000 Subject: [PATCH 195/401] Use get instead of load --- assets/js/frontend/add-to-cart.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/assets/js/frontend/add-to-cart.js b/assets/js/frontend/add-to-cart.js index a0b2809fcaf..5b5a332c699 100644 --- a/assets/js/frontend/add-to-cart.js +++ b/assets/js/frontend/add-to-cart.js @@ -119,15 +119,13 @@ jQuery( function( $ ) { AddToCartHandler.prototype.updateCartPage = function() { var page = window.location.toString().replace( 'add-to-cart', 'added-to-cart' ); - $( '.shop_table.cart' ).load( page + ' .shop_table.cart:eq(0) > *', function() { - $( '.shop_table.cart' ).stop( true ).css( 'opacity', '1' ).unblock(); + $.get( page, function( data ) { + $( '.shop_table.cart:eq(0)' ).replaceWith( $( data ).find( '.shop_table.cart:eq(0)' ) ); + $( '.cart_totals:eq(0)' ).replaceWith( $( data ).find( '.cart_totals:eq(0)' ) ); + $( '.cart_totals, .shop_table.cart' ).stop( true ).css( 'opacity', '1' ).unblock(); $( document.body ).trigger( 'cart_page_refreshed' ); - }); - - $( '.cart_totals' ).load( page + ' .cart_totals:eq(0) > *', function() { - $( '.cart_totals' ).stop( true ).css( 'opacity', '1' ).unblock(); $( document.body ).trigger( 'cart_totals_refreshed' ); - }); + } ); }; /** From e3f2cad544ecc3c9043c09be5c51717c933c82e9 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 1 Feb 2019 16:10:14 +0000 Subject: [PATCH 196/401] Remove escape --- includes/wc-template-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 8df4e80f289..b1fa994903a 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -1066,7 +1066,7 @@ if ( ! function_exists( 'woocommerce_template_loop_product_title' ) ) { * Show the product title in the product loop. By default this is an H2. */ function woocommerce_template_loop_product_title() { - echo '

    ' . esc_html( get_the_title() ) . '

    '; + echo '

    ' . get_the_title() . '

    '; // phpcs:ignore } } if ( ! function_exists( 'woocommerce_template_loop_category_title' ) ) { From a2d5d3dff7d0a57a1b35d812235800eb67fb0dcc Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Fri, 1 Feb 2019 11:47:14 -0500 Subject: [PATCH 197/401] Accessibility: Add a label for product rating star icons --- includes/wc-template-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index b1fa994903a..1f8d03caac6 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -3292,7 +3292,7 @@ function wc_get_stock_html( $product ) { * @return string */ function wc_get_rating_html( $rating, $count = 0 ) { - $html = 0 < $rating ? '' : ''; + $html = 0 < $rating ? '' : ''; return apply_filters( 'woocommerce_product_get_rating_html', $html, $rating, $count ); } From 5825a66a304c196fab377eaa02256e2262b465bf Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 1 Feb 2019 16:55:51 +0000 Subject: [PATCH 198/401] Cleanup method --- includes/wc-template-functions.php | 34 ++++++++++++++---------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index b1fa994903a..45d768b673f 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -16,54 +16,52 @@ defined( 'ABSPATH' ) || exit; function wc_template_redirect() { global $wp_query, $wp; + // When default permalinks are enabled, redirect shop page to post type archive url. if ( ! empty( $_GET['page_id'] ) && '' === get_option( 'permalink_structure' ) && wc_get_page_id( 'shop' ) === absint( $_GET['page_id'] ) && get_post_type_archive_link( 'product' ) ) { // WPCS: input var ok, CSRF ok. - - // When default permalinks are enabled, redirect shop page to post type archive url. wp_safe_redirect( get_post_type_archive_link( 'product' ) ); exit; + } - } elseif ( is_page( wc_get_page_id( 'checkout' ) ) && wc_get_page_id( 'checkout' ) !== wc_get_page_id( 'cart' ) && WC()->cart->is_empty() && empty( $wp->query_vars['order-pay'] ) && ! isset( $wp->query_vars['order-received'] ) && ! is_customize_preview() && apply_filters( 'woocommerce_checkout_redirect_empty_cart', true ) ) { - - // When on the checkout with an empty cart, redirect to cart page. + // When on the checkout with an empty cart, redirect to cart page. + if ( is_page( wc_get_page_id( 'checkout' ) ) && wc_get_page_id( 'checkout' ) !== wc_get_page_id( 'cart' ) && WC()->cart->is_empty() && empty( $wp->query_vars['order-pay'] ) && ! isset( $wp->query_vars['order-received'] ) && ! is_customize_preview() && apply_filters( 'woocommerce_checkout_redirect_empty_cart', true ) ) { wc_add_notice( __( 'Checkout is not available whilst your cart is empty.', 'woocommerce' ), 'notice' ); wp_safe_redirect( wc_get_page_permalink( 'cart' ) ); exit; - } elseif ( isset( $wp->query_vars['customer-logout'] ) && ! empty( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'customer-logout' ) ) { // WPCS: input var ok, CSRF ok. + } - // Logout. + // Logout. + if ( isset( $wp->query_vars['customer-logout'] ) && ! empty( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'customer-logout' ) ) { // WPCS: input var ok, CSRF ok. wp_safe_redirect( str_replace( '&', '&', wp_logout_url( wc_get_page_permalink( 'myaccount' ) ) ) ); exit; + } - } elseif ( isset( $wp->query_vars['customer-logout'] ) && 'true' === $wp->query_vars['customer-logout'] ) { - // Redirect to the correct logout endpoint. + // Redirect to the correct logout endpoint. + if ( isset( $wp->query_vars['customer-logout'] ) && 'true' === $wp->query_vars['customer-logout'] ) { wp_safe_redirect( esc_url_raw( wc_get_account_endpoint_url( 'customer-logout' ) ) ); exit; + } } elseif ( is_search() && is_post_type_archive( 'product' ) && apply_filters( 'woocommerce_redirect_single_search_result', true ) && 1 === absint( $wp_query->found_posts ) ) { - // Redirect to the product page if we have a single product. + // Redirect to the product page if we have a single product. + if ( is_search() && is_post_type_archive( 'product' ) && apply_filters( 'woocommerce_redirect_single_search_result', true ) && 1 === absint( $wp_query->found_posts ) ) { $product = wc_get_product( $wp_query->post ); if ( $product && $product->is_visible() ) { wp_safe_redirect( get_permalink( $product->get_id() ), 302 ); exit; } - } elseif ( is_add_payment_method_page() ) { + } - // Ensure payment gateways are loaded early. - WC()->payment_gateways(); - - } elseif ( is_checkout() ) { - - // Checkout pages handling + // Ensure gateways and shipping methods are loaded early. + if ( is_add_payment_method_page() || is_checkout() ) { // Buffer the checkout page. ob_start(); // Ensure gateways and shipping methods are loaded early. WC()->payment_gateways(); WC()->shipping(); - } } add_action( 'template_redirect', 'wc_template_redirect' ); From a0f47fb429266e1d55fbe5f77db928e478682973 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 1 Feb 2019 16:56:00 +0000 Subject: [PATCH 199/401] 404 on endpoints --- includes/wc-template-functions.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 45d768b673f..55cf1e4a47e 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -42,7 +42,13 @@ function wc_template_redirect() { exit; } - } elseif ( is_search() && is_post_type_archive( 'product' ) && apply_filters( 'woocommerce_redirect_single_search_result', true ) && 1 === absint( $wp_query->found_posts ) ) { + // Trigger 404 if trying to access an endpoint on wrong page. + if ( is_wc_endpoint_url() && ! is_account_page() && ! is_checkout() ) { + $wp_query->set_404(); + status_header( 404 ); + include( get_query_template( '404' ) ); + exit; + } // Redirect to the product page if we have a single product. if ( is_search() && is_post_type_archive( 'product' ) && apply_filters( 'woocommerce_redirect_single_search_result', true ) && 1 === absint( $wp_query->found_posts ) ) { From 7093c652e77e2c95940bf5853e07a00b11dcc0b6 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 1 Feb 2019 17:13:41 +0000 Subject: [PATCH 200/401] Add localisation to new label --- includes/wc-template-functions.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 1f8d03caac6..a085413bbe1 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -3292,7 +3292,14 @@ function wc_get_stock_html( $product ) { * @return string */ function wc_get_rating_html( $rating, $count = 0 ) { - $html = 0 < $rating ? '' : ''; + $html = ''; + + if ( 0 < $rating ) { + /* translators: %s: rating */ + $label = sprintf( __( 'Rated %s out of 5', 'woocommerce' ), $rating ); + $html = ''; + } + return apply_filters( 'woocommerce_product_get_rating_html', $html, $rating, $count ); } From 6ad6e14f3d1b19677f81cc3bca0de750c991a7d4 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 1 Feb 2019 18:12:47 +0000 Subject: [PATCH 201/401] Update dependency eslint to v5.13.0 --- package-lock.json | 409 +++++++++++++++++++++++++++++++++++++++++++--- package.json | 2 +- 2 files changed, 383 insertions(+), 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index e9fffa562ef..f9679966f4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -394,9 +394,9 @@ "dev": true }, "acorn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.5.tgz", - "integrity": "sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.6.tgz", + "integrity": "sha512-5M3G/A4uBSMIlfJ+h9W125vJvPFH/zirISsW5qfxF5YzEvXJCtolLoQvM5yZft0DvMcUrPGKPOlgEu55I6iUtA==", "dev": true }, "acorn-jsx": { @@ -426,6 +426,12 @@ "uri-js": "^4.2.2" } }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, "ansi-escapes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", @@ -610,6 +616,12 @@ "dev": true, "optional": true }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1984,6 +1996,15 @@ "dev": true, "optional": true }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "requires": { + "inherits": "~2.0.0" + } + }, "bluebird": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", @@ -2374,6 +2395,29 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + } + } + }, "clone-regexp": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-1.0.1.tgz", @@ -3057,9 +3101,9 @@ "dev": true }, "eslint": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.12.0.tgz", - "integrity": "sha512-LntwyPxtOHrsJdcSwyQKVtHofPHdv+4+mFwEe91r2V13vqpM8yLr7b1sW+Oo/yheOPkWYsYlYJCkzlFAt8KV7g==", + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.13.0.tgz", + "integrity": "sha512-nqD5WQMisciZC5EHZowejLKQjWGuFS5c70fxqSKlnDME+oz9zmE8KTlX+lHSg+/5wsC/kf9Q9eMkC8qS3oM2fg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -3091,7 +3135,6 @@ "natural-compare": "^1.4.0", "optionator": "^0.8.2", "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", "progress": "^2.0.0", "regexpp": "^2.0.1", "semver": "^5.5.1", @@ -3974,6 +4017,18 @@ "node-pre-gyp": "^0.10.0" } }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -4017,6 +4072,12 @@ "globule": "^1.0.0" } }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", @@ -5165,6 +5226,12 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", + "dev": true + }, "indent-string": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", @@ -5203,26 +5270,32 @@ "dev": true }, "inquirer": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.1.tgz", - "integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz", + "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", "cli-cursor": "^2.1.0", "cli-width": "^2.0.0", - "external-editor": "^3.0.0", + "external-editor": "^3.0.3", "figures": "^2.0.0", - "lodash": "^4.17.10", + "lodash": "^4.17.11", "mute-stream": "0.0.7", "run-async": "^2.2.0", - "rxjs": "^6.1.0", + "rxjs": "^6.4.0", "string-width": "^2.1.0", "strip-ansi": "^5.0.0", "through": "^2.3.6" }, "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -5235,6 +5308,15 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, + "rxjs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", + "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -5284,6 +5366,12 @@ "loose-envify": "^1.0.0" } }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", @@ -6009,6 +6097,15 @@ "integrity": "sha512-bEZlJzXo5V/ApNNa5z375mJC6Nrz4vG43UgcSCrg2OHC+yuB6j0iDSrY7RQ/+PRofFB03wNIIt9iXIVLr4wc7w==", "dev": true }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, "leven": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", @@ -6550,6 +6647,18 @@ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -6557,6 +6666,12 @@ "dev": true, "optional": true }, + "lodash.mergewith": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", + "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", + "dev": true + }, "lodash.unescape": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", @@ -6974,8 +7089,7 @@ "version": "2.12.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", - "dev": true, - "optional": true + "dev": true }, "nanomatch": { "version": "1.2.13", @@ -7040,6 +7154,54 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "dev": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + } + } + }, "node-pre-gyp": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz", @@ -7068,6 +7230,86 @@ "semver": "^5.3.0" } }, + "node-sass": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", + "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", + "dev": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash.assign": "^4.2.0", + "lodash.clonedeep": "^4.3.2", + "lodash.mergewith": "^4.6.0", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.10.0", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, "node-wp-i18n": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/node-wp-i18n/-/node-wp-i18n-1.2.2.tgz", @@ -7188,7 +7430,6 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, - "optional": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -7343,6 +7584,15 @@ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -7354,7 +7604,6 @@ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, - "optional": true, "requires": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" @@ -7614,12 +7863,6 @@ "semver-compare": "^1.0.0" } }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true - }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -8892,12 +9135,24 @@ "uuid": "^3.3.2" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, "requireindex": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", @@ -9039,6 +9294,18 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "dev": true, + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" + } + }, "saucelabs": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", @@ -9054,6 +9321,27 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "dev": true, + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, "selenium-webdriver": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", @@ -9093,8 +9381,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true, - "optional": true + "dev": true }, "set-value": { "version": "2.0.0", @@ -9467,6 +9754,15 @@ } } }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, "string-argv": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.0.2.tgz", @@ -10610,6 +10906,15 @@ "integrity": "sha512-fwkLWH+DimvA4YCy+/nvJd61nWQQ2liO/nF/RjkTpiOGi+zxZzVkhb1mvbHIIW4b/8nDsYI8uTmAlc0nNkRMOw==", "dev": true }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "dev": true, + "requires": { + "glob": "^7.1.2" + } + }, "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", @@ -11035,6 +11340,12 @@ "isexe": "^2.0.0" } }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -11179,6 +11490,12 @@ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "dev": true }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, "yallist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", @@ -11186,6 +11503,44 @@ "dev": true, "optional": true }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "dev": true, + "requires": { + "camelcase": "^3.0.0" + } + } + } + }, "yargs-parser": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", diff --git a/package.json b/package.json index aafe172a9fc..80606e6ff06 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "chromedriver": "2.45.0", "config": "3.0.1", "cross-env": "5.2.0", - "eslint": "5.12.0", + "eslint": "5.13.0", "eslint-config-wpcalypso": "4.0.1", "eslint-plugin-wpcalypso": "4.0.2", "grunt": "1.0.3", From 00610db7fe660b98a4f2763df7dfe0399afdbb95 Mon Sep 17 00:00:00 2001 From: Galen Wright-Watson Date: Fri, 1 Feb 2019 14:45:29 -0800 Subject: [PATCH 202/401] Update: docmented install script's handling of metacharacters in passwords. --- tests/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/README.md b/tests/README.md index feddd0d7560..835ecf37f82 100644 --- a/tests/README.md +++ b/tests/README.md @@ -14,10 +14,17 @@ $ tests/bin/install.sh [db-host] ``` -Sample usage: +The `` will be set as given. Previously, you would have needed to escape certain characters (forward & backward slashes, and ampersand), but install.sh now escapes them when it needs to internally. You may still need to quote strings with backslashes to prevent them from being processed by the shell or other programs. + +Sample usages: $ tests/bin/install.sh woocommerce_tests root root + # The actual password only has a single backslash, but it's escaped + # to prevent the shell and PHP from treating it as a backspace character + $ tests/bin/install.sh woocommerce_tests root 'a\\b/&' + # Previously, the password would have had to be passed as 'a\\\\b\/\&' + **Important**: The `` database will be created if it doesn't exist and all data will be removed during testing. ## Running Tests From 527c7bc03a5c3f5828b3395f61776a2dd237b921 Mon Sep 17 00:00:00 2001 From: MD Sultan Nasir Uddin Date: Sat, 2 Feb 2019 15:54:33 +0600 Subject: [PATCH 203/401] fix wrong variable checking --- includes/class-wc-regenerate-images.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-regenerate-images.php b/includes/class-wc-regenerate-images.php index 4be79d91bf8..c1060445cfd 100644 --- a/includes/class-wc-regenerate-images.php +++ b/includes/class-wc-regenerate-images.php @@ -100,7 +100,7 @@ class WC_Regenerate_Images { public static function add_uncropped_metadata( $meta_data ) { $size_data = wc_get_image_size( 'woocommerce_thumbnail' ); if ( isset( $meta_data['sizes'], $meta_data['sizes']['woocommerce_thumbnail'] ) ) { - $meta_data['sizes']['woocommerce_thumbnail']['uncropped'] = empty( $size_settings['height'] ); + $meta_data['sizes']['woocommerce_thumbnail']['uncropped'] = empty( $size_data['height'] ); } return $meta_data; } From 8f3b9f10d98a523b79edbd35cfaa8105cd40a0f5 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 2 Feb 2019 18:27:00 +0000 Subject: [PATCH 204/401] Update dependency lint-staged to v8.1.3 --- package-lock.json | 365 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 2 +- 2 files changed, 357 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index e9fffa562ef..4e0248e1ba9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -426,6 +426,12 @@ "uri-js": "^4.2.2" } }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, "ansi-escapes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", @@ -610,6 +616,12 @@ "dev": true, "optional": true }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1984,6 +1996,15 @@ "dev": true, "optional": true }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "requires": { + "inherits": "~2.0.0" + } + }, "bluebird": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", @@ -2374,6 +2395,29 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + } + } + }, "clone-regexp": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-1.0.1.tgz", @@ -3974,6 +4018,18 @@ "node-pre-gyp": "^0.10.0" } }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -4017,6 +4073,12 @@ "globule": "^1.0.0" } }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", @@ -5165,6 +5227,12 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", + "dev": true + }, "indent-string": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", @@ -5284,6 +5352,12 @@ "loose-envify": "^1.0.0" } }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", @@ -6009,6 +6083,15 @@ "integrity": "sha512-bEZlJzXo5V/ApNNa5z375mJC6Nrz4vG43UgcSCrg2OHC+yuB6j0iDSrY7RQ/+PRofFB03wNIIt9iXIVLr4wc7w==", "dev": true }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, "leven": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", @@ -6035,9 +6118,9 @@ } }, "lint-staged": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.1.1.tgz", - "integrity": "sha512-6C9tmmCedjDYQMzHydT5mXRtmEgpGUQDoIl+Ser8cfI/n9grsRUsuG2jd1BWqGf62OV+BV+6n/Drt82uYTCgJg==", + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.1.3.tgz", + "integrity": "sha512-6TGkikL1B+6mIOuSNq2TV6oP21IhPMnV8q0cf9oYZ296ArTVNcbFh1l1pfVOHHbBIYLlziWNsQ2q45/ffmJ4AA==", "dev": true, "requires": { "@iamstarkov/listr-update-renderer": "0.4.1", @@ -6550,6 +6633,18 @@ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -6557,6 +6652,12 @@ "dev": true, "optional": true }, + "lodash.mergewith": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", + "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", + "dev": true + }, "lodash.unescape": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", @@ -6974,8 +7075,7 @@ "version": "2.12.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", - "dev": true, - "optional": true + "dev": true }, "nanomatch": { "version": "1.2.13", @@ -7040,6 +7140,54 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "dev": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + } + } + }, "node-pre-gyp": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz", @@ -7068,6 +7216,86 @@ "semver": "^5.3.0" } }, + "node-sass": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", + "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", + "dev": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash.assign": "^4.2.0", + "lodash.clonedeep": "^4.3.2", + "lodash.mergewith": "^4.6.0", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.10.0", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, "node-wp-i18n": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/node-wp-i18n/-/node-wp-i18n-1.2.2.tgz", @@ -7188,7 +7416,6 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, - "optional": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -7343,6 +7570,15 @@ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -7354,7 +7590,6 @@ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, - "optional": true, "requires": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" @@ -8892,12 +9127,24 @@ "uuid": "^3.3.2" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, "requireindex": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", @@ -9039,6 +9286,18 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "dev": true, + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" + } + }, "saucelabs": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", @@ -9054,6 +9313,27 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "dev": true, + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, "selenium-webdriver": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", @@ -9093,8 +9373,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true, - "optional": true + "dev": true }, "set-value": { "version": "2.0.0", @@ -9467,6 +9746,15 @@ } } }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, "string-argv": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.0.2.tgz", @@ -10610,6 +10898,15 @@ "integrity": "sha512-fwkLWH+DimvA4YCy+/nvJd61nWQQ2liO/nF/RjkTpiOGi+zxZzVkhb1mvbHIIW4b/8nDsYI8uTmAlc0nNkRMOw==", "dev": true }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "dev": true, + "requires": { + "glob": "^7.1.2" + } + }, "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", @@ -11035,6 +11332,12 @@ "isexe": "^2.0.0" } }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -11179,6 +11482,12 @@ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "dev": true }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, "yallist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", @@ -11186,6 +11495,44 @@ "dev": true, "optional": true }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "dev": true, + "requires": { + "camelcase": "^3.0.0" + } + } + } + }, "yargs-parser": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", diff --git a/package.json b/package.json index aafe172a9fc..733f5ba6971 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "grunt-wp-i18n": "1.0.3", "husky": "1.3.1", "istanbul": "1.0.0-alpha.2", - "lint-staged": "8.1.1", + "lint-staged": "8.1.3", "mocha": "5.2.0", "node-sass": "4.11.0", "prettier": "github:automattic/calypso-prettier#c56b4251", From e375ffd6e7b5c0abb9ff7e542987f95f8564018d Mon Sep 17 00:00:00 2001 From: nishitlangaliya Date: Mon, 4 Feb 2019 12:02:59 +0530 Subject: [PATCH 205/401] fix:No alert for mis-matched password reset --- includes/wc-template-hooks.php | 1 + templates/myaccount/form-reset-password.php | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/includes/wc-template-hooks.php b/includes/wc-template-hooks.php index 201334b1db9..26a0139922d 100644 --- a/includes/wc-template-hooks.php +++ b/includes/wc-template-hooks.php @@ -303,3 +303,4 @@ add_action( 'woocommerce_account_content', 'woocommerce_output_all_notices', 5 ) add_action( 'woocommerce_before_customer_login_form', 'woocommerce_output_all_notices', 10 ); add_action( 'woocommerce_before_lost_password_form', 'woocommerce_output_all_notices', 10 ); add_action( 'before_woocommerce_pay', 'woocommerce_output_all_notices', 10 ); +add_action( 'woocommerce_before_reset_password_form', 'woocommerce_output_all_notices', 10 ); diff --git a/templates/myaccount/form-reset-password.php b/templates/myaccount/form-reset-password.php index 60c90e6c4c7..e431b152361 100644 --- a/templates/myaccount/form-reset-password.php +++ b/templates/myaccount/form-reset-password.php @@ -12,11 +12,12 @@ * * @see https://docs.woocommerce.com/document/template-structure/ * @package WooCommerce/Templates - * @version 3.5.0 + * @version 3.6.0 */ defined( 'ABSPATH' ) || exit; +do_action( 'woocommerce_before_reset_password_form' ); ?> @@ -47,3 +48,6 @@ defined( 'ABSPATH' ) || exit; + Date: Mon, 4 Feb 2019 13:14:30 +0530 Subject: [PATCH 206/401] fix: reverted bump version to 3.5.5 --- templates/myaccount/form-reset-password.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/myaccount/form-reset-password.php b/templates/myaccount/form-reset-password.php index e431b152361..b97991e4038 100644 --- a/templates/myaccount/form-reset-password.php +++ b/templates/myaccount/form-reset-password.php @@ -12,7 +12,7 @@ * * @see https://docs.woocommerce.com/document/template-structure/ * @package WooCommerce/Templates - * @version 3.6.0 + * @version 3.5.5 */ defined( 'ABSPATH' ) || exit; From 527840748b903741c734181604a455f7e84781b6 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Feb 2019 11:24:26 +0000 Subject: [PATCH 207/401] #22410 --- Gruntfile.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 23ae9bc76ab..70c7bbf43ee 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -23,9 +23,7 @@ module.exports = function( grunt ) { '<%= dirs.js %>/admin/*.js', '!<%= dirs.js %>/admin/*.min.js', '<%= dirs.js %>/frontend/*.js', - '!<%= dirs.js %>/frontend/*.min.js', - 'includes/gateways/simplify-commerce/assets/js/*.js', - '!includes/gateways/simplify-commerce/assets/js/*.min.js' + '!<%= dirs.js %>/frontend/*.min.js' ] }, From 25a92b1d929cbe7457a4c2afe5fb90c166e50b9e Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Feb 2019 12:51:20 +0000 Subject: [PATCH 208/401] Feedback --- assets/js/frontend/add-to-cart-variation.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/assets/js/frontend/add-to-cart-variation.js b/assets/js/frontend/add-to-cart-variation.js index 919ca8d565f..5b1dcdec277 100644 --- a/assets/js/frontend/add-to-cart-variation.js +++ b/assets/js/frontend/add-to-cart-variation.js @@ -380,11 +380,9 @@ // Check if current selection is in attached options. if ( selected_attr_val ) { - if ( 0 === attached_options_count ) { - selected_attr_val_valid = false; - } else { - selected_attr_val_valid = false; + selected_attr_val_valid = false; + if ( 0 !== attached_options_count ) { new_attr_select.find( 'option.attached.enabled' ).each( function( index, el ) { var option_value = $( this ).val(); From 1928f34699d61bd454929375aa381e9441b62b79 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Feb 2019 13:18:09 +0000 Subject: [PATCH 209/401] Add notice hash to store notice cookie Closes #21963 --- assets/js/frontend/woocommerce.js | 15 +++++++++------ includes/wc-template-functions.php | 4 +++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/assets/js/frontend/woocommerce.js b/assets/js/frontend/woocommerce.js index 3183d98e08a..693ece80893 100644 --- a/assets/js/frontend/woocommerce.js +++ b/assets/js/frontend/woocommerce.js @@ -14,19 +14,22 @@ jQuery( function( $ ) { } }); - // Set a cookie and hide the store notice when the dismiss button is clicked - $( '.woocommerce-store-notice__dismiss-link' ).click( function() { - Cookies.set( 'store_notice', 'hidden', { path: '/' } ); - $( '.woocommerce-store-notice' ).hide(); - }); + var noticeID = $( '.woocommerce-store-notice' ).data( 'notice-id' ) || '', + cookieName = 'store_notice' + noticeID; // Check the value of that cookie and show/hide the notice accordingly - if ( 'hidden' === Cookies.get( 'store_notice' ) ) { + if ( 'hidden' === Cookies.get( cookieName ) ) { $( '.woocommerce-store-notice' ).hide(); } else { $( '.woocommerce-store-notice' ).show(); } + // Set a cookie and hide the store notice when the dismiss button is clicked + $( '.woocommerce-store-notice__dismiss-link' ).click( function() { + Cookies.set( cookieName, 'hidden', { path: '/' } ); + $( '.woocommerce-store-notice' ).hide(); + }); + // Make form field descriptions toggle on focus. $( document.body ).on( 'click', function() { $( '.woocommerce-input-wrapper span.description:visible' ).prop( 'aria-hidden', true ).slideUp( 250 ); diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 07700eec66f..1d47c59bd2e 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -963,7 +963,9 @@ if ( ! function_exists( 'woocommerce_demo_store' ) ) { $notice = __( 'This is a demo store for testing purposes — no orders shall be fulfilled.', 'woocommerce' ); } - echo apply_filters( 'woocommerce_demo_store', '

    ' . wp_kses_post( $notice ) . ' ' . esc_html__( 'Dismiss', 'woocommerce' ) . '

    ', $notice ); // WPCS: XSS ok. + $notice_id = md5( $notice ); + + echo apply_filters( 'woocommerce_demo_store', '', $notice ); // WPCS: XSS ok. } } From 600510ea6f9b9f0f028aceae47fbfe86f1b434fa Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Feb 2019 13:56:35 +0000 Subject: [PATCH 210/401] Using wrong post type name in comparison --- includes/wc-stock-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-stock-functions.php b/includes/wc-stock-functions.php index 0e1eb39dbcb..344fa0f7d73 100644 --- a/includes/wc-stock-functions.php +++ b/includes/wc-stock-functions.php @@ -310,7 +310,7 @@ function wc_get_held_stock_quantity( $product, $exclude_order_id = 0 ) { AND posts.post_type IN ( '" . implode( "','", wc_get_order_types() ) . "' ) AND posts.post_status = 'wc-pending' AND posts.ID != %d;", - 'variation' === get_post_type( $product->get_stock_managed_by_id() ) ? '_variation_id' : '_product_id', + 'product_variation' === get_post_type( $product->get_stock_managed_by_id() ) ? '_variation_id' : '_product_id', $product->get_stock_managed_by_id(), $exclude_order_id ) From b5eecbf6a7113ba0ec778e190cf83237b399929f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Feb 2019 14:17:33 +0000 Subject: [PATCH 211/401] Add note to reg form --- templates/myaccount/form-login.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/templates/myaccount/form-login.php b/templates/myaccount/form-login.php index 51f7a8cec24..560001ffef0 100644 --- a/templates/myaccount/form-login.php +++ b/templates/myaccount/form-login.php @@ -94,6 +94,10 @@ do_action( 'woocommerce_before_customer_login_form' ); ?>

    + + +

    + From 4164305b3c5152c45d40bec3d74196533652d6fd Mon Sep 17 00:00:00 2001 From: Paul Dechov Date: Mon, 4 Feb 2019 09:58:39 -0500 Subject: [PATCH 212/401] OBW: Offer Storefront when WP 5.0 default theme is active Because the OBW hadn't been updated to consider Twenty Nineteen a default theme, Storefront was omitted from the 'Recommended' step on the basis of Twenty Nineteen's WooCommerce support. --- includes/admin/class-wc-admin-setup-wizard.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/admin/class-wc-admin-setup-wizard.php b/includes/admin/class-wc-admin-setup-wizard.php index 5585c86433a..3a0cc78055d 100644 --- a/includes/admin/class-wc-admin-setup-wizard.php +++ b/includes/admin/class-wc-admin-setup-wizard.php @@ -91,6 +91,7 @@ class WC_Admin_Setup_Wizard { protected function is_default_theme() { return wc_is_active_theme( array( + 'twentynineteen', 'twentyseventeen', 'twentysixteen', 'twentyfifteen', From 5b3b285a9d6c2e409bc16116d51c1df369aa1747 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Feb 2019 15:31:35 +0000 Subject: [PATCH 213/401] phpcs --- includes/class-wc-form-handler.php | 296 +++++++++++++++-------------- 1 file changed, 150 insertions(+), 146 deletions(-) diff --git a/includes/class-wc-form-handler.php b/includes/class-wc-form-handler.php index a98bc132a07..df0a5381c05 100644 --- a/includes/class-wc-form-handler.php +++ b/includes/class-wc-form-handler.php @@ -2,8 +2,7 @@ /** * Handle frontend forms. * - * @version 2.2.0 - * @package WooCommerce/Classes/ + * @package WooCommerce/Classes/ */ defined( 'ABSPATH' ) || exit; @@ -40,17 +39,17 @@ class WC_Form_Handler { * Remove key and user ID (or user login, as a fallback) from query string, set cookie, and redirect to account page to show the form. */ public static function redirect_reset_password_link() { - if ( is_account_page() && isset( $_GET['key'] ) && ( isset( $_GET['id'] ) || isset( $_GET['login'] ) ) ) { + if ( is_account_page() && isset( $_GET['key'] ) && ( isset( $_GET['id'] ) || isset( $_GET['login'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification // If available, get $user_id from query string parameter for fallback purposes. - if ( isset( $_GET['login'] ) ) { - $user = get_user_by( 'login', sanitize_user( wp_unslash( $_GET['login'] ) ) ); + if ( isset( $_GET['login'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification + $user = get_user_by( 'login', sanitize_user( wp_unslash( $_GET['login'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification $user_id = $user ? $user->ID : 0; } else { $user_id = absint( $_GET['id'] ); } - $value = sprintf( '%d:%s', $user_id, wp_unslash( $_GET['key'] ) ); + $value = sprintf( '%d:%s', $user_id, wp_unslash( $_GET['key'] ) ); // phpcs:ignore WC_Shortcode_My_Account::set_reset_password_cookie( $value ); wp_safe_redirect( add_query_arg( 'show-reset-form', 'true', wc_lostpassword_url() ) ); exit; @@ -64,7 +63,9 @@ class WC_Form_Handler { public static function save_address() { global $wp; - if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) { + $nonce_value = wc_get_var( $_REQUEST['woocommerce-edit-address-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine. + + if ( ! wp_verify_nonce( $nonce_value, 'woocommerce-edit_address' ) ) { return; } @@ -74,12 +75,6 @@ class WC_Form_Handler { wc_nocache_headers(); - $nonce_value = wc_get_var( $_REQUEST['woocommerce-edit-address-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine. - - if ( ! wp_verify_nonce( $nonce_value, 'woocommerce-edit_address' ) ) { - return; - } - $user_id = get_current_user_id(); if ( $user_id <= 0 ) { @@ -88,56 +83,59 @@ class WC_Form_Handler { $load_address = isset( $wp->query_vars['edit-address'] ) ? wc_edit_address_i18n( sanitize_title( $wp->query_vars['edit-address'] ), true ) : 'billing'; - $address = WC()->countries->get_address_fields( esc_attr( $_POST[ $load_address . '_country' ] ), $load_address . '_' ); + if ( ! isset( $_POST[ $load_address . '_country' ] ) ) { + return; + } + + $address = WC()->countries->get_address_fields( wc_clean( wp_unslash( $_POST[ $load_address . '_country' ] ) ), $load_address . '_' ); foreach ( $address as $key => $field ) { - if ( ! isset( $field['type'] ) ) { $field['type'] = 'text'; } // Get Value. - switch ( $field['type'] ) { - case 'checkbox' : - $_POST[ $key ] = (int) isset( $_POST[ $key ] ); - break; - default : - $_POST[ $key ] = isset( $_POST[ $key ] ) ? wc_clean( $_POST[ $key ] ) : ''; - break; + if ( 'checkbox' === $field['type'] ) { + $value = (int) isset( $_POST[ $key ] ); + } else { + $value = isset( $_POST[ $key ] ) ? wc_clean( wp_unslash( $_POST[ $key ] ) ) : ''; } // Hook to allow modification of value. - $_POST[ $key ] = apply_filters( 'woocommerce_process_myaccount_field_' . $key, $_POST[ $key ] ); + $value = apply_filters( 'woocommerce_process_myaccount_field_' . $key, $value ); // Validation: Required fields. - if ( ! empty( $field['required'] ) && empty( $_POST[ $key ] ) ) { + if ( ! empty( $field['required'] ) && empty( $value ) ) { + /* translators: %s: Field name. */ wc_add_notice( sprintf( __( '%s is a required field.', 'woocommerce' ), $field['label'] ), 'error' ); } - if ( ! empty( $_POST[ $key ] ) ) { + if ( ! empty( $value ) ) { // Validation rules. if ( ! empty( $field['validate'] ) && is_array( $field['validate'] ) ) { foreach ( $field['validate'] as $rule ) { switch ( $rule ) { - case 'postcode' : - $_POST[ $key ] = strtoupper( str_replace( ' ', '', $_POST[ $key ] ) ); + case 'postcode': + $value = strtoupper( str_replace( ' ', '', $value ) ); - if ( ! WC_Validation::is_postcode( $_POST[ $key ], $_POST[ $load_address . '_country' ] ) ) { + if ( ! WC_Validation::is_postcode( $value, wc_clean( wp_unslash( $_POST[ $load_address . '_country' ] ) ) ) ) { wc_add_notice( __( 'Please enter a valid postcode / ZIP.', 'woocommerce' ), 'error' ); } else { - $_POST[ $key ] = wc_format_postcode( $_POST[ $key ], $_POST[ $load_address . '_country' ] ); + $value = wc_format_postcode( $value, wc_clean( wp_unslash( $_POST[ $load_address . '_country' ] ) ) ); } break; - case 'phone' : - if ( ! WC_Validation::is_phone( $_POST[ $key ] ) ) { + case 'phone': + if ( ! WC_Validation::is_phone( $value ) ) { + /* translators: %s: Phone number. */ wc_add_notice( sprintf( __( '%s is not a valid phone number.', 'woocommerce' ), '' . $field['label'] . '' ), 'error' ); } break; - case 'email' : - $_POST[ $key ] = strtolower( $_POST[ $key ] ); + case 'email': + $value = strtolower( $value ); - if ( ! is_email( $_POST[ $key ] ) ) { + if ( ! is_email( $value ) ) { + /* translators: %s: Email address. */ wc_add_notice( sprintf( __( '%s is not a valid email address.', 'woocommerce' ), '' . $field['label'] . '' ), 'error' ); } break; @@ -156,13 +154,13 @@ class WC_Form_Handler { if ( $customer ) { foreach ( $address as $key => $field ) { if ( is_callable( array( $customer, "set_$key" ) ) ) { - $customer->{"set_$key"}( wc_clean( $_POST[ $key ] ) ); + $customer->{"set_$key"}( $value ); } else { - $customer->update_meta_data( $key, wc_clean( $_POST[ $key ] ) ); + $customer->update_meta_data( $key, $value ); } if ( WC()->customer && is_callable( array( WC()->customer, "set_$key" ) ) ) { - WC()->customer->{"set_$key"}( wc_clean( $_POST[ $key ] ) ); + WC()->customer->{"set_$key"}( $value ); } } $customer->save(); @@ -181,7 +179,9 @@ class WC_Form_Handler { * Save the password/account details and redirect back to the my account page. */ public static function save_account_details() { - if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) { + $nonce_value = wc_get_var( $_REQUEST['save-account-details-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine. + + if ( ! wp_verify_nonce( $nonce_value, 'save_account_details' ) ) { return; } @@ -191,25 +191,19 @@ class WC_Form_Handler { wc_nocache_headers(); - $nonce_value = wc_get_var( $_REQUEST['save-account-details-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine. - - if ( ! wp_verify_nonce( $nonce_value, 'save_account_details' ) ) { - return; - } - $user_id = get_current_user_id(); if ( $user_id <= 0 ) { return; } - $account_first_name = ! empty( $_POST['account_first_name'] ) ? wc_clean( $_POST['account_first_name'] ): ''; - $account_last_name = ! empty( $_POST['account_last_name'] ) ? wc_clean( $_POST['account_last_name'] ) : ''; - $account_display_name = ! empty( $_POST['account_display_name'] ) ? wc_clean( $_POST['account_display_name'] ) : ''; - $account_email = ! empty( $_POST['account_email'] ) ? wc_clean( $_POST['account_email'] ) : ''; - $pass_cur = ! empty( $_POST['password_current'] ) ? $_POST['password_current'] : ''; - $pass1 = ! empty( $_POST['password_1'] ) ? $_POST['password_1'] : ''; - $pass2 = ! empty( $_POST['password_2'] ) ? $_POST['password_2'] : ''; + $account_first_name = ! empty( $_POST['account_first_name'] ) ? wc_clean( wp_unslash( $_POST['account_first_name'] ) ) : ''; + $account_last_name = ! empty( $_POST['account_last_name'] ) ? wc_clean( wp_unslash( $_POST['account_last_name'] ) ) : ''; + $account_display_name = ! empty( $_POST['account_display_name'] ) ? wc_clean( wp_unslash( $_POST['account_display_name'] ) ) : ''; + $account_email = ! empty( $_POST['account_email'] ) ? wc_clean( wp_unslash( $_POST['account_email'] ) ) : ''; + $pass_cur = ! empty( $_POST['password_current'] ) ? wp_unslash( $_POST['password_current'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $pass1 = ! empty( $_POST['password_1'] ) ? wp_unslash( $_POST['password_1'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $pass2 = ! empty( $_POST['password_2'] ) ? wp_unslash( $_POST['password_2'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $save_pass = true; // Current user data. @@ -219,11 +213,11 @@ class WC_Form_Handler { $current_email = $current_user->user_email; // New user data. - $user = new stdClass(); - $user->ID = $user_id; - $user->first_name = $account_first_name; - $user->last_name = $account_last_name; - $user->display_name = $account_display_name; + $user = new stdClass(); + $user->ID = $user_id; + $user->first_name = $account_first_name; + $user->last_name = $account_last_name; + $user->display_name = $account_display_name; // Prevent display name to be changed to email. if ( is_email( $account_display_name ) ) { @@ -231,15 +225,19 @@ class WC_Form_Handler { } // Handle required fields. - $required_fields = apply_filters( 'woocommerce_save_account_details_required_fields', array( - 'account_first_name' => __( 'First name', 'woocommerce' ), - 'account_last_name' => __( 'Last name', 'woocommerce' ), - 'account_display_name' => __( 'Display name', 'woocommerce' ), - 'account_email' => __( 'Email address', 'woocommerce' ), - ) ); + $required_fields = apply_filters( + 'woocommerce_save_account_details_required_fields', + array( + 'account_first_name' => __( 'First name', 'woocommerce' ), + 'account_last_name' => __( 'Last name', 'woocommerce' ), + 'account_display_name' => __( 'Display name', 'woocommerce' ), + 'account_email' => __( 'Email address', 'woocommerce' ), + ) + ); foreach ( $required_fields as $field_key => $field_name ) { if ( empty( $_POST[ $field_key ] ) ) { + /* translators: %s: Field name. */ wc_add_notice( sprintf( __( '%s is a required field.', 'woocommerce' ), '' . esc_html( $field_name ) . '' ), 'error' ); } } @@ -321,11 +319,11 @@ class WC_Form_Handler { * Process the checkout form. */ public static function checkout_action() { - if ( isset( $_POST['woocommerce_checkout_place_order'] ) || isset( $_POST['woocommerce_checkout_update_totals'] ) ) { + if ( isset( $_POST['woocommerce_checkout_place_order'] ) || isset( $_POST['woocommerce_checkout_update_totals'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification wc_nocache_headers(); if ( WC()->cart->is_empty() ) { - wp_redirect( wc_get_page_permalink( 'cart' ) ); + wp_safe_redirect( wc_get_page_permalink( 'cart' ) ); exit; } @@ -341,7 +339,7 @@ class WC_Form_Handler { public static function pay_action() { global $wp; - if ( isset( $_POST['woocommerce_pay'] ) ) { + if ( isset( $_POST['woocommerce_pay'], $_GET['key'] ) ) { wc_nocache_headers(); $nonce_value = wc_get_var( $_REQUEST['woocommerce-pay-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine. @@ -352,32 +350,33 @@ class WC_Form_Handler { ob_start(); - // Pay for existing order - $order_key = $_GET['key']; - $order_id = absint( $wp->query_vars['order-pay'] ); - $order = wc_get_order( $order_id ); + // Pay for existing order. + $order_key = wp_unslash( $_GET['key'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $order_id = absint( $wp->query_vars['order-pay'] ); + $order = wc_get_order( $order_id ); if ( $order_id === $order->get_id() && hash_equals( $order->get_order_key(), $order_key ) && $order->needs_payment() ) { do_action( 'woocommerce_before_pay_action', $order ); - WC()->customer->set_props( array( - 'billing_country' => $order->get_billing_country() ? $order->get_billing_country() : null, - 'billing_state' => $order->get_billing_state() ? $order->get_billing_state() : null, - 'billing_postcode' => $order->get_billing_postcode() ? $order->get_billing_postcode() : null, - 'billing_city' => $order->get_billing_city() ? $order->get_billing_city() : null, - ) ); + WC()->customer->set_props( + array( + 'billing_country' => $order->get_billing_country() ? $order->get_billing_country() : null, + 'billing_state' => $order->get_billing_state() ? $order->get_billing_state() : null, + 'billing_postcode' => $order->get_billing_postcode() ? $order->get_billing_postcode() : null, + 'billing_city' => $order->get_billing_city() ? $order->get_billing_city() : null, + ) + ); WC()->customer->save(); - // Terms if ( ! empty( $_POST['terms-field'] ) && empty( $_POST['terms'] ) ) { wc_add_notice( __( 'Please read and accept the terms and conditions to proceed with your order.', 'woocommerce' ), 'error' ); return; } - // Update payment method + // Update payment method. if ( $order->needs_payment() ) { - $payment_method = isset( $_POST['payment_method'] ) ? wc_clean( $_POST['payment_method'] ) : false; + $payment_method = isset( $_POST['payment_method'] ) ? wc_clean( wp_unslash( $_POST['payment_method'] ) ) : false; $available_gateways = WC()->payment_gateways->get_available_payment_gateways(); if ( ! $payment_method ) { @@ -385,7 +384,6 @@ class WC_Form_Handler { return; } - // Update meta update_post_meta( $order_id, '_payment_method', $payment_method ); if ( isset( $available_gateways[ $payment_method ] ) ) { @@ -396,22 +394,20 @@ class WC_Form_Handler { update_post_meta( $order_id, '_payment_method_title', $payment_method_title ); - // Validate $available_gateways[ $payment_method ]->validate_fields(); - // Process if ( 0 === wc_notice_count( 'error' ) ) { $result = $available_gateways[ $payment_method ]->process_payment( $order_id ); - // Redirect to success/confirmation/payment page + // Redirect to success/confirmation/payment page. if ( 'success' === $result['result'] ) { - wp_redirect( $result['redirect'] ); + wp_redirect( $result['redirect'] ); //phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect exit; } } } else { - // No payment was required for order + // No payment was required for order. $order->payment_complete(); wp_safe_redirect( $order->get_checkout_order_received_url() ); exit; @@ -466,7 +462,7 @@ class WC_Form_Handler { } if ( ! empty( $result['redirect'] ) ) { - wp_redirect( $result['redirect'] ); + wp_redirect( $result['redirect'] ); //phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect exit(); } } @@ -485,14 +481,14 @@ class WC_Form_Handler { $token_id = absint( $wp->query_vars['delete-payment-method'] ); $token = WC_Payment_Tokens::get( $token_id ); - if ( is_null( $token ) || get_current_user_id() !== $token->get_user_id() || false === wp_verify_nonce( $_REQUEST['_wpnonce'], 'delete-payment-method-' . $token_id ) ) { + if ( is_null( $token ) || get_current_user_id() !== $token->get_user_id() || ! isset( $_REQUEST['_wpnonce'] ) || false === wp_verify_nonce( wp_unslash( $_REQUEST['_wpnonce'] ), 'delete-payment-method-' . $token_id ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized wc_add_notice( __( 'Invalid payment method.', 'woocommerce' ), 'error' ); } else { WC_Payment_Tokens::delete( $token_id ); wc_add_notice( __( 'Payment method deleted.', 'woocommerce' ) ); } - wp_redirect( wc_get_account_endpoint_url( 'payment-methods' ) ); + wp_safe_redirect( wc_get_account_endpoint_url( 'payment-methods' ) ); exit(); } @@ -510,14 +506,14 @@ class WC_Form_Handler { $token_id = absint( $wp->query_vars['set-default-payment-method'] ); $token = WC_Payment_Tokens::get( $token_id ); - if ( is_null( $token ) || get_current_user_id() !== $token->get_user_id() || false === wp_verify_nonce( $_REQUEST['_wpnonce'], 'set-default-payment-method-' . $token_id ) ) { + if ( is_null( $token ) || get_current_user_id() !== $token->get_user_id() || ! isset( $_REQUEST['_wpnonce'] ) || false === wp_verify_nonce( wp_unslash( $_REQUEST['_wpnonce'] ), 'set-default-payment-method-' . $token_id ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized wc_add_notice( __( 'Invalid payment method.', 'woocommerce' ), 'error' ); } else { WC_Payment_Tokens::set_users_default( $token->get_user_id(), intval( $token_id ) ); wc_add_notice( __( 'This payment method was successfully set as your default.', 'woocommerce' ) ); } - wp_redirect( wc_get_account_endpoint_url( 'payment-methods' ) ); + wp_safe_redirect( wc_get_account_endpoint_url( 'payment-methods' ) ); exit(); } @@ -536,10 +532,10 @@ class WC_Form_Handler { $nonce_value = wc_get_var( $_REQUEST['woocommerce-cart-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine. if ( ! empty( $_POST['apply_coupon'] ) && ! empty( $_POST['coupon_code'] ) ) { - WC()->cart->add_discount( sanitize_text_field( wp_unslash( $_POST['coupon_code'] ) ) ); + WC()->cart->add_discount( sanitize_text_field( wp_unslash( $_POST['coupon_code'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification } elseif ( isset( $_GET['remove_coupon'] ) ) { - WC()->cart->remove_coupon( wc_clean( wp_unslash( $_GET['remove_coupon'] ) ) ); + WC()->cart->remove_coupon( wc_clean( wp_unslash( $_GET['remove_coupon'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification } elseif ( ! empty( $_GET['remove_item'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-cart' ) ) { $cart_item_key = sanitize_text_field( wp_unslash( $_GET['remove_item'] ) ); @@ -550,6 +546,7 @@ class WC_Form_Handler { $product = wc_get_product( $cart_item['product_id'] ); + /* translators: %s: Item name. */ $item_removed_title = apply_filters( 'woocommerce_cart_item_removed_title', $product ? sprintf( _x( '“%s”', 'Item name in quotes', 'woocommerce' ), $product->get_name() ) : __( 'Item', 'woocommerce' ), $cart_item ); // Don't show undo link if removed item is out of stock. @@ -565,7 +562,7 @@ class WC_Form_Handler { wc_add_notice( $removed_notice ); } - $referer = wp_get_referer() ? remove_query_arg( array( 'remove_item', 'add-to-cart', 'added-to-cart', 'order_again', '_wpnonce' ), add_query_arg( 'removed_item', '1', wp_get_referer() ) ) : wc_get_cart_url(); + $referer = wp_get_referer() ? remove_query_arg( array( 'remove_item', 'add-to-cart', 'added-to-cart', 'order_again', '_wpnonce' ), add_query_arg( 'removed_item', '1', wp_get_referer() ) ) : wc_get_cart_url(); wp_safe_redirect( $referer ); exit; @@ -576,7 +573,7 @@ class WC_Form_Handler { WC()->cart->restore_cart_item( $cart_item_key ); - $referer = wp_get_referer() ? remove_query_arg( array( 'undo_item', '_wpnonce' ), wp_get_referer() ) : wc_get_cart_url(); + $referer = wp_get_referer() ? remove_query_arg( array( 'undo_item', '_wpnonce' ), wp_get_referer() ) : wc_get_cart_url(); wp_safe_redirect( $referer ); exit; @@ -658,26 +655,23 @@ class WC_Form_Handler { isset( $_GET['cancel_order'] ) && isset( $_GET['order'] ) && isset( $_GET['order_id'] ) && - ( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'], 'woocommerce-cancel_order' ) ) + ( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( wp_unslash( $_GET['_wpnonce'] ), 'woocommerce-cancel_order' ) ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized ) { wc_nocache_headers(); - $order_key = $_GET['order']; + $order_key = wp_unslash( $_GET['order'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $order_id = absint( $_GET['order_id'] ); $order = wc_get_order( $order_id ); $user_can_cancel = current_user_can( 'cancel_order', $order_id ); $order_can_cancel = $order->has_status( apply_filters( 'woocommerce_valid_order_statuses_for_cancel', array( 'pending', 'failed' ) ) ); - $redirect = $_GET['redirect']; + $redirect = isset( $_GET['redirect'] ) ? wp_unslash( $_GET['redirect'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - if ( $order->has_status( 'cancelled' ) ) { - // Already cancelled - take no action - } elseif ( $user_can_cancel && $order_can_cancel && $order->get_id() === $order_id && hash_equals( $order->get_order_key(), $order_key ) ) { + if ( $user_can_cancel && $order_can_cancel && $order->get_id() === $order_id && hash_equals( $order->get_order_key(), $order_key ) ) { - // Cancel the order + restore stock + // Cancel the order + restore stock. WC()->session->set( 'order_awaiting_payment', false ); $order->update_status( 'cancelled', __( 'Order cancelled by customer.', 'woocommerce' ) ); - // Message wc_add_notice( apply_filters( 'woocommerce_order_cancelled_notice', __( 'Your order was cancelled.', 'woocommerce' ) ), apply_filters( 'woocommerce_order_cancelled_notice_type', 'notice' ) ); do_action( 'woocommerce_cancelled_order', $order->get_id() ); @@ -700,18 +694,18 @@ class WC_Form_Handler { * * Checks for a valid request, does validation (via hooks) and then redirects if valid. * - * @param bool $url (default: false) + * @param bool $url (default: false) URL to redirect to. */ public static function add_to_cart_action( $url = false ) { - if ( empty( $_REQUEST['add-to-cart'] ) || ! is_numeric( $_REQUEST['add-to-cart'] ) ) { + if ( ! isset( $_REQUEST['add-to-cart'] ) || ! is_numeric( wp_unslash( $_REQUEST['add-to-cart'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized return; } wc_nocache_headers(); - $product_id = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $_REQUEST['add-to-cart'] ) ); - $was_added_to_cart = false; - $adding_to_cart = wc_get_product( $product_id ); + $product_id = apply_filters( 'woocommerce_add_to_cart_product_id', absint( wp_unslash( $_REQUEST['add-to-cart'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification + $was_added_to_cart = false; + $adding_to_cart = wc_get_product( $product_id ); if ( ! $adding_to_cart ) { return; @@ -731,7 +725,9 @@ class WC_Form_Handler { // If we added the product to the cart we can now optionally do a redirect. if ( $was_added_to_cart && 0 === wc_notice_count( 'error' ) ) { - if ( $url = apply_filters( 'woocommerce_add_to_cart_redirect', $url, $adding_to_cart ) ) { + $url = apply_filters( 'woocommerce_add_to_cart_redirect', $url, $adding_to_cart ); + + if ( $url ) { wp_safe_redirect( $url ); exit; } elseif ( 'yes' === get_option( 'woocommerce_cart_redirect_after_add' ) ) { @@ -749,8 +745,8 @@ class WC_Form_Handler { * @return bool success or not */ private static function add_to_cart_handler_simple( $product_id ) { - $quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( $_REQUEST['quantity'] ); - $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity ); + $quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification + $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity ); if ( $passed_validation && false !== WC()->cart->add_to_cart( $product_id, $quantity ) ) { wc_add_to_cart_message( array( $product_id => $quantity ), true ); @@ -769,24 +765,25 @@ class WC_Form_Handler { private static function add_to_cart_handler_grouped( $product_id ) { $was_added_to_cart = false; $added_to_cart = array(); + $items = isset( $_REQUEST['quantity'] ) && is_array( $_REQUEST['quantity'] ) ? wp_unslash( $_REQUEST['quantity'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - if ( ! empty( $_REQUEST['quantity'] ) && is_array( $_REQUEST['quantity'] ) ) { + if ( ! empty( $items ) ) { $quantity_set = false; - foreach ( $_REQUEST['quantity'] as $item => $quantity ) { + foreach ( $items as $item => $quantity ) { if ( $quantity <= 0 ) { continue; } $quantity_set = true; - // Add to cart validation - $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $item, $quantity ); + // Add to cart validation. + $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $item, $quantity ); // Suppress total recalculation until finished. remove_action( 'woocommerce_add_to_cart', array( WC()->cart, 'calculate_totals' ), 20, 0 ); if ( $passed_validation && false !== WC()->cart->add_to_cart( $item, $quantity ) ) { - $was_added_to_cart = true; + $was_added_to_cart = true; $added_to_cart[ $item ] = $quantity; } @@ -811,13 +808,14 @@ class WC_Form_Handler { * Handle adding variable products to the cart. * * @since 2.4.6 Split from add_to_cart_action. + * @throws Exception If add to cart fails. * @param int $product_id Product ID to add to the cart. * @return bool success or not */ private static function add_to_cart_handler_variable( $product_id ) { try { - $variation_id = empty( $_REQUEST['variation_id'] ) ? '' : absint( wp_unslash( $_REQUEST['variation_id'] ) ); - $quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // WPCS: sanitization ok. + $variation_id = empty( $_REQUEST['variation_id'] ) ? '' : absint( wp_unslash( $_REQUEST['variation_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification + $quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification $missing_attributes = array(); $variations = array(); $adding_to_cart = wc_get_product( $product_id ); @@ -846,12 +844,12 @@ class WC_Form_Handler { } $attribute_key = 'attribute_' . sanitize_title( $attribute['name'] ); - if ( isset( $_REQUEST[ $attribute_key ] ) ) { + if ( isset( $_REQUEST[ $attribute_key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification if ( $attribute['is_taxonomy'] ) { // Don't use wc_clean as it destroys sanitized characters. - $value = sanitize_title( wp_unslash( $_REQUEST[ $attribute_key ] ) ); + $value = sanitize_title( wp_unslash( $_REQUEST[ $attribute_key ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification } else { - $value = html_entity_decode( wc_clean( wp_unslash( $_REQUEST[ $attribute_key ] ) ), ENT_QUOTES, get_bloginfo( 'charset' ) ); // WPCS: sanitization ok. + $value = html_entity_decode( wc_clean( wp_unslash( $_REQUEST[ $attribute_key ] ) ), ENT_QUOTES, get_bloginfo( 'charset' ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification } $posted_attributes[ $attribute_key ] = $value; @@ -879,7 +877,7 @@ class WC_Form_Handler { // Get valid value from variation data. $attribute_key = 'attribute_' . sanitize_title( $attribute['name'] ); - $valid_value = isset( $variation_data[ $attribute_key ] ) ? $variation_data[ $attribute_key ]: ''; + $valid_value = isset( $variation_data[ $attribute_key ] ) ? $variation_data[ $attribute_key ] : ''; /** * If the attribute value was posted, check if it's valid. @@ -892,10 +890,11 @@ class WC_Form_Handler { // Allow if valid or show error. if ( $valid_value === $value ) { $variations[ $attribute_key ] = $value; - } elseif ( '' === $valid_value && in_array( $value, $attribute->get_slugs() ) ) { + } elseif ( '' === $valid_value && in_array( $value, $attribute->get_slugs(), true ) ) { // If valid values are empty, this is an 'any' variation so get all possible values. $variations[ $attribute_key ] = $value; } else { + /* translators: %s: Attribute name. */ throw new Exception( sprintf( __( 'Invalid value posted for %s', 'woocommerce' ), wc_attribute_label( $attribute['name'] ) ) ); } } elseif ( '' === $valid_value ) { @@ -903,6 +902,7 @@ class WC_Form_Handler { } } if ( ! empty( $missing_attributes ) ) { + /* translators: %s: Attribute name. */ throw new Exception( sprintf( _n( '%s is a required field', '%s are required fields', count( $missing_attributes ), 'woocommerce' ), wc_format_list_of_items( $missing_attributes ) ) ); } } catch ( Exception $e ) { @@ -922,22 +922,24 @@ class WC_Form_Handler { /** * Process the login form. + * + * @throws Exception On login error. */ public static function process_login() { // The global form-login.php template used `_wpnonce` in template versions < 3.3.0. $nonce_value = wc_get_var( $_REQUEST['woocommerce-login-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine. - if ( ! empty( $_POST['login'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-login' ) ) { + if ( isset( $_POST['login'], $_POST['username'], $_POST['password'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-login' ) ) { try { $creds = array( - 'user_login' => trim( $_POST['username'] ), - 'user_password' => $_POST['password'], - 'remember' => isset( $_POST['rememberme'] ), + 'user_login' => trim( wp_unslash( $_POST['username'] ) ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + 'user_password' => wp_unslash( $_POST['password'] ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + 'remember' => isset( $_POST['rememberme'] ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized ); $validation_error = new WP_Error(); - $validation_error = apply_filters( 'woocommerce_process_login_errors', $validation_error, $_POST['username'], $_POST['password'] ); + $validation_error = apply_filters( 'woocommerce_process_login_errors', $validation_error, $creds['user_login'], $creds['user_password'] ); if ( $validation_error->get_error_code() ) { throw new Exception( '' . __( 'Error:', 'woocommerce' ) . ' ' . $validation_error->get_error_message() ); @@ -956,7 +958,7 @@ class WC_Form_Handler { } } - // Perform the login + // Perform the login. $user = wp_signon( apply_filters( 'woocommerce_login_credentials', $creds ), is_ssl() ); if ( is_wp_error( $user ) ) { @@ -966,14 +968,14 @@ class WC_Form_Handler { } else { if ( ! empty( $_POST['redirect'] ) ) { - $redirect = $_POST['redirect']; + $redirect = wp_unslash( $_POST['redirect'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized } elseif ( wc_get_raw_referer() ) { $redirect = wc_get_raw_referer(); } else { $redirect = wc_get_page_permalink( 'myaccount' ); } - wp_redirect( wp_validate_redirect( apply_filters( 'woocommerce_login_redirect', remove_query_arg( 'wc_error', $redirect ), $user ), wc_get_page_permalink( 'myaccount' ) ) ); + wp_redirect( wp_validate_redirect( apply_filters( 'woocommerce_login_redirect', remove_query_arg( 'wc_error', $redirect ), $user ), wc_get_page_permalink( 'myaccount' ) ) ); // phpcs:ignore exit; } } catch ( Exception $e ) { @@ -998,7 +1000,7 @@ class WC_Form_Handler { // If successful, redirect to my account with query arg set. if ( $success ) { - wp_redirect( add_query_arg( 'reset-link-sent', 'true', wc_get_account_endpoint_url( 'lost-password' ) ) ); + wp_safe_redirect( add_query_arg( 'reset-link-sent', 'true', wc_get_account_endpoint_url( 'lost-password' ) ) ); exit; } } @@ -1008,19 +1010,19 @@ class WC_Form_Handler { * Handle reset password form. */ public static function process_reset_password() { + $nonce_value = wc_get_var( $_REQUEST['woocommerce-reset-password-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine. + + if ( ! wp_verify_nonce( $nonce_value, 'reset_password' ) ) { + return; + } + $posted_fields = array( 'wc_reset_password', 'password_1', 'password_2', 'reset_key', 'reset_login' ); foreach ( $posted_fields as $field ) { if ( ! isset( $_POST[ $field ] ) ) { return; } - $posted_fields[ $field ] = $_POST[ $field ]; - } - - $nonce_value = wc_get_var( $_REQUEST['woocommerce-reset-password-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine. - - if ( ! wp_verify_nonce( $nonce_value, 'reset_password' ) ) { - return; + $posted_fields[ $field ] = wp_unslash( $_POST[ $field ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized } $user = WC_Shortcode_My_Account::check_password_reset_key( $posted_fields['reset_key'], $posted_fields['reset_login'] ); @@ -1045,7 +1047,7 @@ class WC_Form_Handler { do_action( 'woocommerce_customer_reset_password', $user ); - wp_redirect( add_query_arg( 'password-reset', 'true', wc_get_page_permalink( 'myaccount' ) ) ); + wp_safe_redirect( add_query_arg( 'password-reset', 'true', wc_get_page_permalink( 'myaccount' ) ) ); exit; } } @@ -1053,15 +1055,17 @@ class WC_Form_Handler { /** * Process the registration form. + * + * @throws Exception On registration error. */ public static function process_registration() { - $nonce_value = isset( $_POST['_wpnonce'] ) ? $_POST['_wpnonce'] : ''; - $nonce_value = isset( $_POST['woocommerce-register-nonce'] ) ? $_POST['woocommerce-register-nonce'] : $nonce_value; + $nonce_value = isset( $_POST['_wpnonce'] ) ? wp_unslash( $_POST['_wpnonce'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.NoNonceVerification + $nonce_value = isset( $_POST['woocommerce-register-nonce'] ) ? wp_unslash( $_POST['woocommerce-register-nonce'] ) : $nonce_value; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.NoNonceVerification - if ( ! empty( $_POST['register'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-register' ) ) { - $username = 'no' === get_option( 'woocommerce_registration_generate_username' ) ? $_POST['username'] : ''; - $password = 'no' === get_option( 'woocommerce_registration_generate_password' ) ? $_POST['password'] : ''; - $email = $_POST['email']; + if ( isset( $_POST['register'], $_POST['email'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-register' ) ) { + $username = 'no' === get_option( 'woocommerce_registration_generate_username' ) && isset( $_POST['username'] ) ? wp_unslash( $_POST['username'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $password = 'no' === get_option( 'woocommerce_registration_generate_password' ) && isset( $_POST['password'] ) ? wp_unslash( $_POST['password'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $email = wp_unslash( $_POST['email'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized try { $validation_error = new WP_Error(); @@ -1082,14 +1086,14 @@ class WC_Form_Handler { } if ( ! empty( $_POST['redirect'] ) ) { - $redirect = wp_sanitize_redirect( $_POST['redirect'] ); + $redirect = wp_sanitize_redirect( wp_unslash( $_POST['redirect'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized } elseif ( wc_get_raw_referer() ) { $redirect = wc_get_raw_referer(); } else { $redirect = wc_get_page_permalink( 'myaccount' ); } - wp_redirect( wp_validate_redirect( apply_filters( 'woocommerce_registration_redirect', $redirect ), wc_get_page_permalink( 'myaccount' ) ) ); + wp_safe_redirect( wp_validate_redirect( apply_filters( 'woocommerce_registration_redirect', $redirect ), wc_get_page_permalink( 'myaccount' ) ) ); exit; } catch ( Exception $e ) { From eed91070b1390983cf533c2b07427c942eb93b24 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Feb 2019 15:48:59 +0000 Subject: [PATCH 214/401] Allow notice to persist after customer ID change --- includes/class-wc-checkout.php | 1 - includes/class-wc-session-handler.php | 28 +++++++++++++++++---------- includes/wc-user-functions.php | 8 ++++---- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/includes/class-wc-checkout.php b/includes/class-wc-checkout.php index 4f0bdeaba12..24344b2dbb1 100644 --- a/includes/class-wc-checkout.php +++ b/includes/class-wc-checkout.php @@ -954,7 +954,6 @@ class WC_Checkout { throw new Exception( $customer_id->get_error_message() ); } - wp_set_current_user( $customer_id ); wc_set_customer_auth_cookie( $customer_id ); // As we are now logged in, checkout will need to refresh to show logged in data. diff --git a/includes/class-wc-session-handler.php b/includes/class-wc-session-handler.php index 5a4907b1a97..dbf436d24be 100644 --- a/includes/class-wc-session-handler.php +++ b/includes/class-wc-session-handler.php @@ -66,6 +66,24 @@ class WC_Session_Handler extends WC_Session { * @since 3.3.0 */ public function init() { + $this->init_session_cookie(); + $this->_data = $this->get_session_data(); + + add_action( 'woocommerce_set_cart_cookies', array( $this, 'set_customer_session_cookie' ), 10 ); + add_action( 'shutdown', array( $this, 'save_data' ), 20 ); + add_action( 'wp_logout', array( $this, 'destroy_session' ) ); + + if ( ! is_user_logged_in() ) { + add_filter( 'nonce_user_logged_out', array( $this, 'nonce_user_logged_out' ) ); + } + } + + /** + * Setup cookie and customer ID. + * + * @since 3.6.0 + */ + public function init_session_cookie() { $cookie = $this->get_session_cookie(); if ( $cookie ) { @@ -83,16 +101,6 @@ class WC_Session_Handler extends WC_Session { $this->set_session_expiration(); $this->_customer_id = $this->generate_customer_id(); } - - $this->_data = $this->get_session_data(); - - add_action( 'woocommerce_set_cart_cookies', array( $this, 'set_customer_session_cookie' ), 10 ); - add_action( 'shutdown', array( $this, 'save_data' ), 20 ); - add_action( 'wp_logout', array( $this, 'destroy_session' ) ); - - if ( ! is_user_logged_in() ) { - add_filter( 'nonce_user_logged_out', array( $this, 'nonce_user_logged_out' ) ); - } } /** diff --git a/includes/wc-user-functions.php b/includes/wc-user-functions.php index a01138583c5..a164433b66f 100644 --- a/includes/wc-user-functions.php +++ b/includes/wc-user-functions.php @@ -121,11 +121,11 @@ if ( ! function_exists( 'wc_create_new_customer' ) ) { * @param int $customer_id Customer ID. */ function wc_set_customer_auth_cookie( $customer_id ) { - global $current_user; - - $current_user = get_user_by( 'id', $customer_id ); // WPCS: override ok. - + wp_set_current_user( $customer_id ); wp_set_auth_cookie( $customer_id, true ); + + // Update session. + WC()->session->init_session_cookie(); } /** From 53b9e219e7bd4a9c8223701ed670fc8e6ee6c0af Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Feb 2019 15:49:10 +0000 Subject: [PATCH 215/401] Add notice when registration is complete --- includes/class-wc-form-handler.php | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/includes/class-wc-form-handler.php b/includes/class-wc-form-handler.php index df0a5381c05..f81dce02b60 100644 --- a/includes/class-wc-form-handler.php +++ b/includes/class-wc-form-handler.php @@ -1081,21 +1081,27 @@ class WC_Form_Handler { throw new Exception( $new_customer->get_error_message() ); } + if ( 'yes' === get_option( 'woocommerce_registration_generate_password' ) ) { + wc_add_notice( __( 'Your account was created successfully and a password has been sent to your email address.', 'woocommerce' ) ); + } else { + wc_add_notice( __( 'Your account was created successfully. Your login details have been sent to your email address.', 'woocommerce' ) ); + } + + // Only redirect after a forced login - otherwise output a success notice. if ( apply_filters( 'woocommerce_registration_auth_new_customer', true, $new_customer ) ) { wc_set_customer_auth_cookie( $new_customer ); + + if ( ! empty( $_POST['redirect'] ) ) { + $redirect = wp_sanitize_redirect( wp_unslash( $_POST['redirect'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + } elseif ( wc_get_raw_referer() ) { + $redirect = wc_get_raw_referer(); + } else { + $redirect = wc_get_page_permalink( 'myaccount' ); + } + + wp_redirect( wp_validate_redirect( apply_filters( 'woocommerce_registration_redirect', $redirect ), wc_get_page_permalink( 'myaccount' ) ) ); //phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect + exit; } - - if ( ! empty( $_POST['redirect'] ) ) { - $redirect = wp_sanitize_redirect( wp_unslash( $_POST['redirect'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - } elseif ( wc_get_raw_referer() ) { - $redirect = wc_get_raw_referer(); - } else { - $redirect = wc_get_page_permalink( 'myaccount' ); - } - - wp_safe_redirect( wp_validate_redirect( apply_filters( 'woocommerce_registration_redirect', $redirect ), wc_get_page_permalink( 'myaccount' ) ) ); - exit; - } catch ( Exception $e ) { wc_add_notice( '' . __( 'Error:', 'woocommerce' ) . ' ' . $e->getMessage(), 'error' ); } From c47a6b917264217ec35d3b7687ef2cdf79e40e38 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Feb 2019 16:04:25 +0000 Subject: [PATCH 216/401] Tools and status require rest API init --- includes/admin/class-wc-admin-status.php | 9 +++++++++ includes/admin/views/html-admin-page-status-report.php | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/includes/admin/class-wc-admin-status.php b/includes/admin/class-wc-admin-status.php index 88dc02ce678..293b7768176 100644 --- a/includes/admin/class-wc-admin-status.php +++ b/includes/admin/class-wc-admin-status.php @@ -31,6 +31,15 @@ class WC_Admin_Status { * Handles output of tools. */ public static function status_tools() { + // This screen requires classes from the REST API. + if ( ! did_action( 'rest_api_init' ) ) { + WC()->api->rest_api_includes(); + } + + if ( ! class_exists( 'WC_REST_System_Status_Tools_Controller', false ) ) { + wp_die( 'Cannot load the REST API to access WC_REST_System_Status_Tools_Controller.' ); + } + $tools = self::get_tools(); if ( ! empty( $_GET['action'] ) && ! empty( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( wp_unslash( $_REQUEST['_wpnonce'] ), 'debug_action' ) ) { // WPCS: input var ok, sanitization ok. diff --git a/includes/admin/views/html-admin-page-status-report.php b/includes/admin/views/html-admin-page-status-report.php index 201b4e9db27..bd1c70c188d 100644 --- a/includes/admin/views/html-admin-page-status-report.php +++ b/includes/admin/views/html-admin-page-status-report.php @@ -9,6 +9,11 @@ defined( 'ABSPATH' ) || exit; global $wpdb; +// This screen requires classes from the REST API. +if ( ! did_action( 'rest_api_init' ) ) { + WC()->api->rest_api_includes(); +} + if ( ! class_exists( 'WC_REST_System_Status_Controller', false ) ) { wp_die( 'Cannot load the REST API to access WC_REST_System_Status_Controller.' ); } From 77605719856f428a49caad9fa16b2461e7b362d6 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Feb 2019 16:07:51 +0000 Subject: [PATCH 217/401] Dump max width in notices --- assets/css/activation.scss | 7 ------- 1 file changed, 7 deletions(-) diff --git a/assets/css/activation.scss b/assets/css/activation.scss index edfb51ffbc7..7279e617b07 100644 --- a/assets/css/activation.scss +++ b/assets/css/activation.scss @@ -10,12 +10,6 @@ div.woocommerce-message { overflow: hidden; position: relative; border-left-color: #cc99c2 !important; - p { - max-width: 700px; - } - p:last-child { - max-width: inherit; - } } p.woocommerce-actions, @@ -76,7 +70,6 @@ div.woocommerce-no-shipping-methods-notice { p { position: relative; z-index: 1; - max-width: 700px; line-height: 1.5em; margin: 12px 0; From ebdeab4c2256e144ee71059876db0c1ec0b8d6b2 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Feb 2019 16:31:17 +0000 Subject: [PATCH 218/401] Added woocommerce_paypal_force_one_line_item filter --- .../includes/class-wc-gateway-paypal-request.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php index d22ece35102..2d239ca4590 100644 --- a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php +++ b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php @@ -184,10 +184,16 @@ class WC_Gateway_Paypal_Request { protected function get_paypal_args( $order ) { WC_Gateway_Paypal::log( 'Generating payment form for order ' . $order->get_order_number() . '. Notify URL: ' . $this->notify_url ); + $force_one_line_item = apply_filters( 'woocommerce_paypal_force_one_line_item', false, $order ); + + if ( ( wc_tax_enabled() && wc_prices_include_tax() ) || ! $this->line_items_valid( $order ) ) { + $force_one_line_item = true; + } + $paypal_args = apply_filters( 'woocommerce_paypal_args', array_merge( $this->get_transaction_args( $order ), - $this->get_line_item_args( $order ) + $this->get_line_item_args( $order, $force_one_line_item ) ), $order ); @@ -297,11 +303,8 @@ class WC_Gateway_Paypal_Request { * @return array */ protected function get_line_item_args( $order, $force_one_line_item = false ) { - if ( wc_tax_enabled() && wc_prices_include_tax() || ! $this->line_items_valid( $order ) ) { - $force_one_line_item = true; - } - $line_item_args = array(); + if ( $force_one_line_item ) { /** * Send order as a single item. From e64914bb3cd3013ae404e1230f4c14b2a0f4e01b Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Wed, 30 Jan 2019 10:51:26 -0400 Subject: [PATCH 219/401] add DAY_IN_SECONDS to cron sale price removal check for consistency with #22189 --- includes/data-stores/class-wc-product-data-store-cpt.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index c3b0b6c6b59..ef0d2d2d667 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -1038,7 +1038,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da AND postmeta.meta_value > 0 AND postmeta.meta_value < %s AND postmeta_2.meta_value != postmeta_3.meta_value", - current_time( 'timestamp', true ) + current_time( 'timestamp', true ) - DAY_IN_SECONDS ) ); } From 249bf99df5cef0a1bb896d5b843c472c5954a9dc Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Wed, 30 Jan 2019 11:31:48 -0400 Subject: [PATCH 220/401] phpcs sniff fixes for class-wc-product-data-store-cpt.php --- .../data-stores/class-wc-product-data-store-cpt.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index ef0d2d2d667..8a9480fe153 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -915,8 +915,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da 'post_type' => array( 'product', 'product_variation' ), 'posts_per_page' => -1, 'post_status' => 'publish', - // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_tax_query - 'tax_query' => array( + 'tax_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query 'relation' => 'AND', array( 'taxonomy' => 'product_visibility', @@ -1060,7 +1059,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da 'fields' => 'ids', 'post_status' => 'publish', 'numberposts' => 1, - 'meta_query' => array(), // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_query + 'meta_query' => array(), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query ); // Allow large queries in case user has many variations or attributes. @@ -1593,8 +1592,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da $wp_query_args['date_query'] = array(); } if ( ! isset( $wp_query_args['meta_query'] ) ) { - // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_query - $wp_query_args['meta_query'] = array(); + $wp_query_args['meta_query'] = array(); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query } // Handle product types. @@ -1602,7 +1600,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da $wp_query_args['post_type'] = 'product_variation'; } elseif ( is_array( $query_vars['type'] ) && in_array( 'variation', $query_vars['type'], true ) ) { $wp_query_args['post_type'] = array( 'product_variation', 'product' ); - $wp_query_args['tax_query'][] = array( + $wp_query_args['tax_query'][] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query 'relation' => 'OR', array( 'taxonomy' => 'product_type', From 7b5810d3dada5bc209e62752d4244aef1b16b13a Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Mon, 4 Feb 2019 13:47:44 -0400 Subject: [PATCH 221/401] limit page 2 not showing products to shop showing subcategories --- includes/wc-template-functions.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 87be414b295..f04b1b7391d 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -2226,6 +2226,10 @@ if ( ! function_exists( 'woocommerce_get_loop_display_mode' ) ) { $display_type = '' === $display_type ? get_option( 'woocommerce_category_archive_display', '' ) : $display_type; } + if ( ( ! is_shop() || 'subcategories' !== $display_type ) && 1 < wc_get_loop_prop( 'current_page' ) ) { + return 'products'; + } + // Ensure valid value. if ( '' === $display_type || ! in_array( $display_type, array( 'products', 'subcategories', 'both' ), true ) ) { $display_type = 'products'; From 5aea728847e5a64c119895839fb0ac4a683b2ff3 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 4 Feb 2019 20:41:38 +0000 Subject: [PATCH 222/401] Update dependency chromedriver to v2.46.0 --- package-lock.json | 371 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 2 +- 2 files changed, 360 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index e9fffa562ef..f6db39e9c76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -426,6 +426,12 @@ "uri-js": "^4.2.2" } }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, "ansi-escapes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", @@ -610,6 +616,12 @@ "dev": true, "optional": true }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1984,6 +1996,15 @@ "dev": true, "optional": true }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "requires": { + "inherits": "~2.0.0" + } + }, "bluebird": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", @@ -2263,9 +2284,9 @@ "optional": true }, "chromedriver": { - "version": "2.45.0", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-2.45.0.tgz", - "integrity": "sha512-Qwmcr+2mU3INeR6mVsQ8gO00vZpL8ZeTJLclX44C0dcs88jrSDgckPqbG+qkVX+m2L/aOPnF0lYgPdOiOiLt5w==", + "version": "2.46.0", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-2.46.0.tgz", + "integrity": "sha512-dLtKIJW3y/PuFrPmcw6Mb8Nh+HwSqgVrK1rWgTARXhHfWvV822X2VRkx2meU/tg2+YQL6/nNgT6n5qWwIDHbwg==", "dev": true, "requires": { "del": "^3.0.0", @@ -2374,6 +2395,29 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + } + } + }, "clone-regexp": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-1.0.1.tgz", @@ -3974,6 +4018,18 @@ "node-pre-gyp": "^0.10.0" } }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -4017,6 +4073,12 @@ "globule": "^1.0.0" } }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", @@ -5165,6 +5227,12 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", + "dev": true + }, "indent-string": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", @@ -5284,6 +5352,12 @@ "loose-envify": "^1.0.0" } }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", @@ -6009,6 +6083,15 @@ "integrity": "sha512-bEZlJzXo5V/ApNNa5z375mJC6Nrz4vG43UgcSCrg2OHC+yuB6j0iDSrY7RQ/+PRofFB03wNIIt9iXIVLr4wc7w==", "dev": true }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, "leven": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", @@ -6550,6 +6633,18 @@ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -6557,6 +6652,12 @@ "dev": true, "optional": true }, + "lodash.mergewith": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", + "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", + "dev": true + }, "lodash.unescape": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", @@ -6974,8 +7075,7 @@ "version": "2.12.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", - "dev": true, - "optional": true + "dev": true }, "nanomatch": { "version": "1.2.13", @@ -7040,6 +7140,54 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "dev": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + } + } + }, "node-pre-gyp": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz", @@ -7068,6 +7216,86 @@ "semver": "^5.3.0" } }, + "node-sass": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", + "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", + "dev": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash.assign": "^4.2.0", + "lodash.clonedeep": "^4.3.2", + "lodash.mergewith": "^4.6.0", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.10.0", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, "node-wp-i18n": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/node-wp-i18n/-/node-wp-i18n-1.2.2.tgz", @@ -7188,7 +7416,6 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, - "optional": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -7343,6 +7570,15 @@ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -7354,7 +7590,6 @@ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, - "optional": true, "requires": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" @@ -8892,12 +9127,24 @@ "uuid": "^3.3.2" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, "requireindex": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", @@ -9039,6 +9286,18 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "dev": true, + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" + } + }, "saucelabs": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", @@ -9054,6 +9313,27 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "dev": true, + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, "selenium-webdriver": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", @@ -9093,8 +9373,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true, - "optional": true + "dev": true }, "set-value": { "version": "2.0.0", @@ -9418,9 +9697,9 @@ "integrity": "sha1-8A14/RYBMICbSrNAwDEPqnElPb0=" }, "sshpk": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz", - "integrity": "sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", "dev": true, "requires": { "asn1": "~0.2.3", @@ -9467,6 +9746,15 @@ } } }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, "string-argv": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.0.2.tgz", @@ -10610,6 +10898,15 @@ "integrity": "sha512-fwkLWH+DimvA4YCy+/nvJd61nWQQ2liO/nF/RjkTpiOGi+zxZzVkhb1mvbHIIW4b/8nDsYI8uTmAlc0nNkRMOw==", "dev": true }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "dev": true, + "requires": { + "glob": "^7.1.2" + } + }, "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", @@ -11035,6 +11332,12 @@ "isexe": "^2.0.0" } }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -11179,6 +11482,12 @@ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "dev": true }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, "yallist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", @@ -11186,6 +11495,44 @@ "dev": true, "optional": true }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "dev": true, + "requires": { + "camelcase": "^3.0.0" + } + } + } + }, "yargs-parser": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", diff --git a/package.json b/package.json index aafe172a9fc..100aed58915 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "babel-preset-stage-2": "6.24.1", "chai": "4.2.0", "chai-as-promised": "7.1.1", - "chromedriver": "2.45.0", + "chromedriver": "2.46.0", "config": "3.0.1", "cross-env": "5.2.0", "eslint": "5.12.0", From d082c7dfa5230f145d7ff6d1d9717353ce721c84 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Feb 2019 11:02:21 +0000 Subject: [PATCH 223/401] Swap to wp_strip_all_tags --- includes/class-wc-structured-data.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-structured-data.php b/includes/class-wc-structured-data.php index 25af41c8983..45c0131308b 100644 --- a/includes/class-wc-structured-data.php +++ b/includes/class-wc-structured-data.php @@ -210,7 +210,7 @@ class WC_Structured_Data { } $markup['image'] = wp_get_attachment_url( $product->get_image_id() ); - $markup['description'] = wp_filter_nohtml_kses( do_shortcode( $product->get_short_description() ? $product->get_short_description() : $product->get_description() ) ); + $markup['description'] = wp_strip_all_tags( do_shortcode( $product->get_short_description() ? $product->get_short_description() : $product->get_description() ) ); $markup['sku'] = $product->get_sku(); if ( '' !== $product->get_price() ) { From fa9465a2cbc0a7c72def06f4c7fd6dd358b81822 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Feb 2019 11:23:47 +0000 Subject: [PATCH 224/401] novalidate order review form --- assets/js/frontend/checkout.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/js/frontend/checkout.js b/assets/js/frontend/checkout.js index d367bd791a5..672e9296ba8 100644 --- a/assets/js/frontend/checkout.js +++ b/assets/js/frontend/checkout.js @@ -24,6 +24,7 @@ jQuery( function( $ ) { if ( $( document.body ).hasClass( 'woocommerce-order-pay' ) ) { this.$order_review.on( 'click', 'input[name="payment_method"]', this.payment_method_selected ); + this.$order_review.attr( 'novalidate', 'novalidate' ); } // Prevent HTML5 validation which can conflict. From b091a0975db81e9decf30d0b306b20dfab73c158 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Feb 2019 11:28:46 +0000 Subject: [PATCH 225/401] Switch span to paragraph --- includes/admin/class-wc-admin-profile.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/includes/admin/class-wc-admin-profile.php b/includes/admin/class-wc-admin-profile.php index a530536ebcc..2fb48db3052 100644 --- a/includes/admin/class-wc-admin-profile.php +++ b/includes/admin/class-wc-admin-profile.php @@ -186,8 +186,7 @@ if ( ! class_exists( 'WC_Admin_Profile', false ) ) : -
    - +

    From 3948e9f83ca404d224a36673513aaf10b3d45c87 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Feb 2019 11:40:10 +0000 Subject: [PATCH 226/401] BlockUI on order form submit --- assets/js/frontend/checkout.js | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/assets/js/frontend/checkout.js b/assets/js/frontend/checkout.js index d367bd791a5..3384b797f1c 100644 --- a/assets/js/frontend/checkout.js +++ b/assets/js/frontend/checkout.js @@ -24,6 +24,7 @@ jQuery( function( $ ) { if ( $( document.body ).hasClass( 'woocommerce-order-pay' ) ) { this.$order_review.on( 'click', 'input[name="payment_method"]', this.payment_method_selected ); + this.$order_review.on( 'submit', this.submitOrder ); } // Prevent HTML5 validation which can conflict. @@ -413,6 +414,22 @@ jQuery( function( $ ) { }); }, + blockOnSubmit: function( $form ) { + var form_data = $form.data(); + + if ( 1 !== form_data['blockUI.isBlocked'] ) { + $form.block({ + message: null, + overlayCSS: { + background: '#fff', + opacity: 0.6 + } + }); + } + }, + submitOrder: function() { + wc_checkout_form.blockOnSubmit( $( this ) ); + }, submit: function() { wc_checkout_form.reset_update_checkout_timer(); var $form = $( this ); @@ -426,17 +443,7 @@ jQuery( function( $ ) { $form.addClass( 'processing' ); - var form_data = $form.data(); - - if ( 1 !== form_data['blockUI.isBlocked'] ) { - $form.block({ - message: null, - overlayCSS: { - background: '#fff', - opacity: 0.6 - } - }); - } + wc_checkout_form.blockOnSubmit( $form ); // ajaxSetup is global, but we use it to ensure JSON is valid once returned. $.ajaxSetup( { From c88a92144bb6f176bd8d7c52ea234878955a3b85 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Feb 2019 13:02:20 +0000 Subject: [PATCH 227/401] Only update session cookie if the value changes --- includes/class-wc-session-handler.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-session-handler.php b/includes/class-wc-session-handler.php index 5a4907b1a97..6e18f6774f9 100644 --- a/includes/class-wc-session-handler.php +++ b/includes/class-wc-session-handler.php @@ -111,7 +111,9 @@ class WC_Session_Handler extends WC_Session { $cookie_value = $this->_customer_id . '||' . $this->_session_expiration . '||' . $this->_session_expiring . '||' . $cookie_hash; $this->_has_cookie = true; - wc_setcookie( $this->_cookie, $cookie_value, $this->_session_expiration, apply_filters( 'wc_session_use_secure_cookie', false ) ); + if ( ! isset( $_COOKIE[ $this->_cookie ] ) || $_COOKIE[ $this->_cookie ] !== $cookie_value ) { + wc_setcookie( $this->_cookie, $cookie_value, $this->_session_expiration, apply_filters( 'wc_session_use_secure_cookie', false ) ); + } } } From d004150d71ee2d3591df3f24115dd0017758166f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Feb 2019 13:02:52 +0000 Subject: [PATCH 228/401] Only set and unset cookies if the values change --- includes/class-wc-cart-session.php | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/includes/class-wc-cart-session.php b/includes/class-wc-cart-session.php index a2c2e405d36..ee4ef04a88a 100644 --- a/includes/class-wc-cart-session.php +++ b/includes/class-wc-cart-session.php @@ -253,18 +253,34 @@ final class WC_Cart_Session { } /** - * Set cart hash cookie and items in cart. + * Set cart hash cookie and items in cart if not already set. * * @param bool $set Should cookies be set (true) or unset. */ private function set_cart_cookies( $set = true ) { if ( $set ) { - wc_setcookie( 'woocommerce_items_in_cart', 1 ); - wc_setcookie( 'woocommerce_cart_hash', WC()->cart->get_cart_hash() ); - } elseif ( isset( $_COOKIE['woocommerce_items_in_cart'] ) ) { // WPCS: input var ok. - wc_setcookie( 'woocommerce_items_in_cart', 0, time() - HOUR_IN_SECONDS ); - wc_setcookie( 'woocommerce_cart_hash', '', time() - HOUR_IN_SECONDS ); + $setcookies = array( + 'woocommerce_items_in_cart' => '1', + 'woocommerce_cart_hash' => WC()->cart->get_cart_hash(), + ); + foreach ( $setcookies as $name => $value ) { + if ( ! isset( $_COOKIE[ $name ] ) || $_COOKIE[ $name ] !== $value ) { + wc_setcookie( $name, $value ); + } + } + } else { + $unsetcookies = array( + 'woocommerce_items_in_cart', + 'woocommerce_cart_hash', + ); + foreach ( $unsetcookies as $name ) { + if ( isset( $_COOKIE[ $name ] ) ) { + wc_setcookie( $name, 0, time() - HOUR_IN_SECONDS ); + unset( $_COOKIE[ $name ] ); + } + } } + do_action( 'woocommerce_set_cart_cookies', $set ); } From ac29feac7c1b2587602d69eae5affee2f20f49a8 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Feb 2019 13:03:16 +0000 Subject: [PATCH 229/401] header_register_callback to set cookies before headers are sent (if supported) --- includes/class-wc-cart-session.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-cart-session.php b/includes/class-wc-cart-session.php index ee4ef04a88a..960d023d200 100644 --- a/includes/class-wc-cart-session.php +++ b/includes/class-wc-cart-session.php @@ -47,13 +47,19 @@ final class WC_Cart_Session { public function init() { add_action( 'wp_loaded', array( $this, 'get_cart_from_session' ) ); add_action( 'woocommerce_cart_emptied', array( $this, 'destroy_cart_session' ) ); - add_action( 'wp', array( $this, 'maybe_set_cart_cookies' ), 99 ); - add_action( 'woocommerce_add_to_cart', array( $this, 'maybe_set_cart_cookies' ) ); add_action( 'woocommerce_after_calculate_totals', array( $this, 'set_session' ) ); add_action( 'woocommerce_cart_loaded_from_session', array( $this, 'set_session' ) ); add_action( 'woocommerce_removed_coupon', array( $this, 'set_session' ) ); - add_action( 'shutdown', array( $this, 'maybe_set_cart_cookies' ), 0 ); add_action( 'woocommerce_cart_updated', array( $this, 'persistent_cart_update' ) ); + + // Cookie events - cart cookies need to be set before headers are sent. + if ( function_exists( 'header_register_callback' ) ) { + header_register_callback( array( $this, 'maybe_set_cart_cookies' ) ); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.header_register_callbackFound + } else { + add_action( 'woocommerce_add_to_cart', array( $this, 'maybe_set_cart_cookies' ) ); + add_action( 'wp', array( $this, 'maybe_set_cart_cookies' ), 99 ); + add_action( 'shutdown', array( $this, 'maybe_set_cart_cookies' ), 0 ); + } } /** From cafe8b6b57e9d5e2a691bea65a4b7dfe6287e560 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Feb 2019 14:11:07 +0000 Subject: [PATCH 230/401] Tests check the image URL is contained rather than exact string --- tests/unit-tests/product/data.php | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/tests/unit-tests/product/data.php b/tests/unit-tests/product/data.php index 2bcb7f24c97..12e7f67dc9f 100644 --- a/tests/unit-tests/product/data.php +++ b/tests/unit-tests/product/data.php @@ -80,7 +80,8 @@ class WC_Tests_Product_Data extends WC_Unit_Test_Case { } $this->assertCount( 1, $product->get_attributes() ); $this->assertContains( - current( $product->get_attributes() )->get_data(), array( + current( $product->get_attributes() )->get_data(), + array( 'attribute_id' => 0, 'name' => 'Test Attribute', 'options' => array( 'Fish', 'Fingers' ), @@ -268,6 +269,9 @@ class WC_Tests_Product_Data extends WC_Unit_Test_Case { $this->assertEquals( '£50.00', $product->get_price_html() ); } + /** + * Test: test_get_image_should_return_product_image. + */ public function test_get_image_should_return_product_image() { $product = new WC_Product(); $image_url = $this->set_product_image( $product ); @@ -288,6 +292,9 @@ class WC_Tests_Product_Data extends WC_Unit_Test_Case { ); } + /** + * Test: test_get_image_should_return_parent_product_image. + */ public function test_get_image_should_return_parent_product_image() { $variable_product = WC_Helper_Product::create_variation_product(); $variations = $variable_product->get_children(); @@ -299,7 +306,7 @@ class WC_Tests_Product_Data extends WC_Unit_Test_Case { $variation_1->get_image() ); - $this->assertEquals( + $this->assertContains( '', $variation_1->get_image( 'single' ) ); @@ -310,15 +317,18 @@ class WC_Tests_Product_Data extends WC_Unit_Test_Case { ); } + /** + * Test: test_get_image_should_return_place_holder_image. + */ public function test_get_image_should_return_place_holder_image() { $product = new WC_Product(); - $image_url = wc_placeholder_img_src(); - $expected_result = 'Placeholder'; - $this->assertEquals( $expected_result, $product->get_image() ); - $this->assertEquals( $expected_result, $product->get_image( 'woocommerce_thumbnail', array(), true ) ); + $this->assertContains( $product->get_image(), wc_placeholder_img_src() ); } + /** + * Test: test_get_image_should_return_empty_string. + */ public function test_get_image_should_return_empty_string() { $product = new WC_Product(); $this->assertEquals( '', $product->get_image( 'woocommerce_thumbnail', array(), false ) ); From 4635ccc04fbdd80550c35458afc05e2605699689 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Feb 2019 14:11:16 +0000 Subject: [PATCH 231/401] Check we have a valid attachment --- includes/wc-product-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index fe580ee5ee9..e6c7470ba12 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -317,7 +317,7 @@ function wc_placeholder_img( $size = 'woocommerce_thumbnail' ) { $dimensions = wc_get_image_size( $size ); $placeholder_image = get_option( 'woocommerce_placeholder_image', 0 ); - if ( ! empty( $placeholder_image ) && is_numeric( $placeholder_image ) ) { + if ( ! empty( $placeholder_image ) && is_numeric( $placeholder_image ) && wp_attachment_is_image( $placeholder_image ) ) { $image_html = wp_get_attachment_image( $placeholder_image, $size, From 72314f9dbc92d6b44e663ab6cef04f5f6b0365b2 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Feb 2019 14:21:06 +0000 Subject: [PATCH 232/401] Contains was backwards --- tests/unit-tests/product/data.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit-tests/product/data.php b/tests/unit-tests/product/data.php index 12e7f67dc9f..cedce35c997 100644 --- a/tests/unit-tests/product/data.php +++ b/tests/unit-tests/product/data.php @@ -323,7 +323,7 @@ class WC_Tests_Product_Data extends WC_Unit_Test_Case { public function test_get_image_should_return_place_holder_image() { $product = new WC_Product(); - $this->assertContains( $product->get_image(), wc_placeholder_img_src() ); + $this->assertContains( wc_placeholder_img_src(), $product->get_image() ); } /** From b2542d7e8c10dc79b0a74f0993a7a5d24129ebff Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Feb 2019 17:41:17 +0000 Subject: [PATCH 233/401] Missing rest api class in wizard --- includes/admin/class-wc-admin-setup-wizard.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/includes/admin/class-wc-admin-setup-wizard.php b/includes/admin/class-wc-admin-setup-wizard.php index 3a0cc78055d..6b6d2a9af5e 100644 --- a/includes/admin/class-wc-admin-setup-wizard.php +++ b/includes/admin/class-wc-admin-setup-wizard.php @@ -1056,6 +1056,10 @@ class WC_Admin_Setup_Wizard { public function wc_setup_shipping_save() { check_admin_referer( 'wc-setup' ); + if ( ! did_action( 'rest_api_init' ) ) { + WC()->api->rest_api_includes(); + } + // @codingStandardsIgnoreStart $setup_domestic = isset( $_POST['shipping_zones']['domestic']['enabled'] ) && ( 'yes' === $_POST['shipping_zones']['domestic']['enabled'] ); $domestic_method = isset( $_POST['shipping_zones']['domestic']['method'] ) ? sanitize_text_field( wp_unslash( $_POST['shipping_zones']['domestic']['method'] ) ) : ''; From 6e4854b3aabe9adda0a1c3f4fdd7ff7786e13554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Demarle?= Date: Tue, 5 Feb 2019 20:29:45 +0100 Subject: [PATCH 234/401] Add filter woocommerce_update_product_stock_query --- .../class-wc-product-data-store-cpt.php | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index 0af4fe66764..c833dbd03b8 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -1235,37 +1235,33 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da // Update stock in DB directly. switch ( $operation ) { case 'increase': - // phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery - $wpdb->query( - $wpdb->prepare( - "UPDATE {$wpdb->postmeta} SET meta_value = meta_value + %f WHERE post_id = %d AND meta_key='_stock'", - $stock_quantity, - $product_id_with_stock - ) + $sql = $wpdb->prepare( + "UPDATE {$wpdb->postmeta} SET meta_value = meta_value + %f WHERE post_id = %d AND meta_key='_stock'", + $stock_quantity, + $product_id_with_stock ); break; case 'decrease': - // phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery - $wpdb->query( - $wpdb->prepare( - "UPDATE {$wpdb->postmeta} SET meta_value = meta_value - %f WHERE post_id = %d AND meta_key='_stock'", - $stock_quantity, - $product_id_with_stock - ) + $sql = $wpdb->prepare( + "UPDATE {$wpdb->postmeta} SET meta_value = meta_value - %f WHERE post_id = %d AND meta_key='_stock'", + $stock_quantity, + $product_id_with_stock ); break; default: - // phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery - $wpdb->query( - $wpdb->prepare( - "UPDATE {$wpdb->postmeta} SET meta_value = %f WHERE post_id = %d AND meta_key='_stock'", - $stock_quantity, - $product_id_with_stock - ) + $sql = $wpdb->prepare( + "UPDATE {$wpdb->postmeta} SET meta_value = %f WHERE post_id = %d AND meta_key='_stock'", + $stock_quantity, + $product_id_with_stock ); break; } + $sql = apply_filters( 'woocommerce_update_product_stock_query', $sql, $product_id_with_stock, $stock_quantity, $operation ); + + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery + $wpdb->query( $sql ); + wp_cache_delete( $product_id_with_stock, 'post_meta' ); } From 03a60fddfcfc307da873b342625f95cf7086d0bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Demarle?= Date: Wed, 6 Feb 2019 10:24:41 +0100 Subject: [PATCH 235/401] Ignore WordPress.DB.PreparedSQL.NotPrepared --- includes/data-stores/class-wc-product-data-store-cpt.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index c833dbd03b8..c0e3c79601c 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -1259,7 +1259,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da $sql = apply_filters( 'woocommerce_update_product_stock_query', $sql, $product_id_with_stock, $stock_quantity, $operation ); - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared $wpdb->query( $sql ); wp_cache_delete( $product_id_with_stock, 'post_meta' ); From 7f0ac47291d2ddc157f01f3daed2c8e814c67ac9 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 6 Feb 2019 12:15:29 +0000 Subject: [PATCH 236/401] Fix callback args --- includes/class-wc-cache-helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-cache-helper.php b/includes/class-wc-cache-helper.php index 8cc42643589..a0a8e09aead 100644 --- a/includes/class-wc-cache-helper.php +++ b/includes/class-wc-cache-helper.php @@ -26,7 +26,7 @@ class WC_Cache_Helper { add_action( 'shutdown', array( __CLASS__, 'delete_transients_on_shutdown' ), 10 ); add_action( 'template_redirect', array( __CLASS__, 'geolocation_ajax_redirect' ) ); add_action( 'admin_notices', array( __CLASS__, 'notices' ) ); - add_action( 'delete_version_transients', array( __CLASS__, 'delete_version_transients' ), 10, 2 ); + add_action( 'delete_version_transients', array( __CLASS__, 'delete_version_transients' ), 10 ); add_action( 'wp', array( __CLASS__, 'prevent_caching' ) ); add_action( 'clean_term_cache', array( __CLASS__, 'clean_term_cache' ), 10, 2 ); add_action( 'edit_terms', array( __CLASS__, 'clean_term_cache' ), 10, 2 ); From cb4bf5d1e5e8b2da08a5793a9046d8bc5eae3d7f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 6 Feb 2019 12:44:24 +0000 Subject: [PATCH 237/401] Fix unit tests --- includes/data-stores/class-wc-product-data-store-cpt.php | 1 + .../data-stores/class-wc-product-variable-data-store-cpt.php | 2 +- includes/wc-product-functions.php | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index e0efe4a51c6..12e16499ef3 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -826,6 +826,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da wc_delete_product_transients( $product->get_id() ); if ( $product->get_parent_id( 'edit' ) ) { wc_delete_product_transients( $product->get_parent_id( 'edit' ) ); + WC_Cache_Helper::incr_cache_prefix( 'product_' . $product->get_parent_id( 'edit' ) ); } WC_Cache_Helper::invalidate_attribute_count( array_keys( $product->get_attributes() ) ); WC_Cache_Helper::incr_cache_prefix( 'product_' . $product->get_id() ); diff --git a/includes/data-stores/class-wc-product-variable-data-store-cpt.php b/includes/data-stores/class-wc-product-variable-data-store-cpt.php index 54f9dd3325f..da3cacaa6cd 100644 --- a/includes/data-stores/class-wc-product-variable-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-variable-data-store-cpt.php @@ -167,7 +167,7 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple $variation_attributes = array(); $attributes = $product->get_attributes(); $child_ids = $product->get_children(); - $cache_key = WC_Cache_Helper::get_cache_prefix( 'products' ) . 'product_variation_attributes_' . $product->get_id(); + $cache_key = WC_Cache_Helper::get_cache_prefix( 'product_' . $product->get_id() ) . 'product_variation_attributes_' . $product->get_id(); $cache_group = 'products'; $cached_data = wp_cache_get( $cache_key, $cache_group ); diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index b970a3d06a1..54ed2158d57 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -105,7 +105,9 @@ function wc_delete_product_transients( $post_id = 0 ) { 'wc_low_stock_count', ); - WC_Cache_Helper::queue_delete_transient( $transients_to_clear ); + foreach ( $transients_to_clear as $transient ) { + delete_transient( $transient ); + } if ( $post_id > 0 ) { // Transient names that include an ID - since they are dynamic they cannot be cleaned in bulk without the ID. From ec7db6de069faf14c189b51ace1e4bdf16624efc Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 6 Feb 2019 12:46:36 +0000 Subject: [PATCH 238/401] Simplify check --- includes/wc-product-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index e6c7470ba12..5ef656138d3 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -317,7 +317,7 @@ function wc_placeholder_img( $size = 'woocommerce_thumbnail' ) { $dimensions = wc_get_image_size( $size ); $placeholder_image = get_option( 'woocommerce_placeholder_image', 0 ); - if ( ! empty( $placeholder_image ) && is_numeric( $placeholder_image ) && wp_attachment_is_image( $placeholder_image ) ) { + if ( wp_attachment_is_image( $placeholder_image ) ) { $image_html = wp_get_attachment_image( $placeholder_image, $size, From e604a3ae24b751cfef8ec2441116cee2109d3f77 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 6 Feb 2019 19:57:06 +0000 Subject: [PATCH 239/401] correct id without $post --- includes/wc-template-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index b8abadd2084..c886de15750 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -560,7 +560,7 @@ function wc_get_product_class( $class = '', $product_id = null ) { $product = $product_id; $product_id = $product_id->get_id(); } else { - $product = wc_get_product( $post->ID ); + $product = wc_get_product( $product_id ); } if ( ! is_array( $class ) ) { From 4b485eba135781bd85e2d8b2f0d91a0fc457deab Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 6 Feb 2019 21:00:07 +0000 Subject: [PATCH 240/401] Fixed unit tests --- includes/wc-template-functions.php | 5 ++-- tests/unit-tests/templates/functions.php | 29 ++++++++++++++++++------ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index c886de15750..a6bb7cc5283 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -627,9 +627,10 @@ function wc_get_product_class( $class = '', $product_id = null ) { // Include attributes and any extra taxonomies only if enabled via the hook - this is a performance issue. if ( apply_filters( 'woocommerce_get_product_class_include_taxonomies', false ) ) { $taxonomies = get_taxonomies( array( 'public' => true ) ); + $type = 'variation' === $product->get_type() ? 'product_variation' : 'product'; foreach ( (array) $taxonomies as $taxonomy ) { - if ( is_object_in_taxonomy( $post->post_type, $taxonomy ) && ! in_array( $taxonomy, array( 'product_cat', 'product_tag' ), true ) ) { - $classes = array_merge( $classes, wc_get_product_taxonomy_class( (array) get_the_terms( $post->ID, $taxonomy ), $taxonomy ) ); + if ( is_object_in_taxonomy( $type, $taxonomy ) && ! in_array( $taxonomy, array( 'product_cat', 'product_tag' ), true ) ) { + $classes = array_merge( $classes, wc_get_product_taxonomy_class( (array) get_the_terms( $product->get_id(), $taxonomy ), $taxonomy ) ); } } } diff --git a/tests/unit-tests/templates/functions.php b/tests/unit-tests/templates/functions.php index 465f571e7b4..5b425638a93 100644 --- a/tests/unit-tests/templates/functions.php +++ b/tests/unit-tests/templates/functions.php @@ -1,11 +1,14 @@ set_category_ids( array( $category['term_id'] ) ); $product->save(); + $product = wc_get_product( $product ); // Reload so status is current. $expected = array( 'foo', - 'post-' . $product->get_id(), 'product', 'type-product', + 'post-' . $product->get_id(), 'status-publish', - 'product_cat-some-category', 'first', 'instock', + 'product_cat-some-category', 'sale', 'virtual', 'purchasable', 'product-type-simple', ); + $actual = array_values( wc_get_product_class( 'foo', $product ) ); - $this->assertEquals( $expected, array_values( wc_get_product_class( 'foo', $product ) ) ); + $this->assertEquals( $expected, $actual, print_r( $actual, true ) ); // All taxonomies. add_filter( 'woocommerce_get_product_class_include_taxonomies', '__return_true' ); $expected = array( 'foo', - 'post-' . $product->get_id(), 'product', 'type-product', + 'post-' . $product->get_id(), 'status-publish', - 'product_cat-some-category', 'instock', + 'product_cat-some-category', 'sale', 'virtual', 'purchasable', 'product-type-simple', ); + $actual = array_values( wc_get_product_class( 'foo', $product ) ); - $this->assertEquals( $expected, array_values( wc_get_product_class( 'foo', $product ) ) ); + $this->assertEquals( $expected, $actual, print_r( $actual, true ) ); add_filter( 'woocommerce_get_product_class_include_taxonomies', '__return_false' ); $product->delete( true ); wp_delete_term( $category['term_id'], 'product_cat' ); } + /** + * Test: test_wc_dropdown_variation_attribute_options_no_attributes. + */ public function test_wc_dropdown_variation_attribute_options_no_attributes() { $this->expectOutputString( '' ); wc_dropdown_variation_attribute_options(); } + /** + * Test: test_wc_dropdown_variation_attribute_options_should_return_attributes_list. + */ public function test_wc_dropdown_variation_attribute_options_should_return_attributes_list() { $product = WC_Helper_Product::create_variation_product(); @@ -85,6 +97,9 @@ class WC_Tests_Template_Functions extends WC_Unit_Test_Case { ); } + /** + * Test: test_wc_dropdown_variation_attribute_options_should_return_attributes_list_and_selected_element. + */ public function test_wc_dropdown_variation_attribute_options_should_return_attributes_list_and_selected_element() { $product = WC_Helper_Product::create_variation_product(); $_REQUEST['attribute_pa_size'] = 'large'; From f69ac18ccd4f769599d5f043b7cd514cf9b888d8 Mon Sep 17 00:00:00 2001 From: nishitlangaliya Date: Thu, 7 Feb 2019 13:26:43 +0530 Subject: [PATCH 241/401] fix: default value passed to sorting dropdown --- includes/wc-template-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index c61a928bde7..9198ab01d84 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -1295,7 +1295,7 @@ if ( ! function_exists( 'woocommerce_catalog_ordering' ) ) { if ( ! wc_get_loop_prop( 'is_paginated' ) || ! woocommerce_products_will_display() ) { return; } - $show_default_orderby = 'menu_order' === apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) ); + $show_default_orderby = 'menu_order' === apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby', 'menu_order' ) ); $catalog_orderby_options = apply_filters( 'woocommerce_catalog_orderby', array( From c1d82478a6e31278442cf6fc85398b2af111bd69 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Thu, 7 Feb 2019 13:39:47 +0100 Subject: [PATCH 242/401] Added check for POST variable existance. --- includes/admin/meta-boxes/class-wc-meta-box-product-data.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php index f463f6af277..ce5e32efbfb 100644 --- a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php +++ b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php @@ -365,7 +365,7 @@ class WC_Meta_Box_Product_Data { 'backorders' => isset( $_POST['_backorders'] ) ? wc_clean( wp_unslash( $_POST['_backorders'] ) ) : null, 'stock_status' => wc_clean( wp_unslash( $_POST['_stock_status'] ) ), 'stock_quantity' => $stock, - 'low_stock_amount' => wc_stock_amount( wp_unslash( $_POST['_low_stock_amount'] ) ), + 'low_stock_amount' => isset( $_POST['_low_stock_amount'] ) ? wc_stock_amount( wp_unslash( $_POST['_low_stock_amount'] ) ) : null, 'download_limit' => '' === $_POST['_download_limit'] ? '' : absint( wp_unslash( $_POST['_download_limit'] ) ), 'download_expiry' => '' === $_POST['_download_expiry'] ? '' : absint( wp_unslash( $_POST['_download_expiry'] ) ), 'downloads' => self::prepare_downloads( From 113534a8827dc7b2ba4669a47d0760036d5142c6 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 7 Feb 2019 13:40:50 +0000 Subject: [PATCH 243/401] Set fallback false --- includes/class-wc-geolocation.php | 7 ++++++- includes/wc-core-functions.php | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-geolocation.php b/includes/class-wc-geolocation.php index eb23aeb7af0..d365cfaf47c 100644 --- a/includes/class-wc-geolocation.php +++ b/includes/class-wc-geolocation.php @@ -191,7 +191,7 @@ class WC_Geolocation { * @param bool $api_fallback If true, uses geolocation APIs if the database file doesn't exist (can be slower). * @return array */ - public static function geolocate_ip( $ip_address = '', $fallback = true, $api_fallback = true ) { + public static function geolocate_ip( $ip_address = '', $fallback = false, $api_fallback = true ) { // Filter to allow custom geolocation of the IP address. $country_code = apply_filters( 'woocommerce_geolocate_ip', false, $ip_address, $fallback, $api_fallback ); @@ -216,6 +216,11 @@ class WC_Geolocation { } else { $country_code = ''; } + + if ( ! $country_code && $fallback ) { + // May be a local environment - find external IP. + return self::geolocate_ip( self::get_external_ip_address(), false, $api_fallback ); + } } } diff --git a/includes/wc-core-functions.php b/includes/wc-core-functions.php index b45a15fa6dc..20bcef2afcb 100644 --- a/includes/wc-core-functions.php +++ b/includes/wc-core-functions.php @@ -1079,7 +1079,7 @@ function wc_get_customer_default_location() { $ua = strtolower( wc_get_user_agent() ); if ( ! strstr( $ua, 'bot' ) && ! strstr( $ua, 'spider' ) && ! strstr( $ua, 'crawl' ) ) { - $location = WC_Geolocation::geolocate_ip( '', true, false ); + $location = WC_Geolocation::geolocate_ip( '', false, false ); } // Base fallback. From dbd324abc35d392a4dc131c8d9072324679ed5da Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 7 Feb 2019 14:55:56 +0000 Subject: [PATCH 244/401] phpcs --- .../class-wc-gateway-paypal-request.php | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php index d22ece35102..05f705b373d 100644 --- a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php +++ b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php @@ -167,10 +167,12 @@ class WC_Gateway_Paypal_Request { } return apply_filters( - 'woocommerce_paypal_args', array_merge( + 'woocommerce_paypal_args', + array_merge( $this->get_transaction_args( $order ), $this->get_line_item_args( $order, true ) - ), $order + ), + $order ); } @@ -185,10 +187,12 @@ class WC_Gateway_Paypal_Request { WC_Gateway_Paypal::log( 'Generating payment form for order ' . $order->get_order_number() . '. Notify URL: ' . $this->notify_url ); $paypal_args = apply_filters( - 'woocommerce_paypal_args', array_merge( + 'woocommerce_paypal_args', + array_merge( $this->get_transaction_args( $order ), $this->get_line_item_args( $order ) - ), $order + ), + $order ); return $this->fix_request_length( $order, $paypal_args ); @@ -339,9 +343,10 @@ class WC_Gateway_Paypal_Request { foreach ( $order->get_items() as $item ) { $item_name = $item->get_name(); - $item_meta = strip_tags( + $item_meta = wp_strip_all_tags( wc_display_item_meta( - $item, array( + $item, + array( 'before' => '', 'separator' => ', ', 'after' => '', @@ -370,9 +375,10 @@ class WC_Gateway_Paypal_Request { */ protected function get_order_item_name( $order, $item ) { $item_name = $item->get_name(); - $item_meta = strip_tags( + $item_meta = wp_strip_all_tags( wc_display_item_meta( - $item, array( + $item, + array( 'before' => '', 'separator' => ', ', 'after' => '', @@ -444,12 +450,12 @@ class WC_Gateway_Paypal_Request { // Products. foreach ( $order->get_items( array( 'line_item', 'fee' ) ) as $item ) { if ( 'fee' === $item['type'] ) { - $item_line_total = $this->number_format( $item['line_total'], $order ); + $item_line_total = $this->number_format( $item['line_total'], $order ); $this->add_line_item( $item->get_name(), 1, $item_line_total ); } else { - $product = $item->get_product(); - $sku = $product ? $product->get_sku() : ''; - $item_line_total = $this->number_format( $order->get_item_subtotal( $item, false ), $order ); + $product = $item->get_product(); + $sku = $product ? $product->get_sku() : ''; + $item_line_total = $this->number_format( $order->get_item_subtotal( $item, false ), $order ); $this->add_line_item( $this->get_order_item_name( $order, $item ), $item->get_quantity(), $item_line_total, $sku ); } } @@ -467,12 +473,17 @@ class WC_Gateway_Paypal_Request { $index = ( count( $this->line_items ) / 4 ) + 1; $item = apply_filters( - 'woocommerce_paypal_line_item', array( + 'woocommerce_paypal_line_item', + array( 'item_name' => html_entity_decode( wc_trim_string( $item_name ? $item_name : __( 'Item', 'woocommerce' ), 127 ), ENT_NOQUOTES, 'UTF-8' ), 'quantity' => (int) $quantity, 'amount' => wc_float_to_string( (float) $amount ), 'item_number' => $item_number, - ), $item_name, $quantity, $amount, $item_number + ), + $item_name, + $quantity, + $amount, + $item_number ); $this->line_items[ 'item_name_' . $index ] = $this->limit_length( $item['item_name'], 127 ); From baf6f15baa12d0a1a3107b36f6c6e087963c7b96 Mon Sep 17 00:00:00 2001 From: claudiulodro Date: Thu, 7 Feb 2019 10:59:23 -0800 Subject: [PATCH 245/401] Add aria label for shop orderby --- templates/loop/orderby.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/loop/orderby.php b/templates/loop/orderby.php index 21ab02f7d74..503332b277a 100644 --- a/templates/loop/orderby.php +++ b/templates/loop/orderby.php @@ -12,7 +12,7 @@ * * @see https://docs.woocommerce.com/document/template-structure/ * @package WooCommerce/Templates - * @version 3.3.0 + * @version 3.6.0 */ if ( ! defined( 'ABSPATH' ) ) { @@ -21,7 +21,7 @@ if ( ! defined( 'ABSPATH' ) ) { ?>
    - $name ) : ?> From dca47d5d1453b5affbb6a80ce2d776ba3f0045e4 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 7 Feb 2019 19:09:41 +0000 Subject: [PATCH 246/401] Use mb functions for limiting string length --- .../includes/class-wc-gateway-paypal-request.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php index 05f705b373d..6cad3310a94 100644 --- a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php +++ b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php @@ -91,12 +91,15 @@ class WC_Gateway_Paypal_Request { * @return string */ protected function limit_length( $string, $limit = 127 ) { - // As the output is to be used in http_build_query which applies URL encoding, the string needs to be - // cut as if it was URL-encoded, but returned non-encoded (it will be encoded by http_build_query later). - $url_encoded_str = rawurlencode( $string ); - - if ( strlen( $url_encoded_str ) > $limit ) { - $string = rawurldecode( substr( $url_encoded_str, 0, $limit - 3 ) . '...' ); + $str_limit = $limit - 3; + if ( function_exists( 'mb_strimwidth' ) ) { + if ( mb_strlen( $string ) > $limit ) { + $string = mb_strimwidth( $string, 0, $str_limit ) . '...'; + } + } else { + if ( strlen( $string ) > $limit ) { + $string = substr( $string, 0, $str_limit ) . '...'; + } } return $string; } From 0e21b3f4ce3523592db9e8f4ac4a3a914db63d89 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 7 Feb 2019 20:35:45 +0000 Subject: [PATCH 247/401] Tests --- tests/unit-tests/gateways/paypal/request.php | 22 ++++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/tests/unit-tests/gateways/paypal/request.php b/tests/unit-tests/gateways/paypal/request.php index 384b86e4e22..833cdc3021c 100644 --- a/tests/unit-tests/gateways/paypal/request.php +++ b/tests/unit-tests/gateways/paypal/request.php @@ -4,6 +4,10 @@ * * @package WooCommerce\Tests\Gateways\Paypal */ + +/** + * Class WC_Tests_Paypal_Gateway_Request. + */ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { /** @@ -96,7 +100,7 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { * @param array $product_prices Array of prices to use for created products. Leave empty for default prices. * @param bool $calc_order_totals Whether the WC_Order::calculate_totals() should be triggered when creating order. * @return string - * @throws WC_Data_Exception + * @throws WC_Data_Exception Exception on failure. */ protected function get_request_url( $product_count, $testmode, $product_prices = array(), $calc_order_totals = true ) { // Create products. @@ -179,7 +183,7 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { * * @param bool $shipping_tax_included Whether the shipping tax should be included or not. * @param bool $testmode Whether to use Paypal sandbox. - * @throws WC_Data_Exception + * @throws WC_Data_Exception Exception on failure. */ protected function check_large_order( $shipping_tax_included, $testmode ) { $request_url = $this->get_request_url( 30, $testmode ); @@ -198,7 +202,7 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { $query_array = array(); parse_str( $query_string, $query_array ); foreach ( $fields_limited_to_127_chars as $field_name ) { - $this->assertLessThanOrEqual( 127, strlen( $query_array[ $field_name ] ) ); + $this->assertLessThanOrEqual( 127, mb_strlen( $query_array[ $field_name ] ) ); } // Check that there is actually only one item for order with URL length > limit. @@ -230,7 +234,7 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { * @param bool $testmode Whether to use Paypal sandbox. * @param array $product_prices Array of prices to use for created products. Leave empty for default prices. * @param bool $calc_order_totals Whether the WC_Order::calculate_totals() should be triggered when creating order. - * @throws WC_Data_Exception + * @throws WC_Data_Exception Exception on failure. */ protected function check_small_order( $product_count, $shipping_tax_included, $testmode, $product_prices = array(), $calc_order_totals = true ) { $request_url = $this->get_request_url( $product_count, $testmode, $product_prices, $calc_order_totals ); @@ -258,8 +262,8 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { $this->assertTrue( array_key_exists( 'cmd', $query_array ) ); $this->assertEquals( '_cart', $query_array['cmd'] ); - $this->assertLessThanOrEqual( 127, strlen( $query_array[ 'item_name_' . $i ] ) ); - $this->assertLessThanOrEqual( 127, strlen( $query_array[ 'item_number_' . $i ] ) ); + $this->assertLessThanOrEqual( 127, mb_strlen( $query_array[ 'item_name_' . $i ] ) ); + $this->assertLessThanOrEqual( 127, mb_strlen( $query_array[ 'item_number_' . $i ] ) ); } @@ -272,7 +276,7 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { * will return false. * * @param bool $testmode Whether PayPal request should point to sandbox or live production. - * @throws WC_Data_Exception + * @throws WC_Data_Exception Exception on failure. */ protected function check_negative_amount( $testmode ) { $shipping_tax_included = true; @@ -286,7 +290,7 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { * will return false. * * @param bool $testmode Whether PayPal request should point to sandbox or live production. - * @throws WC_Data_Exception + * @throws WC_Data_Exception Exception on failure. */ protected function check_totals_mismatch( $testmode ) { // totals mismatch forces tax inclusion and single line item. @@ -298,7 +302,7 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { * Test for request_url() method. * * @group timeout - * @throws WC_Data_Exception + * @throws WC_Data_Exception Exception on failure. */ public function test_request_url() { // User set up. From 8dc9a46c179dc147e953a601197a39707bea1450 Mon Sep 17 00:00:00 2001 From: Adrien Foulon <6115458+Tofandel@users.noreply.github.com> Date: Fri, 8 Feb 2019 02:02:44 +0100 Subject: [PATCH 248/401] The "for" attribute of a label for a radio input is invalid The main label of a radio supposedly targeting the first value of the radios contains only the value instead of theId_theValue which is the id of the radio --- includes/wc-template-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 8cdbcb0946f..56da57869d4 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -2723,7 +2723,7 @@ if ( ! function_exists( 'woocommerce_form_field' ) ) { break; case 'radio': - $label_id = current( array_keys( $args['options'] ) ); + $label_id .= '_' . current( array_keys( $args['options'] ) ); if ( ! empty( $args['options'] ) ) { foreach ( $args['options'] as $option_key => $option_text ) { From 73f283bf4a03ae217c414715bb3da34cb3198816 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 8 Feb 2019 10:35:01 +0000 Subject: [PATCH 249/401] Update CA address format --- includes/class-wc-countries.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-countries.php b/includes/class-wc-countries.php index 37d4395613b..de30d2092ed 100644 --- a/includes/class-wc-countries.php +++ b/includes/class-wc-countries.php @@ -453,7 +453,7 @@ class WC_Countries { 'AU' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}", 'AT' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}", 'BE' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}", - 'CA' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}", + 'CA' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {state_code}  {postcode}\n{country}", 'CH' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}", 'CL' => "{company}\n{name}\n{address_1}\n{address_2}\n{state}\n{postcode} {city}\n{country}", 'CN' => "{country} {postcode}\n{state}, {city}, {address_2}, {address_1}\n{company}\n{name}", From f6b1208a329d1f894e466fc5e08db64f87bfa87c Mon Sep 17 00:00:00 2001 From: Gerhard Date: Mon, 11 Feb 2019 09:42:31 +0200 Subject: [PATCH 250/401] Custom payment sections not loading fields due to logic error. --- .../admin/settings/class-wc-settings-payment-gateways.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/admin/settings/class-wc-settings-payment-gateways.php b/includes/admin/settings/class-wc-settings-payment-gateways.php index 62800001bea..7fa0983eed3 100644 --- a/includes/admin/settings/class-wc-settings-payment-gateways.php +++ b/includes/admin/settings/class-wc-settings-payment-gateways.php @@ -50,7 +50,8 @@ class WC_Settings_Payment_Gateways extends WC_Settings_Page { if ( '' === $current_section ) { $settings = apply_filters( - 'woocommerce_payment_gateways_settings', array( + 'woocommerce_payment_gateways_settings', + array( array( 'title' => __( 'Payment methods', 'woocommerce' ), 'desc' => __( 'Installed payment methods are listed below and can be sorted to control their display order on the frontend.', 'woocommerce' ), @@ -94,10 +95,9 @@ class WC_Settings_Payment_Gateways extends WC_Settings_Page { break; } } - } else { - $settings = $this->get_settings(); - WC_Admin_Settings::output_fields( $settings ); } + $settings = $this->get_settings( $current_section ); + WC_Admin_Settings::output_fields( $settings ); } /** From 2d746a99bb0de9dee43026201d809c40918d3771 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Mon, 11 Feb 2019 10:21:17 +0200 Subject: [PATCH 251/401] Use name instead of singular_name for archive pages breadcrumbs. singular_name should only be used on single pages. --- includes/class-wc-breadcrumb.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-breadcrumb.php b/includes/class-wc-breadcrumb.php index 142aa5a7fcd..2715c4f905c 100644 --- a/includes/class-wc-breadcrumb.php +++ b/includes/class-wc-breadcrumb.php @@ -240,7 +240,7 @@ class WC_Breadcrumb { if ( ! $_name ) { $product_post_type = get_post_type_object( 'product' ); - $_name = $product_post_type->labels->singular_name; + $_name = $product_post_type->labels->name; } $this->add_crumb( $_name, get_post_type_archive_link( 'product' ) ); @@ -253,7 +253,7 @@ class WC_Breadcrumb { $post_type = get_post_type_object( get_post_type() ); if ( $post_type ) { - $this->add_crumb( $post_type->labels->singular_name, get_post_type_archive_link( get_post_type() ) ); + $this->add_crumb( $post_type->labels->name, get_post_type_archive_link( get_post_type() ) ); } } From cd369192656b84dac462bbeb6e6a4209ecdbaf26 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Mon, 11 Feb 2019 10:22:30 +0200 Subject: [PATCH 252/401] PHPCS fixes --- includes/class-wc-breadcrumb.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-breadcrumb.php b/includes/class-wc-breadcrumb.php index 2715c4f905c..3e87c874d3b 100644 --- a/includes/class-wc-breadcrumb.php +++ b/includes/class-wc-breadcrumb.php @@ -148,8 +148,11 @@ class WC_Breadcrumb { $this->prepend_shop_page(); $terms = wc_get_product_terms( - $post->ID, 'product_cat', apply_filters( - 'woocommerce_breadcrumb_product_terms_args', array( + $post->ID, + 'product_cat', + apply_filters( + 'woocommerce_breadcrumb_product_terms_args', + array( 'orderby' => 'parent', 'order' => 'DESC', ) From b92cfc51a5fb07c63a86a3b22903eba38c47dd5f Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Mon, 11 Feb 2019 16:34:28 -0400 Subject: [PATCH 253/401] update product table to correctly calculate net product sales --- includes/admin/reports/class-wc-report-sales-by-product.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/includes/admin/reports/class-wc-report-sales-by-product.php b/includes/admin/reports/class-wc-report-sales-by-product.php index ad80e5ac338..69ebdc6e505 100644 --- a/includes/admin/reports/class-wc-report-sales-by-product.php +++ b/includes/admin/reports/class-wc-report-sales-by-product.php @@ -85,6 +85,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { ), 'query_type' => 'get_var', 'filter_range' => true, + 'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ), ) ); @@ -110,6 +111,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { ), 'query_type' => 'get_var', 'filter_range' => true, + 'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ), ) ) ); @@ -461,6 +463,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { 'order_by' => 'post_date ASC', 'query_type' => 'get_results', 'filter_range' => true, + 'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ), ) ); @@ -498,6 +501,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { 'order_by' => 'post_date ASC', 'query_type' => 'get_results', 'filter_range' => true, + 'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ), ) ); From aee5cf2bfff80141bbf1910b540fd87987070312 Mon Sep 17 00:00:00 2001 From: Andrew Lazarus Date: Tue, 12 Feb 2019 13:37:02 +0100 Subject: [PATCH 254/401] Added filter for product title classes within product loop --- includes/wc-template-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 4edfda70dd1..c67b13e1eb9 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -1082,7 +1082,7 @@ if ( ! function_exists( 'woocommerce_template_loop_product_title' ) ) { * Show the product title in the product loop. By default this is an H2. */ function woocommerce_template_loop_product_title() { - echo '

    ' . get_the_title() . '

    '; // phpcs:ignore + echo '

    ' . get_the_title() . '

    '; // phpcs:ignore } } if ( ! function_exists( 'woocommerce_template_loop_category_title' ) ) { From 2f13a048d9ed38421236054757e20607f63503c1 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Tue, 12 Feb 2019 16:08:03 -0200 Subject: [PATCH 255/401] Load REST API endpoints before generate webhook payload --- includes/class-wc-webhook.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/includes/class-wc-webhook.php b/includes/class-wc-webhook.php index 975d2d145f9..7d6a3c34a89 100644 --- a/includes/class-wc-webhook.php +++ b/includes/class-wc-webhook.php @@ -290,6 +290,11 @@ class WC_Webhook extends WC_Legacy_Webhook { $rest_api_versions = wc_get_webhook_rest_api_versions(); $version_suffix = end( $rest_api_versions ) !== $this->get_api_version() ? strtoupper( str_replace( 'wp_api', '', $this->get_api_version() ) ) : ''; + // Load REST API endpoints to generate payload. + if ( ! did_action( 'rest_api_init' ) ) { + WC()->api->rest_api_includes(); + } + switch ( $resource ) { case 'coupon': case 'customer': From 00651bbc76db2f6cf1abb9a66d85ead4f857bf95 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Tue, 12 Feb 2019 16:21:29 -0200 Subject: [PATCH 256/401] Fix special chars on webhook secret By default some values are encoded before saved into the database, so it's required to decode those chars before generate the webhook signature. Fixes #22500 --- includes/class-wc-webhook.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-webhook.php b/includes/class-wc-webhook.php index 975d2d145f9..5f929a1d38d 100644 --- a/includes/class-wc-webhook.php +++ b/includes/class-wc-webhook.php @@ -373,7 +373,7 @@ class WC_Webhook extends WC_Legacy_Webhook { public function generate_signature( $payload ) { $hash_algo = apply_filters( 'woocommerce_webhook_hash_algorithm', 'sha256', $payload, $this->get_id() ); - return base64_encode( hash_hmac( $hash_algo, $payload, $this->get_secret(), true ) ); + return base64_encode( hash_hmac( $hash_algo, $payload, wp_specialchars_decode( $this->get_secret(), ENT_QUOTES ), true ) ); } /** From 11c16f35abbd8323655325549318697e6fdfa177 Mon Sep 17 00:00:00 2001 From: Andrew Lazarus Date: Wed, 13 Feb 2019 15:07:07 +0100 Subject: [PATCH 257/401] Updated product loop title filter with esc_attr() --- includes/wc-template-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index c67b13e1eb9..a20869377bf 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -1082,7 +1082,7 @@ if ( ! function_exists( 'woocommerce_template_loop_product_title' ) ) { * Show the product title in the product loop. By default this is an H2. */ function woocommerce_template_loop_product_title() { - echo '

    ' . get_the_title() . '

    '; // phpcs:ignore + echo '

    ' . get_the_title() . '

    '; // phpcs:ignore } } if ( ! function_exists( 'woocommerce_template_loop_category_title' ) ) { From d0abd6f09ba545b5561cc85265a2fcdcf69cc6a1 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 13 Feb 2019 15:05:20 +0000 Subject: [PATCH 258/401] Check for _reduced_stock meta when restocking refunded items --- includes/wc-order-functions.php | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/includes/wc-order-functions.php b/includes/wc-order-functions.php index 0798dda5c18..3d311fbf293 100644 --- a/includes/wc-order-functions.php +++ b/includes/wc-order-functions.php @@ -689,17 +689,23 @@ function wc_restock_refunded_items( $order, $refunded_line_items ) { if ( ! isset( $refunded_line_items[ $item_id ], $refunded_line_items[ $item_id ]['qty'] ) ) { continue; } - $product = $item->get_product(); + $product = $item->get_product(); + $item_stock_reduced = $item->get_meta( '_reduced_stock', true ); - if ( $product && $product->managing_stock() ) { - $old_stock = $product->get_stock_quantity(); - $new_stock = wc_update_product_stock( $product, $refunded_line_items[ $item_id ]['qty'], 'increase' ); - - /* translators: 1: product ID 2: old stock level 3: new stock level */ - $order->add_order_note( sprintf( __( 'Item #%1$s stock increased from %2$s to %3$s.', 'woocommerce' ), $product->get_id(), $old_stock, $new_stock ) ); - - do_action( 'woocommerce_restock_refunded_item', $product->get_id(), $old_stock, $new_stock, $order, $product ); + if ( ! $item_stock_reduced || ! $product || ! $product->managing_stock() ) { + continue; } + + $old_stock = $product->get_stock_quantity(); + $new_stock = wc_update_product_stock( $product, $refunded_line_items[ $item_id ]['qty'], 'increase' ); + + /* translators: 1: product ID 2: old stock level 3: new stock level */ + $order->add_order_note( sprintf( __( 'Item #%1$s stock increased from %2$s to %3$s.', 'woocommerce' ), $product->get_id(), $old_stock, $new_stock ) ); + + $item->delete_meta_data( '_reduced_stock' ); + $item->save(); + + do_action( 'woocommerce_restock_refunded_item', $product->get_id(), $old_stock, $new_stock, $order, $product ); } } From cf0e9f925cad5322ed8182692e315ec6cb9b7ad3 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 13 Feb 2019 16:20:09 +0000 Subject: [PATCH 259/401] Refactor should_deliver logic into managable chunks --- includes/class-wc-webhook.php | 170 ++++++++++++++++++++++++---------- 1 file changed, 120 insertions(+), 50 deletions(-) diff --git a/includes/class-wc-webhook.php b/includes/class-wc-webhook.php index 9170415044c..1cdd57aaa9a 100644 --- a/includes/class-wc-webhook.php +++ b/includes/class-wc-webhook.php @@ -123,62 +123,132 @@ class WC_Webhook extends WC_Legacy_Webhook { * @return bool True if webhook should be delivered, false otherwise. */ private function should_deliver( $arg ) { - $should_deliver = true; - $current_action = current_action(); + $should_deliver = $this->is_active() && $this->is_valid_topic() && $this->is_valid_action( $arg ); - // Only active webhooks can be delivered. - if ( 'active' !== $this->get_status() ) { - $should_deliver = false; - } elseif ( in_array( $current_action, array( 'delete_post', 'wp_trash_post', 'untrashed_post' ), true ) ) { - // Only deliver deleted/restored event for coupons, orders, and products. - if ( isset( $GLOBALS['post_type'] ) && ! in_array( $GLOBALS['post_type'], array( 'shop_coupon', 'shop_order', 'product' ), true ) ) { - $should_deliver = false; - } - - // Check if is delivering for the correct resource. - if ( isset( $GLOBALS['post_type'] ) && str_replace( 'shop_', '', $GLOBALS['post_type'] ) !== $this->get_resource() ) { - $should_deliver = false; - } - } elseif ( 'delete_user' === $current_action ) { - $user = get_userdata( absint( $arg ) ); - - // Only deliver deleted customer event for users with customer role. - if ( ! $user || ! in_array( 'customer', (array) $user->roles, true ) ) { - $should_deliver = false; - } - } elseif ( 'order' === $this->get_resource() && ! in_array( get_post_type( absint( $arg ) ), wc_get_order_types( 'order-webhooks' ), true ) ) { - // Only if the custom order type has chosen to exclude order webhooks from triggering along with its own webhooks. - $should_deliver = false; - - } elseif ( 0 === strpos( $current_action, 'woocommerce_process_shop' ) || 0 === strpos( $current_action, 'woocommerce_process_product' ) ) { - // The `woocommerce_process_shop_*` and `woocommerce_process_product_*` hooks - // fire for create and update of products and orders, so check the post - // creation date to determine the actual event. - $resource = get_post( absint( $arg ) ); - - // Drafts don't have post_date_gmt so calculate it here. - $gmt_date = get_gmt_from_date( $resource->post_date ); - - // A resource is considered created when the hook is executed within 10 seconds of the post creation date. - $resource_created = ( ( time() - 10 ) <= strtotime( $gmt_date ) ); - - if ( 'created' === $this->get_event() && ! $resource_created ) { - $should_deliver = false; - } elseif ( 'updated' === $this->get_event() && $resource_created ) { - $should_deliver = false; - } - } - - if ( ! wc_is_webhook_valid_topic( $this->get_topic() ) ) { - $should_deliver = false; - } - - /* + /** * Let other plugins intercept deliver for some messages queue like rabbit/zeromq. + * + * @param bool $should_deliver True if the webhook should be sent, or false to not send it. + * @param WC_Webhook $this The current webhook class. + * @param mixed $arg First hook argument. */ return apply_filters( 'woocommerce_webhook_should_deliver', $should_deliver, $this, $arg ); } + /** + * Returns if webhook is active. + * + * @since 3.6.0 + * @return bool True if validation passes. + */ + private function is_active() { + return 'active' === $this->get_status(); + } + + /** + * Returns if topic is valid. + * + * @since 3.6.0 + * @return bool True if validation passes. + */ + private function is_valid_topic() { + return wc_is_webhook_valid_topic( $this->get_topic() ); + } + + /** + * Validates the criteria for certain actions. + * + * @since 3.6.0 + * @param mixed $arg First hook argument. + * @return bool True if validation passes. + */ + private function is_valid_action( $arg ) { + $current_action = current_action(); + $return = true; + + switch ( $current_action ) { + case 'delete_post': + case 'wp_trash_post': + case 'untrashed_post': + $return = $this->is_valid_post_action( $arg ); + break; + case 'delete_user': + $return = $this->is_valid_user_action( $arg ); + break; + } + + if ( 0 === strpos( $current_action, 'woocommerce_process_shop' ) || 0 === strpos( $current_action, 'woocommerce_process_product' ) ) { + $return = $this->is_valid_processing_action( $arg ); + } + + return $return; + } + + /** + * Validates post actions. + * + * @since 3.6.0 + * @param mixed $arg First hook argument. + * @return bool True if validation passes. + */ + private function is_valid_post_action( $arg ) { + // Only deliver deleted/restored event for coupons, orders, and products. + if ( isset( $GLOBALS['post_type'] ) && ! in_array( $GLOBALS['post_type'], array( 'shop_coupon', 'shop_order', 'product' ), true ) ) { + return false; + } + + // Check if is delivering for the correct resource. + if ( isset( $GLOBALS['post_type'] ) && str_replace( 'shop_', '', $GLOBALS['post_type'] ) !== $this->get_resource() ) { + return false; + } + return true; + } + + /** + * Validates user actions. + * + * @since 3.6.0 + * @param mixed $arg First hook argument. + * @return bool True if validation passes. + */ + private function is_valid_user_action( $arg ) { + $user = get_userdata( absint( $arg ) ); + + // Only deliver deleted customer event for users with customer role. + if ( ! $user || ! in_array( 'customer', (array) $user->roles, true ) ) { + return false; + } + + return true; + } + + /** + * Validates WC processing actions. + * + * @since 3.6.0 + * @param mixed $arg First hook argument. + * @return bool True if validation passes. + */ + private function is_valid_processing_action( $arg ) { + // The `woocommerce_process_shop_*` and `woocommerce_process_product_*` hooks + // fire for create and update of products and orders, so check the post + // creation date to determine the actual event. + $resource = get_post( absint( $arg ) ); + + // Drafts don't have post_date_gmt so calculate it here. + $gmt_date = get_gmt_from_date( $resource->post_date ); + + // A resource is considered created when the hook is executed within 10 seconds of the post creation date. + $resource_created = ( ( time() - 10 ) <= strtotime( $gmt_date ) ); + + if ( 'created' === $this->get_event() && ! $resource_created ) { + return false; + } elseif ( 'updated' === $this->get_event() && $resource_created ) { + return false; + } + return true; + } + /** * Deliver the webhook payload using wp_safe_remote_request(). * From 311449e943478348b1105f99d99471b3245f349b Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 13 Feb 2019 16:46:49 +0000 Subject: [PATCH 260/401] Create is_valid_resource method to check for invalid statuses --- includes/class-wc-webhook.php | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-webhook.php b/includes/class-wc-webhook.php index 1cdd57aaa9a..5747ecfcefe 100644 --- a/includes/class-wc-webhook.php +++ b/includes/class-wc-webhook.php @@ -123,7 +123,7 @@ class WC_Webhook extends WC_Legacy_Webhook { * @return bool True if webhook should be delivered, false otherwise. */ private function should_deliver( $arg ) { - $should_deliver = $this->is_active() && $this->is_valid_topic() && $this->is_valid_action( $arg ); + $should_deliver = $this->is_active() && $this->is_valid_topic() && $this->is_valid_action( $arg ) && $this->is_valid_resource( $arg ); /** * Let other plugins intercept deliver for some messages queue like rabbit/zeromq. @@ -249,6 +249,32 @@ class WC_Webhook extends WC_Legacy_Webhook { return true; } + /** + * Checks the resource for this webhook is valid e.g. valid post status. + * + * @since 3.6.0 + * @param mixed $arg First hook argument. + * @return bool True if validation passes. + */ + private function is_valid_resource( $arg ) { + $resource = $this->get_resource(); + + if ( in_array( $resource, array( 'order', 'product', 'coupon' ), true ) ) { + $status = get_post_status( absint( $arg ) ); + + // Ignore auto drafts for all resources. + if ( in_array( $status, array( 'auto-draft', 'wc-auto-draft', 'new' ), true ) ) { + return false; + } + + // Ignore standard drafts for orders. + if ( 'order' === $resource && 'draft' === $status ) { + return false; + } + } + return true; + } + /** * Deliver the webhook payload using wp_safe_remote_request(). * From ae17d6f3bdbe4f55b5d9f569dc9624bcca588618 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 13 Feb 2019 17:52:28 +0000 Subject: [PATCH 261/401] Avoid handling wc-auto-draft WC was erroneously adding wc- prefix to the core WP auto-draft status. #22380 registered it formally but we don't need it. I've reverted #22380 and handled the prefix correctly. --- includes/class-wc-post-types.php | 8 -------- .../abstract-wc-order-data-store-cpt.php | 14 ++++++++++---- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/includes/class-wc-post-types.php b/includes/class-wc-post-types.php index 213cfc4992c..616b3292bb3 100644 --- a/includes/class-wc-post-types.php +++ b/includes/class-wc-post-types.php @@ -484,14 +484,6 @@ class WC_Post_Types { $order_statuses = apply_filters( 'woocommerce_register_shop_order_post_statuses', array( - 'wc-auto-draft' => array( - 'public' => false, - 'exclude_from_search' => false, - 'show_in_admin_all_list' => true, - 'show_in_admin_status_list' => true, - /* translators: %s: number of orders */ - 'label_count' => _n_noop( 'Draft (%s)', 'Draft (%s)', 'woocommerce' ), - ), 'wc-pending' => array( 'label' => _x( 'Pending payment', 'Order status', 'woocommerce' ), 'public' => false, 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 adcb7406d9c..d28a23b6d06 100644 --- a/includes/data-stores/abstract-wc-order-data-store-cpt.php +++ b/includes/data-stores/abstract-wc-order-data-store-cpt.php @@ -58,6 +58,9 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme $order->set_date_created( current_time( 'timestamp', true ) ); $order->set_currency( $order->get_currency() ? $order->get_currency() : get_woocommerce_currency() ); + $raw_status = $order->get_status( 'edit' ) ? $order->get_status( 'edit' ) : apply_filters( 'woocommerce_default_order_status', 'pending' ); + $status = wc_is_order_status( 'wc-' . $raw_status ) ? 'wc-' . $raw_status : $raw_status; + $id = wp_insert_post( apply_filters( 'woocommerce_new_order_data', @@ -65,7 +68,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme '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() ), 'post_type' => $order->get_type( 'edit' ), - 'post_status' => 'wc-' . ( $order->get_status( 'edit' ) ? $order->get_status( 'edit' ) : apply_filters( 'woocommerce_default_order_status', 'pending' ) ), + 'post_status' => $status, 'ping_status' => 'closed', 'post_author' => 1, 'post_title' => $this->get_post_title(), @@ -73,7 +76,8 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme 'post_parent' => $order->get_parent_id( 'edit' ), 'post_excerpt' => $this->get_post_excerpt( $order ), ) - ), true + ), + true ); if ( $id && ! is_wp_error( $id ) ) { @@ -119,7 +123,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme * stored. @todo When meta is flattened, handle this during migration. */ if ( version_compare( $order->get_version( 'edit' ), '2.3.7', '<' ) && $order->get_prices_include_tax( 'edit' ) ) { - $order->set_discount_total( (double) get_post_meta( $order->get_id(), '_cart_discount', true ) - (double) get_post_meta( $order->get_id(), '_cart_discount_tax', true ) ); + $order->set_discount_total( (float) get_post_meta( $order->get_id(), '_cart_discount', true ) - (float) get_post_meta( $order->get_id(), '_cart_discount_tax', true ) ); } } @@ -140,10 +144,12 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme // 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 ) ) ) { + $raw_status = $order->get_status( 'edit' ) ? $order->get_status( 'edit' ) : apply_filters( 'woocommerce_default_order_status', 'pending' ); + $status = wc_is_order_status( 'wc-' . $raw_status ) ? 'wc-' . $raw_status : $raw_status; $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() ), - 'post_status' => 'wc-' . ( $order->get_status( 'edit' ) ? $order->get_status( 'edit' ) : apply_filters( 'woocommerce_default_order_status', 'pending' ) ), + 'post_status' => $status, 'post_parent' => $order->get_parent_id(), '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' ), From 13612ef3f1ce15a6e05ba3c7fa2c3e7b0edbebda Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 13 Feb 2019 17:55:30 +0000 Subject: [PATCH 262/401] Update CRUD update hook based on status transition --- .../class-wc-order-data-store-cpt.php | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) 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 1f4dee00ad0..a73d10f27c9 100644 --- a/includes/data-stores/class-wc-order-data-store-cpt.php +++ b/includes/data-stores/class-wc-order-data-store-cpt.php @@ -156,10 +156,20 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement $order->set_date_paid( $order->get_date_created( 'edit' ) ); } + // Also grab the current status so we can compare. + $previous_status = get_post_status( $order->get_id() ); + // Update the order. parent::update( $order ); - do_action( 'woocommerce_update_order', $order->get_id() ); + // Fire a hook depending on the status - this should be considered a creation if it was previously draft status. + $new_status = $order->get_status( 'edit' ); + + if ( $new_status !== $previous_status && in_array( $previous_status, array( 'new', 'auto-draft', 'draft' ), true ) ) { + do_action( 'woocommerce_new_order', $order->get_id() ); + } else { + do_action( 'woocommerce_update_order', $order->get_id() ); + } } /** @@ -482,8 +492,10 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement * @var array */ $search_fields = array_map( - 'wc_clean', apply_filters( - 'woocommerce_shop_order_search_fields', array( + 'wc_clean', + apply_filters( + 'woocommerce_shop_order_search_fields', + array( '_billing_address_index', '_shipping_address_index', '_billing_last_name', From 588b5903f7845b90a9debbb05003d25c2ebe9bbc Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 13 Feb 2019 17:55:47 +0000 Subject: [PATCH 263/401] Ignore old pre-crud actions --- includes/class-wc-webhook.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/includes/class-wc-webhook.php b/includes/class-wc-webhook.php index 5747ecfcefe..ddb3fa1bc93 100644 --- a/includes/class-wc-webhook.php +++ b/includes/class-wc-webhook.php @@ -263,7 +263,7 @@ class WC_Webhook extends WC_Legacy_Webhook { $status = get_post_status( absint( $arg ) ); // Ignore auto drafts for all resources. - if ( in_array( $status, array( 'auto-draft', 'wc-auto-draft', 'new' ), true ) ) { + if ( in_array( $status, array( 'auto-draft', 'new' ), true ) ) { return false; } @@ -964,11 +964,9 @@ class WC_Webhook extends WC_Legacy_Webhook { 'delete_user', ), 'order.created' => array( - 'woocommerce_process_shop_order_meta', 'woocommerce_new_order', ), 'order.updated' => array( - 'woocommerce_process_shop_order_meta', 'woocommerce_update_order', 'woocommerce_order_refunded', ), From 84299ff5ed361650b88cb6071a106dbe20824e7b Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 13 Feb 2019 17:56:41 +0000 Subject: [PATCH 264/401] Update the tests --- tests/unit-tests/webhooks/crud.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit-tests/webhooks/crud.php b/tests/unit-tests/webhooks/crud.php index 46d5b6fc867..7e7c733e15f 100644 --- a/tests/unit-tests/webhooks/crud.php +++ b/tests/unit-tests/webhooks/crud.php @@ -147,7 +147,6 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { $object = new WC_Webhook(); $object->set_topic( 'order.created' ); $expected = array( - 'woocommerce_process_shop_order_meta', 'woocommerce_new_order', ); $this->assertEquals( $expected, $object->get_hooks() ); From 0b07779e60d0724dbcabe70f9bd46e2a3e1fff04 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 14 Feb 2019 11:27:01 +0000 Subject: [PATCH 265/401] Update dependency lint-staged to v8.1.4 --- package-lock.json | 20 ++++++++++---------- package.json | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0750db45b14..4989aedf777 100644 --- a/package-lock.json +++ b/package-lock.json @@ -433,9 +433,9 @@ "dev": true }, "ansi-escapes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", "dev": true }, "ansi-regex": { @@ -6132,9 +6132,9 @@ } }, "lint-staged": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.1.3.tgz", - "integrity": "sha512-6TGkikL1B+6mIOuSNq2TV6oP21IhPMnV8q0cf9oYZ296ArTVNcbFh1l1pfVOHHbBIYLlziWNsQ2q45/ffmJ4AA==", + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.1.4.tgz", + "integrity": "sha512-oFbbhB/VzN8B3i/sIdb9gMfngGArI6jIfxSn+WPdQb2Ni3GJeS6T4j5VriSbQfxfMuYoQlMHOoFt+lfcWV0HfA==", "dev": true, "requires": { "@iamstarkov/listr-update-renderer": "0.4.1", @@ -6150,7 +6150,7 @@ "is-glob": "^4.0.0", "is-windows": "^1.0.2", "listr": "^0.14.2", - "lodash": "^4.17.5", + "lodash": "^4.17.11", "log-symbols": "^2.2.0", "micromatch": "^3.1.8", "npm-which": "^3.0.1", @@ -9259,9 +9259,9 @@ "dev": true }, "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", + "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", "dev": true, "requires": { "tslib": "^1.9.0" diff --git a/package.json b/package.json index 5c7cbf9df70..42b918f9adf 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "grunt-wp-i18n": "1.0.3", "husky": "1.3.1", "istanbul": "1.0.0-alpha.2", - "lint-staged": "8.1.3", + "lint-staged": "8.1.4", "mocha": "5.2.0", "node-sass": "4.11.0", "prettier": "github:automattic/calypso-prettier#c56b4251", From 39d9e4bd6f5ea12bd0e27db0d9734735154518ab Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 14 Feb 2019 14:45:26 +0000 Subject: [PATCH 266/401] If date paid is not set but payment complete status has passed, set it anyway Ref: #22688 --- includes/class-wc-order.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-order.php b/includes/class-wc-order.php index ae3dc4a81ac..83ec19d4068 100644 --- a/includes/class-wc-order.php +++ b/includes/class-wc-order.php @@ -287,8 +287,18 @@ class WC_Order extends WC_Abstract_Order { * @since 3.0.0 */ public function maybe_set_date_paid() { - if ( ! $this->get_date_paid( 'edit' ) && $this->has_status( apply_filters( 'woocommerce_payment_complete_order_status', $this->needs_processing() ? 'processing' : 'completed', $this->get_id(), $this ) ) ) { - $this->set_date_paid( current_time( 'timestamp', true ) ); + // This logic only runs if the date_paid prop has not been set yet. + if ( ! $this->get_date_paid( 'edit' ) ) { + $payment_completed_status = apply_filters( 'woocommerce_payment_complete_order_status', $this->needs_processing() ? 'processing' : 'completed', $this->get_id(), $this ); + + if ( $this->has_status( $payment_completed_status ) ) { + // If payment complete status is reached, set paid now. + $this->set_date_paid( current_time( 'timestamp', true ) ); + + } elseif ( 'processing' === $payment_completed_status && $this->has_status( 'completed' ) ) { + // If payment complete status was processing, but we've passed that and still have no date, set it now. + $this->set_date_paid( current_time( 'timestamp', true ) ); + } } } From 621229b89425dd47b0a3637ecf1bd5b761d18e66 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 14 Feb 2019 15:05:10 +0000 Subject: [PATCH 267/401] Variations cannot be drafts - set to private. --- includes/import/abstract-wc-product-importer.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/includes/import/abstract-wc-product-importer.php b/includes/import/abstract-wc-product-importer.php index 5fbfcf504b7..96a4453a362 100644 --- a/includes/import/abstract-wc-product-importer.php +++ b/includes/import/abstract-wc-product-importer.php @@ -236,6 +236,12 @@ abstract class WC_Product_Importer implements WC_Importer_Interface { unset( $data['manage_stock'], $data['stock_status'], $data['backorders'], $data['low_stock_amount'] ); } + if ( 'variation' === $object->get_type() ) { + if ( isset( $data['status'] ) && -1 === $data['status'] ) { + $data['status'] = 0; // Variations cannot be drafts - set to private. + } + } + if ( 'importing' === $object->get_status() ) { $object->set_status( 'publish' ); $object->set_slug( '' ); From 8c2e30582360bea34ce019d92126fea31d242024 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 14 Feb 2019 15:05:56 +0000 Subject: [PATCH 268/401] phpcs --- .../import/abstract-wc-product-importer.php | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/includes/import/abstract-wc-product-importer.php b/includes/import/abstract-wc-product-importer.php index 96a4453a362..8734a1ed978 100644 --- a/includes/import/abstract-wc-product-importer.php +++ b/includes/import/abstract-wc-product-importer.php @@ -650,13 +650,15 @@ abstract class WC_Product_Importer implements WC_Importer_Interface { } // If the attribute does not exist, create it. - $attribute_id = wc_create_attribute( array( - 'name' => $raw_name, - 'slug' => $attribute_name, - 'type' => 'select', - 'order_by' => 'menu_order', - 'has_archives' => false, - ) ); + $attribute_id = wc_create_attribute( + array( + 'name' => $raw_name, + 'slug' => $attribute_name, + 'type' => 'select', + 'order_by' => 'menu_order', + 'has_archives' => false, + ) + ); if ( is_wp_error( $attribute_id ) ) { throw new Exception( $attribute_id->get_error_message(), 400 ); @@ -667,15 +669,18 @@ abstract class WC_Product_Importer implements WC_Importer_Interface { register_taxonomy( $taxonomy_name, apply_filters( 'woocommerce_taxonomy_objects_' . $taxonomy_name, array( 'product' ) ), - apply_filters( 'woocommerce_taxonomy_args_' . $taxonomy_name, array( - 'labels' => array( - 'name' => $raw_name, - ), - 'hierarchical' => true, - 'show_ui' => false, - 'query_var' => true, - 'rewrite' => false, - ) ) + apply_filters( + 'woocommerce_taxonomy_args_' . $taxonomy_name, + array( + 'labels' => array( + 'name' => $raw_name, + ), + 'hierarchical' => true, + 'show_ui' => false, + 'query_var' => true, + 'rewrite' => false, + ) + ) ); // Set product attributes global. From c62c1e1d0594fa329d0b4e1f04e50a5711395309 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 14 Feb 2019 15:22:36 +0000 Subject: [PATCH 269/401] Fix save logic by moving setter into loop grabbing the value Broken by https://github.com/woocommerce/woocommerce/pull/22650 phpcs changes --- includes/class-wc-form-handler.php | 66 +++++++++++++++++------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/includes/class-wc-form-handler.php b/includes/class-wc-form-handler.php index f81dce02b60..5f1bc20c35a 100644 --- a/includes/class-wc-form-handler.php +++ b/includes/class-wc-form-handler.php @@ -81,6 +81,12 @@ class WC_Form_Handler { return; } + $customer = new WC_Customer( $user_id ); + + if ( ! $customer ) { + return; + } + $load_address = isset( $wp->query_vars['edit-address'] ) ? wc_edit_address_i18n( sanitize_title( $wp->query_vars['edit-address'] ), true ) : 'billing'; if ( ! isset( $_POST[ $load_address . '_country' ] ) ) { @@ -111,8 +117,7 @@ class WC_Form_Handler { } if ( ! empty( $value ) ) { - - // Validation rules. + // Validation and formatting rules. if ( ! empty( $field['validate'] ) && is_array( $field['validate'] ) ) { foreach ( $field['validate'] as $rule ) { switch ( $rule ) { @@ -143,36 +148,39 @@ class WC_Form_Handler { } } } - } - do_action( 'woocommerce_after_save_address_validation', $user_id, $load_address, $address ); - - if ( 0 === wc_notice_count( 'error' ) ) { - - $customer = new WC_Customer( $user_id ); - - if ( $customer ) { - foreach ( $address as $key => $field ) { - if ( is_callable( array( $customer, "set_$key" ) ) ) { - $customer->{"set_$key"}( $value ); - } else { - $customer->update_meta_data( $key, $value ); - } - - if ( WC()->customer && is_callable( array( WC()->customer, "set_$key" ) ) ) { - WC()->customer->{"set_$key"}( $value ); - } - } - $customer->save(); + // Set prop in customer object. + if ( is_callable( array( $customer, "set_$key" ) ) ) { + $customer->{"set_$key"}( $value ); + } else { + $customer->update_meta_data( $key, $value ); } - - wc_add_notice( __( 'Address changed successfully.', 'woocommerce' ) ); - - do_action( 'woocommerce_customer_save_address', $user_id, $load_address ); - - wp_safe_redirect( wc_get_endpoint_url( 'edit-address', '', wc_get_page_permalink( 'myaccount' ) ) ); - exit; } + + /** + * Hook: woocommerce_after_save_address_validation. + * + * Allow developers to add custom validation logic and throw an error to prevent save. + * + * @param int $user_id User ID being saved. + * @param string $load_address Type of address e.g. billing or shipping. + * @param array $address The address fields. + * @param WC_Customer $customer The customer object being saved. @since 3.6.0 + */ + do_action( 'woocommerce_after_save_address_validation', $user_id, $load_address, $address, $customer ); + + if ( 0 < wc_notice_count( 'error' ) ) { + return; + } + + $customer->save(); + + wc_add_notice( __( 'Address changed successfully.', 'woocommerce' ) ); + + do_action( 'woocommerce_customer_save_address', $user_id, $load_address ); + + wp_safe_redirect( wc_get_endpoint_url( 'edit-address', '', wc_get_page_permalink( 'myaccount' ) ) ); + exit; } /** From 2a0ba812a351f21dccdfa26f445f77597541e12c Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 14 Feb 2019 15:58:12 +0000 Subject: [PATCH 270/401] Check for decoded taxonomy name when unsetting --- includes/data-stores/class-wc-product-data-store-cpt.php | 3 +++ includes/widgets/class-wc-widget-layered-nav.php | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index 8ccd8b10f3f..f80de361903 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -748,6 +748,9 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da if ( taxonomy_exists( $attribute_key ) ) { // Handle attributes that have been unset. wp_set_object_terms( $product->get_id(), array(), $attribute_key ); + } elseif ( taxonomy_exists( urldecode( $attribute_key ) ) ) { + // Handle attributes that have been unset. + wp_set_object_terms( $product->get_id(), array(), urldecode( $attribute_key ) ); } continue; diff --git a/includes/widgets/class-wc-widget-layered-nav.php b/includes/widgets/class-wc-widget-layered-nav.php index 6598aefee40..10622670271 100644 --- a/includes/widgets/class-wc-widget-layered-nav.php +++ b/includes/widgets/class-wc-widget-layered-nav.php @@ -417,7 +417,7 @@ class WC_Widget_Layered_Nav extends WC_Widget { $query_hash = md5( $query ); // Maybe store a transient of the count values. - $cache = apply_filters( 'woocommerce_layered_nav_count_maybe_cache', true ); + $cache = false;//apply_filters( 'woocommerce_layered_nav_count_maybe_cache', true ); if ( true === $cache ) { $cached_counts = (array) get_transient( 'wc_layered_nav_counts_' . sanitize_title( $taxonomy ) ); } else { From 46d5a4c596a150d1de237f4b4646915e389e61f2 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 14 Feb 2019 15:58:53 +0000 Subject: [PATCH 271/401] Revert debug code --- includes/widgets/class-wc-widget-layered-nav.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/widgets/class-wc-widget-layered-nav.php b/includes/widgets/class-wc-widget-layered-nav.php index 10622670271..6598aefee40 100644 --- a/includes/widgets/class-wc-widget-layered-nav.php +++ b/includes/widgets/class-wc-widget-layered-nav.php @@ -417,7 +417,7 @@ class WC_Widget_Layered_Nav extends WC_Widget { $query_hash = md5( $query ); // Maybe store a transient of the count values. - $cache = false;//apply_filters( 'woocommerce_layered_nav_count_maybe_cache', true ); + $cache = apply_filters( 'woocommerce_layered_nav_count_maybe_cache', true ); if ( true === $cache ) { $cached_counts = (array) get_transient( 'wc_layered_nav_counts_' . sanitize_title( $taxonomy ) ); } else { From 6e24b85c6baf4b6240854387aa1213f5a15c8149 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Thu, 14 Feb 2019 17:03:53 +0100 Subject: [PATCH 272/401] Put back status parameter after extra processing. This allows correct further processing of $request, e.g. for next/previous links, etc. --- includes/api/class-wc-rest-orders-controller.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/includes/api/class-wc-rest-orders-controller.php b/includes/api/class-wc-rest-orders-controller.php index f348b59f8cb..62b86306d53 100644 --- a/includes/api/class-wc-rest-orders-controller.php +++ b/includes/api/class-wc-rest-orders-controller.php @@ -210,7 +210,7 @@ class WC_REST_Orders_Controller extends WC_REST_Orders_V2_Controller { * @return array */ protected function prepare_objects_query( $request ) { - // This is needed to get around an array to string notice in WC_REST_Orders_Controller::prepare_objects_query. + // This is needed to get around an array to string notice in WC_REST_Orders_V2_Controller::prepare_objects_query. $statuses = $request['status']; unset( $request['status'] ); $args = parent::prepare_objects_query( $request ); @@ -228,6 +228,9 @@ class WC_REST_Orders_Controller extends WC_REST_Orders_V2_Controller { } } + // Put the statuses back for further processing (next/prev links, etc). + $request['status'] = $statuses; + return $args; } From 07a8183f2a96a02842edb195b2ffdd78a44512cc Mon Sep 17 00:00:00 2001 From: Andrew Lazarus Date: Fri, 15 Feb 2019 09:49:29 +0100 Subject: [PATCH 273/401] Hard coded class now filterable Ability to add library classes to price instead of overwriting template --- templates/single-product/price.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/single-product/price.php b/templates/single-product/price.php index a94e2d1536e..3040f81fd3b 100644 --- a/templates/single-product/price.php +++ b/templates/single-product/price.php @@ -22,4 +22,4 @@ if ( ! defined( 'ABSPATH' ) ) { global $product; ?> -

    get_price_html(); ?>

    +

    get_price_html(); ?>

    From be05eea5da5326661383b18905e6d6ce9ea86bbd Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 15 Feb 2019 17:09:45 +0000 Subject: [PATCH 274/401] phpcs all the things --- includes/class-wc-ajax.php | 728 +++++++++++++++++++++---------------- 1 file changed, 409 insertions(+), 319 deletions(-) diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php index 07036735d26..6055fc846ea 100644 --- a/includes/class-wc-ajax.php +++ b/includes/class-wc-ajax.php @@ -37,14 +37,16 @@ class WC_AJAX { * Set WC AJAX constant and headers. */ public static function define_ajax() { + // phpcs:disable if ( ! empty( $_GET['wc-ajax'] ) ) { wc_maybe_define_constant( 'DOING_AJAX', true ); wc_maybe_define_constant( 'WC_DOING_AJAX', true ); if ( ! WP_DEBUG || ( WP_DEBUG && ! WP_DEBUG_DISPLAY ) ) { - @ini_set( 'display_errors', 0 ); // Turn off display_errors during AJAX events to prevent malformed JSON. + @ini_set( 'display_errors', 0 ); // Turn off display_errors during AJAX events to prevent malformed JSON. phpcs } $GLOBALS['wpdb']->hide_errors(); } + // phpcs:enable } /** @@ -54,8 +56,8 @@ class WC_AJAX { */ private static function wc_ajax_headers() { send_origin_headers(); - @header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) ); - @header( 'X-Robots-Tag: noindex' ); + header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) ); + header( 'X-Robots-Tag: noindex' ); send_nosniff_header(); wc_nocache_headers(); status_header( 200 ); @@ -67,6 +69,7 @@ class WC_AJAX { public static function do_wc_ajax() { global $wp_query; + // phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification if ( ! empty( $_GET['wc-ajax'] ) ) { $wp_query->set( 'wc-ajax', sanitize_text_field( wp_unslash( $_GET['wc-ajax'] ) ) ); } @@ -79,87 +82,93 @@ class WC_AJAX { do_action( 'wc_ajax_' . $action ); wp_die(); } + // phpcs:enable } /** * Hook in methods - uses WordPress ajax handlers (admin-ajax). + * + * Format: woocommerce_EVENT => value nopriv. */ public static function add_ajax_events() { - // woocommerce_EVENT => nopriv. - $ajax_events = array( - 'get_refreshed_fragments' => true, - 'apply_coupon' => true, - 'remove_coupon' => true, - 'update_shipping_method' => true, - 'get_cart_totals' => true, - 'update_order_review' => true, - 'add_to_cart' => true, - 'remove_from_cart' => true, - 'checkout' => true, - 'get_variation' => true, - 'get_customer_location' => true, - 'feature_product' => false, - 'mark_order_status' => false, - 'get_order_details' => false, - 'add_attribute' => false, - 'add_new_attribute' => false, - 'remove_variation' => false, - 'remove_variations' => false, - 'save_attributes' => false, - 'add_variation' => false, - 'link_all_variations' => false, - 'revoke_access_to_download' => false, - 'grant_access_to_download' => false, - 'get_customer_details' => false, - 'add_order_item' => false, - 'add_order_fee' => false, - 'add_order_shipping' => false, - 'add_order_tax' => false, - 'add_coupon_discount' => false, - 'remove_order_coupon' => false, - 'remove_order_item' => false, - 'remove_order_tax' => false, - 'reduce_order_item_stock' => false, - 'increase_order_item_stock' => false, - 'add_order_item_meta' => false, - 'remove_order_item_meta' => false, - 'calc_line_taxes' => false, - 'save_order_items' => false, - 'load_order_items' => false, - 'add_order_note' => false, - 'delete_order_note' => false, - 'json_search_products' => false, - 'json_search_products_and_variations' => false, - 'json_search_downloadable_products_and_variations' => false, - 'json_search_customers' => false, - 'json_search_categories' => false, - 'term_ordering' => false, - 'product_ordering' => false, - 'refund_line_items' => false, - 'delete_refund' => false, - 'rated' => false, - 'update_api_key' => false, - 'load_variations' => false, - 'save_variations' => false, - 'bulk_edit_variations' => false, - 'tax_rates_save_changes' => false, - 'shipping_zones_save_changes' => false, - 'shipping_zone_add_method' => false, - 'shipping_zone_methods_save_changes' => false, - 'shipping_zone_methods_save_settings' => false, - 'shipping_classes_save_changes' => false, - 'toggle_gateway_enabled' => false, + $ajax_events_nopriv = array( + 'get_refreshed_fragments', + 'apply_coupon', + 'remove_coupon', + 'update_shipping_method', + 'get_cart_totals', + 'update_order_review', + 'add_to_cart', + 'remove_from_cart', + 'checkout', + 'get_variation', + 'get_customer_location', ); - foreach ( $ajax_events as $ajax_event => $nopriv ) { + foreach ( $ajax_events_nopriv as $ajax_event ) { add_action( 'wp_ajax_woocommerce_' . $ajax_event, array( __CLASS__, $ajax_event ) ); + add_action( 'wp_ajax_nopriv_woocommerce_' . $ajax_event, array( __CLASS__, $ajax_event ) ); - if ( $nopriv ) { - add_action( 'wp_ajax_nopriv_woocommerce_' . $ajax_event, array( __CLASS__, $ajax_event ) ); + // WC AJAX can be used for frontend ajax requests. + add_action( 'wc_ajax_' . $ajax_event, array( __CLASS__, $ajax_event ) ); + } - // WC AJAX can be used for frontend ajax requests. - add_action( 'wc_ajax_' . $ajax_event, array( __CLASS__, $ajax_event ) ); - } + $ajax_events = array( + 'feature_product', + 'mark_order_status', + 'get_order_details', + 'add_attribute', + 'add_new_attribute', + 'remove_variation', + 'remove_variations', + 'save_attributes', + 'add_variation', + 'link_all_variations', + 'revoke_access_to_download', + 'grant_access_to_download', + 'get_customer_details', + 'add_order_item', + 'add_order_fee', + 'add_order_shipping', + 'add_order_tax', + 'add_coupon_discount', + 'remove_order_coupon', + 'remove_order_item', + 'remove_order_tax', + 'reduce_order_item_stock', + 'increase_order_item_stock', + 'add_order_item_meta', + 'remove_order_item_meta', + 'calc_line_taxes', + 'save_order_items', + 'load_order_items', + 'add_order_note', + 'delete_order_note', + 'json_search_products', + 'json_search_products_and_variations', + 'json_search_downloadable_products_and_variations', + 'json_search_customers', + 'json_search_categories', + 'term_ordering', + 'product_ordering', + 'refund_line_items', + 'delete_refund', + 'rated', + 'update_api_key', + 'load_variations', + 'save_variations', + 'bulk_edit_variations', + 'tax_rates_save_changes', + 'shipping_zones_save_changes', + 'shipping_zone_add_method', + 'shipping_zone_methods_save_changes', + 'shipping_zone_methods_save_settings', + 'shipping_classes_save_changes', + 'toggle_gateway_enabled', + ); + + foreach ( $ajax_events as $ajax_event ) { + add_action( 'wp_ajax_woocommerce_' . $ajax_event, array( __CLASS__, $ajax_event ) ); } } @@ -175,7 +184,8 @@ class WC_AJAX { $data = array( 'fragments' => apply_filters( - 'woocommerce_add_to_cart_fragments', array( + 'woocommerce_add_to_cart_fragments', + array( 'div.widget_shopping_cart_content' => '
    ' . $mini_cart . '
    ', ) ), @@ -208,7 +218,7 @@ class WC_AJAX { public static function remove_coupon() { check_ajax_referer( 'remove-coupon', 'security' ); - $coupon = isset( $_POST['coupon'] ) ? wc_clean( $_POST['coupon'] ) : false; + $coupon = isset( $_POST['coupon'] ) ? wc_clean( wp_unslash( $_POST['coupon'] ) ) : false; if ( empty( $coupon ) ) { wc_add_notice( __( 'Sorry there was a problem removing this coupon.', 'woocommerce' ), 'error' ); @@ -230,10 +240,11 @@ class WC_AJAX { wc_maybe_define_constant( 'WOOCOMMERCE_CART', true ); $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' ); + $posted_shipping_methods = isset( $_POST['shipping_method'] ) ? wc_clean( wp_unslash( $_POST['shipping_method'] ) ) : array(); - if ( isset( $_POST['shipping_method'] ) && is_array( $_POST['shipping_method'] ) ) { - foreach ( $_POST['shipping_method'] as $i => $value ) { - $chosen_shipping_methods[ $i ] = wc_clean( $value ); + if ( is_array( $posted_shipping_methods ) ) { + foreach ( $posted_shipping_methods as $i => $value ) { + $chosen_shipping_methods[ $i ] = $value; } } @@ -259,7 +270,8 @@ class WC_AJAX { wp_send_json( array( 'fragments' => apply_filters( - 'woocommerce_update_order_review_fragments', array( + 'woocommerce_update_order_review_fragments', + array( 'form.woocommerce-checkout' => '
    ' . __( 'Sorry, your session has expired.', 'woocommerce' ) . ' ' . __( 'Return to shop', 'woocommerce' ) . '
    ', ) ), @@ -279,54 +291,55 @@ class WC_AJAX { self::update_order_review_expired(); } - do_action( 'woocommerce_checkout_update_order_review', $_POST['post_data'] ); + do_action( 'woocommerce_checkout_update_order_review', isset( $_POST['post_data'] ) ? wp_unslash( $_POST['post_data'] ) : '' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' ); + $posted_shipping_methods = isset( $_POST['shipping_method'] ) ? wc_clean( wp_unslash( $_POST['shipping_method'] ) ) : array(); - if ( isset( $_POST['shipping_method'] ) && is_array( $_POST['shipping_method'] ) ) { - foreach ( $_POST['shipping_method'] as $i => $value ) { - $chosen_shipping_methods[ $i ] = wc_clean( $value ); + if ( is_array( $posted_shipping_methods ) ) { + foreach ( $posted_shipping_methods as $i => $value ) { + $chosen_shipping_methods[ $i ] = $value; } } WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods ); - WC()->session->set( 'chosen_payment_method', empty( $_POST['payment_method'] ) ? '' : $_POST['payment_method'] ); + WC()->session->set( 'chosen_payment_method', empty( $_POST['payment_method'] ) ? '' : wc_clean( wp_unslash( $_POST['payment_method'] ) ) ); WC()->customer->set_props( array( - 'billing_country' => isset( $_POST['country'] ) ? wp_unslash( $_POST['country'] ) : null, - 'billing_state' => isset( $_POST['state'] ) ? wp_unslash( $_POST['state'] ) : null, - 'billing_postcode' => isset( $_POST['postcode'] ) ? wp_unslash( $_POST['postcode'] ) : null, - 'billing_city' => isset( $_POST['city'] ) ? wp_unslash( $_POST['city'] ) : null, - 'billing_address_1' => isset( $_POST['address'] ) ? wp_unslash( $_POST['address'] ) : null, - 'billing_address_2' => isset( $_POST['address_2'] ) ? wp_unslash( $_POST['address_2'] ) : null, + 'billing_country' => isset( $_POST['country'] ) ? wc_clean( wp_unslash( $_POST['country'] ) ) : null, + 'billing_state' => isset( $_POST['state'] ) ? wc_clean( wp_unslash( $_POST['state'] ) ) : null, + 'billing_postcode' => isset( $_POST['postcode'] ) ? wc_clean( wp_unslash( $_POST['postcode'] ) ) : null, + 'billing_city' => isset( $_POST['city'] ) ? wc_clean( wp_unslash( $_POST['city'] ) ) : null, + 'billing_address_1' => isset( $_POST['address'] ) ? wc_clean( wp_unslash( $_POST['address'] ) ) : null, + 'billing_address_2' => isset( $_POST['address_2'] ) ? wc_clean( wp_unslash( $_POST['address_2'] ) ) : null, ) ); if ( wc_ship_to_billing_address_only() ) { WC()->customer->set_props( array( - 'shipping_country' => isset( $_POST['country'] ) ? wp_unslash( $_POST['country'] ) : null, - 'shipping_state' => isset( $_POST['state'] ) ? wp_unslash( $_POST['state'] ) : null, - 'shipping_postcode' => isset( $_POST['postcode'] ) ? wp_unslash( $_POST['postcode'] ) : null, - 'shipping_city' => isset( $_POST['city'] ) ? wp_unslash( $_POST['city'] ) : null, - 'shipping_address_1' => isset( $_POST['address'] ) ? wp_unslash( $_POST['address'] ) : null, - 'shipping_address_2' => isset( $_POST['address_2'] ) ? wp_unslash( $_POST['address_2'] ) : null, + 'shipping_country' => isset( $_POST['country'] ) ? wc_clean( wp_unslash( $_POST['country'] ) ) : null, + 'shipping_state' => isset( $_POST['state'] ) ? wc_clean( wp_unslash( $_POST['state'] ) ) : null, + 'shipping_postcode' => isset( $_POST['postcode'] ) ? wc_clean( wp_unslash( $_POST['postcode'] ) ) : null, + 'shipping_city' => isset( $_POST['city'] ) ? wc_clean( wp_unslash( $_POST['city'] ) ) : null, + 'shipping_address_1' => isset( $_POST['address'] ) ? wc_clean( wp_unslash( $_POST['address'] ) ) : null, + 'shipping_address_2' => isset( $_POST['address_2'] ) ? wc_clean( wp_unslash( $_POST['address_2'] ) ) : null, ) ); } else { WC()->customer->set_props( array( - 'shipping_country' => isset( $_POST['s_country'] ) ? wp_unslash( $_POST['s_country'] ) : null, - 'shipping_state' => isset( $_POST['s_state'] ) ? wp_unslash( $_POST['s_state'] ) : null, - 'shipping_postcode' => isset( $_POST['s_postcode'] ) ? wp_unslash( $_POST['s_postcode'] ) : null, - 'shipping_city' => isset( $_POST['s_city'] ) ? wp_unslash( $_POST['s_city'] ) : null, - 'shipping_address_1' => isset( $_POST['s_address'] ) ? wp_unslash( $_POST['s_address'] ) : null, - 'shipping_address_2' => isset( $_POST['s_address_2'] ) ? wp_unslash( $_POST['s_address_2'] ) : null, + 'shipping_country' => isset( $_POST['s_country'] ) ? wc_clean( wp_unslash( $_POST['s_country'] ) ) : null, + 'shipping_state' => isset( $_POST['s_state'] ) ? wc_clean( wp_unslash( $_POST['s_state'] ) ) : null, + 'shipping_postcode' => isset( $_POST['s_postcode'] ) ? wc_clean( wp_unslash( $_POST['s_postcode'] ) ) : null, + 'shipping_city' => isset( $_POST['s_city'] ) ? wc_clean( wp_unslash( $_POST['s_city'] ) ) : null, + 'shipping_address_1' => isset( $_POST['s_address'] ) ? wc_clean( wp_unslash( $_POST['s_address'] ) ) : null, + 'shipping_address_2' => isset( $_POST['s_address_2'] ) ? wc_clean( wp_unslash( $_POST['s_address_2'] ) ) : null, ) ); } - if ( wc_string_to_bool( $_POST['has_full_address'] ) ) { + if ( isset( $_POST['has_full_address'] ) && wc_string_to_bool( wc_clean( wp_unslash( $_POST['has_full_address'] ) ) ) ) { WC()->customer->set_calculated_shipping( true ); } else { WC()->customer->set_calculated_shipping( false ); @@ -377,9 +390,14 @@ class WC_AJAX { public static function add_to_cart() { ob_start(); + // phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification + if ( ! isset( $_POST['product_id'] ) ) { + return; + } + $product_id = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $_POST['product_id'] ) ); $product = wc_get_product( $product_id ); - $quantity = empty( $_POST['quantity'] ) ? 1 : wc_stock_amount( $_POST['quantity'] ); + $quantity = empty( $_POST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_POST['quantity'] ) ); $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity ); $product_status = get_post_status( $product_id ); $variation_id = 0; @@ -399,12 +417,11 @@ class WC_AJAX { wc_add_to_cart_message( array( $product_id => $quantity ), true ); } - // Return fragments self::get_refreshed_fragments(); } else { - // If there was an error adding to the cart, redirect to the product page to show any errors + // If there was an error adding to the cart, redirect to the product page to show any errors. $data = array( 'error' => true, 'product_url' => apply_filters( 'woocommerce_cart_redirect_after_error', get_permalink( $product_id ), $product_id ), @@ -412,6 +429,7 @@ class WC_AJAX { wp_send_json( $data ); } + // phpcs:enable } /** @@ -420,7 +438,8 @@ class WC_AJAX { public static function remove_from_cart() { ob_start(); - $cart_item_key = wc_clean( $_POST['cart_item_key'] ); + // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification + $cart_item_key = wc_clean( isset( $_POST['cart_item_key'] ) ? wp_unslash( $_POST['cart_item_key'] ) : '' ); if ( $cart_item_key && false !== WC()->cart->remove_cart_item( $cart_item_key ) ) { self::get_refreshed_fragments(); @@ -444,7 +463,14 @@ class WC_AJAX { public static function get_variation() { ob_start(); - if ( empty( $_POST['product_id'] ) || ! ( $variable_product = wc_get_product( absint( $_POST['product_id'] ) ) ) ) { + // phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification + if ( empty( $_POST['product_id'] ) ) { + wp_die(); + } + + $variable_product = wc_get_product( absint( $_POST['product_id'] ) ); + + if ( ! $variable_product ) { wp_die(); } @@ -452,6 +478,7 @@ class WC_AJAX { $variation_id = $data_store->find_matching_product_variation( $variable_product, wp_unslash( $_POST ) ); $variation = $variation_id ? $variable_product->get_available_variation( $variation_id ) : false; wp_send_json( $variation ); + // phpcs:enable } /** @@ -466,7 +493,7 @@ class WC_AJAX { * Toggle Featured status of a product from admin. */ public static function feature_product() { - if ( current_user_can( 'edit_products' ) && check_admin_referer( 'woocommerce-feature-product' ) ) { + if ( current_user_can( 'edit_products' ) && check_admin_referer( 'woocommerce-feature-product' ) && isset( $_GET['product_id'] ) ) { $product = wc_get_product( absint( $_GET['product_id'] ) ); if ( $product ) { @@ -483,9 +510,9 @@ class WC_AJAX { * Mark an order with a status. */ public static function mark_order_status() { - if ( current_user_can( 'edit_shop_orders' ) && check_admin_referer( 'woocommerce-mark-order-status' ) ) { - $status = sanitize_text_field( $_GET['status'] ); - $order = wc_get_order( absint( $_GET['order_id'] ) ); + if ( current_user_can( 'edit_shop_orders' ) && check_admin_referer( 'woocommerce-mark-order-status' ) && isset( $_GET['status'], $_GET['order_id'] ) ) { + $status = sanitize_text_field( wp_unslash( $_GET['status'] ) ); + $order = wc_get_order( absint( wp_unslash( $_GET['order_id'] ) ) ); if ( wc_is_order_status( 'wc-' . $status ) && $order ) { // Initialize payment gateways in case order has hooked status transition actions. @@ -506,7 +533,7 @@ class WC_AJAX { public static function get_order_details() { check_admin_referer( 'woocommerce-preview-order', 'security' ); - if ( ! current_user_can( 'edit_shop_orders' ) ) { + if ( ! current_user_can( 'edit_shop_orders' ) || ! isset( $_GET['order_id'] ) ) { wp_die( -1 ); } @@ -528,7 +555,7 @@ class WC_AJAX { check_ajax_referer( 'add-attribute', 'security' ); - if ( ! current_user_can( 'edit_products' ) ) { + if ( ! current_user_can( 'edit_products' ) || ! isset( $_POST['taxonomy'], $_POST['i'] ) ) { wp_die( -1 ); } @@ -536,8 +563,8 @@ class WC_AJAX { $metabox_class = array(); $attribute = new WC_Product_Attribute(); - $attribute->set_id( wc_attribute_taxonomy_id_by_name( sanitize_text_field( $_POST['taxonomy'] ) ) ); - $attribute->set_name( sanitize_text_field( $_POST['taxonomy'] ) ); + $attribute->set_id( wc_attribute_taxonomy_id_by_name( sanitize_text_field( wp_unslash( $_POST['taxonomy'] ) ) ) ); + $attribute->set_name( sanitize_text_field( wp_unslash( $_POST['taxonomy'] ) ) ); $attribute->set_visible( apply_filters( 'woocommerce_attribute_default_visibility', 1 ) ); $attribute->set_variation( apply_filters( 'woocommerce_attribute_default_is_variation', 0 ) ); @@ -556,9 +583,9 @@ class WC_AJAX { public static function add_new_attribute() { check_ajax_referer( 'add-attribute', 'security' ); - if ( current_user_can( 'manage_product_terms' ) ) { - $taxonomy = esc_attr( $_POST['taxonomy'] ); - $term = wc_clean( $_POST['term'] ); + if ( current_user_can( 'manage_product_terms' ) && isset( $_POST['taxonomy'], $_POST['term'] ) ) { + $taxonomy = esc_attr( wp_unslash( $_POST['taxonomy'] ) ); // phpcs:ignore + $term = wc_clean( wp_unslash( $_POST['term'] ) ); if ( taxonomy_exists( $taxonomy ) ) { @@ -591,8 +618,8 @@ class WC_AJAX { public static function remove_variations() { check_ajax_referer( 'delete-variations', 'security' ); - if ( current_user_can( 'edit_products' ) ) { - $variation_ids = (array) $_POST['variation_ids']; + if ( current_user_can( 'edit_products' ) && isset( $_POST['variation_ids'] ) ) { + $variation_ids = array_map( 'absint', (array) wp_unslash( $_POST['variation_ids'] ) ); foreach ( $variation_ids as $variation_id ) { if ( 'product_variation' === get_post_type( $variation_id ) ) { @@ -611,18 +638,18 @@ class WC_AJAX { public static function save_attributes() { check_ajax_referer( 'save-attributes', 'security' ); - if ( ! current_user_can( 'edit_products' ) ) { + if ( ! current_user_can( 'edit_products' ) || ! isset( $_POST['data'], $_POST['post_id'] ) ) { wp_die( -1 ); } $response = array(); try { - parse_str( wp_unslash( $_POST['data'] ), $data ); + parse_str( wp_unslash( $_POST['data'] ), $data ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $attributes = WC_Meta_Box_Product_Data::prepare_attributes( $data ); - $product_id = absint( $_POST['post_id'] ); - $product_type = ! empty( $_POST['product_type'] ) ? wc_clean( $_POST['product_type'] ) : 'simple'; + $product_id = absint( wp_unslash( $_POST['post_id'] ) ); + $product_type = ! empty( $_POST['product_type'] ) ? wc_clean( wp_unslash( $_POST['product_type'] ) ) : 'simple'; $classname = WC_Product_Factory::get_product_classname( $product_id, $product_type ); $product = new $classname( $product_id ); @@ -646,7 +673,7 @@ class WC_AJAX { $metabox_class[] = $attribute->get_name(); } - include( 'admin/meta-boxes/views/html-product-attribute.php' ); + include 'admin/meta-boxes/views/html-product-attribute.php'; } $response['html'] = ob_get_clean(); @@ -664,14 +691,14 @@ class WC_AJAX { public static function add_variation() { check_ajax_referer( 'add-variation', 'security' ); - if ( ! current_user_can( 'edit_products' ) ) { + if ( ! current_user_can( 'edit_products' ) || ! isset( $_POST['post_id'], $_POST['loop'] ) ) { wp_die( -1 ); } global $post; // Set $post global so its available, like within the admin screens. $product_id = intval( $_POST['post_id'] ); - $post = get_post( $product_id ); + $post = get_post( $product_id ); // phpcs:ignore $loop = intval( $_POST['loop'] ); $product_object = wc_get_product( $product_id ); $variation_object = new WC_Product_Variation(); @@ -697,7 +724,7 @@ class WC_AJAX { wc_maybe_define_constant( 'WC_MAX_LINKED_VARIATIONS', 49 ); wc_set_time_limit( 0 ); - $post_id = intval( $_POST['post_id'] ); + $post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0; if ( ! $post_id ) { wp_die(); @@ -719,7 +746,7 @@ class WC_AJAX { $possible_attributes = array_reverse( wc_array_cartesian( $attributes ) ); foreach ( $possible_attributes as $possible_attribute ) { - if ( in_array( $possible_attribute, $existing_attributes ) ) { + if ( in_array( $possible_attribute, $existing_attributes, true ) ) { continue; } $variation = new WC_Product_Variation(); @@ -733,7 +760,7 @@ class WC_AJAX { } } - echo $added; + echo esc_html( $added ); } $data_store = $product->get_data_store(); @@ -747,10 +774,10 @@ class WC_AJAX { public static function revoke_access_to_download() { check_ajax_referer( 'revoke-access', 'security' ); - if ( ! current_user_can( 'edit_shop_orders' ) ) { + if ( ! current_user_can( 'edit_shop_orders' ) || ! isset( $_POST['download_id'], $_POST['product_id'], $_POST['order_id'], $_POST['permission_id'] ) ) { wp_die( -1 ); } - $download_id = $_POST['download_id']; + $download_id = wc_clean( wp_unslash( $_POST['download_id'] ) ); $product_id = intval( $_POST['product_id'] ); $order_id = intval( $_POST['order_id'] ); $permission_id = absint( $_POST['permission_id'] ); @@ -769,7 +796,7 @@ class WC_AJAX { check_ajax_referer( 'grant-access', 'security' ); - if ( ! current_user_can( 'edit_shop_orders' ) ) { + if ( ! current_user_can( 'edit_shop_orders' ) || ! isset( $_POST['loop'], $_POST['order_id'], $_POST['product_ids'] ) ) { wp_die( -1 ); } @@ -778,15 +805,11 @@ class WC_AJAX { $wpdb->hide_errors(); $order_id = intval( $_POST['order_id'] ); - $product_ids = $_POST['product_ids']; + $product_ids = array_filter( array_map( 'absint', (array) wp_unslash( $_POST['product_ids'] ) ) ); $loop = intval( $_POST['loop'] ); $file_counter = 0; $order = wc_get_order( $order_id ); - if ( ! is_array( $product_ids ) ) { - $product_ids = array( $product_ids ); - } - foreach ( $product_ids as $product_id ) { $product = wc_get_product( $product_id ); $files = $product->get_downloads(); @@ -797,7 +820,8 @@ class WC_AJAX { if ( ! empty( $files ) ) { foreach ( $files as $download_id => $file ) { - if ( $inserted_id = wc_downloadable_file_permission( $download_id, $product_id, $order ) ) { + $inserted_id = wc_downloadable_file_permission( $download_id, $product_id, $order ); + if ( $inserted_id ) { $download = new WC_Customer_Download( $inserted_id ); $loop ++; $file_counter ++; @@ -822,7 +846,7 @@ class WC_AJAX { public static function get_customer_details() { check_ajax_referer( 'get-customer-details', 'security' ); - if ( ! current_user_can( 'edit_shop_orders' ) ) { + if ( ! current_user_can( 'edit_shop_orders' ) || ! isset( $_POST['user_id'] ) ) { wp_die( -1 ); } @@ -843,6 +867,8 @@ class WC_AJAX { /** * Add order item via ajax. + * + * @throws Exception If order is invalid. */ public static function add_order_item() { check_ajax_referer( 'order-item', 'security' ); @@ -858,15 +884,15 @@ class WC_AJAX { throw new Exception( __( 'Invalid order', 'woocommerce' ) ); } - $order_id = absint( wp_unslash( $_POST['order_id'] ) ); // WPCS: input var ok. - $order = wc_get_order( $order_id ); + $order_id = absint( wp_unslash( $_POST['order_id'] ) ); // WPCS: input var ok. + $order = wc_get_order( $order_id ); if ( ! $order ) { throw new Exception( __( 'Invalid order', 'woocommerce' ) ); } // If we passed through items it means we need to save first before adding a new one. - $items = ( ! empty( $_POST['items'] ) ) ? $_POST['items'] : ''; + $items = ( ! empty( $_POST['items'] ) ) ? wp_unslash( $_POST['items'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized if ( ! empty( $items ) ) { $save_items = array(); @@ -875,7 +901,7 @@ class WC_AJAX { wc_save_order_items( $order->get_id(), $save_items ); } - $items_to_add = array_filter( wp_unslash( (array) $_POST['data'] ) ); + $items_to_add = isset( $_POST['data'] ) ? array_filter( wp_unslash( (array) $_POST['data'] ) ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized // Add items to order. foreach ( $items_to_add as $item ) { @@ -914,6 +940,8 @@ class WC_AJAX { /** * Add order fee via ajax. + * + * @throws Exception If order is invalid. */ public static function add_order_fee() { check_ajax_referer( 'order-item', 'security' ); @@ -925,20 +953,22 @@ class WC_AJAX { $response = array(); try { - $order_id = absint( $_POST['order_id'] ); - $amount = wc_clean( $_POST['amount'] ); - $order = wc_get_order( $order_id ); - $calculate_tax_args = array( - 'country' => strtoupper( wc_clean( $_POST['country'] ) ), - 'state' => strtoupper( wc_clean( $_POST['state'] ) ), - 'postcode' => strtoupper( wc_clean( $_POST['postcode'] ) ), - 'city' => strtoupper( wc_clean( $_POST['city'] ) ), - ); + $order_id = isset( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : 0; + $order = wc_get_order( $order_id ); if ( ! $order ) { - throw new exception( __( 'Invalid order', 'woocommerce' ) ); + throw new Exception( __( 'Invalid order', 'woocommerce' ) ); } + $amount = isset( $_POST['amount'] ) ? wc_clean( wp_unslash( $_POST['amount'] ) ) : 0; + + $calculate_tax_args = array( + 'country' => isset( $_POST['country'] ) ? strtoupper( wc_clean( wp_unslash( $_POST['country'] ) ) ) : '', + 'state' => isset( $_POST['state'] ) ? strtoupper( wc_clean( wp_unslash( $_POST['state'] ) ) ) : '', + 'postcode' => isset( $_POST['postcode'] ) ? strtoupper( wc_clean( wp_unslash( $_POST['postcode'] ) ) ) : '', + 'city' => isset( $_POST['city'] ) ? strtoupper( wc_clean( wp_unslash( $_POST['city'] ) ) ) : '', + ); + if ( strstr( $amount, '%' ) ) { $formatted_amount = $amount; $percent = floatval( trim( $amount, '%' ) ); @@ -972,6 +1002,8 @@ class WC_AJAX { /** * Add order shipping cost via ajax. + * + * @throws Exception If order is invalid. */ public static function add_order_shipping() { check_ajax_referer( 'order-item', 'security' ); @@ -983,12 +1015,17 @@ class WC_AJAX { $response = array(); try { - $order_id = absint( $_POST['order_id'] ); - $order = wc_get_order( $order_id ); + $order_id = isset( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : 0; + $order = wc_get_order( $order_id ); + + if ( ! $order ) { + throw new Exception( __( 'Invalid order', 'woocommerce' ) ); + } + $order_taxes = $order->get_taxes(); $shipping_methods = WC()->shipping() ? WC()->shipping()->load_shipping_methods() : array(); - // Add new shipping + // Add new shipping. $item = new WC_Order_Item_Shipping(); $item->set_shipping_rate( new WC_Shipping_Rate() ); $item->set_order_id( $order_id ); @@ -1007,6 +1044,8 @@ class WC_AJAX { /** * Add order tax column via ajax. + * + * @throws Exception If order is invalid. */ public static function add_order_tax() { check_ajax_referer( 'order-item', 'security' ); @@ -1018,12 +1057,22 @@ class WC_AJAX { $response = array(); try { - $order_id = absint( $_POST['order_id'] ); - $rate_id = absint( $_POST['rate_id'] ); + $order_id = isset( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : 0; $order = wc_get_order( $order_id ); - $data = get_post_meta( $order_id ); - // Add new tax + if ( ! $order ) { + throw new Exception( __( 'Invalid order', 'woocommerce' ) ); + } + + $rate_id = isset( $_POST['rate_id'] ) ? absint( $_POST['rate_id'] ) : ''; + + if ( ! $rate_id ) { + throw new Exception( __( 'Invalid rate', 'woocommerce' ) ); + } + + $data = get_post_meta( $order_id ); + + // Add new tax. $item = new WC_Order_Item_Tax(); $item->set_rate( $rate_id ); $item->set_order_id( $order_id ); @@ -1042,6 +1091,8 @@ class WC_AJAX { /** * Add order discount via ajax. + * + * @throws Exception If order is invalid. */ public static function add_coupon_discount() { check_ajax_referer( 'order-item', 'security' ); @@ -1053,9 +1104,18 @@ class WC_AJAX { $response = array(); try { - $order_id = absint( $_POST['order_id'] ); + $order_id = isset( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : 0; $order = wc_get_order( $order_id ); - $result = $order->apply_coupon( wc_clean( $_POST['coupon'] ) ); + + if ( ! $order ) { + throw new Exception( __( 'Invalid order', 'woocommerce' ) ); + } + + if ( empty( $_POST['coupon'] ) ) { + throw new Exception( __( 'Invalid coupon', 'woocommerce' ) ); + } + + $result = $order->apply_coupon( wc_clean( wp_unslash( $_POST['coupon'] ) ) ); if ( is_wp_error( $result ) ) { throw new Exception( html_entity_decode( wp_strip_all_tags( $result->get_error_message() ) ) ); @@ -1074,6 +1134,8 @@ class WC_AJAX { /** * Remove coupon from an order via ajax. + * + * @throws Exception If order is invalid. */ public static function remove_order_coupon() { check_ajax_referer( 'order-item', 'security' ); @@ -1085,10 +1147,18 @@ class WC_AJAX { $response = array(); try { - $order_id = absint( $_POST['order_id'] ); + $order_id = isset( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : 0; $order = wc_get_order( $order_id ); - $order->remove_coupon( wc_clean( $_POST['coupon'] ) ); + if ( ! $order ) { + throw new Exception( __( 'Invalid order', 'woocommerce' ) ); + } + + if ( empty( $_POST['coupon'] ) ) { + throw new Exception( __( 'Invalid coupon', 'woocommerce' ) ); + } + + $order->remove_coupon( wc_clean( wp_unslash( $_POST['coupon'] ) ) ); ob_start(); include 'admin/meta-boxes/views/html-order-items.php'; @@ -1103,11 +1173,13 @@ class WC_AJAX { /** * Remove an order item. + * + * @throws Exception If order is invalid. */ public static function remove_order_item() { check_ajax_referer( 'order-item', 'security' ); - if ( ! current_user_can( 'edit_shop_orders' ) ) { + if ( ! current_user_can( 'edit_shop_orders' ) || ! isset( $_POST['order_id'], $_POST['order_item_ids'] ) ) { wp_die( -1 ); } @@ -1115,13 +1187,13 @@ class WC_AJAX { try { $order_id = absint( $_POST['order_id'] ); - $order_item_ids = $_POST['order_item_ids']; - $items = ( ! empty( $_POST['items'] ) ) ? $_POST['items'] : ''; + $order_item_ids = wp_unslash( $_POST['order_item_ids'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $items = ( ! empty( $_POST['items'] ) ) ? wp_unslash( $_POST['items'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $calculate_tax_args = array( - 'country' => strtoupper( wc_clean( $_POST['country'] ) ), - 'state' => strtoupper( wc_clean( $_POST['state'] ) ), - 'postcode' => strtoupper( wc_clean( $_POST['postcode'] ) ), - 'city' => strtoupper( wc_clean( $_POST['city'] ) ), + 'country' => isset( $_POST['country'] ) ? strtoupper( wc_clean( wp_unslash( $_POST['country'] ) ) ) : '', + 'state' => isset( $_POST['state'] ) ? strtoupper( wc_clean( wp_unslash( $_POST['state'] ) ) ) : '', + 'postcode' => isset( $_POST['postcode'] ) ? strtoupper( wc_clean( wp_unslash( $_POST['postcode'] ) ) ) : '', + 'city' => isset( $_POST['city'] ) ? strtoupper( wc_clean( wp_unslash( $_POST['city'] ) ) ) : '', ); if ( ! is_array( $order_item_ids ) && is_numeric( $order_item_ids ) ) { @@ -1132,11 +1204,11 @@ class WC_AJAX { if ( ! empty( $items ) ) { $save_items = array(); parse_str( $items, $save_items ); - // Save order items + // Save order items. wc_save_order_items( $order_id, $save_items ); } - if ( sizeof( $order_item_ids ) > 0 ) { + if ( 0 < count( $order_item_ids ) ) { foreach ( $order_item_ids as $id ) { wc_delete_order_item( absint( $id ) ); } @@ -1159,11 +1231,13 @@ class WC_AJAX { /** * Remove an order tax. + * + * @throws Exception If order is invalid. */ public static function remove_order_tax() { check_ajax_referer( 'order-item', 'security' ); - if ( ! current_user_can( 'edit_shop_orders' ) ) { + if ( ! current_user_can( 'edit_shop_orders' ) || ! isset( $_POST['order_id'], $_POST['rate_id'] ) ) { wp_die( -1 ); } @@ -1195,26 +1269,26 @@ class WC_AJAX { public static function calc_line_taxes() { check_ajax_referer( 'calc-totals', 'security' ); - if ( ! current_user_can( 'edit_shop_orders' ) ) { + if ( ! current_user_can( 'edit_shop_orders' ) || ! isset( $_POST['order_id'], $_POST['items'] ) ) { wp_die( -1 ); } $order_id = absint( $_POST['order_id'] ); $calculate_tax_args = array( - 'country' => strtoupper( wc_clean( $_POST['country'] ) ), - 'state' => strtoupper( wc_clean( $_POST['state'] ) ), - 'postcode' => strtoupper( wc_clean( $_POST['postcode'] ) ), - 'city' => strtoupper( wc_clean( $_POST['city'] ) ), + 'country' => isset( $_POST['country'] ) ? strtoupper( wc_clean( wp_unslash( $_POST['country'] ) ) ) : '', + 'state' => isset( $_POST['state'] ) ? strtoupper( wc_clean( wp_unslash( $_POST['state'] ) ) ) : '', + 'postcode' => isset( $_POST['postcode'] ) ? strtoupper( wc_clean( wp_unslash( $_POST['postcode'] ) ) ) : '', + 'city' => isset( $_POST['city'] ) ? strtoupper( wc_clean( wp_unslash( $_POST['city'] ) ) ) : '', ); - // Parse the jQuery serialized items + // Parse the jQuery serialized items. $items = array(); - parse_str( $_POST['items'], $items ); + parse_str( wp_unslash( $_POST['items'] ), $items ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - // Save order items first + // Save order items first. wc_save_order_items( $order_id, $items ); - // Grab the order and recalculate taxes + // Grab the order and recalculate taxes. $order = wc_get_order( $order_id ); $order->calculate_taxes( $calculate_tax_args ); $order->calculate_totals( false ); @@ -1228,21 +1302,21 @@ class WC_AJAX { public static function save_order_items() { check_ajax_referer( 'order-item', 'security' ); - if ( ! current_user_can( 'edit_shop_orders' ) ) { + if ( ! current_user_can( 'edit_shop_orders' ) || ! isset( $_POST['order_id'], $_POST['items'] ) ) { wp_die( -1 ); } if ( isset( $_POST['order_id'], $_POST['items'] ) ) { $order_id = absint( $_POST['order_id'] ); - // Parse the jQuery serialized items + // Parse the jQuery serialized items. $items = array(); - parse_str( $_POST['items'], $items ); + parse_str( wp_unslash( $_POST['items'] ), $items ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - // Save order items + // Save order items. wc_save_order_items( $order_id, $items ); - // Return HTML items + // Return HTML items. $order = wc_get_order( $order_id ); include 'admin/meta-boxes/views/html-order-items.php'; } @@ -1255,11 +1329,11 @@ class WC_AJAX { public static function load_order_items() { check_ajax_referer( 'order-item', 'security' ); - if ( ! current_user_can( 'edit_shop_orders' ) ) { + if ( ! current_user_can( 'edit_shop_orders' ) || ! isset( $_POST['order_id'] ) ) { wp_die( -1 ); } - // Return HTML items + // Return HTML items. $order_id = absint( $_POST['order_id'] ); $order = wc_get_order( $order_id ); include 'admin/meta-boxes/views/html-order-items.php'; @@ -1272,13 +1346,13 @@ class WC_AJAX { public static function add_order_note() { check_ajax_referer( 'add-order-note', 'security' ); - if ( ! current_user_can( 'edit_shop_orders' ) ) { + if ( ! current_user_can( 'edit_shop_orders' ) || ! isset( $_POST['post_id'], $_POST['note'], $_POST['note_type'] ) ) { wp_die( -1 ); } $post_id = absint( $_POST['post_id'] ); - $note = wp_kses_post( trim( wp_unslash( $_POST['note'] ) ) ); - $note_type = $_POST['note_type']; + $note = wp_kses_post( trim( wp_unslash( $_POST['note'] ) ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $note_type = wc_clean( wp_unslash( $_POST['note_type'] ) ); $is_customer_note = ( 'customer' === $note_type ) ? 1 : 0; @@ -1293,22 +1367,22 @@ class WC_AJAX { ?>
  • - content ) ) ) ); ?> + content ) ) ) ); ?>

    - + date_created->date_i18n( wc_date_format() ), $note->date_created->date_i18n( wc_time_format() ) ); + printf( esc_html__( 'added on %1$s at %2$s', 'woocommerce' ), esc_html( $note->date_created->date_i18n( wc_date_format() ) ), esc_html( $note->date_created->date_i18n( wc_time_format() ) ) ); ?> added_by ) : /* translators: %s: note author */ - printf( ' ' . __( 'by %s', 'woocommerce' ), $note->added_by ); + printf( ' ' . esc_html__( 'by %s', 'woocommerce' ), esc_html( $note->added_by ) ); endif; ?> - +

  • search_products( $term, '', (bool) $include_variations, false, $limit ); if ( ! empty( $_GET['exclude'] ) ) { - $ids = array_diff( $ids, (array) $_GET['exclude'] ); + $ids = array_diff( $ids, array_map( 'absint', (array) wp_unslash( $_GET['exclude'] ) ) ); } if ( ! empty( $_GET['include'] ) ) { - $ids = array_intersect( $ids, (array) $_GET['include'] ); + $ids = array_intersect( $ids, array_map( 'absint', (array) wp_unslash( $_GET['include'] ) ) ); } $product_objects = array_filter( array_map( 'wc_get_product', $ids ), 'wc_products_array_filter_readable' ); @@ -1400,16 +1474,16 @@ class WC_AJAX { public static function json_search_downloadable_products_and_variations() { check_ajax_referer( 'search-products', 'security' ); - $term = (string) wc_clean( wp_unslash( $_GET['term'] ) ); + $term = isset( $_GET['term'] ) ? (string) wc_clean( wp_unslash( $_GET['term'] ) ) : ''; $data_store = WC_Data_Store::load( 'product' ); $ids = $data_store->search_products( $term, 'downloadable', true ); if ( ! empty( $_GET['exclude'] ) ) { - $ids = array_diff( $ids, (array) $_GET['exclude'] ); + $ids = array_diff( $ids, array_map( 'absint', (array) wp_unslash( $_GET['exclude'] ) ) ); } if ( ! empty( $_GET['include'] ) ) { - $ids = array_intersect( $ids, (array) $_GET['include'] ); + $ids = array_intersect( $ids, array_map( 'absint', (array) wp_unslash( $_GET['include'] ) ) ); } if ( ! empty( $_GET['limit'] ) ) { @@ -1438,7 +1512,7 @@ class WC_AJAX { wp_die( -1 ); } - $term = wc_clean( wp_unslash( $_GET['term'] ) ); + $term = isset( $_GET['term'] ) ? (string) wc_clean( wp_unslash( $_GET['term'] ) ) : ''; $limit = 0; if ( empty( $term ) ) { @@ -1471,7 +1545,7 @@ class WC_AJAX { $found_customers = array(); if ( ! empty( $_GET['exclude'] ) ) { - $ids = array_diff( $ids, (array) $_GET['exclude'] ); + $ids = array_diff( $ids, array_map( 'absint', (array) wp_unslash( $_GET['exclude'] ) ) ); } foreach ( $ids as $id ) { @@ -1501,7 +1575,9 @@ class WC_AJAX { wp_die( -1 ); } - if ( ! $search_text = wc_clean( wp_unslash( $_GET['term'] ) ) ) { + $search_text = isset( $_GET['term'] ) ? wc_clean( wp_unslash( $_GET['term'] ) ) : ''; + + if ( ! $search_text ) { wp_die(); } @@ -1515,14 +1591,17 @@ class WC_AJAX { 'name__like' => $search_text, ); - if ( $terms = get_terms( $args ) ) { + $terms = get_terms( $args ); + + if ( $terms ) { foreach ( $terms as $term ) { $term->formatted_name = ''; if ( $term->parent ) { $ancestors = array_reverse( get_ancestors( $term->term_id, 'product_cat' ) ); foreach ( $ancestors as $ancestor ) { - if ( $ancestor_term = get_term( $ancestor, 'product_cat' ) ) { + $ancestor_term = get_term( $ancestor, 'product_cat' ); + if ( $ancestor_term ) { $term->formatted_name .= $ancestor_term->name . ' > '; } } @@ -1540,15 +1619,14 @@ class WC_AJAX { * Ajax request handling for categories ordering. */ public static function term_ordering() { - - // check permissions again and make sure we have what we need + // phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification if ( ! current_user_can( 'edit_products' ) || empty( $_POST['id'] ) ) { wp_die( -1 ); } $id = (int) $_POST['id']; $next_id = isset( $_POST['nextid'] ) && (int) $_POST['nextid'] ? (int) $_POST['nextid'] : null; - $taxonomy = isset( $_POST['thetaxonomy'] ) ? esc_attr( $_POST['thetaxonomy'] ) : null; + $taxonomy = isset( $_POST['thetaxonomy'] ) ? esc_attr( wp_unslash( $_POST['thetaxonomy'] ) ) : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $term = get_term_by( 'id', $id, $taxonomy ); if ( ! $id || ! $term || ! $taxonomy ) { @@ -1559,10 +1637,11 @@ class WC_AJAX { $children = get_terms( $taxonomy, "child_of=$id&menu_order=ASC&hide_empty=0" ); - if ( $term && sizeof( $children ) ) { + if ( $term && count( $children ) ) { echo 'children'; wp_die(); } + // phpcs:enable } /** @@ -1573,6 +1652,7 @@ class WC_AJAX { public static function product_ordering() { global $wpdb; + // phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification if ( ! current_user_can( 'edit_products' ) || empty( $_POST['id'] ) ) { wp_die( -1 ); } @@ -1616,6 +1696,7 @@ class WC_AJAX { do_action( 'woocommerce_after_product_ordering', $sorting_id, $menu_orders ); wp_send_json( $menu_orders ); + // phpcs:enable } /** @@ -1632,15 +1713,15 @@ class WC_AJAX { wp_die( -1 ); } - $order_id = absint( $_POST['order_id'] ); - $refund_amount = wc_format_decimal( sanitize_text_field( wp_unslash( $_POST['refund_amount'] ) ), wc_get_price_decimals() ); - $refunded_amount = wc_format_decimal( sanitize_text_field( wp_unslash( $_POST['refunded_amount'] ) ), wc_get_price_decimals() ); - $refund_reason = sanitize_text_field( $_POST['refund_reason'] ); - $line_item_qtys = json_decode( sanitize_text_field( wp_unslash( $_POST['line_item_qtys'] ) ), true ); - $line_item_totals = json_decode( sanitize_text_field( wp_unslash( $_POST['line_item_totals'] ) ), true ); - $line_item_tax_totals = json_decode( sanitize_text_field( wp_unslash( $_POST['line_item_tax_totals'] ) ), true ); - $api_refund = 'true' === $_POST['api_refund']; - $restock_refunded_items = 'true' === $_POST['restock_refunded_items']; + $order_id = isset( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : 0; + $refund_amount = isset( $_POST['refund_amount'] ) ? wc_format_decimal( sanitize_text_field( wp_unslash( $_POST['refund_amount'] ) ), wc_get_price_decimals() ) : 0; + $refunded_amount = isset( $_POST['refunded_amount'] ) ? wc_format_decimal( sanitize_text_field( wp_unslash( $_POST['refunded_amount'] ) ), wc_get_price_decimals() ) : 0; + $refund_reason = isset( $_POST['refund_reason'] ) ? sanitize_text_field( wp_unslash( $_POST['refund_reason'] ) ) : ''; + $line_item_qtys = isset( $_POST['line_item_qtys'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['line_item_qtys'] ) ), true ) : array(); + $line_item_totals = isset( $_POST['line_item_totals'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['line_item_totals'] ) ), true ) : array(); + $line_item_tax_totals = isset( $_POST['line_item_tax_totals'] ) ? json_decode( sanitize_text_field( wp_unslash( $_POST['line_item_tax_totals'] ) ), true ) : array(); + $api_refund = isset( $_POST['api_refund'] ) && 'true' === $_POST['api_refund']; + $restock_refunded_items = isset( $_POST['restock_refunded_items'] ) && 'true' === $_POST['restock_refunded_items']; $refund = false; $response = array(); @@ -1650,16 +1731,16 @@ class WC_AJAX { $max_refund = wc_format_decimal( $order->get_total() - $order->get_total_refunded(), wc_get_price_decimals() ); if ( ! $refund_amount || $max_refund < $refund_amount || 0 > $refund_amount ) { - throw new exception( __( 'Invalid refund amount', 'woocommerce' ) ); + throw new Exception( __( 'Invalid refund amount', 'woocommerce' ) ); } - if ( $refunded_amount !== wc_format_decimal( $order->get_total_refunded(), wc_get_price_decimals() ) ) { - throw new exception( __( 'Error processing refund. Please try again.', 'woocommerce' ) ); + if ( wc_format_decimal( $order->get_total_refunded(), wc_get_price_decimals() ) !== $refunded_amount ) { + throw new Exception( __( 'Error processing refund. Please try again.', 'woocommerce' ) ); } // Prepare line items which we are refunding. $line_items = array(); - $item_ids = array_unique( array_merge( array_keys( $line_item_qtys, $line_item_totals ) ) ); + $item_ids = array_unique( array_merge( array_keys( $line_item_qtys, $line_item_totals, true ) ) ); foreach ( $item_ids as $item_id ) { $line_items[ $item_id ] = array( @@ -1711,11 +1792,11 @@ class WC_AJAX { public static function delete_refund() { check_ajax_referer( 'order-item', 'security' ); - if ( ! current_user_can( 'edit_shop_orders' ) ) { + if ( ! current_user_can( 'edit_shop_orders' ) || ! isset( $_POST['refund_id'] ) ) { wp_die( -1 ); } - $refund_ids = array_map( 'absint', is_array( $_POST['refund_id'] ) ? $_POST['refund_id'] : array( $_POST['refund_id'] ) ); + $refund_ids = array_map( 'absint', is_array( $_POST['refund_id'] ) ? wp_unslash( $_POST['refund_id'] ) : array( wp_unslash( $_POST['refund_id'] ) ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized foreach ( $refund_ids as $refund_id ) { if ( $refund_id && 'shop_order_refund' === get_post_type( $refund_id ) ) { $refund = wc_get_order( $refund_id ); @@ -1740,6 +1821,8 @@ class WC_AJAX { /** * Create/Update API key. + * + * @throws Exception On invalid API key. */ public static function update_api_key() { ob_start(); @@ -1765,9 +1848,9 @@ class WC_AJAX { throw new Exception( __( 'Permissions is missing.', 'woocommerce' ) ); } - $key_id = absint( $_POST['key_id'] ); + $key_id = isset( $_POST['key_id'] ) ? absint( $_POST['key_id'] ) : 0; $description = sanitize_text_field( wp_unslash( $_POST['description'] ) ); - $permissions = ( in_array( $_POST['permissions'], array( 'read', 'write', 'read_write' ) ) ) ? sanitize_text_field( $_POST['permissions'] ) : 'read'; + $permissions = ( in_array( wp_unslash( $_POST['permissions'] ), array( 'read', 'write', 'read_write' ), true ) ) ? sanitize_text_field( wp_unslash( $_POST['permissions'] ) ) : 'read'; $user_id = absint( $_POST['user'] ); // Check if current user can edit other users. @@ -1853,12 +1936,12 @@ class WC_AJAX { wp_die( -1 ); } - // Set $post global so its available, like within the admin screens + // Set $post global so its available, like within the admin screens. global $post; $loop = 0; $product_id = absint( $_POST['product_id'] ); - $post = get_post( $product_id ); + $post = get_post( $product_id ); // phpcs:ignore $product_object = wc_get_product( $product_id ); $per_page = ! empty( $_POST['per_page'] ) ? absint( $_POST['per_page'] ) : 10; $page = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1; @@ -1897,7 +1980,7 @@ class WC_AJAX { check_ajax_referer( 'save-variations', 'security' ); - // Check permissions again and make sure we have what we need + // Check permissions again and make sure we have what we need. if ( ! current_user_can( 'edit_products' ) || empty( $_POST ) || empty( $_POST['product_id'] ) ) { wp_die( -1 ); } @@ -1908,14 +1991,16 @@ class WC_AJAX { do_action( 'woocommerce_ajax_save_product_variations', $product_id ); - if ( $errors = WC_Admin_Meta_Boxes::$meta_box_errors ) { + $errors = WC_Admin_Meta_Boxes::$meta_box_errors; + + if ( $errors ) { echo '
    '; foreach ( $errors as $error ) { echo '

    ' . wp_kses_post( $error ) . '

    '; } - echo ''; + echo ''; echo '
    '; delete_option( 'woocommerce_meta_box_errors' ); @@ -1927,8 +2012,8 @@ class WC_AJAX { /** * Bulk action - Toggle Enabled. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -1943,8 +2028,8 @@ class WC_AJAX { /** * Bulk action - Toggle Downloadable Checkbox. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -1955,8 +2040,8 @@ class WC_AJAX { /** * Bulk action - Toggle Virtual Checkbox. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -1967,8 +2052,8 @@ class WC_AJAX { /** * Bulk action - Toggle Manage Stock Checkbox. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -1979,8 +2064,8 @@ class WC_AJAX { /** * Bulk action - Set Regular Prices. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -1991,8 +2076,8 @@ class WC_AJAX { /** * Bulk action - Set Sale Prices. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2003,8 +2088,8 @@ class WC_AJAX { /** * Bulk action - Set Stock Status as In Stock. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2015,8 +2100,8 @@ class WC_AJAX { /** * Bulk action - Set Stock Status as Out of Stock. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2027,8 +2112,8 @@ class WC_AJAX { /** * Bulk action - Set Stock Status as On Backorder. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2039,8 +2124,8 @@ class WC_AJAX { /** * Bulk action - Set Stock. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2065,8 +2150,8 @@ class WC_AJAX { /** * Bulk action - Set Weight. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2077,8 +2162,8 @@ class WC_AJAX { /** * Bulk action - Set Length. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2089,8 +2174,8 @@ class WC_AJAX { /** * Bulk action - Set Width. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2101,8 +2186,8 @@ class WC_AJAX { /** * Bulk action - Set Height. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2113,8 +2198,8 @@ class WC_AJAX { /** * Bulk action - Set Download Limit. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2125,8 +2210,8 @@ class WC_AJAX { /** * Bulk action - Set Download Expiry. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2137,8 +2222,8 @@ class WC_AJAX { /** * Bulk action - Delete all. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2154,8 +2239,8 @@ class WC_AJAX { /** * Bulk action - Sale Schedule. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2182,8 +2267,8 @@ class WC_AJAX { /** * Bulk action - Increase Regular Prices. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2194,8 +2279,8 @@ class WC_AJAX { /** * Bulk action - Decrease Regular Prices. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2206,8 +2291,8 @@ class WC_AJAX { /** * Bulk action - Increase Sale Prices. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2218,8 +2303,8 @@ class WC_AJAX { /** * Bulk action - Decrease Sale Prices. * - * @param array $variations - * @param array $data + * @param array $variations List of variations. + * @param array $data Data to set. * * @used-by bulk_edit_variations */ @@ -2230,10 +2315,10 @@ class WC_AJAX { /** * Bulk action - Set Price. * - * @param array $variations - * @param string $operator + or - - * @param string $field price being adjusted _regular_price or _sale_price - * @param string $value Price or Percent + * @param array $variations List of variations. + * @param string $field price being adjusted _regular_price or _sale_price. + * @param string $operator + or -. + * @param string $value Price or Percent. * * @used-by bulk_edit_variations */ @@ -2257,9 +2342,9 @@ class WC_AJAX { /** * Bulk set convenience function. * - * @param array $variations - * @param string $field - * @param string $value + * @param array $variations List of variations. + * @param string $field Field to set. + * @param string $value to set. */ private static function variation_bulk_set( $variations, $field, $value ) { foreach ( $variations as $variation_id ) { @@ -2272,8 +2357,8 @@ class WC_AJAX { /** * Bulk toggle convenience function. * - * @param array $variations - * @param string $field + * @param array $variations List of variations. + * @param string $field Field to toggle. */ private static function variation_bulk_toggle( $variations, $field ) { foreach ( $variations as $variation_id ) { @@ -2314,14 +2399,14 @@ class WC_AJAX { check_ajax_referer( 'bulk-edit-variations', 'security' ); - // Check permissions again and make sure we have what we need + // Check permissions again and make sure we have what we need. if ( ! current_user_can( 'edit_products' ) || empty( $_POST['product_id'] ) || empty( $_POST['bulk_action'] ) ) { wp_die( -1 ); } $product_id = absint( $_POST['product_id'] ); - $bulk_action = wc_clean( $_POST['bulk_action'] ); - $data = ! empty( $_POST['data'] ) ? array_map( 'wc_clean', $_POST['data'] ) : array(); + $bulk_action = wc_clean( wp_unslash( $_POST['bulk_action'] ) ); + $data = ! empty( $_POST['data'] ) ? wc_clean( wp_unslash( $_POST['data'] ) ) : array(); $variations = array(); if ( apply_filters( 'woocommerce_bulk_edit_variations_need_children', true ) ) { @@ -2352,27 +2437,28 @@ class WC_AJAX { * Handle submissions from assets/js/settings-views-html-settings-tax.js Backbone model. */ public static function tax_rates_save_changes() { + // phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification if ( ! isset( $_POST['wc_tax_nonce'], $_POST['changes'] ) ) { wp_send_json_error( 'missing_fields' ); wp_die(); } - $current_class = ! empty( $_POST['current_class'] ) ? $_POST['current_class'] : ''; // This is sanitized seven lines later. + $current_class = ! empty( $_POST['current_class'] ) ? wp_unslash( $_POST['current_class'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - if ( ! wp_verify_nonce( $_POST['wc_tax_nonce'], 'wc_tax_nonce-class:' . $current_class ) ) { + if ( ! wp_verify_nonce( wp_unslash( $_POST['wc_tax_nonce'] ), 'wc_tax_nonce-class:' . $current_class ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized wp_send_json_error( 'bad_nonce' ); wp_die(); } $current_class = WC_Tax::format_tax_rate_class( $current_class ); - // Check User Caps + // Check User Caps. if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_send_json_error( 'missing_capabilities' ); wp_die(); } - $changes = stripslashes_deep( $_POST['changes'] ); + $changes = wp_unslash( $_POST['changes'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized foreach ( $changes as $tax_rate_id => $data ) { if ( isset( $data['deleted'] ) ) { if ( isset( $data['newRow'] ) ) { @@ -2384,7 +2470,8 @@ class WC_AJAX { } $tax_rate = array_intersect_key( - $data, array( + $data, + array( 'tax_rate_country' => 1, 'tax_rate_state' => 1, 'tax_rate' => 1, @@ -2425,6 +2512,7 @@ class WC_AJAX { 'rates' => WC_Tax::get_rates_for_tax_class( $current_class ), ) ); + // phpcs:enable } /** @@ -2436,18 +2524,18 @@ class WC_AJAX { wp_die(); } - if ( ! wp_verify_nonce( $_POST['wc_shipping_zones_nonce'], 'wc_shipping_zones_nonce' ) ) { + if ( ! wp_verify_nonce( wp_unslash( $_POST['wc_shipping_zones_nonce'] ), 'wc_shipping_zones_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized wp_send_json_error( 'bad_nonce' ); wp_die(); } - // Check User Caps + // Check User Caps. if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_send_json_error( 'missing_capabilities' ); wp_die(); } - $changes = $_POST['changes']; + $changes = wp_unslash( $_POST['changes'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized foreach ( $changes as $zone_id => $data ) { if ( isset( $data['deleted'] ) ) { if ( isset( $data['newRow'] ) ) { @@ -2460,7 +2548,8 @@ class WC_AJAX { } $zone_data = array_intersect_key( - $data, array( + $data, + array( 'zone_id' => 1, 'zone_order' => 1, ) @@ -2493,20 +2582,20 @@ class WC_AJAX { wp_die(); } - if ( ! wp_verify_nonce( $_POST['wc_shipping_zones_nonce'], 'wc_shipping_zones_nonce' ) ) { + if ( ! wp_verify_nonce( wp_unslash( $_POST['wc_shipping_zones_nonce'] ), 'wc_shipping_zones_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized wp_send_json_error( 'bad_nonce' ); wp_die(); } - // Check User Caps + // Check User Caps. if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_send_json_error( 'missing_capabilities' ); wp_die(); } - $zone_id = wc_clean( $_POST['zone_id'] ); + $zone_id = wc_clean( wp_unslash( $_POST['zone_id'] ) ); $zone = new WC_Shipping_Zone( $zone_id ); - $instance_id = $zone->add_shipping_method( wc_clean( $_POST['method_id'] ) ); + $instance_id = $zone->add_shipping_method( wc_clean( wp_unslash( $_POST['method_id'] ) ) ); wp_send_json_success( array( @@ -2527,7 +2616,7 @@ class WC_AJAX { wp_die(); } - if ( ! wp_verify_nonce( $_POST['wc_shipping_zones_nonce'], 'wc_shipping_zones_nonce' ) ) { + if ( ! wp_verify_nonce( wp_unslash( $_POST['wc_shipping_zones_nonce'] ), 'wc_shipping_zones_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized wp_send_json_error( 'bad_nonce' ); wp_die(); } @@ -2539,9 +2628,9 @@ class WC_AJAX { global $wpdb; - $zone_id = wc_clean( $_POST['zone_id'] ); + $zone_id = wc_clean( wp_unslash( $_POST['zone_id'] ) ); $zone = new WC_Shipping_Zone( $zone_id ); - $changes = $_POST['changes']; + $changes = wp_unslash( $_POST['changes'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized if ( isset( $changes['zone_name'] ) ) { $zone->set_zone_name( wc_clean( $changes['zone_name'] ) ); @@ -2551,7 +2640,7 @@ class WC_AJAX { $zone->clear_locations( array( 'state', 'country', 'continent' ) ); $locations = array_filter( array_map( 'wc_clean', (array) $changes['zone_locations'] ) ); foreach ( $locations as $location ) { - // Each posted location will be in the format type:code + // Each posted location will be in the format type:code. $location_parts = explode( ':', $location ); switch ( $location_parts[0] ) { case 'state': @@ -2590,7 +2679,8 @@ class WC_AJAX { } $method_data = array_intersect_key( - $data, array( + $data, + array( 'method_order' => 1, 'enabled' => 1, ) @@ -2629,7 +2719,7 @@ class WC_AJAX { wp_die(); } - if ( ! wp_verify_nonce( $_POST['wc_shipping_zones_nonce'], 'wc_shipping_zones_nonce' ) ) { + if ( ! wp_verify_nonce( wp_unslash( $_POST['wc_shipping_zones_nonce'] ), 'wc_shipping_zones_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized wp_send_json_error( 'bad_nonce' ); wp_die(); } @@ -2642,7 +2732,7 @@ class WC_AJAX { $instance_id = absint( $_POST['instance_id'] ); $zone = WC_Shipping_Zones::get_zone_by( 'instance_id', $instance_id ); $shipping_method = WC_Shipping_Zones::get_shipping_method( $instance_id ); - $shipping_method->set_post_data( $_POST['data'] ); + $shipping_method->set_post_data( wp_unslash( $_POST['data'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $shipping_method->process_admin_options(); WC_Cache_Helper::get_transient_version( 'shipping', true ); @@ -2666,7 +2756,7 @@ class WC_AJAX { wp_die(); } - if ( ! wp_verify_nonce( $_POST['wc_shipping_classes_nonce'], 'wc_shipping_classes_nonce' ) ) { + if ( ! wp_verify_nonce( wp_unslash( $_POST['wc_shipping_classes_nonce'] ), 'wc_shipping_classes_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized wp_send_json_error( 'bad_nonce' ); wp_die(); } @@ -2676,7 +2766,7 @@ class WC_AJAX { wp_die(); } - $changes = $_POST['changes']; + $changes = wp_unslash( $_POST['changes'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized foreach ( $changes as $term_id => $data ) { $term_id = absint( $term_id ); @@ -2734,7 +2824,7 @@ class WC_AJAX { * @since 3.4.0 */ public static function toggle_gateway_enabled() { - if ( current_user_can( 'manage_woocommerce' ) && check_ajax_referer( 'woocommerce-toggle-payment-gateway-enabled', 'security' ) ) { + if ( current_user_can( 'manage_woocommerce' ) && check_ajax_referer( 'woocommerce-toggle-payment-gateway-enabled', 'security' ) && isset( $_POST['gateway_id'] ) ) { // Load gateways. $payment_gateways = WC()->payment_gateways->payment_gateways(); From e830b42ba7b464231ef60c4928cd65fc177a5c92 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 15 Feb 2019 17:10:17 +0000 Subject: [PATCH 275/401] Remove old safari code --- assets/js/frontend/cart-fragments.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/assets/js/frontend/cart-fragments.js b/assets/js/frontend/cart-fragments.js index 563840edea9..d784a10549f 100644 --- a/assets/js/frontend/cart-fragments.js +++ b/assets/js/frontend/cart-fragments.js @@ -96,19 +96,14 @@ jQuery( function( $ ) { // Refresh when storage changes in another tab $( window ).on( 'storage onstorage', function ( e ) { - if ( cart_hash_key === e.originalEvent.key && localStorage.getItem( cart_hash_key ) !== sessionStorage.getItem( cart_hash_key ) ) { + if ( + cart_hash_key === e.originalEvent.key && + localStorage.getItem( cart_hash_key ) !== sessionStorage.getItem( cart_hash_key ) + ) { refresh_cart_fragment(); } }); - // Refresh when page is shown after back button (safari) - $( window ).on( 'pageshow' , function( e ) { - if ( e.originalEvent.persisted ) { - $( '.widget_shopping_cart_content' ).empty(); - $( document.body ).trigger( 'wc_fragment_refresh' ); - } - } ); - try { var wc_fragments = $.parseJSON( sessionStorage.getItem( wc_cart_fragments_params.fragment_name ) ), cart_hash = sessionStorage.getItem( cart_hash_key ), From 3abe162bd6877ccffd05ba1c8fb263f9605dbd83 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 15 Feb 2019 17:23:34 +0000 Subject: [PATCH 276/401] Error handling in wc_ajax_headers --- includes/class-wc-ajax.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php index 6055fc846ea..dd5bc665856 100644 --- a/includes/class-wc-ajax.php +++ b/includes/class-wc-ajax.php @@ -55,12 +55,17 @@ class WC_AJAX { * @since 2.5.0 */ private static function wc_ajax_headers() { - send_origin_headers(); - header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) ); - header( 'X-Robots-Tag: noindex' ); - send_nosniff_header(); - wc_nocache_headers(); - status_header( 200 ); + if ( ! headers_sent() ) { + send_origin_headers(); + send_nosniff_header(); + wc_nocache_headers(); + header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) ); + header( 'X-Robots-Tag: noindex' ); + status_header( 200 ); + } elseif ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { + headers_sent( $file, $line ); + trigger_error( "wc_ajax_headers cannot set headers - headers already sent by {$file} on line {$line}", E_USER_NOTICE ); // @codingStandardsIgnoreLine + } } /** From 34998caa4379a589b1d093f03ba5726949c3c2ea Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 15 Feb 2019 17:35:44 +0000 Subject: [PATCH 277/401] Add time to fragment request --- assets/js/frontend/cart-fragments.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/assets/js/frontend/cart-fragments.js b/assets/js/frontend/cart-fragments.js index d784a10549f..3af4ba8ad6f 100644 --- a/assets/js/frontend/cart-fragments.js +++ b/assets/js/frontend/cart-fragments.js @@ -38,6 +38,9 @@ jQuery( function( $ ) { var $fragment_refresh = { url: wc_cart_fragments_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'get_refreshed_fragments' ), type: 'POST', + data: { + time: new Date().getTime() + }, timeout: wc_cart_fragments_params.request_timeout, success: function( data ) { if ( data && data.fragments ) { @@ -97,13 +100,21 @@ jQuery( function( $ ) { // Refresh when storage changes in another tab $( window ).on( 'storage onstorage', function ( e ) { if ( - cart_hash_key === e.originalEvent.key && - localStorage.getItem( cart_hash_key ) !== sessionStorage.getItem( cart_hash_key ) + cart_hash_key === e.originalEvent.key + && localStorage.getItem( cart_hash_key ) !== sessionStorage.getItem( cart_hash_key ) ) { refresh_cart_fragment(); } }); + // Refresh when page is shown after back button (safari) + $( window ).on( 'pageshow' , function( e ) { + if ( e.originalEvent.persisted ) { + $( '.widget_shopping_cart_content' ).empty(); + $( document.body ).trigger( 'wc_fragment_refresh' ); + } + } ); + try { var wc_fragments = $.parseJSON( sessionStorage.getItem( wc_cart_fragments_params.fragment_name ) ), cart_hash = sessionStorage.getItem( cart_hash_key ), From 5c375d23fce395dd2078184138aceb82ff59ebc1 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 15 Feb 2019 20:39:01 +0000 Subject: [PATCH 278/401] Update dependency eslint to v5.14.0 --- package-lock.json | 155 +++++++++++++++++++++++++++++++++------------- package.json | 2 +- 2 files changed, 112 insertions(+), 45 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0750db45b14..9532a84ddf5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -394,9 +394,9 @@ "dev": true }, "acorn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.6.tgz", - "integrity": "sha512-5M3G/A4uBSMIlfJ+h9W125vJvPFH/zirISsW5qfxF5YzEvXJCtolLoQvM5yZft0DvMcUrPGKPOlgEu55I6iUtA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.0.tgz", + "integrity": "sha512-MW/FjM+IvU9CgBzjO3UIPCE2pyEwUsoFl+VGdczOPEdxfGFjuKny/gN54mOuX7Qxmb9Rg9MCn2oKiSUeW+pjrw==", "dev": true }, "acorn-jsx": { @@ -2302,12 +2302,6 @@ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "dev": true }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, "cjk-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cjk-regex/-/cjk-regex-1.0.2.tgz", @@ -2909,9 +2903,9 @@ } }, "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "requires": { "esutils": "^2.0.2" @@ -3101,35 +3095,35 @@ "dev": true }, "eslint": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.13.0.tgz", - "integrity": "sha512-nqD5WQMisciZC5EHZowejLKQjWGuFS5c70fxqSKlnDME+oz9zmE8KTlX+lHSg+/5wsC/kf9Q9eMkC8qS3oM2fg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.14.0.tgz", + "integrity": "sha512-jrOhiYyENRrRnWlMYANlGZTqb89r2FuRT+615AabBoajhNjeh9ywDNlh2LU9vTqf0WYN+L3xdXuIi7xuj/tK9w==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "ajv": "^6.5.3", + "ajv": "^6.9.1", "chalk": "^2.1.0", "cross-spawn": "^6.0.5", "debug": "^4.0.1", - "doctrine": "^2.1.0", + "doctrine": "^3.0.0", "eslint-scope": "^4.0.0", "eslint-utils": "^1.3.1", "eslint-visitor-keys": "^1.0.0", - "espree": "^5.0.0", + "espree": "^5.0.1", "esquery": "^1.0.1", "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", + "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", "glob": "^7.1.2", "globals": "^11.7.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", - "inquirer": "^6.1.0", + "inquirer": "^6.2.2", "js-yaml": "^3.12.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", - "lodash": "^4.17.5", + "lodash": "^4.17.11", "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", @@ -3140,10 +3134,22 @@ "semver": "^5.5.1", "strip-ansi": "^4.0.0", "strip-json-comments": "^2.0.1", - "table": "^5.0.2", + "table": "^5.2.3", "text-table": "^0.2.0" }, "dependencies": { + "ajv": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", + "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -3159,6 +3165,12 @@ "ms": "^2.1.1" } }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "eslint-scope": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", @@ -3170,9 +3182,15 @@ } }, "globals": { - "version": "11.10.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz", - "integrity": "sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==", + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", + "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "ms": { @@ -3181,6 +3199,45 @@ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "string-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.0.0.tgz", + "integrity": "sha512-rr8CUxBbvOZDUvc5lNIJ+OC1nPVpz+Siw9VBtUjB9b6jZehZLFt0JMCZzShFHIsI8cbhm0EsNIfWJMFV3cu3Ew==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz", + "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==", + "dev": true + }, + "strip-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz", + "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==", + "dev": true, + "requires": { + "ansi-regex": "^4.0.0" + } + } + } + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -3189,6 +3246,18 @@ "requires": { "ansi-regex": "^3.0.0" } + }, + "table": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz", + "integrity": "sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==", + "dev": true, + "requires": { + "ajv": "^6.9.1", + "lodash": "^4.17.11", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + } } } }, @@ -3230,12 +3299,12 @@ "dev": true }, "espree": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.0.tgz", - "integrity": "sha512-1MpUfwsdS9MMoN7ZXqAr9e9UKdVHDcvrJpyx7mm1WuQlx/ygErEQBzgi5Nh5qBHIoYweprhtMkTCb9GhcAIcsA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", "dev": true, "requires": { - "acorn": "^6.0.2", + "acorn": "^6.0.7", "acorn-jsx": "^5.0.0", "eslint-visitor-keys": "^1.0.0" } @@ -3785,13 +3854,12 @@ } }, "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", "dev": true, "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "flat-cache": "^2.0.1" } }, "filename-regex": { @@ -3890,15 +3958,14 @@ } }, "flat-cache": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", - "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", "dev": true, "requires": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" } }, "flatted": { @@ -11448,9 +11515,9 @@ "dev": true }, "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", "dev": true, "requires": { "mkdirp": "^0.5.1" diff --git a/package.json b/package.json index 5c7cbf9df70..96d2fa6534d 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "chromedriver": "2.46.0", "config": "3.0.1", "cross-env": "5.2.0", - "eslint": "5.13.0", + "eslint": "5.14.0", "eslint-config-wpcalypso": "4.0.1", "eslint-plugin-wpcalypso": "4.0.2", "grunt": "1.0.3", From 63492aa98bd1b15335d581463164d31284d4d644 Mon Sep 17 00:00:00 2001 From: Chris Kreidl Date: Sat, 16 Feb 2019 22:59:33 -0500 Subject: [PATCH 279/401] added IDs to other input fields for consistency --- .../admin/meta-boxes/views/html-product-data-shipping.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/admin/meta-boxes/views/html-product-data-shipping.php b/includes/admin/meta-boxes/views/html-product-data-shipping.php index 72b82540271..c8ac5ef3a1d 100644 --- a/includes/admin/meta-boxes/views/html-product-data-shipping.php +++ b/includes/admin/meta-boxes/views/html-product-data-shipping.php @@ -28,8 +28,8 @@ if ( ! defined( 'ABSPATH' ) ) { - - + +

    From afe219d854ac2895082e3ca7bd7b0425ea731151 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 11:49:46 +0000 Subject: [PATCH 280/401] Select an option vs state --- assets/js/frontend/country-select.js | 4 ++++ includes/wc-template-functions.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/assets/js/frontend/country-select.js b/assets/js/frontend/country-select.js index 64f5bf4f89f..4e4f92be609 100644 --- a/assets/js/frontend/country-select.js +++ b/assets/js/frontend/country-select.js @@ -122,6 +122,10 @@ jQuery( function( $ ) { } } + if ( ! placeholder ) { + placeholder = wc_country_select_params.i18n_select_state_text; + } + $statebox.closest( 'p.form-row' ).show(); if ( $statebox.is( 'input' ) ) { diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 3dfd826902e..8634c7d3d33 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -2672,7 +2672,7 @@ if ( ! function_exists( 'woocommerce_form_field' ) ) { } elseif ( ! is_null( $for_country ) && is_array( $states ) ) { - $field .= ' '; foreach ( $states as $ckey => $cvalue ) { From 461010c787e44a552ebe2066d3bc74755c9c23c8 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 11:55:36 +0000 Subject: [PATCH 281/401] update phpcs line --- woocommerce.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/woocommerce.php b/woocommerce.php index e172a3b0c94..058653f4560 100644 --- a/woocommerce.php +++ b/woocommerce.php @@ -30,7 +30,7 @@ if ( ! class_exists( 'WooCommerce' ) ) { * @since 2.1 * @return WooCommerce */ -function WC() { // phpcs:ignore +function WC() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid return WooCommerce::instance(); } From fa97d1a79187b38f10f31b6c65395e190c2efb48 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 12:27:08 +0000 Subject: [PATCH 282/401] Update reduced stock amount under some circumstances --- includes/wc-order-functions.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/includes/wc-order-functions.php b/includes/wc-order-functions.php index 3d311fbf293..5e59c27e165 100644 --- a/includes/wc-order-functions.php +++ b/includes/wc-order-functions.php @@ -691,18 +691,27 @@ function wc_restock_refunded_items( $order, $refunded_line_items ) { } $product = $item->get_product(); $item_stock_reduced = $item->get_meta( '_reduced_stock', true ); + $qty_to_refund = $refunded_line_items[ $item_id ]['qty']; - if ( ! $item_stock_reduced || ! $product || ! $product->managing_stock() ) { + if ( ! $item_stock_reduced || ! $qty_to_refund || ! $product || ! $product->managing_stock() ) { continue; } $old_stock = $product->get_stock_quantity(); - $new_stock = wc_update_product_stock( $product, $refunded_line_items[ $item_id ]['qty'], 'increase' ); + $new_stock = wc_update_product_stock( $product, $qty_to_refund, 'increase' ); + + // Update _reduced_stock meta to track changes. + $item_stock_reduced = $item_stock_reduced - $qty_to_refund; + + if ( 0 < $item_stock_reduced ) { + $item->update_meta_data( '_reduced_stock', $item_stock_reduced ); + } else { + $item->delete_meta_data( '_reduced_stock' ); + } /* translators: 1: product ID 2: old stock level 3: new stock level */ $order->add_order_note( sprintf( __( 'Item #%1$s stock increased from %2$s to %3$s.', 'woocommerce' ), $product->get_id(), $old_stock, $new_stock ) ); - $item->delete_meta_data( '_reduced_stock' ); $item->save(); do_action( 'woocommerce_restock_refunded_item', $product->get_id(), $old_stock, $new_stock, $order, $product ); From d2682170fdff3c8daebb1077927185a186e180f1 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 12:49:07 +0000 Subject: [PATCH 283/401] Set woocommerce_load_webhooks_limit to no limit --- includes/class-woocommerce.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php index 259f2094b23..fac76f4a4ae 100644 --- a/includes/class-woocommerce.php +++ b/includes/class-woocommerce.php @@ -644,7 +644,13 @@ final class WooCommerce { return; } - $limit = apply_filters( 'woocommerce_load_webhooks_limit', 100 ); + /** + * Hook: woocommerce_load_webhooks_limit. + * + * @since 3.6.0 + * @param int $limit Used to limit how many webhooks are loaded. Default: no limit. + */ + $limit = apply_filters( 'woocommerce_load_webhooks_limit', null ); wc_load_webhooks( 'active', $limit ); } From a66293435568c3fd4720b270bad14f0b244c9e41 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 12:53:59 +0000 Subject: [PATCH 284/401] phpcs --- includes/class-woocommerce.php | 2 +- includes/wc-webhook-functions.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php index fac76f4a4ae..ef6ef27b96e 100644 --- a/includes/class-woocommerce.php +++ b/includes/class-woocommerce.php @@ -183,7 +183,7 @@ final class WooCommerce { */ public function log_errors() { $error = error_get_last(); - if ( in_array( $error['type'], array( E_ERROR, E_PARSE, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR ) ) ) { + if ( in_array( $error['type'], array( E_ERROR, E_PARSE, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR ), true ) ) { $logger = wc_get_logger(); $logger->critical( /* translators: 1: error message 2: file name and path 3: line number */ diff --git a/includes/wc-webhook-functions.php b/includes/wc-webhook-functions.php index aab46c13a9d..faa1aad7674 100644 --- a/includes/wc-webhook-functions.php +++ b/includes/wc-webhook-functions.php @@ -65,7 +65,7 @@ add_action( 'woocommerce_deliver_webhook_async', 'wc_deliver_webhook_async', 10, * @return bool */ function wc_is_webhook_valid_topic( $topic ) { - $invalid_topics = array( + $invalid_topics = array( 'action.woocommerce_login_credentials', 'action.woocommerce_product_csv_importer_check_import_file_path', 'action.woocommerce_webhook_should_deliver', From 1d46294ee8b0097321c77b4db1eec0fdecc4327c Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 13:07:04 +0000 Subject: [PATCH 285/401] Update versions --- includes/data-stores/class-wc-webhook-data-store.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/includes/data-stores/class-wc-webhook-data-store.php b/includes/data-stores/class-wc-webhook-data-store.php index 2a98cd37958..62f5f9bf1af 100644 --- a/includes/data-stores/class-wc-webhook-data-store.php +++ b/includes/data-stores/class-wc-webhook-data-store.php @@ -201,12 +201,12 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { } /** - * Get all webhooks IDs. + * Get webhooks IDs from the database. * * @since 3.3.0 * @throws InvalidArgumentException If a $status value is passed in that is not in the known wc_get_webhook_statuses() keys. - * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.5.0. - * @param null|int $limit Limit returned results. @since 3.5.0. + * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.6.0. + * @param null|int $limit Limit returned results. @since 3.6.0. * @return int[] */ public function get_webhooks_ids( $status = '', $limit = null ) { @@ -372,7 +372,7 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { /** * Check if a given string is in known statuses, based on return value of @see wc_get_webhook_statuses(). * - * @since 3.5.0 + * @since 3.6.0 * @throws InvalidArgumentException If $status is not empty and not in the known wc_get_webhook_statuses() keys. * @param string $status Status to check. */ @@ -385,7 +385,7 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { /** * Get the transient key used to cache a set of webhook IDs, optionally filtered by status. * - * @since 3.5.0 + * @since 3.6.0 * @param string $status Optional - status of cache key. * @return string */ @@ -396,7 +396,7 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { /** * Delete the transients used to cache a set of webhook IDs, optionally filtered by status. * - * @since 3.5.0 + * @since 3.6.0 * @param string $status Optional - status of cache to delete, or 'all' to delete all caches. */ private function delete_transients( $status = '' ) { From 30db8a8d9cb61182d103f3f338cca056c6400c06 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 13:07:57 +0000 Subject: [PATCH 286/401] version comment --- includes/wc-webhook-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-webhook-functions.php b/includes/wc-webhook-functions.php index faa1aad7674..a30be5b4504 100644 --- a/includes/wc-webhook-functions.php +++ b/includes/wc-webhook-functions.php @@ -130,7 +130,7 @@ function wc_get_webhook_statuses() { * @since 3.3.0 * @throws Exception If webhook cannot be read/found and $data parameter of WC_Webhook class constructor is set. * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.5.0. - * @param null|int $limit Limit number of webhooks loaded. @since 3.5.0. + * @param null|int $limit Limit number of webhooks loaded. @since 3.6.0. * @return bool */ function wc_load_webhooks( $status = '', $limit = null ) { From 0c53145f2b202e02394ef6ceb43b03aa4716729c Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 13:13:59 +0000 Subject: [PATCH 287/401] Removed limit option from get_webhooks_ids - since transient stores all ids anyway, this logic makes more sense elsewhere --- includes/data-stores/class-wc-webhook-data-store.php | 10 ++-------- .../class-wc-webhooks-data-store-interface.php | 5 ++--- includes/wc-webhook-functions.php | 2 +- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/includes/data-stores/class-wc-webhook-data-store.php b/includes/data-stores/class-wc-webhook-data-store.php index 62f5f9bf1af..278f008da1c 100644 --- a/includes/data-stores/class-wc-webhook-data-store.php +++ b/includes/data-stores/class-wc-webhook-data-store.php @@ -206,11 +206,9 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { * @since 3.3.0 * @throws InvalidArgumentException If a $status value is passed in that is not in the known wc_get_webhook_statuses() keys. * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.6.0. - * @param null|int $limit Limit returned results. @since 3.6.0. * @return int[] */ - public function get_webhooks_ids( $status = '', $limit = null ) { - + public function get_webhooks_ids( $status = '' ) { if ( ! empty( $status ) ) { $this->validate_status( $status ); } @@ -224,14 +222,10 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { 'status' => $status, ) ); - $ids = array_map( 'intval', $ids ); + $ids = array_map( 'absint', $ids ); set_transient( $this->get_transient_key( $status ), $ids ); } - if ( null !== $limit && $limit > 0 ) { - $ids = array_slice( $ids, 0, absint( $limit ) ); - } - return $ids; } diff --git a/includes/interfaces/class-wc-webhooks-data-store-interface.php b/includes/interfaces/class-wc-webhooks-data-store-interface.php index 7ffdd5fd980..d80c5f5b937 100644 --- a/includes/interfaces/class-wc-webhooks-data-store-interface.php +++ b/includes/interfaces/class-wc-webhooks-data-store-interface.php @@ -29,9 +29,8 @@ interface WC_Webhook_Data_Store_Interface { * * @since 3.2.0 * @throws InvalidArgumentException If a $status value is passed in that is not in the known wc_get_webhook_statuses() keys. - * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.5.0. - * @param null|int $limit Limit returned results. @since 3.5.0. + * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.6.0. * @return int[] */ - public function get_webhooks_ids( $status = '', $limit = null ); + public function get_webhooks_ids( $status = '' ); } diff --git a/includes/wc-webhook-functions.php b/includes/wc-webhook-functions.php index a30be5b4504..807d23f1f8f 100644 --- a/includes/wc-webhook-functions.php +++ b/includes/wc-webhook-functions.php @@ -135,7 +135,7 @@ function wc_get_webhook_statuses() { */ function wc_load_webhooks( $status = '', $limit = null ) { $data_store = WC_Data_Store::load( 'webhook' ); - $webhooks = $data_store->get_webhooks_ids( $status, $limit ); + $webhooks = $data_store->get_webhooks_ids( $status ); $loaded = false; foreach ( $webhooks as $webhook_id ) { From d0438264ff6b6c9e56273acf0b38dca7be930c50 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 13:15:32 +0000 Subject: [PATCH 288/401] Add limit to wc_load_webhooks --- includes/wc-webhook-functions.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/includes/wc-webhook-functions.php b/includes/wc-webhook-functions.php index 807d23f1f8f..88150e391b5 100644 --- a/includes/wc-webhook-functions.php +++ b/includes/wc-webhook-functions.php @@ -136,15 +136,19 @@ function wc_get_webhook_statuses() { function wc_load_webhooks( $status = '', $limit = null ) { $data_store = WC_Data_Store::load( 'webhook' ); $webhooks = $data_store->get_webhooks_ids( $status ); - $loaded = false; + $loaded = 0; foreach ( $webhooks as $webhook_id ) { $webhook = new WC_Webhook( $webhook_id ); $webhook->enqueue(); - $loaded = true; + $loaded ++; + + if ( $loaded >= $limit ) { + break; + } } - return $loaded; + return 0 < $loaded; } /** From 3a4ef0a1d8d5b7930652bda0d445f32d3195bef5 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 13:19:12 +0000 Subject: [PATCH 289/401] Avoid getting all webhook ids --- includes/admin/class-wc-admin-webhooks.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/includes/admin/class-wc-admin-webhooks.php b/includes/admin/class-wc-admin-webhooks.php index c4216044c3e..d96feed1844 100644 --- a/includes/admin/class-wc-admin-webhooks.php +++ b/includes/admin/class-wc-admin-webhooks.php @@ -270,8 +270,9 @@ class WC_Admin_Webhooks { echo '

    ' . esc_html__( 'Webhooks', 'woocommerce' ) . ' ' . esc_html__( 'Add webhook', 'woocommerce' ) . '

    '; // Get the webhooks count. - $data_store = WC_Data_Store::load( 'webhook' ); - $count = count( $data_store->get_webhooks_ids() ); + $data_store = WC_Data_Store::load( 'webhook' ); + $num_webhooks = $data_store->get_count_webhooks_by_status(); + $count = array_sum( $num_webhooks ); if ( 0 < $count ) { $webhooks_table_list->process_bulk_action(); From d787c34206c8f7b33e7b68a9c9e50451f3292631 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 13:34:13 +0000 Subject: [PATCH 290/401] get_webhooks_ids escaping --- .../class-wc-webhook-data-store.php | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/includes/data-stores/class-wc-webhook-data-store.php b/includes/data-stores/class-wc-webhook-data-store.php index 278f008da1c..62e000a0a85 100644 --- a/includes/data-stores/class-wc-webhook-data-store.php +++ b/includes/data-stores/class-wc-webhook-data-store.php @@ -205,7 +205,7 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { * * @since 3.3.0 * @throws InvalidArgumentException If a $status value is passed in that is not in the known wc_get_webhook_statuses() keys. - * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.6.0. + * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.6.0. * @return int[] */ public function get_webhooks_ids( $status = '' ) { @@ -271,15 +271,15 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { 'post_modified' => 'date_modified_gmt', ); $orderby = isset( $orderby_mapping[ $args['orderby'] ] ) ? $orderby_mapping[ $args['orderby'] ] : 'webhook_id'; - - $limit = -1 < $args['limit'] ? sprintf( 'LIMIT %d', $args['limit'] ) : ''; - $offset = 0 < $args['offset'] ? sprintf( 'OFFSET %d', $args['offset'] ) : ''; - $status = ! empty( $args['status'] ) ? "AND `status` = '" . sanitize_key( isset( $statuses[ $args['status'] ] ) ? $statuses[ $args['status'] ] : $args['status'] ) . "'" : ''; - $search = ! empty( $args['search'] ) ? "AND `name` LIKE '%" . $wpdb->esc_like( sanitize_text_field( $args['search'] ) ) . "%'" : ''; - $include = ''; - $exclude = ''; - $date_created = ''; - $date_modified = ''; + $order = "ORDER BY {$orderby} " . esc_sql( strtoupper( $args['order'] ) ); + $limit = -1 < $args['limit'] ? $wpdb->prepare( 'LIMIT %d', $args['limit'] ) : ''; + $offset = 0 < $args['offset'] ? $wpdb->prepare( 'OFFSET %d', $args['offset'] ) : ''; + $status = ! empty( $args['status'] ) ? $wpdb->prepare( 'AND `status` = %s', isset( $statuses[ $args['status'] ] ) ? $statuses[ $args['status'] ] : $args['status'] ) : ''; + $search = ! empty( $args['search'] ) ? "AND `name` LIKE '%" . $wpdb->esc_like( sanitize_text_field( $args['search'] ) ) . "%'" : ''; + $include = ''; + $exclude = ''; + $date_created = ''; + $date_modified = ''; if ( ! empty( $args['include'] ) ) { $args['include'] = implode( ',', wp_parse_id_list( $args['include'] ) ); @@ -295,18 +295,16 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { $args['after'] = empty( $args['after'] ) ? '0000-00-00' : $args['after']; $args['before'] = empty( $args['before'] ) ? current_time( 'mysql', 1 ) : $args['before']; - $date_created = "AND `date_created_gmt` BETWEEN STR_TO_DATE('" . $args['after'] . "', '%Y-%m-%d %H:%i:%s') and STR_TO_DATE('" . $args['before'] . "', '%Y-%m-%d %H:%i:%s')"; + $date_created = "AND `date_created_gmt` BETWEEN STR_TO_DATE('" . esc_sql( $args['after'] ) . "', '%Y-%m-%d %H:%i:%s') and STR_TO_DATE('" . esc_sql( $args['before'] ) . "', '%Y-%m-%d %H:%i:%s')"; } if ( ! empty( $args['modified_after'] ) || ! empty( $args['modified_before'] ) ) { $args['modified_after'] = empty( $args['modified_after'] ) ? '0000-00-00' : $args['modified_after']; $args['modified_before'] = empty( $args['modified_before'] ) ? current_time( 'mysql', 1 ) : $args['modified_before']; - $date_modified = "AND `date_modified_gmt` BETWEEN STR_TO_DATE('" . $args['modified_after'] . "', '%Y-%m-%d %H:%i:%s') and STR_TO_DATE('" . $args['modified_before'] . "', '%Y-%m-%d %H:%i:%s')"; + $date_modified = "AND `date_modified_gmt` BETWEEN STR_TO_DATE('" . esc_sql( $args['modified_after'] ) . "', '%Y-%m-%d %H:%i:%s') and STR_TO_DATE('" . esc_sql( $args['modified_before'] ) . "', '%Y-%m-%d %H:%i:%s')"; } - $order = "ORDER BY {$orderby} " . strtoupper( sanitize_key( $args['order'] ) ); - // Check for cache. $cache_key = WC_Cache_Helper::get_cache_prefix( 'webhooks' ) . 'search_webhooks' . md5( implode( ',', $args ) ); $ids = wp_cache_get( $cache_key, 'webhook_search_results' ); @@ -330,9 +328,8 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { {$offset}" ); - $results = $wpdb->get_results( $query ); // WPCS: cache ok, DB call ok, unprepared SQL ok. + $ids = wp_parse_id_list( $wpdb->get_col( $query ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared - $ids = wp_list_pluck( $results, 'webhook_id' ); wp_cache_set( $cache_key, $ids, 'webhook_search_results' ); return $ids; From 14149e4d54de12338dc2b63cc45bf88de7faeb67 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 13:41:06 +0000 Subject: [PATCH 291/401] phpcs --- includes/admin/class-wc-admin-webhooks-table-list.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/includes/admin/class-wc-admin-webhooks-table-list.php b/includes/admin/class-wc-admin-webhooks-table-list.php index 5650228026c..dc0993bf979 100644 --- a/includes/admin/class-wc-admin-webhooks-table-list.php +++ b/includes/admin/class-wc-admin-webhooks-table-list.php @@ -86,8 +86,10 @@ class WC_Admin_Webhooks_Table_List extends WP_List_Table { add_query_arg( array( 'delete' => $webhook->get_id(), - ), admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=webhooks' ) - ), 'delete-webhook' + ), + admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=webhooks' ) + ), + 'delete-webhook' ) ) . '">' . esc_html__( 'Delete permanently', 'woocommerce' ) . '', ); @@ -262,7 +264,10 @@ class WC_Admin_Webhooks_Table_List extends WP_List_Table { echo ''; echo ''; submit_button( - $text, '', '', false, + $text, + '', + '', + false, array( 'id' => 'search-submit', ) From 4b3c1660f1fdaf56b499117fdc3f8e74faa02d0b Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 13:41:14 +0000 Subject: [PATCH 292/401] Dedicated count method --- .../class-wc-webhook-data-store.php | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/includes/data-stores/class-wc-webhook-data-store.php b/includes/data-stores/class-wc-webhook-data-store.php index 62e000a0a85..f621d4154c7 100644 --- a/includes/data-stores/class-wc-webhook-data-store.php +++ b/includes/data-stores/class-wc-webhook-data-store.php @@ -335,6 +335,27 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { return $ids; } + /** + * Count webhooks. + * + * @since 3.6.0 + * @param string $status Status to count. + * @return int + */ + protected function get_webhook_count( $status = 'active' ) { + global $wpdb; + + $count = wp_cache_get( $status . '_count', 'webhooks' ); + + if ( false === $count ) { + $count = absint( $wpdb->get_var( $wpdb->prepare( "SELECT count( webhook_id ) FROM {$wpdb->prefix}wc_webhooks WHERE `status` = %s;", $status ) ) ); + + wp_cache_add( $status . '_count', $count, 'webhooks' ); + } + + return $count; + } + /** * Get total webhook counts by status. * @@ -345,16 +366,7 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { $counts = array(); foreach ( $statuses as $status ) { - $count = count( - $this->search_webhooks( - array( - 'limit' => -1, - 'status' => $status, - ) - ) - ); - - $counts[ $status ] = $count; + $counts[ $status ] = $this->get_webhook_count( $status ); } return $counts; From 71d3121872d8198c7e5846f84546eee060326547 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 14:58:32 +0000 Subject: [PATCH 293/401] Performance: Support pagination to avoid double queries to search webhooks --- .../class-wc-admin-webhooks-table-list.php | 13 ++- .../api/legacy/v2/class-wc-api-webhooks.php | 23 ++---- .../api/legacy/v3/class-wc-api-webhooks.php | 23 ++---- .../v1/class-wc-rest-webhooks-controller.php | 31 ++++---- .../class-wc-webhook-data-store.php | 79 +++++++++++++------ 5 files changed, 82 insertions(+), 87 deletions(-) diff --git a/includes/admin/class-wc-admin-webhooks-table-list.php b/includes/admin/class-wc-admin-webhooks-table-list.php index dc0993bf979..d2c6ea5bbc1 100644 --- a/includes/admin/class-wc-admin-webhooks-table-list.php +++ b/includes/admin/class-wc-admin-webhooks-table-list.php @@ -297,22 +297,19 @@ class WC_Admin_Webhooks_Table_List extends WP_List_Table { $args['search'] = sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ); // WPCS: input var okay, CSRF ok. } + $args['paginate'] = true; + // Get the webhooks. $data_store = WC_Data_Store::load( 'webhook' ); $webhooks = $data_store->search_webhooks( $args ); - $this->items = array_map( 'wc_get_webhook', $webhooks ); - - // Get total items. - $args['limit'] = -1; - $args['offset'] = 0; - $total_items = count( $data_store->search_webhooks( $args ) ); + $this->items = array_map( 'wc_get_webhook', $webhooks->webhooks ); // Set the pagination. $this->set_pagination_args( array( - 'total_items' => $total_items, + 'total_items' => $webhooks->total, 'per_page' => $per_page, - 'total_pages' => ceil( $total_items / $per_page ), + 'total_pages' => $webhooks->max_num_pages, ) ); } diff --git a/includes/api/legacy/v2/class-wc-api-webhooks.php b/includes/api/legacy/v2/class-wc-api-webhooks.php index 543caa9d77b..877cb8193ad 100644 --- a/includes/api/legacy/v2/class-wc-api-webhooks.php +++ b/includes/api/legacy/v2/class-wc-api-webhooks.php @@ -326,21 +326,6 @@ class WC_API_Webhooks extends WC_API_Resource { return $webhook->delete( true ); } - /** - * Get webhooks total results - * - * @since 3.3.0 - * @param array $args Request arguments for filtering query. - * @return array - */ - private function get_webhooks_total_results( $args = array() ) { - $data_store = WC_Data_Store::load( 'webhook' ); - $args['limit'] = -1; - $args['offset'] = 0; - - return count( $data_store->search_webhooks( $args ) ); - } - /** * Helper method to get webhook post objects * @@ -390,6 +375,8 @@ class WC_API_Webhooks extends WC_API_Resource { unset( $args['date_query'] ); } + $args['paginate'] = true; + // Get the webhooks. $data_store = WC_Data_Store::load( 'webhook' ); $results = $data_store->search_webhooks( $args ); @@ -397,12 +384,12 @@ class WC_API_Webhooks extends WC_API_Resource { // Get total items. $headers = new stdClass; $headers->page = $page; - $headers->total = $this->get_webhooks_total_results( $args ); + $headers->total = $results->total; $headers->is_single = $args['limit'] > $headers->total; - $headers->total_pages = ceil( $headers->total / $args['limit'] ); + $headers->total_pages = $results->max_num_pages; return array( - 'results' => $results, + 'results' => $results->webhooks, 'headers' => $headers, ); } diff --git a/includes/api/legacy/v3/class-wc-api-webhooks.php b/includes/api/legacy/v3/class-wc-api-webhooks.php index 543caa9d77b..877cb8193ad 100644 --- a/includes/api/legacy/v3/class-wc-api-webhooks.php +++ b/includes/api/legacy/v3/class-wc-api-webhooks.php @@ -326,21 +326,6 @@ class WC_API_Webhooks extends WC_API_Resource { return $webhook->delete( true ); } - /** - * Get webhooks total results - * - * @since 3.3.0 - * @param array $args Request arguments for filtering query. - * @return array - */ - private function get_webhooks_total_results( $args = array() ) { - $data_store = WC_Data_Store::load( 'webhook' ); - $args['limit'] = -1; - $args['offset'] = 0; - - return count( $data_store->search_webhooks( $args ) ); - } - /** * Helper method to get webhook post objects * @@ -390,6 +375,8 @@ class WC_API_Webhooks extends WC_API_Resource { unset( $args['date_query'] ); } + $args['paginate'] = true; + // Get the webhooks. $data_store = WC_Data_Store::load( 'webhook' ); $results = $data_store->search_webhooks( $args ); @@ -397,12 +384,12 @@ class WC_API_Webhooks extends WC_API_Resource { // Get total items. $headers = new stdClass; $headers->page = $page; - $headers->total = $this->get_webhooks_total_results( $args ); + $headers->total = $results->total; $headers->is_single = $args['limit'] > $headers->total; - $headers->total_pages = ceil( $headers->total / $args['limit'] ); + $headers->total_pages = $results->max_num_pages; return array( - 'results' => $results, + 'results' => $results->webhooks, 'headers' => $headers, ); } diff --git a/includes/api/v1/class-wc-rest-webhooks-controller.php b/includes/api/v1/class-wc-rest-webhooks-controller.php index 5971bfcfc12..7ebb28edffd 100644 --- a/includes/api/v1/class-wc-rest-webhooks-controller.php +++ b/includes/api/v1/class-wc-rest-webhooks-controller.php @@ -247,32 +247,29 @@ class WC_REST_Webhooks_V1_Controller extends WC_REST_Controller { */ $prepared_args = apply_filters( 'woocommerce_rest_webhook_query', $args, $request ); unset( $prepared_args['page'] ); + $prepared_args['paginate'] = true; // Get the webhooks. - $data_store = WC_Data_Store::load( 'webhook' ); - $results = $data_store->search_webhooks( $prepared_args ); + $webhooks = array(); + $data_store = WC_Data_Store::load( 'webhook' ); + $results = $data_store->search_webhooks( $prepared_args ); + $webhook_ids = $results->webhooks; - $webhooks = array(); - foreach ( $results as $webhook_id ) { + foreach ( $webhook_ids as $webhook_id ) { $data = $this->prepare_item_for_response( $webhook_id, $request ); $webhooks[] = $this->prepare_response_for_collection( $data ); } - $response = rest_ensure_response( $webhooks ); + $response = rest_ensure_response( $webhooks ); + $per_page = (int) $prepared_args['limit']; + $page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 ); + $total_webhooks = $results->total; + $max_pages = $results->max_num_pages; + $base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) ); - // Store pagination values for headers then unset for count query. - $per_page = (int) $prepared_args['limit']; - $page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 ); + $response->header( 'X-WP-Total', $total_webhooks ); + $response->header( 'X-WP-TotalPages', $max_pages ); - // Calculate totals. - $prepared_args['limit'] = -1; - $prepared_args['offset'] = 0; - $total_webhooks = count( $data_store->search_webhooks( $prepared_args ) ); - $response->header( 'X-WP-Total', (int) $total_webhooks ); - $max_pages = ceil( $total_webhooks / $per_page ); - $response->header( 'X-WP-TotalPages', (int) $max_pages ); - - $base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) ); if ( $page > 1 ) { $prev_page = $page - 1; if ( $prev_page > $max_pages ) { diff --git a/includes/data-stores/class-wc-webhook-data-store.php b/includes/data-stores/class-wc-webhook-data-store.php index f621d4154c7..27f9b285fc2 100644 --- a/includes/data-stores/class-wc-webhook-data-store.php +++ b/includes/data-stores/class-wc-webhook-data-store.php @@ -233,7 +233,7 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { * Search webhooks. * * @param array $args Search arguments. - * @return array + * @return array|object */ public function search_webhooks( $args ) { global $wpdb; @@ -241,10 +241,11 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { $args = wp_parse_args( $args, array( - 'limit' => 10, - 'offset' => 0, - 'order' => 'DESC', - 'orderby' => 'id', + 'limit' => 10, + 'offset' => 0, + 'order' => 'DESC', + 'orderby' => 'id', + 'paginate' => false, ) ); @@ -306,33 +307,59 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface { } // Check for cache. - $cache_key = WC_Cache_Helper::get_cache_prefix( 'webhooks' ) . 'search_webhooks' . md5( implode( ',', $args ) ); - $ids = wp_cache_get( $cache_key, 'webhook_search_results' ); + $cache_key = WC_Cache_Helper::get_cache_prefix( 'webhooks' ) . 'search_webhooks' . md5( implode( ',', $args ) ); + $cache_value = wp_cache_get( $cache_key, 'webhook_search_results' ); - if ( false !== $ids ) { - return $ids; + if ( $cache_value ) { + return $cache_value; } - $query = trim( - "SELECT webhook_id - FROM {$wpdb->prefix}wc_webhooks - WHERE 1=1 - {$status} - {$search} - {$include} - {$exclude} - {$date_created} - {$date_modified} - {$order} - {$limit} - {$offset}" - ); + if ( $args['paginate'] ) { + $query = trim( + "SELECT SQL_CALC_FOUND_ROWS webhook_id + FROM {$wpdb->prefix}wc_webhooks + WHERE 1=1 + {$status} + {$search} + {$include} + {$exclude} + {$date_created} + {$date_modified} + {$order} + {$limit} + {$offset}" + ); - $ids = wp_parse_id_list( $wpdb->get_col( $query ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + $webhook_ids = wp_parse_id_list( $wpdb->get_col( $query ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + $total = (int) $wpdb->get_var( 'SELECT FOUND_ROWS();' ); + $return_value = (object) array( + 'webhooks' => $webhook_ids, + 'total' => $total, + 'max_num_pages' => $args['limit'] > 1 ? ceil( $total / $args['limit'] ) : 1, + ); + } else { + $query = trim( + "SELECT webhook_id + FROM {$wpdb->prefix}wc_webhooks + WHERE 1=1 + {$status} + {$search} + {$include} + {$exclude} + {$date_created} + {$date_modified} + {$order} + {$limit} + {$offset}" + ); - wp_cache_set( $cache_key, $ids, 'webhook_search_results' ); + $webhook_ids = wp_parse_id_list( $wpdb->get_col( $query ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + $return_value = $webhook_ids; + } - return $ids; + wp_cache_set( $cache_key, $return_value, 'webhook_search_results' ); + + return $return_value; } /** From 06ee00c3dae4235d2d8a69bb7d4c3f681e575177 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 15:05:48 +0000 Subject: [PATCH 294/401] Update from master --- assets/css/activation.scss | 14 +- assets/css/admin.scss | 12556 +++++++++++++++-------------- assets/css/twenty-seventeen.scss | 249 +- assets/css/woocommerce.scss | 284 +- 4 files changed, 6866 insertions(+), 6237 deletions(-) diff --git a/assets/css/activation.scss b/assets/css/activation.scss index 7279e617b07..2674c9589ad 100644 --- a/assets/css/activation.scss +++ b/assets/css/activation.scss @@ -14,6 +14,7 @@ div.woocommerce-message { p.woocommerce-actions, .woocommerce-message { + .button-primary { background: #bb77ae; border-color: #a36597; @@ -21,7 +22,9 @@ p.woocommerce-actions, color: #fff; text-shadow: 0 -1px 1px #a36597, 1px 0 1px #a36597, 0 1px 1px #a36597, -1px 0 1px #a36597; - &:hover, &:focus, &:active { + &:hover, + &:focus, + &:active { background: #a36597; border-color: #a36597; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; @@ -33,11 +36,12 @@ p.woocommerce-actions, float: right; top: 0; right: 0; - padding: 0px 15px 10px 28px; + padding: 0 15px 10px 28px; margin-top: -10px; font-size: 13px; line-height: 1.23076923; text-decoration: none; + &::before { position: relative; top: 18px; @@ -67,6 +71,7 @@ div.woocommerce-legacy-shipping-notice, div.woocommerce-no-shipping-methods-notice { overflow: hidden; padding: 1px 12px; + p { position: relative; z-index: 1; @@ -77,9 +82,10 @@ div.woocommerce-no-shipping-methods-notice { font-size: 1.1em; } } + &::before { - content: '\e01b'; - font-family: 'WooCommerce'; + content: "\e01b"; + font-family: "WooCommerce"; text-align: center; line-height: 1; color: #f7f1f6; diff --git a/assets/css/admin.scss b/assets/css/admin.scss index ec4dcc27f95..7f78b2cd538 100644 --- a/assets/css/admin.scss +++ b/assets/css/admin.scss @@ -7,733 +7,749 @@ /** * Imports */ - @import 'mixins'; - @import 'variables'; - @import 'animation'; - @import 'fonts'; +@import "mixins"; +@import "variables"; +@import "animation"; +@import "fonts"; - /** +/** * Styling begins */ - .blockUI.blockOverlay { - @include loader(); - } - - .wc_addons_wrap { - max-width: 1200px; - - h1.search-form-title { - clear: left; - padding: 0; - } - - form.search-form { - clear: both; - display: block; - position: relative; - margin-top: 1em; - margin-bottom: 1em; - - input { - border: 1px solid #ddd; - box-shadow: none; - height: 53px; - padding-left: 50px; - width: 100%; - margin: 0; - } - - button { - background: none; - border: none; - cursor: pointer; - height: 53px; - position: absolute; - width: 53px; - } - } - - .update-plugins .update-count { - background-color: #d54e21; - border-radius: 10px; - color: #fff; - display: inline-block; - font-size: 9px; - font-weight: 600; - line-height: 17px; - margin: 1px 0 0 2px; - padding: 0 6px; - vertical-align: text-top; - } - - .addons-featured { - margin: 0; - } - - ul.subsubsub.subsubsub { - margin: -2px 0 12px; - } - - .subsubsub li::after { - content: '|'; - } - - .subsubsub li:last-child::after { - content: ''; - } - - .addons-banner-block-item-icon, - .addons-column-block-item-icon { - align-items: center; - display: flex; - justify-content: center; - } - - .addons-banner-block, - .addons-wcs-banner-block { - background: #ffffff; - border: 1px solid #ddd; - margin: 0 0 1em 0; - padding: 2em 2em 1em; - } - - .addons-banner-block img { - height: 62px; - } - - .addons-banner-block p { - margin: 0 0 20px; - } - - .addons-banner-block-items { - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: space-around; - margin: 0 -10px 0 -10px; - } - - .addons-banner-block-item { - border: 1px solid #e6e6e6; - border-radius: 3px; - flex: 1; - margin: 1em; - min-width: 200px; - width: 30%; - } - - .addons-banner-block-item-icon { - background: #f7f7f7; - height: 143px; - } - - .addons-banner-block-item-content { - display: flex; - flex-direction: column; - height: 184px; - justify-content: space-between; - padding: 24px; - } - - .addons-banner-block-item-content h3 { - margin-top: 0; - } - - .addons-banner-block-item-content p { - margin: 0 0 auto; - } - - .addons-wcs-banner-block { - display: flex; - align-items: center; - } - - .addons-wcs-banner-block-image { - background: #f7f7f7; - border: 1px solid #e6e6e6; - margin-right: 2em; - padding: 4em; - - .addons-img { - max-height: 86px; - max-width: 97px; - } - } - - .addons-shipping-methods .addons-wcs-banner-block { - margin-left: 0; - margin-right: 0; - margin-top: 1em; - } - - .addons-wcs-banner-block-content { - display: flex; - flex-direction: column; - justify-content: space-around; - align-self: stretch; - padding: 1em 0; - - h1 { - padding-bottom: 0; - } - - p { - margin-bottom: 0; - } - - .wcs-service-logo { - max-width: 40px; - } - } - - .addons-column-section { - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: space-around; - } - - .addons-column { - flex: 1; - width: 50%; - padding: 0 .5em; - } - - .addons-column:nth-child(2) { - margin-right: 0; - } - - .addons-small-light-block, - .addons-small-dark-block, - .addons-column-block { - box-sizing: border-box; - border: 1px solid #ddd; - margin: 0 0 1em; - padding: 20px; - } - - .addons-column-block img { - max-height: 50px; - max-width: 50px; - } - - .addons-small-light-block, - .addons-column-block { - background: #ffffff; - } - - .addons-column-block-left { - float: left; - } - - .addons-column-block-right { - float: right; - } - - .addons-column-block-item { - border-top: 2px solid #f9f9f9; - flex-direction: row; - flex-wrap: wrap; - justify-content: space-between; - margin: 0 -20px; - padding: 20px; - } - - .addons-column-block-item-icon { - background: #f7f7f7; - border: 1px solid #e6e6e6; - height: 100px; - margin: 0 10px 10px 0; - width: 100px; - } - - .addons-column-block-item-content { - display: flex; - flex: 1; - flex-wrap: wrap; - height: 20%; - justify-content: space-between; - min-width: 200px; - } - - .addons-column-block-item-content h2 { - float: left; - margin-top: 8px; - } - - .addons-column-block-item-content a { - float: right; - } - - .addons-column-block-item-content p { - float: left; - } - - .addons-banner-block-item, - .addons-column-block-item { - display: none; - } - - .addons-banner-block-item:nth-child(-n+3) { - display: block; - } - .addons-column-block-item:nth-of-type(-n+3) { - display: flex; - } - - .addons-small-dark-block { - background-color: #54687d; - text-align: center; - } - - .addons-small-dark-items { - display: flex; - flex-wrap: wrap; - justify-content: space-around; - } - - .addons-small-dark-item { - margin: 0 0 20px; - } - - .addons-small-dark-block h1 { - color: #ffffff; - } - - .addons-small-dark-block p { - color: #fafafa; - } - - .addons-small-dark-item-icon img { - height: 30px; - } - - .addons-small-dark-item a { - margin: 28px auto 0; - } - - .addons-small-light-block { - display: flex; - flex-wrap: wrap; - } - - .addons-small-light-block h1 { - margin-top: -12px; - } - - .addons-small-light-block p { - margin-top: 0; - } - - .addons-small-light-block img { - height: 225px; - margin: 0 0 0 -20px; - } - - .addons-small-light-block-content { - display: flex; - flex: 1 1 100px; - flex-direction: column; - justify-content: space-around; - } - - .addons-small-light-block-buttons { - display: flex; - justify-content: space-between; - } - - .addons-small-light-block-content a { - width: 48%; - } - - .addons-button { - border-radius: 3px; - cursor: pointer; - display: block; - height: 37px; - line-height: 37px; - text-align: center; - text-decoration: none; - width: 124px; - } - - .addons-button-solid { - background-color: #955a89; - color: #ffffff; - } - - .addons-button-solid:hover { - color: #ffffff; - opacity: 0.8; - } - - .addons-button-outline-green { - border: 1px solid #73ae39; - color: #73ae39; - } - - .addons-button-outline-green:hover { - color: #73ae39; - opacity: 0.8; - } - - .addons-button-outline-white { - border: 1px solid #ffffff; - color: #ffffff; - } - - .addons-button-outline-white:hover { - color: #ffffff; - opacity: 0.8; - } - - .addons-button-installed { - background: #e6e6e6; - color: #3c3c3c; - } - - .addons-button-installed:hover { - color: #3c3c3c; - opacity: 0.8; - } - - @media only screen and (max-width : 400px) { - .addons-featured { - margin: -1% -5%; - } - - .addons-button { - width: 100%; - } - - .addons-small-dark-item { - width: 100%; - } - - .addons-column-block-item-icon { - background: none; - border: none; - height: 75px; - margin: 0 10px 10px 0; - width: 75px; - } - } - - .products { - overflow: hidden; - display: flex; - flex-flow: row; - flex-wrap: wrap; - margin: 0 -.5em; - - li { - float: left; - border: 1px solid #ddd; - margin: 0 .5em 1em !important; - padding: 0; - vertical-align: top; - width: 25%; - min-width: 280px; - min-height: 220px; - flex: 1; - overflow: hidden; - background: #f5f5f5; - box-shadow: - inset 0 1px 0 rgba(255, 255, 255, 0.2), - inset 0 -1px 0 rgba(0, 0, 0, 0.1); - - a { - text-decoration: none; - color: inherit; - display: block; - height: 100%; - - .product-img-wrap { - background: #fff; - display: block; - } - - img { - max-width: 258px; - max-height: 24px; - padding: 17px 20px; - display: block; - margin: 0; - background: #fff; - border-right: 260px solid #fff; - } - - img.extension-thumb + h3 { - display: none; - } - - .price { - display: none; - } - - h2, h3 { - margin: 0 !important; - padding: 20px !important; - background: #fff; - } - - p { - padding: 20px !important; - margin: 0 !important; - border-top: 1px solid #f1f1f1; - } - - &:hover, - &:focus { - background-color: #fff; - } - } - } - } - - .storefront { - background: url('../images/storefront-bg.jpg') bottom right #f6f6f6; - border: 1px solid #ddd; - margin-top: 1em; - padding: 20px; - overflow: hidden; - zoom: 1; - - img { - width: 278px; - height: auto; - float: left; - margin: 0 20px 0 0; - box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1); - } - - p { - max-width: 750px; - } - } - } - - .woocommerce-message, - .woocommerce-BlankState { - a.button-primary, - button.button-primary { - background: #bb77ae; - border-color: #a36597; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; - color: #fff; - text-shadow: 0 -1px 1px #a36597, 1px 0 1px #a36597, 0 1px 1px #a36597, -1px 0 1px #a36597; - display: inline-block; - - &:hover, &:focus, &:active { - background: #a36597; - border-color: #a36597; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; - } - } - } - - .woocommerce-message { - position: relative; - border-left-color: #cc99c2 !important; - overflow: hidden; - - a.skip, - a.docs { - text-decoration: none !important; - } - - a.woocommerce-message-close { - position: static; - float: right; - padding: 0px 15px 10px 28px; - margin-top: -10px; - font-size: 13px; - line-height: 1.23076923; - text-decoration: none; - &::before { - position: relative; - top: 18px; - left: -20px; - transition: all 0.1s ease-in-out; - } - } - - .twitter-share-button { - margin-top: -3px; - margin-left: 3px; - vertical-align: middle; - } - } - - #variable_product_options #message, #variable_product_options .notice { - margin: 10px; - } - - .clear { - clear: both; - } - - .wrap.woocommerce div.updated, - .wrap.woocommerce div.error { - margin-top: 10px; - } - - mark.amount { - background: transparent none; - color: inherit; - } - - /** +.blockUI.blockOverlay { + + @include loader(); +} + +.wc_addons_wrap { + max-width: 1200px; + + h1.search-form-title { + clear: left; + padding: 0; + } + + form.search-form { + clear: both; + display: block; + position: relative; + margin-top: 1em; + margin-bottom: 1em; + + input { + border: 1px solid #ddd; + box-shadow: none; + height: 53px; + padding-left: 50px; + width: 100%; + margin: 0; + } + + button { + background: none; + border: none; + cursor: pointer; + height: 53px; + position: absolute; + width: 53px; + } + } + + .update-plugins .update-count { + background-color: #d54e21; + border-radius: 10px; + color: #fff; + display: inline-block; + font-size: 9px; + font-weight: 600; + line-height: 17px; + margin: 1px 0 0 2px; + padding: 0 6px; + vertical-align: text-top; + } + + .addons-featured { + margin: 0; + } + + ul.subsubsub.subsubsub { + margin: -2px 0 12px; + } + + .subsubsub li::after { + content: "|"; + } + + .subsubsub li:last-child::after { + content: ""; + } + + .addons-banner-block-item-icon, + .addons-column-block-item-icon { + align-items: center; + display: flex; + justify-content: center; + } + + .addons-banner-block, + .addons-wcs-banner-block { + background: #fff; + border: 1px solid #ddd; + margin: 0 0 1em 0; + padding: 2em 2em 1em; + } + + .addons-banner-block img { + height: 62px; + } + + .addons-banner-block p { + margin: 0 0 20px; + } + + .addons-banner-block-items { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-around; + margin: 0 -10px 0 -10px; + } + + .addons-banner-block-item { + border: 1px solid #e6e6e6; + border-radius: 3px; + flex: 1; + margin: 1em; + min-width: 200px; + width: 30%; + } + + .addons-banner-block-item-icon { + background: #f7f7f7; + height: 143px; + } + + .addons-banner-block-item-content { + display: flex; + flex-direction: column; + height: 184px; + justify-content: space-between; + padding: 24px; + } + + .addons-banner-block-item-content h3 { + margin-top: 0; + } + + .addons-banner-block-item-content p { + margin: 0 0 auto; + } + + .addons-wcs-banner-block { + display: flex; + align-items: center; + } + + .addons-wcs-banner-block-image { + background: #f7f7f7; + border: 1px solid #e6e6e6; + margin-right: 2em; + padding: 4em; + + .addons-img { + max-height: 86px; + max-width: 97px; + } + } + + .addons-shipping-methods .addons-wcs-banner-block { + margin-left: 0; + margin-right: 0; + margin-top: 1em; + } + + .addons-wcs-banner-block-content { + display: flex; + flex-direction: column; + justify-content: space-around; + align-self: stretch; + padding: 1em 0; + + h1 { + padding-bottom: 0; + } + + p { + margin-bottom: 0; + } + + .wcs-service-logo { + max-width: 40px; + } + } + + .addons-column-section { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-around; + } + + .addons-column { + flex: 1; + width: 50%; + padding: 0 0.5em; + } + + .addons-column:nth-child(2) { + margin-right: 0; + } + + .addons-small-light-block, + .addons-small-dark-block, + .addons-column-block { + box-sizing: border-box; + border: 1px solid #ddd; + margin: 0 0 1em; + padding: 20px; + } + + .addons-column-block img { + max-height: 50px; + max-width: 50px; + } + + .addons-small-light-block, + .addons-column-block { + background: #fff; + } + + .addons-column-block-left { + float: left; + } + + .addons-column-block-right { + float: right; + } + + .addons-column-block-item { + border-top: 2px solid #f9f9f9; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; + margin: 0 -20px; + padding: 20px; + } + + .addons-column-block-item-icon { + background: #f7f7f7; + border: 1px solid #e6e6e6; + height: 100px; + margin: 0 10px 10px 0; + width: 100px; + } + + .addons-column-block-item-content { + display: flex; + flex: 1; + flex-wrap: wrap; + height: 20%; + justify-content: space-between; + min-width: 200px; + } + + .addons-column-block-item-content h2 { + float: left; + margin-top: 8px; + } + + .addons-column-block-item-content a { + float: right; + } + + .addons-column-block-item-content p { + float: left; + } + + .addons-banner-block-item, + .addons-column-block-item { + display: none; + } + + .addons-banner-block-item:nth-child(-n+3) { + display: block; + } + + .addons-column-block-item:nth-of-type(-n+3) { + display: flex; + } + + .addons-small-dark-block { + background-color: #54687d; + text-align: center; + } + + .addons-small-dark-items { + display: flex; + flex-wrap: wrap; + justify-content: space-around; + } + + .addons-small-dark-item { + margin: 0 0 20px; + } + + .addons-small-dark-block h1 { + color: #fff; + } + + .addons-small-dark-block p { + color: #fafafa; + } + + .addons-small-dark-item-icon img { + height: 30px; + } + + .addons-small-dark-item a { + margin: 28px auto 0; + } + + .addons-small-light-block { + display: flex; + flex-wrap: wrap; + } + + .addons-small-light-block h1 { + margin-top: -12px; + } + + .addons-small-light-block p { + margin-top: 0; + } + + .addons-small-light-block img { + height: 225px; + margin: 0 0 0 -20px; + } + + .addons-small-light-block-content { + display: flex; + flex: 1 1 100px; + flex-direction: column; + justify-content: space-around; + } + + .addons-small-light-block-buttons { + display: flex; + justify-content: space-between; + } + + .addons-small-light-block-content a { + width: 48%; + } + + .addons-button { + border-radius: 3px; + cursor: pointer; + display: block; + height: 37px; + line-height: 37px; + text-align: center; + text-decoration: none; + width: 124px; + } + + .addons-button-solid { + background-color: #955a89; + color: #fff; + } + + .addons-button-solid:hover { + color: #fff; + opacity: 0.8; + } + + .addons-button-outline-green { + border: 1px solid #73ae39; + color: #73ae39; + } + + .addons-button-outline-green:hover { + color: #73ae39; + opacity: 0.8; + } + + .addons-button-outline-white { + border: 1px solid #fff; + color: #fff; + } + + .addons-button-outline-white:hover { + color: #fff; + opacity: 0.8; + } + + .addons-button-installed { + background: #e6e6e6; + color: #3c3c3c; + } + + .addons-button-installed:hover { + color: #3c3c3c; + opacity: 0.8; + } + + @media only screen and (max-width: 400px) { + + .addons-featured { + margin: -1% -5%; + } + + .addons-button { + width: 100%; + } + + .addons-small-dark-item { + width: 100%; + } + + .addons-column-block-item-icon { + background: none; + border: none; + height: 75px; + margin: 0 10px 10px 0; + width: 75px; + } + } + + .products { + overflow: hidden; + display: flex; + flex-flow: row; + flex-wrap: wrap; + margin: 0 -0.5em; + + li { + float: left; + border: 1px solid #ddd; + margin: 0 0.5em 1em !important; + padding: 0; + vertical-align: top; + width: 25%; + min-width: 280px; + min-height: 220px; + flex: 1; + overflow: hidden; + background: #f5f5f5; + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.2), + inset 0 -1px 0 rgba(0, 0, 0, 0.1); + + a { + text-decoration: none; + color: inherit; + display: block; + height: 100%; + + .product-img-wrap { + background: #fff; + display: block; + } + + img { + max-width: 258px; + max-height: 24px; + padding: 17px 20px; + display: block; + margin: 0; + background: #fff; + border-right: 260px solid #fff; + } + + img.extension-thumb + h3 { + display: none; + } + + .price { + display: none; + } + + h2, + h3 { + margin: 0 !important; + padding: 20px !important; + background: #fff; + } + + p { + padding: 20px !important; + margin: 0 !important; + border-top: 1px solid #f1f1f1; + } + + &:hover, + &:focus { + background-color: #fff; + } + } + } + } + + .storefront { + background: url("../images/storefront-bg.jpg") bottom right #f6f6f6; + border: 1px solid #ddd; + margin-top: 1em; + padding: 20px; + overflow: hidden; + zoom: 1; + + img { + width: 278px; + height: auto; + float: left; + margin: 0 20px 0 0; + box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1); + } + + p { + max-width: 750px; + } + } +} + +.woocommerce-message, +.woocommerce-BlankState { + + a.button-primary, + button.button-primary { + background: #bb77ae; + border-color: #a36597; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; + color: #fff; + text-shadow: 0 -1px 1px #a36597, 1px 0 1px #a36597, 0 1px 1px #a36597, -1px 0 1px #a36597; + display: inline-block; + + &:hover, + &:focus, + &:active { + background: #a36597; + border-color: #a36597; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; + } + } +} + +.woocommerce-message { + position: relative; + border-left-color: #cc99c2 !important; + overflow: hidden; + + a.skip, + a.docs { + text-decoration: none !important; + } + + a.woocommerce-message-close { + position: static; + float: right; + padding: 0 15px 10px 28px; + margin-top: -10px; + font-size: 13px; + line-height: 1.23076923; + text-decoration: none; + + &::before { + position: relative; + top: 18px; + left: -20px; + transition: all 0.1s ease-in-out; + } + } + + .twitter-share-button { + margin-top: -3px; + margin-left: 3px; + vertical-align: middle; + } +} + +#variable_product_options #message, + #variable_product_options .notice { + margin: 10px; +} + +.clear { + clear: both; +} + +.wrap.woocommerce div.updated, +.wrap.woocommerce div.error { + margin-top: 10px; +} + +mark.amount { + background: transparent none; + color: inherit; +} + +/** * Help Tip */ - .woocommerce-help-tip { - color: #666; - display: inline-block; - font-size: 1.1em; - font-style: normal; - height: 16px; - line-height: 16px; - position: relative; - vertical-align: middle; - width: 16px; +.woocommerce-help-tip { + color: #666; + display: inline-block; + font-size: 1.1em; + font-style: normal; + height: 16px; + line-height: 16px; + position: relative; + vertical-align: middle; + width: 16px; - &::after { - @include icon_dashicons( '\f223' ); - cursor: help; - } - } + &::after { - h2 .woocommerce-help-tip { - margin-top: -5px; - margin-left: 0.25em; - } + @include icon_dashicons( "\f223" ); + cursor: help; + } +} - table.wc_status_table { - margin-bottom: 1em; +h2 .woocommerce-help-tip { + margin-top: -5px; + margin-left: 0.25em; +} - h2 { - font-size: 14px; - margin: 0; - } +table.wc_status_table { + margin-bottom: 1em; - tr:nth-child( 2n ) { - th, - td { - background: #fcfcfc; - } - } + h2 { + font-size: 14px; + margin: 0; + } - th { - font-weight: 700; - padding: 9px; - } + tr:nth-child(2n) { - td:first-child { - width: 33%; - } + th, + td { + background: #fcfcfc; + } + } - td.help { - width: 1em; - } + th { + font-weight: 700; + padding: 9px; + } - td, th { - font-size: 1.1em; - font-weight: normal; + td:first-child { + width: 33%; + } - &.run-tool { - text-align:right; - } + td.help { + width: 1em; + } - strong.name { - display: block; - margin-bottom: .5em; - } + td, + th { + font-size: 1.1em; + font-weight: normal; - mark { - background: transparent none; - } + &.run-tool { + text-align: right; + } - mark.yes { - color: $green; - } + strong.name { + display: block; + margin-bottom: 0.5em; + } - mark.no { - color: #999; - } + mark { + background: transparent none; + } - mark.error, .red { - color: $red; - } + mark.yes { + color: $green; + } - ul { - margin: 0; - } - } + mark.no { + color: #999; + } - .help_tip { - cursor: help; - } - } + mark.error, + .red { + color: $red; + } - table.wc_status_table--tools { - td, th { - padding: 2em; - } - } + ul { + margin: 0; + } + } - .taxonomy-product_cat { - .check-column .woocommerce-help-tip { - font-size: 1.5em; - margin: -3px 0 0 5px; - display: block; - position: absolute; - } - } + .help_tip { + cursor: help; + } +} - #debug-report { - display: none; - margin: 10px 0; - padding: 0; - position: relative; +table.wc_status_table--tools { - textarea { - font-family: monospace; - width: 100%; - margin: 0; - height: 300px; - padding: 20px; - border-radius: 0; - resize: none; - font-size: 12px; - line-height: 20px; - outline: 0; - } - } + td, + th { + padding: 2em; + } +} + +.taxonomy-product_cat { + + .check-column .woocommerce-help-tip { + font-size: 1.5em; + margin: -3px 0 0 5px; + display: block; + position: absolute; + } +} + +#debug-report { + display: none; + margin: 10px 0; + padding: 0; + position: relative; + + textarea { + font-family: monospace; + width: 100%; + margin: 0; + height: 300px; + padding: 20px; + border-radius: 0; + resize: none; + font-size: 12px; + line-height: 20px; + outline: 0; + } +} - /** +/** * DB log viewer */ - .wp-list-table.logs { +.wp-list-table.logs { - .log-level { - display: inline; - padding: .2em .6em .3em; - font-size: 80%; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .2em; + .log-level { + display: inline; + padding: 0.2em 0.6em 0.3em; + font-size: 80%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 0.2em; - &:empty { - display: none; - } - } + &:empty { + display: none; + } + } - /** + /** * Add color to levels * * Descending severity: @@ -744,2165 +760,2358 @@ * debug -> gree */ - .log-level--emergency, - .log-level--alert { - background-color: #ff4136; - } - .log-level--critical, - .log-level--error { - background-color: #ff851b; - } - .log-level--warning, - .log-level--notice { - color: #222; - background-color: #ffdc00; - } - .log-level--info { - background-color: #0074d9; - } - .log-level--debug { - background-color: #3d9970; - } + .log-level--emergency, + .log-level--alert { + background-color: #ff4136; + } - // Adjust log table columns only when table is not collapsed - @media screen and ( min-width: 783px ) { - .column-timestamp { - width: 18%; - } - .column-level { - width: 14%; - } - .column-source { - width: 15%; - } - } - } + .log-level--critical, + .log-level--error { + background-color: #ff851b; + } - #log-viewer-select { - padding: 10px 0 8px; - line-height: 28px; - h2 a { - vertical-align: middle; - } - } + .log-level--warning, + .log-level--notice { + color: #222; + background-color: #ffdc00; + } - #log-viewer { - background: #fff; - border: 1px solid #e5e5e5; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); - padding: 5px 20px; + .log-level--info { + background-color: #0074d9; + } - pre { - font-family: monospace; - white-space: pre-wrap; - word-wrap: break-word; - } - } + .log-level--debug { + background-color: #3d9970; + } - .inline-edit-product.quick-edit-row { - .inline-edit-col-center, - .inline-edit-col-right { - float: right !important; - } - } + // Adjust log table columns only when table is not collapsed + @media screen and ( min-width: 783px ) { - #woocommerce-fields.inline-edit-col { - clear: left; + .column-timestamp { + width: 18%; + } - label.featured, - label.manage_stock { - margin-left: 10px; - } + .column-level { + width: 14%; + } - label.stock_status_field { - clear: both; - float: left; - } + .column-source { + width: 15%; + } + } +} - .dimensions div { - display: block; - margin: 0.2em 0; +#log-viewer-select { + padding: 10px 0 8px; + line-height: 28px; - span.title { - display: block; - float: left; - width: 5em; - } + h2 a { + vertical-align: middle; + } +} - span.input-text-wrap { - display: block; - margin-left: 5em; - } - } +#log-viewer { + background: #fff; + border: 1px solid #e5e5e5; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); + padding: 5px 20px; - .text { - box-sizing: border-box; - width: 99%; - float: left; - margin: 1px 1% 1px 1px; - } + pre { + font-family: monospace; + white-space: pre-wrap; + word-wrap: break-word; + } +} - .length, .width, .height { - width: 32.33%; - } +.inline-edit-product.quick-edit-row { - .height { - margin-right: 0; - } - } + .inline-edit-col-center, + .inline-edit-col-right { + float: right !important; + } +} - #woocommerce-fields-bulk.inline-edit-col { - label { - clear: left; - } +#woocommerce-fields.inline-edit-col { + clear: left; - .inline-edit-group { - label { - clear: none; - width: 49%; - margin: 0.2em 0; - } - &.dimensions label { - width: 75%; - max-width: 75%; - } - } + label.featured, + label.manage_stock { + margin-left: 10px; + } - .regular_price, - .sale_price, - .weight, - .stock, - .length { - box-sizing: border-box; - width: 100%; - margin-left: 4.4em; - } + label.stock_status_field { + clear: both; + float: left; + } - .length, - .width, - .height { - box-sizing: border-box; - width: 25%; - } - } + .dimensions div { + display: block; + margin: 0.2em 0; - .column-coupon_code { - line-height: 2.25em; - } + span.title { + display: block; + float: left; + width: 5em; + } - ul.wc_coupon_list, - .column-coupon_code { - margin: 0; - overflow: hidden; - zoom: 1; - clear: both; - } + span.input-text-wrap { + display: block; + margin-left: 5em; + } + } - ul.wc_coupon_list { - padding-bottom: 5px; + .text { + box-sizing: border-box; + width: 99%; + float: left; + margin: 1px 1% 1px 1px; + } - li { - margin: 0; + .length, + .width, + .height { + width: 32.33%; + } - &.code { - display: inline-block; - position: relative; - padding: 0 .5em; - background-color: #fff; - border: 1px solid #aaa; - -webkit-box-shadow: 0 1px 0 #dfdfdf; - box-shadow: 0 1px 0 #dfdfdf; + .height { + margin-right: 0; + } +} - border-radius: 4px; - margin-right: 5px; - margin-top: 5px; +#woocommerce-fields-bulk.inline-edit-col { - &.editable { - padding-right: 2em; - } + label { + clear: left; + } - .tips { - cursor: pointer; + .inline-edit-group { - span { - color: #888; + label { + clear: none; + width: 49%; + margin: 0.2em 0; + } - &:hover { - color: #000; - } - } - } + &.dimensions label { + width: 75%; + max-width: 75%; + } + } - .remove-coupon { - text-decoration: none; - color: #888; - position: absolute; - top: 7px; - right: 20px; + .regular_price, + .sale_price, + .weight, + .stock, + .length { + box-sizing: border-box; + width: 100%; + margin-left: 4.4em; + } - /*rtl:raw: + .length, + .width, + .height { + box-sizing: border-box; + width: 25%; + } +} + +.column-coupon_code { + line-height: 2.25em; +} + +ul.wc_coupon_list, +.column-coupon_code { + margin: 0; + overflow: hidden; + zoom: 1; + clear: both; +} + +ul.wc_coupon_list { + padding-bottom: 5px; + + li { + margin: 0; + + &.code { + display: inline-block; + position: relative; + padding: 0 0.5em; + background-color: #fff; + border: 1px solid #aaa; + -webkit-box-shadow: 0 1px 0 #dfdfdf; + box-shadow: 0 1px 0 #dfdfdf; + + border-radius: 4px; + margin-right: 5px; + margin-top: 5px; + + &.editable { + padding-right: 2em; + } + + .tips { + cursor: pointer; + + span { + color: #888; + + &:hover { + color: #000; + } + } + } + + .remove-coupon { + text-decoration: none; + color: #888; + position: absolute; + top: 7px; + right: 20px; + + /*rtl:raw: left: 7px; */ - &::before { - @include icon_dashicons( '\f158' ); - } - &:hover::before { - color: $red; - } - } - } - } - } - - ul.wc_coupon_list_block { - margin: 0; - padding-bottom: 2px; - - li { - border-top: 1px solid #fff; - border-bottom: 1px solid #ccc; - line-height: 2.5em; - margin: 0; - padding: 0.5em 0; - } - - li:first-child { - border-top: 0; - padding-top: 0; - } - - li:last-child { - border-bottom: 0; - padding-bottom: 0; - } - } - - .button.wc-reload { - @include ir(); - padding: 0; - height: 28px; - width: 28px !important; - display: inline-block; - - &::after { - @include icon_dashicons( '\f345' ); - line-height: 28px; - } - } - - #woocommerce-order-data { - .hndle, - .handlediv { - display: none; - } - - .inside { - display: block !important; - } - } - - #order_data { - padding: 23px 24px 12px; - - h2 { - margin: 0; - font-family: 'HelveticaNeue-Light', 'Helvetica Neue Light', 'Helvetica Neue', sans-serif; - font-size: 21px; - font-weight: normal; - line-height: 1.2; - text-shadow: 1px 1px 1px white; - padding: 0; - } - - h3 { - font-size: 14px; - } - - h3, h4 { - color: #333; - margin: 1.33em 0 0; - } - - p { - color: #777; - } - - p.order_number { - margin: 0; - font-family: 'HelveticaNeue-Light', 'Helvetica Neue Light', 'Helvetica Neue', sans-serif; - font-weight: normal; - line-height: 1.6em; - font-size: 16px; - } - - .order_data_column_container { - clear: both; - } - - .order_data_column { - width: 32%; - padding: 0 2% 0 0; - float: left; - - > h3 span { - display: block; - } - - &:last-child { - padding-right: 0; - } - - p { - padding: 0 !important; - } - - .address strong { - display: block; - } - - .form-field { - float: left; - clear: left; - width: 48%; - padding: 0; - margin: 9px 0 0; - - label { - display: block; - padding: 0 0 3px; - } - - input, - textarea { - width: 100%; - } - - select { - width: 100%; - } - - .select2-container { - width: 100% !important; - } - - .date-picker { - width: 50%; - } - - .hour, - .minute { - width: 3.5em; - } - - small { - display: block; - margin: 5px 0 0; - color: #999; - } - } - - .form-field.last, - ._billing_last_name_field, - ._billing_address_2_field, - ._billing_postcode_field, - ._billing_state_field, - ._billing_phone_field, - ._shipping_last_name_field, - ._shipping_address_2_field, - ._shipping_postcode_field, - ._shipping_state_field { - float: right; - clear: right; - } - - .form-field-wide, - ._billing_company_field, - ._shipping_company_field, - ._transaction_id_field { - width: 100%; - clear: both; - - input, - textarea, - select, - .wc-enhanced-select, - .wc-category-search, - .wc-customer-search { - width: 100%; - } - } - - p.none_set { - color: #999; - } - - div.edit_address { - display: none; - zoom: 1; - padding-right: 1px; - } - - .wc-customer-user, .wc-order-status { - label a { - float: right; - margin-left: 8px; - } - } - - a.edit_address { - width: 14px; - height: 0; - padding: 14px 0 0; - margin: 0 0 0 6px; - overflow: hidden; - position: relative; - color: #999; - border: 0; - float: right; - &:hover, &:focus { - color: #000; - } - &::after { - font-family: 'WooCommerce'; - position: absolute; - top: 0; - left: 0; - text-align: center; - vertical-align: top; - line-height: 14px; - font-size: 14px; - font-weight: 400; - } - } - a.edit_address::after { - font-family: 'Dashicons'; - content: '\f464'; - } - - .billing-same-as-shipping, - .load_customer_shipping, - .load_customer_billing { - font-size: 13px; - display: inline-block; - font-weight: normal; - } - - .load_customer_shipping { - margin-right: .3em; - } - } - } - - .order_actions { - margin: 0; - overflow: hidden; - zoom: 1; - - li { - border-top: 1px solid #fff; - border-bottom: 1px solid #ddd; - padding: 6px 0; - margin: 0; - line-height: 1.6em; - float: left; - width: 50%; - text-align: center; - - a { - float: none; - text-align: center; - text-decoration: underline; - } - - &.wide { - width: auto; - float: none; - clear: both; - padding: 6px; - text-align: left; - overflow: hidden; - } - - #delete-action { - line-height: 25px; - vertical-align: middle; - text-align: left; - float: left; - } - - .save_order { - float: right; - } - - &#actions { - overflow: hidden; - - .button { - width: 24px; - box-sizing: border-box; - float: right; - } - - select { - width: 225px; - box-sizing: border-box; - float: left; - } - } - } - } - - #woocommerce-order-items { - .inside { - margin: 0; - padding: 0; - background: #fefefe; - } - - .wc-order-data-row { - border-bottom: 1px solid #dfdfdf; - padding: 1.5em 2em; - background: #f8f8f8; - @include clearfix(); - line-height: 2em; - text-align: right; - - p { - margin: 0; - line-height: 2em; - } - - .wc-used-coupons { - text-align: left; - - .tips { - display: inline-block; - } - } - } - - .wc-used-coupons { - float: left; - width: 50%; - } - - .wc-order-totals { - float: right; - width: 50%; - margin: 0; - padding: 0; - text-align: right; - - .amount { - font-weight: 700; - } - - .label { - vertical-align: top; - } - - .total { - font-size: 1em !important; - width: 10em; - margin: 0 0 0 0.5em; - box-sizing: border-box; - - input[type='text'] { - width: 96%; - float: right; - } - } - - .refunded-total { - color: $red; - } - } - - .refund-actions { - margin-top: 5px; - padding-top: 12px; - border-top: 1px solid #dfdfdf; - - .button { - float: right; - margin-left: 4px; - } - - .cancel-action { - float: left; - margin-left: 0; - } - } - - .add_meta { - margin-left: 0 !important; - } - - h3 small { - color: #999; - } - - .amount { - white-space: nowrap; - } - - .add-items { - .description { - margin-right: 10px; - } - .button { - float: left; - margin-right: 0.25em; - } - .button-primary { - float: none; - margin-right: 0; - } - } - } - - #woocommerce-order-items { - .inside { - display: block !important; - } - .hndle, .handlediv { - display: none; - } - .woocommerce_order_items_wrapper { - margin: 0; - overflow-x: auto; - - table.woocommerce_order_items { - width: 100%; - background: #fff; - - thead th { - text-align: left; - padding: 1em; - font-weight: normal; - color: #999; - background: #f8f8f8; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - &.sortable { - cursor: pointer; - } - &:last-child { - padding-right: 2em; - } - &:first-child { - padding-left: 2em; - } - .wc-arrow { - float: right; - position: relative; - margin-right: -1em; - } - } - - tbody th, td { - padding: 1.5em 1em 1em; - text-align: left; - line-height: 1.5em; - vertical-align: top; - border-bottom: 1px solid #f8f8f8; - - textarea { - width: 100%; - } - - select { - width: 50%; - } - - input, textarea { - font-size: 14px; - padding: 4px; - color: #555; - } - &:last-child { - padding-right: 2em; - } - &:first-child { - padding-left: 2em; - } - } - - tbody tr:last-child td { - border-bottom: 1px solid #dfdfdf; - } - - tbody tr:first-child td { - border-top: 8px solid #f8f8f8; - } - - tbody#order_line_items tr:first-child td { - border-top: none; - } - - td.thumb { - text-align: left; - width: 38px; - padding-bottom: 1.5em; - .wc-order-item-thumbnail { - width: 38px; - height: 38px; - border: 2px solid #e8e8e8; - background: #f8f8f8; - color: #ccc; - position: relative; - font-size: 21px; - display: block; - text-align: center; - &::before { - @include icon_dashicons( '\f128' ); - width: 38px; - line-height: 38px; - display: block; - } - img { - width: 100%; - height: 100%; - margin: 0; - padding: 0; - position: relative; - } - } - } - td.name { - .wc-order-item-sku, .wc-order-item-variation { - display: block; - margin-top: 0.5em; - font-size: 0.92em !important; - color: #888; - } - } - - .item { - min-width: 200px; - } - - .center, - .variation-id { - text-align: center; - } - - .cost, - .tax, - .quantity, - .line_cost, - .line_tax, - .tax_class, - .item_cost { - text-align: right; - - label { - white-space: nowrap; - color: #999; - font-size: 0.833em; - - input { - display: inline; - } - } - - input { - width: 70px; - vertical-align: middle; - text-align: right; - } - - select { - width: 85px; - height: 26px; - vertical-align: middle; - font-size: 1em; - } - - .split-input { - display: inline-block; - background: #fff; - border: 1px solid #ddd; - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.07); - margin: 1px 0; - min-width: 80px; - overflow: hidden; - line-height: 1em; - text-align: right; - - div.input { - width: 100%; - box-sizing: border-box; - label { - font-size: 0.75em; - padding: 4px 6px 0; - color: #555; - display: block; - } - input { - width: 100%; - box-sizing: border-box; - border: 0; - box-shadow: none; - margin: 0; - padding: 0 6px 4px; - color: #555; - background: transparent; - &::-webkit-input-placeholder { - color: #ddd; - } - } - } - div.input:first-child { - border-bottom: 1px dashed #ddd; - background: #fff; - label { - color: #ccc; - } - input { - color: #ccc; - } - } - } - - .view { - white-space: nowrap; - } - - .edit { - text-align: left; - } - - small.times, del, .wc-order-item-taxes, .wc-order-item-discount, .wc-order-item-refund-fields { - font-size: 0.92em !important; - color: #888; - } - - .wc-order-item-taxes, .wc-order-item-refund-fields { - margin: 0; - label { - display: block; - } - } - - .wc-order-item-discount { - display: block; - margin-top: 0.5em; - } - - small.times { - margin-right: 0.25em; - } - } - - .quantity { - text-align: center; - - input { - text-align: center; - width: 50px; - } - } - - span.subtotal { - opacity: 0.5; - } - - td.tax_class, - th.tax_class { - text-align: left; - } - - .calculated { - border-color: #ae8ca2; - border-style: dotted; - } - - table.meta { - width: 100%; - } - - table.meta, - table.display_meta { - margin: 0.5em 0 0; - font-size: 0.92em !important; - color: #888; - - tr { - th { - border: 0; - padding: 0 4px 0.5em 0; - line-height: 1.5em; - width: 20%; - } - td { - padding: 0 4px 0.5em 0; - border: 0; - line-height: 1.5em; - - input { - width: 100%; - margin: 0; - position: relative; - border-bottom: 0; - box-shadow: none; - } - - textarea { - width: 100%; - height: 4em; - margin: 0; - box-shadow: none; - } - - input:focus + textarea { - border-top-color: #999; - } - - p { - margin: 0 0 0.5em; - line-height: 1.5em; - } - - p:last-child { - margin: 0; - } - } - } - } - - .refund_by { - border-bottom: 1px dotted #999; - } - - tr.fee .thumb div { - @include ir(); - font-size: 1.5em; - line-height: 1em; - vertical-align: middle; - margin: 0 auto; - - &::before { - @include icon( '\e007' ); - color: #ccc; - } - } - - tr.refund .thumb div { - @include ir(); - font-size: 1.5em; - line-height: 1em; - vertical-align: middle; - margin: 0 auto; - - &::before { - @include icon( '\e014' ); - color: #ccc; - } - } - - tr.shipping { - .thumb div { - @include ir(); - font-size: 1.5em; - line-height: 1em; - vertical-align: middle; - margin: 0 auto; - - &::before { - @include icon( '\e01a' ); - color: #ccc; - } - } - .shipping_method_name, - .shipping_method { - width: 100%; - margin: 0 0 0.5em; - } - } - - th.line_tax { - white-space: nowrap; - } - - th.line_tax, - td.line_tax { - .delete-order-tax { - @include ir(); - float: right; - font-size: 14px; - visibility: hidden; - margin: 3px -18px 0 0; - - &::before { - @include icon_dashicons( '\f153' ); - color: #999; - } - - &:hover::before { - color: $red; - } - } - - &:hover .delete-order-tax { - visibility: visible; - } - } - - small.refunded { - display: block; - color: $red; - white-space: nowrap; - margin-top: 0.5em; - &::before { - @include icon_dashicons( '\f171' ); - position: relative; - top: auto; - left: auto; - margin: -1px 4px 0 0; - vertical-align: middle; - line-height: 1em; - } - } - } - } - .wc-order-edit-line-item { - padding-left: 0; - } - .wc-order-edit-line-item-actions { - width: 44px; - text-align: right; - padding-left: 0; - vertical-align: middle; - a { - color: #ccc; - display: inline-block; - cursor: pointer; - padding: 0 0 0.5em; - margin: 0 0 0 12px; - vertical-align: middle; - text-decoration: none; - line-height: 16px; - width: 16px; - overflow: hidden; - &::before { - margin: 0; - padding: 0; - font-size: 16px; - width: 16px; - height: 16px; - } - &:hover { - &::before { - color: #999; - } - } - &:first-child { - margin-left: 0; - } - } - - .edit-order-item::before { - @include icon_dashicons( '\f464' ); - position: relative; - } - - .delete-order-item, - .delete_refund { - &::before { - @include icon_dashicons( '\f158' ); - position: relative; - } - &:hover::before { - color: $red; - } - } - } - - tbody tr .wc-order-edit-line-item-actions { - visibility: hidden; - } - tbody tr:hover .wc-order-edit-line-item-actions { - visibility: visible; - } - - .wc-order-totals .wc-order-edit-line-item-actions { - width: 1.5em; - visibility: visible !important; - a { - padding: 0; - } - } - } - - #woocommerce-order-downloads { - .buttons { - float: left; - padding: 0; - margin: 0; - vertical-align: top; - - .add_item_id, - .select2-container { - width: 400px !important; - margin-right: 9px; - vertical-align: top; - float: left; - } - - button { - margin: 2px 0 0; - } - } - - h3 small { - color: #999; - } - } - - #poststuff #woocommerce-order-actions .inside { - margin: 0; - padding: 0; - - ul.order_actions li { - padding: 6px 10px; - box-sizing: border-box; - - &:last-child { - border-bottom: 0; - } - } - } - - #poststuff #woocommerce-order-notes .inside { - margin: 0; - padding: 0; - - ul.order_notes li { - padding: 0 10px; - } - } - - #woocommerce_customers { - p.search-box { - margin: 6px 0 4px; - float: left; - } - - .tablenav { - float: right; - clear: none; - } - } - - .widefat { - &.customers td { - vertical-align: middle; - padding: 4px 7px; - } - - .column-order_title { - width: 15%; - - time { - display: block; - color: #999; - margin: 3px 0; - } - } - - .column-orders, .column-paying, .column-spent { - text-align: center; - width: 8%; - } - - .column-last_order { - width: 11%; - } - - .column-wc_actions { - width: 110px; - - a.button { - @include ir(); - display: inline-block; - margin: 2px 4px 2px 0; - padding: 0 !important; - height: 2em !important; - width: 2em; - overflow: hidden; - vertical-align: middle; - - &::after { - font-family: 'Dashicons'; - speak: none; - font-weight: normal; - font-variant: normal; - text-transform: none; - margin: 0; - text-indent: 0; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - text-align: center; - line-height: 1.85; - } - - img { - display: block; - width: 12px; - height: auto; - } - } - - a.edit::after { - content: '\f464'; - } - - a.link::after { - font-family: 'WooCommerce'; - content: '\e00d'; - } - - a.view::after { - content: '\f177'; - } - - a.refresh::after { - font-family: 'WooCommerce'; - content: '\e031'; - } - - a.processing::after { - font-family: 'WooCommerce'; - content: '\e00f'; - } - - a.complete::after { - content: '\f147'; - } - } - - small.meta { - display: block; - color: #999; - font-size: inherit; - margin: 3px 0; - } - } - - .post-type-shop_order { - .tablenav .one-page .displaying-num { - display: none; - } - .wp-list-table { - margin-top: 1em; - - thead, - tfoot { - th { - padding: .75em 1em; - } - th.sortable a, th.sorted a { - padding: 0; - } - th:first-child { - padding-left: 2em; - } - th:last-child { - padding-right: 2em; - } - } - tbody { - td, - th { - padding: 1em; - line-height: 26px; - } - td:first-child { - padding-left: 2em; - } - td:last-child { - padding-right: 2em; - } - } - tbody tr { - border-top: 1px solid #f5f5f5; - } - tbody tr:hover:not(.status-trash):not(.no-link) td { - cursor: pointer; - } - .no-link { - cursor: default !important; - } - - // Columns. - td, - th { - width: 12ch; - vertical-align: middle; - p { - margin: 0; - } - } - .check-column { - width: 1px; - white-space: nowrap; - padding: 1em 1em 1em 1em !important; - vertical-align: middle; - input { - vertical-align: text-top; - margin: 1px 0; - } - } - .column-order_number { - width: 20ch; - } - .column-order_total { - width: 8ch; - text-align: right; - - a span { - float:right; - } - } - .column-order_date, - .column-order_status { - width: 10ch; - } - .column-order_status { - width: 14ch; - } - .column-shipping_address, - .column-billing_address { - width: 20ch; - line-height: 1.5em; - .description { - display: block; - color: #999; - } - } - .column-wc_actions { - text-align: right; - - a.button { - text-indent: 9999px; - margin: 2px 0 2px 4px; - } - } - .order-preview { - float:right; - width: 16px; - padding: 20px 4px 4px 4px; - height: 0; - overflow: hidden; - position: relative; - border: 2px solid transparent; - border-radius: 4px; - - &::before { - @include icon( '\e010' ); - line-height: 16px; - font-size: 14px; - vertical-align:middle; - top: 4px; - - } - &:hover { - border: 2px solid #00a0d2; - } - } - .order-preview.disabled { - &::before { - content: ''; - background: url('../images/wpspin.gif') no-repeat center top; - } - } - } - } - - .order-status { - display: inline-flex; - line-height: 2.5em; - color: #777; - background: #E5E5E5; - border-radius: 4px; - border-bottom: 1px solid rgba(0,0,0,0.05); - margin: -.25em 0; - cursor: inherit !important; - white-space: nowrap; - max-width: 100%; - - &.status-completed { - background: #C8D7E1; - color: #2e4453; - } - - &.status-on-hold { - background: #f8dda7; - color: #94660c; - } - - &.status-failed { - background: #eba3a3; - color: #761919; - } - - &.status-processing { - background: #C6E1C6; - color: #5B841B; - } - - &.status-trash { - background: #eba3a3; - color: #761919; - } - - > span { - margin: 0 1em; - overflow: hidden; - text-overflow: ellipsis; - } - } - - .wc-order-preview { - .order-status { - float: right; - margin-right: 54px; - } - article { - padding: 0 !important; - } - .modal-close { - border-radius: 0; - } - .wc-order-preview-table { - width: 100%; - margin: 0; - th, td { - padding: 1em 1.5em; - text-align: left; - border: 0; - border-bottom: 1px solid #eee; - margin: 0; - background: transparent; - box-shadow: none; - text-align: right; - vertical-align: top; - } - td:first-child, - th:first-child { - text-align: left; - } - th { - border-color: #ccc; - } - tr:last-child td { - border: 0; - } - .wc-order-item-sku { - margin-top: .5em; - } - .wc-order-item-meta { - margin-top: .5em; - th, td { - padding: 0; - border: 0; - text-align: left; - vertical-align: top; - } - td:last-child { - padding-left: .5em; - } - } - } - .wc-order-preview-addresses { - overflow: hidden; - padding-bottom: 1.5em; - - .wc-order-preview-address, - .wc-order-preview-note { - width: 50%; - float: left; - padding: 1.5em 1.5em 0; - box-sizing: border-box; - word-wrap: break-word; - - h2 { - margin-top: 0; - } - strong { - display: block; - margin-top: 1.5em; - } - strong:first-child { - margin-top: 0; - } - } - } - footer { - .wc-action-button-group { - display: inline-block; - float: left; - } - .button.button-large { - margin-left: 10px; - padding: 0 10px !important; - line-height: 28px; - height: auto; - display: inline-block; - } - } - .wc-action-button-group label { - display: none; - } - } - - .wc-action-button-group { - vertical-align: middle; - line-height: 26px; - text-align: left; - label { - margin-right: 6px; - cursor: default; - font-weight: bold; - line-height: 28px; - } - .wc-action-button-group__items { - display: inline-flex; - flex-flow: row wrap; - align-content: flex-start; - justify-content: flex-start; - } - .wc-action-button { - margin: 0 0 0 -1px !important; - border: 1px solid #ccc; - padding: 0 10px !important; - border-radius: 0 !important; - float: none; - line-height: 28px; - height: auto; - z-index: 1; - position:relative; - overflow: hidden; - text-overflow: ellipsis; - flex: 1 0 auto; - box-sizing: border-box; - text-align: center; - white-space: nowrap; - } - .wc-action-button:hover, - .wc-action-button:focus { - border: 1px solid #999; - z-index: 2; - } - .wc-action-button:first-child { - margin-left: 0 !important; - border-top-left-radius: 3px !important; - border-bottom-left-radius: 3px !important; - } - .wc-action-button:last-child { - border-top-right-radius: 3px !important; - border-bottom-right-radius: 3px !important; - } - } - @media screen and (max-width: 782px) { - .wc-order-preview footer { - .wc-action-button-group .wc-action-button-group__items { - display: flex; - } - .wc-action-button-group { - float: none; - display: block; - margin-bottom: 4px; - } - .button.button-large { - width: 100%; - float: none; - text-align: center; - margin: 0; - display: block; - } - } - - .post-type-shop_order .wp-list-table { - td.check-column { - width: 1em; - } - td.column-order_number { - padding-left: 0; - padding-bottom: .5em; - } - td.column-order_status, - td.column-order_date { - display: inline-block !important; - padding: 0 1em 1em 1em !important; - &:before { - display: none !important; - } - } - td.column-order_date { - padding-left: 0 !important; - } - td.column-order_status { - float: right; - } - } - } - - .column-customer_message .note-on { - @include ir(); - margin: 0 auto; - color: #999; - - &::after { - @include icon( '\e026' ); - line-height: 16px; - } - } - - .column-order_notes .note-on { - @include ir(); - margin: 0 auto; - color: #999; - - &::after { - @include icon( '\e027' ); - line-height: 16px; - } - } - - .attributes-table { - td, - th { - width: 15%; - vertical-align: top; - } - - .attribute-terms { - width: 32%; - } - - .attribute-actions { - width: 2em; - - .configure-terms { - @include ir(); - padding: 0 !important; - height: 2em !important; - width: 2em; - - &::after { - @include icon('\f111'); - font-family: 'Dashicons'; - line-height: 1.85; - } - } - } - } - - /* Order notes */ - ul.order_notes { - padding: 2px 0 0; - - li { - .note_content { - padding: 10px; - background: #efefef; - position: relative; - - p { - margin: 0; - padding: 0; - word-wrap: break-word; - } - } - - p.meta { - padding: 10px; - color: #999; - margin: 0; - font-size: 11px; - - .exact-date { - border-bottom: 1px dotted #999; - } - } - - a.delete_note { - color: $red; - } - - .note_content::after { - content: ''; - display: block; - position: absolute; - bottom: -10px; - left: 20px; - width: 0; - height: 0; - border-width: 10px 10px 0 0; - border-style: solid; - border-color: #efefef transparent; - } - } - li.system-note { - .note_content { - background: #d7cad2; - } - .note_content::after { - border-color: #d7cad2 transparent; - } - } - li.customer-note { - .note_content { - background: #a7cedc; - } - .note_content::after { - border-color: #a7cedc transparent; - } - } - } - - .add_note { - border-top: 1px solid #ddd; - padding: 10px 10px 0; - - h4 { - margin-top: 5px !important; - } - - #add_order_note { - width: 100%; - height: 50px; - } - } - - table.wp-list-table { - .column-thumb { - width: 52px; - text-align: center; - white-space: nowrap; - } - - .column-handle { - width: 17px; - display: none; - } - - tbody { - td.column-handle { - cursor: move; - width: 17px; - text-align: center; - vertical-align: text-top; - - &::before { - content: '\f333'; - font-family: 'Dashicons'; - text-align: center; - line-height: 1; - color: #999; - display: block; - width: 17px; - height: 100%; - margin: 4px 0 0 0; - } - } - } - - .column-name { - width: 22%; - } - - .column-product_cat, - .column-product_tag { - width: 11% !important; - } - - .column-featured, - .column-product_type { - width: 48px; - text-align: left !important; - } - - .column-customer_message, - .column-order_notes { - width: 48px; - text-align: center; - img { - margin: 0 auto; - padding-top: 0 !important; - } - } - - .manage-column.column-featured img, - .manage-column.column-product_type img { - padding-left: 2px; - } - - .column-price .woocommerce-price-suffix { - display: none; - } - - img { - margin: 1px 2px; - } - - .row-actions { - color: #999; - } - - td.column-thumb img { - margin: 0; - width: auto; - height: auto; - max-width: 40px; - max-height: 40px; - vertical-align: middle; - } - - span.na { - color: #999; - } - - .column-sku { - width: 10%; - } - - .column-price { - width: 10ch; - } - - .column-is_in_stock { - text-align: left !important; - width: 12ch; - } - - span.wc-image, - span.wc-featured { - @include ir(); - margin: 0 auto; - - &::before { - @include icon_dashicons( '\f128' ); - } - } - - span.wc-featured { - &::before { - content: '\f155'; - } - &.not-featured::before { - content: '\f154'; - } - } - - td.column-featured span.wc-featured { - font-size: 1.6em; - cursor: pointer; - } - - mark { - &.instock, - &.outofstock, - &.onbackorder { - font-weight: 700; - background: transparent none; - line-height: 1; - } - - &.instock { - color: $green; - } - - &.outofstock { - color: #aa4444; - } - - &.onbackorder { - color: #eaa600; - } - } - - .order-notes_head, - .notes_head, - .status_head { - @include ir(); - margin: 0 auto; - - &::after { - @include icon; - } - } - - .order-notes_head::after { - content: '\e028'; - } - - .notes_head::after { - content: '\e026'; - } - - .status_head::after { - content: '\e011'; - } - - .column-order_items { - width: 12%; - - table.order_items { - width: 100%; - margin: 3px 0 0; - padding: 0; - display: none; - - td { - border: 0; - margin: 0; - padding: 0 0 3px; - } - - td.qty { - color: #999; - padding-right: 6px; - text-align: left; - } - } - } - } - - mark.notice { - background: #fff; - color: $red; - margin: 0 0 0 10px; - } - - a.export_rates, - a.import_rates { - float: right; - margin-left: 9px; - margin-top: -2px; - margin-bottom: 0; - } - - #rates-search { - float: right; - input.wc-tax-rates-search-field { - padding: 4px 8px; - font-size: 1.2em; - } - } - #rates-pagination { - float: right; - margin-right: 0.5em; - .tablenav { - margin: 0; - } - } - - .wc_input_table_wrapper { - overflow-x: auto; - display: block; - } - - table.wc_tax_rates, - table.wc_input_table { - width: 100%; - - th, td { - display: table-cell !important; - } - - span.tips { - color: $blue; - } - - th { - white-space: nowrap; - padding: 10px; - } - - td { - padding: 0; - border-right: 1px solid #dfdfdf; - border-bottom: 1px solid #dfdfdf; - border-top: 0; - background: #fff; - cursor: default; - - input[type=text], - input[type=number] { - width: 100% !important; - min-width: 100px; - padding: 8px 10px; - margin: 0; - border: 0; - outline: 0; - background: transparent none; - - &:focus { - outline: 0; - box-shadow: none; - } - } - - &.compound, - &.apply_to_shipping { - padding: 5px 7px; - vertical-align: middle; - - input { - width: auto; - padding: 0; - } - } - } - - td:last-child { - border-right: 0; - } - - tr.current td { - background-color: #fefbcc; - } - - .item_cost, - .cost { - text-align: right; - - input { - text-align: right; - } - } - - th.sort { - width: 17px; - padding: 0 4px; - } - - td.sort { - padding: 0 4px; - } - - .ui-sortable:not( .ui-sortable-disabled ) td.sort { - cursor: move; - font-size: 15px; - background: #f9f9f9; - text-align: center; - vertical-align: middle; - - &::before { - content: '\f333'; - font-family: 'Dashicons'; - text-align: center; - line-height: 1; - color: #999; - display: block; - width: 17px; - float: left; - height: 100%; - } - - &:hover::before { - color: #333; - } - } - - .button { - float: left; - margin-right: 5px; - } - - .export, - .import { - float: right; - margin-right: 0; - margin-left: 5px; - } - - span.tips { - padding: 0 3px; - } - - .pagination { - float: right; - - .button { - margin-left: 5px; - margin-right: 0; - } - - .current { - background: #bbb; - text-shadow: none; - } - } - - tr:last-child td { - border-bottom: 0; - } - } - - table.wc_gateways, - table.wc_emails, - table.wc_shipping { - position: relative; - - th, td { - display: table-cell !important; - padding: 1em !important; - vertical-align: top; - line-height: 1.75em; - } - - &.wc_emails td { - vertical-align: middle; - } - - tr:nth-child( odd ) td { - background: #f9f9f9; - } - - td.name { - font-weight: 700; - } - - .settings { - text-align: right; - } - - .radio, - .default, - .status { - text-align: center; - - .tips { - margin: 0 auto; - } - - input { - margin: 0; - } - } - td.sort { - font-size: 15px; - text-align: center; - - .wc-item-reorder-nav { + &::before { + + @include icon_dashicons( "\f158" ); + } + + &:hover::before { + color: $red; + } + } + } + } +} + +ul.wc_coupon_list_block { + margin: 0; + padding-bottom: 2px; + + li { + border-top: 1px solid #fff; + border-bottom: 1px solid #ccc; + line-height: 2.5em; + margin: 0; + padding: 0.5em 0; + } + + li:first-child { + border-top: 0; + padding-top: 0; + } + + li:last-child { + border-bottom: 0; + padding-bottom: 0; + } +} + +.button.wc-reload { + + @include ir(); + padding: 0; + height: 28px; + width: 28px !important; + display: inline-block; + + &::after { + + @include icon_dashicons( "\f345" ); + line-height: 28px; + } +} + +#woocommerce-order-data { + + .hndle, + .handlediv { + display: none; + } + + .inside { + display: block !important; + } +} + +#order_data { + padding: 23px 24px 12px; + + h2 { + margin: 0; + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", sans-serif; + font-size: 21px; + font-weight: normal; + line-height: 1.2; + text-shadow: 1px 1px 1px white; + padding: 0; + } + + h3 { + font-size: 14px; + } + + h3, + h4 { + color: #333; + margin: 1.33em 0 0; + } + + p { + color: #777; + } + + p.order_number { + margin: 0; + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", sans-serif; + font-weight: normal; + line-height: 1.6em; + font-size: 16px; + } + + .order_data_column_container { + clear: both; + } + + .order_data_column { + width: 32%; + padding: 0 2% 0 0; + float: left; + + > h3 span { + display: block; + } + + &:last-child { + padding-right: 0; + } + + p { + padding: 0 !important; + } + + .address strong { + display: block; + } + + .form-field { + float: left; + clear: left; + width: 48%; + padding: 0; + margin: 9px 0 0; + + label { + display: block; + padding: 0 0 3px; + } + + input, + textarea { + width: 100%; + } + + select { + width: 100%; + } + + .select2-container { + width: 100% !important; + } + + .date-picker { + width: 50%; + } + + .hour, + .minute { + width: 3.5em; + } + + small { + display: block; + margin: 5px 0 0; + color: #999; + } + } + + .form-field.last, + ._billing_last_name_field, + ._billing_address_2_field, + ._billing_postcode_field, + ._billing_state_field, + ._billing_phone_field, + ._shipping_last_name_field, + ._shipping_address_2_field, + ._shipping_postcode_field, + ._shipping_state_field { + float: right; + clear: right; + } + + .form-field-wide, + ._billing_company_field, + ._shipping_company_field, + ._transaction_id_field { + width: 100%; + clear: both; + + input, + textarea, + select, + .wc-enhanced-select, + .wc-category-search, + .wc-customer-search { + width: 100%; + } + } + + p.none_set { + color: #999; + } + + div.edit_address { + display: none; + zoom: 1; + padding-right: 1px; + } + + .wc-customer-user, + .wc-order-status { + + label a { + float: right; + margin-left: 8px; + } + } + + a.edit_address { + width: 14px; + height: 0; + padding: 14px 0 0; + margin: 0 0 0 6px; + overflow: hidden; + position: relative; + color: #999; + border: 0; + float: right; + + &:hover, + &:focus { + color: #000; + } + + &::after { + font-family: "WooCommerce"; + position: absolute; + top: 0; + left: 0; + text-align: center; + vertical-align: top; + line-height: 14px; + font-size: 14px; + font-weight: 400; + } + } + + a.edit_address::after { + font-family: "Dashicons"; + content: "\f464"; + } + + .billing-same-as-shipping, + .load_customer_shipping, + .load_customer_billing { + font-size: 13px; + display: inline-block; + font-weight: normal; + } + + .load_customer_shipping { + margin-right: 0.3em; + } + } +} + +.order_actions { + margin: 0; + overflow: hidden; + zoom: 1; + + li { + border-top: 1px solid #fff; + border-bottom: 1px solid #ddd; + padding: 6px 0; + margin: 0; + line-height: 1.6em; + float: left; + width: 50%; + text-align: center; + + a { + float: none; + text-align: center; + text-decoration: underline; + } + + &.wide { + width: auto; + float: none; + clear: both; + padding: 6px; + text-align: left; + overflow: hidden; + } + + #delete-action { + line-height: 25px; + vertical-align: middle; + text-align: left; + float: left; + } + + .save_order { + float: right; + } + + &#actions { + overflow: hidden; + + .button { + width: 24px; + box-sizing: border-box; + float: right; + } + + select { + width: 225px; + box-sizing: border-box; + float: left; + } + } + } +} + +#woocommerce-order-items { + + .inside { + margin: 0; + padding: 0; + background: #fefefe; + } + + .wc-order-data-row { + border-bottom: 1px solid #dfdfdf; + padding: 1.5em 2em; + background: #f8f8f8; + + @include clearfix(); + line-height: 2em; + text-align: right; + + p { + margin: 0; + line-height: 2em; + } + + .wc-used-coupons { + text-align: left; + + .tips { + display: inline-block; + } + } + } + + .wc-used-coupons { + float: left; + width: 50%; + } + + .wc-order-totals { + float: right; + width: 50%; + margin: 0; + padding: 0; + text-align: right; + + .amount { + font-weight: 700; + } + + .label { + vertical-align: top; + } + + .total { + font-size: 1em !important; + width: 10em; + margin: 0 0 0 0.5em; + box-sizing: border-box; + + input[type="text"] { + width: 96%; + float: right; + } + } + + .refunded-total { + color: $red; + } + } + + .refund-actions { + margin-top: 5px; + padding-top: 12px; + border-top: 1px solid #dfdfdf; + + .button { + float: right; + margin-left: 4px; + } + + .cancel-action { + float: left; + margin-left: 0; + } + } + + .add_meta { + margin-left: 0 !important; + } + + h3 small { + color: #999; + } + + .amount { + white-space: nowrap; + } + + .add-items { + + .description { + margin-right: 10px; + } + + .button { + float: left; + margin-right: 0.25em; + } + + .button-primary { + float: none; + margin-right: 0; + } + } +} + +#woocommerce-order-items { + + .inside { + display: block !important; + } + + .hndle, + .handlediv { + display: none; + } + + .woocommerce_order_items_wrapper { + margin: 0; + overflow-x: auto; + + table.woocommerce_order_items { + width: 100%; + background: #fff; + + thead th { + text-align: left; + padding: 1em; + font-weight: normal; + color: #999; + background: #f8f8f8; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + &.sortable { + cursor: pointer; + } + + &:last-child { + padding-right: 2em; + } + + &:first-child { + padding-left: 2em; + } + + .wc-arrow { + float: right; + position: relative; + margin-right: -1em; + } + } + + tbody th, + td { + padding: 1.5em 1em 1em; + text-align: left; + line-height: 1.5em; + vertical-align: top; + border-bottom: 1px solid #f8f8f8; + + textarea { + width: 100%; + } + + select { + width: 50%; + } + + input, + textarea { + font-size: 14px; + padding: 4px; + color: #555; + } + + &:last-child { + padding-right: 2em; + } + + &:first-child { + padding-left: 2em; + } + } + + tbody tr:last-child td { + border-bottom: 1px solid #dfdfdf; + } + + tbody tr:first-child td { + border-top: 8px solid #f8f8f8; + } + + tbody#order_line_items tr:first-child td { + border-top: none; + } + + td.thumb { + text-align: left; + width: 38px; + padding-bottom: 1.5em; + + .wc-order-item-thumbnail { + width: 38px; + height: 38px; + border: 2px solid #e8e8e8; + background: #f8f8f8; + color: #ccc; + position: relative; + font-size: 21px; + display: block; + text-align: center; + + &::before { + + @include icon_dashicons( "\f128" ); + width: 38px; + line-height: 38px; + display: block; + } + + img { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + position: relative; + } + } + } + + td.name { + + .wc-order-item-sku, + .wc-order-item-variation { + display: block; + margin-top: 0.5em; + font-size: 0.92em !important; + color: #888; + } + } + + .item { + min-width: 200px; + } + + .center, + .variation-id { + text-align: center; + } + + .cost, + .tax, + .quantity, + .line_cost, + .line_tax, + .tax_class, + .item_cost { + text-align: right; + + label { + white-space: nowrap; + color: #999; + font-size: 0.833em; + + input { + display: inline; + } + } + + input { + width: 70px; + vertical-align: middle; + text-align: right; + } + + select { + width: 85px; + height: 26px; + vertical-align: middle; + font-size: 1em; + } + + .split-input { + display: inline-block; + background: #fff; + border: 1px solid #ddd; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.07); + margin: 1px 0; + min-width: 80px; + overflow: hidden; + line-height: 1em; + text-align: right; + + div.input { + width: 100%; + box-sizing: border-box; + + label { + font-size: 0.75em; + padding: 4px 6px 0; + color: #555; + display: block; + } + + input { + width: 100%; + box-sizing: border-box; + border: 0; + box-shadow: none; + margin: 0; + padding: 0 6px 4px; + color: #555; + background: transparent; + + &::-webkit-input-placeholder { + color: #ddd; + } + } + } + + div.input:first-child { + border-bottom: 1px dashed #ddd; + background: #fff; + + label { + color: #ccc; + } + + input { + color: #ccc; + } + } + } + + .view { + white-space: nowrap; + } + + .edit { + text-align: left; + } + + small.times, + del, + .wc-order-item-taxes, + .wc-order-item-discount, + .wc-order-item-refund-fields { + font-size: 0.92em !important; + color: #888; + } + + .wc-order-item-taxes, + .wc-order-item-refund-fields { + margin: 0; + + label { + display: block; + } + } + + .wc-order-item-discount { + display: block; + margin-top: 0.5em; + } + + small.times { + margin-right: 0.25em; + } + } + + .quantity { + text-align: center; + + input { + text-align: center; + width: 50px; + } + } + + span.subtotal { + opacity: 0.5; + } + + td.tax_class, + th.tax_class { + text-align: left; + } + + .calculated { + border-color: #ae8ca2; + border-style: dotted; + } + + table.meta { + width: 100%; + } + + table.meta, + table.display_meta { + margin: 0.5em 0 0; + font-size: 0.92em !important; + color: #888; + + tr { + + th { + border: 0; + padding: 0 4px 0.5em 0; + line-height: 1.5em; + width: 20%; + } + + td { + padding: 0 4px 0.5em 0; + border: 0; + line-height: 1.5em; + + input { + width: 100%; + margin: 0; + position: relative; + border-bottom: 0; + box-shadow: none; + } + + textarea { + width: 100%; + height: 4em; + margin: 0; + box-shadow: none; + } + + input:focus + textarea { + border-top-color: #999; + } + + p { + margin: 0 0 0.5em; + line-height: 1.5em; + } + + p:last-child { + margin: 0; + } + } + } + } + + .refund_by { + border-bottom: 1px dotted #999; + } + + tr.fee .thumb div { + + @include ir(); + font-size: 1.5em; + line-height: 1em; + vertical-align: middle; + margin: 0 auto; + + &::before { + + @include icon( "\e007" ); + color: #ccc; + } + } + + tr.refund .thumb div { + + @include ir(); + font-size: 1.5em; + line-height: 1em; + vertical-align: middle; + margin: 0 auto; + + &::before { + + @include icon( "\e014" ); + color: #ccc; + } + } + + tr.shipping { + + .thumb div { + + @include ir(); + font-size: 1.5em; + line-height: 1em; + vertical-align: middle; + margin: 0 auto; + + &::before { + + @include icon( "\e01a" ); + color: #ccc; + } + } + + .shipping_method_name, + .shipping_method { + width: 100%; + margin: 0 0 0.5em; + } + } + + th.line_tax { + white-space: nowrap; + } + + th.line_tax, + td.line_tax { + + .delete-order-tax { + + @include ir(); + float: right; + font-size: 14px; + visibility: hidden; + margin: 3px -18px 0 0; + + &::before { + + @include icon_dashicons( "\f153" ); + color: #999; + } + + &:hover::before { + color: $red; + } + } + + &:hover .delete-order-tax { + visibility: visible; + } + } + + small.refunded { + display: block; + color: $red; + white-space: nowrap; + margin-top: 0.5em; + + &::before { + + @include icon_dashicons( "\f171" ); + position: relative; + top: auto; + left: auto; + margin: -1px 4px 0 0; + vertical-align: middle; + line-height: 1em; + } + } + } + } + + .wc-order-edit-line-item { + padding-left: 0; + } + + .wc-order-edit-line-item-actions { + width: 44px; + text-align: right; + padding-left: 0; + vertical-align: middle; + + a { + color: #ccc; + display: inline-block; + cursor: pointer; + padding: 0 0 0.5em; + margin: 0 0 0 12px; + vertical-align: middle; + text-decoration: none; + line-height: 16px; + width: 16px; + overflow: hidden; + + &::before { + margin: 0; + padding: 0; + font-size: 16px; + width: 16px; + height: 16px; + } + + &:hover { + + &::before { + color: #999; + } + } + + &:first-child { + margin-left: 0; + } + } + + .edit-order-item::before { + + @include icon_dashicons( "\f464" ); + position: relative; + } + + .delete-order-item, + .delete_refund { + + &::before { + + @include icon_dashicons( "\f158" ); + position: relative; + } + + &:hover::before { + color: $red; + } + } + } + + tbody tr .wc-order-edit-line-item-actions { + visibility: hidden; + } + + tbody tr:hover .wc-order-edit-line-item-actions { + visibility: visible; + } + + .wc-order-totals .wc-order-edit-line-item-actions { + width: 1.5em; + visibility: visible !important; + + a { + padding: 0; + } + } +} + +#woocommerce-order-downloads { + + .buttons { + float: left; + padding: 0; + margin: 0; + vertical-align: top; + + .add_item_id, + .select2-container { + width: 400px !important; + margin-right: 9px; + vertical-align: top; + float: left; + } + + button { + margin: 2px 0 0; + } + } + + h3 small { + color: #999; + } +} + +#poststuff #woocommerce-order-actions .inside { + margin: 0; + padding: 0; + + ul.order_actions li { + padding: 6px 10px; + box-sizing: border-box; + + &:last-child { + border-bottom: 0; + } + } +} + +#poststuff #woocommerce-order-notes .inside { + margin: 0; + padding: 0; + + ul.order_notes li { + padding: 0 10px; + } +} + +#woocommerce_customers { + + p.search-box { + margin: 6px 0 4px; + float: left; + } + + .tablenav { + float: right; + clear: none; + } +} + +.widefat { + + &.customers td { + vertical-align: middle; + padding: 4px 7px; + } + + .column-order_title { + width: 15%; + + time { + display: block; + color: #999; + margin: 3px 0; + } + } + + .column-orders, + .column-paying, + .column-spent { + text-align: center; + width: 8%; + } + + .column-last_order { + width: 11%; + } + + .column-wc_actions { + width: 110px; + + a.button { + + @include ir(); + display: inline-block; + margin: 2px 4px 2px 0; + padding: 0 !important; + height: 2em !important; + width: 2em; + overflow: hidden; + vertical-align: middle; + + &::after { + font-family: "Dashicons"; + speak: none; + font-weight: normal; + font-variant: normal; + text-transform: none; + margin: 0; + text-indent: 0; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + text-align: center; + line-height: 1.85; + } + + img { + display: block; + width: 12px; + height: auto; + } + } + + a.edit::after { + content: "\f464"; + } + + a.link::after { + font-family: "WooCommerce"; + content: "\e00d"; + } + + a.view::after { + content: "\f177"; + } + + a.refresh::after { + font-family: "WooCommerce"; + content: "\e031"; + } + + a.processing::after { + font-family: "WooCommerce"; + content: "\e00f"; + } + + a.complete::after { + content: "\f147"; + } + } + + small.meta { + display: block; + color: #999; + font-size: inherit; + margin: 3px 0; + } +} + +.post-type-shop_order { + + .tablenav .one-page .displaying-num { + display: none; + } + + .wp-list-table { + margin-top: 1em; + + thead, + tfoot { + + th { + padding: 0.75em 1em; + } + + th.sortable a, + th.sorted a { + padding: 0; + } + + th:first-child { + padding-left: 2em; + } + + th:last-child { + padding-right: 2em; + } + } + + tbody { + + td, + th { + padding: 1em; + line-height: 26px; + } + + td:first-child { + padding-left: 2em; + } + + td:last-child { + padding-right: 2em; + } + } + + tbody tr { + border-top: 1px solid #f5f5f5; + } + + tbody tr:hover:not(.status-trash):not(.no-link) td { + cursor: pointer; + } + + .no-link { + cursor: default !important; + } + + // Columns. + td, + th { + width: 12ch; + vertical-align: middle; + + p { + margin: 0; + } + } + + .check-column { + width: 1px; + white-space: nowrap; + padding: 1em 1em 1em 1em !important; + vertical-align: middle; + + input { + vertical-align: text-top; + margin: 1px 0; + } + } + + .column-order_number { + width: 20ch; + } + + .column-order_total { + width: 8ch; + text-align: right; + + a span { + float: right; + } + } + + .column-order_date, + .column-order_status { + width: 10ch; + } + + .column-order_status { + width: 14ch; + } + + .column-shipping_address, + .column-billing_address { + width: 20ch; + line-height: 1.5em; + + .description { + display: block; + color: #999; + } + } + + .column-wc_actions { + text-align: right; + + a.button { + text-indent: 9999px; + margin: 2px 0 2px 4px; + } + } + + .order-preview { + float: right; + width: 16px; + padding: 20px 4px 4px 4px; + height: 0; + overflow: hidden; + position: relative; + border: 2px solid transparent; + border-radius: 4px; + + &::before { + + @include icon( "\e010" ); + line-height: 16px; + font-size: 14px; + vertical-align: middle; + top: 4px; + + } + + &:hover { + border: 2px solid #00a0d2; + } + } + + .order-preview.disabled { + + &::before { + content: ""; + background: url("../images/wpspin.gif") no-repeat center top; + } + } + } +} + +.order-status { + display: inline-flex; + line-height: 2.5em; + color: #777; + background: #e5e5e5; + border-radius: 4px; + border-bottom: 1px solid rgba(0, 0, 0, 0.05); + margin: -0.25em 0; + cursor: inherit !important; + white-space: nowrap; + max-width: 100%; + + &.status-completed { + background: #c8d7e1; + color: #2e4453; + } + + &.status-on-hold { + background: #f8dda7; + color: #94660c; + } + + &.status-failed { + background: #eba3a3; + color: #761919; + } + + &.status-processing { + background: #c6e1c6; + color: #5b841b; + } + + &.status-trash { + background: #eba3a3; + color: #761919; + } + + > span { + margin: 0 1em; + overflow: hidden; + text-overflow: ellipsis; + } +} + +.wc-order-preview { + + .order-status { + float: right; + margin-right: 54px; + } + + article { + padding: 0 !important; + } + + .modal-close { + border-radius: 0; + } + + .wc-order-preview-table { + width: 100%; + margin: 0; + + th, + td { + padding: 1em 1.5em; + text-align: left; + border: 0; + border-bottom: 1px solid #eee; + margin: 0; + background: transparent; + box-shadow: none; + text-align: right; + vertical-align: top; + } + + td:first-child, + th:first-child { + text-align: left; + } + + th { + border-color: #ccc; + } + + tr:last-child td { + border: 0; + } + + .wc-order-item-sku { + margin-top: 0.5em; + } + + .wc-order-item-meta { + margin-top: 0.5em; + + th, + td { + padding: 0; + border: 0; + text-align: left; + vertical-align: top; + } + + td:last-child { + padding-left: 0.5em; + } + } + } + + .wc-order-preview-addresses { + overflow: hidden; + padding-bottom: 1.5em; + + .wc-order-preview-address, + .wc-order-preview-note { + width: 50%; + float: left; + padding: 1.5em 1.5em 0; + box-sizing: border-box; + word-wrap: break-word; + + h2 { + margin-top: 0; + } + + strong { + display: block; + margin-top: 1.5em; + } + + strong:first-child { + margin-top: 0; + } + } + } + + footer { + + .wc-action-button-group { + display: inline-block; + float: left; + } + + .button.button-large { + margin-left: 10px; + padding: 0 10px !important; + line-height: 28px; + height: auto; + display: inline-block; + } + } + + .wc-action-button-group label { + display: none; + } +} + +.wc-action-button-group { + vertical-align: middle; + line-height: 26px; + text-align: left; + + label { + margin-right: 6px; + cursor: default; + font-weight: bold; + line-height: 28px; + } + + .wc-action-button-group__items { + display: inline-flex; + flex-flow: row wrap; + align-content: flex-start; + justify-content: flex-start; + } + + .wc-action-button { + margin: 0 0 0 -1px !important; + border: 1px solid #ccc; + padding: 0 10px !important; + border-radius: 0 !important; + float: none; + line-height: 28px; + height: auto; + z-index: 1; + position: relative; + overflow: hidden; + text-overflow: ellipsis; + flex: 1 0 auto; + box-sizing: border-box; + text-align: center; + white-space: nowrap; + } + + .wc-action-button:hover, + .wc-action-button:focus { + border: 1px solid #999; + z-index: 2; + } + + .wc-action-button:first-child { + margin-left: 0 !important; + border-top-left-radius: 3px !important; + border-bottom-left-radius: 3px !important; + } + + .wc-action-button:last-child { + border-top-right-radius: 3px !important; + border-bottom-right-radius: 3px !important; + } +} + +@media screen and (max-width: 782px) { + + .wc-order-preview footer { + + .wc-action-button-group .wc-action-button-group__items { + display: flex; + } + + .wc-action-button-group { + float: none; + display: block; + margin-bottom: 4px; + } + + .button.button-large { + width: 100%; + float: none; + text-align: center; + margin: 0; + display: block; + } + } + + .post-type-shop_order .wp-list-table { + + td.check-column { + width: 1em; + } + + td.column-order_number { + padding-left: 0; + padding-bottom: 0.5em; + } + + td.column-order_status, + td.column-order_date { + display: inline-block !important; + padding: 0 1em 1em 1em !important; + + &::before { + display: none !important; + } + } + + td.column-order_date { + padding-left: 0 !important; + } + + td.column-order_status { + float: right; + } + } +} + +.column-customer_message .note-on { + + @include ir(); + margin: 0 auto; + color: #999; + + &::after { + + @include icon( "\e026" ); + line-height: 16px; + } +} + +.column-order_notes .note-on { + + @include ir(); + margin: 0 auto; + color: #999; + + &::after { + + @include icon( "\e027" ); + line-height: 16px; + } +} + +.attributes-table { + + td, + th { + width: 15%; + vertical-align: top; + } + + .attribute-terms { + width: 32%; + } + + .attribute-actions { + width: 2em; + + .configure-terms { + + @include ir(); + padding: 0 !important; + height: 2em !important; + width: 2em; + + &::after { + + @include icon("\f111"); + font-family: "Dashicons"; + line-height: 1.85; + } + } + } +} + +/* Order notes */ +ul.order_notes { + padding: 2px 0 0; + + li { + + .note_content { + padding: 10px; + background: #efefef; + position: relative; + + p { + margin: 0; + padding: 0; + word-wrap: break-word; + } + } + + p.meta { + padding: 10px; + color: #999; + margin: 0; + font-size: 11px; + + .exact-date { + border-bottom: 1px dotted #999; + } + } + + a.delete_note { + color: $red; + } + + .note_content::after { + content: ""; + display: block; + position: absolute; + bottom: -10px; + left: 20px; + width: 0; + height: 0; + border-width: 10px 10px 0 0; + border-style: solid; + border-color: #efefef transparent; + } + } + + li.system-note { + + .note_content { + background: #d7cad2; + } + + .note_content::after { + border-color: #d7cad2 transparent; + } + } + + li.customer-note { + + .note_content { + background: #a7cedc; + } + + .note_content::after { + border-color: #a7cedc transparent; + } + } +} + +.add_note { + border-top: 1px solid #ddd; + padding: 10px 10px 0; + + h4 { + margin-top: 5px !important; + } + + #add_order_note { + width: 100%; + height: 50px; + } +} + +table.wp-list-table { + + .column-thumb { + width: 52px; + text-align: center; + white-space: nowrap; + } + + .column-handle { + width: 17px; + display: none; + } + + tbody { + + td.column-handle { + cursor: move; + width: 17px; + text-align: center; + vertical-align: text-top; + + &::before { + content: "\f333"; + font-family: "Dashicons"; + text-align: center; + line-height: 1; + color: #999; + display: block; + width: 17px; + height: 100%; + margin: 4px 0 0 0; + } + } + } + + .column-name { + width: 22%; + } + + .column-product_cat, + .column-product_tag { + width: 11% !important; + } + + .column-featured, + .column-product_type { + width: 48px; + text-align: left !important; + } + + .column-customer_message, + .column-order_notes { + width: 48px; + text-align: center; + + img { + margin: 0 auto; + padding-top: 0 !important; + } + } + + .manage-column.column-featured img, + .manage-column.column-product_type img { + padding-left: 2px; + } + + .column-price .woocommerce-price-suffix { + display: none; + } + + img { + margin: 1px 2px; + } + + .row-actions { + color: #999; + } + + td.column-thumb img { + margin: 0; + width: auto; + height: auto; + max-width: 40px; + max-height: 40px; + vertical-align: middle; + } + + span.na { + color: #999; + } + + .column-sku { + width: 10%; + } + + .column-price { + width: 10ch; + } + + .column-is_in_stock { + text-align: left !important; + width: 12ch; + } + + span.wc-image, + span.wc-featured { + + @include ir(); + margin: 0 auto; + + &::before { + + @include icon_dashicons( "\f128" ); + } + } + + span.wc-featured { + + &::before { + content: "\f155"; + } + + &.not-featured::before { + content: "\f154"; + } + } + + td.column-featured span.wc-featured { + font-size: 1.6em; + cursor: pointer; + } + + mark { + + &.instock, + &.outofstock, + &.onbackorder { + font-weight: 700; + background: transparent none; + line-height: 1; + } + + &.instock { + color: $green; + } + + &.outofstock { + color: #a44; + } + + &.onbackorder { + color: #eaa600; + } + } + + .order-notes_head, + .notes_head, + .status_head { + + @include ir(); + margin: 0 auto; + + &::after { + + @include icon; + } + } + + .order-notes_head::after { + content: "\e028"; + } + + .notes_head::after { + content: "\e026"; + } + + .status_head::after { + content: "\e011"; + } + + .column-order_items { + width: 12%; + + table.order_items { + width: 100%; + margin: 3px 0 0; + padding: 0; + display: none; + + td { + border: 0; + margin: 0; + padding: 0 0 3px; + } + + td.qty { + color: #999; + padding-right: 6px; + text-align: left; + } + } + } +} + +mark.notice { + background: #fff; + color: $red; + margin: 0 0 0 10px; +} + +a.export_rates, +a.import_rates { + float: right; + margin-left: 9px; + margin-top: -2px; + margin-bottom: 0; +} + +#rates-search { + float: right; + + input.wc-tax-rates-search-field { + padding: 4px 8px; + font-size: 1.2em; + } +} + +#rates-pagination { + float: right; + margin-right: 0.5em; + + .tablenav { + margin: 0; + } +} + +.wc_input_table_wrapper { + overflow-x: auto; + display: block; +} + +table.wc_tax_rates, +table.wc_input_table { + width: 100%; + + th, + td { + display: table-cell !important; + } + + span.tips { + color: $blue; + } + + th { + white-space: nowrap; + padding: 10px; + } + + td { + padding: 0; + border-right: 1px solid #dfdfdf; + border-bottom: 1px solid #dfdfdf; + border-top: 0; + background: #fff; + cursor: default; + + input[type=text], + input[type=number] { + width: 100% !important; + min-width: 100px; + padding: 8px 10px; + margin: 0; + border: 0; + outline: 0; + background: transparent none; + + &:focus { + outline: 0; + box-shadow: none; + } + } + + &.compound, + &.apply_to_shipping { + padding: 5px 7px; + vertical-align: middle; + + input { + width: auto; + padding: 0; + } + } + } + + td:last-child { + border-right: 0; + } + + tr.current td { + background-color: #fefbcc; + } + + .item_cost, + .cost { + text-align: right; + + input { + text-align: right; + } + } + + th.sort { + width: 17px; + padding: 0 4px; + } + + td.sort { + padding: 0 4px; + } + + .ui-sortable:not(.ui-sortable-disabled) td.sort { + cursor: move; + font-size: 15px; + background: #f9f9f9; + text-align: center; + vertical-align: middle; + + &::before { + content: "\f333"; + font-family: "Dashicons"; + text-align: center; + line-height: 1; + color: #999; + display: block; + width: 17px; + float: left; + height: 100%; + } + + &:hover::before { + color: #333; + } + } + + .button { + float: left; + margin-right: 5px; + } + + .export, + .import { + float: right; + margin-right: 0; + margin-left: 5px; + } + + span.tips { + padding: 0 3px; + } + + .pagination { + float: right; + + .button { + margin-left: 5px; + margin-right: 0; + } + + .current { + background: #bbb; + text-shadow: none; + } + } + + tr:last-child td { + border-bottom: 0; + } +} + +table.wc_gateways, +table.wc_emails, +table.wc_shipping { + position: relative; + + th, + td { + display: table-cell !important; + padding: 1em !important; + vertical-align: top; + line-height: 1.75em; + } + + &.wc_emails td { + vertical-align: middle; + } + + tr:nth-child(odd) td { + background: #f9f9f9; + } + + td.name { + font-weight: 700; + } + + .settings { + text-align: right; + } + + .radio, + .default, + .status { + text-align: center; + + .tips { + margin: 0 auto; + } + + input { + margin: 0; + } + } + + td.sort { + font-size: 15px; + text-align: center; + + .wc-item-reorder-nav { white-space: nowrap; width: 72px; - &:before { - content: '\f333'; - font-family: 'Dashicons'; + &::before { + content: "\f333"; + font-family: "Dashicons"; text-align: center; line-height: 1; color: #999; @@ -2913,6 +3122,7 @@ line-height: 24px; cursor: move; } + button { position: relative; overflow: hidden; @@ -2929,7 +3139,8 @@ cursor: pointer; outline: none; } - button:before { + + button::before { display: inline-block; position: absolute; top: 0; @@ -2942,2984 +3153,3179 @@ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } + button:hover, button:focus { color: #191e23; } - .wc-move-down:before { + + .wc-move-down::before { content: "\f347"; } - .wc-move-up:before { + + .wc-move-up::before { content: "\f343"; } + .wc-move-disabled { color: #d5d5d5 !important; cursor: default; pointer-events: none; } - } - } - .wc-payment-gateway-method-name { - font-weight: normal; - } - .wc-email-settings-table-name { - font-weight: 700; - span { - font-weight: normal; - color: #999; - margin: 0 0 0 4px !important; - } - } - .wc-payment-gateway-method-toggle-enabled, - .wc-payment-gateway-method-toggle-disabled { - padding-top: 1px; - display: block; - outline: 0; - box-shadow: none; - } - .wc-email-settings-table-status { - text-align: center; - width: 1em; - .tips { - margin: 0 auto; - } - } - } - .wc-shipping-zone-settings { - th { - padding: 24px 24px 24px 0; - } - td.forminp { - input, textarea { - padding: 8px; - max-width: 100% !important; - } - .wc-shipping-zone-region-select { - width: 448px; - max-width: 100% !important; - .select2-choices { - padding: 8px 8px 4px; - border-color: #DDDDDD; - min-height: 0; - line-height: 1; - input { - padding: 0; - } - li { - margin: 0 4px 4px 0; - } - } - } - } - .wc-shipping-zone-postcodes-toggle { - margin: 0.5em 0 0; - font-size: 0.9em; - text-decoration: underline; - display: block; - } - .wc-shipping-zone-postcodes-toggle + .wc-shipping-zone-postcodes { - display:none; - } - .wc-shipping-zone-postcodes { - textarea { - margin: 10px 0; - } - .description { - font-size: 0.9em; - color: #999; - } - } - } - .wc-shipping-zone-settings + p.submit { - margin-top: 0; - } - table { - tr, tr:hover { - table.wc-shipping-zone-methods { - tr .row-actions { - position: relative; - } - tr:hover .row-actions { - position: static; - } - } - } - } - .wc-shipping-zones-heading .page-title-action { - display: inline-block; - } - table.wc-shipping-zones, table.wc-shipping-zone-methods, table.wc-shipping-classes { - td, th { - vertical-align: top; - line-height: 24px; - padding: 1em !important; - font-size: 14px; - background: #fff; - display: table-cell !important; - li { - line-height: 24px; - font-size: 14px; - } - .woocommerce-help-tip { - margin: 0 !important; - } - } - thead { - th { - vertical-align: middle; - } - .wc-shipping-zone-sort { - text-align: center; - } - } - td.wc-shipping-zones-blank-state, td.wc-shipping-zone-method-blank-state { - background: #f7f1f6 !important; - overflow: hidden; - position: relative; - padding: 7.5em 7.5% !important; - border-bottom: 2px solid #eee2ec; - - &.wc-shipping-zone-method-blank-state { - padding: 2em !important; - p { - margin-bottom: 0; - } - } - p, li { - color: #a46497; - font-size: 1.5em; - line-height: 1.5em; - margin: 0 0 1em; - position: relative; - z-index: 1; - text-shadow: 1px 1px 1px white; - - &.main { - font-size: 2em; - } - } - li { - margin-left: 1em; - list-style: circle inside; - } - &::before { - content: '\e01b'; - font-family: 'WooCommerce'; - text-align: center; - line-height: 1; - color: #eee2ec; - display: block; - width: 1em; - font-size: 40em; - top: 50%; - right: -3.75%; - margin-top: -0.1875em; - position: absolute; - } - .button-primary { - background-color: #804877; - border-color: #804877; - box-shadow: inset 0 1px 0 rgba( 255, 255, 255, 0.2 ), 0 1px 0 rgba( 0, 0, 0, 0.15 ); - margin: 0; - opacity: 1; - text-shadow: 0 -1px 1px #8a4f7f, 1px 0 1px #8a4f7f, 0 1px 1px #8a4f7f, -1px 0 1px #8a4f7f; - font-size: 1.5em; - padding: 0.75em 1em; - height: auto; - position: relative; - z-index: 1; - } - } - .wc-shipping-zone-method-rows { - tr:nth-child( even ) td { - background: #f9f9f9; - } - } - tr.odd, .wc-shipping-class-rows tr:nth-child( odd ) { - td { - background: #f9f9f9; - } - } - tbody.wc-shipping-zone-rows { - td { - border-top: 2px solid #f9f9f9; - } - tr:first-child { - td { - border-top: 0; - } - } - } - tr.wc-shipping-zone-worldwide { - td { - background: #f9f9f9; - border-top: 2px solid #e1e1e1; - } - } - ul, p { - margin: 0; - } - td.wc-shipping-zone-sort, td.wc-shipping-zone-method-sort { - cursor: move; - font-size: 15px; - text-align: center; - - &::before { - content: '\f333'; - font-family: 'Dashicons'; - text-align: center; - line-height: 1; - color: #999; - display: block; - width: 17px; - float: left; - height: 100%; - line-height: 24px; - } - &:hover::before { - color: #333; - } - } - td.wc-shipping-zone-worldwide { - text-align: center; - &::before { - content: '\f319'; - font-family: 'dashicons'; - text-align: center; - line-height: 1; - color: #999; - display: block; - width: 17px; - float: left; - height: 100%; - line-height: 24px; - } - } - .wc-shipping-zone-name, - .wc-shipping-zone-methods { - width: 25%; - } - .wc-shipping-class-description, - .wc-shipping-class-name, - .wc-shipping-class-slug, - .wc-shipping-zone-name, - .wc-shipping-zone-region { - input, select, textarea { - width: 100%; - } - a.wc-shipping-zone-delete, a.wc-shipping-class-delete { - color: #a00; - } - a.wc-shipping-zone-delete:hover, a.wc-shipping-class-delete:hover { - color: red; - } - } - .wc-shipping-class-count { - text-align: center; - } - td.wc-shipping-zone-methods { - color: #555; - .method_disabled { - text-decoration: line-through; - } - ul { - position: relative; - padding-right: 32px; - li { - color: #555; - display: inline; - margin: 0; - } - li::before { - content: ', '; - } - li:first-child::before { - content: ''; - } - } - .add_shipping_method { - display: block; - width: 24px; - padding: 24px 0 0; - height: 0; - overflow: hidden; - cursor: pointer; - - &::before { - @include icon; - font-family: 'Dashicons'; - content: '\f502'; - color: #999; - vertical-align: middle; - line-height: 24px; - font-size: 16px; - margin: 0; - } - - &.disabled { - cursor: not-allowed; - &::before { - color: #ccc; - } - } - } - } - .wc-shipping-zone-method-title { - width: 25%; - .wc-shipping-zone-method-delete { - color: red; - } - } - .wc-shipping-zone-method-enabled { - text-align: center; - - a { - display: inline-block; - } - - .woocommerce-input-toggle { - margin-top: 3px; - } - } - .wc-shipping-zone-method-type { - display: block; - } - tfoot { - input, select { - vertical-align: middle !important; - } - .button-secondary { - float: right; - } - } - .editing { - .wc-shipping-zone-view, .wc-shipping-zone-edit { - display: none; - } - } - } - - .woocommerce-input-toggle { - height: 16px; - width: 32px; - border: 2px solid #935687; - background-color: #935687; - display: inline-block; - text-indent: -9999px; - border-radius: 10em; - position: relative; - margin-top: -1px; - vertical-align: text-top; - - &:before { - content: ""; - display: block; - width: 16px; - height: 16px; - background: #fff; - position: absolute; - top: 0; - right: 0; - border-radius: 100%; - } - - &.woocommerce-input-toggle--disabled { - border-color: #999; - background-color: #999; - - &:before { - right: auto; - left: 0; - } - } - &.woocommerce-input-toggle--loading { - opacity: 0.5; - } - } - - .wc-modal-shipping-method-settings { - background: #f8f8f8; - padding: 1em !important; - form .form-table { - width: 100%; - background: #fff; - margin: 0 0 1.5em; - tr { - th { - width: 30%; - position: relative; - .woocommerce-help-tip { - float: right; - margin: -8px -0.5em 0 0; - vertical-align: middle; - right: 0; - top: 50%; - position: absolute; - } - } - td { - input, select, textarea { - width: 50%; - min-width: 250px; - } - input[type='checkbox'] { - width: auto; - min-width: 16px; - } - } - td, th { - vertical-align: middle; - margin: 0; - line-height: 24px; - padding: 1em; - border-bottom: 1px solid #f8f8f8; - } - } - &:last-of-type { - margin-bottom: 0; - } - } - } - - .wc-backbone-modal .wc-shipping-zone-method-selector { - p { - margin-top: 0; - } - .wc-shipping-zone-method-description { - margin: 0.75em 1px 0; - line-height: 1.5em; - color: #999; - font-style: italic; - } - select { - width: 100%; - cursor: pointer; - } - } - - img.help_tip { - margin: 0 0 0 9px; - vertical-align: middle; - } - - .postbox img.help_tip { - margin-top: 0; - } - - .postbox .woocommerce-help-tip { - margin: 0 0 0 9px; - } - - .status-enabled, - .status-manual, - .status-disabled { - font-size: 1.4em; - @include ir(); - } - - .status-manual::before { - @include icon( '\e008' ); - color: #999; - } - - .status-enabled::before { - @include icon( '\e015' ); - color: $woocommerce; - } - - .status-disabled::before { - @include icon( '\e013' ); - color: #ccc; - } - - .woocommerce { - - h2.woo-nav-tab-wrapper { - margin-bottom: 1em; - } - - nav.woo-nav-tab-wrapper { - margin: 1.5em 0 1em; - } - - .subsubsub { - margin: -8px 0 0; - } - - .wc-admin-breadcrumb { - margin-left: 0.5em; - a { - color: #a46497; - } - } - - #template div { - margin: 0; - - p .button { - float: right; - margin-left: 10px; - margin-top: -4px; - } - - .editor textarea { - margin-bottom: 8px; - } - } - - textarea[disabled='disabled'] { - background: #dfdfdf !important; - } - - table.form-table { - margin: 0; - position: relative; - table-layout: fixed; - - .forminp-radio ul { - margin: 0; - li { - line-height: 1.4em; - } - } - - input[type="text"], - input[type="number"], - input[type="email"] { - height: auto; - } - - textarea.input-text { - height: 100%; - min-width: 150px; - display: block; - } - - // Give regular settings inputs a standard width and padding. - textarea, - input[type="text"], - input[type="email"], - input[type="number"], - input[type="password"], - input[type="datetime"], - input[type="datetime-local"], - input[type="date"], - input[type="time"], - input[type="week"], - input[type="url"], - input[type="tel"], - input.regular-input { - width: 400px; - margin: 0; - padding: 6px; - box-sizing: border-box; - vertical-align: top; - } - - input[type="datetime-local"], - input[type="date"], - input[type="time"], - input[type="week"], - input[type="tel"] { - width: 200px; - } - - select { - width: 400px; - margin: 0; - box-sizing: border-box; - height: 32px; - line-height: 32px; - vertical-align: top; - } - - input[size] { - width: auto !important; - } - - // Ignore nested inputs. - table { - select, - textarea, - input[type="text"], - input[type="email"], - input[type="number"], - input.regular-input { - width: auto; - } - } - - textarea.wide-input { - width: 100%; - } - - img.help_tip, - .woocommerce-help-tip { - padding: 0; - margin: -4px 0 0 5px; - vertical-align: middle; - cursor: help; - line-height: 1; - } - - span.help_tip { - cursor: help; - color: $blue; - } - - th { - position: relative; - padding-right: 24px; - } - - th label { - position: relative; - display: block; - - img.help_tip, - .woocommerce-help-tip { - margin: -8px -24px 0 0; - position: absolute; - right: 0; - top: 50%; - } - } - - th label + .woocommerce-help-tip { - margin: 0 0 0 0; - position: absolute; - right: 0; - top: 20px; - } - - woocommerce-help-tip - - .select2-container { - vertical-align: top; - margin-bottom: 3px; - } - - table.widefat th { - padding-right: inherit; - } - - .wp-list-table .woocommerce-help-tip { - float: none; - } - - fieldset { - margin-top: 4px; - - img.help_tip, - .woocommerce-help-tip { - margin: -3px 0 0 5px; - } - - p.description { - margin-bottom: 8px; - } - - &:first-child { - margin-top: 0; - } - } - - .iris-picker { - z-index: 100; - display: none; - position: absolute; - border: 1px solid #ccc; - border-radius: 3px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); - - .ui-slider { - border: 0 !important; - margin: 0 !important; - width: auto !important; - height: auto !important; - background: none transparent !important; - - .ui-slider-handle { - margin-bottom: 0 !important; - } - } - } - - .iris-error { - background-color: #ffafaf; - } - - .colorpickpreview { - padding: 7px 0; - line-height: 1em; - display: inline-block; - width: 26px; - border: 1px solid #ddd; - font-size: 14px; - } - - .image_width_settings { - vertical-align: middle; - - label { - margin-left: 10px; - } - - input { - width: auto; - } - } - - .wc_payment_gateways_wrapper, - .wc_emails_wrapper { - padding: 0 15px 10px 0; - } - } - - .wc-shipping-zone-settings { - - td.forminp { - input, textarea { - width: 448px; - padding: 6px 11px; - } - - .select2-search input { - padding: 6px; - } - } - } - } - - .woocommerce #tabs-wrap table a.remove { - margin-left: 4px; - } - - .woocommerce #tabs-wrap table p { - margin: 0 0 4px !important; - overflow: hidden; - zoom: 1; - } - - .woocommerce #tabs-wrap table p a.add { - float: left; - } - - #wp-excerpt-editor-container { - background: #fff; - } - - #product_variation-parent #parent_id { - width: 100%; - } - - #postimagediv img { - border: 1px solid #d5d5d5; - max-width: 100%; - } - - #woocommerce-product-images .inside { - margin: 0; - padding: 0; - - .add_product_images { - padding: 0 12px 12px; - } - - #product_images_container { - padding: 0 0 0 9px; - - ul { - @include clearfix(); - margin: 0; - padding: 0; - - li.image, - li.add, - li.wc-metabox-sortable-placeholder { - width: 80px; - float: left; - cursor: move; - border: 1px solid #d5d5d5; - margin: 9px 9px 0 0; - background: #f7f7f7; - @include border-radius(2px); - position: relative; - box-sizing: border-box; - - img { - width: 100%; - height: auto; - display: block; - } - } - - li.wc-metabox-sortable-placeholder { - border: 3px dashed #dddddd; - position: relative; - - &::after { - @include icon_dashicons( '\f161' ); - font-size: 2.618em; - line-height: 72px; - color: #ddd; - } - } - - ul.actions { - position: absolute; - top: -8px; - right: -8px; - padding: 2px; - display: none; - - li { - float: right; - margin: 0 0 0 2px; - - a { - width: 1em; - height: 1em; - margin: 0; - height: 0; - display: block; - overflow: hidden; - - &.tips { - cursor: pointer; - } - } - - a.delete { - @include ir(); - font-size: 1.4em; - - &::before { - @include icon_dashicons( '\f153' ); - color: #999; - background: #fff; - border-radius: 50%; - height: 1em; - width: 1em; - line-height: 1em; - } - - &:hover::before { - color: $red; - } - } - } - } - - li:hover ul.actions { - display: block; - } - } - } - } - - #woocommerce-product-data { - .hndle { - padding: 10px; - - span { - display: block; - vertical-align: middle; - line-height: 24px; - - span { - display: inline; - line-height: inherit; - vertical-align: baseline; - } - } - - select { - margin: 0; - } - - label { - padding-right: 1em; - font-size: 12px; - vertical-align: baseline; - } - - label:first-child { - margin-right: 1em; - border-right: 1px solid #dfdfdf; - } - - input, select { - margin-top: -3px 0 0; - vertical-align: middle; - } - - select { - margin-left: 0.5em; - } - } - - > .handlediv { - margin-top: 4px; - } - - .wrap { - margin: 0; - } - } - - #woocommerce-coupon-description { - padding: 3px 8px; - font-size: 1.7em; - line-height: 1.42em; - height: auto; - width: 100%; - outline: 0; - margin: 10px 0; - display: block; - - &::-webkit-input-placeholder { - line-height: 1.42em; - color: #bbb; - } - - &::-moz-placeholder { - line-height: - 1.42em; - color: #bbb; - } - - &:-ms-input-placeholder { - line-height: 1.42em; - color: #bbb; - } - - &:-moz-placeholder { - line-height: 1.42em; - color: #bbb; - } - } - - #woocommerce-product-data, - #woocommerce-coupon-data { - .panel-wrap { - background: #fff; - } - - .woocommerce_options_panel, - .wc-metaboxes-wrapper { - float: left; - width: 80%; - - .wc-radios { - display: block; - float: left; - margin: 0; - - li { - display: block; - padding: 0 0 10px; - - input { - width: auto; - } - } - } - } - } - - #woocommerce-product-data, - #woocommerce-coupon-data, - .woocommerce { - .panel-wrap { - overflow: hidden; - } - - ul.wc-tabs { - margin: 0; - width: 20%; - float: left; - line-height: 1em; - padding: 0 0 10px; - position: relative; - background-color: #fafafa; - border-right: 1px solid #eee; - box-sizing: border-box; - - &::after { - content: ''; - display: block; - width: 100%; - height: 9999em; - position: absolute; - bottom: -9999em; - left: 0; - background-color: #fafafa; - border-right: 1px solid #eee; - } - - li { - margin: 0; - padding: 0; - display: block; - position: relative; - - a { - margin: 0; - padding: 10px; - display: block; - box-shadow: none; - text-decoration: none; - line-height: 20px !important; - border-bottom: 1px solid #eee; - - span { - margin-left: 0.618em; - margin-right: 0.618em; - } - - &::before { - @include iconbeforedashicons( '\f107' ); - } - } - - &.general_options a::before { - content: '\f107'; - } - - &.inventory_options a::before { - content: '\f481'; - } - - &.shipping_options a::before { - font-family: 'WooCommerce'; - content: '\e01a'; - } - - &.linked_product_options a::before { - content: '\f103'; - } - - &.attribute_options a::before { - content: '\f175'; - } - - &.advanced_options a::before { - font-family: 'Dashicons'; - content: '\f111'; - } - - &.variations_options a::before { - content: '\f509'; - } - - &.usage_restriction_options a::before { - font-family: 'WooCommerce'; - content: '\e602'; - } - - &.usage_limit_options a::before { - font-family: 'WooCommerce'; - content: '\e601'; - } - - &.general_coupon_data a::before { - font-family: 'WooCommerce'; - content: '\e600'; - } - - &.active a { - color: #555; - position: relative; - background-color: #eee; - } - } - } - } - - /** + } + } + + .wc-payment-gateway-method-name { + font-weight: normal; + } + + .wc-email-settings-table-name { + font-weight: 700; + + span { + font-weight: normal; + color: #999; + margin: 0 0 0 4px !important; + } + } + + .wc-payment-gateway-method-toggle-enabled, + .wc-payment-gateway-method-toggle-disabled { + padding-top: 1px; + display: block; + outline: 0; + box-shadow: none; + } + + .wc-email-settings-table-status { + text-align: center; + width: 1em; + + .tips { + margin: 0 auto; + } + } +} + +.wc-shipping-zone-settings { + + th { + padding: 24px 24px 24px 0; + } + + td.forminp { + + input, + textarea { + padding: 8px; + max-width: 100% !important; + } + + .wc-shipping-zone-region-select { + width: 448px; + max-width: 100% !important; + + .select2-choices { + padding: 8px 8px 4px; + border-color: #ddd; + min-height: 0; + line-height: 1; + + input { + padding: 0; + } + + li { + margin: 0 4px 4px 0; + } + } + } + } + + .wc-shipping-zone-postcodes-toggle { + margin: 0.5em 0 0; + font-size: 0.9em; + text-decoration: underline; + display: block; + } + + .wc-shipping-zone-postcodes-toggle + .wc-shipping-zone-postcodes { + display: none; + } + + .wc-shipping-zone-postcodes { + + textarea { + margin: 10px 0; + } + + .description { + font-size: 0.9em; + color: #999; + } + } +} + +.wc-shipping-zone-settings + p.submit { + margin-top: 0; +} + +table { + + tr, + tr:hover { + + table.wc-shipping-zone-methods { + + tr .row-actions { + position: relative; + } + + tr:hover .row-actions { + position: static; + } + } + } +} + +.wc-shipping-zones-heading .page-title-action { + display: inline-block; +} + +table.wc-shipping-zones, + table.wc-shipping-zone-methods, + table.wc-shipping-classes { + + td, + th { + vertical-align: top; + line-height: 24px; + padding: 1em !important; + font-size: 14px; + background: #fff; + display: table-cell !important; + + li { + line-height: 24px; + font-size: 14px; + } + + .woocommerce-help-tip { + margin: 0 !important; + } + } + + thead { + + th { + vertical-align: middle; + } + + .wc-shipping-zone-sort { + text-align: center; + } + } + + td.wc-shipping-zones-blank-state, + td.wc-shipping-zone-method-blank-state { + background: #f7f1f6 !important; + overflow: hidden; + position: relative; + padding: 7.5em 7.5% !important; + border-bottom: 2px solid #eee2ec; + + &.wc-shipping-zone-method-blank-state { + padding: 2em !important; + + p { + margin-bottom: 0; + } + } + + p, + li { + color: #a46497; + font-size: 1.5em; + line-height: 1.5em; + margin: 0 0 1em; + position: relative; + z-index: 1; + text-shadow: 1px 1px 1px white; + + &.main { + font-size: 2em; + } + } + + li { + margin-left: 1em; + list-style: circle inside; + } + + &::before { + content: "\e01b"; + font-family: "WooCommerce"; + text-align: center; + line-height: 1; + color: #eee2ec; + display: block; + width: 1em; + font-size: 40em; + top: 50%; + right: -3.75%; + margin-top: -0.1875em; + position: absolute; + } + + .button-primary { + background-color: #804877; + border-color: #804877; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 0 rgba(0, 0, 0, 0.15); + margin: 0; + opacity: 1; + text-shadow: 0 -1px 1px #8a4f7f, 1px 0 1px #8a4f7f, 0 1px 1px #8a4f7f, -1px 0 1px #8a4f7f; + font-size: 1.5em; + padding: 0.75em 1em; + height: auto; + position: relative; + z-index: 1; + } + } + + .wc-shipping-zone-method-rows { + + tr:nth-child(even) td { + background: #f9f9f9; + } + } + + tr.odd, + .wc-shipping-class-rows tr:nth-child(odd) { + + td { + background: #f9f9f9; + } + } + + tbody.wc-shipping-zone-rows { + + td { + border-top: 2px solid #f9f9f9; + } + + tr:first-child { + + td { + border-top: 0; + } + } + } + + tr.wc-shipping-zone-worldwide { + + td { + background: #f9f9f9; + border-top: 2px solid #e1e1e1; + } + } + + ul, + p { + margin: 0; + } + + td.wc-shipping-zone-sort, + td.wc-shipping-zone-method-sort { + cursor: move; + font-size: 15px; + text-align: center; + + &::before { + content: "\f333"; + font-family: "Dashicons"; + text-align: center; + line-height: 1; + color: #999; + display: block; + width: 17px; + float: left; + height: 100%; + line-height: 24px; + } + + &:hover::before { + color: #333; + } + } + + td.wc-shipping-zone-worldwide { + text-align: center; + + &::before { + content: "\f319"; + font-family: "dashicons"; + text-align: center; + line-height: 1; + color: #999; + display: block; + width: 17px; + float: left; + height: 100%; + line-height: 24px; + } + } + + .wc-shipping-zone-name, + .wc-shipping-zone-methods { + width: 25%; + } + + .wc-shipping-class-description, + .wc-shipping-class-name, + .wc-shipping-class-slug, + .wc-shipping-zone-name, + .wc-shipping-zone-region { + + input, + select, + textarea { + width: 100%; + } + + a.wc-shipping-zone-delete, + a.wc-shipping-class-delete { + color: #a00; + } + + a.wc-shipping-zone-delete:hover, + a.wc-shipping-class-delete:hover { + color: red; + } + } + + .wc-shipping-class-count { + text-align: center; + } + + td.wc-shipping-zone-methods { + color: #555; + + .method_disabled { + text-decoration: line-through; + } + + ul { + position: relative; + padding-right: 32px; + + li { + color: #555; + display: inline; + margin: 0; + } + + li::before { + content: ", "; + } + + li:first-child::before { + content: ""; + } + } + + .add_shipping_method { + display: block; + width: 24px; + padding: 24px 0 0; + height: 0; + overflow: hidden; + cursor: pointer; + + &::before { + + @include icon; + font-family: "Dashicons"; + content: "\f502"; + color: #999; + vertical-align: middle; + line-height: 24px; + font-size: 16px; + margin: 0; + } + + &.disabled { + cursor: not-allowed; + + &::before { + color: #ccc; + } + } + } + } + + .wc-shipping-zone-method-title { + width: 25%; + + .wc-shipping-zone-method-delete { + color: red; + } + } + + .wc-shipping-zone-method-enabled { + text-align: center; + + a { + display: inline-block; + } + + .woocommerce-input-toggle { + margin-top: 3px; + } + } + + .wc-shipping-zone-method-type { + display: block; + } + + tfoot { + + input, + select { + vertical-align: middle !important; + } + + .button-secondary { + float: right; + } + } + + .editing { + + .wc-shipping-zone-view, + .wc-shipping-zone-edit { + display: none; + } + } +} + +.woocommerce-input-toggle { + height: 16px; + width: 32px; + border: 2px solid #935687; + background-color: #935687; + display: inline-block; + text-indent: -9999px; + border-radius: 10em; + position: relative; + margin-top: -1px; + vertical-align: text-top; + + &::before { + content: ""; + display: block; + width: 16px; + height: 16px; + background: #fff; + position: absolute; + top: 0; + right: 0; + border-radius: 100%; + } + + &.woocommerce-input-toggle--disabled { + border-color: #999; + background-color: #999; + + &::before { + right: auto; + left: 0; + } + } + + &.woocommerce-input-toggle--loading { + opacity: 0.5; + } +} + +.wc-modal-shipping-method-settings { + background: #f8f8f8; + padding: 1em !important; + + form .form-table { + width: 100%; + background: #fff; + margin: 0 0 1.5em; + + tr { + + th { + width: 30%; + position: relative; + + .woocommerce-help-tip { + float: right; + margin: -8px -0.5em 0 0; + vertical-align: middle; + right: 0; + top: 50%; + position: absolute; + } + } + + td { + + input, + select, + textarea { + width: 50%; + min-width: 250px; + } + + input[type="checkbox"] { + width: auto; + min-width: 16px; + } + } + + td, + th { + vertical-align: middle; + margin: 0; + line-height: 24px; + padding: 1em; + border-bottom: 1px solid #f8f8f8; + } + } + + &:last-of-type { + margin-bottom: 0; + } + } +} + +.wc-backbone-modal .wc-shipping-zone-method-selector { + + p { + margin-top: 0; + } + + .wc-shipping-zone-method-description { + margin: 0.75em 1px 0; + line-height: 1.5em; + color: #999; + font-style: italic; + } + + select { + width: 100%; + cursor: pointer; + } +} + +img.help_tip { + margin: 0 0 0 9px; + vertical-align: middle; +} + +.postbox img.help_tip { + margin-top: 0; +} + +.postbox .woocommerce-help-tip { + margin: 0 0 0 9px; +} + +.status-enabled, +.status-manual, +.status-disabled { + font-size: 1.4em; + + @include ir(); +} + +.status-manual::before { + + @include icon( "\e008" ); + color: #999; +} + +.status-enabled::before { + + @include icon( "\e015" ); + color: $woocommerce; +} + +.status-disabled::before { + + @include icon( "\e013" ); + color: #ccc; +} + +.woocommerce { + + h2.woo-nav-tab-wrapper { + margin-bottom: 1em; + } + + nav.woo-nav-tab-wrapper { + margin: 1.5em 0 1em; + } + + .subsubsub { + margin: -8px 0 0; + } + + .wc-admin-breadcrumb { + margin-left: 0.5em; + + a { + color: #a46497; + } + } + + #template div { + margin: 0; + + p .button { + float: right; + margin-left: 10px; + margin-top: -4px; + } + + .editor textarea { + margin-bottom: 8px; + } + } + + textarea[disabled="disabled"] { + background: #dfdfdf !important; + } + + table.form-table { + margin: 0; + position: relative; + table-layout: fixed; + + .forminp-radio ul { + margin: 0; + + li { + line-height: 1.4em; + } + } + + input[type="text"], + input[type="number"], + input[type="email"] { + height: auto; + } + + textarea.input-text { + height: 100%; + min-width: 150px; + display: block; + } + + // Give regular settings inputs a standard width and padding. + textarea, + input[type="text"], + input[type="email"], + input[type="number"], + input[type="password"], + input[type="datetime"], + input[type="datetime-local"], + input[type="date"], + input[type="time"], + input[type="week"], + input[type="url"], + input[type="tel"], + input.regular-input { + width: 400px; + margin: 0; + padding: 6px; + box-sizing: border-box; + vertical-align: top; + } + + input[type="datetime-local"], + input[type="date"], + input[type="time"], + input[type="week"], + input[type="tel"] { + width: 200px; + } + + select { + width: 400px; + margin: 0; + box-sizing: border-box; + height: 32px; + line-height: 32px; + vertical-align: top; + } + + input[size] { + width: auto !important; + } + + // Ignore nested inputs. + table { + + select, + textarea, + input[type="text"], + input[type="email"], + input[type="number"], + input.regular-input { + width: auto; + } + } + + textarea.wide-input { + width: 100%; + } + + img.help_tip, + .woocommerce-help-tip { + padding: 0; + margin: -4px 0 0 5px; + vertical-align: middle; + cursor: help; + line-height: 1; + } + + span.help_tip { + cursor: help; + color: $blue; + } + + th { + position: relative; + padding-right: 24px; + } + + th label { + position: relative; + display: block; + + img.help_tip, + .woocommerce-help-tip { + margin: -8px -24px 0 0; + position: absolute; + right: 0; + top: 50%; + } + } + + th label + .woocommerce-help-tip { + margin: 0 0 0 0; + position: absolute; + right: 0; + top: 20px; + } + + woocommerce-help-tip + + .select2-container { + vertical-align: top; + margin-bottom: 3px; + } + + table.widefat th { + padding-right: inherit; + } + + .wp-list-table .woocommerce-help-tip { + float: none; + } + + fieldset { + margin-top: 4px; + + img.help_tip, + .woocommerce-help-tip { + margin: -3px 0 0 5px; + } + + p.description { + margin-bottom: 8px; + } + + &:first-child { + margin-top: 0; + } + } + + .iris-picker { + z-index: 100; + display: none; + position: absolute; + border: 1px solid #ccc; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); + + .ui-slider { + border: 0 !important; + margin: 0 !important; + width: auto !important; + height: auto !important; + background: none transparent !important; + + .ui-slider-handle { + margin-bottom: 0 !important; + } + } + } + + .iris-error { + background-color: #ffafaf; + } + + .colorpickpreview { + padding: 7px 0; + line-height: 1em; + display: inline-block; + width: 26px; + border: 1px solid #ddd; + font-size: 14px; + } + + .image_width_settings { + vertical-align: middle; + + label { + margin-left: 10px; + } + + input { + width: auto; + } + } + + .wc_payment_gateways_wrapper, + .wc_emails_wrapper { + padding: 0 15px 10px 0; + } + } + + .wc-shipping-zone-settings { + + td.forminp { + + input, + textarea { + width: 448px; + padding: 6px 11px; + } + + .select2-search input { + padding: 6px; + } + } + } +} + +.woocommerce #tabs-wrap table a.remove { + margin-left: 4px; +} + +.woocommerce #tabs-wrap table p { + margin: 0 0 4px !important; + overflow: hidden; + zoom: 1; +} + +.woocommerce #tabs-wrap table p a.add { + float: left; +} + +#wp-excerpt-editor-container { + background: #fff; +} + +#product_variation-parent #parent_id { + width: 100%; +} + +#postimagediv img { + border: 1px solid #d5d5d5; + max-width: 100%; +} + +#woocommerce-product-images .inside { + margin: 0; + padding: 0; + + .add_product_images { + padding: 0 12px 12px; + } + + #product_images_container { + padding: 0 0 0 9px; + + ul { + + @include clearfix(); + margin: 0; + padding: 0; + + li.image, + li.add, + li.wc-metabox-sortable-placeholder { + width: 80px; + float: left; + cursor: move; + border: 1px solid #d5d5d5; + margin: 9px 9px 0 0; + background: #f7f7f7; + + @include border-radius(2px); + position: relative; + box-sizing: border-box; + + img { + width: 100%; + height: auto; + display: block; + } + } + + li.wc-metabox-sortable-placeholder { + border: 3px dashed #ddd; + position: relative; + + &::after { + + @include icon_dashicons( "\f161" ); + font-size: 2.618em; + line-height: 72px; + color: #ddd; + } + } + + ul.actions { + position: absolute; + top: -8px; + right: -8px; + padding: 2px; + display: none; + + li { + float: right; + margin: 0 0 0 2px; + + a { + width: 1em; + height: 1em; + margin: 0; + height: 0; + display: block; + overflow: hidden; + + &.tips { + cursor: pointer; + } + } + + a.delete { + + @include ir(); + font-size: 1.4em; + + &::before { + + @include icon_dashicons( "\f153" ); + color: #999; + background: #fff; + border-radius: 50%; + height: 1em; + width: 1em; + line-height: 1em; + } + + &:hover::before { + color: $red; + } + } + } + } + + li:hover ul.actions { + display: block; + } + } + } +} + +#woocommerce-product-data { + + .hndle { + padding: 10px; + + span { + display: block; + vertical-align: middle; + line-height: 24px; + + span { + display: inline; + line-height: inherit; + vertical-align: baseline; + } + } + + select { + margin: 0; + } + + label { + padding-right: 1em; + font-size: 12px; + vertical-align: baseline; + } + + label:first-child { + margin-right: 1em; + border-right: 1px solid #dfdfdf; + } + + input, + select { + margin-top: -3px 0 0; + vertical-align: middle; + } + + select { + margin-left: 0.5em; + } + } + + > .handlediv { + margin-top: 4px; + } + + .wrap { + margin: 0; + } +} + +#woocommerce-coupon-description { + padding: 3px 8px; + font-size: 1.7em; + line-height: 1.42em; + height: auto; + width: 100%; + outline: 0; + margin: 10px 0; + display: block; + + &::-webkit-input-placeholder { + line-height: 1.42em; + color: #bbb; + } + + &::-moz-placeholder { + line-height: 1.42em; + color: #bbb; + } + + &:-ms-input-placeholder { + line-height: 1.42em; + color: #bbb; + } + + &:-moz-placeholder { + line-height: 1.42em; + color: #bbb; + } +} + +#woocommerce-product-data, +#woocommerce-coupon-data { + + .panel-wrap { + background: #fff; + } + + .woocommerce_options_panel, + .wc-metaboxes-wrapper { + float: left; + width: 80%; + + .wc-radios { + display: block; + float: left; + margin: 0; + + li { + display: block; + padding: 0 0 10px; + + input { + width: auto; + } + } + } + } +} + +#woocommerce-product-data, +#woocommerce-coupon-data, +.woocommerce { + + .panel-wrap { + overflow: hidden; + } + + ul.wc-tabs { + margin: 0; + width: 20%; + float: left; + line-height: 1em; + padding: 0 0 10px; + position: relative; + background-color: #fafafa; + border-right: 1px solid #eee; + box-sizing: border-box; + + &::after { + content: ""; + display: block; + width: 100%; + height: 9999em; + position: absolute; + bottom: -9999em; + left: 0; + background-color: #fafafa; + border-right: 1px solid #eee; + } + + li { + margin: 0; + padding: 0; + display: block; + position: relative; + + a { + margin: 0; + padding: 10px; + display: block; + box-shadow: none; + text-decoration: none; + line-height: 20px !important; + border-bottom: 1px solid #eee; + + span { + margin-left: 0.618em; + margin-right: 0.618em; + } + + &::before { + + @include iconbeforedashicons( "\f107" ); + } + } + + &.general_options a::before { + content: "\f107"; + } + + &.inventory_options a::before { + content: "\f481"; + } + + &.shipping_options a::before { + font-family: "WooCommerce"; + content: "\e01a"; + } + + &.linked_product_options a::before { + content: "\f103"; + } + + &.attribute_options a::before { + content: "\f175"; + } + + &.advanced_options a::before { + font-family: "Dashicons"; + content: "\f111"; + } + + &.variations_options a::before { + content: "\f509"; + } + + &.usage_restriction_options a::before { + font-family: "WooCommerce"; + content: "\e602"; + } + + &.usage_limit_options a::before { + font-family: "WooCommerce"; + content: "\e601"; + } + + &.general_coupon_data a::before { + font-family: "WooCommerce"; + content: "\e600"; + } + + &.active a { + color: #555; + position: relative; + background-color: #eee; + } + } + } +} + +/** * Shipping */ - .woocommerce_page_wc-settings { - input[type=url], input[type=email] { - direction: ltr; - } +.woocommerce_page_wc-settings { - .shippingrows { - th.check-column { - padding-top: 20px; - } + input[type=url], + input[type=email] { + direction: ltr; + } - tfoot th { - padding-left: 10px; - } + .shippingrows { - .add.button::before { - @include iconbefore( '\e007' ); - } - } + th.check-column { + padding-top: 20px; + } - h3.wc-settings-sub-title { - font-size: 1.2em; - } - } + tfoot th { + padding-left: 10px; + } - #woocommerce-product-data, - #woocommerce-product-type-options, - #woocommerce-order-data, - #woocommerce-order-downloads, - #woocommerce-coupon-data { - .inside { - margin: 0; - padding: 0; - } - } + .add.button::before { - .woocommerce_options_panel, - .panel { - padding: 9px; - color: #555; + @include iconbefore( "\e007" ); + } + } - .form-field .woocommerce-help-tip { - font-size: 1.4em; - } - } + h3.wc-settings-sub-title { + font-size: 1.2em; + } +} - .woocommerce_page_settings .woocommerce_options_panel, - .panel { - padding: 0; - } +#woocommerce-product-data, +#woocommerce-product-type-options, +#woocommerce-order-data, +#woocommerce-order-downloads, +#woocommerce-coupon-data { - #woocommerce-product-type-options .panel, - #woocommerce-product-specs .inside { - margin: 0; - padding: 9px; - } + .inside { + margin: 0; + padding: 0; + } +} - .woocommerce_options_panel p, - #woocommerce-product-type-options .panel p, - .woocommerce_options_panel fieldset.form-field { - margin: 0 0 9px; - font-size: 12px; - padding: 5px 9px; - line-height: 24px; +.woocommerce_options_panel, +.panel { + padding: 9px; + color: #555; - &::after { - content: '.'; - display: block; - height: 0; - clear: both; - visibility: hidden; - } - } + .form-field .woocommerce-help-tip { + font-size: 1.4em; + } +} - .woocommerce_options_panel .checkbox, - .woocommerce_variable_attributes .checkbox { - width: auto; - margin: 4px 0 !important; - vertical-align: middle; - float: left; - } +.woocommerce_page_settings .woocommerce_options_panel, +.panel { + padding: 0; +} - .woocommerce_variations, - .woocommerce_options_panel { - .downloadable_files table { - width: 100%; - padding: 0 !important; +#woocommerce-product-type-options .panel, +#woocommerce-product-specs .inside { + margin: 0; + padding: 9px; +} - th { - padding: 7px 0 7px 7px !important; +.woocommerce_options_panel p, +#woocommerce-product-type-options .panel p, +.woocommerce_options_panel fieldset.form-field { + margin: 0 0 9px; + font-size: 12px; + padding: 5px 9px; + line-height: 24px; - &.sort { - width: 17px; - padding: 7px !important; - } + &::after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; + } +} - .woocommerce-help-tip { - font-size: 1.1em; - margin-left: 0; - } - } +.woocommerce_options_panel .checkbox, +.woocommerce_variable_attributes .checkbox { + width: auto; + margin: 4px 0 !important; + vertical-align: middle; + float: left; +} - td { - vertical-align: middle !important; - padding: 4px 0 4px 7px !important; - position: relative; +.woocommerce_variations, +.woocommerce_options_panel { - &:last-child { - padding-right: 7px !important; - } + .downloadable_files table { + width: 100%; + padding: 0 !important; - input.input_text { - width: 100%; - float: none; - min-width: 0; - margin: 1px 0; - } + th { + padding: 7px 0 7px 7px !important; - .upload_file_button { - width: auto; - float: right; - cursor: pointer; - } + &.sort { + width: 17px; + padding: 7px !important; + } - .delete { - @include ir(); - font-size: 1.2em; + .woocommerce-help-tip { + font-size: 1.1em; + margin-left: 0; + } + } - &::before { - @include icon_dashicons( '\f153' ); - color: #999; - } + td { + vertical-align: middle !important; + padding: 4px 0 4px 7px !important; + position: relative; - &:hover { - &::before { - color: $red; - } - } - } - } + &:last-child { + padding-right: 7px !important; + } - td.sort { - width: 17px; - cursor: move; - font-size: 15px; - text-align: center; - background: #f9f9f9; - padding-right: 7px !important; + input.input_text { + width: 100%; + float: none; + min-width: 0; + margin: 1px 0; + } - &::before { - content: '\f333'; - font-family: 'Dashicons'; - text-align: center; - line-height: 1; - color: #999; - display: block; - width: 17px; - float: left; - height: 100%; - } + .upload_file_button { + width: auto; + float: right; + cursor: pointer; + } - &:hover::before { - color: #333; - } - } - } - } - .woocommerce_variation { - h3 .sort { - width: 17px; - height: 26px; - cursor: move; - float: right; - font-size: 15px; - font-weight: 400; - margin-right: 0.5em; - visibility: hidden; - text-align: center; - vertical-align: middle; + .delete { - &::before { - content: '\f333'; - font-family: 'Dashicons'; - text-align: center; - line-height: 28px; - color: #999; - display: block; - width: 17px; - float: left; - height: 100%; - } + @include ir(); + font-size: 1.2em; - &:hover::before { - color: #777; - } - } - h3:hover, &.ui-sortable-helper { - .sort { - visibility: visible; - } - } - } + &::before { - .woocommerce_options_panel { - min-height: 175px; - box-sizing: border-box; + @include icon_dashicons( "\f153" ); + color: #999; + } - .downloadable_files { - padding: 0 9px 0 162px; - position: relative; - margin: 9px 0; + &:hover { - label { - position: absolute; - left: 0; - margin: 0 0 0 12px; - line-height: 24px; - } - } + &::before { + color: $red; + } + } + } + } - p { - margin: 9px 0; - } + td.sort { + width: 17px; + cursor: move; + font-size: 15px; + text-align: center; + background: #f9f9f9; + padding-right: 7px !important; - p.form-field, - fieldset.form-field { - padding: 5px 20px 5px 162px !important; /** Padding for aligning labels left - 12px + 150 label width **/ - } + &::before { + content: "\f333"; + font-family: "Dashicons"; + text-align: center; + line-height: 1; + color: #999; + display: block; + width: 17px; + float: left; + height: 100%; + } - .sale_price_dates_fields { - .short:first-of-type { - margin-bottom: 1em; - } + &:hover::before { + color: #333; + } + } + } +} - .short:nth-of-type( 2 ) { - clear: left; - } - } +.woocommerce_variation { - label, - legend { - float: left; - width: 150px; - padding: 0; - margin: 0 0 0 -150px; + h3 .sort { + width: 17px; + height: 26px; + cursor: move; + float: right; + font-size: 15px; + font-weight: 400; + margin-right: 0.5em; + visibility: hidden; + text-align: center; + vertical-align: middle; - .req { - font-weight: 700; - font-style: normal; - color: $red; - } - } + &::before { + content: "\f333"; + font-family: "Dashicons"; + text-align: center; + line-height: 28px; + color: #999; + display: block; + width: 17px; + float: left; + height: 100%; + } - .description { - padding: 0; - margin: 0 0 0 7px; - clear: none; - display: inline; - } + &:hover::before { + color: #777; + } + } - .description-block { - margin-left: 0; - display: block; - } + h3:hover, + &.ui-sortable-helper { - textarea, - input, - select { - margin: 0; - } + .sort { + visibility: visible; + } + } +} - textarea { - float: left; - height: 3.5em; - line-height: 1.5em; - vertical-align: top; - } +.woocommerce_options_panel { + min-height: 175px; + box-sizing: border-box; - input[type='text'], - input[type='email'], - input[type='number'], - input[type='password'] { - width: 50%; - float: left; - } + .downloadable_files { + padding: 0 9px 0 162px; + position: relative; + margin: 9px 0; - input.button { - width: auto; - margin-left: 8px; - } + label { + position: absolute; + left: 0; + margin: 0 0 0 12px; + line-height: 24px; + } + } - select { - float: left; - } + p { + margin: 9px 0; + } - input[type='text'].short, - input[type='email'].short, - input[type='number'].short, - input[type='password'].short, - .short { - width: 50%; - } + p.form-field, + fieldset.form-field { + padding: 5px 20px 5px 162px !important; /** Padding for aligning labels left - 12px + 150 label width **/ + } - .sized { - width: auto !important; - margin-right: 6px; - } + .sale_price_dates_fields { - .options_group { - border-top: 1px solid white; - border-bottom: 1px solid #eee; + .short:first-of-type { + margin-bottom: 1em; + } - &:first-child { - border-top: 0; - } + .short:nth-of-type(2) { + clear: left; + } + } - &:last-child { - border-bottom: 0; - } + label, + legend { + float: left; + width: 150px; + padding: 0; + margin: 0 0 0 -150px; - fieldset { - margin: 9px 0; - font-size: 12px; - padding: 5px 9px; - line-height: 24px; + .req { + font-weight: 700; + font-style: normal; + color: $red; + } + } - label { - width: auto; - float: none; - } + .description { + padding: 0; + margin: 0 0 0 7px; + clear: none; + display: inline; + } - ul { - float: left; - width: 50%; - margin: 0; - padding: 0; + .description-block { + margin-left: 0; + display: block; + } - li { - margin: 0; - width: auto; + textarea, + input, + select { + margin: 0; + } - input { - width: auto; - float: none; - margin-right: 4px; - } - } - } + textarea { + float: left; + height: 3.5em; + line-height: 1.5em; + vertical-align: top; + } - ul.wc-radios label { - margin-left: 0; - } - } - } + input[type="text"], + input[type="email"], + input[type="number"], + input[type="password"] { + width: 50%; + float: left; + } - .dimensions_field .wrap { - display: block; - width: 50%; + input.button { + width: auto; + margin-left: 8px; + } - input { - width: 30.75%; - margin-right: 3.8%; - } + select { + float: left; + } - .last { - margin-right: 0; - } - } + input[type="text"].short, + input[type="email"].short, + input[type="number"].short, + input[type="password"].short, + .short { + width: 50%; + } - &.padded { - padding: 1em; - } + .sized { + width: auto !important; + margin-right: 6px; + } - .select2-container { - float: left; - } - } + .options_group { + border-top: 1px solid white; + border-bottom: 1px solid #eee; - #woocommerce-product-data input.dp-applied { - float: left; - } + &:first-child { + border-top: 0; + } - #grouped_product_options, - #virtual_product_options, - #simple_product_options { - padding: 12px; - font-style: italic; - color: #666; - } + &:last-child { + border-bottom: 0; + } - /** + fieldset { + margin: 9px 0; + font-size: 12px; + padding: 5px 9px; + line-height: 24px; + + label { + width: auto; + float: none; + } + + ul { + float: left; + width: 50%; + margin: 0; + padding: 0; + + li { + margin: 0; + width: auto; + + input { + width: auto; + float: none; + margin-right: 4px; + } + } + } + + ul.wc-radios label { + margin-left: 0; + } + } + } + + .dimensions_field .wrap { + display: block; + width: 50%; + + input { + width: 30.75%; + margin-right: 3.8%; + } + + .last { + margin-right: 0; + } + } + + &.padded { + padding: 1em; + } + + .select2-container { + float: left; + } +} + +#woocommerce-product-data input.dp-applied { + float: left; +} + +#grouped_product_options, +#virtual_product_options, +#simple_product_options { + padding: 12px; + font-style: italic; + color: #666; +} + +/** * WooCommerce meta boxes */ - .wc-metaboxes-wrapper { - .toolbar { - margin: 0 !important; - border-top: 1px solid white; - border-bottom: 1px solid #eee; - padding: 9px 12px !important; - - &:first-child { - border-top: 0; - } - - &:last-child { - border-bottom: 0; - } - - .add_variation { - float: right; - margin-left: 5px; - } - - .save-variation-changes, - .cancel-variation-changes { - float: left; - margin-right: 5px; - } - } - - p.toolbar { - overflow: hidden; - zoom: 1; - } - - .expand-close { - margin-right: 2px; - color: #777; - font-size: 12px; - font-style: italic; - a { - background: none; - padding: 0; - font-size: 12px; - text-decoration: none; - } - } - - &#product_attributes .expand-close { - float: right; - line-height: 28px; - } - - button.add_variable_attribute, - .fr { - float: right; - margin: 0 0 0 6px; - } - - .wc-metaboxes { - border-bottom: 1px solid #eee; - } - - .wc-metabox-sortable-placeholder { - border-color: #bbb; - background-color: #f5f5f5; - margin-bottom: 9px; - border-width: 1px; - border-style: dashed; - } - - .wc-metabox { - background: #fff; - border-bottom: 1px solid #eee; - margin: 0 !important; - - select { - font-weight: 400; - } - - &:last-of-type { - border-bottom: 0; - } - - .handlediv { - width: 27px; - - &::before { - content: '\f142' !important; - cursor: pointer; - display: inline-block; - font: 400 20px/1 'Dashicons'; - line-height: 0.5 !important; - padding: 8px 10px; - position: relative; - right: 12px; - top: 0; - } - } - - &.closed { - @include border-radius(3px); - - .handlediv::before { - content: '\f140' !important; - } - - h3 { - border: 0; - } - } - - h3 { - margin: 0 !important; - padding: 0.75em 0.75em 0.75em 1em !important; - font-size: 1em !important; - overflow: hidden; - zoom: 1; - cursor: move; - - button, a.delete { - float: right; - } - - a.delete { - color: red; - font-weight: normal; - line-height: 26px; - text-decoration: none; - position: relative; - visibility: hidden; - } - - strong { - font-weight: normal; - line-height: 26px; - font-weight: 700; - } - - select { - font-family: sans-serif; - max-width: 20%; - margin: 0.25em 0.25em 0.25em 0; - } - - .handlediv { - background-position: 6px 5px !important; - visibility: hidden; - height: 26px; - } - - &.fixed { - cursor: pointer !important; - } - } - - &.woocommerce_variation h3 { - cursor: pointer; - padding: 0.5em 0.75em 0.5em 1em !important; - a.delete, .handlediv, .sort { - margin-top: 0.25em; - } - } - - h3:hover, &.ui-sortable-helper { - a.delete, .handlediv { - visibility: visible; - } - } - - table { - width: 100%; - position: relative; - background-color: #fdfdfd; - padding: 1em; - border-top: 1px solid #eee; - - td { - text-align: left; - padding: 0 6px 1em 0; - vertical-align: top; - border: 0; - - label { - text-align: left; - display: block; - line-height: 21px; - } - - input { - float: left; - min-width: 200px; - } - - input, - textarea { - width: 100%; - margin: 0; - display: block; - font-size: 14px; - padding: 4px; - color: #555; - } - - select, - .select2-container { - width: 100% !important; - } - - input.short { - width: 200px; - } - - input.checkbox { - width: 16px; - min-width: inherit; - vertical-align: text-bottom; - display: inline-block; - float: none; - } - } - - td.attribute_name { - width: 200px; - } - - .plus, - .minus { - margin-top: 6px; - } - - .fl { - float: left; - } - - .fr { - float: right; - } - } - } - } - - .variations-pagenav { - float: right; - line-height: 24px; - - .displaying-num { - color: #777; - font-size: 12px; - font-style: italic; - } - - a { - padding: 0 10px 3px; - background: rgba(0, 0, 0, 0.05); - font-size: 16px; - font-weight: 400; - text-decoration: none; - } - - a.disabled, - a.disabled:active, - a.disabled:focus, - a.disabled:hover { - color: #a0a5aa; - background: rgba(0, 0, 0, 0.05); - } - } - - .variations-defaults { - float: left; - select { - margin: 0.25em 0.25em 0.25em 0; - } - } - - .woocommerce_variable_attributes { - background-color: #fdfdfd; - border-top: 1px solid #eee; - - .data { - @include clearfix; - padding: 1em 2em; - } - - .upload_image_button { - display: block; - width: 64px; - height: 64px; - float: left; - margin-right: 20px; - position: relative; - cursor: pointer; - - img { - width: 100%; - height: auto; - display: none; - } - - &::before { - content: '\f128'; - font-family: 'Dashicons'; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - text-align: center; - line-height: 64px; - font-size: 64px; - font-weight: 400; - -webkit-font-smoothing: antialiased; - } - - &.remove { - img { - display: block; - } - - &::before { - content: '\f335'; - display: none; - } - - &:hover::before { - display: block; - } - } - } - - .options { - border: 1px solid #eee; - border-width: 1px 0; - padding: 0.25em 0; - - label { - display: inline-block; - padding: 4px 1em 2px 0; - } - - input[type=checkbox] { - margin: 0 5px 0 .5em!important; - vertical-align: middle; - } - } - } - - .form-row { - label { - display: inline-block; - } - - .woocommerce-help-tip { - float: right; - } - - input[type="text"], - input[type="number"], - input[type="password"], - input[type="color"], - input[type="date"], - input[type="datetime"], - input[type="datetime-local"], - input[type="email"], - input[type="month"], - input[type="search"], - input[type="tel"], - input[type="time"], - input[type="url"], - input[type="week"], - select, - textarea { - width: 100%; - vertical-align: middle; - margin: 2px 0 0; - padding: 5px; - } - - select { - height: 30px; - line-height: 30px; - } - - &.dimensions_field { - .wrap { - clear:left; - display: block; - } - input { - width: 33%; - float: left; - vertical-align: middle; - - &:last-of-type { - margin-right: 0; - width: 34%; - } - } - } - - &.form-row-first, - &.form-row-last { - width: 48%; - float: right; - } - - &.form-row-first { - clear: both; - float: left; - } - - &.form-row-full { - clear: both; - } - } - - /** +.wc-metaboxes-wrapper { + + .toolbar { + margin: 0 !important; + border-top: 1px solid white; + border-bottom: 1px solid #eee; + padding: 9px 12px !important; + + &:first-child { + border-top: 0; + } + + &:last-child { + border-bottom: 0; + } + + .add_variation { + float: right; + margin-left: 5px; + } + + .save-variation-changes, + .cancel-variation-changes { + float: left; + margin-right: 5px; + } + } + + p.toolbar { + overflow: hidden; + zoom: 1; + } + + .expand-close { + margin-right: 2px; + color: #777; + font-size: 12px; + font-style: italic; + + a { + background: none; + padding: 0; + font-size: 12px; + text-decoration: none; + } + } + + &#product_attributes .expand-close { + float: right; + line-height: 28px; + } + + button.add_variable_attribute, + .fr { + float: right; + margin: 0 0 0 6px; + } + + .wc-metaboxes { + border-bottom: 1px solid #eee; + } + + .wc-metabox-sortable-placeholder { + border-color: #bbb; + background-color: #f5f5f5; + margin-bottom: 9px; + border-width: 1px; + border-style: dashed; + } + + .wc-metabox { + background: #fff; + border-bottom: 1px solid #eee; + margin: 0 !important; + + select { + font-weight: 400; + } + + &:last-of-type { + border-bottom: 0; + } + + .handlediv { + width: 27px; + + &::before { + content: "\f142" !important; + cursor: pointer; + display: inline-block; + font: 400 20px/1 "Dashicons"; + line-height: 0.5 !important; + padding: 8px 10px; + position: relative; + right: 12px; + top: 0; + } + } + + &.closed { + + @include border-radius(3px); + + .handlediv::before { + content: "\f140" !important; + } + + h3 { + border: 0; + } + } + + h3 { + margin: 0 !important; + padding: 0.75em 0.75em 0.75em 1em !important; + font-size: 1em !important; + overflow: hidden; + zoom: 1; + cursor: move; + + button, + a.delete { + float: right; + } + + a.delete { + color: red; + font-weight: normal; + line-height: 26px; + text-decoration: none; + position: relative; + visibility: hidden; + } + + strong { + font-weight: normal; + line-height: 26px; + font-weight: 700; + } + + select { + font-family: sans-serif; + max-width: 20%; + margin: 0.25em 0.25em 0.25em 0; + } + + .handlediv { + background-position: 6px 5px !important; + visibility: hidden; + height: 26px; + } + + &.fixed { + cursor: pointer !important; + } + } + + &.woocommerce_variation h3 { + cursor: pointer; + padding: 0.5em 0.75em 0.5em 1em !important; + + a.delete, + .handlediv, + .sort { + margin-top: 0.25em; + } + } + + h3:hover, + &.ui-sortable-helper { + + a.delete, + .handlediv { + visibility: visible; + } + } + + table { + width: 100%; + position: relative; + background-color: #fdfdfd; + padding: 1em; + border-top: 1px solid #eee; + + td { + text-align: left; + padding: 0 6px 1em 0; + vertical-align: top; + border: 0; + + label { + text-align: left; + display: block; + line-height: 21px; + } + + input { + float: left; + min-width: 200px; + } + + input, + textarea { + width: 100%; + margin: 0; + display: block; + font-size: 14px; + padding: 4px; + color: #555; + } + + select, + .select2-container { + width: 100% !important; + } + + input.short { + width: 200px; + } + + input.checkbox { + width: 16px; + min-width: inherit; + vertical-align: text-bottom; + display: inline-block; + float: none; + } + } + + td.attribute_name { + width: 200px; + } + + .plus, + .minus { + margin-top: 6px; + } + + .fl { + float: left; + } + + .fr { + float: right; + } + } + } +} + +.variations-pagenav { + float: right; + line-height: 24px; + + .displaying-num { + color: #777; + font-size: 12px; + font-style: italic; + } + + a { + padding: 0 10px 3px; + background: rgba(0, 0, 0, 0.05); + font-size: 16px; + font-weight: 400; + text-decoration: none; + } + + a.disabled, + a.disabled:active, + a.disabled:focus, + a.disabled:hover { + color: #a0a5aa; + background: rgba(0, 0, 0, 0.05); + } +} + +.variations-defaults { + float: left; + + select { + margin: 0.25em 0.25em 0.25em 0; + } +} + +.woocommerce_variable_attributes { + background-color: #fdfdfd; + border-top: 1px solid #eee; + + .data { + + @include clearfix; + padding: 1em 2em; + } + + .upload_image_button { + display: block; + width: 64px; + height: 64px; + float: left; + margin-right: 20px; + position: relative; + cursor: pointer; + + img { + width: 100%; + height: auto; + display: none; + } + + &::before { + content: "\f128"; + font-family: "Dashicons"; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + text-align: center; + line-height: 64px; + font-size: 64px; + font-weight: 400; + -webkit-font-smoothing: antialiased; + } + + &.remove { + + img { + display: block; + } + + &::before { + content: "\f335"; + display: none; + } + + &:hover::before { + display: block; + } + } + } + + .options { + border: 1px solid #eee; + border-width: 1px 0; + padding: 0.25em 0; + + label { + display: inline-block; + padding: 4px 1em 2px 0; + } + + input[type=checkbox] { + margin: 0 5px 0 0.5em !important; + vertical-align: middle; + } + } +} + +.form-row { + + label { + display: inline-block; + } + + .woocommerce-help-tip { + float: right; + } + + input[type="text"], + input[type="number"], + input[type="password"], + input[type="color"], + input[type="date"], + input[type="datetime"], + input[type="datetime-local"], + input[type="email"], + input[type="month"], + input[type="search"], + input[type="tel"], + input[type="time"], + input[type="url"], + input[type="week"], + select, + textarea { + width: 100%; + vertical-align: middle; + margin: 2px 0 0; + padding: 5px; + } + + select { + height: 30px; + line-height: 30px; + } + + &.dimensions_field { + + .wrap { + clear: left; + display: block; + } + + input { + width: 33%; + float: left; + vertical-align: middle; + + &:last-of-type { + margin-right: 0; + width: 34%; + } + } + } + + &.form-row-first, + &.form-row-last { + width: 48%; + float: right; + } + + &.form-row-first { + clear: both; + float: left; + } + + &.form-row-full { + clear: both; + } +} + +/** * Tooltips */ - .tips { - cursor: help; - text-decoration: none; - } +.tips { + cursor: help; + text-decoration: none; +} - img.tips { - padding: 5px 0 0; - } +img.tips { + padding: 5px 0 0; +} - #tiptip_holder { - display: none; - z-index: 8675309; - position: absolute; - top: 0; - /*rtl:ignore*/ - left: 0; +#tiptip_holder { + display: none; + z-index: 8675309; + position: absolute; + top: 0; + + /*rtl:ignore*/ + left: 0; - &.tip_top { - padding-bottom: 5px; + &.tip_top { + padding-bottom: 5px; - #tiptip_arrow_inner { - margin-top: -7px; - margin-left: -6px; - border-top-color: #333; - } - } + #tiptip_arrow_inner { + margin-top: -7px; + margin-left: -6px; + border-top-color: #333; + } + } - &.tip_bottom { - padding-top: 5px; + &.tip_bottom { + padding-top: 5px; - #tiptip_arrow_inner { - margin-top: -5px; - margin-left: -6px; - border-bottom-color: #333; - } - } + #tiptip_arrow_inner { + margin-top: -5px; + margin-left: -6px; + border-bottom-color: #333; + } + } - &.tip_right { - padding-left: 5px; + &.tip_right { + padding-left: 5px; - #tiptip_arrow_inner { - margin-top: -6px; - margin-left: -5px; - border-right-color: #333; - } - } + #tiptip_arrow_inner { + margin-top: -6px; + margin-left: -5px; + border-right-color: #333; + } + } - &.tip_left { - padding-right: 5px; + &.tip_left { + padding-right: 5px; - #tiptip_arrow_inner { - margin-top: -6px; - margin-left: -7px; - border-left-color: #333; - } - } - } + #tiptip_arrow_inner { + margin-top: -6px; + margin-left: -7px; + border-left-color: #333; + } + } +} - #tiptip_content, - .chart-tooltip, - .wc_error_tip { - color: #fff; - font-size: 0.8em; - max-width: 150px; - background: #333; - text-align: center; - border-radius: 3px; - padding: 0.618em 1em; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); +#tiptip_content, +.chart-tooltip, +.wc_error_tip { + color: #fff; + font-size: 0.8em; + max-width: 150px; + background: #333; + text-align: center; + border-radius: 3px; + padding: 0.618em 1em; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); - code { - padding: 1px; - background: #888; - } - } + code { + padding: 1px; + background: #888; + } +} - #tiptip_arrow, - #tiptip_arrow_inner { - position: absolute; - border-color: transparent; - border-style: solid; - border-width: 6px; - height: 0; - width: 0; - } +#tiptip_arrow, +#tiptip_arrow_inner { + position: absolute; + border-color: transparent; + border-style: solid; + border-width: 6px; + height: 0; + width: 0; +} - /*rtl:raw: +/*rtl:raw: #tiptip_arrow { right: 50%; margin-right: -6px; } */ - .wc_error_tip { - max-width: 20em; - line-height: 1.8em; - position: absolute; - white-space: normal; - background: #d82223; - margin: 1.5em 1px 0 -1em; - z-index: 9999999; +.wc_error_tip { + max-width: 20em; + line-height: 1.8em; + position: absolute; + white-space: normal; + background: #d82223; + margin: 1.5em 1px 0 -1em; + z-index: 9999999; - &::after { - content: ''; - display: block; - border: 8px solid #d82223; - border-right-color: transparent; - border-left-color: transparent; - border-top-color: transparent; - position: absolute; - top: -3px; - left: 50%; - margin: -1em 0 0 -3px; - } - } + &::after { + content: ""; + display: block; + border: 8px solid #d82223; + border-right-color: transparent; + border-left-color: transparent; + border-top-color: transparent; + position: absolute; + top: -3px; + left: 50%; + margin: -1em 0 0 -3px; + } +} - /** +/** * Date picker */ - img.ui-datepicker-trigger { - vertical-align: middle; - margin-top: -1px; - cursor: pointer; - } +img.ui-datepicker-trigger { + vertical-align: middle; + margin-top: -1px; + cursor: pointer; +} - .woocommerce_options_panel img.ui-datepicker-trigger, - .wc-metabox-content img.ui-datepicker-trigger { - float: left; - margin-right: 8px; - margin-top: 4px; - margin-left: 4px; - } +.woocommerce_options_panel img.ui-datepicker-trigger, +.wc-metabox-content img.ui-datepicker-trigger { + float: left; + margin-right: 8px; + margin-top: 4px; + margin-left: 4px; +} - #ui-datepicker-div { - display: none; - } +#ui-datepicker-div { + display: none; +} - /** +/** * Reports */ - .woocommerce-reports-remove-filter { - color: red; - text-decoration: none; - } - .woocommerce-reports-wrap, - .woocommerce-reports-wide { - &.woocommerce-reports-wrap { - margin-left: 300px; - padding-top: 18px; - } - - &.halved { - margin: 0; - overflow: hidden; - zoom: 1; - } - - .widefat th { - padding: 7px; - } - - .widefat td { - vertical-align: top; - padding: 7px; - .description { - margin: 4px 0 0; - } - } - - .postbox { - &::after { - content: '.'; - display: block; - height: 0; - clear: both; - visibility: hidden; - } - - h3 { - cursor: default !important; - } - - .inside { - padding: 10px; - margin: 0 !important; - } - - div.stats_range, - h3.stats_range { - border-bottom-color: #dfdfdf; - margin: 0; - padding: 0 !important; - - .export_csv { - float: right; - line-height: 26px; - border-left: 1px solid #dfdfdf; - padding: 10px; - display: block; - text-decoration: none; - - &::before { - @include iconbeforedashicons( '\f346' ); - margin-right: 4px; - } - } - - ul { - list-style: none outside; - margin: 0; - padding: 0; - zoom: 1; - background: #f5f5f5; - border-bottom: 1px solid #ccc; - - &::before, - &::after { - content: ' '; - display: table; - } - - &::after { - clear: both; - } - - li { - float: left; - margin: 0; - padding: 0; - line-height: 26px; - font-weight: bold; - font-size: 14px; - - a { - border-right: 1px solid #dfdfdf; - padding: 10px; - display: block; - text-decoration: none; - } - - &.active { - background: #fff; - box-shadow: 0 4px 0 0 #fff; - - a { - color: #777; - } - } - - &.custom { - padding: 9px 10px; - vertical-align: middle; - - form, - div { - display: inline; - margin: 0; - - input.range_datepicker { - padding: 0; - margin: 0 10px 0 0; - background: transparent; - border: 0; - color: #777; - text-align: center; - box-shadow: none; - - &.from { - margin-right: 0; - } - } - } - } - } - } - } - - .chart-with-sidebar { - padding: 12px 12px 12px 249px; - margin: 0 !important; - - .chart-sidebar { - width: 225px; - margin-left: -237px; - float: left; - } - } - - .chart-widgets { - margin: 0; - padding: 0; - - li.chart-widget { - margin: 0 0 1em; - background: #fafafa; - border: 1px solid #dfdfdf; - - &::after { - content: '.'; - display: block; - height: 0; - clear: both; - visibility: hidden; - } - - h4 { - background: #fff; - border: 1px solid #dfdfdf; - border-left-width: 0; - border-right-width: 0; - padding: 10px; - margin: 0; - color: $blue; - border-top-width: 0; - background-image: linear-gradient(to top, #ececec, #f9f9f9); - - &.section_title:hover { - color: $red; - } - } - - .section_title { - cursor: pointer; - - span { - display: block; - - &::after { - @include iconafter( '\e035' ); - float: right; - font-size: 0.9em; - line-height: 1.618; - } - } - - &.open { - color: #333; - - span::after { - display: none; - } - } - } - - .section { - border-bottom: 1px solid #dfdfdf; - - .select2-container { - width: 100% !important; - } - - &:last-of-type { - border-radius: 0 0 3px 3px; - } - } - - table { - width: 100%; - - td { - padding: 7px 10px; - vertical-align: top; - border-top: 1px solid #e5e5e5; - line-height: 1.4em; - } - - tr:first-child td { - border-top: 0; - } - - td.count { - background: #f5f5f5; - } - - td.name { - max-width: 175px; - - a { - word-wrap: break-word; - } - } - - td.sparkline { - vertical-align: middle; - } - - .wc_sparkline { - width: 32px; - height: 1em; - display: block; - float: right; - } - - tr.active td { - background: #f5f5f5; - } - } - - form, - p { - margin: 0; - padding: 10px; - - .submit { - margin-top: 10px; - } - } - - #product_ids { - width: 100%; - } - - .select_all, - .select_none { - float: right; - color: #999; - margin-left: 4px; - margin-top: 10px; - } - - .description { - margin-left: .5em; - font-weight: normal; - opacity: .8; - } - } - } - - .chart-legend { - list-style: none outside; - margin: 0 0 1em; - padding: 0; - border: 1px solid #dfdfdf; - border-right-width: 0; - border-bottom-width: 0; - background: #fff; - - li { - border-right: 5px solid #aaa; - color: #aaa; - padding: 1em; - display: block; - margin: 0; - transition: all ease 0.5s; - box-shadow: - inset 0 -1px 0 0 #dfdfdf; - - strong { - font-size: 1.618em; - line-height: 1.2em; - color: #464646; - font-weight: normal; - display: block; - font-family: 'HelveticaNeue-Light', 'Helvetica Neue Light', 'Helvetica Neue', sans-serif; - - del { - color: #e74c3c; - font-weight: normal; - } - } - - &:hover { - box-shadow: - inset 0 -1px 0 0 #dfdfdf, - inset 300px 0 0 rgba(156, 93, 144, 0.1); - border-right: 5px solid #9c5d90 !important; - padding-left: 1.5em; - color: #9c5d90; - } - } - } - - .pie-chart-legend { - margin: 12px 0 0; - overflow: hidden; - - li { - float: left; - margin: 0; - padding: 6px 0 0; - border-top: 4px solid #999; - text-align: center; - box-sizing: border-box; - width: 50%; - } - } - - .stat { - font-size: 1.5em !important; - font-weight: 700; - text-align: center; - } - - .chart-placeholder { - width: 100%; - height: 650px; - overflow: hidden; - position: relative; - } - - .chart-prompt { - line-height: 650px; - margin: 0; - color: #999; - font-size: 1.2em; - font-style: italic; - text-align: center; - } - - .chart-container { - background: #fff; - padding: 12px; - position: relative; - border: 1px solid #dfdfdf; - border-radius: 3px; - } - - .main .chart-legend { - margin-top: 12px; - - li { - border-right: 0; - margin: 0 8px 0 0; - float: left; - border-top: 4px solid #aaa; - } - } - } - - .woocommerce-reports-main { - float: left; - min-width: 100%; - - table td { - padding: 9px; - } - } - - .woocommerce-reports-sidebar { - display: inline; - width: 281px; - margin-left: -300px; - clear: both; - float: left; - } - - .woocommerce-reports-left { - width: 49.5%; - float: left; - } - - .woocommerce-reports-right { - width: 49.5%; - float: right; - } - } - - .woocommerce-wide-reports-wrap { - padding-bottom: 11px; - - .widefat { - .export-data { - float: right; - } - - th, - td { - vertical-align: middle; - padding: 7px; - } - } - } - - form.report_filters { - p { - vertical-align: middle; - } - - label, - input, - div { - vertical-align: middle; - } - } - - .chart-tooltip { - position: absolute; - display: none; - line-height: 1; - } - - table.bar_chart { - width: 100%; - - thead th { - text-align: left; - color: #ccc; - padding: 6px 0; - } - - tbody { - th { - padding: 6px 0; - width: 25%; - text-align: left !important; - font-weight: normal !important; - border-bottom: 1px solid #fee; - } - - td { - text-align: right; - line-height: 24px; - padding: 6px 6px 6px 0; - border-bottom: 1px solid #fee; - - span { - color: #8a4b75; - display: block; - } - - span.alt { - color: #47a03e; - margin-top: 6px; - } - } - - td.bars { - position: relative; - text-align: left; - padding: 6px 6px 6px 0; - border-bottom: 1px solid #fee; - - span, - a { - text-decoration: none; - clear: both; - background: #8a4b75; - float: left; - display: block; - line-height: 24px; - height: 24px; - border-radius: 3px; - } - - span.alt { - clear: both; - background: #47a03e; - - span { - margin: 0; - color: #c5dec2 !important; - text-shadow: 0 1px 0 #47a03e; - background: transparent; - } - } - } - } - } - - .post-type-shop_order .woocommerce-BlankState-message::before { - @include icon( '\e01d' ); - } - - .post-type-shop_coupon .woocommerce-BlankState-message::before { - @include icon( '\e600' ); - } - - .post-type-product .woocommerce-BlankState-message::before { - @include icon( '\e006' ); - } - - .woocommerce-BlankState--api .woocommerce-BlankState-message::before { - @include icon( '\e01c' ); - } - - .woocommerce-BlankState--webhooks .woocommerce-BlankState-message::before { - @include icon( '\e01b' ); - } - - .woocommerce-BlankState { - text-align: center; - padding: 5em 0 0; - - .woocommerce-BlankState-message { - color: #aaa; - margin: 0 auto 1.5em; - line-height: 1.5em; - font-size: 1.2em; - max-width: 500px; - - &::before { - color: #ddd; - text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.2), 0 1px 0 rgba(255, 255, 255, 0.8); - font-size: 8em; - display: block; - position: relative !important; - top: auto; - left: auto; - line-height: 1em; - margin: 0 0 0.1875em; - } - } - - .woocommerce-BlankState-cta { - font-size: 1.2em; - padding: 0.75em 1.5em; - margin: 0 .25em; - height: auto; - display: inline-block !important - } - } - - /** +.woocommerce-reports-remove-filter { + color: red; + text-decoration: none; +} + +.woocommerce-reports-wrap, +.woocommerce-reports-wide { + + &.woocommerce-reports-wrap { + margin-left: 300px; + padding-top: 18px; + } + + &.halved { + margin: 0; + overflow: hidden; + zoom: 1; + } + + .widefat th { + padding: 7px; + } + + .widefat td { + vertical-align: top; + padding: 7px; + + .description { + margin: 4px 0 0; + } + } + + .postbox { + + &::after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; + } + + h3 { + cursor: default !important; + } + + .inside { + padding: 10px; + margin: 0 !important; + } + + div.stats_range, + h3.stats_range { + border-bottom-color: #dfdfdf; + margin: 0; + padding: 0 !important; + + .export_csv { + float: right; + line-height: 26px; + border-left: 1px solid #dfdfdf; + padding: 10px; + display: block; + text-decoration: none; + + &::before { + + @include iconbeforedashicons( "\f346" ); + margin-right: 4px; + } + } + + ul { + list-style: none outside; + margin: 0; + padding: 0; + zoom: 1; + background: #f5f5f5; + border-bottom: 1px solid #ccc; + + &::before, + &::after { + content: " "; + display: table; + } + + &::after { + clear: both; + } + + li { + float: left; + margin: 0; + padding: 0; + line-height: 26px; + font-weight: bold; + font-size: 14px; + + a { + border-right: 1px solid #dfdfdf; + padding: 10px; + display: block; + text-decoration: none; + } + + &.active { + background: #fff; + box-shadow: 0 4px 0 0 #fff; + + a { + color: #777; + } + } + + &.custom { + padding: 9px 10px; + vertical-align: middle; + + form, + div { + display: inline; + margin: 0; + + input.range_datepicker { + padding: 0; + margin: 0 10px 0 0; + background: transparent; + border: 0; + color: #777; + text-align: center; + box-shadow: none; + + &.from { + margin-right: 0; + } + } + } + } + } + } + } + + .chart-with-sidebar { + padding: 12px 12px 12px 249px; + margin: 0 !important; + + .chart-sidebar { + width: 225px; + margin-left: -237px; + float: left; + } + } + + .chart-widgets { + margin: 0; + padding: 0; + + li.chart-widget { + margin: 0 0 1em; + background: #fafafa; + border: 1px solid #dfdfdf; + + &::after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; + } + + h4 { + background: #fff; + border: 1px solid #dfdfdf; + border-left-width: 0; + border-right-width: 0; + padding: 10px; + margin: 0; + color: $blue; + border-top-width: 0; + background-image: linear-gradient(to top, #ececec, #f9f9f9); + + &.section_title:hover { + color: $red; + } + } + + .section_title { + cursor: pointer; + + span { + display: block; + + &::after { + + @include iconafter( "\e035" ); + float: right; + font-size: 0.9em; + line-height: 1.618; + } + } + + &.open { + color: #333; + + span::after { + display: none; + } + } + } + + .section { + border-bottom: 1px solid #dfdfdf; + + .select2-container { + width: 100% !important; + } + + &:last-of-type { + border-radius: 0 0 3px 3px; + } + } + + table { + width: 100%; + + td { + padding: 7px 10px; + vertical-align: top; + border-top: 1px solid #e5e5e5; + line-height: 1.4em; + } + + tr:first-child td { + border-top: 0; + } + + td.count { + background: #f5f5f5; + } + + td.name { + max-width: 175px; + + a { + word-wrap: break-word; + } + } + + td.sparkline { + vertical-align: middle; + } + + .wc_sparkline { + width: 32px; + height: 1em; + display: block; + float: right; + } + + tr.active td { + background: #f5f5f5; + } + } + + form, + p { + margin: 0; + padding: 10px; + + .submit { + margin-top: 10px; + } + } + + #product_ids { + width: 100%; + } + + .select_all, + .select_none { + float: right; + color: #999; + margin-left: 4px; + margin-top: 10px; + } + + .description { + margin-left: 0.5em; + font-weight: normal; + opacity: 0.8; + } + } + } + + .chart-legend { + list-style: none outside; + margin: 0 0 1em; + padding: 0; + border: 1px solid #dfdfdf; + border-right-width: 0; + border-bottom-width: 0; + background: #fff; + + li { + border-right: 5px solid #aaa; + color: #aaa; + padding: 1em; + display: block; + margin: 0; + transition: all ease 0.5s; + box-shadow: inset 0 -1px 0 0 #dfdfdf; + + strong { + font-size: 1.618em; + line-height: 1.2em; + color: #464646; + font-weight: normal; + display: block; + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", sans-serif; + + del { + color: #e74c3c; + font-weight: normal; + } + } + + &:hover { + box-shadow: + inset 0 -1px 0 0 #dfdfdf, + inset 300px 0 0 rgba(156, 93, 144, 0.1); + border-right: 5px solid #9c5d90 !important; + padding-left: 1.5em; + color: #9c5d90; + } + } + } + + .pie-chart-legend { + margin: 12px 0 0; + overflow: hidden; + + li { + float: left; + margin: 0; + padding: 6px 0 0; + border-top: 4px solid #999; + text-align: center; + box-sizing: border-box; + width: 50%; + } + } + + .stat { + font-size: 1.5em !important; + font-weight: 700; + text-align: center; + } + + .chart-placeholder { + width: 100%; + height: 650px; + overflow: hidden; + position: relative; + } + + .chart-prompt { + line-height: 650px; + margin: 0; + color: #999; + font-size: 1.2em; + font-style: italic; + text-align: center; + } + + .chart-container { + background: #fff; + padding: 12px; + position: relative; + border: 1px solid #dfdfdf; + border-radius: 3px; + } + + .main .chart-legend { + margin-top: 12px; + + li { + border-right: 0; + margin: 0 8px 0 0; + float: left; + border-top: 4px solid #aaa; + } + } + } + + .woocommerce-reports-main { + float: left; + min-width: 100%; + + table td { + padding: 9px; + } + } + + .woocommerce-reports-sidebar { + display: inline; + width: 281px; + margin-left: -300px; + clear: both; + float: left; + } + + .woocommerce-reports-left { + width: 49.5%; + float: left; + } + + .woocommerce-reports-right { + width: 49.5%; + float: right; + } +} + +.woocommerce-wide-reports-wrap { + padding-bottom: 11px; + + .widefat { + + .export-data { + float: right; + } + + th, + td { + vertical-align: middle; + padding: 7px; + } + } +} + +form.report_filters { + + p { + vertical-align: middle; + } + + label, + input, + div { + vertical-align: middle; + } +} + +.chart-tooltip { + position: absolute; + display: none; + line-height: 1; +} + +table.bar_chart { + width: 100%; + + thead th { + text-align: left; + color: #ccc; + padding: 6px 0; + } + + tbody { + + th { + padding: 6px 0; + width: 25%; + text-align: left !important; + font-weight: normal !important; + border-bottom: 1px solid #fee; + } + + td { + text-align: right; + line-height: 24px; + padding: 6px 6px 6px 0; + border-bottom: 1px solid #fee; + + span { + color: #8a4b75; + display: block; + } + + span.alt { + color: #47a03e; + margin-top: 6px; + } + } + + td.bars { + position: relative; + text-align: left; + padding: 6px 6px 6px 0; + border-bottom: 1px solid #fee; + + span, + a { + text-decoration: none; + clear: both; + background: #8a4b75; + float: left; + display: block; + line-height: 24px; + height: 24px; + border-radius: 3px; + } + + span.alt { + clear: both; + background: #47a03e; + + span { + margin: 0; + color: #c5dec2 !important; + text-shadow: 0 1px 0 #47a03e; + background: transparent; + } + } + } + } +} + +.post-type-shop_order .woocommerce-BlankState-message::before { + + @include icon( "\e01d" ); +} + +.post-type-shop_coupon .woocommerce-BlankState-message::before { + + @include icon( "\e600" ); +} + +.post-type-product .woocommerce-BlankState-message::before { + + @include icon( "\e006" ); +} + +.woocommerce-BlankState--api .woocommerce-BlankState-message::before { + + @include icon( "\e01c" ); +} + +.woocommerce-BlankState--webhooks .woocommerce-BlankState-message::before { + + @include icon( "\e01b" ); +} + +.woocommerce-BlankState { + text-align: center; + padding: 5em 0 0; + + .woocommerce-BlankState-message { + color: #aaa; + margin: 0 auto 1.5em; + line-height: 1.5em; + font-size: 1.2em; + max-width: 500px; + + &::before { + color: #ddd; + text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.2), 0 1px 0 rgba(255, 255, 255, 0.8); + font-size: 8em; + display: block; + position: relative !important; + top: auto; + left: auto; + line-height: 1em; + margin: 0 0 0.1875em; + } + } + + .woocommerce-BlankState-cta { + font-size: 1.2em; + padding: 0.75em 1.5em; + margin: 0 0.25em; + height: auto; + display: inline-block !important; + } +} + +/** * Small screen optimisation */ - @media only screen and (max-width: 1280px) { - #order_data { - .order_data_column { - width: 48%; +@media only screen and (max-width: 1280px) { - &:first-child { - width: 100%; - } - } - } - .woocommerce_options_panel { - .description { - display: block; - clear: both; - margin-left: 0; - } + #order_data { - .short, - input[type='text'].short, - input[type='email'].short, - input[type='number'].short, - input[type='password'].short, - .dimensions_field .wrap { - width: 80%; - } - } + .order_data_column { + width: 48%; + + &:first-child { + width: 100%; + } + } + } + + .woocommerce_options_panel { + + .description { + display: block; + clear: both; + margin-left: 0; + } + + .short, + input[type="text"].short, + input[type="email"].short, + input[type="number"].short, + input[type="password"].short, + .dimensions_field .wrap { + width: 80%; + } + } - .woocommerce_variations, - .woocommerce_options_panel { - .downloadable_files { - padding: 0; - clear: both; + .woocommerce_variations, + .woocommerce_options_panel { - label { - position: static; - } + .downloadable_files { + padding: 0; + clear: both; - table { - margin: 0 12px 24px; - width: 94%; + label { + position: static; + } - .sort { - visibility: hidden; - } - } - } + table { + margin: 0 12px 24px; + width: 94%; - .woocommerce_variable_attributes .downloadable_files table { - margin: 0 0 1em; - width: 100%; - } - } - } + .sort { + visibility: hidden; + } + } + } - /** + .woocommerce_variable_attributes .downloadable_files table { + margin: 0 0 1em; + width: 100%; + } + } +} + +/** * Optimisation for screens 900px and smaller */ - @media only screen and (max-width: 900px) { +@media only screen and (max-width: 900px) { - #woocommerce-coupon-data ul.coupon_data_tabs, - #woocommerce-product-data ul.product_data_tabs, - #woocommerce-product-data .wc-tabs-back { - width: 10%; - } + #woocommerce-coupon-data ul.coupon_data_tabs, + #woocommerce-product-data ul.product_data_tabs, + #woocommerce-product-data .wc-tabs-back { + width: 10%; + } - #woocommerce-coupon-data .wc-metaboxes-wrapper, - #woocommerce-coupon-data .woocommerce_options_panel, - #woocommerce-product-data .wc-metaboxes-wrapper, - #woocommerce-product-data .woocommerce_options_panel { - width: 90%; - } + #woocommerce-coupon-data .wc-metaboxes-wrapper, + #woocommerce-coupon-data .woocommerce_options_panel, + #woocommerce-product-data .wc-metaboxes-wrapper, + #woocommerce-product-data .woocommerce_options_panel { + width: 90%; + } - #woocommerce-coupon-data ul.coupon_data_tabs li a, - #woocommerce-product-data ul.product_data_tabs li a { - position: relative; - text-indent: -999px; - padding: 10px; + #woocommerce-coupon-data ul.coupon_data_tabs li a, + #woocommerce-product-data ul.product_data_tabs li a { + position: relative; + text-indent: -999px; + padding: 10px; - &::before { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - text-indent: 0; - text-align: center; - line-height: 40px; - width: 100%; - height: 40px; - } - } - } + &::before { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + text-indent: 0; + text-align: center; + line-height: 40px; + width: 100%; + height: 40px; + } + } +} - /** +/** * Optimisation for screens 782px and smaller */ - @media only screen and (max-width: 782px) { - #wp-excerpt-media-buttons a { - font-size: 16px; - line-height: 37px; - height: 39px; - padding: 0 20px 0 15px; - } +@media only screen and (max-width: 782px) { - #wp-excerpt-editor-tools { - padding-top: 20px; - padding-right: 15px; - overflow: hidden; - margin-bottom: -1px; - } + #wp-excerpt-media-buttons a { + font-size: 16px; + line-height: 37px; + height: 39px; + padding: 0 20px 0 15px; + } - #woocommerce-product-data .checkbox { - width: 25px; - } + #wp-excerpt-editor-tools { + padding-top: 20px; + padding-right: 15px; + overflow: hidden; + margin-bottom: -1px; + } - .variations-pagenav { - float: none; - text-align: center; - font-size: 18px; + #woocommerce-product-data .checkbox { + width: 25px; + } - .displaying-num { - font-size: 16px; - } + .variations-pagenav { + float: none; + text-align: center; + font-size: 18px; - a { - padding: 8px 20px 11px; - font-size: 18px; - } + .displaying-num { + font-size: 16px; + } - select { - padding: 0 20px; - } - } + a { + padding: 8px 20px 11px; + font-size: 18px; + } - .variations-defaults { - float: none; - text-align: center; - margin-top: 10px; - } + select { + padding: 0 20px; + } + } - .post-type-product { - .wp-list-table { - .column-thumb { - display: none; - text-align: left; - padding-bottom: 0; + .variations-defaults { + float: none; + text-align: center; + margin-top: 10px; + } - &::before { - display: none !important; - } + .post-type-product { - img { - max-width: 32px; - } - } + .wp-list-table { - .is-expanded td:not( .hidden ) { - overflow: visible; - } + .column-thumb { + display: none; + text-align: left; + padding-bottom: 0; - .toggle-row { - top: -28px; - } - } - } + &::before { + display: none !important; + } - .post-type-shop_order { - .wp-list-table { - .column-customer_message, - .column-order_notes { - text-align: inherit; - } + img { + max-width: 32px; + } + } - .column-order_notes .note-on { - font-size: 1.3em; - margin: 0; - } + .is-expanded td:not(.hidden) { + overflow: visible; + } - .is-expanded td:not(.hidden ) { - overflow: visible; - } + .toggle-row { + top: -28px; + } + } + } - .toggle-row { - top: -15px; - } - } - } - } + .post-type-shop_order { - @media only screen and (max-width: 500px) { - .woocommerce_options_panel label, - .woocommerce_options_panel legend { - float: none; - width: auto; - display: block; - margin: 0; - } + .wp-list-table { - .woocommerce_options_panel fieldset.form-field, - .woocommerce_options_panel p.form-field { - padding: 5px 20px !important; - } + .column-customer_message, + .column-order_notes { + text-align: inherit; + } - .addons-wcs-banner-block { - flex-direction: column; - } + .column-order_notes .note-on { + font-size: 1.3em; + margin: 0; + } - .wc_addons_wrap { - .addons-wcs-banner-block { - padding: 40px; - } + .is-expanded td:not(.hidden) { + overflow: visible; + } - .addons-wcs-banner-block-image { - padding: 1em; - text-align: center; - width: 100%; - padding: 2em 0; - margin: 0; + .toggle-row { + top: -15px; + } + } + } +} - .addons-img { - margin: 0; - } - } - } - } +@media only screen and (max-width: 500px) { - /** + .woocommerce_options_panel label, + .woocommerce_options_panel legend { + float: none; + width: auto; + display: block; + margin: 0; + } + + .woocommerce_options_panel fieldset.form-field, + .woocommerce_options_panel p.form-field { + padding: 5px 20px !important; + } + + .addons-wcs-banner-block { + flex-direction: column; + } + + .wc_addons_wrap { + + .addons-wcs-banner-block { + padding: 40px; + } + + .addons-wcs-banner-block-image { + padding: 1em; + text-align: center; + width: 100%; + padding: 2em 0; + margin: 0; + + .addons-img { + margin: 0; + } + } + } +} + +/** * Backbone modal dialog */ - .wc-backbone-modal { - * { - box-sizing: border-box; - } +.wc-backbone-modal { - .wc-backbone-modal-content { - position: fixed; - background: #fff; - z-index: 100000; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - max-width: 100%; - min-width: 500px; - article { - overflow: auto; - } - } + * { + box-sizing: border-box; + } - &.wc-backbone-modal-shipping-method-settings .wc-backbone-modal-content { - width: 75%; - min-width: 500px; - } + .wc-backbone-modal-content { + position: fixed; + background: #fff; + z-index: 100000; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + max-width: 100%; + min-width: 500px; - .select2-container { - width: 100% !important; - } - } + article { + overflow: auto; + } + } - @media screen and (max-width: 782px) { - .wc-backbone-modal .wc-backbone-modal-content { - width: 100%; - height: 100%; - min-width: 100%; - } - } + &.wc-backbone-modal-shipping-method-settings .wc-backbone-modal-content { + width: 75%; + min-width: 500px; + } - .wc-backbone-modal-backdrop { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - min-height: 360px; - background: #000; - opacity: 0.7; - z-index: 99900; - } + .select2-container { + width: 100% !important; + } +} - .wc-backbone-modal-main { - padding-bottom: 55px; +@media screen and (max-width: 782px) { - header, - article { - display: block; - position: relative; - } + .wc-backbone-modal .wc-backbone-modal-content { + width: 100%; + height: 100%; + min-width: 100%; + } +} - .wc-backbone-modal-header { - height: auto; - background: #fcfcfc; - padding: 1em 1.5em; - border-bottom: 1px solid #ddd; +.wc-backbone-modal-backdrop { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + min-height: 360px; + background: #000; + opacity: 0.7; + z-index: 99900; +} - h1 { - margin: 0; - font-size: 18px; - font-weight: 700; - line-height: 1.5em; - } +.wc-backbone-modal-main { + padding-bottom: 55px; - .modal-close-link { - cursor: pointer; - color: #777; - height: 54px; - width: 54px; - padding: 0; - position: absolute; - top: 0; - right: 0; - text-align: center; - border: 0; - border-left: 1px solid #ddd; - background-color: transparent; - transition: color 0.1s ease-in-out, background 0.1s ease-in-out; + header, + article { + display: block; + position: relative; + } - &::before { - font: normal 22px/50px 'dashicons' !important; - color: #666; - display: block; - content: '\f335'; - font-weight: 300; - } + .wc-backbone-modal-header { + height: auto; + background: #fcfcfc; + padding: 1em 1.5em; + border-bottom: 1px solid #ddd; - &:hover, - &:focus { - background: #ddd; - border-color: #ccc; - color: #000; - } + h1 { + margin: 0; + font-size: 18px; + font-weight: 700; + line-height: 1.5em; + } - &:focus { - outline: none; - } - } - } + .modal-close-link { + cursor: pointer; + color: #777; + height: 54px; + width: 54px; + padding: 0; + position: absolute; + top: 0; + right: 0; + text-align: center; + border: 0; + border-left: 1px solid #ddd; + background-color: transparent; + transition: color 0.1s ease-in-out, background 0.1s ease-in-out; - article { - padding: 1.5em; + &::before { + font: normal 22px/50px "dashicons" !important; + color: #666; + display: block; + content: "\f335"; + font-weight: 300; + } - p { + &:hover, + &:focus { + background: #ddd; + border-color: #ccc; + color: #000; + } + + &:focus { + outline: none; + } + } + } + + article { + padding: 1.5em; + + p { margin: 1.5em 0; - } - p:first-child { - margin-top: 0; - } + } - p:last-child { + p:first-child { + margin-top: 0; + } + + p:last-child { margin-bottom: 0; - } - .pagination { + } + + .pagination { padding: 10px 0 0; text-align: center; - } - table.widefat { + } + + table.widefat { margin: 0; width: 100%; border: 0; @@ -5932,13 +6338,15 @@ &:first-child { padding-left: 0; } + &:last-child { padding-right: 0; text-align: right; } } - tbody td, tbody th { + tbody td, + tbody th { padding: 1em; text-align: left; vertical-align: middle; @@ -5946,6 +6354,7 @@ &:first-child { padding-left: 0; } + &:last-child { padding-right: 0; text-align: right; @@ -5959,481 +6368,524 @@ } } - footer { - position: absolute; - left: 0; - right: 0; - bottom: 0; - z-index: 100; - padding: 1em 1.5em; - background: #fcfcfc; - border-top: 1px solid #dfdfdf; - box-shadow: 0 -4px 4px -4px rgba(0, 0, 0, 0.1); + footer { + position: absolute; + left: 0; + right: 0; + bottom: 0; + z-index: 100; + padding: 1em 1.5em; + background: #fcfcfc; + border-top: 1px solid #dfdfdf; + box-shadow: 0 -4px 4px -4px rgba(0, 0, 0, 0.1); - .inner { - text-align: right; - line-height: 23px; + .inner { + text-align: right; + line-height: 23px; - .button { - margin-bottom: 0; - } - } - } - } + .button { + margin-bottom: 0; + } + } + } +} - /** +/** * Select2 elements. */ - .select2-drop, - .select2-dropdown { - z-index: 999999 !important; - } - .select2-results { - line-height: 1.5em; - .select2-results__option, .select2-results__group { - margin: 0; - padding: 8px; - } - .description { - display: block; - color: #999; - padding-top: 4px; - } - } - .select2-dropdown { - border-color: #ddd; - } - .select2-dropdown--below { - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); - } - .select2-dropdown--above { - box-shadow: 0 -1px 1px rgba(0, 0, 0, 0.1); - } - .select2-container { - .select2-selection__rendered.ui-sortable li { - cursor: move; - } - .select2-selection { - border-color: #ddd; - } - .select2-search__field { - min-width: 150px; - } - .select2-selection--single { - height: 32px; - .select2-selection__rendered { - line-height: 32px; - padding-right: 24px; - } - .select2-selection__arrow { - right: 3px; - height: 30px; - } - } - .select2-selection--multiple { - min-height: 28px; - border-radius: 0; - line-height: 1.5; - li { - margin: 0; - } - .select2-selection__choice { - padding: 2px 6px; - .description { - display: none; - } - } - } - .select2-selection__clear { - color: #999; - margin-top: -1px; - } - .select2-search--inline .select2-search__field { - font-family: inherit; - font-size: inherit; - font-weight: inherit; - padding: 3px 0; - } - } - .woocommerce table.form-table .select2-container { - min-width: 400px !important; - } - .post-type-product .tablenav, - .post-type-shop_order .tablenav { - .actions { - overflow: visible; - } - select, - input { - line-height: 1; - height: 32px; - } - .select2-container { - float: left; - width: 240px !important; - font-size: 14px; - vertical-align: middle; - margin: 1px 6px 4px 1px; - } - } +.select2-drop, +.select2-dropdown { + z-index: 999999 !important; +} - .woocommerce-progress-form-wrapper, - .woocommerce-exporter-wrapper, - .woocommerce-importer-wrapper { - text-align: center; - max-width: 700px; - margin: 40px auto; +.select2-results { + line-height: 1.5em; - .error { - text-align: left; - } + .select2-results__option, + .select2-results__group { + margin: 0; + padding: 8px; + } - .wc-progress-steps { - padding: 0 0 24px; - margin: 0; - list-style: none outside; - overflow: hidden; - color: #ccc; - width:100%; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; - li { - width: 25%; - float: left; - padding: 0 0 0.8em; - margin: 0; - text-align: center; - position: relative; - border-bottom: 4px solid #ccc; - line-height: 1.4em; - } - li::before { - content: ''; - border: 4px solid #ccc; - border-radius: 100%; - width: 4px; - height: 4px; - position: absolute; - bottom: 0; - left: 50%; - margin-left: -6px; - margin-bottom: -8px; - background: #fff; - } - li.active { - border-color: #a16696; - color: #a16696; - &::before { - border-color: #a16696; - } - } - li.done { - border-color: #a16696; - color: #a16696; - &::before { - border-color: #a16696; - background: #a16696; - } - } - } + .description { + display: block; + color: #999; + padding-top: 4px; + } +} - .button { - font-size: 1.25em; - padding: 0.5em 1em !important; - line-height: 1.5em !important; - margin-right: 0.5em; - margin-bottom: 2px; - height: auto !important; - border-radius: 4px; - background-color: #bb77ae; - border-color: #a36597; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; - text-shadow: 0 -1px 1px #a36597, 1px 0 1px #a36597, 0 1px 1px #a36597, -1px 0 1px #a36597; - margin: 0; - opacity: 1; +.select2-dropdown { + border-color: #ddd; +} - &:hover, &:focus, &:active { - background: #a36597; - border-color: #a36597; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; - } - } +.select2-dropdown--below { + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); +} - .error .button { - font-size: 1em; - } +.select2-dropdown--above { + box-shadow: 0 -1px 1px rgba(0, 0, 0, 0.1); +} - .wc-actions { - overflow: hidden; - border-top: 1px solid #eee; - margin: 0; - padding: 23px 24px 24px; - line-height: 3em; +.select2-container { - .button { - float: right; - } + .select2-selection__rendered.ui-sortable li { + cursor: move; + } - .woocommerce-importer-toggle-advanced-options { - color: #999; - } - } + .select2-selection { + border-color: #ddd; + } - .woocommerce-exporter, - .woocommerce-importer, - .wc-progress-form-content { - background: #fff; - overflow: hidden; - padding: 0; - margin: 0 0 16px; - box-shadow: 0 1px 3px rgba(0,0,0,.13); - color: #555; - text-align: left; + .select2-search__field { + min-width: 150px; + } - header { - border-bottom: 1px solid #eee; - margin: 0; - padding: 24px 24px 0; - } + .select2-selection--single { + height: 32px; - section { - padding: 24px 24px 0; - } + .select2-selection__rendered { + line-height: 32px; + padding-right: 24px; + } - h2 { - margin: 0 0 24px; - color: #555; - font-size: 24px; - font-weight: normal; - line-height: 1em; - } + .select2-selection__arrow { + right: 3px; + height: 30px; + } + } - p { - font-size: 1em; - line-height: 1.75em; - font-size: 16px; - color: #555; - margin: 0 0 24px; - } + .select2-selection--multiple { + min-height: 28px; + border-radius: 0; + line-height: 1.5; - .form-row { - margin-top: 24px; - } + li { + margin: 0; + } - .spinner { - display: none; - } + .select2-selection__choice { + padding: 2px 6px; - .woocommerce-importer-options th, - .woocommerce-importer-options td, - .woocommerce-exporter-options th, - .woocommerce-exporter-options td { - vertical-align: top; - line-height: 1.75em; - padding: 0 0 24px 0; + .description { + display: none; + } + } + } - label { - color: #555; - font-weight: normal; - } + .select2-selection__clear { + color: #999; + margin-top: -1px; + } - input[type="checkbox"] { - margin: 0 4px 0 0; - padding: 7px; - } + .select2-search--inline .select2-search__field { + font-family: inherit; + font-size: inherit; + font-weight: inherit; + padding: 3px 0; + } +} - input[type="text"], - input[type="number"] { - padding: 7px; - height: auto; - margin: 0; - } +.woocommerce table.form-table .select2-container { + min-width: 400px !important; +} - .woocommerce-importer-file-url-field-wrapper { - border: 1px solid #ddd; - -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.07); - box-shadow: inset 0 1px 2px rgba(0,0,0,.07); - background-color: #fff; - color: #32373c; - outline: 0; - line-height: 1; - display: block; +.post-type-product .tablenav, +.post-type-shop_order .tablenav { - code { - background: none; - font-size: smaller; - padding: 0; - margin: 0; - color: #999; - padding: 7px 0 0 7px; - display: inline-block; - } - input { - font-family: Consolas,Monaco,monospace; - border: 0; - margin: 0; - outline: 0; - box-shadow: none; - display: inline-block; - min-width: 100%; - } - } - } + .actions { + overflow: visible; + } - .woocommerce-exporter-options th, - .woocommerce-importer-options th { - width: 35%; - padding-right: 20px; - } + select, + input { + line-height: 1; + height: 32px; + } - progress { - width: 100%; - height: 42px; - margin: 0 auto 24px; - display: block; - -webkit-appearance: none; - border: none; - display: none; - background: #f5f5f5; - border: 2px solid #eee; - border-radius: 4px; - padding: 0; - box-shadow: 0 1px 0px 0 rgba(255, 255, 255, 0.2); - } + .select2-container { + float: left; + width: 240px !important; + font-size: 14px; + vertical-align: middle; + margin: 1px 6px 4px 1px; + } +} - progress::-webkit-progress-bar { - background: transparent none; - border: 0; - border-radius: 4px; - padding: 0; - box-shadow: none; - } +.woocommerce-progress-form-wrapper, +.woocommerce-exporter-wrapper, +.woocommerce-importer-wrapper { + text-align: center; + max-width: 700px; + margin: 40px auto; - progress::-webkit-progress-value { - border-radius: 3px; - box-shadow: inset 0 1px 1px 0 rgba(255, 255, 255, 0.4); - background: #A46497; - background: linear-gradient( top, #A46497, #66405F ), #A46497; - transition: width 1s ease; - } + .error { + text-align: left; + } - progress::-moz-progress-bar { - border-radius: 3px; - box-shadow: inset 0 1px 1px 0 rgba(255, 255, 255, 0.4); - background: #A46497; - background: linear-gradient( top, #A46497, #66405F ), #A46497; - transition: width 1s ease; - } + .wc-progress-steps { + padding: 0 0 24px; + margin: 0; + list-style: none outside; + overflow: hidden; + color: #ccc; + width: 100%; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; - progress::-ms-fill { - border-radius: 3px; - box-shadow: inset 0 1px 1px 0 rgba(255, 255, 255, 0.4); - background: #A46497; - background: linear-gradient( to bottom, #A46497, #66405F ), #A46497; - transition: width 1s ease; - } + li { + width: 25%; + float: left; + padding: 0 0 0.8em; + margin: 0; + text-align: center; + position: relative; + border-bottom: 4px solid #ccc; + line-height: 1.4em; + } - &.woocommerce-exporter__exporting, - &.woocommerce-importer__importing { - .spinner { - display: block; - } - progress { - display: block; - } - .wc-actions, - .woocommerce-exporter-options { - display: none; - } - } + li::before { + content: ""; + border: 4px solid #ccc; + border-radius: 100%; + width: 4px; + height: 4px; + position: absolute; + bottom: 0; + left: 50%; + margin-left: -6px; + margin-bottom: -8px; + background: #fff; + } - .wc-importer-mapping-table-wrapper, - .wc-importer-error-log { - padding: 0; - } + li.active { + border-color: #a16696; + color: #a16696; - .wc-importer-mapping-table, - .wc-importer-error-log-table { - margin: 0; - border: 0; - box-shadow: none; - width: 100%; - table-layout: fixed; + &::before { + border-color: #a16696; + } + } - td, th { - border: 0; - padding: 12px; - vertical-align: middle; - word-wrap: break-word; + li.done { + border-color: #a16696; + color: #a16696; - select { - width: 100%; - } - } + &::before { + border-color: #a16696; + background: #a16696; + } + } + } - tbody tr:nth-child(odd) td, - tbody tr:nth-child(odd) th { - background: #fbfbfb; - } + .button { + font-size: 1.25em; + padding: 0.5em 1em !important; + line-height: 1.5em !important; + margin-right: 0.5em; + margin-bottom: 2px; + height: auto !important; + border-radius: 4px; + background-color: #bb77ae; + border-color: #a36597; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; + text-shadow: 0 -1px 1px #a36597, 1px 0 1px #a36597, 0 1px 1px #a36597, -1px 0 1px #a36597; + margin: 0; + opacity: 1; - th { - font-weight: bold; - } + &:hover, + &:focus, + &:active { + background: #a36597; + border-color: #a36597; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; + } + } - td:first-child, - th:first-child { - padding-left: 24px; - } + .error .button { + font-size: 1em; + } - td:last-child, - th:last-child { - padding-right: 24px; - } + .wc-actions { + overflow: hidden; + border-top: 1px solid #eee; + margin: 0; + padding: 23px 24px 24px; + line-height: 3em; - .wc-importer-mapping-table-name { - width: 50%; - .description { - color: #999; - margin-top: 4px; - display: block; - code { - background: none; - padding: 0; - white-space: pre-line; /* CSS 3 (and 2.1 as well, actually) */ - word-wrap: break-word; /* IE */ - word-break: break-all; - } - } - } - } + .button { + float: right; + } - .woocommerce-importer-done { - text-align: center; - padding: 48px 24px; - font-size: 1.5em; - line-height: 1.75em; + .woocommerce-importer-toggle-advanced-options { + color: #999; + } + } - &::before { - @include icon( '\e015' ); - color: #A16696; - position: static; - font-size: 100px; - display: block; - float: none; - margin: 0 0 24px; - } - } - } - } + .woocommerce-exporter, + .woocommerce-importer, + .wc-progress-form-content { + background: #fff; + overflow: hidden; + padding: 0; + margin: 0 0 16px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.13); + color: #555; + text-align: left; - .wc-pointer { - .wc-pointer-buttons { - .close { - float: left; - margin: 6px 0 0 15px; - } - } - } + header { + border-bottom: 1px solid #eee; + margin: 0; + padding: 24px 24px 0; + } + + section { + padding: 24px 24px 0; + } + + h2 { + margin: 0 0 24px; + color: #555; + font-size: 24px; + font-weight: normal; + line-height: 1em; + } + + p { + font-size: 1em; + line-height: 1.75em; + font-size: 16px; + color: #555; + margin: 0 0 24px; + } + + .form-row { + margin-top: 24px; + } + + .spinner { + display: none; + } + + .woocommerce-importer-options th, + .woocommerce-importer-options td, + .woocommerce-exporter-options th, + .woocommerce-exporter-options td { + vertical-align: top; + line-height: 1.75em; + padding: 0 0 24px 0; + + label { + color: #555; + font-weight: normal; + } + + input[type="checkbox"] { + margin: 0 4px 0 0; + padding: 7px; + } + + input[type="text"], + input[type="number"] { + padding: 7px; + height: auto; + margin: 0; + } + + .woocommerce-importer-file-url-field-wrapper { + border: 1px solid #ddd; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.07); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.07); + background-color: #fff; + color: #32373c; + outline: 0; + line-height: 1; + display: block; + + code { + background: none; + font-size: smaller; + padding: 0; + margin: 0; + color: #999; + padding: 7px 0 0 7px; + display: inline-block; + } + + input { + font-family: Consolas, Monaco, monospace; + border: 0; + margin: 0; + outline: 0; + box-shadow: none; + display: inline-block; + min-width: 100%; + } + } + } + + .woocommerce-exporter-options th, + .woocommerce-importer-options th { + width: 35%; + padding-right: 20px; + } + + progress { + width: 100%; + height: 42px; + margin: 0 auto 24px; + display: block; + -webkit-appearance: none; + border: none; + display: none; + background: #f5f5f5; + border: 2px solid #eee; + border-radius: 4px; + padding: 0; + box-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.2); + } + + progress::-webkit-progress-bar { + background: transparent none; + border: 0; + border-radius: 4px; + padding: 0; + box-shadow: none; + } + + progress::-webkit-progress-value { + border-radius: 3px; + box-shadow: inset 0 1px 1px 0 rgba(255, 255, 255, 0.4); + background: #a46497; + background: linear-gradient(top, #a46497, #66405f), #a46497; + transition: width 1s ease; + } + + progress::-moz-progress-bar { + border-radius: 3px; + box-shadow: inset 0 1px 1px 0 rgba(255, 255, 255, 0.4); + background: #a46497; + background: linear-gradient(top, #a46497, #66405f), #a46497; + transition: width 1s ease; + } + + progress::-ms-fill { + border-radius: 3px; + box-shadow: inset 0 1px 1px 0 rgba(255, 255, 255, 0.4); + background: #a46497; + background: linear-gradient(to bottom, #a46497, #66405f), #a46497; + transition: width 1s ease; + } + + &.woocommerce-exporter__exporting, + &.woocommerce-importer__importing { + + .spinner { + display: block; + } + + progress { + display: block; + } + + .wc-actions, + .woocommerce-exporter-options { + display: none; + } + } + + .wc-importer-mapping-table-wrapper, + .wc-importer-error-log { + padding: 0; + } + + .wc-importer-mapping-table, + .wc-importer-error-log-table { + margin: 0; + border: 0; + box-shadow: none; + width: 100%; + table-layout: fixed; + + td, + th { + border: 0; + padding: 12px; + vertical-align: middle; + word-wrap: break-word; + + select { + width: 100%; + } + } + + tbody tr:nth-child(odd) td, + tbody tr:nth-child(odd) th { + background: #fbfbfb; + } + + th { + font-weight: bold; + } + + td:first-child, + th:first-child { + padding-left: 24px; + } + + td:last-child, + th:last-child { + padding-right: 24px; + } + + .wc-importer-mapping-table-name { + width: 50%; + + .description { + color: #999; + margin-top: 4px; + display: block; + + code { + background: none; + padding: 0; + white-space: pre-line; /* CSS 3 (and 2.1 as well, actually) */ + word-wrap: break-word; /* IE */ + word-break: break-all; + } + } + } + } + + .woocommerce-importer-done { + text-align: center; + padding: 48px 24px; + font-size: 1.5em; + line-height: 1.75em; + + &::before { + + @include icon( "\e015" ); + color: #a16696; + position: static; + font-size: 100px; + display: block; + float: none; + margin: 0 0 24px; + } + } + } +} + +.wc-pointer { + + .wc-pointer-buttons { + + .close { + float: left; + margin: 6px 0 0 15px; + } + } +} diff --git a/assets/css/twenty-seventeen.scss b/assets/css/twenty-seventeen.scss index 6181d04469d..a736eb31b93 100644 --- a/assets/css/twenty-seventeen.scss +++ b/assets/css/twenty-seventeen.scss @@ -1,30 +1,32 @@ /** * Twenty Seventeen integration styles */ -@import 'mixins'; -@import 'animation'; +@import "mixins"; +@import "animation"; /** * Fonts */ @font-face { - font-family: 'star'; - src: url('../fonts/star.eot'); - src: url('../fonts/star.eot?#iefix') format('embedded-opentype'), - url('../fonts/star.woff') format('woff'), - url('../fonts/star.ttf') format('truetype'), - url('../fonts/star.svg#star') format('svg'); + font-family: "star"; + src: url("../fonts/star.eot"); + src: + url("../fonts/star.eot?#iefix") format("embedded-opentype"), + url("../fonts/star.woff") format("woff"), + url("../fonts/star.ttf") format("truetype"), + url("../fonts/star.svg#star") format("svg"); font-weight: normal; font-style: normal; } @font-face { - font-family: 'WooCommerce'; - src: url('../fonts/WooCommerce.eot'); - src: url('../fonts/WooCommerce.eot?#iefix') format('embedded-opentype'), - url('../fonts/WooCommerce.woff') format('woff'), - url('../fonts/WooCommerce.ttf') format('truetype'), - url('../fonts/WooCommerce.svg#WooCommerce') format('svg'); + font-family: "WooCommerce"; + src: url("../fonts/WooCommerce.eot"); + src: + url("../fonts/WooCommerce.eot?#iefix") format("embedded-opentype"), + url("../fonts/WooCommerce.woff") format("woff"), + url("../fonts/WooCommerce.ttf") format("truetype"), + url("../fonts/WooCommerce.svg#WooCommerce") format("svg"); font-weight: normal; font-style: normal; } @@ -55,17 +57,21 @@ /** * Global elements */ - .woocommerce { +.woocommerce { + .blockUI.blockOverlay { position: relative; + @include loader(); } .loader { + @include loader(); } form .form-row { + .required { color: firebrick; text-decoration: none; @@ -78,14 +84,16 @@ .optional { visibility: visible; - } + } } .woocommerce-form-login { + .woocommerce-form-login__submit { float: left; margin-right: 1em; } + .woocommerce-form-login__rememberme { display: inline-block; line-height: 3em; @@ -101,6 +109,7 @@ font-size: 0.8125rem; a { + @include link(); } } @@ -128,7 +137,7 @@ float: none; line-height: 1.5; border-radius: 2px; - transition: background-color ease-in-out .3s; + transition: background-color ease-in-out 0.3s; } span.page-numbers { @@ -147,7 +156,7 @@ top: 0; left: 0; display: inline-block; - padding: .5em 1em; + padding: 0.5em 1em; font-size: 13px; font-size: 0.8125rem; text-transform: uppercase; @@ -155,10 +164,12 @@ } .price { + del { - opacity: .5; + opacity: 0.5; display: inline-block; } + ins { display: inline-block; } @@ -190,7 +201,9 @@ .woocommerce-message, .woocommerce-error, .woocommerce-info { + a { + @include link_white(); } } @@ -224,37 +237,40 @@ * Shop page */ .woocommerce-result-count { - padding: .75em 0; + padding: 0.75em 0; } /** * Products */ ul.products { + li.product { list-style: none; .price, .star-rating { display: block; - margin-bottom: .75em; + margin-bottom: 0.75em; } .woocommerce-placeholder { - border: 1px solid #F2F2F2; + border: 1px solid #f2f2f2; } .button { + @include link(); &.loading { - opacity: .5; + opacity: 0.5; } } .added_to_cart { + @include link(); - margin-left: .5em; + margin-left: 0.5em; } } } @@ -266,10 +282,10 @@ ul.products { line-height: 1; font-size: 1em; width: 5.4em; - font-family: 'star'; + font-family: "star"; &::before { - content: '\73\73\73\73\73'; + content: "\73\73\73\73\73"; float: left; top: 0; left: 0; @@ -286,7 +302,7 @@ ul.products { } span::before { - content: '\53\53\53\53\53'; + content: "\53\53\53\53\53"; top: 0; position: absolute; left: 0; @@ -318,13 +334,15 @@ a.remove { } } -dl.variation, .wc-item-meta { +dl.variation, + .wc-item-meta { list-style: none outside; - dt, .wc-item-meta-label { + dt, + .wc-item-meta-label { float: left; clear: both; - margin-right: .25em; + margin-right: 0.25em; display: inline-block; list-style: none outside; } @@ -343,6 +361,7 @@ dl.variation, .wc-item-meta { * Single product */ .single-product { + div.product { position: relative; } @@ -365,14 +384,15 @@ dl.variation, .wc-item-meta { .star-rating { float: left; - margin-right: .25em; + margin-right: 0.25em; } } form.cart { + .quantity { float: left; - margin-right: .5em; + margin-right: 0.5em; } input { @@ -381,10 +401,12 @@ dl.variation, .wc-item-meta { } .woocommerce-variation-add-to-cart { + .button { - padding-top: .72em; - padding-bottom: .72em; + padding-top: 0.72em; + padding-bottom: 0.72em; } + .button.disabled { opacity: 0.2; } @@ -392,12 +414,13 @@ dl.variation, .wc-item-meta { } table.variations { + label { margin: 0; } select { - margin-right: .5em; + margin-right: 0.5em; } } @@ -421,7 +444,7 @@ table.variations { } .woocommerce-product-gallery__image--placeholder { - border: 1px solid #F2F2F2; + border: 1px solid #f2f2f2; } .woocommerce-product-gallery__image:nth-child(n+2) { @@ -430,6 +453,7 @@ table.variations { } .flex-control-thumbs { + li { list-style: none; cursor: pointer; @@ -437,7 +461,7 @@ table.variations { } img { - opacity: .5; + opacity: 0.5; &:hover, &.flex-active { @@ -453,27 +477,33 @@ table.variations { } .woocommerce-product-gallery--columns-3 { + .flex-control-thumbs li { width: 33.3333%; } + .flex-control-thumbs li:nth-child(3n+1) { clear: left; } } .woocommerce-product-gallery--columns-4 { + .flex-control-thumbs li { width: 25%; } + .flex-control-thumbs li:nth-child(4n+1) { clear: left; } } .woocommerce-product-gallery--columns-5 { + .flex-control-thumbs li { width: 20%; } + .flex-control-thumbs li:nth-child(5n+1) { clear: left; } @@ -493,6 +523,7 @@ table.variations { margin-right: 1em; &.active { + a { box-shadow: 0 3px 0 rgba(15, 15, 15, 1); } @@ -500,6 +531,7 @@ table.variations { } a { + @include link(); } @@ -517,6 +549,7 @@ table.variations { } #reviews { + li.review, li.comment { list-style: none; @@ -530,11 +563,12 @@ table.variations { } p.meta { - margin-bottom: .5em; + margin-bottom: 0.5em; } } p.stars { + a { position: relative; height: 1em; @@ -552,40 +586,46 @@ table.variations { width: 1em; height: 1em; line-height: 1; - font-family: 'WooCommerce'; - content: '\e021'; + font-family: "WooCommerce"; + content: "\e021"; text-indent: 0; } &:hover { + ~ a::before { - content: '\e021'; + content: "\e021"; } } } &:hover { + a { + &::before { - content: '\e020'; + content: "\e020"; } } } &.selected { + a.active { + &::before { - content: '\e020'; + content: "\e020"; } ~ a::before { - content: '\e021'; + content: "\e021"; } } - a:not( .active ) { + a:not(.active) { + &::before { - content: '\e020'; + content: "\e020"; } } } @@ -624,17 +664,21 @@ table.variations { } .widget_shopping_cart { + .buttons { + a { display: inline-block; - margin: 0 .5em 0 0; + margin: 0 0.5em 0 0; } } } .widget_layered_nav { + .chosen { - &:before { + + &::before { content: "×"; display: inline-block; width: 16px; @@ -644,12 +688,13 @@ table.variations { text-align: center; border-radius: 100%; border: 1px solid black; - margin-right: .25em; + margin-right: 0.25em; } } } .widget_price_filter { + .price_slider { margin-bottom: 1em; } @@ -661,7 +706,7 @@ table.variations { .button { float: left; - padding: .4em 1em; + padding: 0.4em 1em; } } @@ -720,17 +765,19 @@ table.variations { } .widget_rating_filter { + li { text-align: right; .star-rating { float: left; - margin-top: .3em; + margin-top: 0.3em; } } } .widget_product_search { + form { position: relative; } @@ -741,8 +788,8 @@ table.variations { input[type=submit] { position: absolute; - top: .5em; - right: .5em; + top: 0.5em; + right: 0.5em; padding-left: 1em; padding-right: 1em; } @@ -752,6 +799,7 @@ table.variations { * Account section */ .woocommerce-account { + .woocommerce-MyAccount-navigation { float: right; width: 25%; @@ -759,7 +807,7 @@ table.variations { li { list-style: none; - padding: .5em 0; + padding: 0.5em 0; border-bottom: 1px solid #ddd; a { @@ -770,14 +818,15 @@ table.variations { } } - &:before { + &::before { content: "→"; display: inline-block; - margin-right: .25em; + margin-right: 0.25em; color: #ddd; } &.is-active { + a { box-shadow: 0 3px 0 rgba(15, 15, 15, 1); } @@ -794,8 +843,9 @@ table.variations { * Cart */ .woocommerce-cart-form { + td { - padding: 1em .5em; + padding: 1em 0.5em; } img { @@ -818,14 +868,16 @@ table.variations { } .actions { + .input-text { width: 130px !important; float: left; - margin-right: .25em; + margin-right: 0.25em; } } .quantity { + input { width: 4em; } @@ -833,21 +885,25 @@ table.variations { } .cart_totals { - th, td { + + th, + td { vertical-align: top; padding: 1em 0; line-height: 1.5em; } + th { padding-right: 1em; } + .woocommerce-shipping-destination { margin-bottom: 0; } } .shipping-calculator-button { - margin-top: .5em; + margin-top: 0.5em; display: inline-block; } @@ -860,7 +916,7 @@ table.variations { margin: 0; li { - margin-bottom: .5em; + margin-bottom: 0.5em; input { float: left; @@ -886,7 +942,7 @@ table.variations { border-color: #999; } - &:after { + &::after { content: "→"; } } @@ -895,6 +951,7 @@ table.variations { * Checkout */ #ship-to-different-address { + label { font-weight: 300; cursor: pointer; @@ -903,7 +960,7 @@ table.variations { position: relative; display: block; - &:before { + &::before { content: ""; display: block; height: 16px; @@ -912,13 +969,13 @@ table.variations { background: #bbb; border-radius: 13em; box-sizing: content-box; - transition: all ease-in-out .3s; + transition: all ease-in-out 0.3s; position: absolute; top: 4px; right: 0; } - &:after { + &::after { content: ""; display: block; width: 14px; @@ -928,7 +985,7 @@ table.variations { top: 7px; right: 17px; border-radius: 13em; - transition: all ease-in-out .3s; + transition: all ease-in-out 0.3s; } } @@ -936,11 +993,11 @@ table.variations { display: none; } - input[type=checkbox]:checked + span:after { + input[type=checkbox]:checked + span::after { right: 3px; } - input[type=checkbox]:checked + span:before { + input[type=checkbox]:checked + span::before { border-color: #000; background: #000; } @@ -948,10 +1005,12 @@ table.variations { } .woocommerce-no-js { + form.woocommerce-form-login, form.woocommerce-form-coupon { display: block !important; } + .woocommerce-form-login-toggle, .woocommerce-form-coupon-toggle, .showcoupon { @@ -960,37 +1019,40 @@ table.variations { } .woocommerce-terms-and-conditions { - border: 1px solid rgba(0,0,0,.2); - box-shadow: inset 0 1px 2px rgba(0,0,0,.1); - background: rgba(0,0,0,.05); + border: 1px solid rgba(0, 0, 0, 0.2); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + background: rgba(0, 0, 0, 0.05); } .woocommerce-terms-and-conditions-link { display: inline-block; - &:after { + &::after { content: ""; display: inline-block; border-style: solid; margin-bottom: 2px; - margin-left: .25em; + margin-left: 0.25em; border-width: 6px 6px 0 6px; border-color: #111 transparent transparent transparent; } - &.woocommerce-terms-and-conditions-link--open:after { + + &.woocommerce-terms-and-conditions-link--open::after { border-width: 0 6px 6px 6px; border-color: transparent transparent #111 transparent; } } .woocommerce-checkout { + .woocommerce-input-wrapper { + .description { background: royalblue; color: #fff; border-radius: 3px; padding: 1em; - margin: .5em 0 0; + margin: 0.5em 0 0; clear: both; display: none; position: relative; @@ -1002,11 +1064,11 @@ table.variations { box-shadow: none; } - &:before { + &::before { left: 50%; top: 0%; margin-top: -4px; - transform: translatex(-50%) rotate(180deg); + transform: translateX(-50%) rotate(180deg); content: ""; position: absolute; border-width: 4px 6px 0 6px; @@ -1022,26 +1084,32 @@ table.variations { .select2-choice:hover { box-shadow: none !important; } + .select2-choice { - padding: .7em 0 .7em .7em; + padding: 0.7em 0 0.7em 0.7em; } + .select2-container .select2-selection--single { height: 48px; } + .select2-container .select2-selection--single .select2-selection__rendered { line-height: 48px; } + .select2-container--default .select2-selection--single .select2-selection__arrow { height: 46px; } + .select2-container--focus .select2-selection { border-color: black; } } .woocommerce-checkout-review-order-table { + td { - padding: 1em .5em; + padding: 1em 0.5em; } dl.variation { @@ -1063,6 +1131,7 @@ table.variations { ul, ol { + &:last-of-type { margin-bottom: 0; } @@ -1102,7 +1171,8 @@ table.variations { display: none; & + label { - &:before { + + &::before { content: ""; display: inline-block; width: 16px; @@ -1111,14 +1181,15 @@ table.variations { box-shadow: 0 0 0 2px black; background: white; margin-left: 4px; - margin-right: .5em; + margin-right: 0.5em; border-radius: 100%; transform: translateY(2px); } } &:checked + label { - &:before { + + &::before { background: black; } } @@ -1126,10 +1197,12 @@ table.variations { } .colors-dark { + .page-numbers { color: #444; - &.next, &.prev { + &.next, + &.prev { color: #ddd; } } @@ -1143,17 +1216,21 @@ table.variations { } .wc_payment_method { + .payment_box { background: #333; } } .select2-container--default { + .select2-results { + .select2-results__options { - background:#333; + background: #333; } - .select2-results__option[data-selected="true"]{ + + .select2-results__option[data-selected="true"] { color: #333; } } @@ -1177,7 +1254,9 @@ table.variations { * Layout stuff */ @media screen and (min-width: 48em) { + .has-sidebar.woocommerce-page:not(.error404) { + #primary { width: 74%; } diff --git a/assets/css/woocommerce.scss b/assets/css/woocommerce.scss index 556920251db..20f5c1e02c5 100644 --- a/assets/css/woocommerce.scss +++ b/assets/css/woocommerce.scss @@ -7,10 +7,10 @@ /** * Imports */ -@import 'mixins'; -@import 'variables'; -@import 'animation'; -@import 'fonts'; +@import "mixins"; +@import "variables"; +@import "animation"; +@import "fonts"; /** * Global styles @@ -40,11 +40,11 @@ p.demo_store, .screen-reader-text { clip: rect(1px, 1px, 1px, 1px); - height: 1px; - overflow: hidden; - position: absolute !important; - width: 1px; - word-wrap: normal !important; + height: 1px; + overflow: hidden; + position: absolute !important; + width: 1px; + word-wrap: normal !important; } .admin-bar p.demo_store { @@ -62,12 +62,15 @@ p.demo_store, * Main WooCommerce styles */ .woocommerce { + .blockUI.blockOverlay { position: relative; + @include loader(); } .loader { + @include loader(); } @@ -98,6 +101,7 @@ p.demo_store, } .woocommerce-breadcrumb { + @include clearfix(); margin: 0 0 1em; padding: 0; @@ -179,7 +183,7 @@ p.demo_store, } .woocommerce-product-gallery__wrapper { - transition: all cubic-bezier(0.795, -0.035, 0.000, 1.000) .5s; + transition: all cubic-bezier(0.795, -0.035, 0, 1) 0.5s; margin: 0; padding: 0; } @@ -190,7 +194,7 @@ p.demo_store, } .woocommerce-product-gallery__image--placeholder { - border: 1px solid #F2F2F2; + border: 1px solid #f2f2f2; } .woocommerce-product-gallery__image:nth-child(n+2) { @@ -200,8 +204,8 @@ p.demo_store, .woocommerce-product-gallery__trigger { position: absolute; - top: .5em; - right: .5em; + top: 0.5em; + right: 0.5em; font-size: 2em; z-index: 9; width: 36px; @@ -211,7 +215,7 @@ p.demo_store, border-radius: 100%; box-sizing: content-box; - &:before { + &::before { content: ""; display: block; width: 10px; @@ -224,7 +228,7 @@ p.demo_store, box-sizing: content-box; } - &:after { + &::after { content: ""; display: block; width: 2px; @@ -253,7 +257,7 @@ p.demo_store, img { cursor: pointer; - opacity: .5; + opacity: 0.5; margin: 0; &.flex-active, @@ -266,18 +270,21 @@ p.demo_store, } .woocommerce-product-gallery--columns-3 { + .flex-control-thumbs li:nth-child(3n+1) { clear: left; } } .woocommerce-product-gallery--columns-4 { + .flex-control-thumbs li:nth-child(4n+1) { clear: left; } } .woocommerce-product-gallery--columns-5 { + .flex-control-thumbs li:nth-child(5n+1) { clear: left; } @@ -311,6 +318,7 @@ p.demo_store, } .woocommerce-tabs { + ul.tabs { list-style: none; padding: 0 0 0 1em; @@ -319,7 +327,7 @@ p.demo_store, position: relative; li { - border: 1px solid darken( $secondary, 10% ); + border: 1px solid darken($secondary, 10%); background-color: $secondary; display: inline-block; position: relative; @@ -337,7 +345,7 @@ p.demo_store, &:hover { text-decoration: none; - color: lighten( $secondarytext, 10% ); + color: lighten($secondarytext, 10%); } } @@ -362,12 +370,12 @@ p.demo_store, &::before, &::after { - border: 1px solid darken( $secondary, 10% ); + border: 1px solid darken($secondary, 10%); position: absolute; bottom: -1px; width: 5px; height: 5px; - content: ' '; + content: " "; box-sizing: border-box; } @@ -388,11 +396,11 @@ p.demo_store, &::before { position: absolute; - content: ' '; + content: " "; width: 100%; bottom: 0; left: 0; - border-bottom: 1px solid darken( $secondary, 10% ); + border-bottom: 1px solid darken($secondary, 10%); z-index: 1; } } @@ -405,11 +413,13 @@ p.demo_store, p.cart { margin-bottom: 2em; + @include clearfix(); } form.cart { margin-bottom: 2em; + @include clearfix(); div.quantity { @@ -482,6 +492,7 @@ p.demo_store, } .group_table { + td.woocommerce-grouped-product-list-item__label { padding-right: 1em; padding-left: 1em; @@ -502,7 +513,7 @@ p.demo_store, display: inline-block; width: auto; margin: 0 auto; - transform:scale(1.5, 1.5); + transform: scale(1.5, 1.5); } } } @@ -536,6 +547,7 @@ p.demo_store, padding: 0; list-style: none outside; clear: both; + @include clearfix(); li { @@ -544,6 +556,7 @@ p.demo_store, } ul.products li.product { + .onsale { top: 0; right: 0; @@ -576,7 +589,7 @@ p.demo_store, } .woocommerce-placeholder { - border: 1px solid #F2F2F2; + border: 1px solid #f2f2f2; } .star-rating { @@ -635,12 +648,12 @@ p.demo_store, white-space: nowrap; padding: 0; clear: both; - border: 1px solid darken( $secondary, 10% ); + border: 1px solid darken($secondary, 10%); border-right: 0; margin: 1px; li { - border-right: 1px solid darken( $secondary, 10% ); + border-right: 1px solid darken($secondary, 10%); padding: 0; margin: 0; float: left; @@ -664,7 +677,7 @@ p.demo_store, a:hover, a:focus { background: $secondary; - color: darken( $secondary, 40% ); + color: darken($secondary, 40%); } } } @@ -701,8 +714,8 @@ p.demo_store, padding-right: 2.618em; &::after { - font-family: 'WooCommerce'; - content: '\e01c'; + font-family: "WooCommerce"; + content: "\e01c"; vertical-align: top; font-weight: 400; position: absolute; @@ -713,8 +726,8 @@ p.demo_store, } &.added::after { - font-family: 'WooCommerce'; - content: '\e017'; + font-family: "WooCommerce"; + content: "\e017"; margin-left: 0.53em; vertical-align: bottom; } @@ -776,6 +789,7 @@ p.demo_store, * Reviews */ #reviews { + h2 small { float: right; color: $subtext; @@ -803,7 +817,9 @@ p.demo_store, } #comments { + .add_review { + @include clearfix(); } @@ -812,6 +828,7 @@ p.demo_store, } ol.commentlist { + @include clearfix(); margin: 0; width: 100%; @@ -840,16 +857,17 @@ p.demo_store, width: 32px; height: auto; background: $secondary; - border: 1px solid darken( $secondary, 3% ); + border: 1px solid darken($secondary, 3%); margin: 0; box-shadow: none; } .comment-text { margin: 0 0 0 50px; - border: 1px solid darken( $secondary, 3% ); + border: 1px solid darken($secondary, 3%); border-radius: 4px; padding: 1em 1em 0; + @include clearfix(); p { @@ -872,7 +890,7 @@ p.demo_store, } #respond { - border: 1px solid darken( $secondary, 3% ); + border: 1px solid darken($secondary, 3%); border-radius: 4px; padding: 1em 1em 0; margin: 20px 0 0 50px; @@ -880,7 +898,7 @@ p.demo_store, } .commentlist > li::before { - content: ''; + content: ""; } } } @@ -896,11 +914,11 @@ p.demo_store, line-height: 1; font-size: 1em; width: 5.4em; - font-family: 'star'; + font-family: "star"; &::before { - content: '\73\73\73\73\73'; - color: darken( $secondary, 10% ); + content: "\73\73\73\73\73"; + color: darken($secondary, 10%); float: left; top: 0; left: 0; @@ -917,7 +935,7 @@ p.demo_store, } span::before { - content: '\53\53\53\53\53'; + content: "\53\53\53\53\53"; top: 0; position: absolute; left: 0; @@ -925,6 +943,7 @@ p.demo_store, } .woocommerce-product-rating { + @include clearfix(); line-height: 2; display: block; @@ -946,6 +965,7 @@ p.demo_store, } #review_form #respond { + @include clearfix(); position: static; margin: 0; @@ -969,6 +989,7 @@ p.demo_store, } p.stars { + a { position: relative; height: 1em; @@ -985,33 +1006,35 @@ p.demo_store, width: 1em; height: 1em; line-height: 1; - font-family: 'WooCommerce'; - content: '\e021'; + font-family: "WooCommerce"; + content: "\e021"; text-indent: 0; } &:hover ~ a::before { - content: '\e021'; + content: "\e021"; } } &:hover a::before { - content: '\e020'; + content: "\e020"; } &.selected { + a.active { + &::before { - content: '\e020'; + content: "\e020"; } ~ a::before { - content: '\e021'; + content: "\e021"; } } - a:not( .active )::before { - content: '\e020'; + a:not(.active)::before { + content: "\e020"; } } } @@ -1081,6 +1104,7 @@ p.demo_store, } tbody:first-child tr:first-child { + th, td { border-top: 0; @@ -1118,6 +1142,7 @@ p.demo_store, } table.woocommerce-MyAccount-downloads { + td, th { vertical-align: top; @@ -1126,24 +1151,29 @@ p.demo_store, &:first-child { text-align: left; } + &:last-child { text-align: left; } + .woocommerce-MyAccount-downloads-file::before { - content: '\2193'; + content: "\2193"; display: inline-block; } } } td.product-name { - dl.variation, .wc-item-meta { + + dl.variation, + .wc-item-meta { list-style: none outside; - dt, .wc-item-meta-label { + dt, + .wc-item-meta-label { float: left; clear: both; - margin-right: .25em; + margin-right: 0.25em; display: inline-block; list-style: none outside; } @@ -1179,6 +1209,7 @@ p.demo_store, li { padding: 4px 0; margin: 0; + @include clearfix(); list-style: none; @@ -1199,6 +1230,7 @@ p.demo_store, margin: 0; padding-left: 1em; border-left: 2px solid rgba(0, 0, 0, 0.1); + @include clearfix(); dt, @@ -1232,6 +1264,7 @@ p.demo_store, &.widget_shopping_cart, .widget_shopping_cart { + .total { border-top: 3px double $secondary; padding: 4px 0 0; @@ -1255,7 +1288,9 @@ p.demo_store, } .buttons { + @include clearfix(); + a { margin-right: 5px; margin-bottom: 5px; @@ -1288,12 +1323,13 @@ p.demo_store, } .woocommerce-input-wrapper { + .description { background: #1e85be; color: #fff; border-radius: 3px; padding: 1em; - margin: .5em 0 0; + margin: 0.5em 0 0; clear: both; display: none; position: relative; @@ -1305,11 +1341,11 @@ p.demo_store, box-shadow: none; } - &:before { + &::before { left: 50%; top: 0%; margin-top: -4px; - transform: translatex(-50%) rotate(180deg); + transform: translateX(-50%) rotate(180deg); content: ""; position: absolute; border-width: 4px 6px 0 6px; @@ -1336,7 +1372,7 @@ p.demo_store, .optional { visibility: visible; - } + } .input-checkbox { display: inline; @@ -1367,9 +1403,11 @@ p.demo_store, } &.woocommerce-invalid { + label { color: $red; } + .select2-container, input.input-text, select { @@ -1378,6 +1416,7 @@ p.demo_store, } &.woocommerce-validated { + .select2-container, input.input-text, select { @@ -1401,7 +1440,7 @@ p.demo_store, form.login, form.checkout_coupon, form.register { - border: 1px solid darken( $secondary, 10% ); + border: 1px solid darken($secondary, 10%); padding: 20px; margin: 2em 0; text-align: left; @@ -1414,13 +1453,15 @@ p.demo_store, padding: 0; li { - margin: 0 0 .5em; + margin: 0 0 0.5em; line-height: 1.5em; list-style: none outside; + input { margin: 3px 0.4375em 0 0; vertical-align: top; } + label { display: inline; } @@ -1439,6 +1480,7 @@ p.demo_store, * Order page */ ul.order_details { + @include clearfix(); margin: 0 0 3em; list-style: none; @@ -1449,7 +1491,7 @@ p.demo_store, text-transform: uppercase; font-size: 0.715em; line-height: 1; - border-right: 1px dashed darken( $secondary, 10% ); + border-right: 1px dashed darken($secondary, 10%); padding-right: 2em; margin-left: 0; padding-left: 0; @@ -1481,7 +1523,9 @@ p.demo_store, margin-bottom: 0; } } + .woocommerce-customer-details { + address { font-style: normal; margin-bottom: 0; @@ -1493,19 +1537,24 @@ p.demo_store, border-radius: 5px; padding: 6px 12px; } + .woocommerce-customer-details--phone, .woocommerce-customer-details--email { margin-bottom: 0; padding-left: 1.5em; } + .woocommerce-customer-details--phone::before { - @include iconbefore( '\e037' ); + + @include iconbefore( "\e037" ); margin-left: -1.5em; line-height: 1.75; position: absolute; } + .woocommerce-customer-details--email::before { - @include iconbefore( '\e02d' ); + + @include iconbefore( "\e02d" ); margin-left: -1.5em; line-height: 1.75; position: absolute; @@ -1522,6 +1571,7 @@ p.demo_store, list-style: none outside; .woocommerce-widget-layered-nav-list__item { + @include clearfix(); padding: 0 0 1px; list-style: none; @@ -1531,11 +1581,14 @@ p.demo_store, padding: 1px 0; } } + .woocommerce-widget-layered-nav-list__item--chosen a::before { - @include iconbefore( '\e013' ); + + @include iconbefore( "\e013" ); color: $red; } } + .woocommerce-widget-layered-nav-dropdown__submit { margin-top: 1em; } @@ -1557,10 +1610,11 @@ p.demo_store, text-decoration: none; &::before { - @include iconbefore( '\e013' ); + + @include iconbefore( "\e013" ); color: $red; vertical-align: inherit; - margin-right: .5em; + margin-right: 0.5em; } } } @@ -1570,6 +1624,7 @@ p.demo_store, * Price filter widget */ .widget_price_filter { + .price_slider { margin-bottom: 1em; } @@ -1602,6 +1657,7 @@ p.demo_store, cursor: ew-resize; outline: none; top: -0.3em; + /* rtl:ignore */ margin-left: -0.5em; } @@ -1650,6 +1706,7 @@ p.demo_store, list-style: none outside; li { + @include clearfix(); padding: 0 0 1px; list-style: none; @@ -1666,16 +1723,19 @@ p.demo_store, } li.chosen a::before { - @include iconbefore( '\e013' ); + + @include iconbefore( "\e013" ); color: $red; } } .woocommerce-form-login { + .woocommerce-form-login__submit { float: left; margin-right: 1em; } + .woocommerce-form-login__rememberme { display: inline-block; } @@ -1683,10 +1743,12 @@ p.demo_store, } .woocommerce-no-js { + form.woocommerce-form-login, form.woocommerce-form-coupon { display: block !important; } + .woocommerce-form-login-toggle, .woocommerce-form-coupon-toggle, .showcoupon { @@ -1704,13 +1766,14 @@ p.demo_store, color: $secondarytext; border-top: 3px solid $primary; list-style: none outside; + @include clearfix(); width: auto; word-wrap: break-word; &::before { - font-family: 'WooCommerce'; - content: '\e028'; + font-family: "WooCommerce"; + content: "\e028"; display: inline-block; position: absolute; top: 1em; @@ -1731,8 +1794,9 @@ p.demo_store, /** * Right to left styles */ - .rtl.woocommerce .price_label, - .rtl.woocommerce .price_label span { +.rtl.woocommerce .price_label, +.rtl.woocommerce .price_label span { + /* rtl:ignore */ direction: ltr; unicode-bidi: embed; @@ -1742,7 +1806,7 @@ p.demo_store, border-top-color: #8fae1b; &::before { - content: '\e015'; + content: "\e015"; color: #8fae1b; } } @@ -1759,7 +1823,7 @@ p.demo_store, border-top-color: #b81c23; &::before { - content: '\e016'; + content: "\e016"; color: #b81c23; } } @@ -1768,11 +1832,14 @@ p.demo_store, * Account page */ .woocommerce-account { + .woocommerce { + @include clearfix(); } .addresses .title { + @include clearfix(); h3 { @@ -1785,6 +1852,7 @@ p.demo_store, } ol.commentlist.notes li.note { + p.meta { font-weight: 700; margin-bottom: 0; @@ -1794,6 +1862,7 @@ p.demo_store, margin-bottom: 0; } } + ul.digital-downloads { margin-left: 0; padding-left: 0; @@ -1804,7 +1873,8 @@ p.demo_store, padding-left: 0; &::before { - @include iconbefore( '\e00a' ); + + @include iconbefore( "\e00a" ); } .count { @@ -1820,7 +1890,9 @@ p.demo_store, .woocommerce-cart, .woocommerce-checkout, #add_payment_method { + table.cart { + .product-thumbnail { min-width: 32px; } @@ -1838,7 +1910,7 @@ p.demo_store, td.actions .coupon .input-text { float: left; box-sizing: border-box; - border: 1px solid darken( $secondary, 10% ); + border: 1px solid darken($secondary, 10%); padding: 6px 6px 5px; margin: 0 4px 0 0; outline: 0; @@ -1851,6 +1923,7 @@ p.demo_store, } .wc-proceed-to-checkout { + @include clearfix; padding: 1em 0; @@ -1864,14 +1937,16 @@ p.demo_store, } .cart-collaterals { + .shipping-calculator-button { float: none; - margin-top: .5em; + margin-top: 0.5em; display: inline-block; } .shipping-calculator-button::after { - @include iconafter( '\e019' ); + + @include iconafter( "\e019" ); } .shipping-calculator-form { @@ -1879,6 +1954,7 @@ p.demo_store, } .cart_totals { + p small { color: $subtext; font-size: 0.83em; @@ -1890,6 +1966,7 @@ p.demo_store, padding: 0; tr:first-child { + th, td { border-top: 0; @@ -1925,6 +2002,7 @@ p.demo_store, tr th { border-top: 1px solid $secondary; } + .woocommerce-shipping-destination { margin-bottom: 0; } @@ -1934,8 +2012,11 @@ p.demo_store, margin-top: 0; } } + .checkout { + .col-2 { + h3#ship-to-different-address { float: left; clear: none; @@ -1972,10 +2053,11 @@ p.demo_store, border-radius: 5px; ul.payment_methods { + @include clearfix(); text-align: left; padding: 1em; - border-bottom: 1px solid darken( $secondary, 10% ); + border-bottom: 1px solid darken($secondary, 10%); margin: 0; list-style: none outside; @@ -2003,6 +2085,7 @@ p.demo_store, } li:not(.woocommerce-notice) { + @include clearfix; } } @@ -2020,36 +2103,40 @@ p.demo_store, font-size: 0.92em; border-radius: 2px; line-height: 1.5; - background-color: darken( $secondary, 5% ); + background-color: darken($secondary, 5%); color: $secondarytext; - input.input-text, textarea { - border-color: darken( $secondary, 15% ); - border-top-color: darken( $secondary, 20% ); + input.input-text, + textarea { + border-color: darken($secondary, 15%); + border-top-color: darken($secondary, 20%); } ::-webkit-input-placeholder { - color: darken( $secondary, 20% ); + color: darken($secondary, 20%); } :-moz-placeholder { - color: darken( $secondary, 20% ); + color: darken($secondary, 20%); } :-ms-input-placeholder { - color: darken( $secondary, 20% ); + color: darken($secondary, 20%); } .woocommerce-SavedPaymentMethods { list-style: none outside; margin: 0; + .woocommerce-SavedPaymentMethods-token, .woocommerce-SavedPaymentMethods-new { margin: 0 0 0.5em; + label { cursor: pointer; } } + .woocommerce-SavedPaymentMethods-tokenInput { vertical-align: middle; margin: -3px 1em 0 0; @@ -2062,6 +2149,7 @@ p.demo_store, padding: 0; margin: 1em 0 0; } + .wc-credit-card-form-card-number, .wc-credit-card-form-card-expiry, .wc-credit-card-form-card-cvc { @@ -2072,34 +2160,35 @@ p.demo_store, background-size: 32px 20px; &.visa { - background-image: url('../images/icons/credit-cards/visa.svg'); + background-image: url("../images/icons/credit-cards/visa.svg"); } &.mastercard { - background-image: url('../images/icons/credit-cards/mastercard.svg'); + background-image: url("../images/icons/credit-cards/mastercard.svg"); } &.laser { - background-image: url('../images/icons/credit-cards/laser.svg'); + background-image: url("../images/icons/credit-cards/laser.svg"); } &.dinersclub { - background-image: url('../images/icons/credit-cards/diners.svg'); + background-image: url("../images/icons/credit-cards/diners.svg"); } &.maestro { - background-image: url('../images/icons/credit-cards/maestro.svg'); + background-image: url("../images/icons/credit-cards/maestro.svg"); } &.jcb { - background-image: url('../images/icons/credit-cards/jcb.svg'); + background-image: url("../images/icons/credit-cards/jcb.svg"); } &.amex { - background-image: url('../images/icons/credit-cards/amex.svg'); + background-image: url("../images/icons/credit-cards/amex.svg"); } + &.discover { - background-image: url('../images/icons/credit-cards/discover.svg'); + background-image: url("../images/icons/credit-cards/discover.svg"); } } @@ -2118,9 +2207,9 @@ p.demo_store, } &::before { - content: ''; + content: ""; display: block; - border: 1em solid darken( $secondary, 5% ); /* arrow size / color */ + border: 1em solid darken($secondary, 5%); /* arrow size / color */ border-right-color: transparent; border-left-color: transparent; border-top-color: transparent; @@ -2132,6 +2221,7 @@ p.demo_store, } .payment_method_paypal { + .about_paypal { float: right; line-height: 52px; @@ -2147,12 +2237,13 @@ p.demo_store, } .woocommerce-terms-and-conditions { - border: 1px solid rgba(0,0,0,.2); - box-shadow: inset 0 1px 2px rgba(0,0,0,.1); - background: rgba(0,0,0,.05); + border: 1px solid rgba(0, 0, 0, 0.2); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + background: rgba(0, 0, 0, 0.05); } .woocommerce-invalid { + #terms { outline: 2px solid red; outline-offset: 2px; @@ -2206,6 +2297,7 @@ p.demo_store, * Twenty Thirteen specific styles */ .single-product .twentythirteen { + .entry-summary, #reply-title, #respond #commentform { @@ -2231,7 +2323,7 @@ p.demo_store, /** * Twenty Sixteen specific styles */ -body:not( .search-results ) .twentysixteen .entry-summary { +body:not(.search-results) .twentysixteen .entry-summary { color: inherit; font-size: inherit; line-height: inherit; From 387b20480c9c6ce61dc230be055d07b7f1abedee Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 15:06:35 +0000 Subject: [PATCH 295/401] Update from master --- assets/css/activation.scss | 4 +- assets/css/admin.scss | 122 +++++++++++++++---------------- assets/css/twenty-seventeen.scss | 8 +- assets/css/woocommerce.scss | 6 +- 4 files changed, 70 insertions(+), 70 deletions(-) diff --git a/assets/css/activation.scss b/assets/css/activation.scss index 2674c9589ad..e21c839aa7c 100644 --- a/assets/css/activation.scss +++ b/assets/css/activation.scss @@ -23,8 +23,8 @@ p.woocommerce-actions, text-shadow: 0 -1px 1px #a36597, 1px 0 1px #a36597, 0 1px 1px #a36597, -1px 0 1px #a36597; &:hover, - &:focus, - &:active { + &:focus, + &:active { background: #a36597; border-color: #a36597; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; diff --git a/assets/css/admin.scss b/assets/css/admin.scss index 7f78b2cd538..dbafd787f45 100644 --- a/assets/css/admin.scss +++ b/assets/css/admin.scss @@ -475,7 +475,7 @@ } h2, - h3 { + h3 { margin: 0 !important; padding: 20px !important; background: #fff; @@ -530,8 +530,8 @@ display: inline-block; &:hover, - &:focus, - &:active { + &:focus, + &:active { background: #a36597; border-color: #a36597; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; @@ -574,7 +574,7 @@ } #variable_product_options #message, - #variable_product_options .notice { +#variable_product_options .notice { margin: 10px; } @@ -648,7 +648,7 @@ table.wc_status_table { } td, - th { + th { font-size: 1.1em; font-weight: normal; @@ -674,7 +674,7 @@ table.wc_status_table { } mark.error, - .red { + .red { color: $red; } @@ -691,7 +691,7 @@ table.wc_status_table { table.wc_status_table--tools { td, - th { + th { padding: 2em; } } @@ -868,8 +868,8 @@ table.wc_status_table--tools { } .length, - .width, - .height { + .width, + .height { width: 32.33%; } @@ -1055,7 +1055,7 @@ ul.wc_coupon_list_block { } h3, - h4 { + h4 { color: #333; margin: 1.33em 0 0; } @@ -1180,7 +1180,7 @@ ul.wc_coupon_list_block { } .wc-customer-user, - .wc-order-status { + .wc-order-status { label a { float: right; @@ -1200,7 +1200,7 @@ ul.wc_coupon_list_block { float: right; &:hover, - &:focus { + &:focus { color: #000; } @@ -1416,7 +1416,7 @@ ul.wc_coupon_list_block { } .hndle, - .handlediv { + .handlediv { display: none; } @@ -1461,7 +1461,7 @@ ul.wc_coupon_list_block { } tbody th, - td { + td { padding: 1.5em 1em 1em; text-align: left; line-height: 1.5em; @@ -1477,7 +1477,7 @@ ul.wc_coupon_list_block { } input, - textarea { + textarea { font-size: 14px; padding: 4px; color: #555; @@ -1541,7 +1541,7 @@ ul.wc_coupon_list_block { td.name { .wc-order-item-sku, - .wc-order-item-variation { + .wc-order-item-variation { display: block; margin-top: 0.5em; font-size: 0.92em !important; @@ -1651,16 +1651,16 @@ ul.wc_coupon_list_block { } small.times, - del, - .wc-order-item-taxes, - .wc-order-item-discount, - .wc-order-item-refund-fields { + del, + .wc-order-item-taxes, + .wc-order-item-discount, + .wc-order-item-refund-fields { font-size: 0.92em !important; color: #888; } .wc-order-item-taxes, - .wc-order-item-refund-fields { + .wc-order-item-refund-fields { margin: 0; label { @@ -2026,8 +2026,8 @@ ul.wc_coupon_list_block { } .column-orders, - .column-paying, - .column-spent { + .column-paying, + .column-spent { text-align: center; width: 8%; } @@ -2127,7 +2127,7 @@ ul.wc_coupon_list_block { } th.sortable a, - th.sorted a { + th.sorted a { padding: 0; } @@ -2333,7 +2333,7 @@ ul.wc_coupon_list_block { margin: 0; th, - td { + td { padding: 1em 1.5em; text-align: left; border: 0; @@ -2366,7 +2366,7 @@ ul.wc_coupon_list_block { margin-top: 0.5em; th, - td { + td { padding: 0; border: 0; text-align: left; @@ -2924,7 +2924,7 @@ table.wc_input_table { width: 100%; th, - td { + td { display: table-cell !important; } @@ -3064,7 +3064,7 @@ table.wc_shipping { position: relative; th, - td { + td { display: table-cell !important; padding: 1em !important; vertical-align: top; @@ -3216,7 +3216,7 @@ table.wc_shipping { td.forminp { input, - textarea { + textarea { padding: 8px; max-width: 100% !important; } @@ -3273,7 +3273,7 @@ table.wc_shipping { table { tr, - tr:hover { + tr:hover { table.wc-shipping-zone-methods { @@ -3293,11 +3293,11 @@ table { } table.wc-shipping-zones, - table.wc-shipping-zone-methods, - table.wc-shipping-classes { +table.wc-shipping-zone-methods, +table.wc-shipping-classes { td, - th { + th { vertical-align: top; line-height: 24px; padding: 1em !important; @@ -3327,7 +3327,7 @@ table.wc-shipping-zones, } td.wc-shipping-zones-blank-state, - td.wc-shipping-zone-method-blank-state { + td.wc-shipping-zone-method-blank-state { background: #f7f1f6 !important; overflow: hidden; position: relative; @@ -3343,7 +3343,7 @@ table.wc-shipping-zones, } p, - li { + li { color: #a46497; font-size: 1.5em; line-height: 1.5em; @@ -3400,7 +3400,7 @@ table.wc-shipping-zones, } tr.odd, - .wc-shipping-class-rows tr:nth-child(odd) { + .wc-shipping-class-rows tr:nth-child(odd) { td { background: #f9f9f9; @@ -3430,12 +3430,12 @@ table.wc-shipping-zones, } ul, - p { + p { margin: 0; } td.wc-shipping-zone-sort, - td.wc-shipping-zone-method-sort { + td.wc-shipping-zone-method-sort { cursor: move; font-size: 15px; text-align: center; @@ -3487,18 +3487,18 @@ table.wc-shipping-zones, .wc-shipping-zone-region { input, - select, - textarea { + select, + textarea { width: 100%; } a.wc-shipping-zone-delete, - a.wc-shipping-class-delete { + a.wc-shipping-class-delete { color: #a00; } a.wc-shipping-zone-delete:hover, - a.wc-shipping-class-delete:hover { + a.wc-shipping-class-delete:hover { color: red; } } @@ -3590,7 +3590,7 @@ table.wc-shipping-zones, tfoot { input, - select { + select { vertical-align: middle !important; } @@ -3602,7 +3602,7 @@ table.wc-shipping-zones, .editing { .wc-shipping-zone-view, - .wc-shipping-zone-edit { + .wc-shipping-zone-edit { display: none; } } @@ -3675,8 +3675,8 @@ table.wc-shipping-zones, td { input, - select, - textarea { + select, + textarea { width: 50%; min-width: 250px; } @@ -3688,7 +3688,7 @@ table.wc-shipping-zones, } td, - th { + th { vertical-align: middle; margin: 0; line-height: 24px; @@ -4013,7 +4013,7 @@ img.help_tip { td.forminp { input, - textarea { + textarea { width: 448px; padding: 6px 11px; } @@ -4190,7 +4190,7 @@ img.help_tip { } input, - select { + select { margin-top: -3px 0 0; vertical-align: middle; } @@ -4386,7 +4386,7 @@ img.help_tip { .woocommerce_page_wc-settings { input[type=url], - input[type=email] { + input[type=email] { direction: ltr; } @@ -4591,7 +4591,7 @@ img.help_tip { } h3:hover, - &.ui-sortable-helper { + &.ui-sortable-helper { .sort { visibility: visible; @@ -4911,7 +4911,7 @@ img.help_tip { cursor: move; button, - a.delete { + a.delete { float: right; } @@ -4952,17 +4952,17 @@ img.help_tip { padding: 0.5em 0.75em 0.5em 1em !important; a.delete, - .handlediv, - .sort { + .handlediv, + .sort { margin-top: 0.25em; } } h3:hover, - &.ui-sortable-helper { + &.ui-sortable-helper { a.delete, - .handlediv { + .handlediv { visibility: visible; } } @@ -6346,7 +6346,7 @@ table.bar_chart { } tbody td, - tbody th { + tbody th { padding: 1em; text-align: left; vertical-align: middle; @@ -6402,7 +6402,7 @@ table.bar_chart { line-height: 1.5em; .select2-results__option, - .select2-results__group { + .select2-results__group { margin: 0; padding: 8px; } @@ -6595,8 +6595,8 @@ table.bar_chart { opacity: 1; &:hover, - &:focus, - &:active { + &:focus, + &:active { background: #a36597; border-color: #a36597; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; @@ -6810,7 +6810,7 @@ table.bar_chart { table-layout: fixed; td, - th { + th { border: 0; padding: 12px; vertical-align: middle; diff --git a/assets/css/twenty-seventeen.scss b/assets/css/twenty-seventeen.scss index a736eb31b93..55e386e10e3 100644 --- a/assets/css/twenty-seventeen.scss +++ b/assets/css/twenty-seventeen.scss @@ -335,11 +335,11 @@ a.remove { } dl.variation, - .wc-item-meta { +.wc-item-meta { list-style: none outside; dt, - .wc-item-meta-label { + .wc-item-meta-label { float: left; clear: both; margin-right: 0.25em; @@ -887,7 +887,7 @@ table.variations { .cart_totals { th, - td { + td { vertical-align: top; padding: 1em 0; line-height: 1.5em; @@ -1202,7 +1202,7 @@ table.variations { color: #444; &.next, - &.prev { + &.prev { color: #ddd; } } diff --git a/assets/css/woocommerce.scss b/assets/css/woocommerce.scss index 20f5c1e02c5..b34562dffb8 100644 --- a/assets/css/woocommerce.scss +++ b/assets/css/woocommerce.scss @@ -1166,11 +1166,11 @@ p.demo_store, td.product-name { dl.variation, - .wc-item-meta { + .wc-item-meta { list-style: none outside; dt, - .wc-item-meta-label { + .wc-item-meta-label { float: left; clear: both; margin-right: 0.25em; @@ -2107,7 +2107,7 @@ p.demo_store, color: $secondarytext; input.input-text, - textarea { + textarea { border-color: darken($secondary, 15%); border-top-color: darken($secondary, 20%); } From 3ededc2a07f2927ba373b3d293f99f998b5944e1 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 16:29:29 +0000 Subject: [PATCH 296/401] Move BN partner ID --- .../includes/class-wc-gateway-paypal-request.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php index 2d239ca4590..eadb2d4dc58 100644 --- a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php +++ b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php @@ -61,9 +61,12 @@ class WC_Gateway_Paypal_Request { * @return string */ public function get_request_url( $order, $sandbox = false ) { - $this->endpoint = $sandbox ? 'https://www.sandbox.paypal.com/cgi-bin/webscr?test_ipn=1&' : 'https://www.paypal.com/cgi-bin/webscr?'; - $paypal_args = $this->get_paypal_args( $order ); - $mask = array( + $this->endpoint = $sandbox ? 'https://www.sandbox.paypal.com/cgi-bin/webscr?test_ipn=1&' : 'https://www.paypal.com/cgi-bin/webscr?'; + $paypal_args = $this->get_paypal_args( $order ); + $paypal_args['bn'] = 'WooThemes_Cart'; // Append WooCommerce PayPal Partner Attribution ID. This should not be overridden for this gateway. + + // Mask (remove) PII from the logs. + $mask = array( 'first_name' => '***', 'last_name' => '***', 'address1' => '***', @@ -122,7 +125,6 @@ class WC_Gateway_Paypal_Request { 'page_style' => $this->gateway->get_option( 'page_style' ), 'image_url' => esc_url_raw( $this->gateway->get_option( 'image_url' ) ), 'paymentaction' => $this->gateway->get_option( 'paymentaction' ), - 'bn' => 'WooThemes_Cart', 'invoice' => $this->limit_length( $this->gateway->get_option( 'invoice_prefix' ) . $order->get_order_number(), 127 ), 'custom' => wp_json_encode( array( From ad12ddd2c2659156d50c52c695a10db8f14741a6 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 16:45:55 +0000 Subject: [PATCH 297/401] wc_maybe_adjust_line_item_product_stock should return false if nothing happens --- includes/admin/wc-admin-functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/admin/wc-admin-functions.php b/includes/admin/wc-admin-functions.php index 724ed230f65..04bdb1078d4 100644 --- a/includes/admin/wc-admin-functions.php +++ b/includes/admin/wc-admin-functions.php @@ -190,7 +190,7 @@ function woocommerce_settings_get_option( $option_name, $default = '' ) { */ function wc_maybe_adjust_line_item_product_stock( $item, $item_quantity = -1 ) { if ( 'line_item' !== $item->get_type() ) { - return; + return false; } $product = $item->get_product(); @@ -198,7 +198,7 @@ function wc_maybe_adjust_line_item_product_stock( $item, $item_quantity = -1 ) { $already_reduced_stock = wc_stock_amount( $item->get_meta( '_reduced_stock', true ) ); if ( ! $product || ! $product->managing_stock() || ! $already_reduced_stock || $item_quantity === $already_reduced_stock ) { - return; + return false; } $diff = $item_quantity - $already_reduced_stock; From dc883012b82a6c3d749856aa621c07a0ce567bfe Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 18 Feb 2019 16:58:16 +0000 Subject: [PATCH 298/401] Missed null check in wc_load_webhooks --- includes/wc-webhook-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-webhook-functions.php b/includes/wc-webhook-functions.php index 88150e391b5..4845889f952 100644 --- a/includes/wc-webhook-functions.php +++ b/includes/wc-webhook-functions.php @@ -143,7 +143,7 @@ function wc_load_webhooks( $status = '', $limit = null ) { $webhook->enqueue(); $loaded ++; - if ( $loaded >= $limit ) { + if ( ! is_null( $limit ) && $loaded >= $limit ) { break; } } From 0b92e1abdfbb4abc08fa906b72c39311f856f664 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 18 Feb 2019 17:43:23 +0000 Subject: [PATCH 299/401] Update dependency eslint to v5.14.1 --- package-lock.json | 21 +++------------------ package.json | 2 +- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6cd8656c146..626f682df07 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3095,9 +3095,9 @@ "dev": true }, "eslint": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.14.0.tgz", - "integrity": "sha512-jrOhiYyENRrRnWlMYANlGZTqb89r2FuRT+615AabBoajhNjeh9ywDNlh2LU9vTqf0WYN+L3xdXuIi7xuj/tK9w==", + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.14.1.tgz", + "integrity": "sha512-CyUMbmsjxedx8B0mr79mNOqetvkbij/zrXnFeK2zc3pGRn3/tibjiNAv/3UxFEyfMDjh+ZqTrJrEGBFiGfD5Og==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -5357,12 +5357,6 @@ "through": "^2.3.6" }, "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -5375,15 +5369,6 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", diff --git a/package.json b/package.json index a83c2668676..36b9f5a5d12 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "chromedriver": "2.46.0", "config": "3.0.1", "cross-env": "5.2.0", - "eslint": "5.14.0", + "eslint": "5.14.1", "eslint-config-wpcalypso": "4.0.1", "eslint-plugin-wpcalypso": "4.0.2", "grunt": "1.0.3", From ba12cf723c5cdf09dee539ed30f1935be125dd67 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 18 Feb 2019 18:54:56 +0100 Subject: [PATCH 300/401] Renamed filter and reused the function in auth code. --- includes/api/class-wc-rest-authentication.php | 9 +-------- includes/class-woocommerce.php | 11 +++++------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/includes/api/class-wc-rest-authentication.php b/includes/api/class-wc-rest-authentication.php index 7e620354e94..a17f6eac6e6 100644 --- a/includes/api/class-wc-rest-authentication.php +++ b/includes/api/class-wc-rest-authentication.php @@ -50,19 +50,12 @@ class WC_REST_Authentication { * @return bool */ protected function is_request_to_rest_api() { - if ( empty( $_SERVER['REQUEST_URI'] ) ) { - return false; - } - $rest_prefix = trailingslashit( rest_get_url_prefix() ); - // Check if our endpoint. - $woocommerce = ( false !== strpos( $_SERVER['REQUEST_URI'], $rest_prefix . 'wc/' ) ); // @codingStandardsIgnoreLine - // Allow third party plugins use our authentication methods. $third_party = ( false !== strpos( $_SERVER['REQUEST_URI'], $rest_prefix . 'wc-' ) ); // @codingStandardsIgnoreLine - return apply_filters( 'woocommerce_rest_is_request_to_rest_api', $woocommerce || $third_party ); + return apply_filters( 'woocommerce_rest_is_request_to_rest_api', WC()->is_rest_api_request() || $third_party ); } /** diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php index 209babc249b..d3517137fda 100644 --- a/includes/class-woocommerce.php +++ b/includes/class-woocommerce.php @@ -236,19 +236,18 @@ final class WooCommerce { * * @return bool */ - private static function is_rest_api_request() { - $request_uri = $_SERVER['REQUEST_URI']; // @codingStandardsIgnoreLine - if ( empty( $request_uri ) ) { - return false; + public function is_rest_api_request() { + if ( empty( $_SERVER['REQUEST_URI'] ) ) { + return apply_filters( 'woocommerce_is_rest_api_request', false ); } // REST API prefix. $rest_prefix = trailingslashit( rest_get_url_prefix() ); // Check if this is a WC endpoint. - $is_woocommerce_endpoint = ( false !== strpos( $request_uri, $rest_prefix . 'wc/' ) ); + $is_woocommerce_endpoint = ( false !== strpos( $_SERVER['REQUEST_URI'], $rest_prefix . 'wc/' ) ); // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - return apply_filters( 'woocommerce_is_request_to_nonlegacy_rest_api', $is_woocommerce_endpoint ); + return apply_filters( 'woocommerce_is_rest_api_request', $is_woocommerce_endpoint ); } /** From 42855592e446c201f893b35455cc60dbee9fe5b0 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 18 Feb 2019 18:59:37 +0100 Subject: [PATCH 301/401] Updated call to reflect function change. --- includes/class-woocommerce.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php index d3517137fda..54309404c59 100644 --- a/includes/class-woocommerce.php +++ b/includes/class-woocommerce.php @@ -265,7 +265,7 @@ final class WooCommerce { case 'cron': return defined( 'DOING_CRON' ); case 'frontend': - return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' ) && ! self::is_rest_api_request(); + return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' ) && ! $this->is_rest_api_request(); } } From 02ee0f21bb9243b931e8a55675d0c534ebf74a2b Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 18 Feb 2019 16:48:01 -0300 Subject: [PATCH 302/401] Fixed coding standards --- includes/interfaces/class-wc-webhooks-data-store-interface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/interfaces/class-wc-webhooks-data-store-interface.php b/includes/interfaces/class-wc-webhooks-data-store-interface.php index d80c5f5b937..3d4839de9f0 100644 --- a/includes/interfaces/class-wc-webhooks-data-store-interface.php +++ b/includes/interfaces/class-wc-webhooks-data-store-interface.php @@ -29,7 +29,7 @@ interface WC_Webhook_Data_Store_Interface { * * @since 3.2.0 * @throws InvalidArgumentException If a $status value is passed in that is not in the known wc_get_webhook_statuses() keys. - * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.6.0. + * @param string $status Optional - status to filter results by. Must be a key in return value of @see wc_get_webhook_statuses(). @since 3.6.0. * @return int[] */ public function get_webhooks_ids( $status = '' ); From ff614f4f99c92cf8a24928bcc5a9383131620502 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 18 Feb 2019 17:25:25 -0300 Subject: [PATCH 303/401] Catch WC_Data_Exception to avoid black screen Also incldued a check to avoid duplicated messages about invalid billing address emails. --- includes/class-wc-form-handler.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/includes/class-wc-form-handler.php b/includes/class-wc-form-handler.php index 5f1bc20c35a..a22322ab2d8 100644 --- a/includes/class-wc-form-handler.php +++ b/includes/class-wc-form-handler.php @@ -149,11 +149,18 @@ class WC_Form_Handler { } } - // Set prop in customer object. - if ( is_callable( array( $customer, "set_$key" ) ) ) { - $customer->{"set_$key"}( $value ); - } else { - $customer->update_meta_data( $key, $value ); + try { + // Set prop in customer object. + if ( is_callable( array( $customer, "set_$key" ) ) ) { + $customer->{"set_$key"}( $value ); + } else { + $customer->update_meta_data( $key, $value ); + } + } catch ( WC_Data_Exception $e ) { + // Set notices. Ignore invalid billing email, since is already validated. + if ( 'customer_invalid_billing_email' !== $e->getErrorCode() ) { + wc_add_notice( $e->getMessage(), 'error' ); + } } } From c01e334500fe9720407f2d60f79ad165c3d59457 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 18 Feb 2019 17:59:34 -0300 Subject: [PATCH 304/401] Fixed coding standards --- tests/unit-tests/webhooks/crud.php | 43 ++++++++++++++++-------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/tests/unit-tests/webhooks/crud.php b/tests/unit-tests/webhooks/crud.php index 7e7c733e15f..4c8675c560e 100644 --- a/tests/unit-tests/webhooks/crud.php +++ b/tests/unit-tests/webhooks/crud.php @@ -1,15 +1,18 @@ save(); $this->assertEquals( $id, $object->get_id() ); @@ -19,7 +22,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_data */ - function test_get_data() { + public function test_get_data() { $object = new WC_Webhook(); $this->assertInternalType( 'array', $object->get_data() ); } @@ -27,7 +30,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_name */ - function test_get_name() { + public function test_get_name() { $object = new WC_Webhook(); $expected = 'test'; $object->set_name( $expected ); @@ -37,7 +40,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_date_created */ - function test_get_date_created() { + public function test_get_date_created() { $object = new WC_Webhook(); $object->set_date_created( '2016-12-12' ); $this->assertEquals( '1481500800', $object->get_date_created()->getOffsetTimestamp() ); @@ -49,7 +52,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_date_modified */ - function test_get_date_modified() { + public function test_get_date_modified() { $object = new WC_Webhook(); $object->set_date_modified( '2016-12-12' ); $this->assertEquals( '1481500800', $object->get_date_modified()->getOffsetTimestamp() ); @@ -61,8 +64,8 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_status */ - function test_get_status() { - $object = new WC_Webhook(); + public function test_get_status() { + $object = new WC_Webhook(); $this->assertEquals( 'disabled', $object->get_status() ); $expected = 'active'; @@ -73,7 +76,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_secret */ - function test_get_secret() { + public function test_get_secret() { $object = new WC_Webhook(); $expected = 'secret'; $object->set_secret( $expected ); @@ -83,7 +86,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_topic */ - function test_get_topic() { + public function test_get_topic() { $object = new WC_Webhook(); $expected = 'order.created'; $object->set_topic( $expected ); @@ -93,7 +96,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_delivery_url */ - function test_get_delivery_url() { + public function test_get_delivery_url() { $object = new WC_Webhook(); $expected = 'https://woocommerce.com'; $object->set_delivery_url( $expected ); @@ -103,7 +106,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_user_id */ - function test_get_user_id() { + public function test_get_user_id() { $object = new WC_Webhook(); $expected = 1; $object->set_user_id( $expected ); @@ -113,7 +116,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_api_version */ - function test_get_api_version() { + public function test_get_api_version() { $object = new WC_Webhook(); $expected = 'wp_api_v2'; $object->set_api_version( $expected ); @@ -123,7 +126,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_failure_count */ - function test_get_failure_count() { + public function test_get_failure_count() { $object = new WC_Webhook(); $expected = 1; $object->set_failure_count( $expected ); @@ -133,7 +136,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_pending_delivery */ - function test_get_pending_delivery() { + public function test_get_pending_delivery() { $object = new WC_Webhook(); $expected = true; $object->set_pending_delivery( $expected ); @@ -143,7 +146,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_hooks */ - function test_get_hooks() { + public function test_get_hooks() { $object = new WC_Webhook(); $object->set_topic( 'order.created' ); $expected = array( @@ -155,7 +158,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_resource */ - function test_get_resource() { + public function test_get_resource() { $object = new WC_Webhook(); $object->set_topic( 'order.created' ); $this->assertEquals( 'order', $object->get_resource() ); @@ -164,7 +167,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_event */ - function test_get_event() { + public function test_get_event() { $object = new WC_Webhook(); $object->set_topic( 'order.created' ); $this->assertEquals( 'created', $object->get_event() ); @@ -173,7 +176,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: get_i18n_status */ - function test_get_i18n_status() { + public function test_get_i18n_status() { $object = new WC_Webhook(); $object->set_status( 'active' ); $this->assertEquals( 'Active', $object->get_i18n_status() ); @@ -182,7 +185,7 @@ class WC_Tests_CRUD_Webhooks extends WC_Unit_Test_Case { /** * Test: generate_signature */ - function test_generate_signature() { + public function test_generate_signature() { $object = new WC_Webhook(); $this->assertEquals( 'GBDo00G55h6IiV+6CxqivQPLbI//KzaOZm747971tPs=', $object->generate_signature( 'secret' ) ); } From 47d1f0ec8ec4aa864f41fba90f4fb50ffa110325 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 18 Feb 2019 21:27:01 +0000 Subject: [PATCH 305/401] Update dependency mocha to v6 --- package-lock.json | 990 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 2 +- 2 files changed, 957 insertions(+), 35 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6cd8656c146..8c82e45fc04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -432,6 +432,12 @@ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true + }, "ansi-escapes": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", @@ -2768,6 +2774,15 @@ "strip-bom": "^2.0.0" } }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", @@ -2854,6 +2869,12 @@ "integrity": "sha512-b5dDNQYdy2vW9WXUD8+RQlfoxvqztLLhDE+T7Gd37I5E8My7nJkKu6FmhdDeRWJ8B+yjZKuwjCta8pgi8kgSqA==", "dev": true }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, "detect-indent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", @@ -3065,6 +3086,31 @@ "is-arrayish": "^0.2.1" } }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "es6-promise": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", @@ -3407,6 +3453,15 @@ "fill-range": "^2.1.0" } }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -3957,6 +4012,23 @@ } } }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "dev": true + } + } + }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -4096,6 +4168,12 @@ "rimraf": "2" } }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -4973,6 +5051,15 @@ "har-schema": "^2.0.0" } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -4994,6 +5081,12 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -5062,9 +5155,9 @@ } }, "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, "home-or-tmp": { @@ -5077,6 +5170,15 @@ "os-tmpdir": "^1.0.1" } }, + "homedir-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", + "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, "hooker": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", @@ -5507,6 +5609,12 @@ "builtin-modules": "^1.0.0" } }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, "is-ci": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", @@ -5525,6 +5633,12 @@ "kind-of": "^3.0.2" } }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, "is-decimal": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.2.tgz", @@ -5712,6 +5826,15 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, "is-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", @@ -5730,6 +5853,15 @@ "integrity": "sha512-3vcJecUUrpgCqc/ca0aWeNu64UGgxcvO60K/Fkr1N6RSvfGCTU60UKN68JDmKokgba0rFFJs12EnzOQa14ubKQ==", "dev": true }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -6799,6 +6931,15 @@ "pseudomap": "^1.0.1" } }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -7082,61 +7223,562 @@ } }, "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.0.0.tgz", + "integrity": "sha512-A7g9k3yr8oJaXn2IItFnfgjyxFc/LTe6Wwv7FczP+e8G74o9xYNSbMYmCf1ouldRojLrFcOb+z75P6Ak0GX6ug==", "dev": true, "requires": { + "ansi-colors": "3.2.3", "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", + "debug": "3.2.6", "diff": "3.5.0", "escape-string-regexp": "1.0.5", - "glob": "7.1.2", + "findup-sync": "2.0.0", + "glob": "7.1.3", "growl": "1.10.5", - "he": "1.1.1", + "he": "1.2.0", + "js-yaml": "3.12.0", + "log-symbols": "2.2.0", "minimatch": "3.0.4", "mkdirp": "0.5.1", - "supports-color": "5.4.0" + "ms": "2.1.1", + "node-environment-flags": "1.0.4", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "12.0.5", + "yargs-parser": "11.1.1", + "yargs-unparser": "1.5.0" }, "dependencies": { - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "ms": "2.0.0" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } } }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "mem": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz", + "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^2.0.0" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" } }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", "dev": true, "requires": { "has-flag": "^3.0.0" } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, @@ -7221,6 +7863,15 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node-environment-flags": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.4.tgz", + "integrity": "sha512-M9rwCnWVLW7PX+NUWe3ejEdiLYinRpsEre9hMkU/6NS4h+EEulYaDH1gCEZ2gyXsmw+RXYDaV2JkkTNcsPDJ0Q==", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3" + } + }, "node-gyp": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", @@ -7550,6 +8201,12 @@ } } }, + "object-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", + "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==", + "dev": true + }, "object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", @@ -7567,6 +8224,28 @@ } } }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, "object.omit": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", @@ -7687,12 +8366,24 @@ "object-assign": "^4.1.0" } }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, + "p-is-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", + "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==", + "dev": true + }, "p-limit": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", @@ -7774,6 +8465,12 @@ "error-ex": "^1.2.0" } }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, "parse5": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", @@ -9232,6 +9929,42 @@ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "dev": true }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "dependencies": { + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + } + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -11418,7 +12151,6 @@ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, - "optional": true, "requires": { "string-width": "^1.0.2 || 2" } @@ -11625,6 +12357,196 @@ } } }, + "yargs-unparser": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", + "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", + "dev": true, + "requires": { + "flat": "^4.1.0", + "lodash": "^4.17.11", + "yargs": "^12.0.5" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "mem": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz", + "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^2.0.0" + } + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, "yauzl": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", diff --git a/package.json b/package.json index a83c2668676..da957837e7e 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "husky": "1.3.1", "istanbul": "1.0.0-alpha.2", "lint-staged": "8.1.4", - "mocha": "5.2.0", + "mocha": "6.0.0", "node-sass": "4.11.0", "prettier": "github:automattic/calypso-prettier#c56b4251", "stylelint": "9.10.1", From 4d5c6b90422b7f09b4774bdd8ac321b96a475947 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 18 Feb 2019 18:38:29 -0300 Subject: [PATCH 306/401] Use short cuts for wpdb properties --- .../data-stores/class-wc-product-data-store-cpt.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index 349c1ca81fe..2723efc306c 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -1075,12 +1075,12 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da // Get the attributes of the variations. $query = $wpdb->prepare( " - SELECT post_id, meta_key, meta_value FROM {$wpdb->prefix}postmeta + SELECT post_id, meta_key, meta_value FROM {$wpdb->postmeta} WHERE post_id IN ( - SELECT ID FROM {$wpdb->prefix}posts - WHERE {$wpdb->prefix}posts.post_parent = %d - AND {$wpdb->prefix}posts.post_status = 'publish' - AND {$wpdb->prefix}posts.post_type = 'product_variation' + SELECT ID FROM {$wpdb->posts} + WHERE {$wpdb->posts}.post_parent = %d + AND {$wpdb->posts}.post_status = 'publish' + AND {$wpdb->posts}.post_type = 'product_variation' ORDER BY menu_order ASC, ID ASC ) ", From 096e92554b987ca8f0584adbd456a8ec54870ff6 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 18 Feb 2019 18:42:11 -0300 Subject: [PATCH 307/401] Only disable WordPress.DB.SlowDBQuery.slow_db_query_tax_query --- includes/data-stores/class-wc-product-data-store-cpt.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index 7fe715bdfae..9392048ebb4 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -920,7 +920,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da public function get_featured_product_ids() { $product_visibility_term_ids = wc_get_product_visibility_term_ids(); - // phpcs:disable return get_posts( array( 'post_type' => array( 'product', 'product_variation' ), @@ -943,7 +942,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da 'fields' => 'id=>parent', ) ); - // phpcs:enable } /** From 8388325985c97f10f30d218ba86f05dd7e7fe05f Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 18 Feb 2019 18:45:07 -0300 Subject: [PATCH 308/401] PHPCS ignore only what is necessary --- includes/data-stores/class-wc-product-data-store-cpt.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index 9392048ebb4..efb3a6d8fd9 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -1097,7 +1097,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da $query .= ' AND meta_key IN ( "' . implode( '","', array_map( 'esc_sql', $meta_attribute_names ) ) . '" );'; - $attributes = $wpdb->get_results( $query ); // phpcs:ignore + $attributes = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared if ( ! $attributes ) { return 0; @@ -1106,7 +1106,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da $sorted_meta = array(); foreach ( $attributes as $m ) { - $sorted_meta[ $m->post_id ][ $m->meta_key ] = $m->meta_value; // phpcs:ignore + $sorted_meta[ $m->post_id ][ $m->meta_key ] = $m->meta_value; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key } /** From 989fad5a7e94bf8d30bd067e649f8d45a8f2cf87 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 18 Feb 2019 22:52:39 +0100 Subject: [PATCH 309/401] Perhaps it does not make much sense to filter empty URI if it's REST request or not. --- includes/class-woocommerce.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php index 54309404c59..556a15fc8ad 100644 --- a/includes/class-woocommerce.php +++ b/includes/class-woocommerce.php @@ -238,7 +238,7 @@ final class WooCommerce { */ public function is_rest_api_request() { if ( empty( $_SERVER['REQUEST_URI'] ) ) { - return apply_filters( 'woocommerce_is_rest_api_request', false ); + return false; } // REST API prefix. From d405c28bb7fb7c6b1f8ba2139212394e0df08d48 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 18 Feb 2019 22:53:58 +0100 Subject: [PATCH 310/401] Added back check for empty REQUEST_URI to authentication function, as it's still needed, the value is used further down. --- includes/api/class-wc-rest-authentication.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/includes/api/class-wc-rest-authentication.php b/includes/api/class-wc-rest-authentication.php index a17f6eac6e6..dc3a9eafcf8 100644 --- a/includes/api/class-wc-rest-authentication.php +++ b/includes/api/class-wc-rest-authentication.php @@ -50,6 +50,10 @@ class WC_REST_Authentication { * @return bool */ protected function is_request_to_rest_api() { + if ( empty( $_SERVER['REQUEST_URI'] ) ) { + return false; + } + $rest_prefix = trailingslashit( rest_get_url_prefix() ); // Allow third party plugins use our authentication methods. From f4524fadd20e550eb979c98ae778e963a945b89b Mon Sep 17 00:00:00 2001 From: Luminus Olumide Alabi Date: Tue, 19 Feb 2019 10:54:59 +0000 Subject: [PATCH 311/401] Rename Macedonia to North Macedonia --- i18n/countries.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/countries.php b/i18n/countries.php index 338a695fd66..99957a9bbc9 100644 --- a/i18n/countries.php +++ b/i18n/countries.php @@ -140,7 +140,7 @@ return array( 'LT' => __( 'Lithuania', 'woocommerce' ), 'LU' => __( 'Luxembourg', 'woocommerce' ), 'MO' => __( 'Macao S.A.R., China', 'woocommerce' ), - 'MK' => __( 'Macedonia', 'woocommerce' ), + 'MK' => __( 'North Macedonia', 'woocommerce' ), 'MG' => __( 'Madagascar', 'woocommerce' ), 'MW' => __( 'Malawi', 'woocommerce' ), 'MY' => __( 'Malaysia', 'woocommerce' ), From a37d8ae385b3f39c2ec34fa4b48e853dbeec8e25 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 19 Feb 2019 11:22:13 +0000 Subject: [PATCH 312/401] SCSS mailchimp color change missing --- assets/css/wc-setup.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/css/wc-setup.scss b/assets/css/wc-setup.scss index bffdc285f41..4de0adf0146 100644 --- a/assets/css/wc-setup.scss +++ b/assets/css/wc-setup.scss @@ -962,7 +962,7 @@ h3.jetpack-reasons { & > * { display: block; } - + .plugin-install-info-list-item::after { content: ', '; } @@ -1299,7 +1299,7 @@ p.jetpack-terms { } &.recommended-item-icon-mailchimp { - background-color: #209bbb; + background-color: #ffe01b; height: 2em; padding: ( 3.5em - 2em ) / 2; } @@ -1354,4 +1354,4 @@ p.jetpack-terms { border-bottom: 1px solid #eee; margin-top: 0; padding: 30px 0; -} \ No newline at end of file +} From a6499d83abc1d7a015101de62c986a0904c3d5ef Mon Sep 17 00:00:00 2001 From: Martin Snajdr Date: Tue, 19 Feb 2019 15:49:35 +0100 Subject: [PATCH 313/401] Flat rate shipping cost / class cost filters added. Allows to tweak flatrate shipping cost / class cost before it gets calculated and applied to shipping. Very useful for multicurrency plugins, because you can add field for fixed shipping cost in another currency and use this value instead of the default value from cost field. --- includes/shipping/flat-rate/class-wc-shipping-flat-rate.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php b/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php index d8ea696e628..f6ebc1227f3 100644 --- a/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php +++ b/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php @@ -146,7 +146,7 @@ class WC_Shipping_Flat_Rate extends WC_Shipping_Method { // Calculate the costs. $has_costs = false; // True when a cost is set. False if all costs are blank strings. - $cost = $this->get_option( 'cost' ); + $cost = apply_filters( 'woocommerce_' . $this->id . '_shipping_cost', $this->get_option( 'cost' ), $this ); if ( '' !== $cost ) { $has_costs = true; @@ -170,6 +170,8 @@ class WC_Shipping_Flat_Rate extends WC_Shipping_Method { $shipping_class_term = get_term_by( 'slug', $shipping_class, 'product_shipping_class' ); $class_cost_string = $shipping_class_term && $shipping_class_term->term_id ? $this->get_option( 'class_cost_' . $shipping_class_term->term_id, $this->get_option( 'class_cost_' . $shipping_class, '' ) ) : $this->get_option( 'no_class_cost', '' ); + $class_cost_string = apply_filters( 'woocommerce_' . $this->id . '_shipping_class_cost', $class_cost_string, $shipping_class_term, $this ); + if ( '' === $class_cost_string ) { continue; } From a2abe8783ec14056e71d56f3cf2d781e84130763 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Tue, 19 Feb 2019 11:08:14 -0400 Subject: [PATCH 314/401] check low_stock for empty string vs isset --- includes/admin/meta-boxes/class-wc-meta-box-product-data.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php index ce5e32efbfb..66657b767a2 100644 --- a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php +++ b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php @@ -365,7 +365,7 @@ class WC_Meta_Box_Product_Data { 'backorders' => isset( $_POST['_backorders'] ) ? wc_clean( wp_unslash( $_POST['_backorders'] ) ) : null, 'stock_status' => wc_clean( wp_unslash( $_POST['_stock_status'] ) ), 'stock_quantity' => $stock, - 'low_stock_amount' => isset( $_POST['_low_stock_amount'] ) ? wc_stock_amount( wp_unslash( $_POST['_low_stock_amount'] ) ) : null, + 'low_stock_amount' => '' !== $_POST['_low_stock_amount'] ? wc_stock_amount( wp_unslash( $_POST['_low_stock_amount'] ) ) : null, 'download_limit' => '' === $_POST['_download_limit'] ? '' : absint( wp_unslash( $_POST['_download_limit'] ) ), 'download_expiry' => '' === $_POST['_download_expiry'] ? '' : absint( wp_unslash( $_POST['_download_expiry'] ) ), 'downloads' => self::prepare_downloads( From 3b6bbd2f3d8fe4104006c58edb31926ab0e8dbc2 Mon Sep 17 00:00:00 2001 From: "SASAGAWA, Kiyoshi" Date: Wed, 20 Feb 2019 01:20:02 +0900 Subject: [PATCH 315/401] Fixing order of JP address. JP address must be ordered as 'Postal Code' -> 'State (Prefecture)' -> 'City' -> 'address'. Changing the priority. --- includes/class-wc-countries.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-countries.php b/includes/class-wc-countries.php index 9b5c27c0e97..977eff5e667 100644 --- a/includes/class-wc-countries.php +++ b/includes/class-wc-countries.php @@ -960,12 +960,21 @@ class WC_Countries { ), ), 'JP' => array( + 'postcode' => array( + 'priority' => 65, + ), 'state' => array( 'label' => __( 'Prefecture', 'woocommerce' ), 'priority' => 66, ), - 'postcode' => array( - 'priority' => 65, + 'city' => array( + 'priority' => 67, + ), + 'address_1' => array( + 'priority' => 68, + ), + 'address_2' => array( + 'priority' => 69, ), ), 'KR' => array( From b5219aac3147444657765912c18fc72e7e5f0d2f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 19 Feb 2019 16:35:26 +0000 Subject: [PATCH 316/401] Add extra cache headers --- includes/class-wc-cache-helper.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/includes/class-wc-cache-helper.php b/includes/class-wc-cache-helper.php index a0a8e09aead..fa68234eab5 100644 --- a/includes/class-wc-cache-helper.php +++ b/includes/class-wc-cache-helper.php @@ -23,6 +23,7 @@ class WC_Cache_Helper { * Hook in methods. */ public static function init() { + add_filter( 'nocache_headers', array( __CLASS__, 'additional_nocache_headers' ), 10 ); add_action( 'shutdown', array( __CLASS__, 'delete_transients_on_shutdown' ), 10 ); add_action( 'template_redirect', array( __CLASS__, 'geolocation_ajax_redirect' ) ); add_action( 'admin_notices', array( __CLASS__, 'notices' ) ); @@ -32,6 +33,18 @@ class WC_Cache_Helper { add_action( 'edit_terms', array( __CLASS__, 'clean_term_cache' ), 10, 2 ); } + /** + * Set additonal nocache headers. + * + * @param array $headers Header names and field values. + * @since 3.6.0 + */ + public static function additional_nocache_headers( $headers ) { + // Opt-out of Google weblight if page is dynamic e.g. cart/checkout. https://support.google.com/webmasters/answer/6211428?hl=en. + $headers['Cache-Control'] = 'no-transform, no-cache, must-revalidate, max-age=0'; + return $headers; + } + /** * Add a transient to delete on shutdown. * From 8c18367e578fb1b6014131a3f903e80bccb9fd20 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Tue, 19 Feb 2019 14:17:16 -0300 Subject: [PATCH 317/401] [REST API] Better description for order's status collection param --- includes/api/class-wc-rest-orders-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/api/class-wc-rest-orders-controller.php b/includes/api/class-wc-rest-orders-controller.php index 62b86306d53..7c6cd06a91e 100644 --- a/includes/api/class-wc-rest-orders-controller.php +++ b/includes/api/class-wc-rest-orders-controller.php @@ -257,7 +257,7 @@ class WC_REST_Orders_Controller extends WC_REST_Orders_V2_Controller { $params['status'] = array( 'default' => 'any', - 'description' => __( 'Limit result set to orders assigned a specific status.', 'woocommerce' ), + 'description' => __( 'Limit result set to orders assigned to specific statuses.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'string', From 4c7f61e1231b71ceebebec780febd973d60407e5 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Tue, 19 Feb 2019 13:41:14 -0400 Subject: [PATCH 318/401] phpcs except for ignore use of $_POST --- .../class-wc-meta-box-product-data.php | 108 +++++++++--------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php index 66657b767a2..c06dec34599 100644 --- a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php +++ b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php @@ -4,8 +4,6 @@ * * Displays the product data box, tabbed, with several panels covering price, stock etc. * - * @author WooThemes - * @category Admin * @package WooCommerce/Admin/Meta Boxes * @version 3.0.0 */ @@ -22,7 +20,7 @@ class WC_Meta_Box_Product_Data { /** * Output the metabox. * - * @param WP_Post $post + * @param WP_Post $post Post object. */ public static function output( $post ) { global $thepostid, $product_object; @@ -56,7 +54,8 @@ class WC_Meta_Box_Product_Data { */ private static function get_product_type_options() { return apply_filters( - 'product_type_options', array( + 'product_type_options', + array( 'virtual' => array( 'id' => '_virtual', 'wrapper_class' => 'show_if_simple', @@ -82,7 +81,8 @@ class WC_Meta_Box_Product_Data { */ private static function get_product_data_tabs() { $tabs = apply_filters( - 'woocommerce_product_data_tabs', array( + 'woocommerce_product_data_tabs', + array( 'general' => array( 'label' => __( 'General', 'woocommerce' ), 'target' => 'general_product_data', @@ -148,7 +148,7 @@ class WC_Meta_Box_Product_Data { return -1; } - if ( $a['priority'] == $b['priority'] ) { + if ( $a['priority'] === $b['priority'] ) { return 0; } @@ -158,7 +158,7 @@ class WC_Meta_Box_Product_Data { /** * Filter callback for finding variation attributes. * - * @param WC_Product_Attribute $attribute + * @param WC_Product_Attribute $attribute Product attribute. * @return bool */ private static function filter_variation_attributes( $attribute ) { @@ -183,9 +183,9 @@ class WC_Meta_Box_Product_Data { /** * Prepare downloads for save. * - * @param array $file_names - * @param array $file_urls - * @param array $file_hashes + * @param array $file_names File names. + * @param array $file_urls File urls. + * @param array $file_hashes File hashes. * * @return array */ @@ -193,7 +193,7 @@ class WC_Meta_Box_Product_Data { $downloads = array(); if ( ! empty( $file_urls ) ) { - $file_url_size = sizeof( $file_urls ); + $file_url_size = count( $file_urls ); for ( $i = 0; $i < $file_url_size; $i ++ ) { if ( ! empty( $file_urls[ $i ] ) ) { @@ -220,7 +220,7 @@ class WC_Meta_Box_Product_Data { /** * Prepare attributes for save. * - * @param array $data + * @param array $data Attribute data. * * @return array */ @@ -281,9 +281,9 @@ class WC_Meta_Box_Product_Data { /** * Prepare attributes for a specific variation or defaults. * - * @param array $all_attributes - * @param string $key_prefix - * @param int $index + * @param array $all_attributes List of attribute keys. + * @param string $key_prefix Attribute key prefix. + * @param int $index Attribute array index. * @return array */ private static function prepare_set_attributes( $all_attributes, $key_prefix = 'attribute_', $index = null ) { @@ -318,12 +318,12 @@ class WC_Meta_Box_Product_Data { /** * Save meta box data. * - * @param int $post_id - * @param $post + * @param int $post_id WP post id. + * @param WP_Post $post Post object. */ public static function save( $post_id, $post ) { // Process product type first so we have the correct class to run setters. - $product_type = empty( $_POST['product-type'] ) ? WC_Product_Factory::get_product_type( $post_id ) : sanitize_title( stripslashes( $_POST['product-type'] ) ); + $product_type = empty( $_POST['product-type'] ) ? WC_Product_Factory::get_product_type( $post_id ) : sanitize_title( wp_unslash( $_POST['product-type'] ) ); $classname = WC_Product_Factory::get_product_classname( $post_id, $product_type ? $product_type : 'simple' ); $product = new $classname( $post_id ); $attributes = self::prepare_attributes(); @@ -331,7 +331,7 @@ class WC_Meta_Box_Product_Data { // Handle stock changes. if ( isset( $_POST['_stock'] ) ) { - if ( isset( $_POST['_original_stock'] ) && wc_stock_amount( $product->get_stock_quantity( 'edit' ) ) !== wc_stock_amount( $_POST['_original_stock'] ) ) { + if ( isset( $_POST['_original_stock'] ) && wc_stock_amount( $product->get_stock_quantity( 'edit' ) ) !== wc_stock_amount( wp_unslash( $_POST['_original_stock'] ) ) ) { /* translators: 1: product ID 2: quantity in stock */ WC_Admin_Meta_Boxes::add_error( sprintf( __( 'The stock has not been updated because the value has changed since editing. Product %1$d has %2$d units in stock.', 'woocommerce' ), $product->get_id(), $product->get_stock_quantity( 'edit' ) ) ); } else { @@ -373,12 +373,12 @@ class WC_Meta_Box_Product_Data { isset( $_POST['_wc_file_urls'] ) ? wp_unslash( $_POST['_wc_file_urls'] ) : array(), isset( $_POST['_wc_file_hashes'] ) ? wp_unslash( $_POST['_wc_file_hashes'] ) : array() ), - 'product_url' => esc_url_raw( wp_unslash( $_POST['_product_url'] ) ), - 'button_text' => wc_clean( wp_unslash( $_POST['_button_text'] ) ), - 'children' => 'grouped' === $product_type ? self::prepare_children() : null, - 'reviews_allowed' => ! empty( $_POST['comment_status'] ) && 'open' === $_POST['comment_status'], - 'attributes' => $attributes, - 'default_attributes' => self::prepare_set_attributes( $attributes, 'default_attribute_' ), + 'product_url' => esc_url_raw( wp_unslash( $_POST['_product_url'] ) ), + 'button_text' => wc_clean( wp_unslash( $_POST['_button_text'] ) ), + 'children' => 'grouped' === $product_type ? self::prepare_children() : null, + 'reviews_allowed' => ! empty( $_POST['comment_status'] ) && 'open' === $_POST['comment_status'], + 'attributes' => $attributes, + 'default_attributes' => self::prepare_set_attributes( $attributes, 'default_attribute_' ), ) ); @@ -387,24 +387,26 @@ class WC_Meta_Box_Product_Data { } /** - * @since 3.0.0 to set props before save. + * Set props before save. + * + * @since 3.0.0 */ do_action( 'woocommerce_admin_process_product_object', $product ); $product->save(); if ( $product->is_type( 'variable' ) ) { - $product->get_data_store()->sync_variation_names( $product, wc_clean( $_POST['original_post_title'] ), wc_clean( $_POST['post_title'] ) ); + $product->get_data_store()->sync_variation_names( $product, wc_clean( wp_unslash( $_POST['original_post_title'] ) ), wc_clean( wp_unslash( $_POST['post_title'] ) ) ); } do_action( 'woocommerce_process_product_meta_' . $product_type, $post_id ); } /** - * Save meta box data. + * Save variation meta box data. * - * @param int $post_id - * @param WP_Post $post + * @param int $post_id WP post id. + * @param WP_Post $post Post object. */ public static function save_variations( $post_id, $post ) { if ( isset( $_POST['variable_post_id'] ) ) { @@ -412,7 +414,7 @@ class WC_Meta_Box_Product_Data { $parent->set_default_attributes( self::prepare_set_attributes( $parent->get_attributes(), 'default_attribute_' ) ); $parent->save(); - $max_loop = max( array_keys( $_POST['variable_post_id'] ) ); + $max_loop = max( array_keys( wp_unslash( $_POST['variable_post_id'] ) ) ); $data_store = $parent->get_data_store(); $data_store->sort_all_product_variations( $parent->get_id() ); @@ -427,45 +429,45 @@ class WC_Meta_Box_Product_Data { // Handle stock changes. if ( isset( $_POST['variable_stock'], $_POST['variable_stock'][ $i ] ) ) { - if ( isset( $_POST['variable_original_stock'], $_POST['variable_original_stock'][ $i ] ) && wc_stock_amount( $variation->get_stock_quantity( 'edit' ) ) !== wc_stock_amount( $_POST['variable_original_stock'][ $i ] ) ) { + if ( isset( $_POST['variable_original_stock'], $_POST['variable_original_stock'][ $i ] ) && wc_stock_amount( $variation->get_stock_quantity( 'edit' ) ) !== wc_stock_amount( wp_unslash( $_POST['variable_original_stock'][ $i ] ) ) ) { /* translators: 1: product ID 2: quantity in stock */ WC_Admin_Meta_Boxes::add_error( sprintf( __( 'The stock has not been updated because the value has changed since editing. Product %1$d has %2$d units in stock.', 'woocommerce' ), $variation->get_id(), $variation->get_stock_quantity( 'edit' ) ) ); } else { - $stock = wc_stock_amount( $_POST['variable_stock'][ $i ] ); + $stock = wc_stock_amount( wp_unslash( $_POST['variable_stock'][ $i ] ) ); } } $errors = $variation->set_props( array( 'status' => isset( $_POST['variable_enabled'][ $i ] ) ? 'publish' : 'private', - 'menu_order' => wc_clean( $_POST['variation_menu_order'][ $i ] ), - 'regular_price' => wc_clean( $_POST['variable_regular_price'][ $i ] ), - 'sale_price' => wc_clean( $_POST['variable_sale_price'][ $i ] ), + 'menu_order' => wc_clean( wp_unslash( $_POST['variation_menu_order'][ $i ] ) ), + 'regular_price' => wc_clean( wp_unslash( $_POST['variable_regular_price'][ $i ] ) ), + 'sale_price' => wc_clean( wp_unslash( $_POST['variable_sale_price'][ $i ] ) ), 'virtual' => isset( $_POST['variable_is_virtual'][ $i ] ), 'downloadable' => isset( $_POST['variable_is_downloadable'][ $i ] ), - 'date_on_sale_from' => wc_clean( $_POST['variable_sale_price_dates_from'][ $i ] ), - 'date_on_sale_to' => wc_clean( $_POST['variable_sale_price_dates_to'][ $i ] ), - 'description' => wp_kses_post( $_POST['variable_description'][ $i ] ), - 'download_limit' => wc_clean( $_POST['variable_download_limit'][ $i ] ), - 'download_expiry' => wc_clean( $_POST['variable_download_expiry'][ $i ] ), + 'date_on_sale_from' => wc_clean( wp_unslash( $_POST['variable_sale_price_dates_from'][ $i ] ) ), + 'date_on_sale_to' => wc_clean( wp_unslash( $_POST['variable_sale_price_dates_to'][ $i ] ) ), + 'description' => wp_kses_post( wp_unslash( $_POST['variable_description'][ $i ] ) ), + 'download_limit' => wc_clean( wp_unslash( $_POST['variable_download_limit'][ $i ] ) ), + 'download_expiry' => wc_clean( wp_unslash( $_POST['variable_download_expiry'][ $i ] ) ), 'downloads' => self::prepare_downloads( - isset( $_POST['_wc_variation_file_names'][ $variation_id ] ) ? $_POST['_wc_variation_file_names'][ $variation_id ] : array(), - isset( $_POST['_wc_variation_file_urls'][ $variation_id ] ) ? $_POST['_wc_variation_file_urls'][ $variation_id ] : array(), - isset( $_POST['_wc_variation_file_hashes'][ $variation_id ] ) ? $_POST['_wc_variation_file_hashes'][ $variation_id ] : array() + isset( $_POST['_wc_variation_file_names'][ $variation_id ] ) ? wp_unslash( $_POST['_wc_variation_file_names'][ $variation_id ] ) : array(), + isset( $_POST['_wc_variation_file_urls'][ $variation_id ] ) ? wp_unslash( $_POST['_wc_variation_file_urls'][ $variation_id ] ) : array(), + isset( $_POST['_wc_variation_file_hashes'][ $variation_id ] ) ? wp_unslash( $_POST['_wc_variation_file_hashes'][ $variation_id ] ) : array() ), 'manage_stock' => isset( $_POST['variable_manage_stock'][ $i ] ), 'stock_quantity' => $stock, - 'backorders' => isset( $_POST['variable_backorders'], $_POST['variable_backorders'][ $i ] ) ? wc_clean( $_POST['variable_backorders'][ $i ] ) : null, - 'stock_status' => wc_clean( $_POST['variable_stock_status'][ $i ] ), - 'image_id' => wc_clean( $_POST['upload_image_id'][ $i ] ), + 'backorders' => isset( $_POST['variable_backorders'], $_POST['variable_backorders'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_backorders'][ $i ] ) ) : null, + 'stock_status' => wc_clean( wp_unslash( $_POST['variable_stock_status'][ $i ] ) ), + 'image_id' => wc_clean( wp_unslash( $_POST['upload_image_id'][ $i ] ) ), 'attributes' => self::prepare_set_attributes( $parent->get_attributes(), 'attribute_', $i ), 'sku' => isset( $_POST['variable_sku'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_sku'][ $i ] ) ) : '', - 'weight' => isset( $_POST['variable_weight'][ $i ] ) ? wc_clean( $_POST['variable_weight'][ $i ] ) : '', - 'length' => isset( $_POST['variable_length'][ $i ] ) ? wc_clean( $_POST['variable_length'][ $i ] ) : '', - 'width' => isset( $_POST['variable_width'][ $i ] ) ? wc_clean( $_POST['variable_width'][ $i ] ) : '', - 'height' => isset( $_POST['variable_height'][ $i ] ) ? wc_clean( $_POST['variable_height'][ $i ] ) : '', - 'shipping_class_id' => wc_clean( $_POST['variable_shipping_class'][ $i ] ), - 'tax_class' => isset( $_POST['variable_tax_class'][ $i ] ) ? wc_clean( $_POST['variable_tax_class'][ $i ] ) : null, + 'weight' => isset( $_POST['variable_weight'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_weight'][ $i ] ) ) : '', + 'length' => isset( $_POST['variable_length'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_length'][ $i ] ) ) : '', + 'width' => isset( $_POST['variable_width'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_width'][ $i ] ) ) : '', + 'height' => isset( $_POST['variable_height'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_height'][ $i ] ) ) : '', + 'shipping_class_id' => wc_clean( wp_unslash( $_POST['variable_shipping_class'][ $i ] ) ), + 'tax_class' => isset( $_POST['variable_tax_class'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_tax_class'][ $i ] ) ) : null, ) ); From e9a98ce4aeb32a06a7470697f4104483c35af72c Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Tue, 19 Feb 2019 15:37:45 -0400 Subject: [PATCH 319/401] fixes from review --- includes/admin/meta-boxes/class-wc-meta-box-product-data.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php index c06dec34599..f546bd12d36 100644 --- a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php +++ b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php @@ -365,7 +365,7 @@ class WC_Meta_Box_Product_Data { 'backorders' => isset( $_POST['_backorders'] ) ? wc_clean( wp_unslash( $_POST['_backorders'] ) ) : null, 'stock_status' => wc_clean( wp_unslash( $_POST['_stock_status'] ) ), 'stock_quantity' => $stock, - 'low_stock_amount' => '' !== $_POST['_low_stock_amount'] ? wc_stock_amount( wp_unslash( $_POST['_low_stock_amount'] ) ) : null, + 'low_stock_amount' => isset( $_POST['_low_stock_amount'] ) && '' !== $_POST['_low_stock_amount'] ? wc_stock_amount( wp_unslash( $_POST['_low_stock_amount'] ) ) : '', 'download_limit' => '' === $_POST['_download_limit'] ? '' : absint( wp_unslash( $_POST['_download_limit'] ) ), 'download_expiry' => '' === $_POST['_download_expiry'] ? '' : absint( wp_unslash( $_POST['_download_expiry'] ) ), 'downloads' => self::prepare_downloads( From cb6405e98047cadea90ff0cb506c7755d5d319e8 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 20 Feb 2019 00:54:12 +0000 Subject: [PATCH 320/401] Update dependency autoprefixer to v9.4.8 --- package-lock.json | 26 +++++++++++++------------- package.json | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 87b3131d737..d4c77e93479 100644 --- a/package-lock.json +++ b/package-lock.json @@ -641,13 +641,13 @@ "dev": true }, "autoprefixer": { - "version": "9.4.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.7.tgz", - "integrity": "sha512-qS5wW6aXHkm53Y4z73tFGsUhmZu4aMPV9iHXYlF0c/wxjknXNHuj/1cIQb+6YH692DbJGGWcckAXX+VxKvahMA==", + "version": "9.4.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.8.tgz", + "integrity": "sha512-DIhd0KMi9Nql3oJkJ2HCeOVihrXFPtWXc6ckwaUNwliDOt9OGr0fk8vV8jCLWXnZc1EXvQ2uLUzGpcPxFAQHEQ==", "dev": true, "requires": { "browserslist": "^4.4.1", - "caniuse-lite": "^1.0.30000932", + "caniuse-lite": "^1.0.30000938", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", "postcss": "^7.0.14", @@ -2166,9 +2166,9 @@ } }, "caniuse-lite": { - "version": "1.0.30000932", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000932.tgz", - "integrity": "sha512-4bghJFItvzz8m0T3lLZbacmEY9X1Z2AtIzTr7s7byqZIOumASfr4ynDx7rtm0J85nDmx8vsgR6vnaSoeU8Oh0A==", + "version": "1.0.30000938", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000938.tgz", + "integrity": "sha512-ekW8NQ3/FvokviDxhdKLZZAx7PptXNwxKgXtnR5y+PR3hckwuP3yJ1Ir+4/c97dsHNqtAyfKUGdw8P4EYzBNgw==", "dev": true }, "caseless": { @@ -3026,9 +3026,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.108", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.108.tgz", - "integrity": "sha512-/QI4hMpAh48a1Sea6PALGv+kuVne9A2EWGd8HrWHMdYhIzGtbhVVHh6heL5fAzGaDnZuPyrlWJRl8WPm4RyiQQ==", + "version": "1.3.113", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz", + "integrity": "sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==", "dev": true }, "elegant-spinner": { @@ -7925,9 +7925,9 @@ } }, "node-releases": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.5.tgz", - "integrity": "sha512-6C2K0x1QlYTz9wCueMN/DVZFcBVg/qsj2k9iV5gV/+OvG4KNrl7Nu7TWbWFQ3/Z2V10qVFQWtj5Xa+VBodcI6g==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.8.tgz", + "integrity": "sha512-gQm+K9mGCiT/NXHy+V/ZZS1N/LOaGGqRAAJJs3X9Ah1g+CIbRcBgNyoNYQ+SEtcyAtB9KqDruu+fF7nWjsqRaA==", "dev": true, "requires": { "semver": "^5.3.0" diff --git a/package.json b/package.json index 4dba3b74952..ebc43e305d3 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "git:update-hooks": "rm -r .git/hooks && mkdir -p .git/hooks && node ./node_modules/husky/husky.js install" }, "devDependencies": { - "autoprefixer": "9.4.7", + "autoprefixer": "9.4.8", "babel": "6.23.0", "babel-cli": "6.26.0", "babel-eslint": "10.0.1", From dfd4f4ea6dc17900f6b85b7fbff36df9c63232ec Mon Sep 17 00:00:00 2001 From: Martin Snajdr Date: Wed, 20 Feb 2019 11:54:59 +0100 Subject: [PATCH 321/401] Filters added to shipping method function get_option --- includes/abstracts/abstract-wc-shipping-method.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/includes/abstracts/abstract-wc-shipping-method.php b/includes/abstracts/abstract-wc-shipping-method.php index c4657b39255..2f5f9744144 100644 --- a/includes/abstracts/abstract-wc-shipping-method.php +++ b/includes/abstracts/abstract-wc-shipping-method.php @@ -456,11 +456,13 @@ abstract class WC_Shipping_Method extends WC_Settings_API { public function get_option( $key, $empty_value = null ) { // Instance options take priority over global options. if ( $this->instance_id && array_key_exists( $key, $this->get_instance_form_fields() ) ) { - return $this->get_instance_option( $key, $empty_value ); + $instance_option = apply_filters( 'woocommerce_shipping_' . $this->id . '_instance_option', $this->get_instance_option( $key, $empty_value ), $this ); + return $instance_option; } // Return global option. - return parent::get_option( $key, $empty_value ); + $global_option = apply_filters( 'woocommerce_shipping_' . $this->id . '_global_option', parent::get_option( $key, $empty_value ), $this ); + return $global_option; } /** From 89016f1e709cec493e465826b57f3a0b313f0d8c Mon Sep 17 00:00:00 2001 From: Martin Snajdr Date: Wed, 20 Feb 2019 11:57:03 +0100 Subject: [PATCH 322/401] WP coding standards fix. --- includes/shipping/flat-rate/class-wc-shipping-flat-rate.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php b/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php index f6ebc1227f3..ffbec37b92b 100644 --- a/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php +++ b/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php @@ -169,8 +169,7 @@ class WC_Shipping_Flat_Rate extends WC_Shipping_Method { // Also handles BW compatibility when slugs were used instead of ids. $shipping_class_term = get_term_by( 'slug', $shipping_class, 'product_shipping_class' ); $class_cost_string = $shipping_class_term && $shipping_class_term->term_id ? $this->get_option( 'class_cost_' . $shipping_class_term->term_id, $this->get_option( 'class_cost_' . $shipping_class, '' ) ) : $this->get_option( 'no_class_cost', '' ); - - $class_cost_string = apply_filters( 'woocommerce_' . $this->id . '_shipping_class_cost', $class_cost_string, $shipping_class_term, $this ); + $class_cost_string = apply_filters( 'woocommerce_' . $this->id . '_shipping_class_cost', $class_cost_string, $shipping_class_term, $this ); if ( '' === $class_cost_string ) { continue; From 1caeb43baf3c465f3c3c4349875d9b409935f079 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 14 Feb 2019 11:07:16 +0000 Subject: [PATCH 323/401] Update photoswipe --- .../photoswipe/default-skin/default-skin.css | 15 ++++--- .../photoswipe/default-skin/default-skin.png | Bin .../photoswipe/default-skin/default-skin.svg | 2 +- .../css/photoswipe/default-skin/preloader.gif | Bin assets/css/photoswipe/photoswipe.css | 36 ++--------------- assets/js/photoswipe/photoswipe-ui-default.js | 4 +- .../photoswipe/photoswipe-ui-default.min.js | 6 +-- assets/js/photoswipe/photoswipe.js | 38 +++++++++++++----- assets/js/photoswipe/photoswipe.min.js | 6 +-- 9 files changed, 46 insertions(+), 61 deletions(-) mode change 100644 => 100755 assets/css/photoswipe/default-skin/default-skin.css mode change 100644 => 100755 assets/css/photoswipe/default-skin/default-skin.png mode change 100644 => 100755 assets/css/photoswipe/default-skin/default-skin.svg mode change 100644 => 100755 assets/css/photoswipe/default-skin/preloader.gif mode change 100644 => 100755 assets/css/photoswipe/photoswipe.css mode change 100644 => 100755 assets/js/photoswipe/photoswipe-ui-default.js mode change 100644 => 100755 assets/js/photoswipe/photoswipe-ui-default.min.js mode change 100644 => 100755 assets/js/photoswipe/photoswipe.js mode change 100644 => 100755 assets/js/photoswipe/photoswipe.min.js diff --git a/assets/css/photoswipe/default-skin/default-skin.css b/assets/css/photoswipe/default-skin/default-skin.css old mode 100644 new mode 100755 index 4424deee14b..c961632663d --- a/assets/css/photoswipe/default-skin/default-skin.css +++ b/assets/css/photoswipe/default-skin/default-skin.css @@ -12,7 +12,7 @@ */ /* - + 1. Buttons */ @@ -31,13 +31,11 @@ margin: 0; float: right; opacity: 0.75; - z-index: 1600; -webkit-transition: opacity 0.2s; transition: opacity 0.2s; -webkit-box-shadow: none; box-shadow: none; } - .pswp__button:focus, - .pswp__button:hover { + .pswp__button:focus, .pswp__button:hover { opacity: 1; } .pswp__button:active { outline: none; @@ -258,7 +256,7 @@ a.pswp__share--download:hover { padding: 0 10px; } /* - + 4. Caption */ @@ -339,8 +337,8 @@ a.pswp__share--download:hover { margin: 0; } .pswp--css_animation .pswp__preloader__cut { - /* - The idea of animating inner circle is based on Polymer ("material") loading indicator + /* + The idea of animating inner circle is based on Polymer ("material") loading indicator by Keanu Lee https://blog.keanulee.com/2014/10/20/the-tale-of-three-spinners.html */ position: relative; @@ -410,12 +408,13 @@ a.pswp__share--download:hover { transform: rotate(0); } } /* - + 6. Additional styles */ /* root element of UI */ .pswp__ui { + -webkit-font-smoothing: auto; visibility: visible; opacity: 1; z-index: 1550; } diff --git a/assets/css/photoswipe/default-skin/default-skin.png b/assets/css/photoswipe/default-skin/default-skin.png old mode 100644 new mode 100755 diff --git a/assets/css/photoswipe/default-skin/default-skin.svg b/assets/css/photoswipe/default-skin/default-skin.svg old mode 100644 new mode 100755 index ffc2bbb67b9..9d5f0c6a10a --- a/assets/css/photoswipe/default-skin/default-skin.svg +++ b/assets/css/photoswipe/default-skin/default-skin.svg @@ -1 +1 @@ - \ No newline at end of file +default-skin 2 \ No newline at end of file diff --git a/assets/css/photoswipe/default-skin/preloader.gif b/assets/css/photoswipe/default-skin/preloader.gif old mode 100644 new mode 100755 diff --git a/assets/css/photoswipe/photoswipe.css b/assets/css/photoswipe/photoswipe.css old mode 100644 new mode 100755 index 14cf7face91..0ca0f802889 --- a/assets/css/photoswipe/photoswipe.css +++ b/assets/css/photoswipe/photoswipe.css @@ -1,35 +1,3 @@ -/** - * WooCommerce Photoswipe styles. - * 1. These styles are required to overwrite default theme button styles (Twenty Twelve adds gradients via background-image). - * 2. For zooming on mobile. - */ -.woocommerce img.pswp__img, -.woocommerce-page img.pswp__img { - max-width: none; /* 2 */ -} -button.pswp__button { - box-shadow: none !important; - background-image: url('default-skin/default-skin.png') !important; -} -button.pswp__button, -button.pswp__button:hover, -button.pswp__button--arrow--left::before, -button.pswp__button--arrow--right::before { - background-color: transparent !important; /* 1 */ -} -button.pswp__button--arrow--left, -button.pswp__button--arrow--right, -button.pswp__button--arrow--left:hover, -button.pswp__button--arrow--right:hover { - background-image: none !important; /* 1 */ -} -button.pswp__button--close:hover { - background-position: 0 -44px; -} -button.pswp__button--zoom:hover { - background-position: -88px 0; -} - /*! PhotoSwipe main CSS by Dmitry Semenov | photoswipe.com | MIT license */ /* Styles for basic PhotoSwipe functionality (sliding area, open/close transitions) @@ -45,7 +13,7 @@ button.pswp__button--zoom:hover { overflow: hidden; -ms-touch-action: none; touch-action: none; - z-index: 999999; + z-index: 1500; -webkit-text-size-adjust: 100%; /* create separate layer, to avoid paint on window.onscroll in webkit/blink */ -webkit-backface-visibility: hidden; @@ -98,6 +66,8 @@ button.pswp__button--zoom:hover { height: 100%; background: #000; opacity: 0; + -webkit-transform: translateZ(0); + transform: translateZ(0); -webkit-backface-visibility: hidden; will-change: opacity; } diff --git a/assets/js/photoswipe/photoswipe-ui-default.js b/assets/js/photoswipe/photoswipe-ui-default.js old mode 100644 new mode 100755 index 7f3a71a0fdc..f3733e1bc7a --- a/assets/js/photoswipe/photoswipe-ui-default.js +++ b/assets/js/photoswipe/photoswipe-ui-default.js @@ -1,6 +1,6 @@ -/*! PhotoSwipe Default UI - 4.1.1 - 2015-12-24 +/*! PhotoSwipe Default UI - 4.1.3 - 2019-01-08 * http://photoswipe.com -* Copyright (c) 2015 Dmitry Semenov; */ +* Copyright (c) 2019 Dmitry Semenov; */ /** * * UI on top of main sliding area (caption, arrows, close button, etc.). diff --git a/assets/js/photoswipe/photoswipe-ui-default.min.js b/assets/js/photoswipe/photoswipe-ui-default.min.js old mode 100644 new mode 100755 index 13aa1f80c8c..ab7bedc76d1 --- a/assets/js/photoswipe/photoswipe-ui-default.min.js +++ b/assets/js/photoswipe/photoswipe-ui-default.min.js @@ -1,4 +1,4 @@ -/*! PhotoSwipe Default UI - 4.1.1 - 2015-12-24 +/*! PhotoSwipe Default UI - 4.1.3 - 2019-01-08 * http://photoswipe.com -* Copyright (c) 2015 Dmitry Semenov; */ -!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.PhotoSwipeUI_Default=t()}(this,function(){"use strict";return function(e,t){var n,o,l,r,i,s,a,u,c,p,d,m,f,h,w,g,v,b,_,C=this,T=!1,I=!0,E=!0,F={barsSize:{top:44,bottom:"auto"},closeElClasses:["item","caption","zoom-wrap","ui","top-bar"],timeToIdle:4e3,timeToIdleOutside:1e3,loadingIndicatorDelay:1e3,addCaptionHTMLFn:function(e,t){return e.title?(t.children[0].innerHTML=e.title,!0):(t.children[0].innerHTML="",!1)},closeEl:!0,captionEl:!0,fullscreenEl:!0,zoomEl:!0,shareEl:!0,counterEl:!0,arrowEl:!0,preloaderEl:!0,tapToClose:!1,tapToToggleControls:!0,clickToCloseNonZoomable:!0,shareButtons:[{id:"facebook",label:"Share on Facebook",url:"https://www.facebook.com/sharer/sharer.php?u={{url}}"},{id:"twitter",label:"Tweet",url:"https://twitter.com/intent/tweet?text={{text}}&url={{url}}"},{id:"pinterest",label:"Pin it",url:"http://www.pinterest.com/pin/create/button/?url={{url}}&media={{image_url}}&description={{text}}"},{id:"download",label:"Download image",url:"{{raw_image_url}}",download:!0}],getImageURLForShare:function(){return e.currItem.src||""},getPageURLForShare:function(){return window.location.href},getTextForShare:function(){return e.currItem.title||""},indexIndicatorSep:" / ",fitControlsWidth:1200},x=function(e){if(g)return!0;e=e||window.event,w.timeToIdle&&w.mouseUsed&&!c&&D();for(var n,o,l=(e.target||e.srcElement).getAttribute("class")||"",r=0;r-1&&(n.onTap(),o=!0);if(o){e.stopPropagation&&e.stopPropagation(),g=!0;var i=t.features.isOldAndroid?600:30;v=setTimeout(function(){g=!1},i)}},S=function(){return!e.likelyTouchDevice||w.mouseUsed||screen.width>w.fitControlsWidth},k=function(e,n,o){t[(o?"add":"remove")+"Class"](e,"pswp__"+n)},K=function(){var e=1===w.getNumItemsFn();e!==h&&(k(o,"ui--one-slide",e),h=e)},L=function(){k(a,"share-modal--hidden",E)},O=function(){return(E=!E)?(t.removeClass(a,"pswp__share-modal--fade-in"),setTimeout(function(){E&&L()},300)):(L(),setTimeout(function(){E||t.addClass(a,"pswp__share-modal--fade-in")},30)),E||y(),!1},R=function(t){var n=(t=t||window.event).target||t.srcElement;return e.shout("shareLinkClick",t,n),!(!n.href||!n.hasAttribute("download")&&(window.open(n.href,"pswp_share","scrollbars=yes,resizable=yes,toolbar=no,location=yes,width=550,height=420,top=100,left="+(window.screen?Math.round(screen.width/2-275):100)),E||O(),1))},y=function(){for(var e,t,n,o,l="",r=0;r
    @@ -799,7 +799,7 @@ class WC_Admin_Setup_Wizard { id="" name="" class="method wc-enhanced-select" - data-plugins="get_wcs_requisite_plugins() ) ); ?>" + data-plugins="get_wcs_requisite_plugins() ) ); ?>" > $method ) : ?> @@ -1653,7 +1653,7 @@ class WC_Admin_Setup_Wizard { placeholder="" - data-plugins="" + data-plugins="" /> @@ -1670,7 +1670,7 @@ class WC_Admin_Setup_Wizard { type="checkbox" name="wc-wizard-service--enabled" value="yes" - data-plugins="" + data-plugins="" />
    - - -
    -
    +
    diff --git a/includes/admin/reports/class-wc-admin-report.php b/includes/admin/reports/class-wc-admin-report.php index 60efaf696f2..49f0bb5344f 100644 --- a/includes/admin/reports/class-wc-admin-report.php +++ b/includes/admin/reports/class-wc-admin-report.php @@ -338,7 +338,7 @@ class WC_Admin_Report { $wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' ); $big_selects = true; } - + $cached_results[ $query_hash ] = apply_filters( 'woocommerce_reports_get_order_report_data', $wpdb->$query_type( $query ), $data ); set_transient( strtolower( get_class( $this ) ), $cached_results, DAY_IN_SECONDS ); } @@ -512,7 +512,7 @@ class WC_Admin_Report { $sparkline_data = array_values( $this->prepare_chart_data( $data, 'post_date', 'sparkline_value', $days - 1, strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ), 'day' ) ); - return ''; + return ''; } /** diff --git a/includes/admin/reports/class-wc-report-coupon-usage.php b/includes/admin/reports/class-wc-report-coupon-usage.php index 59141e02b05..eca8540b8e5 100644 --- a/includes/admin/reports/class-wc-report-coupon-usage.php +++ b/includes/admin/reports/class-wc-report-coupon-usage.php @@ -455,7 +455,7 @@ class WC_Report_Coupon_Usage extends WC_Admin_Report { $order_discount_amounts = $this->prepare_chart_data( $order_discount_amounts, 'post_date', 'discount_amount', $this->chart_interval, $this->start_date, $this->chart_groupby ); // Encode in json format. - $chart_data = json_encode( + $chart_data = wp_json_encode( array( 'order_coupon_counts' => array_values( $order_coupon_counts ), 'order_discount_amounts' => array_values( $order_discount_amounts ), @@ -469,7 +469,7 @@ class WC_Report_Coupon_Usage extends WC_Admin_Report { var main_chart; jQuery(function(){ - var order_data = jQuery.parseJSON( '' ); + var order_data = JSON.parse( decodeURIComponent( '' ) ); var drawGraph = function( highlight ) { var series = [ @@ -525,7 +525,7 @@ class WC_Report_Coupon_Usage extends WC_Admin_Report { tickColor: 'transparent', mode: "time", timeformat: "chart_groupby ) ? '%d %b' : '%b'; ?>", - monthNames: month_abbrev ) ); ?>, + monthNames: JSON.parse( decodeURIComponent( 'month_abbrev ) ) ); ?>' ) ), tickLength: 1, minTickSize: [1, "chart_groupby ); ?>"], font: { diff --git a/includes/admin/reports/class-wc-report-customers.php b/includes/admin/reports/class-wc-report-customers.php index 511a734890e..4b949d2180d 100644 --- a/includes/admin/reports/class-wc-report-customers.php +++ b/includes/admin/reports/class-wc-report-customers.php @@ -309,10 +309,12 @@ class WC_Report_Customers extends WC_Admin_Report { $customer_orders = $this->prepare_chart_data( $customer_orders, 'post_date', 'total_orders', $this->chart_interval, $this->start_date, $this->chart_groupby ); $guest_orders = $this->prepare_chart_data( $guest_orders, 'post_date', 'total_orders', $this->chart_interval, $this->start_date, $this->chart_groupby ); - $chart_data = array( - 'signups' => array_values( $signups ), - 'customer_orders' => array_values( $customer_orders ), - 'guest_orders' => array_values( $guest_orders ), + $chart_data = wp_json_encode( + array( + 'signups' => array_values( $signups ), + 'customer_orders' => array_values( $customer_orders ), + 'guest_orders' => array_values( $guest_orders ), + ) ); ?>
    @@ -322,7 +324,7 @@ class WC_Report_Customers extends WC_Admin_Report { var main_chart; jQuery(function(){ - var chart_data = jQuery.parseJSON( '' ); + var chart_data = JSON.parse( decodeURIComponent( '' ) ); var drawGraph = function( highlight ) { var series = [ @@ -391,7 +393,7 @@ class WC_Report_Customers extends WC_Admin_Report { tickColor: 'transparent', mode: "time", timeformat: "chart_groupby ) ? '%d %b' : '%b'; ?>", - monthNames: month_abbrev ) ); ?>, + monthNames: JSON.parse( decodeURIComponent( 'month_abbrev ) ) ); ?>' ) ), tickLength: 1, minTickSize: [1, "chart_groupby ); ?>"], tickSize: [1, "chart_groupby ); ?>"], diff --git a/includes/admin/reports/class-wc-report-sales-by-category.php b/includes/admin/reports/class-wc-report-sales-by-category.php index 746f9ac3ea5..2a445eedea2 100644 --- a/includes/admin/reports/class-wc-report-sales-by-category.php +++ b/includes/admin/reports/class-wc-report-sales-by-category.php @@ -348,12 +348,16 @@ class WC_Report_Sales_By_Category extends WC_Admin_Report { $width = $this->barwidth / sizeof( $chart_data ); $offset = ( $width * $index ); $series = $data['data']; + foreach ( $series as $key => $series_data ) { $series[ $key ][0] = $series_data[0] + $offset; } + + $series = wp_json_encode( $series ); + echo '{ label: "' . esc_js( $data['category'] ) . '", - data: jQuery.parseJSON( "' . json_encode( $series ) . '" ), + data: JSON.parse( decodeURIComponent( "' . rawurlencode( $series ) . '" ) ), color: "' . $color . '", bars: { fillColor: "' . $color . '", @@ -407,7 +411,7 @@ class WC_Report_Sales_By_Category extends WC_Admin_Report { tickColor: 'transparent', mode: "time", timeformat: "chart_groupby ) ? '%d %b' : '%b'; ?>", - monthNames: month_abbrev ) ); ?>, + monthNames: JSON.parse( decodeURIComponent( 'month_abbrev ) ) ); ?>' ) ), tickLength: 1, minTickSize: [1, "chart_groupby; ?>"], tickSize: [1, "chart_groupby; ?>"], diff --git a/includes/admin/reports/class-wc-report-sales-by-date.php b/includes/admin/reports/class-wc-report-sales-by-date.php index 62c085ffec9..0f9adfc5d26 100644 --- a/includes/admin/reports/class-wc-report-sales-by-date.php +++ b/includes/admin/reports/class-wc-report-sales-by-date.php @@ -693,7 +693,7 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report { var main_chart; jQuery(function(){ - var order_data = jQuery.parseJSON( '' ); + var order_data = JSON.parse( decodeURIComponent( '' ) ); var drawGraph = function( highlight ) { var series = [ { @@ -817,7 +817,7 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report { tickColor: 'transparent', mode: "time", timeformat: "chart_groupby ) ? '%d %b' : '%b'; ?>", - monthNames: month_abbrev ) ); ?>, + monthNames: JSON.parse( decodeURIComponent( 'month_abbrev ) ) ); ?>' ) ), tickLength: 1, minTickSize: [1, "chart_groupby ); ?>"], font: { diff --git a/includes/admin/reports/class-wc-report-sales-by-product.php b/includes/admin/reports/class-wc-report-sales-by-product.php index 69ebdc6e505..1c1687fab3d 100644 --- a/includes/admin/reports/class-wc-report-sales-by-product.php +++ b/includes/admin/reports/class-wc-report-sales-by-product.php @@ -525,7 +525,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { var main_chart; jQuery(function(){ - var order_data = jQuery.parseJSON( '' ); + var order_data = JSON.parse( decodeURIComponent( '' ) ); var drawGraph = function( highlight ) { @@ -582,7 +582,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report { tickColor: 'transparent', mode: "time", timeformat: "chart_groupby ) ? '%d %b' : '%b'; ?>", - monthNames: month_abbrev ) ) ?>, + monthNames: JSON.parse( decodeURIComponent( 'month_abbrev ) ) ); ?>' ) ), tickLength: 1, minTickSize: [1, "chart_groupby; ?>"], font: { diff --git a/includes/api/legacy/v1/class-wc-api-json-handler.php b/includes/api/legacy/v1/class-wc-api-json-handler.php index adc51290673..691bbb9663c 100644 --- a/includes/api/legacy/v1/class-wc-api-json-handler.php +++ b/includes/api/legacy/v1/class-wc-api-json-handler.php @@ -48,32 +48,27 @@ class WC_API_JSON_Handler implements WC_API_Handler { * @return string */ public function generate_response( $data ) { - if ( isset( $_GET['_jsonp'] ) ) { - // JSONP enabled by default if ( ! apply_filters( 'woocommerce_api_jsonp_enabled', true ) ) { - WC()->api->server->send_status( 400 ); - - $data = array( array( 'code' => 'woocommerce_api_jsonp_disabled', 'message' => __( 'JSONP support is disabled on this site', 'woocommerce' ) ) ); + return wp_json_encode( array( array( 'code' => 'woocommerce_api_jsonp_disabled', 'message' => __( 'JSONP support is disabled on this site', 'woocommerce' ) ) ) ); } - // Check for invalid characters (only alphanumeric allowed) - if ( preg_match( '/\W/', $_GET['_jsonp'] ) ) { + $jsonp_callback = $_GET['_jsonp']; + if ( ! wp_check_jsonp_callback( $jsonp_callback ) ) { WC()->api->server->send_status( 400 ); - - $data = array( array( 'code' => 'woocommerce_api_jsonp_callback_invalid', __( 'The JSONP callback function is invalid', 'woocommerce' ) ) ); + return wp_json_encode( array( array( 'code' => 'woocommerce_api_jsonp_callback_invalid', __( 'The JSONP callback function is invalid', 'woocommerce' ) ) ) ); } - // see http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/ WC()->api->server->header( 'X-Content-Type-Options', 'nosniff' ); - // Prepend '/**/' to mitigate possible JSONP Flash attacks - return '/**/' . $_GET['_jsonp'] . '(' . json_encode( $data ) . ')'; + // Prepend '/**/' to mitigate possible JSONP Flash attacks. + // https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/ + return '/**/' . $jsonp_callback . '(' . wp_json_encode( $data ) . ')'; } - return json_encode( $data ); + return wp_json_encode( $data ); } } diff --git a/includes/api/legacy/v2/class-wc-api-json-handler.php b/includes/api/legacy/v2/class-wc-api-json-handler.php index e19ac15d371..672aa8850c2 100644 --- a/includes/api/legacy/v2/class-wc-api-json-handler.php +++ b/includes/api/legacy/v2/class-wc-api-json-handler.php @@ -47,32 +47,27 @@ class WC_API_JSON_Handler implements WC_API_Handler { * @return string */ public function generate_response( $data ) { - if ( isset( $_GET['_jsonp'] ) ) { - // JSONP enabled by default if ( ! apply_filters( 'woocommerce_api_jsonp_enabled', true ) ) { - WC()->api->server->send_status( 400 ); - - $data = array( array( 'code' => 'woocommerce_api_jsonp_disabled', 'message' => __( 'JSONP support is disabled on this site', 'woocommerce' ) ) ); + return wp_json_encode( array( array( 'code' => 'woocommerce_api_jsonp_disabled', 'message' => __( 'JSONP support is disabled on this site', 'woocommerce' ) ) ) ); } - // Check for invalid characters (only alphanumeric allowed) - if ( preg_match( '/\W/', $_GET['_jsonp'] ) ) { + $jsonp_callback = $_GET['_jsonp']; + if ( ! wp_check_jsonp_callback( $jsonp_callback ) ) { WC()->api->server->send_status( 400 ); - - $data = array( array( 'code' => 'woocommerce_api_jsonp_callback_invalid', __( 'The JSONP callback function is invalid', 'woocommerce' ) ) ); + return wp_json_encode( array( array( 'code' => 'woocommerce_api_jsonp_callback_invalid', __( 'The JSONP callback function is invalid', 'woocommerce' ) ) ) ); } - // see http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/ WC()->api->server->header( 'X-Content-Type-Options', 'nosniff' ); - // Prepend '/**/' to mitigate possible JSONP Flash attacks - return '/**/' . $_GET['_jsonp'] . '(' . json_encode( $data ) . ')'; + // Prepend '/**/' to mitigate possible JSONP Flash attacks. + // https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/ + return '/**/' . $jsonp_callback . '(' . wp_json_encode( $data ) . ')'; } - return json_encode( $data ); + return wp_json_encode( $data ); } } diff --git a/includes/api/legacy/v3/class-wc-api-json-handler.php b/includes/api/legacy/v3/class-wc-api-json-handler.php index e19ac15d371..672aa8850c2 100644 --- a/includes/api/legacy/v3/class-wc-api-json-handler.php +++ b/includes/api/legacy/v3/class-wc-api-json-handler.php @@ -47,32 +47,27 @@ class WC_API_JSON_Handler implements WC_API_Handler { * @return string */ public function generate_response( $data ) { - if ( isset( $_GET['_jsonp'] ) ) { - // JSONP enabled by default if ( ! apply_filters( 'woocommerce_api_jsonp_enabled', true ) ) { - WC()->api->server->send_status( 400 ); - - $data = array( array( 'code' => 'woocommerce_api_jsonp_disabled', 'message' => __( 'JSONP support is disabled on this site', 'woocommerce' ) ) ); + return wp_json_encode( array( array( 'code' => 'woocommerce_api_jsonp_disabled', 'message' => __( 'JSONP support is disabled on this site', 'woocommerce' ) ) ) ); } - // Check for invalid characters (only alphanumeric allowed) - if ( preg_match( '/\W/', $_GET['_jsonp'] ) ) { + $jsonp_callback = $_GET['_jsonp']; + if ( ! wp_check_jsonp_callback( $jsonp_callback ) ) { WC()->api->server->send_status( 400 ); - - $data = array( array( 'code' => 'woocommerce_api_jsonp_callback_invalid', __( 'The JSONP callback function is invalid', 'woocommerce' ) ) ); + return wp_json_encode( array( array( 'code' => 'woocommerce_api_jsonp_callback_invalid', __( 'The JSONP callback function is invalid', 'woocommerce' ) ) ) ); } - // see http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/ WC()->api->server->header( 'X-Content-Type-Options', 'nosniff' ); - // Prepend '/**/' to mitigate possible JSONP Flash attacks - return '/**/' . $_GET['_jsonp'] . '(' . json_encode( $data ) . ')'; + // Prepend '/**/' to mitigate possible JSONP Flash attacks. + // https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/ + return '/**/' . $jsonp_callback . '(' . wp_json_encode( $data ) . ')'; } - return json_encode( $data ); + return wp_json_encode( $data ); } } diff --git a/includes/class-wc-structured-data.php b/includes/class-wc-structured-data.php index 0d9b07470c7..49f064bc4fd 100644 --- a/includes/class-wc-structured-data.php +++ b/includes/class-wc-structured-data.php @@ -153,7 +153,7 @@ class WC_Structured_Data { $data = $this->get_structured_data( $types ); if ( $data ) { - echo ''; + echo ''; } } diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore.php b/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore.php index b91e3d19e0f..590726f4ced 100644 --- a/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore.php +++ b/includes/libraries/action-scheduler/classes/ActionScheduler_wpPostStore.php @@ -32,7 +32,7 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store { $post = array( 'post_type' => self::POST_TYPE, 'post_title' => $action->get_hook(), - 'post_content' => json_encode($action->get_args()), + 'post_content' => wp_json_encode($action->get_args()), 'post_status' => ( $action->is_finished() ? 'publish' : 'pending' ), 'post_date_gmt' => $this->get_scheduled_date_string( $action, $scheduled_date ), 'post_date' => $this->get_scheduled_date_string_local( $action, $scheduled_date ), @@ -190,7 +190,7 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store { $args[] = self::POST_TYPE; if ( !is_null($params['args']) ) { $query .= " AND p.post_content=%s"; - $args[] = json_encode($params['args']); + $args[] = wp_json_encode($params['args']); } if ( ! empty( $params['status'] ) ) { @@ -271,7 +271,7 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store { } if ( !is_null($query['args']) ) { $sql .= " AND p.post_content=%s"; - $sql_params[] = json_encode($query['args']); + $sql_params[] = wp_json_encode($query['args']); } if ( ! empty( $query['status'] ) ) { @@ -736,7 +736,7 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store { * @param ActionScheduler_Action $action */ protected function validate_action( ActionScheduler_Action $action ) { - if ( strlen( json_encode( $action->get_args() ) ) > self::$max_index_length ) { + if ( strlen( wp_json_encode( $action->get_args() ) ) > self::$max_index_length ) { _doing_it_wrong( 'ActionScheduler_Action::$args', sprintf( 'To ensure the action args column can be indexed, action args should not be more than %d characters when encoded as JSON. Support for strings longer than this will be removed in a future version.', self::$max_index_length ), '2.1.0' ); } } diff --git a/includes/wc-formatting-functions.php b/includes/wc-formatting-functions.php index fa9160df1e5..3884d84aa53 100644 --- a/includes/wc-formatting-functions.php +++ b/includes/wc-formatting-functions.php @@ -1372,6 +1372,23 @@ function wc_implode_html_attributes( $raw_attributes ) { return implode( ' ', $attributes ); } +/** + * Escape JSON for use on HTML or attribute text nodes. + * + * @since 3.5.5 + * @param string $json JSON to escape. + * @param bool $html True if escaping for HTML text node, false for attributes. Determines how quotes are handled. + * @return string Escaped JSON. + */ +function wc_esc_json( $json, $html = false ) { + return _wp_specialchars( + $json, + $html ? ENT_NOQUOTES : ENT_QUOTES, // Escape quotes in attribute nodes only, + 'UTF-8', // json_encode() outputs UTF-8 (really just ASCII), not the blog's charset. + true // Double escape entities: `&` -> `&amp;` + ); +} + /** * Parse a relative date option from the settings API into a standard format. * diff --git a/includes/widgets/class-wc-widget-layered-nav.php b/includes/widgets/class-wc-widget-layered-nav.php index 6598aefee40..cf27c074e2e 100644 --- a/includes/widgets/class-wc-widget-layered-nav.php +++ b/includes/widgets/class-wc-widget-layered-nav.php @@ -339,7 +339,7 @@ class WC_Widget_Layered_Nav extends WC_Widget { if ( jQuery().selectWoo ) { var wc_layered_nav_select = function() { jQuery( '.dropdown_layered_nav_" . esc_js( $taxonomy_filter_name ) . "' ).selectWoo( { - placeholder: " . wp_json_encode( (string) wp_specialchars_decode( $any_label ) ) . ", + placeholder: decodeURIComponent('" . rawurlencode( (string) wp_specialchars_decode( $any_label ) ) . "'), minimumResultsForSearch: 5, width: '100%', allowClear: " . ( $multiple ? 'false' : 'true' ) . ", diff --git a/templates/single-product/add-to-cart/variable.php b/templates/single-product/add-to-cart/variable.php index 852a3a51a0d..4aa4c596fd0 100644 --- a/templates/single-product/add-to-cart/variable.php +++ b/templates/single-product/add-to-cart/variable.php @@ -12,18 +12,20 @@ * * @see https://docs.woocommerce.com/document/template-structure/ * @package WooCommerce/Templates - * @version 3.4.1 + * @version 3.5.5 */ defined( 'ABSPATH' ) || exit; global $product; -$attribute_keys = array_keys( $attributes ); +$attribute_keys = array_keys( $attributes ); +$variations_json = wp_json_encode( $available_variations ); +$variations_attr = function_exists( 'wc_esc_json' ) ? wc_esc_json( $variations_json ) : _wp_specialchars( $variations_json, ENT_QUOTES, 'UTF-8', true ); do_action( 'woocommerce_before_add_to_cart_form' ); ?> - + diff --git a/tests/unit-tests/util/class-wc-tests-core-functions.php b/tests/unit-tests/util/class-wc-tests-core-functions.php index ed60277656e..048dc6bfd2e 100644 --- a/tests/unit-tests/util/class-wc-tests-core-functions.php +++ b/tests/unit-tests/util/class-wc-tests-core-functions.php @@ -433,7 +433,7 @@ class WC_Tests_Core_Functions extends WC_Unit_Test_Case { $this->assertEquals( print_r( $arr, true ), wc_print_r( $arr, true ) ); $this->assertEquals( var_export( $arr, true ), wc_print_r( $arr, true ) ); - $this->assertEquals( json_encode( $arr ), wc_print_r( $arr, true ) ); + $this->assertEquals( wp_json_encode( $arr ), wc_print_r( $arr, true ) ); $this->assertEquals( serialize( $arr ), wc_print_r( $arr, true ) ); $this->assertFalse( wc_print_r( $arr, true ) ); From 1c678372d495b9a1074551269dbfeb8099f81a12 Mon Sep 17 00:00:00 2001 From: Martin Snajdr Date: Wed, 20 Feb 2019 15:59:40 +0100 Subject: [PATCH 326/401] Shipping cost filters removed & added two additional parameters get_option function filters --- .../abstracts/abstract-wc-shipping-method.php | 32 +++++++++++-------- .../flat-rate/class-wc-shipping-flat-rate.php | 13 +++++--- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/includes/abstracts/abstract-wc-shipping-method.php b/includes/abstracts/abstract-wc-shipping-method.php index 2f5f9744144..d4a2a9846ce 100644 --- a/includes/abstracts/abstract-wc-shipping-method.php +++ b/includes/abstracts/abstract-wc-shipping-method.php @@ -263,16 +263,23 @@ abstract class WC_Shipping_Method extends WC_Settings_API { * @param array $args Arguments (default: array()). */ public function add_rate( $args = array() ) { - $args = apply_filters( 'woocommerce_shipping_method_add_rate_args', wp_parse_args( $args, array( - 'id' => $this->get_rate_id(), // ID for the rate. If not passed, this id:instance default will be used. - 'label' => '', // Label for the rate. - 'cost' => '0', // Amount or array of costs (per item shipping). - 'taxes' => '', // Pass taxes, or leave empty to have it calculated for you, or 'false' to disable calculations. - 'calc_tax' => 'per_order', // Calc tax per_order or per_item. Per item needs an array of costs. - 'meta_data' => array(), // Array of misc meta data to store along with this rate - key value pairs. - 'package' => false, // Package array this rate was generated for @since 2.6.0. - 'price_decimals' => wc_get_price_decimals(), - ) ), $this ); + $args = apply_filters( + 'woocommerce_shipping_method_add_rate_args', + wp_parse_args( + $args, + array( + 'id' => $this->get_rate_id(), // ID for the rate. If not passed, this id:instance default will be used. + 'label' => '', // Label for the rate. + 'cost' => '0', // Amount or array of costs (per item shipping). + 'taxes' => '', // Pass taxes, or leave empty to have it calculated for you, or 'false' to disable calculations. + 'calc_tax' => 'per_order', // Calc tax per_order or per_item. Per item needs an array of costs. + 'meta_data' => array(), // Array of misc meta data to store along with this rate - key value pairs. + 'package' => false, // Package array this rate was generated for @since 2.6.0. + 'price_decimals' => wc_get_price_decimals(), + ) + ), + $this + ); // ID and label are required. if ( ! $args['id'] || ! $args['label'] ) { @@ -323,7 +330,6 @@ abstract class WC_Shipping_Method extends WC_Settings_API { * Calc taxes per item being shipping in costs array. * * @since 2.6.0 - * @access protected * @param array $costs Costs. * @return array of taxes */ @@ -456,12 +462,12 @@ abstract class WC_Shipping_Method extends WC_Settings_API { public function get_option( $key, $empty_value = null ) { // Instance options take priority over global options. if ( $this->instance_id && array_key_exists( $key, $this->get_instance_form_fields() ) ) { - $instance_option = apply_filters( 'woocommerce_shipping_' . $this->id . '_instance_option', $this->get_instance_option( $key, $empty_value ), $this ); + $instance_option = apply_filters( 'woocommerce_shipping_' . $this->id . '_instance_option', $this->get_instance_option( $key, $empty_value ), $key, $empty_value, $this ); return $instance_option; } // Return global option. - $global_option = apply_filters( 'woocommerce_shipping_' . $this->id . '_global_option', parent::get_option( $key, $empty_value ), $this ); + $global_option = apply_filters( 'woocommerce_shipping_' . $this->id . '_global_option', parent::get_option( $key, $empty_value ), $key, $empty_value, $this ); return $global_option; } diff --git a/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php b/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php index ffbec37b92b..4605828ab31 100644 --- a/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php +++ b/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php @@ -111,7 +111,9 @@ class WC_Shipping_Flat_Rate extends WC_Shipping_Method { 'percent' => '', 'min_fee' => '', 'max_fee' => '', - ), $atts, 'fee' + ), + $atts, + 'fee' ); $calculated_fee = 0; @@ -146,12 +148,13 @@ class WC_Shipping_Flat_Rate extends WC_Shipping_Method { // Calculate the costs. $has_costs = false; // True when a cost is set. False if all costs are blank strings. - $cost = apply_filters( 'woocommerce_' . $this->id . '_shipping_cost', $this->get_option( 'cost' ), $this ); + $cost = $this->get_option( 'cost' ); if ( '' !== $cost ) { $has_costs = true; $rate['cost'] = $this->evaluate_cost( - $cost, array( + $cost, + array( 'qty' => $this->get_package_item_qty( $package ), 'cost' => $package['contents_cost'], ) @@ -169,7 +172,6 @@ class WC_Shipping_Flat_Rate extends WC_Shipping_Method { // Also handles BW compatibility when slugs were used instead of ids. $shipping_class_term = get_term_by( 'slug', $shipping_class, 'product_shipping_class' ); $class_cost_string = $shipping_class_term && $shipping_class_term->term_id ? $this->get_option( 'class_cost_' . $shipping_class_term->term_id, $this->get_option( 'class_cost_' . $shipping_class, '' ) ) : $this->get_option( 'no_class_cost', '' ); - $class_cost_string = apply_filters( 'woocommerce_' . $this->id . '_shipping_class_cost', $class_cost_string, $shipping_class_term, $this ); if ( '' === $class_cost_string ) { continue; @@ -177,7 +179,8 @@ class WC_Shipping_Flat_Rate extends WC_Shipping_Method { $has_costs = true; $class_cost = $this->evaluate_cost( - $class_cost_string, array( + $class_cost_string, + array( 'qty' => array_sum( wp_list_pluck( $products, 'quantity' ) ), 'cost' => array_sum( wp_list_pluck( $products, 'line_total' ) ), ) From 7fb12465dd2cbd926dea2ca4a8e420f05c9466a1 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Wed, 20 Feb 2019 13:17:52 -0300 Subject: [PATCH 327/401] Updated changelog --- CHANGELOG.txt | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index d4eebaded6a..80f83b38fd6 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,5 +1,125 @@ == Changelog == += 3.5.5 - 2019-02-20 = +* Fix - Fix allow product low stock threshold be the WC settings default. #22777 +* Fix - Fix error on product category when sorting by multiple fields. #22066 +* Fix - Recalculate coupon totals after adding a coupon to an order. #22580 +* Fix - Include refunded orders in top sellers, earners sales by product. #22581 +* Fix - Fix issue where "Any" attributes on variable products not always selectable on front end. #22067 +* Fix - Ensure partial refunds fire order.updated webhooks. #22072 +* Fix - Reload the cart page when the cart is empty. #22114 +* Fix - Always show the price filter widget when filtering products by price. #22303 +* Fix - Added body `{padding: 0;}` CSS rule to the email-styles.php to fix the iOS emails layout issue. #22309 +* Fix - Update variable product default attributes to reflect attribute terms slug edit. #22398 +* Fix - Adds all 3 callback arguments to the `woocommerce_order_item_display_meta_value` filter called from the `get_formatted_legacy` method of the WC_Order_Item_Meta class. #22411 +* Fix - Remove html from add coupon error alert during manual order entry. #22424 +* Fix - Include tax in subtotals when validating coupon minimum and maximum in manual order entry. #22464 +* Fix - Fix ssl check in case shop page no longer exists. #22531 +* Fix - Exclude `paged` from price slider and rating filter. #22533 +* Fix - Limit bulk variation percentage price adjustment to decimal places in pricing settings. #22537 +* Fix - Fix category image `name` field to be used for API POST/PUT. #22553 +* Fix - Fix remote request test in `get_environment_info()`. #22551 +* Fix - Fix notices when images have no metadata or their metadata is removed. #22562 +* Fix - Check for presence of 'save' entry in post data when determining whether to save settings. #22572 +* Fix - Additional CSS support for more input types on variations panel in admin. #22590 +* Fix - Over escaping rating widget html. #22593 +* Fix - Update cron sale price removal to remove the price at midnight after the sale ends. #22609 +* Fix - WC_Log_Handler_File::remove - fix for MS Windows #22624 +* Fix - Only require flat shipping rate when shipping method is enabled in the On-Boarding Wizard. #22599 +* Fix - Fix wrong variable check in `add_uncropped_metadata`. #22638 +* Fix - No alert for mis-matched password reset. #22642 +* Fix - Hold-stock behavior between simple products and variable products was different. #22646 +* Fix - OBW: Offer Storefront when WP 5.0 default theme is active #22649 +* Fix - Add novalidate attribute to payment form to prevent hidden fields preventing submission. #22662 +* Fix - Switch span to paragraph for descriptions in admin user profile view to correct spacing. #22663 +* Fix - Added POST variable check in product data meta box. #22681 +* Fix - PayPal item name encoding. #22684 +* Fix - Move PayPal BN partner ID. #22763 +* Fix - The "for" attribute of a label for a radio input is invalid in `woocommerce_form_field`. #22690 +* Fix - Custom payment options sections was not loading settings. #22704 +* Fix - Breadcrumbs on custom post types was using the singular name instead of plural. #22705 +* Fix - Fixed generate webhook signature when secret contains special chars. #22722 +* Fix - Set correct item meta after restocking items with refunds. #22729 +* Fix - Sales by Product to consistently calculate net sales counts and amounts. #22711 +* Fix - Importer - Variations cannot be drafts so set to private. #22736 +* Fix - Next/previous links for orders REST endpoint when `status` query parameter is present. #22741 +* Fix - Default value passed to sorting dropdown #22677 +* Tweak - Updates Mailchimp branding in setup wizard. #22514 +* Tweak - Refactor `@id` generation for product structured data to prevent plugin conflicts. #22554 +* Tweak - Keep count of the number of times custom coupons apply. #22529 +* Tweak - Change WooCommerce emails footer from `Powered by WooCommerce` to `Built with WooCommerce`. #22530 +* Security - Improved escaping for Photoswipe captions. +* Security - Improved escaping for JSON attributes and structured data. + += 3.5.4 - 2019-01-21 = +* Tweak - Allow limited html in woocommerce_rating_filter_count filter. #21904 +* Tweak - Remove 'on-hold' orders from admin tax reports for more logical reporting. #22419 +* Tweak - Remove payment phrases from processing emails. #22418 +* Tweak - Removed display of cost for local pickup when free. #22446 +* Fix - Unescape CSV formulas in product attributes in CSV importer/exporter. #21938 +* Fix - Remove use of non-existing `WC_REST_Dev_Setting_Options_Controller` class. #22121 +* Fix - Fix edge case where `get_plugins` would not have the custom WooCommerce plugin headers if `get_plugins` was called early. #21669 +* Fix - Prevent PHP warning when deprecated user meta starts with uppercase. #21943 +* Fix - Fixed support for multiple query parameters translated to meta queries via REST API requests. #22108 +* Fix - Prevent PHP errors when trying to access non-existant report tabs. #22183 +* Fix - Filter by attributes dropdown placeholder text should not be wrapped in quotes. #22185 +* Fix - Apply sale price until end of closing sale date. #22189 +* Fix - Allow empty schema again when registering a custom field for the API. #22204 +* Fix - Don't display escaped html on checkout when javascript is disabled. #22214 +* Fix - Fixed formatted address in uppercase for languages that use accents. #22096 +* Fix - Reload the cart page when the cart is empty when there is a hash in the URL. #22114 +* Fix - Do not schedule duplicate webhooks within 10 minutes of each other to maintain previous behavior. #22088 +* Fix - Return correct next scheduled date for items in queue by fixing date instantiation in WC_Action_Queue::get_next(). #22104 +* Fix - Allow products to use default low stock threshold. #22084 +* Fix - Fix 0 value attribute permalink calculation, property population in REST api. #22026 +* Fix - Ensure cache delete on coupon trash or delete. #22053 +* Fix - Ensure product parent exists before getting its image. #22074 +* Fix - Correctly use wildcard character on email restrictions on coupons. #22167 +* Fix - Avoids Warnings in Action Scheduler Library for PHP 5.2. #22160 +* Fix - Don't include product in BreadcrumbList structured data so Google will recognize stand-alone Product structured data. #22344 +* Fix - Fix Product widget showing hidden products when hide out of stock was enabled. #22230 +* Fix - Run webhook status updates through new wc_is_webhook_valid_status functions when doing API requests. #22205 +* Fix - Correct quote handling in tax class names. #22270 +* Fix - Prevent style side-effects on notices on the Extensions pages. #22330 +* Fix - Check stock status of items when 'ordering again' from the account page. #22331 +* Fix - Improve rounding when rounding at subtotal level in cart. #21217 +* Fix - Restores an opportunity to print non-cart related notices that a few extensions are relying on. #22337 +* Fix - Correct order item meta alignment in order emails when using an RTL language. #22376 +* Fix - Fix bug where product status was erroneously going to draft status in some circumstances on new published variable products. #20667 +* Fix - Load customer data for logged in users regardless of being member of sub-site to avoid errors. #22448 +* Fix - Use slug sanitization on product export category slugs for better foreign character support. #22320 +* Fix - Correct item subtotal rounding when multiple taxes are applied so it matches the cart. #22416 +* Fix - Prevent fatal errors when retrieving network orders for sites that do not have WooCommerce activated. #22439 +* Fix - Numerous bug fixes around checkout field locales on first load. #22444 +* Fix - Correct position of admin notices on my-account pages. #22445 +* Fix - Fixed padding of addresses in email template. #22466 +* Fix - Prevent payment method descriptions sliding up/down if selected after ajax updates. #22459 +* Fix - Fixed formatted address in uppercase for languages that use accents. #22096 +* Fix - Fix product updating on import for SKUs with special characters. #22071 +* Fix - Ensure cache_delete on coupon deletion. #22053 +* Fix - Make product edit form aware publish was pressed. #20667 +* Fix - Unescape imported CSV formulas in product attributes. #21938 +* Fix - Warning when deprecated user meta starts with uppercase. #21943 +* Fix - Filter out buttons from the onRowClick event on the Orders list view page. #21966 +* Fix - Update "Filter Products by Attribute" widget when product stock quantity changes via "Quick Edit" or WC API. #22029 +* Fix - Ensure product parent exists before getting its image. #22074 +* Fix - Fixed support for multiple query parameters translated to meta queries via REST API requests. #22108 +* Fix - Strip hash from URL when reload refunds in the dashboard. #22116 +* Fix - Prevent notice when using non existing value for tabs in WooCommerce > Reports > Stock. #22183 +* Fix - Filter by attributes dropdown placeholder text wrapped in quotes. #22185 +* Fix - Fix escaped html on checkout when javascript is disabled. #22214 +* Fix - Allow empty schema again when registering a custom field for the API. #22204 +* Fix - Fix import & export of newline characters in product description fields. #22298 +* Fix - Allow quotes in tax class names. #22270 +* Fix - Sale price applies to end of closing sale date. #22189 +* Fix - Product export by unicode product categories. #22320 +* Fix - Check stock status of items when 'ordering again' from the account page. #22331 +* Fix - Issue where images offloaded to external servers caused errors and broken images when changing aspect ratios. #22461 +* Fix - Remove block comments from shop page description. #22334 +* Dev - REST API - 0 value attribute permalink calculation, property population in REST api. #22026 +* Dev - REST API - Fixed support to order results by slugs. #22168 +* Dev - REST API - Removed extra inherited filters from product endpoint in variations endpoint. #22452 + = 3.5.3 - 2018-12-20 = * Fix - Fix orders list in the admin after a change introduced in WordPress 5.0.2. #22273 From 9529d6eee72510b1422d6e1a6d8d02b7a315acab Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 20 Feb 2019 16:22:20 +0000 Subject: [PATCH 328/401] Use total, not amount, when calculating fees --- includes/abstracts/abstract-wc-order.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index df37ba977a2..78d470ff72e 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -1444,7 +1444,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { $cart_subtotal = 0; $cart_total = 0; - $fee_total = 0; + $fees_total = 0; $shipping_total = 0; $cart_subtotal_tax = 0; $cart_total_tax = 0; @@ -1464,18 +1464,17 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { // Sum fee costs. foreach ( $this->get_fees() as $item ) { - $amount = $item->get_amount(); + $fee_total = $item->get_total(); - if ( 0 > $amount ) { - $item->set_total( $amount ); - $max_discount = round( $cart_total + $fee_total + $shipping_total, wc_get_price_decimals() ) * -1; + if ( 0 > $fee_total ) { + $max_discount = round( $cart_total + $fees_total + $shipping_total, wc_get_price_decimals() ) * -1; - if ( $item->get_total() < $max_discount ) { + if ( $fee_total < $max_discount ) { $item->set_total( $max_discount ); } } - $fee_total += $item->get_total(); + $fees_total += $item->get_total(); } // Calculate taxes for items, shipping, discounts. Note; this also triggers save(). @@ -1498,7 +1497,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { $this->set_discount_total( $cart_subtotal - $cart_total ); $this->set_discount_tax( wc_round_tax_total( $cart_subtotal_tax - $cart_total_tax ) ); - $this->set_total( round( $cart_total + $fee_total + $this->get_shipping_total() + $this->get_cart_tax() + $this->get_shipping_tax(), wc_get_price_decimals() ) ); + $this->set_total( round( $cart_total + $fees_total + $this->get_shipping_total() + $this->get_cart_tax() + $this->get_shipping_tax(), wc_get_price_decimals() ) ); do_action( 'woocommerce_order_after_calculate_totals', $and_taxes, $this ); From 76b7a4facf3e2b7190222feb8fcdf665a926fcee Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 20 Feb 2019 16:37:40 +0000 Subject: [PATCH 329/401] cs --- assets/js/frontend/single-product.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/assets/js/frontend/single-product.js b/assets/js/frontend/single-product.js index e80b46711f8..e4ba732e3f7 100644 --- a/assets/js/frontend/single-product.js +++ b/assets/js/frontend/single-product.js @@ -44,7 +44,19 @@ jQuery( function( $ ) { } ) // Star ratings for comments .on( 'init', '#rating', function() { - $( '#rating' ).hide().before( '

    12345

    ' ); + $( '#rating' ) + .hide() + .before( + '

    \ + \ + 1\ + 2\ + 3\ + 4\ + 5\ + \ +

    ' + ); } ) .on( 'click', '#respond p.stars a', function() { var $star = $( this ), @@ -307,12 +319,12 @@ jQuery( function( $ ) { * Initialize all galleries on page. */ $( '.woocommerce-product-gallery' ).each( function() { - + $( this ).trigger( 'wc-product-gallery-before-init', [ this, wc_single_product_params ] ); - + $( this ).wc_product_gallery( wc_single_product_params ); - + $( this ).trigger( 'wc-product-gallery-after-init', [ this, wc_single_product_params ] ); - + } ); } ); From efaa723a5be643b2282c6a2fb4be5af51d358610 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Wed, 20 Feb 2019 13:55:51 -0300 Subject: [PATCH 330/401] Only set user first and last names when those fields are empty --- includes/class-wc-checkout.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-checkout.php b/includes/class-wc-checkout.php index 50a277b8091..6b85cf2d118 100644 --- a/includes/class-wc-checkout.php +++ b/includes/class-wc-checkout.php @@ -989,17 +989,17 @@ class WC_Checkout { if ( $customer_id && apply_filters( 'woocommerce_checkout_update_customer_data', true, $this ) ) { $customer = new WC_Customer( $customer_id ); - if ( ! empty( $data['billing_first_name'] ) ) { + if ( ! empty( $data['billing_first_name'] ) && '' === $customer->get_first_name() ) { $customer->set_first_name( $data['billing_first_name'] ); } - if ( ! empty( $data['billing_last_name'] ) ) { + if ( ! empty( $data['billing_last_name'] ) && '' === $customer->get_last_name() ) { $customer->set_last_name( $data['billing_last_name'] ); } // If the display name is an email, update to the user's full name. if ( is_email( $customer->get_display_name() ) ) { - $customer->set_display_name( $data['billing_first_name'] . ' ' . $data['billing_last_name'] ); + $customer->set_display_name( $customer->get_first_name() . ' ' . $customer->get_last_name() ); } foreach ( $data as $key => $value ) { From fc1a274fcbaf9e463f5181dc999570602bc810f5 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 20 Feb 2019 17:22:43 +0000 Subject: [PATCH 331/401] Check for hover after timeout --- assets/js/frontend/single-product.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/assets/js/frontend/single-product.js b/assets/js/frontend/single-product.js index e4ba732e3f7..c31fa2dc60b 100644 --- a/assets/js/frontend/single-product.js +++ b/assets/js/frontend/single-product.js @@ -222,6 +222,12 @@ jQuery( function( $ ) { zoomTarget.trigger( 'zoom.destroy' ); zoomTarget.zoom( zoom_options ); + + setTimeout( function() { + if ( zoomTarget.find(':hover').length ) { + zoomTarget.trigger( 'mouseover' ); + } + }, 100 ); } }; From 8b8498a37ab6f5749f0458b107e486198d2c40b3 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Wed, 20 Feb 2019 16:18:43 -0300 Subject: [PATCH 332/401] Included extra argument into wc_create_new_customer Allows passing extra arguments to wp_insert_user() function --- includes/wc-user-functions.php | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/includes/wc-user-functions.php b/includes/wc-user-functions.php index 07ff413f28f..2a98693d22c 100644 --- a/includes/wc-user-functions.php +++ b/includes/wc-user-functions.php @@ -32,12 +32,13 @@ if ( ! function_exists( 'wc_create_new_customer' ) ) { /** * Create a new customer. * - * @param string $email Customer email. + * @param string $email Customer email. * @param string $username Customer username. * @param string $password Customer password. + * @param array $args List of arguments to pass to `wp_insert_user()`. * @return int|WP_Error Returns WP_Error on failure, Int (user ID) on success. */ - function wc_create_new_customer( $email, $username = '', $password = '' ) { + function wc_create_new_customer( $email, $username = '', $password = '', $args = array() ) { // Check the email address. if ( empty( $email ) || ! is_email( $email ) ) { @@ -96,11 +97,14 @@ if ( ! function_exists( 'wc_create_new_customer' ) ) { $new_customer_data = apply_filters( 'woocommerce_new_customer_data', - array( - 'user_login' => $username, - 'user_pass' => $password, - 'user_email' => $email, - 'role' => 'customer', + array_merge( + $args, + array( + 'user_login' => $username, + 'user_pass' => $password, + 'user_email' => $email, + 'role' => 'customer', + ) ) ); From 3ac5f32a8b8d3d536e2d618233cc4d158b095b28 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Wed, 20 Feb 2019 16:29:10 -0300 Subject: [PATCH 333/401] Updated tests for wc_create_new_customer --- tests/unit-tests/customer/functions.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/unit-tests/customer/functions.php b/tests/unit-tests/customer/functions.php index e3ac9871e60..86815e95ed1 100644 --- a/tests/unit-tests/customer/functions.php +++ b/tests/unit-tests/customer/functions.php @@ -45,6 +45,19 @@ class WC_Tests_Customer_Functions extends WC_Unit_Test_Case { $userdata = get_userdata( $id ); $this->assertEquals( 'fred2', $userdata->user_login ); + // Test extra arguments to generate display_name. + $id = wc_create_new_customer( + 'john.doe@example.com', + '', + 'testpassword', + array( + 'first_name' => 'John', + 'last_name' => 'Doe' + ) + ); + $userdata = get_userdata( $id ); + $this->assertEquals( 'John Doe', $userdata->display_name ); + // No password. update_option( 'woocommerce_registration_generate_password', 'no' ); $id = wc_create_new_customer( 'joe@example.com', 'joecustomer', '' ); From 5302434057e5d1b6af67a0c1f4ef872924524022 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Wed, 20 Feb 2019 16:29:34 -0300 Subject: [PATCH 334/401] Fill user first and last name during checkout --- includes/class-wc-checkout.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-checkout.php b/includes/class-wc-checkout.php index 50a277b8091..526fb10fa21 100644 --- a/includes/class-wc-checkout.php +++ b/includes/class-wc-checkout.php @@ -965,7 +965,15 @@ class WC_Checkout { if ( ! is_user_logged_in() && ( $this->is_registration_required() || ! empty( $data['createaccount'] ) ) ) { $username = ! empty( $data['account_username'] ) ? $data['account_username'] : ''; $password = ! empty( $data['account_password'] ) ? $data['account_password'] : ''; - $customer_id = wc_create_new_customer( $data['billing_email'], $username, $password ); + $customer_id = wc_create_new_customer( + $data['billing_email'], + $username, + $password, + array( + 'first_name' => ! empty( $data['billing_first_name'] ) ? $data['billing_first_name'] : '', + 'last_name' => ! empty( $data['billing_last_name'] ) ? $data['billing_last_name'] : '', + ) + ); if ( is_wp_error( $customer_id ) ) { throw new Exception( $customer_id->get_error_message() ); From 79847ccb93ac38cd21798b969dbd1f39bdffdab7 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Wed, 20 Feb 2019 16:30:46 -0300 Subject: [PATCH 335/401] Fixed coding standards --- includes/wc-user-functions.php | 4 +--- tests/unit-tests/customer/functions.php | 12 ++++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/includes/wc-user-functions.php b/includes/wc-user-functions.php index 2a98693d22c..fda2c738a6f 100644 --- a/includes/wc-user-functions.php +++ b/includes/wc-user-functions.php @@ -384,9 +384,7 @@ add_filter( 'user_has_cap', 'wc_customer_has_capability', 10, 3 ); function wc_shop_manager_has_capability( $allcaps, $caps, $args, $user ) { if ( wc_user_has_role( $user, 'shop_manager' ) ) { - /** - * @see wc_modify_map_meta_cap, which limits editing to customers. - */ + // @see wc_modify_map_meta_cap, which limits editing to customers. $allcaps['edit_users'] = true; } diff --git a/tests/unit-tests/customer/functions.php b/tests/unit-tests/customer/functions.php index 86815e95ed1..812ed590a99 100644 --- a/tests/unit-tests/customer/functions.php +++ b/tests/unit-tests/customer/functions.php @@ -1,8 +1,12 @@ 'John', - 'last_name' => 'Doe' + 'last_name' => 'Doe', ) ); $userdata = get_userdata( $id ); @@ -83,7 +87,7 @@ class WC_Tests_Customer_Functions extends WC_Unit_Test_Case { $order2 = new WC_Order(); $order2->save(); - // Test download permissions + // Test download permissions. $prod_download = new WC_Product_Download(); $prod_download->set_file( plugin_dir_url( __FILE__ ) . '/assets/images/help.png' ); $prod_download->set_id( 'download' ); From 0f771bf4fe75dff21998364aee33cbceb3284e57 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Thu, 21 Feb 2019 12:50:12 +0200 Subject: [PATCH 336/401] Remove old hooks for woocommerce_theme_background_installer and woocommerce_plugin_background_installer. There are no scheduled events for these anymore and the Wizard has it's own theme/plugin installer that uses the shutdown hook instead of cron. --- includes/class-wc-install.php | 216 ---------------------------------- 1 file changed, 216 deletions(-) diff --git a/includes/class-wc-install.php b/includes/class-wc-install.php index 79983d1635f..160e89d7a08 100644 --- a/includes/class-wc-install.php +++ b/includes/class-wc-install.php @@ -144,8 +144,6 @@ class WC_Install { add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 ); add_filter( 'wpmu_drop_tables', array( __CLASS__, 'wpmu_drop_tables' ) ); add_filter( 'cron_schedules', array( __CLASS__, 'cron_schedules' ) ); - add_action( 'woocommerce_plugin_background_installer', array( __CLASS__, 'background_installer' ), 10, 2 ); - add_action( 'woocommerce_theme_background_installer', array( __CLASS__, 'theme_background_installer' ), 10, 1 ); } /** @@ -1222,154 +1220,6 @@ CREATE TABLE {$wpdb->prefix}woocommerce_termmeta ( return $plugins; } - /** - * Install a plugin from .org in the background via a cron job (used by - * installer - opt in). - * - * @param string $plugin_to_install_id Plugin ID. - * @param array $plugin_to_install Plugin information. - * - * @throws Exception If unable to proceed with plugin installation. - * @since 2.6.0 - */ - public static function background_installer( $plugin_to_install_id, $plugin_to_install ) { - // Explicitly clear the event. - $args = func_get_args(); - wp_clear_scheduled_hook( 'woocommerce_plugin_background_installer', $args ); - - if ( ! empty( $plugin_to_install['repo-slug'] ) ) { - require_once ABSPATH . 'wp-admin/includes/file.php'; - require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; - require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - - WP_Filesystem(); - - $skin = new Automatic_Upgrader_Skin(); - $upgrader = new WP_Upgrader( $skin ); - $installed_plugins = array_reduce( array_keys( get_plugins() ), array( __CLASS__, 'associate_plugin_file' ) ); - if ( empty( $installed_plugins ) ) { - $installed_plugins = array(); - } - $plugin_slug = $plugin_to_install['repo-slug']; - $plugin_file = isset( $plugin_to_install['file'] ) ? $plugin_to_install['file'] : $plugin_slug . '.php'; - $installed = false; - $activate = false; - - // See if the plugin is installed already. - if ( isset( $installed_plugins[ $plugin_file ] ) ) { - $installed = true; - $activate = ! is_plugin_active( $installed_plugins[ $plugin_file ] ); - } - - // Install this thing! - if ( ! $installed ) { - // Suppress feedback. - ob_start(); - - try { - $plugin_information = plugins_api( - 'plugin_information', - array( - 'slug' => $plugin_slug, - 'fields' => array( - 'short_description' => false, - 'sections' => false, - 'requires' => false, - 'rating' => false, - 'ratings' => false, - 'downloaded' => false, - 'last_updated' => false, - 'added' => false, - 'tags' => false, - 'homepage' => false, - 'donate_link' => false, - 'author_profile' => false, - 'author' => false, - ), - ) - ); - - if ( is_wp_error( $plugin_information ) ) { - throw new Exception( $plugin_information->get_error_message() ); - } - - $package = $plugin_information->download_link; - $download = $upgrader->download_package( $package ); - - if ( is_wp_error( $download ) ) { - throw new Exception( $download->get_error_message() ); - } - - $working_dir = $upgrader->unpack_package( $download, true ); - - if ( is_wp_error( $working_dir ) ) { - throw new Exception( $working_dir->get_error_message() ); - } - - $result = $upgrader->install_package( - array( - 'source' => $working_dir, - 'destination' => WP_PLUGIN_DIR, - 'clear_destination' => false, - 'abort_if_destination_exists' => false, - 'clear_working' => true, - 'hook_extra' => array( - 'type' => 'plugin', - 'action' => 'install', - ), - ) - ); - - if ( is_wp_error( $result ) ) { - throw new Exception( $result->get_error_message() ); - } - - $activate = true; - - } catch ( Exception $e ) { - WC_Admin_Notices::add_custom_notice( - $plugin_to_install_id . '_install_error', - sprintf( - // translators: 1: plugin name, 2: error message, 3: URL to install plugin manually. - __( '%1$s could not be installed (%2$s). Please install it manually by clicking here.', 'woocommerce' ), - $plugin_to_install['name'], - $e->getMessage(), - esc_url( admin_url( 'index.php?wc-install-plugin-redirect=' . $plugin_slug ) ) - ) - ); - } - - // Discard feedback. - ob_end_clean(); - } - - wp_clean_plugins_cache(); - - // Activate this thing. - if ( $activate ) { - try { - add_action( 'add_option_mailchimp_woocommerce_plugin_do_activation_redirect', array( __CLASS__, 'remove_mailchimps_redirect' ), 10, 2 ); - $result = activate_plugin( $installed ? $installed_plugins[ $plugin_file ] : $plugin_slug . '/' . $plugin_file ); - - if ( is_wp_error( $result ) ) { - throw new Exception( $result->get_error_message() ); - } - } catch ( Exception $e ) { - WC_Admin_Notices::add_custom_notice( - $plugin_to_install_id . '_install_error', - sprintf( - // translators: 1: plugin name, 2: URL to WP plugin page. - __( '%1$s was installed but could not be activated. Please activate it manually by clicking here.', 'woocommerce' ), - $plugin_to_install['name'], - admin_url( 'plugins.php' ) - ) - ); - } - } - } - } - /** * Removes redirect added during MailChimp plugin's activation. * @@ -1383,72 +1233,6 @@ CREATE TABLE {$wpdb->prefix}woocommerce_termmeta ( // Update redirect back to false. update_option( 'mailchimp_woocommerce_plugin_do_activation_redirect', false ); } - - /** - * Install a theme from .org in the background via a cron job (used by installer - opt in). - * - * @param string $theme_slug Theme slug. - * - * @throws Exception If unable to proceed with theme installation. - * @since 3.1.0 - */ - public static function theme_background_installer( $theme_slug ) { - // Explicitly clear the event. - $args = func_get_args(); - wp_clear_scheduled_hook( 'woocommerce_theme_background_installer', $args ); - - if ( ! empty( $theme_slug ) ) { - // Suppress feedback. - ob_start(); - - try { - $theme = wp_get_theme( $theme_slug ); - - if ( ! $theme->exists() ) { - require_once ABSPATH . 'wp-admin/includes/file.php'; - include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; - include_once ABSPATH . 'wp-admin/includes/theme.php'; - - WP_Filesystem(); - - $skin = new Automatic_Upgrader_Skin(); - $upgrader = new Theme_Upgrader( $skin ); - $api = themes_api( - 'theme_information', - array( - 'slug' => $theme_slug, - 'fields' => array( 'sections' => false ), - ) - ); - $result = $upgrader->install( $api->download_link ); - - if ( is_wp_error( $result ) ) { - throw new Exception( $result->get_error_message() ); - } elseif ( is_wp_error( $skin->result ) ) { - throw new Exception( $skin->result->get_error_message() ); - } elseif ( is_null( $result ) ) { - throw new Exception( 'Unable to connect to the filesystem. Please confirm your credentials.' ); - } - } - - switch_theme( $theme_slug ); - } catch ( Exception $e ) { - WC_Admin_Notices::add_custom_notice( - $theme_slug . '_install_error', - sprintf( - // translators: 1: theme slug, 2: error message, 3: URL to install theme manually. - __( '%1$s could not be installed (%2$s). Please install it manually by clicking here.', 'woocommerce' ), - $theme_slug, - $e->getMessage(), - esc_url( admin_url( 'update.php?action=install-theme&theme=' . $theme_slug . '&_wpnonce=' . wp_create_nonce( 'install-theme_' . $theme_slug ) ) ) - ) - ); - } - - // Discard feedback. - ob_end_clean(); - } - } } WC_Install::init(); From d30221b256317b52ce67206bbb6bafcf96e3c0d9 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 21 Feb 2019 20:15:06 +0000 Subject: [PATCH 337/401] Update dependency mocha to v6.0.1 --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index d4c77e93479..5ac4ef93d01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5171,9 +5171,9 @@ } }, "homedir-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", - "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, "requires": { "parse-passwd": "^1.0.0" @@ -7208,9 +7208,9 @@ } }, "mocha": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.0.0.tgz", - "integrity": "sha512-A7g9k3yr8oJaXn2IItFnfgjyxFc/LTe6Wwv7FczP+e8G74o9xYNSbMYmCf1ouldRojLrFcOb+z75P6Ak0GX6ug==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.0.1.tgz", + "integrity": "sha512-tQzCxWqxSD6Oyg5r7Ptbev0yAMD8p+Vfh4snPFuiUsWqYj0eVYTDT2DkEY307FTj0WRlIWN9rWMMAUzRmijgVQ==", "dev": true, "requires": { "ansi-colors": "3.2.3", diff --git a/package.json b/package.json index ebc43e305d3..79a1f86c3fe 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "husky": "1.3.1", "istanbul": "1.0.0-alpha.2", "lint-staged": "8.1.4", - "mocha": "6.0.0", + "mocha": "6.0.1", "node-sass": "4.11.0", "prettier": "github:automattic/calypso-prettier#c56b4251", "stylelint": "9.10.1", From d07d1cf0b0b98142a7519d33e3200c67c2e8a957 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 22 Feb 2019 13:30:49 +0000 Subject: [PATCH 338/401] If taxes are enabled, make the refund box readonly --- .../meta-boxes/views/html-order-items.php | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/includes/admin/meta-boxes/views/html-order-items.php b/includes/admin/meta-boxes/views/html-order-items.php index a641743ef66..e54e08b283f 100644 --- a/includes/admin/meta-boxes/views/html-order-items.php +++ b/includes/admin/meta-boxes/views/html-order-items.php @@ -265,14 +265,30 @@ if ( wc_tax_enabled() ) { get_total() - $order->get_total_refunded(), array( 'currency' => $order->get_currency() ) ); // WPCS: XSS ok. ?> - + + + - + />
    - + + +
    From 27ac2e157765b61248139c71e213e819c9e05e78 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 22 Feb 2019 13:33:41 +0000 Subject: [PATCH 339/401] phpcs --- .../meta-boxes/views/html-order-items.php | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/includes/admin/meta-boxes/views/html-order-items.php b/includes/admin/meta-boxes/views/html-order-items.php index e54e08b283f..7cfe3093d8c 100644 --- a/includes/admin/meta-boxes/views/html-order-items.php +++ b/includes/admin/meta-boxes/views/html-order-items.php @@ -105,19 +105,24 @@ if ( wc_tax_enabled() ) {
  • $item ) : - $post_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish' LIMIT 1;", $item->get_code() ) ); + $post_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish' LIMIT 1;", $item->get_code() ) ); // phpcs:disable WordPress.WP.GlobalVariablesOverride.OverrideProhibited $class = $order->is_editable() ? 'code editable' : 'code'; ?>
  • $post_id, - 'action' => 'edit', + $post_url = apply_filters( + 'woocommerce_admin_order_item_coupon_url', + add_query_arg( + array( + 'post' => $post_id, + 'action' => 'edit', + ), + admin_url( 'post.php' ) ), - admin_url( 'post.php' ) - ), $item, $order ); + $item, + $order + ); ?> get_code() ); ?> @@ -156,11 +161,11 @@ if ( wc_tax_enabled() ) { get_total_shipping_refunded(); if ( $refunded > 0 ) { - echo '' . strip_tags( wc_price( $order->get_shipping_total(), array( 'currency' => $order->get_currency() ) ) ) . ' ' . wc_price( $order->get_shipping_total() - $refunded, array( 'currency' => $order->get_currency() ) ) . ''; // WPCS: XSS ok. + echo '' . wp_strip_all_tags( wc_price( $order->get_shipping_total(), array( 'currency' => $order->get_currency() ) ) ) . ' ' . wc_price( $order->get_shipping_total() - $refunded, array( 'currency' => $order->get_currency() ) ) . ''; // WPCS: XSS ok. } else { echo wc_price( $order->get_shipping_total(), array( 'currency' => $order->get_currency() ) ); // WPCS: XSS ok. } - ?> + ?> @@ -168,19 +173,19 @@ if ( wc_tax_enabled() ) { get_id() ); ?> - get_tax_totals() as $code => $tax ) : ?> + get_tax_totals() as $code => $tax_total ) : ?> - label ); ?>: + label ); ?>: get_total_tax_refunded_by_rate_id( $tax->rate_id ); + $refunded = $order->get_total_tax_refunded_by_rate_id( $tax_total->rate_id ); if ( $refunded > 0 ) { - echo '' . strip_tags( $tax->formatted_amount ) . ' ' . wc_price( WC_Tax::round( $tax->amount, wc_get_price_decimals() ) - WC_Tax::round( $refunded, wc_get_price_decimals() ), array( 'currency' => $order->get_currency() ) ) . ''; // WPCS: XSS ok. + echo '' . wp_strip_all_tags( $tax_total->formatted_amount ) . ' ' . wc_price( WC_Tax::round( $tax_total->amount, wc_get_price_decimals() ) - WC_Tax::round( $refunded, wc_get_price_decimals() ), array( 'currency' => $order->get_currency() ) ) . ''; // WPCS: XSS ok. } else { - echo wp_kses_post( $tax->formatted_amount ); + echo wp_kses_post( $tax_total->formatted_amount ); } - ?> + ?> @@ -272,13 +277,15 @@ if ( wc_tax_enabled() ) { - /> + ?> + />
    From f741d507ba818f13b202dddcdbed7ade124725a3 Mon Sep 17 00:00:00 2001 From: Naveen giri <1naveengiri@gmail.com> Date: Fri, 22 Feb 2019 19:13:35 +0530 Subject: [PATCH 340/401] Bugfix/#22821: Add Fix for Warning and Notices on save attribute button click --- includes/class-wc-ajax.php | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php index 2ba970ab370..086a381f9e6 100644 --- a/includes/class-wc-ajax.php +++ b/includes/class-wc-ajax.php @@ -632,21 +632,22 @@ class WC_AJAX { ob_start(); $attributes = $product->get_attributes( 'edit' ); $i = -1; + if( isset( $data['attribute_names'] ) && !empty( $data['attribute_names'] ) ){ + foreach ( $data['attribute_names'] as $attribute_name ) { + $attribute = isset( $attributes[ sanitize_title( $attribute_name ) ] ) ? $attributes[ sanitize_title( $attribute_name ) ] : false; + if ( ! $attribute ) { + continue; + } + $i++; + $metabox_class = array(); - foreach ( $data['attribute_names'] as $attribute_name ) { - $attribute = isset( $attributes[ sanitize_title( $attribute_name ) ] ) ? $attributes[ sanitize_title( $attribute_name ) ] : false; - if ( ! $attribute ) { - continue; + if ( $attribute->is_taxonomy() ) { + $metabox_class[] = 'taxonomy'; + $metabox_class[] = $attribute->get_name(); + } + + include( 'admin/meta-boxes/views/html-product-attribute.php' ); } - $i++; - $metabox_class = array(); - - if ( $attribute->is_taxonomy() ) { - $metabox_class[] = 'taxonomy'; - $metabox_class[] = $attribute->get_name(); - } - - include( 'admin/meta-boxes/views/html-product-attribute.php' ); } $response['html'] = ob_get_clean(); From 5f87fa49e15394be4a4ab23934f8e627b59a3d16 Mon Sep 17 00:00:00 2001 From: Tarik Causevic Date: Fri, 22 Feb 2019 17:39:58 +0100 Subject: [PATCH 341/401] Correct invalid param type from id to int --- includes/data-stores/class-wc-payment-token-data-store.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/data-stores/class-wc-payment-token-data-store.php b/includes/data-stores/class-wc-payment-token-data-store.php index 18513eb1daa..4e8aa18c4f7 100644 --- a/includes/data-stores/class-wc-payment-token-data-store.php +++ b/includes/data-stores/class-wc-payment-token-data-store.php @@ -287,7 +287,7 @@ class WC_Payment_Token_Data_Store extends WC_Data_Store_WP implements WC_Payment * Should contain the fields token_id, gateway_id, token, user_id, type, is_default. * * @since 3.0.0 - * @param id $user_id User ID. + * @param int $user_id User ID. * @return object */ public function get_users_default_token( $user_id ) { From e21912294a9faa693b376dfdd6ab921e8d4aa102 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 22 Feb 2019 16:43:42 +0000 Subject: [PATCH 342/401] calc shipping before totals --- includes/class-wc-ajax.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php index 2ba970ab370..d82107faa98 100644 --- a/includes/class-wc-ajax.php +++ b/includes/class-wc-ajax.php @@ -333,6 +333,9 @@ class WC_AJAX { } WC()->customer->save(); + + // Calculate shipping before totals. This will ensure any shipping methods that affect things like taxes are chosen prior to final totals being calculated. Ref: #22708 + WC()->cart->calculate_shipping(); WC()->cart->calculate_totals(); // Get order review fragment. From ca316d89335c53d95d8db5fdaa7affdc904ed351 Mon Sep 17 00:00:00 2001 From: Valerie Date: Thu, 13 Dec 2018 13:39:25 -0500 Subject: [PATCH 343/401] OBW: Remove customized blurb on recommended step --- includes/admin/class-wc-admin-setup-wizard.php | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/includes/admin/class-wc-admin-setup-wizard.php b/includes/admin/class-wc-admin-setup-wizard.php index e824bd0734f..f8aa77ac216 100644 --- a/includes/admin/class-wc-admin-setup-wizard.php +++ b/includes/admin/class-wc-admin-setup-wizard.php @@ -1868,20 +1868,9 @@ class WC_Admin_Setup_Wizard { public function wc_setup_recommended() { ?>

    -

    should_show_theme() - && $this->should_show_automated_tax() - && $this->should_show_mailchimp() - ) : - esc_html_e( 'Select from the list below to enable automated taxes and MailChimp’s best-in-class email services — and design your store with our official, free WooCommerce theme.', 'woocommerce' ); - else : - esc_html_e( 'Enhance your store with these recommended features.', 'woocommerce' ); - endif; - ?>

    +

    + +

    @@ -1930,6 +1957,7 @@ class WC_Admin_Setup_Wizard { $setup_storefront = isset( $_POST['setup_storefront_theme'] ) && 'yes' === $_POST['setup_storefront_theme']; $setup_automated_tax = isset( $_POST['setup_automated_taxes'] ) && 'yes' === $_POST['setup_automated_taxes']; $setup_mailchimp = isset( $_POST['setup_mailchimp'] ) && 'yes' === $_POST['setup_mailchimp']; + $setup_facebook = isset( $_POST['setup_facebook'] ) && 'yes' === $_POST['setup_facebook']; update_option( 'woocommerce_calc_taxes', $setup_automated_tax ? 'yes' : 'no' ); update_option( 'woocommerce_setup_automated_taxes', $setup_automated_tax ); @@ -1956,6 +1984,16 @@ class WC_Admin_Setup_Wizard { ); } + if ( $setup_facebook ) { + $this->install_plugin( + 'facebook-for-woocommerce', + array( + 'name' => __( 'Facebook for WooCommerce', 'woocommerce' ), + 'repo-slug' => 'facebook-for-woocommerce', + ) + ); + } + wp_redirect( esc_url_raw( $this->get_next_step_link() ) ); exit; } From 6aeaf90155d1402929d60e57ad3dc9a2caf3f9d6 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 25 Feb 2019 03:54:24 +0000 Subject: [PATCH 346/401] Update dependency autoprefixer to v9.4.9 --- package-lock.json | 28 ++++++++++++++-------------- package.json | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5ac4ef93d01..5256d8c1a8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -641,13 +641,13 @@ "dev": true }, "autoprefixer": { - "version": "9.4.8", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.8.tgz", - "integrity": "sha512-DIhd0KMi9Nql3oJkJ2HCeOVihrXFPtWXc6ckwaUNwliDOt9OGr0fk8vV8jCLWXnZc1EXvQ2uLUzGpcPxFAQHEQ==", + "version": "9.4.9", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.9.tgz", + "integrity": "sha512-OyUl7KvbGBoFQbGQu51hMywz1aaVeud/6uX8r1R1DNcqFvqGUUy6+BDHnAZE8s5t5JyEObaSw+O1DpAdjAmLuw==", "dev": true, "requires": { - "browserslist": "^4.4.1", - "caniuse-lite": "^1.0.30000938", + "browserslist": "^4.4.2", + "caniuse-lite": "^1.0.30000939", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", "postcss": "^7.0.14", @@ -2058,14 +2058,14 @@ "dev": true }, "browserslist": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.1.tgz", - "integrity": "sha512-pEBxEXg7JwaakBXjATYw/D1YZh4QUSCX/Mnd/wnqSRPPSi1U39iDhDoKGoBUcraKdxDlrYqJxSI5nNvD+dWP2A==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.2.tgz", + "integrity": "sha512-ISS/AIAiHERJ3d45Fz0AVYKkgcy+F/eJHzKEvv1j0wwKGKD9T3BrwKr/5g45L+Y4XIK5PlTqefHciRFcfE1Jxg==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000929", - "electron-to-chromium": "^1.3.103", - "node-releases": "^1.1.3" + "caniuse-lite": "^1.0.30000939", + "electron-to-chromium": "^1.3.113", + "node-releases": "^1.1.8" } }, "buffer-from": { @@ -2166,9 +2166,9 @@ } }, "caniuse-lite": { - "version": "1.0.30000938", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000938.tgz", - "integrity": "sha512-ekW8NQ3/FvokviDxhdKLZZAx7PptXNwxKgXtnR5y+PR3hckwuP3yJ1Ir+4/c97dsHNqtAyfKUGdw8P4EYzBNgw==", + "version": "1.0.30000939", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000939.tgz", + "integrity": "sha512-oXB23ImDJOgQpGjRv1tCtzAvJr4/OvrHi5SO2vUgB0g0xpdZZoA/BxfImiWfdwoYdUTtQrPsXsvYU/dmCSM8gg==", "dev": true }, "caseless": { diff --git a/package.json b/package.json index 79a1f86c3fe..1fe665c69c7 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "git:update-hooks": "rm -r .git/hooks && mkdir -p .git/hooks && node ./node_modules/husky/husky.js install" }, "devDependencies": { - "autoprefixer": "9.4.8", + "autoprefixer": "9.4.9", "babel": "6.23.0", "babel-cli": "6.26.0", "babel-eslint": "10.0.1", From 8a9990f98d618699e275aac6378f75801589d9c9 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Mon, 25 Feb 2019 06:01:38 +0100 Subject: [PATCH 347/401] [ADD] Do Action before cart is emptied --- includes/class-wc-cart.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/class-wc-cart.php b/includes/class-wc-cart.php index 4d3fb52b4a6..dea6f2d433e 100644 --- a/includes/class-wc-cart.php +++ b/includes/class-wc-cart.php @@ -634,6 +634,7 @@ class WC_Cart extends WC_Legacy_Cart { * @param bool $clear_persistent_cart Should the persistant cart be cleared too. Defaults to true. */ public function empty_cart( $clear_persistent_cart = true ) { + do_action( 'woocommerce_empty_cart' ); $this->cart_contents = array(); $this->removed_cart_contents = array(); $this->shipping_methods = array(); From ee621eec8ac5a67207ed4b2ac661e3c33374fbeb Mon Sep 17 00:00:00 2001 From: nishitlangaliya Date: Mon, 25 Feb 2019 19:03:57 +0530 Subject: [PATCH 348/401] fix: filter prefix changed and unit test added for fn:wc_get_product_stock_status_options --- includes/wc-product-functions.php | 2 +- tests/unit-tests/product/functions.php | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index bf94552a52e..7f13dd7476a 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -848,7 +848,7 @@ function wc_get_product_tax_class_options() { * @return array */ function wc_get_product_stock_status_options() { - return apply_filters( 'wc_product_stock_status_options', array( + return apply_filters( 'woocommerce_product_stock_status_options', array( 'instock' => __( 'In stock', 'woocommerce' ), 'outofstock' => __( 'Out of stock', 'woocommerce' ), 'onbackorder' => __( 'On backorder', 'woocommerce' ), diff --git a/tests/unit-tests/product/functions.php b/tests/unit-tests/product/functions.php index 4ec1ccd06fc..b2646f60017 100644 --- a/tests/unit-tests/product/functions.php +++ b/tests/unit-tests/product/functions.php @@ -1028,4 +1028,21 @@ class WC_Tests_Product_Functions extends WC_Unit_Test_Case { unset( $image_attr, $expected_attr ); } + + /** + * Test wc_get_product_stock_status_options(). + * + * @since 3.6.0 + */ + public function test_wc_get_product_stock_status_options() { + $status_options = (array) apply_filters( + 'woocommerce_product_stock_status_options', array( + 'instock' => 'In stock', + 'outofstock' => 'Out of stock', + 'onbackorder' => 'On backorder', + ) + ); + + $this->assertEquals( $status_options, wc_get_product_stock_status_options() ); + } } From 185270d6967b0d77cb331e7fbe3c4720b2213f2c Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 25 Feb 2019 18:26:18 +0000 Subject: [PATCH 349/401] Update dependency mocha to v6.0.2 --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5ac4ef93d01..0c3d5c9b773 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7208,9 +7208,9 @@ } }, "mocha": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.0.1.tgz", - "integrity": "sha512-tQzCxWqxSD6Oyg5r7Ptbev0yAMD8p+Vfh4snPFuiUsWqYj0eVYTDT2DkEY307FTj0WRlIWN9rWMMAUzRmijgVQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.0.2.tgz", + "integrity": "sha512-RtTJsmmToGyeTznSOMoM6TPEk1A84FQaHIciKrRqARZx+B5ccJ5tXlmJzEKGBxZdqk9UjpRsesZTUkZmR5YnuQ==", "dev": true, "requires": { "ansi-colors": "3.2.3", diff --git a/package.json b/package.json index 79a1f86c3fe..5e2f7e10f2a 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "husky": "1.3.1", "istanbul": "1.0.0-alpha.2", "lint-staged": "8.1.4", - "mocha": "6.0.1", + "mocha": "6.0.2", "node-sass": "4.11.0", "prettier": "github:automattic/calypso-prettier#c56b4251", "stylelint": "9.10.1", From 80aee99fee034211c7dced100f7bec29c2b2f56b Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Mon, 25 Feb 2019 15:49:14 -0300 Subject: [PATCH 350/401] Prevent a few structured data warnings This prevents warnings about 'image', 'description' and 'sku'. 'brand' included as empty fields just to register. Note that warnings still comes for empty fields like 'sku' or 'brand'. This is the message that should throw from Google Console: > The brand field is recommended. Please provide a value if available. Since is recommended, this PR should solve the max warnings as possible. Fixes #22842 --- includes/class-wc-structured-data.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/includes/class-wc-structured-data.php b/includes/class-wc-structured-data.php index 49f064bc4fd..817c8224f45 100644 --- a/includes/class-wc-structured-data.php +++ b/includes/class-wc-structured-data.php @@ -203,6 +203,11 @@ class WC_Structured_Data { 'name' => $product->get_name(), ); + $markup['image'] = wp_get_attachment_url( $product->get_image_id() ); + $markup['description'] = wp_strip_all_tags( do_shortcode( $product->get_short_description() ? $product->get_short_description() : $product->get_description() ) ); + $markup['sku'] = $product->get_sku(); + $markup['brand'] = ''; + if ( apply_filters( 'woocommerce_structured_data_product_limit', is_product_taxonomy() || is_shop() ) ) { $markup['url'] = $permalink; @@ -210,10 +215,6 @@ class WC_Structured_Data { return; } - $markup['image'] = wp_get_attachment_url( $product->get_image_id() ); - $markup['description'] = wp_strip_all_tags( do_shortcode( $product->get_short_description() ? $product->get_short_description() : $product->get_description() ) ); - $markup['sku'] = $product->get_sku(); - if ( '' !== $product->get_price() ) { if ( $product->is_type( 'variable' ) ) { $lowest = $product->get_variation_price( 'min', false ); From 946b1e2469a66833793fae58dd1447d97cfd433d Mon Sep 17 00:00:00 2001 From: Krzysztof Grabania Date: Mon, 25 Feb 2019 20:12:54 +0100 Subject: [PATCH 351/401] Remove whitespace characters from Select2 option title --- includes/admin/class-wc-admin-settings.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/includes/admin/class-wc-admin-settings.php b/includes/admin/class-wc-admin-settings.php index 93e0bd4c9d5..a14070723a1 100644 --- a/includes/admin/class-wc-admin-settings.php +++ b/includes/admin/class-wc-admin-settings.php @@ -392,8 +392,7 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) : } ?> - > - + > From 8792b53ecb5eb61f928fa607f485aac4c5c94365 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 25 Feb 2019 22:01:07 +0000 Subject: [PATCH 352/401] shop_messages shortcode requires wc_print_notices --- includes/class-wc-shortcodes.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/includes/class-wc-shortcodes.php b/includes/class-wc-shortcodes.php index 42d2ca5902a..89029649539 100644 --- a/includes/class-wc-shortcodes.php +++ b/includes/class-wc-shortcodes.php @@ -588,6 +588,9 @@ class WC_Shortcodes { * @return string */ public static function shop_messages() { + if ( ! function_exists( 'wc_print_notices' ) ) { + return; + } return '

    ' . wc_print_notices( true ) . '
    '; } From 6c35df15519d9c89ba2655d4fed08e7106b61595 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 25 Feb 2019 22:01:22 +0000 Subject: [PATCH 353/401] Return string --- includes/class-wc-shortcodes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-shortcodes.php b/includes/class-wc-shortcodes.php index 89029649539..1e9e7e4c29f 100644 --- a/includes/class-wc-shortcodes.php +++ b/includes/class-wc-shortcodes.php @@ -589,7 +589,7 @@ class WC_Shortcodes { */ public static function shop_messages() { if ( ! function_exists( 'wc_print_notices' ) ) { - return; + return ''; } return '
    ' . wc_print_notices( true ) . '
    '; } From ccbb8313b61367f510154768619c7cfb9fe4ce46 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Tue, 26 Feb 2019 12:24:56 +0200 Subject: [PATCH 354/401] Revert "Remove old hooks for woocommerce_theme_background_installer and woocommerce_plugin_background_installer. There are no scheduled events for these anymore and the Wizard has it's own theme/plugin installer that uses the shutdown hook instead of cron." This reverts commit 0f771bf4fe75dff21998364aee33cbceb3284e57. --- includes/class-wc-install.php | 216 ++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) diff --git a/includes/class-wc-install.php b/includes/class-wc-install.php index 160e89d7a08..79983d1635f 100644 --- a/includes/class-wc-install.php +++ b/includes/class-wc-install.php @@ -144,6 +144,8 @@ class WC_Install { add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 ); add_filter( 'wpmu_drop_tables', array( __CLASS__, 'wpmu_drop_tables' ) ); add_filter( 'cron_schedules', array( __CLASS__, 'cron_schedules' ) ); + add_action( 'woocommerce_plugin_background_installer', array( __CLASS__, 'background_installer' ), 10, 2 ); + add_action( 'woocommerce_theme_background_installer', array( __CLASS__, 'theme_background_installer' ), 10, 1 ); } /** @@ -1220,6 +1222,154 @@ CREATE TABLE {$wpdb->prefix}woocommerce_termmeta ( return $plugins; } + /** + * Install a plugin from .org in the background via a cron job (used by + * installer - opt in). + * + * @param string $plugin_to_install_id Plugin ID. + * @param array $plugin_to_install Plugin information. + * + * @throws Exception If unable to proceed with plugin installation. + * @since 2.6.0 + */ + public static function background_installer( $plugin_to_install_id, $plugin_to_install ) { + // Explicitly clear the event. + $args = func_get_args(); + wp_clear_scheduled_hook( 'woocommerce_plugin_background_installer', $args ); + + if ( ! empty( $plugin_to_install['repo-slug'] ) ) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + require_once ABSPATH . 'wp-admin/includes/plugin.php'; + + WP_Filesystem(); + + $skin = new Automatic_Upgrader_Skin(); + $upgrader = new WP_Upgrader( $skin ); + $installed_plugins = array_reduce( array_keys( get_plugins() ), array( __CLASS__, 'associate_plugin_file' ) ); + if ( empty( $installed_plugins ) ) { + $installed_plugins = array(); + } + $plugin_slug = $plugin_to_install['repo-slug']; + $plugin_file = isset( $plugin_to_install['file'] ) ? $plugin_to_install['file'] : $plugin_slug . '.php'; + $installed = false; + $activate = false; + + // See if the plugin is installed already. + if ( isset( $installed_plugins[ $plugin_file ] ) ) { + $installed = true; + $activate = ! is_plugin_active( $installed_plugins[ $plugin_file ] ); + } + + // Install this thing! + if ( ! $installed ) { + // Suppress feedback. + ob_start(); + + try { + $plugin_information = plugins_api( + 'plugin_information', + array( + 'slug' => $plugin_slug, + 'fields' => array( + 'short_description' => false, + 'sections' => false, + 'requires' => false, + 'rating' => false, + 'ratings' => false, + 'downloaded' => false, + 'last_updated' => false, + 'added' => false, + 'tags' => false, + 'homepage' => false, + 'donate_link' => false, + 'author_profile' => false, + 'author' => false, + ), + ) + ); + + if ( is_wp_error( $plugin_information ) ) { + throw new Exception( $plugin_information->get_error_message() ); + } + + $package = $plugin_information->download_link; + $download = $upgrader->download_package( $package ); + + if ( is_wp_error( $download ) ) { + throw new Exception( $download->get_error_message() ); + } + + $working_dir = $upgrader->unpack_package( $download, true ); + + if ( is_wp_error( $working_dir ) ) { + throw new Exception( $working_dir->get_error_message() ); + } + + $result = $upgrader->install_package( + array( + 'source' => $working_dir, + 'destination' => WP_PLUGIN_DIR, + 'clear_destination' => false, + 'abort_if_destination_exists' => false, + 'clear_working' => true, + 'hook_extra' => array( + 'type' => 'plugin', + 'action' => 'install', + ), + ) + ); + + if ( is_wp_error( $result ) ) { + throw new Exception( $result->get_error_message() ); + } + + $activate = true; + + } catch ( Exception $e ) { + WC_Admin_Notices::add_custom_notice( + $plugin_to_install_id . '_install_error', + sprintf( + // translators: 1: plugin name, 2: error message, 3: URL to install plugin manually. + __( '%1$s could not be installed (%2$s).
    Please install it manually by clicking here.', 'woocommerce' ), + $plugin_to_install['name'], + $e->getMessage(), + esc_url( admin_url( 'index.php?wc-install-plugin-redirect=' . $plugin_slug ) ) + ) + ); + } + + // Discard feedback. + ob_end_clean(); + } + + wp_clean_plugins_cache(); + + // Activate this thing. + if ( $activate ) { + try { + add_action( 'add_option_mailchimp_woocommerce_plugin_do_activation_redirect', array( __CLASS__, 'remove_mailchimps_redirect' ), 10, 2 ); + $result = activate_plugin( $installed ? $installed_plugins[ $plugin_file ] : $plugin_slug . '/' . $plugin_file ); + + if ( is_wp_error( $result ) ) { + throw new Exception( $result->get_error_message() ); + } + } catch ( Exception $e ) { + WC_Admin_Notices::add_custom_notice( + $plugin_to_install_id . '_install_error', + sprintf( + // translators: 1: plugin name, 2: URL to WP plugin page. + __( '%1$s was installed but could not be activated. Please activate it manually by clicking here.', 'woocommerce' ), + $plugin_to_install['name'], + admin_url( 'plugins.php' ) + ) + ); + } + } + } + } + /** * Removes redirect added during MailChimp plugin's activation. * @@ -1233,6 +1383,72 @@ CREATE TABLE {$wpdb->prefix}woocommerce_termmeta ( // Update redirect back to false. update_option( 'mailchimp_woocommerce_plugin_do_activation_redirect', false ); } + + /** + * Install a theme from .org in the background via a cron job (used by installer - opt in). + * + * @param string $theme_slug Theme slug. + * + * @throws Exception If unable to proceed with theme installation. + * @since 3.1.0 + */ + public static function theme_background_installer( $theme_slug ) { + // Explicitly clear the event. + $args = func_get_args(); + wp_clear_scheduled_hook( 'woocommerce_theme_background_installer', $args ); + + if ( ! empty( $theme_slug ) ) { + // Suppress feedback. + ob_start(); + + try { + $theme = wp_get_theme( $theme_slug ); + + if ( ! $theme->exists() ) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + include_once ABSPATH . 'wp-admin/includes/theme.php'; + + WP_Filesystem(); + + $skin = new Automatic_Upgrader_Skin(); + $upgrader = new Theme_Upgrader( $skin ); + $api = themes_api( + 'theme_information', + array( + 'slug' => $theme_slug, + 'fields' => array( 'sections' => false ), + ) + ); + $result = $upgrader->install( $api->download_link ); + + if ( is_wp_error( $result ) ) { + throw new Exception( $result->get_error_message() ); + } elseif ( is_wp_error( $skin->result ) ) { + throw new Exception( $skin->result->get_error_message() ); + } elseif ( is_null( $result ) ) { + throw new Exception( 'Unable to connect to the filesystem. Please confirm your credentials.' ); + } + } + + switch_theme( $theme_slug ); + } catch ( Exception $e ) { + WC_Admin_Notices::add_custom_notice( + $theme_slug . '_install_error', + sprintf( + // translators: 1: theme slug, 2: error message, 3: URL to install theme manually. + __( '%1$s could not be installed (%2$s). Please install it manually by clicking here.', 'woocommerce' ), + $theme_slug, + $e->getMessage(), + esc_url( admin_url( 'update.php?action=install-theme&theme=' . $theme_slug . '&_wpnonce=' . wp_create_nonce( 'install-theme_' . $theme_slug ) ) ) + ) + ); + } + + // Discard feedback. + ob_end_clean(); + } + } } WC_Install::init(); From 1e21ebe3672d2ab98233820d813f71b987185870 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Tue, 26 Feb 2019 12:25:12 +0200 Subject: [PATCH 355/401] Remove the callbacks only --- includes/class-wc-install.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/includes/class-wc-install.php b/includes/class-wc-install.php index 79983d1635f..53fb3a37727 100644 --- a/includes/class-wc-install.php +++ b/includes/class-wc-install.php @@ -144,8 +144,6 @@ class WC_Install { add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 ); add_filter( 'wpmu_drop_tables', array( __CLASS__, 'wpmu_drop_tables' ) ); add_filter( 'cron_schedules', array( __CLASS__, 'cron_schedules' ) ); - add_action( 'woocommerce_plugin_background_installer', array( __CLASS__, 'background_installer' ), 10, 2 ); - add_action( 'woocommerce_theme_background_installer', array( __CLASS__, 'theme_background_installer' ), 10, 1 ); } /** From 3dde01570a8d9defbee84a42a7fd5fd97d472dbf Mon Sep 17 00:00:00 2001 From: Gerhard Date: Tue, 26 Feb 2019 12:44:27 +0200 Subject: [PATCH 356/401] Remove wp_clear_scheduled_hook for the plugin and theme background installer that does not run via cron anymore. --- includes/class-wc-install.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/includes/class-wc-install.php b/includes/class-wc-install.php index 53fb3a37727..f1e2a970581 100644 --- a/includes/class-wc-install.php +++ b/includes/class-wc-install.php @@ -1233,7 +1233,6 @@ CREATE TABLE {$wpdb->prefix}woocommerce_termmeta ( public static function background_installer( $plugin_to_install_id, $plugin_to_install ) { // Explicitly clear the event. $args = func_get_args(); - wp_clear_scheduled_hook( 'woocommerce_plugin_background_installer', $args ); if ( ! empty( $plugin_to_install['repo-slug'] ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; @@ -1393,7 +1392,6 @@ CREATE TABLE {$wpdb->prefix}woocommerce_termmeta ( public static function theme_background_installer( $theme_slug ) { // Explicitly clear the event. $args = func_get_args(); - wp_clear_scheduled_hook( 'woocommerce_theme_background_installer', $args ); if ( ! empty( $theme_slug ) ) { // Suppress feedback. From 913b8e84bf176331eae6dd34756b6d6fc11bf828 Mon Sep 17 00:00:00 2001 From: Naveen giri <1naveengiri@gmail.com> Date: Tue, 26 Feb 2019 18:31:34 +0530 Subject: [PATCH 357/401] Fix wpcs error and avoid unnecessary checks --- includes/class-wc-ajax.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php index 086a381f9e6..89cda31924a 100644 --- a/includes/class-wc-ajax.php +++ b/includes/class-wc-ajax.php @@ -632,7 +632,7 @@ class WC_AJAX { ob_start(); $attributes = $product->get_attributes( 'edit' ); $i = -1; - if( isset( $data['attribute_names'] ) && !empty( $data['attribute_names'] ) ){ + if( ! empty( $data['attribute_names'] ) ) { foreach ( $data['attribute_names'] as $attribute_name ) { $attribute = isset( $attributes[ sanitize_title( $attribute_name ) ] ) ? $attributes[ sanitize_title( $attribute_name ) ] : false; if ( ! $attribute ) { From 20eb50312833a63015f02b0d82a833548a2bad38 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 26 Feb 2019 14:03:25 +0000 Subject: [PATCH 358/401] Add alt text to gallery thumbnail --- includes/wc-template-functions.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index e03b084b942..aff8a276672 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -1426,6 +1426,7 @@ function wc_get_gallery_image_html( $attachment_id, $main_image = false ) { $full_size = apply_filters( 'woocommerce_gallery_full_size', apply_filters( 'woocommerce_product_thumbnails_large_size', 'full' ) ); $thumbnail_src = wp_get_attachment_image_src( $attachment_id, $thumbnail_size ); $full_src = wp_get_attachment_image_src( $attachment_id, $full_size ); + $alt_text = trim( wp_strip_all_tags( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) ); $image = wp_get_attachment_image( $attachment_id, $image_size, @@ -1447,7 +1448,7 @@ function wc_get_gallery_image_html( $attachment_id, $main_image = false ) { ) ); - return ''; + return ''; } if ( ! function_exists( 'woocommerce_output_product_data_tabs' ) ) { From d5ab20d1a5cc7feccc28892724a8b642e8cf8c1c Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 26 Feb 2019 20:29:17 +0000 Subject: [PATCH 359/401] get ID before running actions --- includes/class-wc-tax.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-tax.php b/includes/class-wc-tax.php index 91fe8e410b6..18bd21f4f6e 100644 --- a/includes/class-wc-tax.php +++ b/includes/class-wc-tax.php @@ -882,11 +882,13 @@ class WC_Tax { $wpdb->insert( $wpdb->prefix . 'woocommerce_tax_rates', self::prepare_tax_rate( $tax_rate ) ); + $tax_rate_id = $wpdb->insert_id; + WC_Cache_Helper::incr_cache_prefix( 'taxes' ); - do_action( 'woocommerce_tax_rate_added', $wpdb->insert_id, $tax_rate ); + do_action( 'woocommerce_tax_rate_added', $tax_rate_id, $tax_rate ); - return $wpdb->insert_id; + return $tax_rate_id; } /** From a1c304831815f7b505c866866ca349141f2978c1 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Wed, 27 Feb 2019 12:55:49 +0200 Subject: [PATCH 360/401] Keep tracking enabled if already enabled when saving the wizard. --- includes/admin/class-wc-admin-setup-wizard.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/admin/class-wc-admin-setup-wizard.php b/includes/admin/class-wc-admin-setup-wizard.php index e824bd0734f..e3eba4d5979 100644 --- a/includes/admin/class-wc-admin-setup-wizard.php +++ b/includes/admin/class-wc-admin-setup-wizard.php @@ -546,7 +546,7 @@ class WC_Admin_Setup_Wizard { $currency_code = sanitize_text_field( $_POST['currency_code'] ); $product_type = sanitize_text_field( $_POST['product_type'] ); $sell_in_person = isset( $_POST['sell_in_person'] ) && ( 'yes' === sanitize_text_field( $_POST['sell_in_person'] ) ); - $tracking = isset( $_POST['wc_tracker_checkbox'] ) && ( 'yes' === sanitize_text_field( $_POST['wc_tracker_checkbox'] ) ); + $tracking = ( isset( $_POST['wc_tracker_checkbox'] ) && ( 'yes' === sanitize_text_field( $_POST['wc_tracker_checkbox'] ) ) ) || ( 'yes' === get_option( 'woocommerce_allow_tracking', 'unknown' ) ); // phpcs:enable if ( ! $state ) { From 72ea7b05e7a78d5a29845dcd35940b2cdf0a74e0 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Wed, 27 Feb 2019 13:38:36 +0200 Subject: [PATCH 361/401] Move tracking check to the if statement to avoid writing to the DB when it already has a value stored. --- includes/admin/class-wc-admin-setup-wizard.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/admin/class-wc-admin-setup-wizard.php b/includes/admin/class-wc-admin-setup-wizard.php index e3eba4d5979..d3c7d442660 100644 --- a/includes/admin/class-wc-admin-setup-wizard.php +++ b/includes/admin/class-wc-admin-setup-wizard.php @@ -546,7 +546,7 @@ class WC_Admin_Setup_Wizard { $currency_code = sanitize_text_field( $_POST['currency_code'] ); $product_type = sanitize_text_field( $_POST['product_type'] ); $sell_in_person = isset( $_POST['sell_in_person'] ) && ( 'yes' === sanitize_text_field( $_POST['sell_in_person'] ) ); - $tracking = ( isset( $_POST['wc_tracker_checkbox'] ) && ( 'yes' === sanitize_text_field( $_POST['wc_tracker_checkbox'] ) ) ) || ( 'yes' === get_option( 'woocommerce_allow_tracking', 'unknown' ) ); + $tracking = isset( $_POST['wc_tracker_checkbox'] ) && ( 'yes' === sanitize_text_field( $_POST['wc_tracker_checkbox'] ) ); // phpcs:enable if ( ! $state ) { @@ -577,7 +577,7 @@ class WC_Admin_Setup_Wizard { } } - if ( $tracking ) { + if ( $tracking && 'unknown' === get_option( 'woocommerce_allow_tracking', 'unknown' ) ) { update_option( 'woocommerce_allow_tracking', 'yes' ); wp_schedule_single_event( time() + 10, 'woocommerce_tracker_send_event', array( true ) ); } else { From bb8bf5f7495434d2079536a720c2ae35dd54ad71 Mon Sep 17 00:00:00 2001 From: yaroslawww Date: Wed, 27 Feb 2019 13:53:32 +0200 Subject: [PATCH 362/401] Update class-wc-regenerate-images.php Sometimes imagedata has not $imagedata['sizes']['full'] params. In my case, due to the fact that I am loading the SVG picture. Therefore, an error is visible *Undefined index: height in class-wc-regenerate-images.php on line 222* Therefore, a check for the emptiness of these parameters is added. --- includes/class-wc-regenerate-images.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-regenerate-images.php b/includes/class-wc-regenerate-images.php index c1060445cfd..fb3475c9843 100644 --- a/includes/class-wc-regenerate-images.php +++ b/includes/class-wc-regenerate-images.php @@ -219,7 +219,9 @@ class WC_Regenerate_Images { $imagedata['width'] = $imagedata['sizes']['full']['width']; } - $ratio_match = wp_image_matches_ratio( $image[1], $image[2], $imagedata['width'], $imagedata['height'] ); + if ( $image_size['width'] && $image_size['height'] ) { + $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'] ); } From c854e74471fde91fafd7ec33df010e425fc11550 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 27 Feb 2019 11:56:00 +0000 Subject: [PATCH 363/401] Spacing --- includes/class-wc-ajax.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php index 89cda31924a..a9c2f74a9f1 100644 --- a/includes/class-wc-ajax.php +++ b/includes/class-wc-ajax.php @@ -632,7 +632,7 @@ class WC_AJAX { ob_start(); $attributes = $product->get_attributes( 'edit' ); $i = -1; - if( ! empty( $data['attribute_names'] ) ) { + if ( ! empty( $data['attribute_names'] ) ) { foreach ( $data['attribute_names'] as $attribute_name ) { $attribute = isset( $attributes[ sanitize_title( $attribute_name ) ] ) ? $attributes[ sanitize_title( $attribute_name ) ] : false; if ( ! $attribute ) { From 70f493523668925b7a444af3940c93d10dcc57c7 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Wed, 27 Feb 2019 14:30:00 +0200 Subject: [PATCH 364/401] Fix logic to avoid setting to no again --- includes/admin/class-wc-admin-setup-wizard.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/includes/admin/class-wc-admin-setup-wizard.php b/includes/admin/class-wc-admin-setup-wizard.php index d3c7d442660..cdde2dbe559 100644 --- a/includes/admin/class-wc-admin-setup-wizard.php +++ b/includes/admin/class-wc-admin-setup-wizard.php @@ -576,12 +576,13 @@ class WC_Admin_Setup_Wizard { update_option( 'woocommerce_price_thousand_sep', $locale_info[ $country ]['thousand_sep'] ); } } - - if ( $tracking && 'unknown' === get_option( 'woocommerce_allow_tracking', 'unknown' ) ) { - update_option( 'woocommerce_allow_tracking', 'yes' ); - wp_schedule_single_event( time() + 10, 'woocommerce_tracker_send_event', array( true ) ); - } else { - update_option( 'woocommerce_allow_tracking', 'no' ); + if ( 'unknown' === get_option( 'woocommerce_allow_tracking', 'unknown' ) ) { + if ( $tracking ) { + update_option( 'woocommerce_allow_tracking', 'yes' ); + wp_schedule_single_event( time() + 10, 'woocommerce_tracker_send_event', array( true ) ); + } else { + update_option( 'woocommerce_allow_tracking', 'no' ); + } } WC_Install::create_pages(); From 807878692bcc37079655407d4d56a9a7b7958ab7 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 27 Feb 2019 13:43:01 +0000 Subject: [PATCH 365/401] Add precision to tax --- includes/class-wc-discounts.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-discounts.php b/includes/class-wc-discounts.php index 1a722f4c3ad..c5c9bf744f2 100644 --- a/includes/class-wc-discounts.php +++ b/includes/class-wc-discounts.php @@ -910,7 +910,13 @@ class WC_Discounts { return wc_add_number_precision( $this->object->get_displayed_subtotal() ); } elseif ( is_a( $this->object, 'WC_Order' ) ) { $subtotal = wc_add_number_precision( $this->object->get_subtotal() ); - return $this->object->get_prices_include_tax() ? $subtotal + round( $this->object->get_total_tax(), wc_get_price_decimals() ) : $subtotal; + + if ( $this->object->get_prices_include_tax() ) { + // Add tax to tax-exclusive subtotal. + $subtotal = $subtotal + wc_add_number_precision( round( $this->object->get_total_tax(), wc_get_price_decimals() ) ); + } + + return $subtotal; } else { return array_sum( wp_list_pluck( $this->items, 'price' ) ); } From 49065c0d1ae3e6e2c0692369cd2692512736929e Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 27 Feb 2019 14:50:37 +0000 Subject: [PATCH 366/401] Add meta data, before changing order status --- .../class-wc-gateway-paypal-ipn-handler.php | 7 +++---- .../class-wc-gateway-paypal-pdt-handler.php | 14 +++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/includes/gateways/paypal/includes/class-wc-gateway-paypal-ipn-handler.php b/includes/gateways/paypal/includes/class-wc-gateway-paypal-ipn-handler.php index 402d9d04f5d..ca15f4a74d6 100644 --- a/includes/gateways/paypal/includes/class-wc-gateway-paypal-ipn-handler.php +++ b/includes/gateways/paypal/includes/class-wc-gateway-paypal-ipn-handler.php @@ -201,12 +201,11 @@ class WC_Gateway_Paypal_IPN_Handler extends WC_Gateway_Paypal_Response { $this->payment_status_paid_cancelled_order( $order, $posted ); } - $this->payment_complete( $order, ( ! empty( $posted['txn_id'] ) ? wc_clean( $posted['txn_id'] ) : '' ), __( 'IPN payment completed', 'woocommerce' ) ); - if ( ! empty( $posted['mc_fee'] ) ) { - // Log paypal transaction fee. - update_post_meta( $order->get_id(), 'PayPal Transaction Fee', wc_clean( $posted['mc_fee'] ) ); + $order->add_meta_data( 'PayPal Transaction Fee', wc_clean( $posted['mc_fee'] ) ); } + + $this->payment_complete( $order, ( ! empty( $posted['txn_id'] ) ? wc_clean( $posted['txn_id'] ) : '' ), __( 'IPN payment completed', 'woocommerce' ) ); } else { if ( 'authorization' === $posted['pending_reason'] ) { $this->payment_on_hold( $order, __( 'Payment authorized. Change payment status to processing or complete to capture funds.', 'woocommerce' ) ); diff --git a/includes/gateways/paypal/includes/class-wc-gateway-paypal-pdt-handler.php b/includes/gateways/paypal/includes/class-wc-gateway-paypal-pdt-handler.php index 92b05236fd7..d8ead0f5faf 100644 --- a/includes/gateways/paypal/includes/class-wc-gateway-paypal-pdt-handler.php +++ b/includes/gateways/paypal/includes/class-wc-gateway-paypal-pdt-handler.php @@ -89,7 +89,7 @@ class WC_Gateway_Paypal_PDT_Handler extends WC_Gateway_Paypal_Response { $order_id = wc_clean( wp_unslash( $_REQUEST['cm'] ) ); // WPCS: input var ok, CSRF ok, sanitization ok. $status = wc_clean( strtolower( wp_unslash( $_REQUEST['st'] ) ) ); // WPCS: input var ok, CSRF ok, sanitization ok. - $amount = wc_clean( wp_unslash( $_REQUEST['amt'] ) ); // WPCS: input var ok, CSRF ok, sanitization ok. + $amount = isset( $_REQUEST['amt'] ) ? wc_clean( wp_unslash( $_REQUEST['amt'] ) ) : 0; // WPCS: input var ok, CSRF ok, sanitization ok. $transaction = wc_clean( wp_unslash( $_REQUEST['tx'] ) ); // WPCS: input var ok, CSRF ok, sanitization ok. $order = $this->get_paypal_order( $order_id ); @@ -102,8 +102,8 @@ class WC_Gateway_Paypal_PDT_Handler extends WC_Gateway_Paypal_Response { if ( $transaction_result ) { WC_Gateway_Paypal::log( 'PDT Transaction Status: ' . wc_print_r( $status, true ) ); - update_post_meta( $order->get_id(), '_paypal_status', $status ); - update_post_meta( $order->get_id(), '_transaction_id', $transaction ); + $order->add_meta_data( '_paypal_status', $status ); + $order->set_transaction_id( $transaction ); if ( 'completed' === $status ) { if ( number_format( $order->get_total(), 2, '.', '' ) !== number_format( $amount, 2, '.', '' ) ) { @@ -111,15 +111,15 @@ class WC_Gateway_Paypal_PDT_Handler extends WC_Gateway_Paypal_Response { /* translators: 1: Payment amount */ $this->payment_on_hold( $order, sprintf( __( 'Validation error: PayPal amounts do not match (amt %s).', 'woocommerce' ), $amount ) ); } else { - $this->payment_complete( $order, $transaction, __( 'PDT payment completed', 'woocommerce' ) ); - // Log paypal transaction fee and payment type. if ( ! empty( $transaction_result['mc_fee'] ) ) { - update_post_meta( $order->get_id(), 'PayPal Transaction Fee', $transaction_result['mc_fee'] ); + $order->add_meta_data( 'PayPal Transaction Fee', wc_clean( $transaction_result['mc_fee'] ) ); } if ( ! empty( $transaction_result['payment_type'] ) ) { - update_post_meta( $order->get_id(), 'Payment type', $transaction_result['payment_type'] ); + $order->add_meta_data( 'Payment type', wc_clean( $transaction_result['payment_type'] ) ); } + + $this->payment_complete( $order, $transaction, __( 'PDT payment completed', 'woocommerce' ) ); } } else { if ( 'authorization' === $transaction_result['pending_reason'] ) { From a89caf0b8963375419e6d02caa51b38f3ef494c9 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 27 Feb 2019 15:25:27 +0000 Subject: [PATCH 367/401] Add wrapper for is_plugin_active --- includes/admin/class-wc-admin-notices.php | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/includes/admin/class-wc-admin-notices.php b/includes/admin/class-wc-admin-notices.php index 83707d70432..48b292f26fb 100644 --- a/includes/admin/class-wc-admin-notices.php +++ b/includes/admin/class-wc-admin-notices.php @@ -351,7 +351,7 @@ class WC_Admin_Notices { * @todo Remove this notice and associated code once the feature plugin has been merged into core. */ public static function add_wootenberg_feature_plugin_notice() { - if ( ( is_plugin_active( 'gutenberg/gutenberg.php' ) || version_compare( get_bloginfo( 'version' ), '5.0', '>=' ) ) && ! is_plugin_active( 'woo-gutenberg-products-block/woocommerce-gutenberg-products-block.php' ) ) { + if ( ( self::is_plugin_active( 'gutenberg/gutenberg.php' ) || version_compare( get_bloginfo( 'version' ), '5.0', '>=' ) ) && ! self::is_plugin_active( 'woo-gutenberg-products-block/woocommerce-gutenberg-products-block.php' ) ) { self::add_notice( 'wootenberg' ); } } @@ -363,7 +363,7 @@ class WC_Admin_Notices { * @todo Remove this notice and associated code once the feature plugin has been merged into core. */ public static function add_wootenberg_feature_plugin_notice_on_gutenberg_activate() { - if ( ! is_plugin_active( 'woo-gutenberg-products-block/woocommerce-gutenberg-products-block.php' ) && version_compare( get_bloginfo( 'version' ), '5.0', '<' ) ) { + if ( ! self::is_plugin_active( 'woo-gutenberg-products-block/woocommerce-gutenberg-products-block.php' ) && version_compare( get_bloginfo( 'version' ), '5.0', '<' ) ) { self::add_notice( 'wootenberg' ); } } @@ -372,7 +372,7 @@ class WC_Admin_Notices { * Notice about trying the Products block. */ public static function wootenberg_feature_plugin_notice() { - if ( get_user_meta( get_current_user_id(), 'dismissed_wootenberg_notice', true ) || is_plugin_active( 'woo-gutenberg-products-block/woocommerce-gutenberg-products-block.php' ) ) { + if ( get_user_meta( get_current_user_id(), 'dismissed_wootenberg_notice', true ) || self::is_plugin_active( 'woo-gutenberg-products-block/woocommerce-gutenberg-products-block.php' ) ) { self::remove_notice( 'wootenberg' ); return; } @@ -392,6 +392,18 @@ class WC_Admin_Notices { return ( is_ssl() && 'https' === substr( $shop_page, 0, 5 ) ); } + /** + * Wrapper for is_plugin_active. + * + * @param string $plugin Plugin to check. + * @return boolean + */ + protected static function is_plugin_active( $plugin ) { + if ( ! function_exists( 'is_plugin_active' ) ) { + include_once ABSPATH . 'wp-admin/includes/plugin.php'; + } + return is_plugin_active( $plugin ); + } } WC_Admin_Notices::init(); From 4f9d47ea25a0617e956ac806188c32337a2c2eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20=C5=A0najdr?= Date: Wed, 27 Feb 2019 17:31:51 +0100 Subject: [PATCH 368/401] PR requested changes: shipping instance option filter moved to get_instance_option function, filters renamed. --- includes/abstracts/abstract-wc-shipping-method.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/includes/abstracts/abstract-wc-shipping-method.php b/includes/abstracts/abstract-wc-shipping-method.php index d4a2a9846ce..8be35f82e9c 100644 --- a/includes/abstracts/abstract-wc-shipping-method.php +++ b/includes/abstracts/abstract-wc-shipping-method.php @@ -462,13 +462,12 @@ abstract class WC_Shipping_Method extends WC_Settings_API { public function get_option( $key, $empty_value = null ) { // Instance options take priority over global options. if ( $this->instance_id && array_key_exists( $key, $this->get_instance_form_fields() ) ) { - $instance_option = apply_filters( 'woocommerce_shipping_' . $this->id . '_instance_option', $this->get_instance_option( $key, $empty_value ), $key, $empty_value, $this ); - return $instance_option; + return $this->get_instance_option( $key, $empty_value ); } // Return global option. - $global_option = apply_filters( 'woocommerce_shipping_' . $this->id . '_global_option', parent::get_option( $key, $empty_value ), $key, $empty_value, $this ); - return $global_option; + $option = apply_filters( 'woocommerce_shipping_' . $this->id . '_option', parent::get_option( $key, $empty_value ), $key, $this ); + return $option; } /** @@ -493,7 +492,8 @@ abstract class WC_Shipping_Method extends WC_Settings_API { $this->instance_settings[ $key ] = $empty_value; } - return $this->instance_settings[ $key ]; + $instance_option = apply_filters( 'woocommerce_shipping_' . $this->id . '_instance_option', $this->instance_settings[ $key ], $key, $this ); + return $instance_option; } /** From 8cf31c3c3d0aa8321ec44fd46563bea7e52cc6d0 Mon Sep 17 00:00:00 2001 From: Mihai Grigori Date: Thu, 28 Feb 2019 07:07:26 +0200 Subject: [PATCH 369/401] Change exception string to facilitate translating --- .../classes/ActionScheduler_WPCLI_QueueRunner.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_WPCLI_QueueRunner.php b/includes/libraries/action-scheduler/classes/ActionScheduler_WPCLI_QueueRunner.php index b3f1730d97c..780476efb19 100644 --- a/includes/libraries/action-scheduler/classes/ActionScheduler_WPCLI_QueueRunner.php +++ b/includes/libraries/action-scheduler/classes/ActionScheduler_WPCLI_QueueRunner.php @@ -27,7 +27,7 @@ class ActionScheduler_WPCLI_QueueRunner extends ActionScheduler_Abstract_QueueRu */ public function __construct( ActionScheduler_Store $store = null, ActionScheduler_FatalErrorMonitor $monitor = null, ActionScheduler_QueueCleaner $cleaner = null ) { if ( ! ( defined( 'WP_CLI' ) && WP_CLI ) ) { - throw new Exception( __( 'The ' . __CLASS__ . ' class can only be run within WP CLI.', 'action-scheduler' ) ); + throw new Exception( sprintf( __( 'The %s class can only be run within WP CLI.', 'action-scheduler' ), __CLASS__ ) ); } parent::__construct( $store, $monitor, $cleaner ); From 207a5ef4b3c02ce6aee788876ecfed3c1ee4eb6b Mon Sep 17 00:00:00 2001 From: Gerhard Date: Thu, 28 Feb 2019 15:30:17 +0200 Subject: [PATCH 370/401] Add user_order_remaining_expires index to woocommerce_downloadable_product_permissions create table statement. --- includes/class-wc-install.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-install.php b/includes/class-wc-install.php index f1e2a970581..9234156b23a 100644 --- a/includes/class-wc-install.php +++ b/includes/class-wc-install.php @@ -692,7 +692,8 @@ CREATE TABLE {$wpdb->prefix}woocommerce_downloadable_product_permissions ( PRIMARY KEY (permission_id), KEY download_order_key_product (product_id,order_id,order_key(16),download_id), KEY download_order_product (download_id,order_id,product_id), - KEY order_id (order_id) + KEY order_id (order_id), + KEY user_order_remaining_expires (user_id,order_id,downloads_remaining,access_expires) ) $collate; CREATE TABLE {$wpdb->prefix}woocommerce_order_items ( order_item_id BIGINT UNSIGNED NOT NULL auto_increment, From b8a5a9ec5531019de9fe4522f4b1d0247a5d009d Mon Sep 17 00:00:00 2001 From: Gerhard Date: Thu, 28 Feb 2019 15:36:55 +0200 Subject: [PATCH 371/401] Add update routine to add user_order_remaining_expires index and hook it up to the installer for 3.6.0 --- includes/class-wc-install.php | 4 ++++ includes/wc-update-functions.php | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/includes/class-wc-install.php b/includes/class-wc-install.php index 9234156b23a..e84ccc051ad 100644 --- a/includes/class-wc-install.php +++ b/includes/class-wc-install.php @@ -124,6 +124,10 @@ class WC_Install { 'wc_update_354_modify_shop_manager_caps', 'wc_update_354_db_version', ), + '3.6.0' => array( + 'wc_update_360_downloadable_product_permissions_index', + 'wc_update_360_db_version', + ), ); /** diff --git a/includes/wc-update-functions.php b/includes/wc-update-functions.php index 469b21f2668..6800a9535e2 100644 --- a/includes/wc-update-functions.php +++ b/includes/wc-update-functions.php @@ -1932,3 +1932,25 @@ function wc_update_354_modify_shop_manager_caps() { function wc_update_354_db_version() { WC_Install::update_db_version( '3.5.4' ); } + +/** + * Add new user_order_remaining_expires to speed up user download permission fetching. + * + * @return void + */ +function wc_update_360_downloadable_product_permissions_index() { + global $wpdb; + + $index_exists = $wpdb->get_row( "SHOW INDEX FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE key_name = 'user_order_remaining_expires'" ); + + if ( is_null( $index_exists ) ) { + $wpdb->query( "ALTER TABLE {$wpdb->prefix}woocommerce_downloadable_product_permissions ADD INDEX user_order_remaining_expires (user_id,order_id,downloads_remaining,access_expires)" ); + } +} + +/** + * Update DB Version. + */ +function wc_update_360_db_version() { + WC_Install::update_db_version( '3.6.0' ); +} From 36f0041673e5dfd6e37820eed44de97ff7688ca4 Mon Sep 17 00:00:00 2001 From: Gerhard Date: Thu, 28 Feb 2019 15:37:37 +0200 Subject: [PATCH 372/401] PHPCS fixes --- includes/wc-update-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-update-functions.php b/includes/wc-update-functions.php index 6800a9535e2..affe3f71a27 100644 --- a/includes/wc-update-functions.php +++ b/includes/wc-update-functions.php @@ -740,7 +740,7 @@ function wc_update_240_shipping_methods() { if ( version_compare( $shipping_method->get_option( 'version', 0 ), '2.4.0', '<' ) ) { $shipping_classes = WC()->shipping()->get_shipping_classes(); $has_classes = count( $shipping_classes ) > 0; - $cost_key = $has_classes ? 'no_class_cost': 'cost'; + $cost_key = $has_classes ? 'no_class_cost' : 'cost'; $min_fee = $shipping_method->get_option( 'minimum_fee' ); $math_cost_strings = array( 'cost' => array(), From 6937f231da13cabac0fb644c0e1a7a05ffdb6076 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 28 Feb 2019 13:59:42 +0000 Subject: [PATCH 373/401] Pin dependency composer/installers to 1.6.0 --- composer.json | 2 +- composer.lock | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 83359209593..1d57e933678 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "prefer-stable": true, "minimum-stability": "dev", "require": { - "composer/installers": "~1.6" + "composer/installers": "1.6.0" }, "require-dev": { "apigen/apigen": "4.1.2", diff --git a/composer.lock b/composer.lock index a140bb4df0c..d1a859f0ee8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "67b8066152baf2f08393562b576da266", + "content-hash": "e7a81f490b054c067759791d696be7bd", "packages": [ { "name": "composer/installers", @@ -247,7 +247,7 @@ "homepage": "https://github.com/kukulich" }, { - "name": "Tomas Votruba", + "name": "Tomáš Votruba", "email": "tomas.vot@gmail.com" }, { @@ -2805,6 +2805,7 @@ "mock", "xunit" ], + "abandoned": true, "time": "2018-08-09T05:50:03+00:00" }, { @@ -3731,7 +3732,7 @@ }, { "name": "Gert de Pagter", - "email": "backendtea@gmail.com" + "email": "BackEndTea@gmail.com" } ], "description": "Symfony polyfill for ctype functions", From 04b333484a0b7cceddfe4e95fce2c87703e3638b Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 12 Feb 2019 11:57:56 +0000 Subject: [PATCH 374/401] These transient no longer exist --- includes/class-wc-post-data.php | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/includes/class-wc-post-data.php b/includes/class-wc-post-data.php index 4f503d45036..acf619a907b 100644 --- a/includes/class-wc-post-data.php +++ b/includes/class-wc-post-data.php @@ -134,23 +134,7 @@ class WC_Post_Data { * Delete product view transients when needed e.g. when post status changes, or visibility/stock status is modified. */ public static function delete_product_query_transients() { - // Increments the transient version to invalidate cache. WC_Cache_Helper::get_transient_version( 'product_query', true ); - - // If not using an external caching system, we can clear the transients out manually and avoid filling our DB. - if ( ! wp_using_ext_object_cache() ) { - global $wpdb; - - $wpdb->query( - " - DELETE FROM `$wpdb->options` - WHERE `option_name` LIKE ('\_transient\_wc\_uf\_pid\_%') - OR `option_name` LIKE ('\_transient\_timeout\_wc\_uf\_pid\_%') - OR `option_name` LIKE ('\_transient\_wc\_products\_will\_display\_%') - OR `option_name` LIKE ('\_transient\_timeout\_wc\_products\_will\_display\_%') - " - ); - } } /** From b7f59b4596dcd346b186e38b434a8a14542de7cc Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 12 Feb 2019 11:55:35 +0000 Subject: [PATCH 375/401] wc_ln_count_ no longer exists --- includes/class-wc-post-data.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/includes/class-wc-post-data.php b/includes/class-wc-post-data.php index acf619a907b..7cd0df1098c 100644 --- a/includes/class-wc-post-data.php +++ b/includes/class-wc-post-data.php @@ -109,9 +109,6 @@ class WC_Post_Data { * @param array $old_tt_ids Old array of term taxonomy IDs. */ public static function set_object_terms( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) { - foreach ( array_merge( $tt_ids, $old_tt_ids ) as $id ) { - delete_transient( 'wc_ln_count_' . md5( sanitize_key( $taxonomy ) . sanitize_key( $id ) ) ); - } if ( in_array( get_post_type( $object_id ), array( 'product', 'product_variation' ), true ) ) { self::delete_product_query_transients(); } From 5f9225a1cb1bc04be775180a89a64e32d56e1452 Mon Sep 17 00:00:00 2001 From: yaroslawww Date: Thu, 28 Feb 2019 16:48:18 +0200 Subject: [PATCH 376/401] Update class-wc-regenerate-images.php Fix coding styles and naming error --- includes/class-wc-regenerate-images.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-regenerate-images.php b/includes/class-wc-regenerate-images.php index fb3475c9843..90b41202ceb 100644 --- a/includes/class-wc-regenerate-images.php +++ b/includes/class-wc-regenerate-images.php @@ -219,8 +219,8 @@ class WC_Regenerate_Images { $imagedata['width'] = $imagedata['sizes']['full']['width']; } - if ( $image_size['width'] && $image_size['height'] ) { - $ratio_match = wp_image_matches_ratio($image[1], $image[2], $imagedata['width'], $imagedata['height']); + if ( $imagedata['width'] && $imagedata['height'] ) { + $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'] ); From 55c49b661efd5e1669e79e07352d447de99defbd Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 12 Feb 2019 13:22:25 +0000 Subject: [PATCH 377/401] Missing cache flush when updating sales --- includes/wc-product-functions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 7f13dd7476a..0d7312f176d 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -420,6 +420,7 @@ function wc_scheduled_sales() { } do_action( 'wc_after_products_starting_sales', $product_ids ); + WC_Cache_Helper::get_transient_version( 'product', true ); delete_transient( 'wc_products_onsale' ); } From 81767385d6f86c3dd3c4dad0c4c109437abf343d Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 12 Feb 2019 12:15:33 +0000 Subject: [PATCH 378/401] Tidy up deprecated methods --- includes/class-wc-post-data.php | 87 ++++++++++++++++----------------- 1 file changed, 42 insertions(+), 45 deletions(-) diff --git a/includes/class-wc-post-data.php b/includes/class-wc-post-data.php index 4f503d45036..961c510e501 100644 --- a/includes/class-wc-post-data.php +++ b/includes/class-wc-post-data.php @@ -28,10 +28,8 @@ class WC_Post_Data { public static function init() { add_filter( 'post_type_link', array( __CLASS__, 'variation_post_link' ), 10, 2 ); add_action( 'shutdown', array( __CLASS__, 'do_deferred_product_sync' ), 10 ); - add_action( 'set_object_terms', array( __CLASS__, 'set_object_terms' ), 10, 6 ); add_action( 'set_object_terms', array( __CLASS__, 'force_default_term' ), 10, 5 ); - - add_action( 'transition_post_status', array( __CLASS__, 'transition_post_status' ), 10, 3 ); + add_action( 'set_object_terms', array( __CLASS__, 'delete_product_query_transients' ) ); add_action( 'woocommerce_product_set_stock_status', array( __CLASS__, 'delete_product_query_transients' ) ); add_action( 'woocommerce_product_set_visibility', array( __CLASS__, 'delete_product_query_transients' ) ); add_action( 'woocommerce_product_type_changed', array( __CLASS__, 'product_type_changed' ), 10, 3 ); @@ -44,6 +42,7 @@ class WC_Post_Data { add_filter( 'oembed_response_data', array( __CLASS__, 'filter_oembed_response_data' ), 10, 2 ); // Status transitions. + add_action( 'transition_post_status', array( __CLASS__, 'transition_post_status' ), 10, 3 ); add_action( 'delete_post', array( __CLASS__, 'delete_post' ) ); add_action( 'wp_trash_post', array( __CLASS__, 'trash_post' ) ); add_action( 'untrashed_post', array( __CLASS__, 'untrash_post' ) ); @@ -98,25 +97,6 @@ class WC_Post_Data { } } - /** - * Delete transients when terms are set. - * - * @param int $object_id Object ID. - * @param mixed $terms An array of object terms. - * @param array $tt_ids An array of term taxonomy IDs. - * @param string $taxonomy Taxonomy slug. - * @param mixed $append Whether to append new terms to the old terms. - * @param array $old_tt_ids Old array of term taxonomy IDs. - */ - public static function set_object_terms( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) { - foreach ( array_merge( $tt_ids, $old_tt_ids ) as $id ) { - delete_transient( 'wc_ln_count_' . md5( sanitize_key( $taxonomy ) . sanitize_key( $id ) ) ); - } - if ( in_array( get_post_type( $object_id ), array( 'product', 'product_variation' ), true ) ) { - self::delete_product_query_transients(); - } - } - /** * When a post status changes. * @@ -266,17 +246,6 @@ class WC_Post_Data { return $check; } - /** - * When setting stock level, ensure the stock status is kept in sync. - * - * @param int $meta_id Meta ID. - * @param int $object_id Object ID. - * @param string $meta_key Meta key. - * @param mixed $meta_value Meta value. - * @deprecated - */ - public static function sync_product_stock_status( $meta_id, $object_id, $meta_key, $meta_value ) {} - /** * Forces the order posts to have a title in a certain format (containing the date). * Forces certain product data based on the product's type, e.g. grouped products cannot have a parent. @@ -496,18 +465,6 @@ class WC_Post_Data { } } - /** - * Update changed downloads. - * - * @deprecated 3.3.0 No action is necessary on changes to download paths since download_id is no longer based on file hash. - * @param int $product_id Product ID. - * @param int $variation_id Variation ID. Optional product variation identifier. - * @param array $downloads Newly set files. - */ - public static function process_product_file_download_paths( $product_id, $variation_id, $downloads ) { - wc_deprecated_function( __FUNCTION__, '3.3' ); - } - /** * Flush meta cache for CRUD objects on direct update. * @@ -540,6 +497,46 @@ class WC_Post_Data { } } } + + /** + * When setting stock level, ensure the stock status is kept in sync. + * + * @param int $meta_id Meta ID. + * @param int $object_id Object ID. + * @param string $meta_key Meta key. + * @param mixed $meta_value Meta value. + * @deprecated 3.3 + */ + public static function sync_product_stock_status( $meta_id, $object_id, $meta_key, $meta_value ) {} + + /** + * Update changed downloads. + * + * @deprecated 3.3.0 No action is necessary on changes to download paths since download_id is no longer based on file hash. + * @param int $product_id Product ID. + * @param int $variation_id Variation ID. Optional product variation identifier. + * @param array $downloads Newly set files. + */ + public static function process_product_file_download_paths( $product_id, $variation_id, $downloads ) { + wc_deprecated_function( __FUNCTION__, '3.3' ); + } + + /** + * Delete transients when terms are set. + * + * @deprecated 3.6 + * @param int $object_id Object ID. + * @param mixed $terms An array of object terms. + * @param array $tt_ids An array of term taxonomy IDs. + * @param string $taxonomy Taxonomy slug. + * @param mixed $append Whether to append new terms to the old terms. + * @param array $old_tt_ids Old array of term taxonomy IDs. + */ + public static function set_object_terms( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) { + if ( in_array( get_post_type( $object_id ), array( 'product', 'product_variation' ), true ) ) { + self::delete_product_query_transients(); + } + } } WC_Post_Data::init(); From a43a873a1344703141f8e022d0624f640da13f12 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 28 Feb 2019 16:27:57 +0000 Subject: [PATCH 379/401] Update placeholders --- assets/images/placeholder-attachment.png | Bin 215889 -> 103036 bytes assets/images/placeholder.png | Bin 9529 -> 15697 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/images/placeholder-attachment.png b/assets/images/placeholder-attachment.png index 82c49a723167c3ea0906fca63fa4bcb954567160..3a2c478366d9d798e486fb9d3016e710ad155b79 100644 GIT binary patch literal 103036 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 z zO*v~wtF>MkSshD$4>b{Grzky#&68IDvRGGE$XC@3VPhCfBB2}%_JoS=UTf;>W^qnY zquXz68rfG_^D`9(ADio}wx9nl5BXovprhIEuAPva_un7)zBO$pC-^Xyn$13Mz_x)I z$dCk(gju6_QOr$4f{jh{%Rhugx~M9PjI~q4;={Ze6ya7qSkDyVe}~p&z(a?O$gB5; z0mLlXzEUULgxs^sTF6KwQj7TN{^iGITcC3__kBMc#`m-?n2~&Y{te*IGdS&%x6s78 z0xfDq3j4?R1n8kk_5IvGuHFd9`Cry?i6Z_<3PPiRw{0Q}d$)1v&BWrzkIT-72-Nbv zFzFZnEwj%W>Ybxfp$) zv)R~FT^=O&a+;^N#2y=xx)NPLo4mx0V{`eLdhV3eU*|{q&tAST+-6#LYAiBDFuOQA zpLg6&0paFWD#VK~7wsQufH3z|d+X<2-fIkRz1LG!^Ckc(le;Ho8>ZlRUA)t6IMhb= zo8|!7u%;PjD%S#g0?%@^_N}g$e{eGOe*oMxDI~)vB{7m!xGfn`HOSt*J#Z=kd5Tmf zHT3*MmqBBW1~4PIKN15Sh^81=)>@>I%1%7~ybvdG9fXEt}df+&~EWOZ!aqQr<90Ws14I$ja%NS|s zNJV;KEil9*!NTcOg{>;87y4UmJbz+(g`Z9m!s({ep30OdfKaMt{{;WJFOfaWu^SvS zZ)+0Z#`WQ0Q>SSV8H2cBX!C5Cu|y~YM^N!48UL>s{XE$s+&V7DU4Ck z%0D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 literal 215889 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 diff --git a/assets/images/placeholder.png b/assets/images/placeholder.png index 0a03955706246580e85762e6d909bf13fd05fd04..c6b1cfa0846352424592b207636880e8c2e2f343 100644 GIT binary patch literal 15697 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ν(AgXFlx{ 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 E0L+U~Q2+n{ From a01ae4d0d6127d2a8a5468899da0be2eee202693 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 28 Feb 2019 16:32:50 +0000 Subject: [PATCH 380/401] Compress images --- assets/images/obw-mailchimp-icon.svg | 25 +---------------------- assets/images/placeholder-attachment.png | Bin 103036 -> 102644 bytes 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/assets/images/obw-mailchimp-icon.svg b/assets/images/obw-mailchimp-icon.svg index abb1b390891..f540158c66a 100644 --- a/assets/images/obw-mailchimp-icon.svg +++ b/assets/images/obw-mailchimp-icon.svg @@ -1,24 +1 @@ - - - - Mailchimp_Logo-Vertical_Black - Created with Sketch. - - - - - - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/assets/images/placeholder-attachment.png b/assets/images/placeholder-attachment.png index 3a2c478366d9d798e486fb9d3016e710ad155b79..09639d4a8570902b69758369ed1dd0c864988421 100644 GIT binary patch literal 102644 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 z zO*v~wtF>MkSshD$4>b{Grzky#&68IDvRGGE$XC@3VPhCfBB2}%_JoS=UTf;>W^qnY zquXz68rfG_^D`9(ADio}wx9nl5BXovprhIEuAPva_un7)zBO$pC-^Xyn$13Mz_x)I z$dCk(gju6_QOr$4f{jh{%Rhugx~M9PjI~q4;={Ze6ya7qSkDyVe}~p&z(a?O$gB5; z0mLlXzEUULgxs^sTF6KwQj7TN{^iGITcC3__kBMc#`m-?n2~&Y{te*IGdS&%x6s78 z0xfDq3j4?R1n8kk_5IvGuHFd9`Cry?i6Z_<3PPiRw{0Q}d$)1v&BWrzkIT-72-Nbv zFzFZnEwj%W>Ybxfp$) zv)R~FT^=O&a+;^N#2y=xx)NPLo4mx0V{`eLdhV3eU*|{q&tAST+-6#LYAiBDFuOQA zpLg6&0paFWD#VK~7wsQufH3z|d+X<2-fIkRz1LG!^Ckc(le;Ho8>ZlRUA)t6IMhb= zo8|!7u%;PjD%S#g0?%@^_N}g$e{eGOe*oMxDI~)vB{7m!xGfn`HOSt*J#Z=kd5Tmf zHT3*MmqBBW1~4PIKN15Sh^81=)>@>I%1%7~ybvdG9fXEt}df+&~EWOZ!aqQr<90Ws14I$ja%NS|s zNJV;KEil9*!NTcOg{>;87y4UmJbz+(g`Z9m!s({ep30OdfKaMt{{;WJFOfaWu^SvS zZ)+0Z#`WQ0Q>SSV8H2cBX!C5Cu|y~YM^N!48UL>s{XE$s+&V7DU4Ck z%0D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 From e442f46db3849da2c508eb8e509844307c3f6ec4 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Thu, 28 Feb 2019 21:56:43 +0100 Subject: [PATCH 381/401] Update class-wc-cart.php --- includes/class-wc-cart.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-cart.php b/includes/class-wc-cart.php index dea6f2d433e..6ba8058e383 100644 --- a/includes/class-wc-cart.php +++ b/includes/class-wc-cart.php @@ -634,7 +634,9 @@ class WC_Cart extends WC_Legacy_Cart { * @param bool $clear_persistent_cart Should the persistant cart be cleared too. Defaults to true. */ public function empty_cart( $clear_persistent_cart = true ) { - do_action( 'woocommerce_empty_cart' ); + + do_action( 'woocommerce_before_cart_emptied' ); + $this->cart_contents = array(); $this->removed_cart_contents = array(); $this->shipping_methods = array(); From b0f673a920f227b7e6bd96ac1b8fd84e9c639e34 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 1 Mar 2019 16:26:04 +0000 Subject: [PATCH 382/401] Update dependency lint-staged to v8.1.5 --- package-lock.json | 76 +++-------------------------------------------- package.json | 2 +- 2 files changed, 5 insertions(+), 73 deletions(-) diff --git a/package-lock.json b/package-lock.json index b0f2d535d65..0b530c23454 100644 --- a/package-lock.json +++ b/package-lock.json @@ -261,74 +261,6 @@ } } }, - "@iamstarkov/listr-update-renderer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@iamstarkov/listr-update-renderer/-/listr-update-renderer-0.4.1.tgz", - "integrity": "sha512-IJyxQWsYDEkf8C8QthBn5N8tIUR9V9je6j3sMIpAkonaadjbvxmRC6RAhpa3RKxndhNnU2M6iNbtJwd7usQYIA==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "cli-truncate": "^0.2.1", - "elegant-spinner": "^1.0.1", - "figures": "^1.7.0", - "indent-string": "^3.0.0", - "log-symbols": "^1.0.2", - "log-update": "^2.3.0", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - } - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "dev": true, - "requires": { - "chalk": "^1.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, "@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", @@ -6316,12 +6248,11 @@ } }, "lint-staged": { - "version": "8.1.4", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.1.4.tgz", - "integrity": "sha512-oFbbhB/VzN8B3i/sIdb9gMfngGArI6jIfxSn+WPdQb2Ni3GJeS6T4j5VriSbQfxfMuYoQlMHOoFt+lfcWV0HfA==", + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.1.5.tgz", + "integrity": "sha512-e5ZavfnSLcBJE1BTzRTqw6ly8OkqVyO3GL2M6teSmTBYQ/2BuueD5GIt2RPsP31u/vjKdexUyDCxSyK75q4BDA==", "dev": true, "requires": { - "@iamstarkov/listr-update-renderer": "0.4.1", "chalk": "^2.3.1", "commander": "^2.14.1", "cosmiconfig": "^5.0.2", @@ -6334,6 +6265,7 @@ "is-glob": "^4.0.0", "is-windows": "^1.0.2", "listr": "^0.14.2", + "listr-update-renderer": "^0.5.0", "lodash": "^4.17.11", "log-symbols": "^2.2.0", "micromatch": "^3.1.8", diff --git a/package.json b/package.json index 8b96423ddb4..8ccaa8d0830 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "grunt-wp-i18n": "1.0.3", "husky": "1.3.1", "istanbul": "1.0.0-alpha.2", - "lint-staged": "8.1.4", + "lint-staged": "8.1.5", "mocha": "6.0.2", "node-sass": "4.11.0", "prettier": "github:automattic/calypso-prettier#c56b4251", From e131ba5358b32cc248f7c81a46609adec4035709 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Fri, 1 Mar 2019 20:01:03 -0300 Subject: [PATCH 383/401] Updated per feedback --- includes/api/class-wc-rest-orders-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/api/class-wc-rest-orders-controller.php b/includes/api/class-wc-rest-orders-controller.php index 7c6cd06a91e..d2bb6f06663 100644 --- a/includes/api/class-wc-rest-orders-controller.php +++ b/includes/api/class-wc-rest-orders-controller.php @@ -257,7 +257,7 @@ class WC_REST_Orders_Controller extends WC_REST_Orders_V2_Controller { $params['status'] = array( 'default' => 'any', - 'description' => __( 'Limit result set to orders assigned to specific statuses.', 'woocommerce' ), + 'description' => __( 'Limit result set to orders which have specific statuses.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'string', From c21573ac9c55f519cd27f270350a7fa620a0e2d0 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 2 Mar 2019 03:10:58 +0000 Subject: [PATCH 384/401] Update dependency eslint to v5.15.0 --- package-lock.json | 26 +++++++++++++------------- package.json | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index b0f2d535d65..424dd37e2f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -394,9 +394,9 @@ "dev": true }, "acorn": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.0.tgz", - "integrity": "sha512-MW/FjM+IvU9CgBzjO3UIPCE2pyEwUsoFl+VGdczOPEdxfGFjuKny/gN54mOuX7Qxmb9Rg9MCn2oKiSUeW+pjrw==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", "dev": true }, "acorn-jsx": { @@ -3141,9 +3141,9 @@ "dev": true }, "eslint": { - "version": "5.14.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.14.1.tgz", - "integrity": "sha512-CyUMbmsjxedx8B0mr79mNOqetvkbij/zrXnFeK2zc3pGRn3/tibjiNAv/3UxFEyfMDjh+ZqTrJrEGBFiGfD5Og==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.15.0.tgz", + "integrity": "sha512-xwG7SS5JLeqkiR3iOmVgtF8Y6xPdtr6AAsN6ph7Q6R/fv+3UlKYoika8SmNzmb35qdRF+RfTY35kMEdtbi+9wg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -3152,7 +3152,7 @@ "cross-spawn": "^6.0.5", "debug": "^4.0.1", "doctrine": "^3.0.0", - "eslint-scope": "^4.0.0", + "eslint-scope": "^4.0.2", "eslint-utils": "^1.3.1", "eslint-visitor-keys": "^1.0.0", "espree": "^5.0.1", @@ -3185,9 +3185,9 @@ }, "dependencies": { "ajv": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", - "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.2.tgz", + "integrity": "sha512-4UFy0/LgDo7Oa/+wOAlj44tp9K78u38E5/359eSrqEp1Z5PdVfimCcs7SluXMP755RUQu6d2b4AvF0R1C9RZjg==", "dev": true, "requires": { "fast-deep-equal": "^2.0.1", @@ -3218,9 +3218,9 @@ "dev": true }, "eslint-scope": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", - "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.2.tgz", + "integrity": "sha512-5q1+B/ogmHl8+paxtOKx38Z8LtWkVGuNt3+GQNErqwLl6ViNp/gdJGMCjZNxZ8j/VYjDNZ2Fo+eQc1TAVPIzbg==", "dev": true, "requires": { "esrecurse": "^4.1.0", diff --git a/package.json b/package.json index 8b96423ddb4..17f1cd763d7 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "chromedriver": "2.46.0", "config": "3.0.1", "cross-env": "5.2.0", - "eslint": "5.14.1", + "eslint": "5.15.0", "eslint-config-wpcalypso": "4.0.1", "eslint-plugin-wpcalypso": "4.0.2", "grunt": "1.0.3", From 26af79c8d7b2880379ed579a641a0f10b507dd3d Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 4 Mar 2019 09:12:19 +0100 Subject: [PATCH 385/401] Update issue templates Added 'template' for security issue. --- .github/ISSUE_TEMPLATE/-----security-issue.md | 12 ++++ .github/ISSUE_TEMPLATE/Bug_report.md | 71 ++++++++++--------- .github/ISSUE_TEMPLATE/Enhancement.md | 6 +- .github/ISSUE_TEMPLATE/Feature_request.md | 38 +++++----- .github/ISSUE_TEMPLATE/Support.md | 47 ++++++------ 5 files changed, 100 insertions(+), 74 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/-----security-issue.md diff --git a/.github/ISSUE_TEMPLATE/-----security-issue.md b/.github/ISSUE_TEMPLATE/-----security-issue.md new file mode 100644 index 00000000000..6c5ca091158 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/-----security-issue.md @@ -0,0 +1,12 @@ +--- +name: "\U0001F46E‍♂️Security issue" +about: Please report security issues *only* via https://www.hackerone.com +title: '' +labels: '' +assignees: '' + +--- + +For security reasons, please report all security issues via https://www.hackerone.com. Also, if the issue is valid, a bug bounty will be paid out to you. + +Please disclose responsibly and not via GitHub (which allows for exploiting issues in the wild before the patch is released). diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 1caed3aa63c..261ca9edb8b 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -1,34 +1,37 @@ ---- -name: "\U0001F41E Bug report" -about: Report a bug if something isn't working as expected in the core WooCommerce - plugin. - ---- - -**Describe the bug** -A clear and concise description of what the bug is. Please be as descriptive as possible; issues lacking detail, or for any other reason than to report a bug, may be closed without action. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Isolating the problem (mark completed items with an [x]):** -- [ ] I have deactivated other plugins and confirmed this bug occurs when only WooCommerce plugin is active. -- [ ] This bug happens with a default WordPress theme active, or [Storefront](https://woocommerce.com/storefront/). -- [ ] I can reproduce this bug consistently using the steps above. - -**WordPress Environment** -
    -``` -Copy and paste the system status report from **WooCommerce > System Status** in WordPress admin. -``` -
    +--- +name: "\U0001F41E Bug report" +about: Report a bug if something isn't working as expected in the core WooCommerce + plugin. +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. Please be as descriptive as possible; issues lacking detail, or for any other reason than to report a bug, may be closed without action. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Isolating the problem (mark completed items with an [x]):** +- [ ] I have deactivated other plugins and confirmed this bug occurs when only WooCommerce plugin is active. +- [ ] This bug happens with a default WordPress theme active, or [Storefront](https://woocommerce.com/storefront/). +- [ ] I can reproduce this bug consistently using the steps above. + +**WordPress Environment** +
    +``` +Copy and paste the system status report from **WooCommerce > System Status** in WordPress admin. +``` +
    diff --git a/.github/ISSUE_TEMPLATE/Enhancement.md b/.github/ISSUE_TEMPLATE/Enhancement.md index cb690414090..9f625ac6afc 100644 --- a/.github/ISSUE_TEMPLATE/Enhancement.md +++ b/.github/ISSUE_TEMPLATE/Enhancement.md @@ -1,6 +1,10 @@ --- name: "✨ New Enhancement" -about: "If you have an idea to improve an existing feature in core or need something for development (such as a new hook) please let us know or submit a Pull Request!" +about: If you have an idea to improve an existing feature in core or need something + for development (such as a new hook) please let us know or submit a Pull Request! +title: '' +labels: '' +assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md index e7e5ee9021b..4b05fbc4035 100644 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -1,17 +1,21 @@ ---- -name: "\U0001F680 Feature request" -about: "Suggest a new feature \U0001F389 We'll consider building it if it receives sufficient interest! \U0001F44D" - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. +--- +name: "\U0001F680 Feature request" +about: "Suggest a new feature \U0001F389 We'll consider building it if it receives + sufficient interest! \U0001F44D" +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/Support.md b/.github/ISSUE_TEMPLATE/Support.md index 33beabefdbe..6804c6d2c99 100644 --- a/.github/ISSUE_TEMPLATE/Support.md +++ b/.github/ISSUE_TEMPLATE/Support.md @@ -1,22 +1,25 @@ ---- -name: "❓ Support Question" -about: "If you have a question \U0001F4AC please see our docs or use our forums, helpdesk, - or Slack Community!" - ---- - -We don't offer technical support on GitHub so we recommend using the following: - -**Reading our documentation** -Usage docs can be found here: https://docs.woocommerce.com/ - -If you have a problem, you may want to start with the self help guide here: https://docs.woocommerce.com/document/woocommerce-self-service-guide/ - -**Technical support for premium extensions or if you're a WooCommerce.com customer** - from a human being - submit a ticket via the helpdesk -https://woocommerce.com/contact-us/ - -**General usage and development questions** -- WooCommerce Slack Community: https://woocommerce.com/community-slack/ -- WordPress.org Forums: https://wordpress.org/support/plugin/woocommerce -- The WooCommerce Help and Share Facebook group +--- +name: "❓ Support Question" +about: "If you have a question \U0001F4AC please see our docs or use our forums, helpdesk, + or Slack Community!" +title: '' +labels: '' +assignees: '' + +--- + +We don't offer technical support on GitHub so we recommend using the following: + +**Reading our documentation** +Usage docs can be found here: https://docs.woocommerce.com/ + +If you have a problem, you may want to start with the self help guide here: https://docs.woocommerce.com/document/woocommerce-self-service-guide/ + +**Technical support for premium extensions or if you're a WooCommerce.com customer** + from a human being - submit a ticket via the helpdesk +https://woocommerce.com/contact-us/ + +**General usage and development questions** +- WooCommerce Slack Community: https://woocommerce.com/community-slack/ +- WordPress.org Forums: https://wordpress.org/support/plugin/woocommerce +- The WooCommerce Help and Share Facebook group From bf7571cc5b1ec3c0749a957e8aefcfb42a0ee9b8 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 4 Mar 2019 09:16:28 +0100 Subject: [PATCH 386/401] Update -----security-issue.md Added more direct link to Automattic's HackerOne. --- .github/ISSUE_TEMPLATE/-----security-issue.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/-----security-issue.md b/.github/ISSUE_TEMPLATE/-----security-issue.md index 6c5ca091158..68278bb4da4 100644 --- a/.github/ISSUE_TEMPLATE/-----security-issue.md +++ b/.github/ISSUE_TEMPLATE/-----security-issue.md @@ -7,6 +7,6 @@ assignees: '' --- -For security reasons, please report all security issues via https://www.hackerone.com. Also, if the issue is valid, a bug bounty will be paid out to you. +For security reasons, please report all security issues via https://hackerone.com/automattic/. Also, if the issue is valid, a bug bounty will be paid out to you. Please disclose responsibly and not via GitHub (which allows for exploiting issues in the wild before the patch is released). From 41de832580dce027a18340b2edca1b842bb33f19 Mon Sep 17 00:00:00 2001 From: Tim Howe Date: Thu, 28 Feb 2019 16:10:30 -0500 Subject: [PATCH 387/401] #22912 --- includes/class-wc-structured-data.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-structured-data.php b/includes/class-wc-structured-data.php index 817c8224f45..33be29369d3 100644 --- a/includes/class-wc-structured-data.php +++ b/includes/class-wc-structured-data.php @@ -28,7 +28,6 @@ class WC_Structured_Data { // Generate structured data. add_action( 'woocommerce_before_main_content', array( $this, 'generate_website_data' ), 30 ); add_action( 'woocommerce_breadcrumb', array( $this, 'generate_breadcrumblist_data' ), 10 ); - add_action( 'woocommerce_shop_loop', array( $this, 'generate_product_data' ), 10 ); add_action( 'woocommerce_single_product_summary', array( $this, 'generate_product_data' ), 60 ); add_action( 'woocommerce_review_meta', array( $this, 'generate_review_data' ), 20 ); add_action( 'woocommerce_email_order_details', array( $this, 'generate_order_data' ), 20, 3 ); @@ -153,7 +152,7 @@ class WC_Structured_Data { $data = $this->get_structured_data( $types ); if ( $data ) { - echo ''; + echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } @@ -208,7 +207,7 @@ class WC_Structured_Data { $markup['sku'] = $product->get_sku(); $markup['brand'] = ''; - if ( apply_filters( 'woocommerce_structured_data_product_limit', is_product_taxonomy() || is_shop() ) ) { + if ( apply_filters( 'woocommerce_structured_data_product_limit', false ) ) { $markup['url'] = $permalink; $this->set_data( apply_filters( 'woocommerce_structured_data_product_limited', $markup, $product ) ); From 60d8a10bcffc3208eaf94da94dea1ce42936c420 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Mar 2019 11:44:52 +0000 Subject: [PATCH 388/401] Remove woocommerce_structured_data_product_limit With the removal of generation on archives, the filters `woocommerce_structured_data_product_limit` and `woocommerce_structured_data_product_limited` have no context nor purpose. This commit removes them so full schema is generated when used. --- includes/class-wc-structured-data.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/includes/class-wc-structured-data.php b/includes/class-wc-structured-data.php index 33be29369d3..1472146bf5f 100644 --- a/includes/class-wc-structured-data.php +++ b/includes/class-wc-structured-data.php @@ -207,13 +207,6 @@ class WC_Structured_Data { $markup['sku'] = $product->get_sku(); $markup['brand'] = ''; - if ( apply_filters( 'woocommerce_structured_data_product_limit', false ) ) { - $markup['url'] = $permalink; - - $this->set_data( apply_filters( 'woocommerce_structured_data_product_limited', $markup, $product ) ); - return; - } - if ( '' !== $product->get_price() ) { if ( $product->is_type( 'variable' ) ) { $lowest = $product->get_variation_price( 'min', false ); From 6caa0fab61e68948612309901b19138c1e458284 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Mar 2019 12:47:24 +0000 Subject: [PATCH 389/401] Tidy data --- includes/class-wc-structured-data.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-structured-data.php b/includes/class-wc-structured-data.php index 1472146bf5f..ab00dfaa348 100644 --- a/includes/class-wc-structured-data.php +++ b/includes/class-wc-structured-data.php @@ -197,9 +197,11 @@ class WC_Structured_Data { $permalink = get_permalink( $product->get_id() ); $markup = array( - '@type' => 'Product', - '@id' => $permalink . '#product', // Append '#product' to differentiate between this @id and the @id generated for the Breadcrumblist. - 'name' => $product->get_name(), + '@type' => 'Product', + '@id' => $permalink . '#product', // Append '#product' to differentiate between this @id and the @id generated for the Breadcrumblist. + 'name' => $product->get_name(), + 'image' => wp_get_attachment_url( $product->get_image_id() ), + 'description' => wp_strip_all_tags( do_shortcode( $product->get_short_description() ? $product->get_short_description() : $product->get_description() ) ), ); $markup['image'] = wp_get_attachment_url( $product->get_image_id() ); From 52533d9da6bc48b0b51796d1505f7816b9445b26 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Mar 2019 12:47:31 +0000 Subject: [PATCH 390/401] Add URL field --- includes/class-wc-structured-data.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/class-wc-structured-data.php b/includes/class-wc-structured-data.php index ab00dfaa348..40e64b17b58 100644 --- a/includes/class-wc-structured-data.php +++ b/includes/class-wc-structured-data.php @@ -200,6 +200,7 @@ class WC_Structured_Data { '@type' => 'Product', '@id' => $permalink . '#product', // Append '#product' to differentiate between this @id and the @id generated for the Breadcrumblist. 'name' => $product->get_name(), + 'url' => $permalink, 'image' => wp_get_attachment_url( $product->get_image_id() ), 'description' => wp_strip_all_tags( do_shortcode( $product->get_short_description() ? $product->get_short_description() : $product->get_description() ) ), ); From d104bb7d3f53e73a7400cb4632f4bc7de20dec36 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Mar 2019 12:47:40 +0000 Subject: [PATCH 391/401] Add SKU with ID fallback --- includes/class-wc-structured-data.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/includes/class-wc-structured-data.php b/includes/class-wc-structured-data.php index 40e64b17b58..a31d31ed6f1 100644 --- a/includes/class-wc-structured-data.php +++ b/includes/class-wc-structured-data.php @@ -205,10 +205,12 @@ class WC_Structured_Data { 'description' => wp_strip_all_tags( do_shortcode( $product->get_short_description() ? $product->get_short_description() : $product->get_description() ) ), ); - $markup['image'] = wp_get_attachment_url( $product->get_image_id() ); - $markup['description'] = wp_strip_all_tags( do_shortcode( $product->get_short_description() ? $product->get_short_description() : $product->get_description() ) ); - $markup['sku'] = $product->get_sku(); - $markup['brand'] = ''; + // Declare SKU or fallback to ID. + if ( $product->get_sku() ) { + $markup['sku'] = $product->get_sku(); + } else { + $markup['sku'] = $product->get_id(); + } if ( '' !== $product->get_price() ) { if ( $product->is_type( 'variable' ) ) { From 246c78db732f86a0ea2c1d93798ed71cfa874b86 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Mar 2019 13:05:29 +0000 Subject: [PATCH 392/401] Add a review to product schema --- includes/class-wc-structured-data.php | 42 ++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-structured-data.php b/includes/class-wc-structured-data.php index a31d31ed6f1..4c0b6adfe88 100644 --- a/includes/class-wc-structured-data.php +++ b/includes/class-wc-structured-data.php @@ -268,6 +268,44 @@ class WC_Structured_Data { ); } + // Markup most recent rating/review. + $comments = get_comments( + array( + 'number' => 1, + 'post_id' => $product->get_id(), + 'status' => 'approve', + 'post_status' => 'publish', + 'post_type' => 'product', + 'parent' => 0, + 'meta_key' => 'rating', + 'orderby' => 'meta_value_num', + ) + ); + + if ( $comments ) { + foreach ( $comments as $comment ) { + $rating = get_comment_meta( $comment->comment_ID, 'rating', true ); + + if ( ! $rating ) { + continue; + } + + $markup['review'] = array( + '@type' => 'Review', + 'reviewRating' => array( + '@type' => 'Rating', + 'ratingValue' => $rating, + 'bestRating' => 5, + 'worstRating' => 1, + ), + 'author' => array( + '@type' => 'Person', + 'name' => get_comment_author( $comment->comment_ID ), + ), + ); + } + } + $this->set_data( apply_filters( 'woocommerce_structured_data_product', $markup, $product ) ); } @@ -294,8 +332,10 @@ class WC_Structured_Data { if ( $rating ) { $markup['reviewRating'] = array( - '@type' => 'rating', + '@type' => 'Rating', 'ratingValue' => $rating, + 'bestRating' => 5, + 'worstRating' => 1, ); } elseif ( $comment->comment_parent ) { return; From 5937625b5e9f7adea541cc995fac1e2719feede2 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Mar 2019 13:17:27 +0000 Subject: [PATCH 393/401] Include priceValidUntil --- includes/class-wc-structured-data.php | 74 +++++++++++++++------------ 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/includes/class-wc-structured-data.php b/includes/class-wc-structured-data.php index 4c0b6adfe88..5534a03e9f5 100644 --- a/includes/class-wc-structured-data.php +++ b/includes/class-wc-structured-data.php @@ -213,6 +213,9 @@ class WC_Structured_Data { } if ( '' !== $product->get_price() ) { + // Assume prices will be valid until the end of next year, unless on sale and there is an end date. + $price_valid_until = date( 'Y-12-31', current_time( 'timestamp', true ) + YEAR_IN_SECONDS ); + if ( $product->is_type( 'variable' ) ) { $lowest = $product->get_variation_price( 'min', false ); $highest = $product->get_variation_price( 'max', false ); @@ -221,6 +224,7 @@ class WC_Structured_Data { $markup_offer = array( '@type' => 'Offer', 'price' => wc_format_decimal( $lowest, wc_get_price_decimals() ), + 'priceValidUntil' => $price_valid_until, 'priceSpecification' => array( 'price' => wc_format_decimal( $lowest, wc_get_price_decimals() ), 'priceCurrency' => $currency, @@ -235,9 +239,13 @@ class WC_Structured_Data { ); } } else { + if ( $product->is_on_sale() && $product->get_date_on_sale_to() ) { + $price_valid_until = date( 'Y-m-d', $product->get_date_on_sale_to()->getTimestamp() ); + } $markup_offer = array( '@type' => 'Offer', 'price' => wc_format_decimal( $product->get_price(), wc_get_price_decimals() ), + 'priceValidUntil' => $price_valid_until, 'priceSpecification' => array( 'price' => wc_format_decimal( $product->get_price(), wc_get_price_decimals() ), 'priceCurrency' => $currency, @@ -266,43 +274,43 @@ class WC_Structured_Data { 'ratingValue' => $product->get_average_rating(), 'reviewCount' => $product->get_review_count(), ); - } - // Markup most recent rating/review. - $comments = get_comments( - array( - 'number' => 1, - 'post_id' => $product->get_id(), - 'status' => 'approve', - 'post_status' => 'publish', - 'post_type' => 'product', - 'parent' => 0, - 'meta_key' => 'rating', - 'orderby' => 'meta_value_num', - ) - ); + // Markup most recent rating/review. + $comments = get_comments( + array( + 'number' => 1, + 'post_id' => $product->get_id(), + 'status' => 'approve', + 'post_status' => 'publish', + 'post_type' => 'product', + 'parent' => 0, + 'meta_key' => 'rating', + 'orderby' => 'meta_value_num', + ) + ); - if ( $comments ) { - foreach ( $comments as $comment ) { - $rating = get_comment_meta( $comment->comment_ID, 'rating', true ); + if ( $comments ) { + foreach ( $comments as $comment ) { + $rating = get_comment_meta( $comment->comment_ID, 'rating', true ); - if ( ! $rating ) { - continue; + if ( ! $rating ) { + continue; + } + + $markup['review'] = array( + '@type' => 'Review', + 'reviewRating' => array( + '@type' => 'Rating', + 'ratingValue' => $rating, + 'bestRating' => 5, + 'worstRating' => 1, + ), + 'author' => array( + '@type' => 'Person', + 'name' => get_comment_author( $comment->comment_ID ), + ), + ); } - - $markup['review'] = array( - '@type' => 'Review', - 'reviewRating' => array( - '@type' => 'Rating', - 'ratingValue' => $rating, - 'bestRating' => 5, - 'worstRating' => 1, - ), - 'author' => array( - '@type' => 'Person', - 'name' => get_comment_author( $comment->comment_ID ), - ), - ); } } From 8147b82b881f408714a41f8b65cb2e6c2f231e05 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 4 Mar 2019 14:17:11 +0000 Subject: [PATCH 394/401] Feedback --- includes/class-wc-structured-data.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/includes/class-wc-structured-data.php b/includes/class-wc-structured-data.php index 5534a03e9f5..86ea80a236b 100644 --- a/includes/class-wc-structured-data.php +++ b/includes/class-wc-structured-data.php @@ -302,8 +302,6 @@ class WC_Structured_Data { 'reviewRating' => array( '@type' => 'Rating', 'ratingValue' => $rating, - 'bestRating' => 5, - 'worstRating' => 1, ), 'author' => array( '@type' => 'Person', @@ -342,8 +340,6 @@ class WC_Structured_Data { $markup['reviewRating'] = array( '@type' => 'Rating', 'ratingValue' => $rating, - 'bestRating' => 5, - 'worstRating' => 1, ); } elseif ( $comment->comment_parent ) { return; From ab5a3ca519409c6ed2d2974990dd0ba3ca4a84b6 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 5 Mar 2019 01:40:21 +0000 Subject: [PATCH 395/401] Update dependency autoprefixer to v9.4.10 --- package-lock.json | 20 ++++++++++---------- package.json | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index dd52fe4461d..30047730936 100644 --- a/package-lock.json +++ b/package-lock.json @@ -573,13 +573,13 @@ "dev": true }, "autoprefixer": { - "version": "9.4.9", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.9.tgz", - "integrity": "sha512-OyUl7KvbGBoFQbGQu51hMywz1aaVeud/6uX8r1R1DNcqFvqGUUy6+BDHnAZE8s5t5JyEObaSw+O1DpAdjAmLuw==", + "version": "9.4.10", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.10.tgz", + "integrity": "sha512-XR8XZ09tUrrSzgSlys4+hy5r2/z4Jp7Ag3pHm31U4g/CTccYPOVe19AkaJ4ey/vRd1sfj+5TtuD6I0PXtutjvQ==", "dev": true, "requires": { "browserslist": "^4.4.2", - "caniuse-lite": "^1.0.30000939", + "caniuse-lite": "^1.0.30000940", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", "postcss": "^7.0.14", @@ -2098,9 +2098,9 @@ } }, "caniuse-lite": { - "version": "1.0.30000939", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000939.tgz", - "integrity": "sha512-oXB23ImDJOgQpGjRv1tCtzAvJr4/OvrHi5SO2vUgB0g0xpdZZoA/BxfImiWfdwoYdUTtQrPsXsvYU/dmCSM8gg==", + "version": "1.0.30000940", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000940.tgz", + "integrity": "sha512-rp/086IBUfCsNgBpko6DGQv674jRjeXPesDatDB2kxrkmDfD+S5Gesw+uT8YjpRWvLKLMRBy72SLRZ8I0EgQFw==", "dev": true }, "caseless": { @@ -7857,9 +7857,9 @@ } }, "node-releases": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.8.tgz", - "integrity": "sha512-gQm+K9mGCiT/NXHy+V/ZZS1N/LOaGGqRAAJJs3X9Ah1g+CIbRcBgNyoNYQ+SEtcyAtB9KqDruu+fF7nWjsqRaA==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.9.tgz", + "integrity": "sha512-oic3GT4OtbWWKfRolz5Syw0Xus0KRFxeorLNj0s93ofX6PWyuzKjsiGxsCtWktBwwmTF6DdRRf2KreGqeOk5KA==", "dev": true, "requires": { "semver": "^5.3.0" diff --git a/package.json b/package.json index c80cfb78e46..0ba68285b31 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "git:update-hooks": "rm -r .git/hooks && mkdir -p .git/hooks && node ./node_modules/husky/husky.js install" }, "devDependencies": { - "autoprefixer": "9.4.9", + "autoprefixer": "9.4.10", "babel": "6.23.0", "babel-cli": "6.26.0", "babel-eslint": "10.0.1", From 686727ada1fcc04f008f7789cbaea0e1374c6c25 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 5 Mar 2019 03:12:39 +0000 Subject: [PATCH 396/401] Update dependency eslint to v5.15.1 --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index dd52fe4461d..a438f6ccf82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3073,9 +3073,9 @@ "dev": true }, "eslint": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.15.0.tgz", - "integrity": "sha512-xwG7SS5JLeqkiR3iOmVgtF8Y6xPdtr6AAsN6ph7Q6R/fv+3UlKYoika8SmNzmb35qdRF+RfTY35kMEdtbi+9wg==", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.15.1.tgz", + "integrity": "sha512-NTcm6vQ+PTgN3UBsALw5BMhgO6i5EpIjQF/Xb5tIh3sk9QhrFafujUOczGz4J24JBlzWclSB9Vmx8d+9Z6bFCg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -3117,9 +3117,9 @@ }, "dependencies": { "ajv": { - "version": "6.9.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.2.tgz", - "integrity": "sha512-4UFy0/LgDo7Oa/+wOAlj44tp9K78u38E5/359eSrqEp1Z5PdVfimCcs7SluXMP755RUQu6d2b4AvF0R1C9RZjg==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", "dev": true, "requires": { "fast-deep-equal": "^2.0.1", diff --git a/package.json b/package.json index c80cfb78e46..a2039f5d2ae 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "chromedriver": "2.46.0", "config": "3.0.1", "cross-env": "5.2.0", - "eslint": "5.15.0", + "eslint": "5.15.1", "eslint-config-wpcalypso": "4.0.1", "eslint-plugin-wpcalypso": "4.0.2", "grunt": "1.0.3", From bf11dbf76f5eead7b9966cc30c8a17e6bcf4937f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Mar 2019 11:38:53 +0000 Subject: [PATCH 397/401] If there is no url on last breadcrumb trail, use current url Fixes #22923 --- includes/class-wc-structured-data.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/includes/class-wc-structured-data.php b/includes/class-wc-structured-data.php index 86ea80a236b..f4e57e7ab4c 100644 --- a/includes/class-wc-structured-data.php +++ b/includes/class-wc-structured-data.php @@ -119,7 +119,7 @@ class WC_Structured_Data { $types[] = is_shop() || is_product_category() || is_product() ? 'product' : ''; $types[] = is_shop() && is_front_page() ? 'website' : ''; $types[] = is_product() ? 'review' : ''; - $types[] = ! is_shop() ? 'breadcrumblist' : ''; + $types[] = 'breadcrumblist'; $types[] = 'order'; return array_filter( apply_filters( 'woocommerce_structured_data_type_for_page', $types ) ); @@ -382,6 +382,10 @@ class WC_Structured_Data { if ( ! empty( $crumb[1] ) ) { $markup['itemListElement'][ $key ]['item'] += array( '@id' => $crumb[1] ); + } elseif ( isset( $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'] ) ) { + $current_url = set_url_scheme( 'http://' . wp_unslash( $_SERVER['HTTP_HOST'] ) . wp_unslash( $_SERVER['REQUEST_URI'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + + $markup['itemListElement'][ $key ]['item'] += array( '@id' => $current_url ); } } From 545cbbb79c9440a172a6e2dc9be125fdef34ef04 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Mar 2019 11:44:26 +0000 Subject: [PATCH 398/401] Check for required fields or bail --- includes/class-wc-structured-data.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/includes/class-wc-structured-data.php b/includes/class-wc-structured-data.php index f4e57e7ab4c..e22e8c63d32 100644 --- a/includes/class-wc-structured-data.php +++ b/includes/class-wc-structured-data.php @@ -312,6 +312,11 @@ class WC_Structured_Data { } } + // Check we have required data. + if ( empty( $markup['aggregateRating'] ) && empty( $markup['offers'] ) && empty( $markup['review'] ) ) { + return; + } + $this->set_data( apply_filters( 'woocommerce_structured_data_product', $markup, $product ) ); } From a49d4a04a197afc77b600d3cdd6840e4f8d5dc0c Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Mar 2019 13:05:55 +0000 Subject: [PATCH 399/401] Update throws tags --- includes/class-wc-ajax.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php index dd5bc665856..2d9dd880609 100644 --- a/includes/class-wc-ajax.php +++ b/includes/class-wc-ajax.php @@ -1050,7 +1050,7 @@ class WC_AJAX { /** * Add order tax column via ajax. * - * @throws Exception If order is invalid. + * @throws Exception If order or tax rate is invalid. */ public static function add_order_tax() { check_ajax_referer( 'order-item', 'security' ); @@ -1097,7 +1097,7 @@ class WC_AJAX { /** * Add order discount via ajax. * - * @throws Exception If order is invalid. + * @throws Exception If order or coupon is invalid. */ public static function add_coupon_discount() { check_ajax_referer( 'order-item', 'security' ); @@ -1140,7 +1140,7 @@ class WC_AJAX { /** * Remove coupon from an order via ajax. * - * @throws Exception If order is invalid. + * @throws Exception If order or coupon is invalid. */ public static function remove_order_coupon() { check_ajax_referer( 'order-item', 'security' ); @@ -1237,7 +1237,7 @@ class WC_AJAX { /** * Remove an order tax. * - * @throws Exception If order is invalid. + * @throws Exception If there is an error whilst deleting the rate. */ public static function remove_order_tax() { check_ajax_referer( 'order-item', 'security' ); @@ -1827,7 +1827,7 @@ class WC_AJAX { /** * Create/Update API key. * - * @throws Exception On invalid API key. + * @throws Exception On invalid or empty description, user, or permissions. */ public static function update_api_key() { ob_start(); From 46bdad07e99a0d441b9e276ce50250926a913e66 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 5 Mar 2019 14:51:58 +0000 Subject: [PATCH 400/401] Array init code was wrong --- includes/class-wc-ajax.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-ajax.php b/includes/class-wc-ajax.php index 182f30d0e21..e0981312932 100644 --- a/includes/class-wc-ajax.php +++ b/includes/class-wc-ajax.php @@ -1830,7 +1830,7 @@ class WC_AJAX { // Prepare line items which we are refunding. $line_items = array(); - $item_ids = array_unique( array_merge( array_keys( $line_item_qtys, $line_item_totals, true ) ) ); + $item_ids = array_unique( array_merge( array_keys( $line_item_qtys ), array_keys( $line_item_totals ) ) ); foreach ( $item_ids as $item_id ) { $line_items[ $item_id ] = array( From b33554a4bf02cc5f6e305aaeecb3786a1c3c9dec Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 6 Mar 2019 09:45:57 +0000 Subject: [PATCH 401/401] Update troubleshooting link --- templates/emails/admin-failed-order.php | 2 +- templates/emails/plain/admin-failed-order.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/emails/admin-failed-order.php b/templates/emails/admin-failed-order.php index 5c96812aa88..5e174d1e7fb 100644 --- a/templates/emails/admin-failed-order.php +++ b/templates/emails/admin-failed-order.php @@ -49,7 +49,7 @@ do_action( 'woocommerce_email_order_meta', $order, $sent_to_admin, $plain_text, do_action( 'woocommerce_email_customer_details', $order, $sent_to_admin, $plain_text, $email ); ?>
    troubleshooting failed payments.', 'woocommerce' ) . "\n\n"; +echo esc_html__( 'Hopefully they’ll be back. Read more about troubleshooting failed payments.', 'woocommerce' ) . "\n\n"; echo "\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n";

    -troubleshooting failed payments.', 'woocommerce' ) ); ?> +troubleshooting failed payments.', 'woocommerce' ) ); ?>