From 555d680f6e45ae70dab2ccbdfb01205af8c84bd4 Mon Sep 17 00:00:00 2001 From: mksunlab Date: Fri, 16 Mar 2018 10:25:37 +0100 Subject: [PATCH 01/34] add autocomplete attributes to account and register forms patch for issue #19425 --- templates/myaccount/form-login.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/myaccount/form-login.php b/templates/myaccount/form-login.php index e9f0f0ab429..3f7e7c7723e 100644 --- a/templates/myaccount/form-login.php +++ b/templates/myaccount/form-login.php @@ -42,11 +42,11 @@ if ( ! defined( 'ABSPATH' ) ) {

- +

- +

@@ -82,21 +82,21 @@ if ( ! defined( 'ABSPATH' ) ) {

- +

- +

- +

From 6a29551979d474203a5ee9176f23811743caff76 Mon Sep 17 00:00:00 2001 From: mksunlab Date: Fri, 16 Mar 2018 11:00:54 +0100 Subject: [PATCH 02/34] Revert "add autocomplete attributes to account and register forms" This reverts commit 555d680f6e45ae70dab2ccbdfb01205af8c84bd4. --- templates/myaccount/form-login.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/myaccount/form-login.php b/templates/myaccount/form-login.php index 3f7e7c7723e..e9f0f0ab429 100644 --- a/templates/myaccount/form-login.php +++ b/templates/myaccount/form-login.php @@ -42,11 +42,11 @@ if ( ! defined( 'ABSPATH' ) ) {

- +

- +

@@ -82,21 +82,21 @@ if ( ! defined( 'ABSPATH' ) ) {

- +

- +

- +

From 5a19f00c5a3532843b6bbca2142d378d317d27b8 Mon Sep 17 00:00:00 2001 From: michakrapp Date: Fri, 16 Mar 2018 11:07:02 +0100 Subject: [PATCH 03/34] accout login autocomplete - commit with correct user account on first commit used false user account --- templates/myaccount/form-login.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/myaccount/form-login.php b/templates/myaccount/form-login.php index e9f0f0ab429..3f7e7c7723e 100644 --- a/templates/myaccount/form-login.php +++ b/templates/myaccount/form-login.php @@ -42,11 +42,11 @@ if ( ! defined( 'ABSPATH' ) ) {

- +

- +

@@ -82,21 +82,21 @@ if ( ! defined( 'ABSPATH' ) ) {

- +

- +

- +

From 7145b8f7291e94965b8692a485aab9ee356a0005 Mon Sep 17 00:00:00 2001 From: michakrapp Date: Mon, 19 Mar 2018 08:53:53 +0100 Subject: [PATCH 04/34] template global form-login autocomplete attributes --- templates/global/form-login.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/global/form-login.php b/templates/global/form-login.php index 2f509421eff..f335396844f 100644 --- a/templates/global/form-login.php +++ b/templates/global/form-login.php @@ -33,11 +33,11 @@ if ( is_user_logged_in() ) {

- +

- +

From 4283804f38057468b89506ffd00ee3ff74104e45 Mon Sep 17 00:00:00 2001 From: michakrapp Date: Mon, 19 Mar 2018 09:22:00 +0100 Subject: [PATCH 05/34] Add autocomplete attributes to account edit form maybe "password-current" should get autocomplete="off" to prevent trigger password change on default --- templates/myaccount/form-edit-account.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/templates/myaccount/form-edit-account.php b/templates/myaccount/form-edit-account.php index cea11aa5b1b..c181257952c 100644 --- a/templates/myaccount/form-edit-account.php +++ b/templates/myaccount/form-edit-account.php @@ -28,11 +28,11 @@ do_action( 'woocommerce_before_edit_account_form' ); ?>

- +

- +

@@ -44,7 +44,7 @@ do_action( 'woocommerce_before_edit_account_form' ); ?>

- +

@@ -52,15 +52,15 @@ do_action( 'woocommerce_before_edit_account_form' ); ?>

- +

- +

- +

From cc742b908a643b794711b92802deaa7b3df41c42 Mon Sep 17 00:00:00 2001 From: michakrapp Date: Mon, 19 Mar 2018 09:25:57 +0100 Subject: [PATCH 06/34] Add autocomplete attributes to lost password form --- templates/myaccount/form-lost-password.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/myaccount/form-lost-password.php b/templates/myaccount/form-lost-password.php index ac543384442..5fe47b7b091 100644 --- a/templates/myaccount/form-lost-password.php +++ b/templates/myaccount/form-lost-password.php @@ -28,7 +28,7 @@ wc_print_notices(); ?>

- +

From cef2b1ee131c3edbf4f22c8da6a5206ec39dcf87 Mon Sep 17 00:00:00 2001 From: michakrapp Date: Mon, 19 Mar 2018 09:30:11 +0100 Subject: [PATCH 07/34] Add autocomplete attributes to reset password form --- templates/myaccount/form-reset-password.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/myaccount/form-reset-password.php b/templates/myaccount/form-reset-password.php index 72b19647ae6..d6e3a41709a 100644 --- a/templates/myaccount/form-reset-password.php +++ b/templates/myaccount/form-reset-password.php @@ -28,11 +28,11 @@ wc_print_notices(); ?>

- +

- +

From 27a309d2dfc6546d51fc962a2e417c42edb9e766 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Thu, 22 Mar 2018 15:39:30 +0100 Subject: [PATCH 08/34] Removed line item limit and make sure the URL is shorter than 2083 characters - removed limit to 9 line items per Paypal order - if the request URL with all line items will be longer than 2083 characters, send it as one line item - fix the character limit function to account for URL encoding happening in http_build_query --- .../class-wc-gateway-paypal-request.php | 187 ++++++++----- tests/unit-tests/gateways/paypal/request.php | 261 ++++++++++++++++++ 2 files changed, 376 insertions(+), 72 deletions(-) create mode 100644 tests/unit-tests/gateways/paypal/request.php 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 3df9a58a0c2..29cf2e2d026 100644 --- a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php +++ b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php @@ -53,15 +53,16 @@ class WC_Gateway_Paypal_Request { * @return string */ public function get_request_url( $order, $sandbox = false ) { + if ( $sandbox ) { + $this->request_url_prefix = 'https://www.sandbox.paypal.com/cgi-bin/webscr?test_ipn=1&'; + } else { + $this->request_url_prefix = 'https://www.paypal.com/cgi-bin/webscr?'; + } $paypal_args = http_build_query( $this->get_paypal_args( $order ), '', '&' ); WC_Gateway_Paypal::log( 'PayPal Request Args for order ' . $order->get_order_number() . ': ' . wc_print_r( $paypal_args, true ) ); - if ( $sandbox ) { - return 'https://www.sandbox.paypal.com/cgi-bin/webscr?test_ipn=1&' . $paypal_args; - } else { - return 'https://www.paypal.com/cgi-bin/webscr?' . $paypal_args; - } + return $this->request_url_prefix . $paypal_args; } /** @@ -72,8 +73,12 @@ class WC_Gateway_Paypal_Request { * @return string */ protected function limit_length( $string, $limit = 127 ) { - if ( strlen( $string ) > $limit ) { - $string = substr( $string, 0, $limit - 3 ) . '...'; + // 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 ) . '...' ); } return $string; } @@ -87,43 +92,49 @@ 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 ); + $initial_paypal_args = array( + 'cmd' => '_cart', + 'business' => $this->gateway->get_option( 'email' ), + 'no_note' => 1, + 'currency_code' => get_woocommerce_currency(), + 'charset' => 'utf-8', + 'rm' => is_ssl() ? 2 : 1, + 'upload' => 1, + 'return' => esc_url_raw( add_query_arg( 'utm_nooverride', '1', $this->gateway->get_return_url( $order ) ) ), + 'cancel_return' => esc_url_raw( $order->get_cancel_order_url_raw() ), + '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( + 'order_id' => $order->get_id(), + 'order_key' => $order->get_order_key(), + ) + ), + 'notify_url' => $this->limit_length( $this->notify_url, 255 ), + 'first_name' => $this->limit_length( $order->get_billing_first_name(), 32 ), + 'last_name' => $this->limit_length( $order->get_billing_last_name(), 64 ), + 'address1' => $this->limit_length( $order->get_billing_address_1(), 100 ), + 'address2' => $this->limit_length( $order->get_billing_address_2(), 100 ), + 'city' => $this->limit_length( $order->get_billing_city(), 40 ), + 'state' => $this->get_paypal_state( $order->get_billing_country(), $order->get_billing_state() ), + 'zip' => $this->limit_length( wc_format_postcode( $order->get_billing_postcode(), $order->get_billing_country() ), 32 ), + 'country' => $this->limit_length( $order->get_billing_country(), 2 ), + 'email' => $this->limit_length( $order->get_billing_email() ), + ); + $phone_number_args = $this->get_phone_number_args( $order ); + $shipping_args = $this->get_shipping_args( $order ); + $query_str_except_line_items = http_build_query( array_merge( $initial_paypal_args, $phone_number_args, $shipping_args ), '', '&' ); + $url_except_line_items_length = strlen( $this->request_url_prefix . $query_str_except_line_items ); + return apply_filters( 'woocommerce_paypal_args', array_merge( - array( - 'cmd' => '_cart', - 'business' => $this->gateway->get_option( 'email' ), - 'no_note' => 1, - 'currency_code' => get_woocommerce_currency(), - 'charset' => 'utf-8', - 'rm' => is_ssl() ? 2 : 1, - 'upload' => 1, - 'return' => esc_url_raw( add_query_arg( 'utm_nooverride', '1', $this->gateway->get_return_url( $order ) ) ), - 'cancel_return' => esc_url_raw( $order->get_cancel_order_url_raw() ), - '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( - 'order_id' => $order->get_id(), - 'order_key' => $order->get_order_key(), - ) - ), - 'notify_url' => $this->limit_length( $this->notify_url, 255 ), - 'first_name' => $this->limit_length( $order->get_billing_first_name(), 32 ), - 'last_name' => $this->limit_length( $order->get_billing_last_name(), 64 ), - 'address1' => $this->limit_length( $order->get_billing_address_1(), 100 ), - 'address2' => $this->limit_length( $order->get_billing_address_2(), 100 ), - 'city' => $this->limit_length( $order->get_billing_city(), 40 ), - 'state' => $this->get_paypal_state( $order->get_billing_country(), $order->get_billing_state() ), - 'zip' => $this->limit_length( wc_format_postcode( $order->get_billing_postcode(), $order->get_billing_country() ), 32 ), - 'country' => $this->limit_length( $order->get_billing_country(), 2 ), - 'email' => $this->limit_length( $order->get_billing_email() ), - ), - $this->get_phone_number_args( $order ), - $this->get_shipping_args( $order ), - $this->get_line_item_args( $order ) + $initial_paypal_args, + $phone_number_args, + $shipping_args, + $this->get_line_item_args( $order, $url_except_line_items_length ) ), $order ); } @@ -180,19 +191,63 @@ class WC_Gateway_Paypal_Request { return $shipping_args; } + /** + * Get shipping cost line item args for paypal request. + * + * @param WC_Order $order Order object. + * @param bool $include_shipping_tax Whether to include shipping tax or not. + * @return array + */ + protected function get_shipping_cost_line_item( $order, $include_shipping_tax ) { + $line_item_args = array(); + $shipping_total = $order->get_shipping_total(); + if ( $include_shipping_tax ) { + $shipping_total += $order->get_shipping_tax(); + } + + // Add shipping costs. Paypal ignores anything over 5 digits (999.99 is the max). + // We also check that shipping is not the **only** cost as PayPal won't allow payment + // if the items have no cost. + if ( $order->get_shipping_total() > 0 && $order->get_shipping_total() < 999.99 && $this->number_format( $order->get_shipping_total() + $order->get_shipping_tax(), $order ) !== $this->number_format( $order->get_total(), $order ) ) { + $line_item_args['shipping_1'] = $this->number_format( $shipping_total, $order ); + } elseif ( $order->get_shipping_total() > 0 ) { + /* translators: %s: Order shipping method */ + $this->add_line_item( sprintf( __( 'Shipping via %s', 'woocommerce' ), $order->get_shipping_method() ), 1, $this->number_format( $shipping_total, $order ) ); + } + + return $line_item_args; + } + + /** + * Get line item args for paypal request as a single line item. + * + * @param WC_Order $order Order object. + * @param bool $include_shipping_tax Whether to include shipping tax or not. + * @return array + */ + protected function get_line_item_args_single_item( $order, $include_shipping_tax ) { + $this->delete_line_items(); + + $all_items_name = $this->get_order_item_names( $order ); + $this->add_line_item( $all_items_name ? $all_items_name : __( 'Order', 'woocommerce' ), 1, $this->number_format( $order->get_total() - $this->round( $order->get_shipping_total() + $order->get_shipping_tax(), $order ), $order ), $order->get_order_number() ); + $line_item_args = $this->get_shipping_cost_line_item( $order, $include_shipping_tax ); + + return array_merge( $line_item_args, $this->get_line_items() ); + } + /** * Get line item args for paypal request. * * @param WC_Order $order Order object. + * @param int $url_except_line_items_length Length of URL without line items. * @return array */ - protected function get_line_item_args( $order ) { + protected function get_line_item_args( $order, $url_except_line_items_length ) { /** * Try passing a line item per product if supported. */ if ( ( ! wc_tax_enabled() || ! wc_prices_include_tax() ) && $this->prepare_line_items( $order ) ) { - $line_item_args = array(); $line_item_args['tax_cart'] = $this->number_format( $order->get_total_tax(), $order ); @@ -200,41 +255,29 @@ class WC_Gateway_Paypal_Request { $line_item_args['discount_amount_cart'] = $this->number_format( $this->round( $order->get_total_discount(), $order ), $order ); } - // Add shipping costs. Paypal ignores anything over 5 digits (999.99 is the max). - // We also check that shipping is not the **only** cost as PayPal won't allow payment - // if the items have no cost. - if ( $order->get_shipping_total() > 0 && $order->get_shipping_total() < 999.99 && $this->number_format( $order->get_shipping_total() + $order->get_shipping_tax(), $order ) !== $this->number_format( $order->get_total(), $order ) ) { - $line_item_args['shipping_1'] = $this->number_format( $order->get_shipping_total(), $order ); - } elseif ( $order->get_shipping_total() > 0 ) { - /* translators: %s: Order shipping method */ - $this->add_line_item( sprintf( __( 'Shipping via %s', 'woocommerce' ), $order->get_shipping_method() ), 1, $this->number_format( $order->get_shipping_total(), $order ) ); - } - + $include_shipping_tax = false; + $line_item_args = array_merge( $line_item_args, $this->get_shipping_cost_line_item( $order, $include_shipping_tax ) ); $line_item_args = array_merge( $line_item_args, $this->get_line_items() ); + + $line_items_query_str_length = strlen( http_build_query( $line_item_args, '', '&' ) ); + + // If URL is longer than 2,083 chars, ignore line items and send cart to Paypal as a single item. + // One item's name can only be 127 characters long, so the URL should not be longer than limit. + // URL character limit via: + // https://support.microsoft.com/en-us/help/208427/maximum-url-length-is-2-083-characters-in-internet-explorer + // +1 for '&' that needs to connect 2 query strings. + if ( $url_except_line_items_length + 1 + $line_items_query_str_length > 2083 ) { + $line_item_args = $this->get_line_item_args_single_item( $order, $include_shipping_tax ); + } } else { /** * Send order as a single item. * * For shipping, we longer use shipping_1 because paypal ignores it if *any* shipping rules are within paypal, and paypal ignores anything over 5 digits (999.99 is the max). */ + $include_shipping_tax = true; + $line_item_args = $this->get_line_item_args_single_item( $order, $include_shipping_tax ); - $this->delete_line_items(); - - $line_item_args = array(); - $all_items_name = $this->get_order_item_names( $order ); - $this->add_line_item( $all_items_name ? $all_items_name : __( 'Order', 'woocommerce' ), 1, $this->number_format( $order->get_total() - $this->round( $order->get_shipping_total() + $order->get_shipping_tax(), $order ), $order ), $order->get_order_number() ); - - // Add shipping costs. Paypal ignores anything over 5 digits (999.99 is the max). - // We also check that shipping is not the **only** cost as PayPal won't allow payment - // if the items have no cost. - if ( $order->get_shipping_total() > 0 && $order->get_shipping_total() < 999.99 && $this->number_format( $order->get_shipping_total() + $order->get_shipping_tax(), $order ) !== $this->number_format( $order->get_total(), $order ) ) { - $line_item_args['shipping_1'] = $this->number_format( $order->get_shipping_total() + $order->get_shipping_tax(), $order ); - } elseif ( $order->get_shipping_total() > 0 ) { - /* translators: %s: Order shipping method */ - $this->add_line_item( sprintf( __( 'Shipping via %s', 'woocommerce' ), $order->get_shipping_method() ), 1, $this->number_format( $order->get_shipping_total() + $order->get_shipping_tax(), $order ) ); - } - - $line_item_args = array_merge( $line_item_args, $this->get_line_items() ); } return $line_item_args; @@ -364,7 +407,7 @@ class WC_Gateway_Paypal_Request { protected function add_line_item( $item_name, $quantity = 1, $amount = 0.0, $item_number = '' ) { $index = ( count( $this->line_items ) / 4 ) + 1; - if ( $amount < 0 || $index > 9 ) { + if ( $amount < 0 ) { return false; } diff --git a/tests/unit-tests/gateways/paypal/request.php b/tests/unit-tests/gateways/paypal/request.php new file mode 100644 index 00000000000..8d544913b68 --- /dev/null +++ b/tests/unit-tests/gateways/paypal/request.php @@ -0,0 +1,261 @@ +products. + * + * @param int $product_count Number of products to create. + */ + protected function create_products( $product_count = 30 ) { + $this->products = array(); + for ( $i = 0; $i < $product_count; $i++ ) { + $product = WC_Helper_Product::create_simple_product(); + $product->set_name( 'Dummy Product ' . $i ); + $this->products[] = $product; + + } + + // To test limit length properly, we need a product with a name that is shorter than 127 chars, + // but longer than 127 chars when URL-encoded. + if ( array_key_exists( 0, $this->products ) ) { + $this->products[0]->set_name( 'Dummy Product 😎😎😎😎😎😎😎😎😎😎😎' ); + } + + } + + /** + * Add products from $this->products to $order as items, clearing existing order items. + * + * @param WC_Order $order Order to which the products should be added. + */ + protected function add_products_to_order( $order ) { + // Remove previous items. + foreach ( $order->get_items() as $item ) { + $order->remove_item( $item->get_id() ); + } + + // Add new products. + foreach ( $this->products as $product ) { + $item = new WC_Order_Item_Product(); + $item->set_props( array( + 'product' => $product, + 'quantity' => 3, + 'subtotal' => wc_get_price_excluding_tax( $product, array( 'qty' => 3 ) ), + 'total' => wc_get_price_excluding_tax( $product, array( 'qty' => 3 ) ), + ) ); + + $item->save(); + $order->add_item( $item ); + } + + } + + /** + * Initialize the Paypal gateway and Request objects. + */ + public function setUp() { + parent::setUp(); + + $bootstrap = WC_Unit_Tests_Bootstrap::instance(); + include_once $bootstrap->plugin_dir . '/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php'; + + $this->paypal_gateway = new WC_Gateway_Paypal(); + $this->paypal_request = new WC_Gateway_Paypal_Request( $this->paypal_gateway ); + } + + + /** + * Create Paypal request URL for $product_count number of products. + * + * @param int $product_count Number of products to include in the order. + * @param bool $testmode Whether to test using sandbox or not. + * + * @return string + * @throws WC_Data_Exception + */ + protected function get_request_url( $product_count, $testmode ) { + // Create products. + $this->create_products( $product_count ); + + $this->order = WC_Helper_Order::create_order( $this->user ); + $this->add_products_to_order( $this->order ); + + // Set payment method to Paypal. + $payment_gateways = WC()->payment_gateways->payment_gateways(); + $this->order->set_payment_method( $payment_gateways['paypal'] ); + $this->order->calculate_totals(); + $this->order->calculate_shipping(); + + return $this->paypal_request->get_request_url( $this->order, $testmode ); + } + + /** + * Clean up order, deletes all products in order, too. + */ + protected function clean_up() { + WC_Helper_Order::delete_order( $this->order->get_id() ); + } + + /** + * Check if the shipping tax is included in the total according to $shipping_tax_included. + * + * @param array $query_array Request URL parsed into associative array. + * @param bool $shipping_tax_included Whether the shipping tax should be included or not. + */ + protected function check_shipping_tax( $query_array, $shipping_tax_included ) { + $shipping_total = $this->order->get_shipping_total(); + if ( $shipping_tax_included ) { + $shipping_total += $this->order->get_shipping_tax(); + } + $epsilon = 0.01; + $this->assertTrue( abs( $shipping_total - floatval( $query_array['shipping_1'] ) ) < $epsilon ); + + } + + /** + * Test common order asserts. + * + * @param string $request_url Paypal request URL. + * @param bool $testmode Whether Paypal sandbox is used or not. + */ + protected function check_order_common_props( $request_url, $testmode ) { + if ( $testmode ) { + $this->assertEquals( 'https://www.sandbox.paypal.com', substr( $request_url, 0, 30 ) ); + } else { + $this->assertEquals( 'https://www.paypal.com', substr( $request_url, 0, 22 ) ); + } + $this->assertLessThanOrEqual( 2083, strlen( $request_url ) ); + } + + /** + * Test large order with 30 items, URL length > 2083 characters. + * + * @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 + */ + protected function check_large_order( $shipping_tax_included, $testmode ) { + $request_url = $this->get_request_url( 30, $testmode ); + + $this->check_order_common_props( $request_url, $testmode ); + + // Check fields limited to 127 characters for length. + $fields_limited_to_127_chars = array( + 'invoice', + 'item_name_1', + 'item_number_1', + ); + + $query_string = wp_parse_url( $request_url, PHP_URL_QUERY ) + ? wp_parse_url( $request_url, PHP_URL_QUERY ) + : ''; + $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 ] ) ); + } + + // Check that there is actually only one item for order with URL length > limit. + $this->assertFalse( array_key_exists( 'item_name_2', $query_array ) ); + + $this->check_shipping_tax( $query_array, $shipping_tax_included ); + + // Remove order and created products. + $this->clean_up(); + + } + + /** + * Test small order with fewer items, URL length should be < 2083 characters. + * + * @param int $product_count Number of products to include in the order. + * @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 + */ + protected function check_small_order( $product_count, $shipping_tax_included, $testmode ) { + $request_url = $this->get_request_url( $product_count, $testmode ); + + $this->check_order_common_props( $request_url, $testmode ); + + $query_string = wp_parse_url( $request_url, PHP_URL_QUERY ) + ? wp_parse_url( $request_url, PHP_URL_QUERY ) + : ''; + $query_array = array(); + parse_str( $query_string, $query_array ); + + // Check that there are $product_count line items in the request URL. + // However, if shipping tax is included, there is only one item. + if ( $shipping_tax_included ) { + $product_count = 1; + } + + for ( $i = 1; $i <= $product_count; $i++ ) { + $this->assertTrue( array_key_exists( 'item_name_' . $i, $query_array ), 'Item name ' . $i . ' does not exist' ); + $this->assertTrue( array_key_exists( 'quantity_' . $i, $query_array ) ); + $this->assertTrue( array_key_exists( 'amount_' . $i, $query_array ) ); + $this->assertTrue( array_key_exists( 'item_number_' . $i, $query_array ) ); + + $this->assertLessThanOrEqual( 127, strlen( $query_array[ 'item_name_' . $i ] ) ); + $this->assertLessThanOrEqual( 127, strlen( $query_array[ 'item_number_' . $i ] ) ); + + } + + $this->check_shipping_tax( $query_array, $shipping_tax_included ); + + // Remove order and created products. + $this->clean_up(); + } + + /** + * @throws WC_Data_Exception + */ + public function test_request_url() { + // User set up. + $this->user = $this->factory->user->create( array( + 'role' => 'administrator', + ) ); + wp_set_current_user( $this->user ); + + // Paths through code changed by: + // $sandbox (true/false) + // wc_tax_enabled() === get_option( 'woocommerce_calc_taxes' ) === 'yes' + // wc_prices_include_tax() === wc_tax_enabled() && 'yes' === get_option( 'woocommerce_prices_include_tax' );. + $correct_options = array( + [ 'no', 'no', false ], + [ 'yes', 'no', false ], + // ['no', 'yes', false], // this is not a valid option due to definition of wc_prices_include_tax + [ 'yes', 'yes', true ], + ); + foreach ( array( true, false ) as $testmode ) { + foreach ( $correct_options as $values ) { + update_option( 'woocommerce_calc_taxes', $values[0] ); + update_option( 'woocommerce_prices_include_tax', $values[1] ); + $shipping_tax_included = $values[2]; + + // Test order with < 9 items (URL shorter than limit). + $this->check_small_order( 5, $shipping_tax_included, $testmode ); + + // Test order with >9 items with URL shorter than limit. + $this->check_small_order( 11, $shipping_tax_included, $testmode ); + + // Test order with URL longer than limit. + $this->check_large_order( $shipping_tax_included, $testmode ); + + } + } + + } + +} + From fda6421deea53cdfd0d175509348e8e4dfb4b9be Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Thu, 22 Mar 2018 20:42:19 +0100 Subject: [PATCH 09/34] Fixed compatibility with older PHP versions, removed superfluous asserts. --- tests/unit-tests/gateways/paypal/request.php | 40 ++++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/tests/unit-tests/gateways/paypal/request.php b/tests/unit-tests/gateways/paypal/request.php index 8d544913b68..2840adcde0a 100644 --- a/tests/unit-tests/gateways/paypal/request.php +++ b/tests/unit-tests/gateways/paypal/request.php @@ -232,27 +232,35 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { // wc_tax_enabled() === get_option( 'woocommerce_calc_taxes' ) === 'yes' // wc_prices_include_tax() === wc_tax_enabled() && 'yes' === get_option( 'woocommerce_prices_include_tax' );. $correct_options = array( - [ 'no', 'no', false ], - [ 'yes', 'no', false ], - // ['no', 'yes', false], // this is not a valid option due to definition of wc_prices_include_tax - [ 'yes', 'yes', true ], + array( 'no', 'no', false ), + array( 'yes', 'no', false ), + // array( 'no', 'yes', false ), // this is not a valid option due to definition of wc_prices_include_tax + array( 'yes', 'yes', true ), ); - foreach ( array( true, false ) as $testmode ) { - foreach ( $correct_options as $values ) { - update_option( 'woocommerce_calc_taxes', $values[0] ); - update_option( 'woocommerce_prices_include_tax', $values[1] ); - $shipping_tax_included = $values[2]; - // Test order with < 9 items (URL shorter than limit). - $this->check_small_order( 5, $shipping_tax_included, $testmode ); + // One test without sandbox. + $testmode = false; + update_option( 'woocommerce_calc_taxes', 'no' ); + update_option( 'woocommerce_prices_include_tax', 'no' ); + $shipping_tax_included = false; + $this->check_small_order( 5, $shipping_tax_included, $testmode ); - // Test order with >9 items with URL shorter than limit. - $this->check_small_order( 11, $shipping_tax_included, $testmode ); + // Other tests with sandbox active. + $testmode = true; + foreach ( $correct_options as $values ) { + update_option( 'woocommerce_calc_taxes', $values[0] ); + update_option( 'woocommerce_prices_include_tax', $values[1] ); + $shipping_tax_included = $values[2]; - // Test order with URL longer than limit. - $this->check_large_order( $shipping_tax_included, $testmode ); + // Test order with < 9 items (URL shorter than limit). + $this->check_small_order( 5, $shipping_tax_included, $testmode ); + + // Test order with >9 items with URL shorter than limit. + $this->check_small_order( 11, $shipping_tax_included, $testmode ); + + // Test order with URL longer than limit. + $this->check_large_order( $shipping_tax_included, $testmode ); - } } } From 04cce41b6b257701451d88debfa71aad822f5e86 Mon Sep 17 00:00:00 2001 From: Gerhard Potgieter Date: Fri, 23 Mar 2018 11:19:02 +0200 Subject: [PATCH 10/34] Default to no when importing backorders. --- includes/import/class-wc-product-csv-importer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/import/class-wc-product-csv-importer.php b/includes/import/class-wc-product-csv-importer.php index efcad3f9811..30e6c6b86c9 100644 --- a/includes/import/class-wc-product-csv-importer.php +++ b/includes/import/class-wc-product-csv-importer.php @@ -502,7 +502,7 @@ class WC_Product_CSV_Importer extends WC_Product_Importer { return $value ? 'yes' : 'no'; } - return ''; + return 'no'; } /** From e876f0dfb24e5b44b5129a9b451f86f4947d227b Mon Sep 17 00:00:00 2001 From: Gerhard Potgieter Date: Fri, 23 Mar 2018 11:21:59 +0200 Subject: [PATCH 11/34] empty check on 0 will be true, return no when empty. --- includes/import/class-wc-product-csv-importer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/import/class-wc-product-csv-importer.php b/includes/import/class-wc-product-csv-importer.php index 30e6c6b86c9..8e3c646cdd8 100644 --- a/includes/import/class-wc-product-csv-importer.php +++ b/includes/import/class-wc-product-csv-importer.php @@ -491,7 +491,7 @@ class WC_Product_CSV_Importer extends WC_Product_Importer { */ public function parse_backorders_field( $value ) { if ( empty( $value ) ) { - return ''; + return 'no'; } $value = $this->parse_bool_field( $value ); From 0573232c62a13ef7ad1730952599193fff81342f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 23 Mar 2018 12:54:03 +0000 Subject: [PATCH 12/34] Call empty cart when completing payment in PayPal --- .../paypal/includes/class-wc-gateway-paypal-response.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/gateways/paypal/includes/class-wc-gateway-paypal-response.php b/includes/gateways/paypal/includes/class-wc-gateway-paypal-response.php index c917a8aeec3..05f00fb47c6 100644 --- a/includes/gateways/paypal/includes/class-wc-gateway-paypal-response.php +++ b/includes/gateways/paypal/includes/class-wc-gateway-paypal-response.php @@ -65,6 +65,7 @@ abstract class WC_Gateway_Paypal_Response { protected function payment_complete( $order, $txn_id = '', $note = '' ) { $order->add_order_note( $note ); $order->payment_complete( $txn_id ); + WC()->cart->empty_cart(); } /** From 26144afbc655c1d183ae9cd6956d9f1cd7b2e6e1 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Fri, 23 Mar 2018 17:22:09 +0100 Subject: [PATCH 13/34] Test request length after applying filter - added default value to get_line_item_args to not break interface - smaller code review updates --- .../class-wc-gateway-paypal-request.php | 189 ++++++++++++------ tests/unit-tests/gateways/paypal/request.php | 11 +- 2 files changed, 131 insertions(+), 69 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 29cf2e2d026..b462fc42225 100644 --- a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php +++ b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php @@ -35,6 +35,14 @@ class WC_Gateway_Paypal_Request { */ protected $notify_url; + /** + * Endpoint for requests from PayPal. + * + * @var string + */ + protected $endpoint; + + /** * Constructor. * @@ -54,15 +62,15 @@ class WC_Gateway_Paypal_Request { */ public function get_request_url( $order, $sandbox = false ) { if ( $sandbox ) { - $this->request_url_prefix = 'https://www.sandbox.paypal.com/cgi-bin/webscr?test_ipn=1&'; + $this->endpoint = 'https://www.sandbox.paypal.com/cgi-bin/webscr?test_ipn=1&'; } else { - $this->request_url_prefix = 'https://www.paypal.com/cgi-bin/webscr?'; + $this->endpoint = 'https://www.paypal.com/cgi-bin/webscr?'; } $paypal_args = http_build_query( $this->get_paypal_args( $order ), '', '&' ); WC_Gateway_Paypal::log( 'PayPal Request Args for order ' . $order->get_order_number() . ': ' . wc_print_r( $paypal_args, true ) ); - return $this->request_url_prefix . $paypal_args; + return $this->endpoint . $paypal_args; } /** @@ -83,6 +91,67 @@ class WC_Gateway_Paypal_Request { return $string; } + /** + * If the default request with line items is too long, generate a new one with only one line item. + * + * @param WC_Order $order Order to be sent to Paypal. + * @param array $paypal_args Arguments sent to Paypal in the request. + * + * @return array + */ + protected function fix_request_legth( $order, $paypal_args ) { + $max_paypal_length = 2083; + $query_candidate = http_build_query( $paypal_args, '', '&' ); + // If URL is longer than 2,083 chars, ignore line items and send cart to Paypal as a single item. + // One item's name can only be 127 characters long, so the URL should not be longer than limit. + // URL character limit via: + // https://support.microsoft.com/en-us/help/208427/maximum-url-length-is-2-083-characters-in-internet-explorer + if ( strlen( $this->endpoint . $query_candidate ) <= $max_paypal_length ) { + return $paypal_args; + } + + return apply_filters( + 'woocommerce_paypal_args', array_merge( + array( + 'cmd' => '_cart', + 'business' => $this->gateway->get_option( 'email' ), + 'no_note' => 1, + 'currency_code' => get_woocommerce_currency(), + 'charset' => 'utf-8', + 'rm' => is_ssl() ? 2 : 1, + 'upload' => 1, + 'return' => esc_url_raw( add_query_arg( 'utm_nooverride', '1', $this->gateway->get_return_url( $order ) ) ), + 'cancel_return' => esc_url_raw( $order->get_cancel_order_url_raw() ), + '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( + 'order_id' => $order->get_id(), + 'order_key' => $order->get_order_key(), + ) + ), + 'notify_url' => $this->limit_length( $this->notify_url, 255 ), + 'first_name' => $this->limit_length( $order->get_billing_first_name(), 32 ), + 'last_name' => $this->limit_length( $order->get_billing_last_name(), 64 ), + 'address1' => $this->limit_length( $order->get_billing_address_1(), 100 ), + 'address2' => $this->limit_length( $order->get_billing_address_2(), 100 ), + 'city' => $this->limit_length( $order->get_billing_city(), 40 ), + 'state' => $this->get_paypal_state( $order->get_billing_country(), $order->get_billing_state() ), + 'zip' => $this->limit_length( wc_format_postcode( $order->get_billing_postcode(), $order->get_billing_country() ), 32 ), + 'country' => $this->limit_length( $order->get_billing_country(), 2 ), + 'email' => $this->limit_length( $order->get_billing_email() ), + ), + $this->get_phone_number_args( $order ), + $this->get_shipping_args( $order ), + $this->get_line_item_args( $order, true ) + ), $order + ); + + } + /** * Get PayPal Args for passing to PP. * @@ -92,51 +161,47 @@ 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 ); - $initial_paypal_args = array( - 'cmd' => '_cart', - 'business' => $this->gateway->get_option( 'email' ), - 'no_note' => 1, - 'currency_code' => get_woocommerce_currency(), - 'charset' => 'utf-8', - 'rm' => is_ssl() ? 2 : 1, - 'upload' => 1, - 'return' => esc_url_raw( add_query_arg( 'utm_nooverride', '1', $this->gateway->get_return_url( $order ) ) ), - 'cancel_return' => esc_url_raw( $order->get_cancel_order_url_raw() ), - '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( - 'order_id' => $order->get_id(), - 'order_key' => $order->get_order_key(), - ) - ), - 'notify_url' => $this->limit_length( $this->notify_url, 255 ), - 'first_name' => $this->limit_length( $order->get_billing_first_name(), 32 ), - 'last_name' => $this->limit_length( $order->get_billing_last_name(), 64 ), - 'address1' => $this->limit_length( $order->get_billing_address_1(), 100 ), - 'address2' => $this->limit_length( $order->get_billing_address_2(), 100 ), - 'city' => $this->limit_length( $order->get_billing_city(), 40 ), - 'state' => $this->get_paypal_state( $order->get_billing_country(), $order->get_billing_state() ), - 'zip' => $this->limit_length( wc_format_postcode( $order->get_billing_postcode(), $order->get_billing_country() ), 32 ), - 'country' => $this->limit_length( $order->get_billing_country(), 2 ), - 'email' => $this->limit_length( $order->get_billing_email() ), - ); - $phone_number_args = $this->get_phone_number_args( $order ); - $shipping_args = $this->get_shipping_args( $order ); - $query_str_except_line_items = http_build_query( array_merge( $initial_paypal_args, $phone_number_args, $shipping_args ), '', '&' ); - $url_except_line_items_length = strlen( $this->request_url_prefix . $query_str_except_line_items ); - - return apply_filters( + $paypal_args = apply_filters( 'woocommerce_paypal_args', array_merge( - $initial_paypal_args, - $phone_number_args, - $shipping_args, - $this->get_line_item_args( $order, $url_except_line_items_length ) + array( + 'cmd' => '_cart', + 'business' => $this->gateway->get_option( 'email' ), + 'no_note' => 1, + 'currency_code' => get_woocommerce_currency(), + 'charset' => 'utf-8', + 'rm' => is_ssl() ? 2 : 1, + 'upload' => 1, + 'return' => esc_url_raw( add_query_arg( 'utm_nooverride', '1', $this->gateway->get_return_url( $order ) ) ), + 'cancel_return' => esc_url_raw( $order->get_cancel_order_url_raw() ), + '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( + 'order_id' => $order->get_id(), + 'order_key' => $order->get_order_key(), + ) + ), + 'notify_url' => $this->limit_length( $this->notify_url, 255 ), + 'first_name' => $this->limit_length( $order->get_billing_first_name(), 32 ), + 'last_name' => $this->limit_length( $order->get_billing_last_name(), 64 ), + 'address1' => $this->limit_length( $order->get_billing_address_1(), 100 ), + 'address2' => $this->limit_length( $order->get_billing_address_2(), 100 ), + 'city' => $this->limit_length( $order->get_billing_city(), 40 ), + 'state' => $this->get_paypal_state( $order->get_billing_country(), $order->get_billing_state() ), + 'zip' => $this->limit_length( wc_format_postcode( $order->get_billing_postcode(), $order->get_billing_country() ), 32 ), + 'country' => $this->limit_length( $order->get_billing_country(), 2 ), + 'email' => $this->limit_length( $order->get_billing_email() ), + ), + $this->get_phone_number_args( $order ), + $this->get_shipping_args( $order ), + $this->get_line_item_args( $order ) ), $order ); + + return $this->fix_request_legth( $order, $paypal_args ); } /** @@ -239,35 +304,29 @@ class WC_Gateway_Paypal_Request { * Get line item args for paypal request. * * @param WC_Order $order Order object. - * @param int $url_except_line_items_length Length of URL without line items. + * @param bool $force_one_line_item Create only one item for this order. * @return array */ - protected function get_line_item_args( $order, $url_except_line_items_length ) { + protected function get_line_item_args( $order, $force_one_line_item = false ) { /** * Try passing a line item per product if supported. */ if ( ( ! wc_tax_enabled() || ! wc_prices_include_tax() ) && $this->prepare_line_items( $order ) ) { - $line_item_args = array(); - $line_item_args['tax_cart'] = $this->number_format( $order->get_total_tax(), $order ); - - if ( $order->get_total_discount() > 0 ) { - $line_item_args['discount_amount_cart'] = $this->number_format( $this->round( $order->get_total_discount(), $order ), $order ); - } - + $line_item_args = array(); $include_shipping_tax = false; - $line_item_args = array_merge( $line_item_args, $this->get_shipping_cost_line_item( $order, $include_shipping_tax ) ); - $line_item_args = array_merge( $line_item_args, $this->get_line_items() ); - - $line_items_query_str_length = strlen( http_build_query( $line_item_args, '', '&' ) ); - - // If URL is longer than 2,083 chars, ignore line items and send cart to Paypal as a single item. - // One item's name can only be 127 characters long, so the URL should not be longer than limit. - // URL character limit via: - // https://support.microsoft.com/en-us/help/208427/maximum-url-length-is-2-083-characters-in-internet-explorer - // +1 for '&' that needs to connect 2 query strings. - if ( $url_except_line_items_length + 1 + $line_items_query_str_length > 2083 ) { + if ( $force_one_line_item ) { $line_item_args = $this->get_line_item_args_single_item( $order, $include_shipping_tax ); + } else { + $line_item_args['tax_cart'] = $this->number_format( $order->get_total_tax(), $order ); + + if ( $order->get_total_discount() > 0 ) { + $line_item_args['discount_amount_cart'] = $this->number_format( $this->round( $order->get_total_discount(), $order ), $order ); + } + + $line_item_args = array_merge( $line_item_args, $this->get_shipping_cost_line_item( $order, $include_shipping_tax ) ); + $line_item_args = array_merge( $line_item_args, $this->get_line_items() ); + } } else { /** diff --git a/tests/unit-tests/gateways/paypal/request.php b/tests/unit-tests/gateways/paypal/request.php index 2840adcde0a..66179069d60 100644 --- a/tests/unit-tests/gateways/paypal/request.php +++ b/tests/unit-tests/gateways/paypal/request.php @@ -228,13 +228,16 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { wp_set_current_user( $this->user ); // Paths through code changed by: - // $sandbox (true/false) - // wc_tax_enabled() === get_option( 'woocommerce_calc_taxes' ) === 'yes' - // wc_prices_include_tax() === wc_tax_enabled() && 'yes' === get_option( 'woocommerce_prices_include_tax' );. + // - $sandbox (true/false) + // - wc_tax_enabled() + // - wc_prices_include_tax(). + // wc_tax_enabled() and wc_prices_include_tax() determine if shipping tax should be included, + // these are the correct options. $correct_options = array( + // woocommerce_calc_taxes, woocommerce_prices_include_tax, $shipping_tax_included values. array( 'no', 'no', false ), array( 'yes', 'no', false ), - // array( 'no', 'yes', false ), // this is not a valid option due to definition of wc_prices_include_tax + // array( 'no', 'yes', false ), // this is not a valid option due to definition of wc_prices_include_tax() array( 'yes', 'yes', true ), ); From a940c0bfca30a194e747d8c4d32db8cb81795901 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Fri, 23 Mar 2018 18:13:50 +0100 Subject: [PATCH 14/34] Apply strip_tags to attributes in loop template Fixes #19491 --- templates/loop/add-to-cart.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/loop/add-to-cart.php b/templates/loop/add-to-cart.php index 67182c69576..27c999956fc 100644 --- a/templates/loop/add-to-cart.php +++ b/templates/loop/add-to-cart.php @@ -27,7 +27,7 @@ echo apply_filters( 'woocommerce_loop_add_to_cart_link', // WPCS: XSS ok. esc_url( $product->add_to_cart_url() ), esc_attr( isset( $args['quantity'] ) ? $args['quantity'] : 1 ), esc_attr( isset( $args['class'] ) ? $args['class'] : 'button' ), - isset( $args['attributes'] ) ? wc_implode_html_attributes( $args['attributes'] ) : '', + isset( $args['attributes'] ) ? wc_implode_html_attributes( array_map( 'strip_tags', $args['attributes'] ) ) : '', esc_html( $product->add_to_cart_text() ) ), $product, $args ); From b6860803a513e1c80f2610acc7a0b00136d18915 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 26 Mar 2018 09:02:51 +0200 Subject: [PATCH 15/34] Fix PHPCS violations --- templates/loop/add-to-cart.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/templates/loop/add-to-cart.php b/templates/loop/add-to-cart.php index 27c999956fc..edb46e36116 100644 --- a/templates/loop/add-to-cart.php +++ b/templates/loop/add-to-cart.php @@ -10,9 +10,8 @@ * 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/ - * @author WooThemes - * @package WooCommerce/Templates + * @see https://docs.woocommerce.com/document/template-structure/ + * @package WooCommerce/Templates * @version 3.3.0 */ From e19d64e51e6be32c097128696bcb0165a3501e1c Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 26 Mar 2018 11:00:28 +0200 Subject: [PATCH 16/34] Fixed PHPCS violations --- .../paypal/includes/class-wc-gateway-paypal-request.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b462fc42225..8eb541ce756 100644 --- a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php +++ b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php @@ -105,7 +105,7 @@ class WC_Gateway_Paypal_Request { // If URL is longer than 2,083 chars, ignore line items and send cart to Paypal as a single item. // One item's name can only be 127 characters long, so the URL should not be longer than limit. // URL character limit via: - // https://support.microsoft.com/en-us/help/208427/maximum-url-length-is-2-083-characters-in-internet-explorer + // https://support.microsoft.com/en-us/help/208427/maximum-url-length-is-2-083-characters-in-internet-explorer. if ( strlen( $this->endpoint . $query_candidate ) <= $max_paypal_length ) { return $paypal_args; } From ced5980e792d9da836a9349cc178e512eeaaf632 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 26 Mar 2018 12:03:00 +0100 Subject: [PATCH 17/34] Tweak `wc_get_price_excluding_tax` to not round the return value so calculations in admin are not pre-rounded. --- 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 eee69f05bff..23a8c1a7327 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -999,7 +999,7 @@ function wc_get_price_excluding_tax( $product, $args = array() ) { $tax_rates = WC_Tax::get_rates( $product->get_tax_class() ); $base_tax_rates = WC_Tax::get_base_tax_rates( $product->get_tax_class( 'unfiltered' ) ); $remove_taxes = apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ? WC_Tax::calc_tax( $line_price, $base_tax_rates, true ) : WC_Tax::calc_tax( $line_price, $tax_rates, true ); - $return_price = WC_Tax::round( $line_price - wc_round_tax_total( array_sum( $remove_taxes ) ) ); + $return_price = $line_price - array_sum( $remove_taxes ); } else { $return_price = $line_price; } From 88588eccbcb9c2bbd11d7656f71753862df66cd3 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 26 Mar 2018 13:05:55 +0200 Subject: [PATCH 18/34] Code duplication removed Added test for non line item agrument presence Fixed incorrect comment --- .../class-wc-gateway-paypal-request.php | 122 ++++++++---------- tests/unit-tests/gateways/paypal/request.php | 9 ++ 2 files changed, 60 insertions(+), 71 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 8eb541ce756..c887b497505 100644 --- a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php +++ b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php @@ -36,7 +36,7 @@ class WC_Gateway_Paypal_Request { protected $notify_url; /** - * Endpoint for requests from PayPal. + * Endpoint for requests to PayPal. * * @var string */ @@ -91,6 +91,52 @@ class WC_Gateway_Paypal_Request { return $string; } + /** + * Get args for paypal request, except for line item args. + * + * @param WC_Order $order Order object. + * + * @return array + */ + protected function get_non_line_item_args( $order ) { + return array_merge( + array( + 'cmd' => '_cart', + 'business' => $this->gateway->get_option( 'email' ), + 'no_note' => 1, + 'currency_code' => get_woocommerce_currency(), + 'charset' => 'utf-8', + 'rm' => is_ssl() ? 2 : 1, + 'upload' => 1, + 'return' => esc_url_raw( add_query_arg( 'utm_nooverride', '1', $this->gateway->get_return_url( $order ) ) ), + 'cancel_return' => esc_url_raw( $order->get_cancel_order_url_raw() ), + '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( + 'order_id' => $order->get_id(), + 'order_key' => $order->get_order_key(), + ) + ), + 'notify_url' => $this->limit_length( $this->notify_url, 255 ), + 'first_name' => $this->limit_length( $order->get_billing_first_name(), 32 ), + 'last_name' => $this->limit_length( $order->get_billing_last_name(), 64 ), + 'address1' => $this->limit_length( $order->get_billing_address_1(), 100 ), + 'address2' => $this->limit_length( $order->get_billing_address_2(), 100 ), + 'city' => $this->limit_length( $order->get_billing_city(), 40 ), + 'state' => $this->get_paypal_state( $order->get_billing_country(), $order->get_billing_state() ), + 'zip' => $this->limit_length( wc_format_postcode( $order->get_billing_postcode(), $order->get_billing_country() ), 32 ), + 'country' => $this->limit_length( $order->get_billing_country(), 2 ), + 'email' => $this->limit_length( $order->get_billing_email() ), + ), + $this->get_phone_number_args( $order ), + $this->get_shipping_args( $order ) + ); + } + /** * If the default request with line items is too long, generate a new one with only one line item. * @@ -99,7 +145,7 @@ class WC_Gateway_Paypal_Request { * * @return array */ - protected function fix_request_legth( $order, $paypal_args ) { + protected function fix_request_length( $order, $paypal_args ) { $max_paypal_length = 2083; $query_candidate = http_build_query( $paypal_args, '', '&' ); // If URL is longer than 2,083 chars, ignore line items and send cart to Paypal as a single item. @@ -112,40 +158,7 @@ class WC_Gateway_Paypal_Request { return apply_filters( 'woocommerce_paypal_args', array_merge( - array( - 'cmd' => '_cart', - 'business' => $this->gateway->get_option( 'email' ), - 'no_note' => 1, - 'currency_code' => get_woocommerce_currency(), - 'charset' => 'utf-8', - 'rm' => is_ssl() ? 2 : 1, - 'upload' => 1, - 'return' => esc_url_raw( add_query_arg( 'utm_nooverride', '1', $this->gateway->get_return_url( $order ) ) ), - 'cancel_return' => esc_url_raw( $order->get_cancel_order_url_raw() ), - '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( - 'order_id' => $order->get_id(), - 'order_key' => $order->get_order_key(), - ) - ), - 'notify_url' => $this->limit_length( $this->notify_url, 255 ), - 'first_name' => $this->limit_length( $order->get_billing_first_name(), 32 ), - 'last_name' => $this->limit_length( $order->get_billing_last_name(), 64 ), - 'address1' => $this->limit_length( $order->get_billing_address_1(), 100 ), - 'address2' => $this->limit_length( $order->get_billing_address_2(), 100 ), - 'city' => $this->limit_length( $order->get_billing_city(), 40 ), - 'state' => $this->get_paypal_state( $order->get_billing_country(), $order->get_billing_state() ), - 'zip' => $this->limit_length( wc_format_postcode( $order->get_billing_postcode(), $order->get_billing_country() ), 32 ), - 'country' => $this->limit_length( $order->get_billing_country(), 2 ), - 'email' => $this->limit_length( $order->get_billing_email() ), - ), - $this->get_phone_number_args( $order ), - $this->get_shipping_args( $order ), + $this->get_non_line_item_args( $order ), $this->get_line_item_args( $order, true ) ), $order ); @@ -163,45 +176,12 @@ class WC_Gateway_Paypal_Request { $paypal_args = apply_filters( 'woocommerce_paypal_args', array_merge( - array( - 'cmd' => '_cart', - 'business' => $this->gateway->get_option( 'email' ), - 'no_note' => 1, - 'currency_code' => get_woocommerce_currency(), - 'charset' => 'utf-8', - 'rm' => is_ssl() ? 2 : 1, - 'upload' => 1, - 'return' => esc_url_raw( add_query_arg( 'utm_nooverride', '1', $this->gateway->get_return_url( $order ) ) ), - 'cancel_return' => esc_url_raw( $order->get_cancel_order_url_raw() ), - '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( - 'order_id' => $order->get_id(), - 'order_key' => $order->get_order_key(), - ) - ), - 'notify_url' => $this->limit_length( $this->notify_url, 255 ), - 'first_name' => $this->limit_length( $order->get_billing_first_name(), 32 ), - 'last_name' => $this->limit_length( $order->get_billing_last_name(), 64 ), - 'address1' => $this->limit_length( $order->get_billing_address_1(), 100 ), - 'address2' => $this->limit_length( $order->get_billing_address_2(), 100 ), - 'city' => $this->limit_length( $order->get_billing_city(), 40 ), - 'state' => $this->get_paypal_state( $order->get_billing_country(), $order->get_billing_state() ), - 'zip' => $this->limit_length( wc_format_postcode( $order->get_billing_postcode(), $order->get_billing_country() ), 32 ), - 'country' => $this->limit_length( $order->get_billing_country(), 2 ), - 'email' => $this->limit_length( $order->get_billing_email() ), - ), - $this->get_phone_number_args( $order ), - $this->get_shipping_args( $order ), + $this->get_non_line_item_args( $order ), $this->get_line_item_args( $order ) ), $order ); - return $this->fix_request_legth( $order, $paypal_args ); + return $this->fix_request_length( $order, $paypal_args ); } /** diff --git a/tests/unit-tests/gateways/paypal/request.php b/tests/unit-tests/gateways/paypal/request.php index 66179069d60..8bb7617fc3c 100644 --- a/tests/unit-tests/gateways/paypal/request.php +++ b/tests/unit-tests/gateways/paypal/request.php @@ -167,6 +167,10 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { // Check that there is actually only one item for order with URL length > limit. $this->assertFalse( array_key_exists( 'item_name_2', $query_array ) ); + // Check that non-line item parameters are included. + $this->assertTrue( array_key_exists( 'cmd', $query_array ) ); + $this->assertEquals( '_cart', $query_array['cmd'] ); + $this->check_shipping_tax( $query_array, $shipping_tax_included ); // Remove order and created products. @@ -206,6 +210,11 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { $this->assertTrue( array_key_exists( 'amount_' . $i, $query_array ) ); $this->assertTrue( array_key_exists( 'item_number_' . $i, $query_array ) ); + // Check that non-line item parameters are included. + $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 ] ) ); From f6d748a812b8ade535858096c62ba6022a641ed6 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 26 Mar 2018 13:31:29 +0200 Subject: [PATCH 19/34] Fixed PHPCS violation --- tests/unit-tests/gateways/paypal/request.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit-tests/gateways/paypal/request.php b/tests/unit-tests/gateways/paypal/request.php index 8bb7617fc3c..0ba20e10f16 100644 --- a/tests/unit-tests/gateways/paypal/request.php +++ b/tests/unit-tests/gateways/paypal/request.php @@ -214,7 +214,6 @@ 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 ] ) ); From 97c591a5e5920a4e838849a0606102791df0131b Mon Sep 17 00:00:00 2001 From: Jonathan Belcher Date: Mon, 26 Mar 2018 13:44:16 -0400 Subject: [PATCH 20/34] Add escaping to piblish date --- templates/single-product/review-meta.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/templates/single-product/review-meta.php b/templates/single-product/review-meta.php index 991432fae28..ab7ab0db9cb 100644 --- a/templates/single-product/review-meta.php +++ b/templates/single-product/review-meta.php @@ -36,7 +36,10 @@ if ( '0' === $comment->comment_approved ) { ?> echo '(' . esc_attr__( 'verified owner', 'woocommerce' ) . ') '; } - ?> + ?> +

Date: Mon, 26 Mar 2018 13:51:24 -0400 Subject: [PATCH 21/34] Fixes stray space in tabbing --- templates/single-product/up-sells.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/single-product/up-sells.php b/templates/single-product/up-sells.php index 75ca4e40c22..baaa90e753e 100644 --- a/templates/single-product/up-sells.php +++ b/templates/single-product/up-sells.php @@ -31,7 +31,7 @@ if ( $upsells ) : ?> get_id() ); + $post_object = get_post( $upsell->get_id() ); setup_postdata( $GLOBALS['post'] =& $post_object ); From dd3b99885139c23144de889daeb1881066ec1c46 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 27 Mar 2018 13:00:19 +0100 Subject: [PATCH 22/34] Tweak wording for opt-out --- .../admin/class-wc-admin-setup-wizard.php | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/includes/admin/class-wc-admin-setup-wizard.php b/includes/admin/class-wc-admin-setup-wizard.php index 77af416eac3..b067a2c18a6 100644 --- a/includes/admin/class-wc-admin-setup-wizard.php +++ b/includes/admin/class-wc-admin-setup-wizard.php @@ -489,26 +489,15 @@ class WC_Admin_Setup_Wizard { countries->get_european_union_countries(), true ) ) { - $tracking_opt_out = false; - } - - // Respect the DNT header. - if ( ! empty( $_SERVER['HTTP_DNT'] ) ) { // WPCS: input var ok. - $tracking_opt_out = false; - } ?>

- /> - + +

' . esc_html__( 'Read more about what we collect.', 'woocommerce' ) . ''; ?>

@@ -537,7 +526,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_optin'] ) && ( 'yes' === sanitize_text_field( $_POST['wc_tracker_optin'] ) ); + $tracking = isset( $_POST['wc_tracker_checkbox'] ) && ( 'yes' === sanitize_text_field( $_POST['wc_tracker_checkbox'] ) ); // @codingStandardsIgnoreEnd update_option( 'woocommerce_store_address', $address ); update_option( 'woocommerce_store_address_2', $address_2 ); From 0599c06b20a6fd38547588d25f2e3c43a6d4503a Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 27 Mar 2018 13:28:03 +0100 Subject: [PATCH 23/34] Disable Gutenberg for products. --- includes/class-wc-post-types.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/includes/class-wc-post-types.php b/includes/class-wc-post-types.php index 2849a253d93..09d810292bd 100644 --- a/includes/class-wc-post-types.php +++ b/includes/class-wc-post-types.php @@ -26,6 +26,7 @@ class WC_Post_Types { add_filter( 'rest_api_allowed_post_types', array( __CLASS__, 'rest_api_allowed_post_types' ) ); add_action( 'woocommerce_after_register_post_type', array( __CLASS__, 'maybe_flush_rewrite_rules' ) ); add_action( 'woocommerce_flush_rewrite_rules', array( __CLASS__, 'flush_rewrite_rules' ) ); + add_filter( 'gutenberg_can_edit_post_type', array( __CLASS__, 'gutenberg_can_edit_post_type' ), 10, 2 ); } /** @@ -567,6 +568,17 @@ class WC_Post_Types { flush_rewrite_rules(); } + /** + * Disable Gutenberg for products. + * + * @param bool $can_edit Whether the post type can be edited or not. + * @param string $post_type The post type being checked. + * @return bool + */ + public static function gutenberg_can_edit_post_type( $can_edit, $post_type ) { + return 'product' === $post_type ? false : $can_edit; + } + /** * Add Product Support to Jetpack Omnisearch. */ From 5eda49f98a8dbe7bad3073eb3a366d29fc84c4f0 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Tue, 27 Mar 2018 15:55:03 +0200 Subject: [PATCH 24/34] Line item logic refactored Renamed function get_non_line_item_args Documentation updates ...all part of code review fixes --- .../class-wc-gateway-paypal-request.php | 52 +++--- tests/unit-tests/gateways/paypal/request.php | 152 ++++++++++++++---- 2 files changed, 149 insertions(+), 55 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 c887b497505..0e2b5b1f683 100644 --- a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php +++ b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php @@ -92,13 +92,12 @@ class WC_Gateway_Paypal_Request { } /** - * Get args for paypal request, except for line item args. + * Get transaction args for paypal request, except for line item args. * * @param WC_Order $order Order object. - * * @return array */ - protected function get_non_line_item_args( $order ) { + protected function get_transaction_args( $order ) { return array_merge( array( 'cmd' => '_cart', @@ -140,25 +139,26 @@ class WC_Gateway_Paypal_Request { /** * If the default request with line items is too long, generate a new one with only one line item. * + * If URL is longer than 2,083 chars, ignore line items and send cart to Paypal as a single item. + * One item's name can only be 127 characters long, so the URL should not be longer than limit. + * URL character limit via: + * https://support.microsoft.com/en-us/help/208427/maximum-url-length-is-2-083-characters-in-internet-explorer. + * * @param WC_Order $order Order to be sent to Paypal. * @param array $paypal_args Arguments sent to Paypal in the request. - * * @return array */ protected function fix_request_length( $order, $paypal_args ) { $max_paypal_length = 2083; $query_candidate = http_build_query( $paypal_args, '', '&' ); - // If URL is longer than 2,083 chars, ignore line items and send cart to Paypal as a single item. - // One item's name can only be 127 characters long, so the URL should not be longer than limit. - // URL character limit via: - // https://support.microsoft.com/en-us/help/208427/maximum-url-length-is-2-083-characters-in-internet-explorer. + if ( strlen( $this->endpoint . $query_candidate ) <= $max_paypal_length ) { return $paypal_args; } return apply_filters( 'woocommerce_paypal_args', array_merge( - $this->get_non_line_item_args( $order ), + $this->get_transaction_args( $order ), $this->get_line_item_args( $order, true ) ), $order ); @@ -176,7 +176,7 @@ class WC_Gateway_Paypal_Request { $paypal_args = apply_filters( 'woocommerce_paypal_args', array_merge( - $this->get_non_line_item_args( $order ), + $this->get_transaction_args( $order ), $this->get_line_item_args( $order ) ), $order ); @@ -293,29 +293,31 @@ class WC_Gateway_Paypal_Request { * Try passing a line item per product if supported. */ if ( ( ! wc_tax_enabled() || ! wc_prices_include_tax() ) && $this->prepare_line_items( $order ) ) { - $line_item_args = array(); $include_shipping_tax = false; - if ( $force_one_line_item ) { - $line_item_args = $this->get_line_item_args_single_item( $order, $include_shipping_tax ); - } else { - $line_item_args['tax_cart'] = $this->number_format( $order->get_total_tax(), $order ); - - if ( $order->get_total_discount() > 0 ) { - $line_item_args['discount_amount_cart'] = $this->number_format( $this->round( $order->get_total_discount(), $order ), $order ); - } - - $line_item_args = array_merge( $line_item_args, $this->get_shipping_cost_line_item( $order, $include_shipping_tax ) ); - $line_item_args = array_merge( $line_item_args, $this->get_line_items() ); - - } } else { + $include_shipping_tax = true; + } + + $line_item_args = array(); + if ( $force_one_line_item || $include_shipping_tax ) { /** * Send order as a single item. * * For shipping, we longer use shipping_1 because paypal ignores it if *any* shipping rules are within paypal, and paypal ignores anything over 5 digits (999.99 is the max). */ - $include_shipping_tax = true; $line_item_args = $this->get_line_item_args_single_item( $order, $include_shipping_tax ); + } else { + /** + * Passing a line item per product if supported. + */ + $line_item_args['tax_cart'] = $this->number_format( $order->get_total_tax(), $order ); + + if ( $order->get_total_discount() > 0 ) { + $line_item_args['discount_amount_cart'] = $this->number_format( $this->round( $order->get_total_discount(), $order ), $order ); + } + + $line_item_args = array_merge( $line_item_args, $this->get_shipping_cost_line_item( $order, $include_shipping_tax ) ); + $line_item_args = array_merge( $line_item_args, $this->get_line_items() ); } diff --git a/tests/unit-tests/gateways/paypal/request.php b/tests/unit-tests/gateways/paypal/request.php index 0ba20e10f16..8f576be8bde 100644 --- a/tests/unit-tests/gateways/paypal/request.php +++ b/tests/unit-tests/gateways/paypal/request.php @@ -4,11 +4,22 @@ * * @package WooCommerce\Tests\Gateways\Paypal */ - class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { + /** + * Products to use in order. + * + * @var array + */ protected $products; + /** + * Order to submit to PayPal. + * + * @var WC_Order + */ + protected $order; + /** * Create $product_count simple products and store them in $this->products. * @@ -34,26 +45,30 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { /** * Add products from $this->products to $order as items, clearing existing order items. * - * @param WC_Order $order Order to which the products should be added. + * @param WC_Order $order Order to which the products should be added. + * @param array $prices Array of prices to use for created products. Leave empty for default prices. */ - protected function add_products_to_order( $order ) { + protected function add_products_to_order( $order, $prices = array() ) { // Remove previous items. foreach ( $order->get_items() as $item ) { $order->remove_item( $item->get_id() ); } // Add new products. + $prod_count = 0; foreach ( $this->products as $product ) { $item = new WC_Order_Item_Product(); $item->set_props( array( 'product' => $product, 'quantity' => 3, - 'subtotal' => wc_get_price_excluding_tax( $product, array( 'qty' => 3 ) ), - 'total' => wc_get_price_excluding_tax( $product, array( 'qty' => 3 ) ), + 'subtotal' => $prices ? $prices[ $prod_count ] : wc_get_price_excluding_tax( $product, array( 'qty' => 3 ) ), + 'total' => $prices ? $prices[ $prod_count ] : wc_get_price_excluding_tax( $product, array( 'qty' => 3 ) ), ) ); $item->save(); $order->add_item( $item ); + + $prod_count++; } } @@ -75,33 +90,65 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { /** * Create Paypal request URL for $product_count number of products. * - * @param int $product_count Number of products to include in the order. - * @param bool $testmode Whether to test using sandbox or not. - * + * @param int $product_count Number of products to include in the order. + * @param bool $testmode Whether to test using sandbox or not. + * @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 */ - protected function get_request_url( $product_count, $testmode ) { + protected function get_request_url( $product_count, $testmode, $product_prices = array(), $calc_order_totals = true ) { // Create products. $this->create_products( $product_count ); $this->order = WC_Helper_Order::create_order( $this->user ); - $this->add_products_to_order( $this->order ); + $this->add_products_to_order( $this->order, $product_prices ); // Set payment method to Paypal. $payment_gateways = WC()->payment_gateways->payment_gateways(); $this->order->set_payment_method( $payment_gateways['paypal'] ); - $this->order->calculate_totals(); + + // Add tax. + if ( wc_tax_enabled() ) { + $tax_rate = array( + 'tax_rate_country' => '', + 'tax_rate_state' => '', + 'tax_rate' => '11.0000', + 'tax_rate_name' => 'TAX', + 'tax_rate_priority' => '1', + 'tax_rate_compound' => '0', + 'tax_rate_shipping' => '1', + 'tax_rate_order' => '1', + 'tax_rate_class' => '', + ); + WC_Tax::_insert_tax_rate( $tax_rate ); + + $tax_item = new WC_Order_Item_Tax(); + $tax_item->set_rate( 100 ); + $tax_item->set_tax_total( 100 ); + $tax_item->set_shipping_tax_total( 100 ); + $this->order->add_item( $tax_item ); + $this->order->save(); + } + $this->order->calculate_shipping(); + if ( $calc_order_totals ) { + $this->order->calculate_totals(); + } return $this->paypal_request->get_request_url( $this->order, $testmode ); } /** - * Clean up order, deletes all products in order, too. + * Clean up order, tax, deletes all products in order, too. */ protected function clean_up() { + global $wpdb; + $wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rates" ); + $wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_tax_rate_locations" ); + WC_Helper_Order::delete_order( $this->order->get_id() ); + } /** @@ -116,7 +163,8 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { $shipping_total += $this->order->get_shipping_tax(); } $epsilon = 0.01; - $this->assertTrue( abs( $shipping_total - floatval( $query_array['shipping_1'] ) ) < $epsilon ); + $this->assertTrue( abs( $shipping_total - floatval( $query_array['shipping_1'] ) ) < $epsilon, + 'Shipping tax mismatch: shipping total=' . $shipping_total . ' vs request shipping=' . $query_array['shipping_1'] ); } @@ -140,12 +188,10 @@ 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 */ protected function check_large_order( $shipping_tax_included, $testmode ) { $request_url = $this->get_request_url( 30, $testmode ); - $this->check_order_common_props( $request_url, $testmode ); // Check fields limited to 127 characters for length. @@ -178,18 +224,29 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { } + + /** + * Return true if value is < 0, false otherwise. + * + * @param int|float $value Tested value. + * @return bool + */ + protected function is_negative( $value ) { + return $value < 0; + } + /** * Test small order with fewer items, URL length should be < 2083 characters. * - * @param int $product_count Number of products to include in the order. - * @param bool $shipping_tax_included Whether the shipping tax should be included or not. - * @param bool $testmode Whether to use Paypal sandbox. - * + * @param int $product_count Number of products to include in the order. + * @param bool $shipping_tax_included Whether the shipping tax should be included or not. + * @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 */ - protected function check_small_order( $product_count, $shipping_tax_included, $testmode ) { - $request_url = $this->get_request_url( $product_count, $testmode ); - + 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 ); $this->check_order_common_props( $request_url, $testmode ); $query_string = wp_parse_url( $request_url, PHP_URL_QUERY ) @@ -200,7 +257,7 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { // Check that there are $product_count line items in the request URL. // However, if shipping tax is included, there is only one item. - if ( $shipping_tax_included ) { + if ( $shipping_tax_included || array_filter( $product_prices, array( $this, 'is_negative' ) ) || ! $calc_order_totals ) { $product_count = 1; } @@ -226,6 +283,36 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { } /** + * Test order with one product having negative amount. + * Amount < 0 forces tax inclusion and single line item, since WC_Gateway_Paypal_Request::prepare_line_items() + * will return false. + * + * @param bool $testmode Whether PayPal request should point to sandbox or live production. + * @throws WC_Data_Exception + */ + protected function check_negative_amount( $testmode ) { + $shipping_tax_included = true; + $product_prices = array( 6, 6, 6, 6, -3 ); + $this->check_small_order( count( $product_prices ), $shipping_tax_included, $testmode, $product_prices ); + } + + /** + * Test order with totals mismatched. + * This forces tax inclusion and single line item, since WC_Gateway_Paypal_Request::prepare_line_items() + * will return false. + * + * @param bool $testmode Whether PayPal request should point to sandbox or live production. + * @throws WC_Data_Exception + */ + protected function check_totals_mismatch( $testmode ) { + // totals mismatch forces tax inclusion and single line item. + $shipping_tax_included = true; + $this->check_small_order( 5, $shipping_tax_included, $testmode, array(), false ); + } + + /** + * Test for request_url() method. + * * @throws WC_Data_Exception */ public function test_request_url() { @@ -235,17 +322,16 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { ) ); wp_set_current_user( $this->user ); - // Paths through code changed by: - // - $sandbox (true/false) - // - wc_tax_enabled() - // - wc_prices_include_tax(). - // wc_tax_enabled() and wc_prices_include_tax() determine if shipping tax should be included, - // these are the correct options. + // wc_tax_enabled(), wc_prices_include_tax() and WC_Gateway_Paypal_Request::prepare_line_items() determine if + // shipping tax should be included, these are the correct options. + // Note that prepare_line_items() can return false in 2 cases, tested separately below: + // - order totals mismatch and + // - item amount < 0. $correct_options = array( // woocommerce_calc_taxes, woocommerce_prices_include_tax, $shipping_tax_included values. array( 'no', 'no', false ), array( 'yes', 'no', false ), - // array( 'no', 'yes', false ), // this is not a valid option due to definition of wc_prices_include_tax() + // array( 'no', 'yes', false ), // this is not a valid option due to definition of wc_prices_include_tax(). array( 'yes', 'yes', true ), ); @@ -272,6 +358,12 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { // Test order with URL longer than limit. $this->check_large_order( $shipping_tax_included, $testmode ); + // Test amount < 0. + $this->check_negative_amount( $testmode ); + + // Check order totals mismatch. + $this->check_totals_mismatch( $testmode ); + } } From f39e8e39002b2c7bfbac2e24b8d4ece0c5f0eb16 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Tue, 27 Mar 2018 16:28:25 +0200 Subject: [PATCH 25/34] Removed incorrect comment --- .../paypal/includes/class-wc-gateway-paypal-request.php | 3 --- 1 file changed, 3 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 0e2b5b1f683..4afd4cdf7e7 100644 --- a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php +++ b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php @@ -289,9 +289,6 @@ class WC_Gateway_Paypal_Request { */ protected function get_line_item_args( $order, $force_one_line_item = false ) { - /** - * Try passing a line item per product if supported. - */ if ( ( ! wc_tax_enabled() || ! wc_prices_include_tax() ) && $this->prepare_line_items( $order ) ) { $include_shipping_tax = false; } else { From 86f1b3701fd8486eb9a41b4eb8cb0e4bacddda98 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Tue, 27 Mar 2018 17:19:49 +0200 Subject: [PATCH 26/34] Tag stripped outside template Stripping only affects aria-label attribute now. --- includes/wc-template-functions.php | 4 ++++ templates/loop/add-to-cart.php | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 617499ab525..febfcf4d870 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -893,6 +893,10 @@ 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 ( array_key_exists( 'attributes', $args ) && array_key_exists( 'aria-label', $args['attributes'] ) ) { + $args['attributes']['aria-label'] = strip_tags( $args['attributes']['aria-label'] ); + } + wc_get_template( 'loop/add-to-cart.php', $args ); } } diff --git a/templates/loop/add-to-cart.php b/templates/loop/add-to-cart.php index edb46e36116..67182c69576 100644 --- a/templates/loop/add-to-cart.php +++ b/templates/loop/add-to-cart.php @@ -10,8 +10,9 @@ * 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 + * @see https://docs.woocommerce.com/document/template-structure/ + * @author WooThemes + * @package WooCommerce/Templates * @version 3.3.0 */ @@ -26,7 +27,7 @@ echo apply_filters( 'woocommerce_loop_add_to_cart_link', // WPCS: XSS ok. esc_url( $product->add_to_cart_url() ), esc_attr( isset( $args['quantity'] ) ? $args['quantity'] : 1 ), esc_attr( isset( $args['class'] ) ? $args['class'] : 'button' ), - isset( $args['attributes'] ) ? wc_implode_html_attributes( array_map( 'strip_tags', $args['attributes'] ) ) : '', + isset( $args['attributes'] ) ? wc_implode_html_attributes( $args['attributes'] ) : '', esc_html( $product->add_to_cart_text() ) ), $product, $args ); From 435337c3e0353ecfdf6c28756a0e1aba2cf72844 Mon Sep 17 00:00:00 2001 From: Jonathan Belcher Date: Tue, 27 Mar 2018 11:51:47 -0400 Subject: [PATCH 27/34] Bump version Number --- templates/single-product/review-meta.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/single-product/review-meta.php b/templates/single-product/review-meta.php index ab7ab0db9cb..e84269bd8be 100644 --- a/templates/single-product/review-meta.php +++ b/templates/single-product/review-meta.php @@ -13,7 +13,7 @@ * @see https://docs.woocommerce.com/document/template-structure/ * @author WooThemes * @package WooCommerce/Templates - * @version 3.0.0 + * @version 3.4.0 */ if ( ! defined( 'ABSPATH' ) ) { From 80ec10fc21e0ff18a199a51178d0cdf8d8c3dae0 Mon Sep 17 00:00:00 2001 From: Gerhard Potgieter Date: Wed, 28 Mar 2018 08:28:13 +0200 Subject: [PATCH 28/34] Fix unit test for import, the way the test is set up it expects a no for backorders and not an empty string --- tests/unit-tests/importer/product.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/unit-tests/importer/product.php b/tests/unit-tests/importer/product.php index 28f4fe9d5ac..01633f00408 100644 --- a/tests/unit-tests/importer/product.php +++ b/tests/unit-tests/importer/product.php @@ -306,7 +306,7 @@ class WC_Tests_Product_CSV_Importer extends WC_Unit_Test_Case { 'tax_class' => 'standard', 'stock_status' => 'instock', 'stock_quantity' => '', - 'backorders' => '', + 'backorders' => 'no', 'sold_individually' => '', 'weight' => '', 'length' => '', @@ -358,7 +358,7 @@ class WC_Tests_Product_CSV_Importer extends WC_Unit_Test_Case { 'tax_class' => 'standard', 'stock_status' => 'instock', 'stock_quantity' => '', - 'backorders' => '', + 'backorders' => 'no', 'sold_individually' => '', 'weight' => '', 'length' => '', @@ -394,7 +394,7 @@ class WC_Tests_Product_CSV_Importer extends WC_Unit_Test_Case { 'tax_class' => '', 'stock_status' => 'outofstock', 'stock_quantity' => '', - 'backorders' => '', + 'backorders' => 'no', 'sold_individually' => '', 'weight' => '', 'length' => '', @@ -446,7 +446,7 @@ class WC_Tests_Product_CSV_Importer extends WC_Unit_Test_Case { 'tax_class' => 'standard', 'stock_status' => 'instock', 'stock_quantity' => 6, - 'backorders' => '', + 'backorders' => 'no', 'sold_individually' => '', 'weight' => 1.0, 'length' => 2.0, @@ -536,7 +536,7 @@ class WC_Tests_Product_CSV_Importer extends WC_Unit_Test_Case { 'tax_class' => '', 'stock_status' => 'instock', 'stock_quantity' => '', - 'backorders' => '', + 'backorders' => 'no', 'sold_individually' => '', 'weight' => '', 'length' => '', From 1f2468ca3e23372b98986351adf27791b6630f97 Mon Sep 17 00:00:00 2001 From: Gerhard Potgieter Date: Wed, 28 Mar 2018 09:47:15 +0200 Subject: [PATCH 29/34] When no per_page is defined for list wp-cli command default to displaying all items as per the wp-cli standard. --- includes/cli/class-wc-cli-rest-command.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/includes/cli/class-wc-cli-rest-command.php b/includes/cli/class-wc-cli-rest-command.php index be2a050835d..9a5adca6fb9 100644 --- a/includes/cli/class-wc-cli-rest-command.php +++ b/includes/cli/class-wc-cli-rest-command.php @@ -223,6 +223,10 @@ class WC_CLI_REST_Command { $assoc_args['format'] = 'table'; } + if ( empty( $assoc_args['per_page'] ) ) { + $assoc_args['per_page'] = '-1'; + } + if ( ! empty( $assoc_args['format'] ) && 'count' === $assoc_args['format'] ) { echo (int) $headers['X-WP-Total']; } elseif ( 'headers' === $assoc_args['format'] ) { From 9c67760bc07efb2daec275a3ca26a48f9eed696a Mon Sep 17 00:00:00 2001 From: Gerhard Potgieter Date: Wed, 28 Mar 2018 10:18:10 +0200 Subject: [PATCH 30/34] per_page must be between 1 and 100, default to 100. --- includes/cli/class-wc-cli-rest-command.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/cli/class-wc-cli-rest-command.php b/includes/cli/class-wc-cli-rest-command.php index 9a5adca6fb9..2b0a218739e 100644 --- a/includes/cli/class-wc-cli-rest-command.php +++ b/includes/cli/class-wc-cli-rest-command.php @@ -206,6 +206,10 @@ class WC_CLI_REST_Command { $method = 'GET'; } + if ( ! isset( $assoc_args['per_page'] ) || empty( $assoc_args['per_page'] ) ) { + $assoc_args['per_page'] = '100'; + } + list( $status, $body, $headers ) = $this->do_request( $method, $this->get_filled_route( $args ), $assoc_args ); if ( ! empty( $assoc_args['format'] ) && 'ids' === $assoc_args['format'] ) { $items = array_column( $body, 'id' ); @@ -223,10 +227,6 @@ class WC_CLI_REST_Command { $assoc_args['format'] = 'table'; } - if ( empty( $assoc_args['per_page'] ) ) { - $assoc_args['per_page'] = '-1'; - } - if ( ! empty( $assoc_args['format'] ) && 'count' === $assoc_args['format'] ) { echo (int) $headers['X-WP-Total']; } elseif ( 'headers' === $assoc_args['format'] ) { From 59c47c440790009c963ac72634c7de0576f986c6 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Wed, 28 Mar 2018 11:04:34 +0200 Subject: [PATCH 31/34] Refactored line item logic - prepare_line_items is not used as a test anymore, it only updates line items - add_line_item is not used as a test anymore, only adds line items - added method line_items_valid to test for valid line items - shipping tax inclusion test moved to get_shipping_cost_line_item to encapsulate shipping tax-related logic --- .../class-wc-gateway-paypal-request.php | 84 ++++++++++--------- 1 file changed, 44 insertions(+), 40 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 4afd4cdf7e7..bd4a0dc09ec 100644 --- a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php +++ b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php @@ -240,13 +240,12 @@ class WC_Gateway_Paypal_Request { * Get shipping cost line item args for paypal request. * * @param WC_Order $order Order object. - * @param bool $include_shipping_tax Whether to include shipping tax or not. * @return array */ - protected function get_shipping_cost_line_item( $order, $include_shipping_tax ) { + protected function get_shipping_cost_line_item( $order ) { $line_item_args = array(); $shipping_total = $order->get_shipping_total(); - if ( $include_shipping_tax ) { + if ( wc_tax_enabled() && wc_prices_include_tax() || ! $this->line_items_valid( $order ) ) { $shipping_total += $order->get_shipping_tax(); } @@ -267,15 +266,14 @@ class WC_Gateway_Paypal_Request { * Get line item args for paypal request as a single line item. * * @param WC_Order $order Order object. - * @param bool $include_shipping_tax Whether to include shipping tax or not. * @return array */ - protected function get_line_item_args_single_item( $order, $include_shipping_tax ) { + protected function get_line_item_args_single_item( $order ) { $this->delete_line_items(); $all_items_name = $this->get_order_item_names( $order ); $this->add_line_item( $all_items_name ? $all_items_name : __( 'Order', 'woocommerce' ), 1, $this->number_format( $order->get_total() - $this->round( $order->get_shipping_total() + $order->get_shipping_tax(), $order ), $order ), $order->get_order_number() ); - $line_item_args = $this->get_shipping_cost_line_item( $order, $include_shipping_tax ); + $line_item_args = $this->get_shipping_cost_line_item( $order ); return array_merge( $line_item_args, $this->get_line_items() ); } @@ -288,32 +286,30 @@ 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->prepare_line_items( $order ) ) { - $include_shipping_tax = false; - } else { - $include_shipping_tax = true; + 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 || $include_shipping_tax ) { + if ( $force_one_line_item ) { /** * Send order as a single item. * * For shipping, we longer use shipping_1 because paypal ignores it if *any* shipping rules are within paypal, and paypal ignores anything over 5 digits (999.99 is the max). */ - $line_item_args = $this->get_line_item_args_single_item( $order, $include_shipping_tax ); + $line_item_args = $this->get_line_item_args_single_item( $order ); } else { /** * Passing a line item per product if supported. */ + $this->prepare_line_items( $order ); $line_item_args['tax_cart'] = $this->number_format( $order->get_total_tax(), $order ); if ( $order->get_total_discount() > 0 ) { $line_item_args['discount_amount_cart'] = $this->number_format( $this->round( $order->get_total_discount(), $order ), $order ); } - $line_item_args = array_merge( $line_item_args, $this->get_shipping_cost_line_item( $order, $include_shipping_tax ) ); + $line_item_args = array_merge( $line_item_args, $this->get_shipping_cost_line_item( $order ) ); $line_item_args = array_merge( $line_item_args, $this->get_line_items() ); } @@ -397,40 +393,55 @@ class WC_Gateway_Paypal_Request { } /** - * Get line items to send to paypal. + * Check if the order has valid line items to use for PayPal request. * - * @param WC_Order $order Order object. + * The line items are invalid in case of mismatch in totals or if any amount < 0. + * + * @param WC_Order $order Order to be examined. * @return bool */ - protected function prepare_line_items( $order ) { - $this->delete_line_items(); - $calculated_total = 0; + protected function line_items_valid( $order ) { + $negative_item_amount = false; + $calculated_total = 0; // 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 ); - $line_item = $this->add_line_item( $item->get_name(), 1, $item_line_total ); $calculated_total += $item_line_total; + } else { + $item_line_total = $this->number_format( $order->get_item_subtotal( $item, false ), $order ); + $calculated_total += $item_line_total * $item->get_quantity(); + } + + if ( $item_line_total < 0 ) { + $negative_item_amount = true; + } + } + $mismatched_totals = $this->number_format( $calculated_total + $order->get_total_tax() + $this->round( $order->get_shipping_total(), $order ) - $this->round( $order->get_total_discount(), $order ), $order ) !== $this->number_format( $order->get_total(), $order ); + return ! $negative_item_amount && ! $mismatched_totals; + } + + /** + * Get line items to send to paypal. + * + * @param WC_Order $order Order object. + */ + protected function prepare_line_items( $order ) { + $this->delete_line_items(); + + // 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 ); + $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 ); - $line_item = $this->add_line_item( $this->get_order_item_name( $order, $item ), $item->get_quantity(), $item_line_total, $sku ); - $calculated_total += $item_line_total * $item->get_quantity(); - } - - if ( ! $line_item ) { - return false; + $this->add_line_item( $this->get_order_item_name( $order, $item ), $item->get_quantity(), $item_line_total, $sku ); } } - - // Check for mismatched totals. - if ( $this->number_format( $calculated_total + $order->get_total_tax() + $this->round( $order->get_shipping_total(), $order ) - $this->round( $order->get_total_discount(), $order ), $order ) !== $this->number_format( $order->get_total(), $order ) ) { - return false; - } - - return true; } /** @@ -440,15 +451,10 @@ class WC_Gateway_Paypal_Request { * @param int $quantity Item quantity. * @param float $amount Amount. * @param string $item_number Item number. - * @return bool successfully added or not */ protected function add_line_item( $item_name, $quantity = 1, $amount = 0.0, $item_number = '' ) { $index = ( count( $this->line_items ) / 4 ) + 1; - if ( $amount < 0 ) { - return false; - } - $item = apply_filters( 'woocommerce_paypal_line_item', array( 'item_name' => html_entity_decode( wc_trim_string( $item_name ? $item_name : __( 'Item', 'woocommerce' ), 127 ), ENT_NOQUOTES, 'UTF-8' ), @@ -462,8 +468,6 @@ class WC_Gateway_Paypal_Request { $this->line_items[ 'quantity_' . $index ] = $item['quantity']; $this->line_items[ 'amount_' . $index ] = $item['amount']; $this->line_items[ 'item_number_' . $index ] = $this->limit_length( $item['item_number'], 127 ); - - return true; } /** From c2e27919cea0458bb4863472c2333760877bb27c Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Wed, 28 Mar 2018 11:19:03 +0200 Subject: [PATCH 32/34] Replaced array_key_exists with isset test --- 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 febfcf4d870..d68b1e65466 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -893,7 +893,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 ( array_key_exists( 'attributes', $args ) && array_key_exists( 'aria-label', $args['attributes'] ) ) { + if ( isset( $args['attributes']['aria-label'] ) ) { $args['attributes']['aria-label'] = strip_tags( $args['attributes']['aria-label'] ); } From 8d3c76d980db2962b86070120a354d17bc780c18 Mon Sep 17 00:00:00 2001 From: michakrapp Date: Wed, 28 Mar 2018 12:18:14 +0200 Subject: [PATCH 33/34] account edit form disble autocomplete for password fields --- templates/myaccount/form-edit-account.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/myaccount/form-edit-account.php b/templates/myaccount/form-edit-account.php index c181257952c..e76bcf60b32 100644 --- a/templates/myaccount/form-edit-account.php +++ b/templates/myaccount/form-edit-account.php @@ -52,15 +52,15 @@ do_action( 'woocommerce_before_edit_account_form' ); ?>

- +

- +

- +

From 3809ab3f4c0ad15bdcb0ce14491e10ab96b9090f Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Wed, 28 Mar 2018 13:08:05 +0200 Subject: [PATCH 34/34] Fixed shipping tax logic to behave the same as originally --- .../paypal/includes/class-wc-gateway-paypal-request.php | 9 +++++---- tests/unit-tests/gateways/paypal/request.php | 3 ++- 2 files changed, 7 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 bd4a0dc09ec..588d15eea10 100644 --- a/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php +++ b/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php @@ -240,12 +240,13 @@ class WC_Gateway_Paypal_Request { * Get shipping cost line item args for paypal request. * * @param WC_Order $order Order object. + * @param bool $force_one_line_item Whether one line item was forced by validation or URL length. * @return array */ - protected function get_shipping_cost_line_item( $order ) { + protected function get_shipping_cost_line_item( $order, $force_one_line_item ) { $line_item_args = array(); $shipping_total = $order->get_shipping_total(); - if ( wc_tax_enabled() && wc_prices_include_tax() || ! $this->line_items_valid( $order ) ) { + if ( $force_one_line_item ) { $shipping_total += $order->get_shipping_tax(); } @@ -273,7 +274,7 @@ class WC_Gateway_Paypal_Request { $all_items_name = $this->get_order_item_names( $order ); $this->add_line_item( $all_items_name ? $all_items_name : __( 'Order', 'woocommerce' ), 1, $this->number_format( $order->get_total() - $this->round( $order->get_shipping_total() + $order->get_shipping_tax(), $order ), $order ), $order->get_order_number() ); - $line_item_args = $this->get_shipping_cost_line_item( $order ); + $line_item_args = $this->get_shipping_cost_line_item( $order, true ); return array_merge( $line_item_args, $this->get_line_items() ); } @@ -309,7 +310,7 @@ class WC_Gateway_Paypal_Request { $line_item_args['discount_amount_cart'] = $this->number_format( $this->round( $order->get_total_discount(), $order ), $order ); } - $line_item_args = array_merge( $line_item_args, $this->get_shipping_cost_line_item( $order ) ); + $line_item_args = array_merge( $line_item_args, $this->get_shipping_cost_line_item( $order, false ) ); $line_item_args = array_merge( $line_item_args, $this->get_line_items() ); } diff --git a/tests/unit-tests/gateways/paypal/request.php b/tests/unit-tests/gateways/paypal/request.php index 8f576be8bde..3c98f1a8f60 100644 --- a/tests/unit-tests/gateways/paypal/request.php +++ b/tests/unit-tests/gateways/paypal/request.php @@ -356,7 +356,8 @@ class WC_Tests_Paypal_Gateway_Request extends WC_Unit_Test_Case { $this->check_small_order( 11, $shipping_tax_included, $testmode ); // Test order with URL longer than limit. - $this->check_large_order( $shipping_tax_included, $testmode ); + // Many items in order -> forced to use one line item -> shipping tax included. + $this->check_large_order( true, $testmode ); // Test amount < 0. $this->check_negative_amount( $testmode );