Merge branch 'master' into fix/incorrect-escaping

This commit is contained in:
Claudio Sanches 2018-03-28 14:32:51 -03:00
commit 9ab58dde47
17 changed files with 619 additions and 149 deletions

View File

@ -489,26 +489,15 @@ class WC_Admin_Setup_Wizard {
<?php
if ( 'unknown' === get_option( 'woocommerce_allow_tracking', 'unknown' ) ) {
$tracking_opt_out = true;
// EU should be opt-in.
if ( in_array( $country, WC()->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;
}
?>
<div class="woocommerce-tracker">
<p class="checkbox">
<input type="checkbox" id="wc_tracker_optin" name="wc_tracker_optin" value="yes" <?php echo $tracking_opt_out ? 'checked' : ''; ?> />
<label for="wc_tracker_optin"><?php esc_html_e( 'Help WooCommerce improve by enabling usage tracking.', 'woocommerce' ); ?></label>
<input type="checkbox" id="wc_tracker_checkbox" name="wc_tracker_checkbox" value="yes" checked />
<label for="wc_tracker_checkbox"><?php esc_html_e( 'Help WooCommerce improve with usage tracking.', 'woocommerce' ); ?></label>
</p>
<p>
<?php
esc_html_e( 'Checking this box means making WooCommerce better &mdash; your store will be considered as we evaluate new features, judge the quality of an update, or determine if an improvement makes sense. If you do not check this box, we will not know this store exists and we will not collect any usage data.', 'woocommerce' );
esc_html_e( 'Gathering usage data allows us to make WooCommerce better &mdash; your store will be considered as we evaluate new features, judge the quality of an update, or determine if an improvement makes sense. If you would rather opt-out, and do not check this box, we will not know this store exists and we will not collect any usage data.', 'woocommerce' );
echo ' <a target="_blank" href="https://woocommerce.com/usage-tracking/">' . esc_html__( 'Read more about what we collect.', 'woocommerce' ) . '</a>';
?>
</p>
@ -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 );

View File

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

View File

@ -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' );

View File

@ -35,6 +35,14 @@ class WC_Gateway_Paypal_Request {
*/
protected $notify_url;
/**
* Endpoint for requests to PayPal.
*
* @var string
*/
protected $endpoint;
/**
* Constructor.
*
@ -53,15 +61,16 @@ class WC_Gateway_Paypal_Request {
* @return string
*/
public function get_request_url( $order, $sandbox = false ) {
if ( $sandbox ) {
$this->endpoint = 'https://www.sandbox.paypal.com/cgi-bin/webscr?test_ipn=1&';
} else {
$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 ) );
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->endpoint . $paypal_args;
}
/**
@ -72,12 +81,90 @@ 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;
}
/**
* Get transaction args for paypal request, except for line item args.
*
* @param WC_Order $order Order object.
* @return array
*/
protected function get_transaction_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.
*
* 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 ( strlen( $this->endpoint . $query_candidate ) <= $max_paypal_length ) {
return $paypal_args;
}
return apply_filters(
'woocommerce_paypal_args', array_merge(
$this->get_transaction_args( $order ),
$this->get_line_item_args( $order, true )
), $order
);
}
/**
* Get PayPal Args for passing to PP.
*
@ -87,45 +174,14 @@ 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 );
return apply_filters(
$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_transaction_args( $order ),
$this->get_line_item_args( $order )
), $order
);
return $this->fix_request_length( $order, $paypal_args );
}
/**
@ -181,60 +237,82 @@ class WC_Gateway_Paypal_Request {
}
/**
* Get line item args for 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, $force_one_line_item ) {
$line_item_args = array();
$shipping_total = $order->get_shipping_total();
if ( $force_one_line_item ) {
$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.
* @return array
*/
protected function get_line_item_args( $order ) {
protected function get_line_item_args_single_item( $order ) {
$this->delete_line_items();
/**
* Try passing a line item per product if supported.
*/
if ( ( ! wc_tax_enabled() || ! wc_prices_include_tax() ) && $this->prepare_line_items( $order ) ) {
$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, true );
$line_item_args = array();
return array_merge( $line_item_args, $this->get_line_items() );
}
/**
* Get line item args for paypal request.
*
* @param WC_Order $order Order object.
* @param bool $force_one_line_item Create only one item for this order.
* @return array
*/
protected function get_line_item_args( $order, $force_one_line_item = false ) {
if ( wc_tax_enabled() && wc_prices_include_tax() || ! $this->line_items_valid( $order ) ) {
$force_one_line_item = true;
}
$line_item_args = array();
if ( $force_one_line_item ) {
/**
* Send order as a single item.
*
* 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 );
} 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 );
}
// 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 ) );
}
$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() );
} 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).
*/
$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;
@ -316,40 +394,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;
}
/**
@ -359,15 +452,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 || $index > 9 ) {
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' ),
@ -381,8 +469,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;
}
/**

View File

@ -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();
}
/**

View File

@ -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 );
@ -502,7 +502,7 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
return $value ? 'yes' : 'no';
}
return '';
return 'no';
}
/**

View File

@ -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;
}

View File

@ -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 ( isset( $args['attributes']['aria-label'] ) ) {
$args['attributes']['aria-label'] = strip_tags( $args['attributes']['aria-label'] );
}
wc_get_template( 'loop/add-to-cart.php', $args );
}
}

View File

@ -33,11 +33,11 @@ if ( is_user_logged_in() ) {
<p class="form-row form-row-first">
<label for="username"><?php esc_html_e( 'Username or email', 'woocommerce' ); ?> <span class="required">*</span></label>
<input type="text" class="input-text" name="username" id="username" />
<input type="text" class="input-text" name="username" id="username" autocomplete="username" />
</p>
<p class="form-row form-row-last">
<label for="password"><?php esc_html_e( 'Password', 'woocommerce' ); ?> <span class="required">*</span></label>
<input class="input-text" type="password" name="password" id="password" />
<input class="input-text" type="password" name="password" id="password" autocomplete="current-password" />
</p>
<div class="clear"></div>

View File

@ -25,11 +25,11 @@ do_action( 'woocommerce_before_edit_account_form' ); ?>
<p class="woocommerce-form-row woocommerce-form-row--first form-row form-row-first">
<label for="account_first_name"><?php esc_html_e( 'First name', 'woocommerce' ); ?> <span class="required">*</span></label>
<input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="account_first_name" id="account_first_name" value="<?php echo esc_attr( $user->first_name ); ?>" />
<input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="account_first_name" id="account_first_name" autocomplete="given-name" value="<?php echo esc_attr( $user->first_name ); ?>" />
</p>
<p class="woocommerce-form-row woocommerce-form-row--last form-row form-row-last">
<label for="account_last_name"><?php esc_html_e( 'Last name', 'woocommerce' ); ?> <span class="required">*</span></label>
<input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="account_last_name" id="account_last_name" value="<?php echo esc_attr( $user->last_name ); ?>" />
<input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="account_last_name" id="account_last_name" autocomplete="family-name" value="<?php echo esc_attr( $user->last_name ); ?>" />
</p>
<div class="clear"></div>
@ -41,7 +41,7 @@ do_action( 'woocommerce_before_edit_account_form' ); ?>
<p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
<label for="account_email"><?php esc_html_e( 'Email address', 'woocommerce' ); ?> <span class="required">*</span></label>
<input type="email" class="woocommerce-Input woocommerce-Input--email input-text" name="account_email" id="account_email" value="<?php echo esc_attr( $user->user_email ); ?>" />
<input type="email" class="woocommerce-Input woocommerce-Input--email input-text" name="account_email" id="account_email" autocomplete="email" value="<?php echo esc_attr( $user->user_email ); ?>" />
</p>
<fieldset>
@ -49,15 +49,15 @@ do_action( 'woocommerce_before_edit_account_form' ); ?>
<p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
<label for="password_current"><?php esc_html_e( 'Current password (leave blank to leave unchanged)', 'woocommerce' ); ?></label>
<input type="password" class="woocommerce-Input woocommerce-Input--password input-text" name="password_current" id="password_current" />
<input type="password" class="woocommerce-Input woocommerce-Input--password input-text" name="password_current" id="password_current" autocomplete="off" />
</p>
<p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
<label for="password_1"><?php esc_html_e( 'New password (leave blank to leave unchanged)', 'woocommerce' ); ?></label>
<input type="password" class="woocommerce-Input woocommerce-Input--password input-text" name="password_1" id="password_1" />
<input type="password" class="woocommerce-Input woocommerce-Input--password input-text" name="password_1" id="password_1" autocomplete="off" />
</p>
<p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
<label for="password_2"><?php esc_html_e( 'Confirm new password', 'woocommerce' ); ?></label>
<input type="password" class="woocommerce-Input woocommerce-Input--password input-text" name="password_2" id="password_2" />
<input type="password" class="woocommerce-Input woocommerce-Input--password input-text" name="password_2" id="password_2" autocomplete="off" />
</p>
</fieldset>
<div class="clear"></div>

View File

@ -41,11 +41,11 @@ if ( ! defined( 'ABSPATH' ) ) {
<p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
<label for="username"><?php esc_html_e( 'Username or email address', 'woocommerce' ); ?> <span class="required">*</span></label>
<input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="username" id="username" value="<?php echo ( ! empty( $_POST['username'] ) ) ? esc_attr( wp_unslash( $_POST['username'] ) ) : ''; ?>" /><?php // @codingStandardsIgnoreLine ?>
<input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="username" id="username" autocomplete="username" value="<?php echo ( ! empty( $_POST['username'] ) ) ? esc_attr( wp_unslash( $_POST['username'] ) ) : ''; ?>" /><?php // @codingStandardsIgnoreLine ?>
</p>
<p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
<label for="password"><?php esc_html_e( 'Password', 'woocommerce' ); ?> <span class="required">*</span></label>
<input class="woocommerce-Input woocommerce-Input--text input-text" type="password" name="password" id="password" />
<input class="woocommerce-Input woocommerce-Input--text input-text" type="password" name="password" id="password" autocomplete="current-password" />
</p>
<?php do_action( 'woocommerce_login_form' ); ?>
@ -81,21 +81,21 @@ if ( ! defined( 'ABSPATH' ) ) {
<p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
<label for="reg_username"><?php esc_html_e( 'Username', 'woocommerce' ); ?> <span class="required">*</span></label>
<input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="username" id="reg_username" value="<?php echo ( ! empty( $_POST['username'] ) ) ? esc_attr( wp_unslash( $_POST['username'] ) ) : ''; ?>" /><?php // @codingStandardsIgnoreLine ?>
<input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="username" id="reg_username" autocomplete="username" value="<?php echo ( ! empty( $_POST['username'] ) ) ? esc_attr( wp_unslash( $_POST['username'] ) ) : ''; ?>" /><?php // @codingStandardsIgnoreLine ?>
</p>
<?php endif; ?>
<p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
<label for="reg_email"><?php esc_html_e( 'Email address', 'woocommerce' ); ?> <span class="required">*</span></label>
<input type="email" class="woocommerce-Input woocommerce-Input--text input-text" name="email" id="reg_email" value="<?php echo ( ! empty( $_POST['email'] ) ) ? esc_attr( wp_unslash( $_POST['email'] ) ) : ''; ?>" /><?php // @codingStandardsIgnoreLine ?>
<input type="email" class="woocommerce-Input woocommerce-Input--text input-text" name="email" id="reg_email" autocomplete="email" value="<?php echo ( ! empty( $_POST['email'] ) ) ? esc_attr( wp_unslash( $_POST['email'] ) ) : ''; ?>" /><?php // @codingStandardsIgnoreLine ?>
</p>
<?php if ( 'no' === get_option( 'woocommerce_registration_generate_password' ) ) : ?>
<p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
<label for="reg_password"><?php esc_html_e( 'Password', 'woocommerce' ); ?> <span class="required">*</span></label>
<input type="password" class="woocommerce-Input woocommerce-Input--text input-text" name="password" id="reg_password" />
<input type="password" class="woocommerce-Input woocommerce-Input--text input-text" name="password" id="reg_password" autocomplete="new-password" />
</p>
<?php endif; ?>

View File

@ -25,7 +25,7 @@ wc_print_notices(); ?>
<p class="woocommerce-form-row woocommerce-form-row--first form-row form-row-first">
<label for="user_login"><?php esc_html_e( 'Username or email', 'woocommerce' ); ?></label>
<input class="woocommerce-Input woocommerce-Input--text input-text" type="text" name="user_login" id="user_login" />
<input class="woocommerce-Input woocommerce-Input--text input-text" type="text" name="user_login" id="user_login" autocomplete="username" />
</p>
<div class="clear"></div>

View File

@ -25,11 +25,11 @@ wc_print_notices(); ?>
<p class="woocommerce-form-row woocommerce-form-row--first form-row form-row-first">
<label for="password_1"><?php esc_html_e( 'New password', 'woocommerce' ); ?> <span class="required">*</span></label>
<input type="password" class="woocommerce-Input woocommerce-Input--text input-text" name="password_1" id="password_1" />
<input type="password" class="woocommerce-Input woocommerce-Input--text input-text" name="password_1" id="password_1" autocomplete="new-password" />
</p>
<p class="woocommerce-form-row woocommerce-form-row--last form-row form-row-last">
<label for="password_2"><?php esc_html_e( 'Re-enter new password', 'woocommerce' ); ?> <span class="required">*</span></label>
<input type="password" class="woocommerce-Input woocommerce-Input--text input-text" name="password_2" id="password_2" />
<input type="password" class="woocommerce-Input woocommerce-Input--text input-text" name="password_2" id="password_2" autocomplete="new-password" />
</p>
<input type="hidden" name="reset_key" value="<?php echo esc_attr( $args['key'] ); ?>" />

View File

@ -36,8 +36,9 @@ if ( '0' === $comment->comment_approved ) { ?>
if ( 'yes' === get_option( 'woocommerce_review_rating_verification_label' ) && $verified ) {
echo '<em class="woocommerce-review__verified verified">(' . esc_attr__( 'verified owner', 'woocommerce' ) . ')</em> ';
}
?>
<span class="woocommerce-review__dash">&ndash;</span> <time class="woocommerce-review__published-date" datetime="<?php echo get_comment_date( 'c' ); ?>"><?php echo get_comment_date( wc_date_format() ); ?></time>
<span class="woocommerce-review__dash">&ndash;</span> <time class="woocommerce-review__published-date" datetime="<?php echo esc_attr( get_comment_date( 'c' ) ); ?>"><?php echo esc_html( get_comment_date( wc_date_format() ) ); ?></time>
</p>
<?php

View File

@ -31,7 +31,7 @@ if ( $upsells ) : ?>
<?php foreach ( $upsells as $upsell ) : ?>
<?php
$post_object = get_post( $upsell->get_id() );
$post_object = get_post( $upsell->get_id() );
setup_postdata( $GLOBALS['post'] =& $post_object );

View File

@ -0,0 +1,373 @@
<?php
/**
* Unit tests for Paypal standard gateway request.
*
* @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.
*
* @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.
* @param array $prices Array of prices to use for created products. Leave empty for default prices.
*/
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' => $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++;
}
}
/**
* 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.
* @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, $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, $product_prices );
// Set payment method to Paypal.
$payment_gateways = WC()->payment_gateways->payment_gateways();
$this->order->set_payment_method( $payment_gateways['paypal'] );
// 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, 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() );
}
/**
* 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,
'Shipping tax mismatch: shipping total=' . $shipping_total . ' vs request shipping=' . $query_array['shipping_1'] );
}
/**
* 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 ) );
// 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.
$this->clean_up();
}
/**
* 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 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, $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 )
? 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 || array_filter( $product_prices, array( $this, 'is_negative' ) ) || ! $calc_order_totals ) {
$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 ) );
// 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 ] ) );
}
$this->check_shipping_tax( $query_array, $shipping_tax_included );
// Remove order and created products.
$this->clean_up();
}
/**
* 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() {
// User set up.
$this->user = $this->factory->user->create( array(
'role' => 'administrator',
) );
wp_set_current_user( $this->user );
// 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( 'yes', 'yes', true ),
);
// 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 );
// 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 < 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.
// 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 );
// Check order totals mismatch.
$this->check_totals_mismatch( $testmode );
}
}
}

View File

@ -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' => '',