Merge remote-tracking branch 'origin/master' into HEAD

This commit is contained in:
claudiulodro 2018-01-03 10:13:23 -08:00
commit d2618c2330
40 changed files with 521 additions and 127 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1946,9 +1946,23 @@ ul.wc_coupon_list_block {
padding: 0 !important;
height: 2em !important;
width: 2em;
overflow: hidden;
&::after {
@include icon_dashicons;
font-family: 'Dashicons';
speak: none;
font-weight: normal;
font-variant: normal;
text-transform: none;
-webkit-font-smoothing: antialiased;
margin: 0;
text-indent: 0;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-align: center;
line-height: 1.85;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -491,6 +491,7 @@ body {
padding: 0;
border-bottom: 1px solid #eee;
color: #666;
align-items: center;
&:last-child {
border-bottom: 0;
@ -508,7 +509,7 @@ body {
padding: 2em 0;
align-self: stretch;
display: flex;
align-items: center;
align-items: baseline;
justify-content: center;
img {
@ -516,8 +517,13 @@ body {
}
}
&.stripe-logo .wc-wizard-service-name img {
padding: 8px 0;
}
&.paypal-logo .wc-wizard-service-name img {
max-width: 87px;
padding: 2px 0;
}
&.klarna-logo .wc-wizard-service-name {
@ -525,6 +531,7 @@ body {
img {
max-width: 87px;
padding: 12px 0;
}
}
@ -533,12 +540,13 @@ body {
img {
max-width: 95px;
padding: 12px 0;
}
}
.wc-wizard-service-description {
flex-grow: 1;
padding: 2em 20px;
padding: 20px;
p {
margin-bottom: 0;
@ -562,6 +570,8 @@ body {
cursor: pointer;
padding: 2em 0;
position: relative;
max-height: 1.5em;
align-self: flex-start;
}
.wc-wizard-service-toggle {
@ -1036,10 +1046,10 @@ p.jetpack-terms {
}
}
.wc-wizard-service-setting-stripe_email, .wc-wizard-service-setting-ppec_paypal_api_subject {
.wc-wizard-service-setting-stripe_email, .wc-wizard-service-setting-ppec_paypal_email {
margin-top: 0.75em;
label.stripe_email, label.ppec_paypal_api_subject {
label.stripe_email, label.ppec_paypal_email {
position: absolute;
margin: -1px;
padding: 0;

View File

@ -22,19 +22,27 @@
*/
productExportForm.prototype.onSubmit = function( event ) {
event.preventDefault();
var currentDate = new Date(),
day = currentDate.getDate(),
month = currentDate.getMonth() + 1,
year = currentDate.getFullYear(),
timestamp = currentDate.getTime(),
filename = 'wc-product-export-' + day + '-' + month + '-' + year + '-' + timestamp + '.csv';
event.data.productExportForm.$form.addClass( 'woocommerce-exporter__exporting' );
event.data.productExportForm.$form.find('.woocommerce-exporter-progress').val( 0 );
event.data.productExportForm.$form.find('.woocommerce-exporter-button').prop( 'disabled', true );
event.data.productExportForm.processStep( 1, $( this ).serialize(), '' );
event.data.productExportForm.processStep( 1, $( this ).serialize(), '', filename );
};
/**
* Process the current export step.
*/
productExportForm.prototype.processStep = function( step, data, columns ) {
var $this = this,
productExportForm.prototype.processStep = function( step, data, columns, filename ) {
var $this = this,
selected_columns = $( '.woocommerce-exporter-columns' ).val(),
export_meta = $( '#woocommerce-exporter-meta:checked' ).length ? 1 : 0,
export_meta = $( '#woocommerce-exporter-meta:checked' ).length ? 1: 0,
export_types = $( '.woocommerce-exporter-types' ).val();
$.ajax( {
@ -48,6 +56,7 @@
selected_columns : selected_columns,
export_meta : export_meta,
export_types : export_types,
filename : filename,
security : wc_product_export_params.export_nonce
},
dataType: 'json',
@ -62,7 +71,7 @@
}, 2000 );
} else {
$this.$form.find('.woocommerce-exporter-progress').val( response.data.percentage );
$this.processStep( parseInt( response.data.step, 10 ), data, response.data.columns );
$this.processStep( parseInt( response.data.step, 10 ), data, response.data.columns, filename );
}
}

View File

@ -130,14 +130,14 @@ jQuery( function( $ ) {
.find( 'input.payment-email-input' )
.prop( 'required', true );
$( this ).closest( '.wc-wizard-service-settings' )
.find( '.wc-wizard-service-setting-stripe_email, .wc-wizard-service-setting-ppec_paypal_api_subject' )
.find( '.wc-wizard-service-setting-stripe_email, .wc-wizard-service-setting-ppec_paypal_email' )
.show();
} else {
$( this ).closest( '.wc-wizard-service-settings' )
.find( 'input.payment-email-input' )
.prop( 'required', false );
$( this ).closest( '.wc-wizard-service-settings' )
.find( '.wc-wizard-service-setting-stripe_email, .wc-wizard-service-setting-ppec_paypal_api_subject' )
.find( '.wc-wizard-service-setting-stripe_email, .wc-wizard-service-setting-ppec_paypal_email' )
.hide();
}
} ).find( 'input#stripe_create_account, input#ppec_paypal_reroute_requests' ).change();

View File

@ -9888,6 +9888,10 @@ msgstr ""
msgid "Learn more about coupons"
msgstr ""
#: includes/admin/list-tables/class-wc-admin-list-table-coupons.php:49
msgid "Create your first coupon"
msgstr ""
#: includes/admin/list-tables/class-wc-admin-list-table-coupons.php:83
msgid "Code"
msgstr ""

View File

@ -378,6 +378,26 @@ return array(
),
),
),
'MD' => array(
'currency_code' => 'MDL',
'currency_pos' => 'right_space',
'thousand_sep' => '.',
'decimal_sep' => ',',
'num_decimals' => 2,
'weight_unit' => 'kg',
'dimension_unit' => 'cm',
'tax_rates' => array(
'' => array(
array(
'country' => 'MD',
'state' => '',
'rate' => '20.0000',
'name' => 'TVA',
'shipping' => true,
),
),
),
),
'NL' => array(
'currency_code' => 'EUR',
'currency_pos' => 'left',

View File

@ -5,6 +5,8 @@
* For more details check:
* https://ro.wikipedia.org/wiki/Organizarea_administrativ-teritorial%C4%83_a_Republicii_Moldova
* https://ro.wikipedia.org/wiki/Raioanele_Republicii_Moldova
* https://en.wikipedia.org/wiki/ISO_3166-2:MD
* https://en.wikipedia.org/wiki/Romanian_alphabet#Unicode_and_HTML
*
* @author Alexander Minza
* @package WooCommerce/i18n
@ -18,39 +20,39 @@ if ( ! defined( 'ABSPATH' ) ) {
}
$states['MD'] = array(
'C' => __( 'Chişinău', 'woocommerce' ),
'BL' => __( 'Bălţi', 'woocommerce' ),
'C' => __( 'Chișinău', 'woocommerce' ),
'BL' => __( 'Bălți', 'woocommerce' ),
'AN' => __( 'Anenii Noi', 'woocommerce' ),
'BS' => __( 'Basarabeasca', 'woocommerce' ),
'BR' => __( 'Briceni', 'woocommerce' ),
'CH' => __( 'Cahul', 'woocommerce' ),
'CT' => __( 'Cantemir', 'woocommerce' ),
'CL' => __( 'Călăraşi', 'woocommerce' ),
'CS' => __( 'Căuşeni', 'woocommerce' ),
'CM' => __( 'Cimişlia', 'woocommerce' ),
'CL' => __( 'Călărași', 'woocommerce' ),
'CS' => __( 'Căușeni', 'woocommerce' ),
'CM' => __( 'Cimișlia', 'woocommerce' ),
'CR' => __( 'Criuleni', 'woocommerce' ),
'DN' => __( 'Donduşeni', 'woocommerce' ),
'DN' => __( 'Dondușeni', 'woocommerce' ),
'DR' => __( 'Drochia', 'woocommerce' ),
'DB' => __( 'Dubăsari', 'woocommerce' ),
'ED' => __( 'Edineţ', 'woocommerce' ),
'FL' => __( 'Făleşti', 'woocommerce' ),
'FR' => __( 'Floreşti', 'woocommerce' ),
'GE' => __( 'Găgăuzia', 'woocommerce' ),
'ED' => __( 'Edineț', 'woocommerce' ),
'FL' => __( 'Fălești', 'woocommerce' ),
'FR' => __( 'Florești', 'woocommerce' ),
'GE' => __( 'UTA Găgăuzia', 'woocommerce' ),
'GL' => __( 'Glodeni', 'woocommerce' ),
'HN' => __( 'Hînceşti', 'woocommerce' ),
'HN' => __( 'Hîncești', 'woocommerce' ),
'IL' => __( 'Ialoveni', 'woocommerce' ),
'LV' => __( 'Leova', 'woocommerce' ),
'NS' => __( 'Nisporeni', 'woocommerce' ),
'OC' => __( 'Ocniţa', 'woocommerce' ),
'OC' => __( 'Ocnița', 'woocommerce' ),
'OR' => __( 'Orhei', 'woocommerce' ),
'RZ' => __( 'Rezina', 'woocommerce' ),
'RS' => __( 'Rîşcani', 'woocommerce' ),
'RS' => __( 'Rîșcani', 'woocommerce' ),
'SG' => __( 'Sîngerei', 'woocommerce' ),
'SR' => __( 'Soroca', 'woocommerce' ),
'ST' => __( 'Străşeni', 'woocommerce' ),
'SD' => __( 'Şoldăneşti', 'woocommerce' ),
'SV' => __( 'Ştefan Vodă', 'woocommerce' ),
'ST' => __( 'Strășeni', 'woocommerce' ),
'SD' => __( 'Șoldănești', 'woocommerce' ),
'SV' => __( 'Ștefan Vodă', 'woocommerce' ),
'TR' => __( 'Taraclia', 'woocommerce' ),
'TL' => __( 'Teleneşti', 'woocommerce' ),
'TL' => __( 'Telenești', 'woocommerce' ),
'UN' => __( 'Ungheni', 'woocommerce' ),
);

View File

@ -1177,7 +1177,7 @@ class WC_Product extends WC_Abstract_Legacy_Product {
if ( is_a( $download, 'WC_Product_Download' ) ) {
$download_object = $download;
} else {
$download_object = new WC_Product_Download();
$download_object = new WC_Product_Download();
// If we don't have a previous hash, generate UUID for download.
if ( empty( $download['download_id'] ) ) {

View File

@ -2,8 +2,6 @@
/**
* Init WooCommerce data exporters.
*
* @author WooThemes
* @category Admin
* @package WooCommerce/Admin
* @version 3.1.0
*/
@ -95,6 +93,11 @@ class WC_Admin_Exporters {
if ( isset( $_GET['action'], $_GET['nonce'] ) && wp_verify_nonce( wp_unslash( $_GET['nonce'] ), 'product-csv' ) && 'download_product_csv' === wp_unslash( $_GET['action'] ) ) { // WPCS: input var ok, sanitization ok.
include_once( WC_ABSPATH . 'includes/export/class-wc-product-csv-exporter.php' );
$exporter = new WC_Product_CSV_Exporter();
if ( ! empty( $_GET['filename'] ) ) { // WPCS: input var ok.
$exporter->set_filename( wp_unslash( $_GET['filename'] ) ); // WPCS: input var ok, sanitization ok.
}
$exporter->export();
}
}
@ -130,10 +133,14 @@ class WC_Admin_Exporters {
$exporter->set_product_types_to_export( wp_unslash( $_POST['export_types'] ) ); // WPCS: input var ok, sanitization ok.
}
if ( ! empty( $_POST['filename'] ) ) { // WPCS: input var ok.
$exporter->set_filename( wp_unslash( $_POST['filename'] ) ); // WPCS: input var ok, sanitization ok.
}
$exporter->set_page( $step );
$exporter->generate_file();
$query_args = apply_filters( 'woocommerce_export_get_ajax_query_args', array( 'nonce' => wp_create_nonce( 'product-csv' ), 'action' => 'download_product_csv' ) );
$query_args = apply_filters( 'woocommerce_export_get_ajax_query_args', array( 'nonce' => wp_create_nonce( 'product-csv' ), 'action' => 'download_product_csv', 'filename' => $exporter->get_filename() ) );
if ( 100 === $exporter->get_percent_complete() ) {
wp_send_json_success( array(

View File

@ -1120,7 +1120,7 @@ class WC_Admin_Setup_Wizard {
'name' => __( 'Stripe', 'woocommerce' ),
'image' => WC()->plugin_url() . '/assets/images/stripe.png',
'description' => $stripe_description,
'class' => 'checked',
'class' => 'checked stripe-logo',
'repo-slug' => 'woocommerce-gateway-stripe',
'settings' => array(
'create_account' => array(
@ -1155,7 +1155,7 @@ class WC_Admin_Setup_Wizard {
'placeholder' => '',
'required' => false,
),
'api_subject' => array(
'email' => array(
'label' => __( 'Direct payments to email address:', 'woocommerce' ),
'type' => 'email',
'value' => $user_email,

View File

@ -47,6 +47,7 @@ class WC_Admin_List_Table_Coupons extends WC_Admin_List_Table {
echo '<div class="woocommerce-BlankState">';
echo '<h2 class="woocommerce-BlankState-message">' . esc_html__( 'Coupons are a great way to offer discounts and rewards to your customers. They will appear here once created.', 'woocommerce' ) . '</h2>';
echo '<a class="woocommerce-BlankState-cta button-primary button" target="_blank" href="https://docs.woocommerce.com/document/coupon-management/?utm_source=blankslate&utm_medium=product&utm_content=couponsdoc&utm_campaign=woocommerceplugin">' . esc_html__( 'Learn more about coupons', 'woocommerce' ) . '</a>';
echo '<a class="woocommerce-BlankState-cta button-primary button" href="' . esc_url ( admin_url( 'post-new.php?post_type=shop_coupon' ) ) . '">' . esc_html__( 'Create your first coupon', 'woocommerce' ) . '</a>';
echo '</div>';
}

View File

@ -30,6 +30,8 @@ class WC_Meta_Box_Product_Data {
$thepostid = $post->ID;
$product_object = $thepostid ? wc_get_product( $thepostid ) : new WC_Product;
wp_nonce_field( 'woocommerce_save_data', 'woocommerce_meta_nonce' );
include( 'views/html-product-data-panel.php' );
}

View File

@ -25,6 +25,7 @@ class WC_Meta_Box_Product_Images {
* @param WP_Post $post
*/
public static function output( $post ) {
wp_nonce_field( 'woocommerce_save_data', 'woocommerce_meta_nonce' );
?>
<div id="product_images_container">
<ul class="product_images">

View File

@ -44,7 +44,6 @@ if ( ! defined( 'ABSPATH' ) ) {
self::output_variations();
do_action( 'woocommerce_product_data_panels' );
wc_do_deprecated_action( 'woocommerce_product_write_panels', array(), '2.6', 'Use woocommerce_product_data_panels action instead.' );
wp_nonce_field( 'woocommerce_save_data', 'woocommerce_meta_nonce' );
?>
<div class="clear"></div>
</div>

View File

@ -1172,10 +1172,10 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
}
$download = new WC_Product_Download();
$download->set_id( $key );
$download->set_id( $file['id'] ? $file['id'] : wp_generate_uuid4() );
$download->set_name( $file['name'] ? $file['name'] : wc_get_filename_from_url( $file['file'] ) );
$download->set_file( apply_filters( 'woocommerce_file_download_path', $file['file'], $product, $key ) );
$files[] = $download;
$files[] = $download;
}
$product->set_downloads( $files );

View File

@ -1527,7 +1527,7 @@ class WC_API_Products extends WC_API_Resource {
}
$download = new WC_Product_Download();
$download->set_id( $key );
$download->set_id( $file['id'] ? $file['id'] : wp_generate_uuid4() );
$download->set_name( $file['name'] ? $file['name'] : wc_get_filename_from_url( $file['file'] ) );
$download->set_file( apply_filters( 'woocommerce_file_download_path', $file['file'], $product, $key ) );
$files[] = $download;

View File

@ -2031,7 +2031,7 @@ class WC_API_Products extends WC_API_Resource {
}
$download = new WC_Product_Download();
$download->set_id( $key );
$download->set_id( $file['id'] ? $file['id'] : wp_generate_uuid4() );
$download->set_name( $file['name'] ? $file['name'] : wc_get_filename_from_url( $file['file'] ) );
$download->set_file( apply_filters( 'woocommerce_file_download_path', $file['file'], $product, $key ) );
$files[] = $download;

View File

@ -960,7 +960,7 @@ class WC_REST_Products_V1_Controller extends WC_REST_Posts_Controller {
}
$download = new WC_Product_Download();
$download->set_id( $key );
$download->set_id( $file['id'] ? $file['id'] : wp_generate_uuid4() );
$download->set_name( $file['name'] ? $file['name'] : wc_get_filename_from_url( $file['file'] ) );
$download->set_file( apply_filters( 'woocommerce_file_download_path', $file['file'], $product, $key ) );
$files[] = $download;

View File

@ -4,7 +4,6 @@
*
* @version 3.3.0
* @package WooCommerce\l10n
* @author WooThemes
*/
if ( ! defined( 'ABSPATH' ) ) {
@ -492,7 +491,7 @@ class WC_Countries {
'IN' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} - {postcode}\n{state}, {country}",
'IS' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
'IT' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode}\n{city}\n{state_upper}\n{country}",
'JP' => "{postcode}\n{state}{city}{address_1}\n{address_2}\n{company}\n{last_name} {first_name}\n{country}",
'JP' => "{postcode}\n{state} {city} {address_1}\n{address_2}\n{company}\n{last_name} {first_name}\n{country}",
'TW' => "{company}\n{last_name} {first_name}\n{address_1}\n{address_2}\n{state}, {city} {postcode}\n{country}",
'LI' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
'NL' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
@ -1019,6 +1018,7 @@ class WC_Countries {
),
'RO' => array(
'state' => array(
'label' => __( 'County', 'woocommerce' ),
'required' => false,
),
),
@ -1073,7 +1073,7 @@ class WC_Countries {
),
'MD' => array(
'state' => array(
'label' => __( 'Municipality / Region', 'woocommerce' ),
'label' => __( 'Municipality / District', 'woocommerce' ),
),
),
'SE' => array(

View File

@ -817,7 +817,7 @@ class WC_Discounts {
$categories = array();
foreach ( $this->items as $item ) {
if ( $coupon->get_exclude_sale_items() && $item->product && $item->product->is_on_sale() ) {
if ( ! $item->product ) {
continue;
}

View File

@ -37,7 +37,7 @@ class WC_Download_Handler {
$product = wc_get_product( $product_id );
$data_store = WC_Data_Store::load( 'customer-download' );
if ( ! $product || ! isset( $_GET['key'], $_GET['order'] ) ) {
if ( ! $product || empty( $_GET['key'] ) || empty( $_GET['order'] ) ) {
self::download_error( __( 'Invalid download link.', 'woocommerce' ) );
}

View File

@ -92,7 +92,7 @@ class WC_Geolocation {
} elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { // WPCS: input var ok, CSRF ok.
// Proxy servers can send through this header like this: X-Forwarded-For: client1, proxy1, proxy2
// Make sure we always only send through the first IP in the list which should always be the client IP.
return (string) rest_is_ip_address( trim( current( explode( ',', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ) ) ) ); // WPCS: input var ok, CSRF ok.
return (string) rest_is_ip_address( trim( current( preg_split( '/[,:]/', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ) ) ) ); // WPCS: input var ok, CSRF ok.
} elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) { // @codingStandardsIgnoreLine
return sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ); // @codingStandardsIgnoreLine
}

View File

@ -731,7 +731,9 @@ class WC_Query {
}
/**
* Layered Nav Init.
* Get an array of attributes and terms selected with the layered nav widget.
*
* @return array
*/
public static function get_layered_nav_chosen_attributes() {
if ( ! is_array( self::$_chosen_attributes ) ) {

View File

@ -378,10 +378,6 @@ final class WooCommerce {
$this->frontend_includes();
}
if ( $this->is_request( 'frontend' ) || $this->is_request( 'cron' ) ) {
include_once( WC_ABSPATH . 'includes/class-wc-session-handler.php' );
}
if ( $this->is_request( 'cron' ) && 'yes' === get_option( 'woocommerce_allow_tracking', 'no' ) ) {
include_once( WC_ABSPATH . 'includes/class-wc-tracker.php' );
}
@ -444,6 +440,7 @@ final class WooCommerce {
include_once( WC_ABSPATH . 'includes/class-wc-shortcodes.php' ); // Shortcodes class.
include_once( WC_ABSPATH . 'includes/class-wc-embed.php' ); // Embeds.
include_once( WC_ABSPATH . 'includes/class-wc-structured-data.php' ); // Structured Data class.
include_once( WC_ABSPATH . 'includes/class-wc-session-handler.php' ); // Session handler class.
}
/**

View File

@ -282,7 +282,7 @@ class WC_Shop_Customizer {
$wp_customize->add_setting(
'woocommerce_default_catalog_orderby',
array(
'default' => '',
'default' => 'menu_order',
'type' => 'option',
'capability' => 'manage_woocommerce',
'sanitize_callback' => array( $this, 'sanitize_default_catalog_orderby' ),

View File

@ -4,8 +4,6 @@
*
* Based on https://pippinsplugins.com/batch-processing-for-big-data/
*
* @author Automattic
* @category Admin
* @package WooCommerce/Export
* @version 3.1.0
*/
@ -25,13 +23,6 @@ if ( ! class_exists( 'WC_CSV_Exporter', false ) ) {
*/
abstract class WC_CSV_Batch_Exporter extends WC_CSV_Exporter {
/**
* The file being exported to.
*
* @var string
*/
protected $file;
/**
* Page being exported
*
@ -43,11 +34,19 @@ abstract class WC_CSV_Batch_Exporter extends WC_CSV_Exporter {
* Constructor.
*/
public function __construct() {
$upload_dir = wp_upload_dir();
$this->file = trailingslashit( $upload_dir['basedir'] ) . $this->get_filename();
$this->column_names = $this->get_default_column_names();
}
/**
* Get file path to export to.
*
* @return string
*/
protected function get_file_path() {
$upload_dir = wp_upload_dir();
return trailingslashit( $upload_dir['basedir'] ) . $this->get_filename();
}
/**
* Get the file contents.
*
@ -56,11 +55,11 @@ abstract class WC_CSV_Batch_Exporter extends WC_CSV_Exporter {
*/
public function get_file() {
$file = '';
if ( @file_exists( $this->file ) ) {
$file = @file_get_contents( $this->file );
if ( @file_exists( $this->get_file_path() ) ) {
$file = @file_get_contents( $this->get_file_path() );
} else {
@file_put_contents( $this->file, '' );
@chmod( $this->file, 0664 );
@file_put_contents( $this->get_file_path(), '' );
@chmod( $this->get_file_path(), 0664 );
}
return $file;
}
@ -73,7 +72,7 @@ abstract class WC_CSV_Batch_Exporter extends WC_CSV_Exporter {
public function export() {
$this->send_headers();
$this->send_content( $this->get_file() );
@unlink( $this->file );
@unlink( $this->get_file_path() );
die();
}
@ -84,7 +83,7 @@ abstract class WC_CSV_Batch_Exporter extends WC_CSV_Exporter {
*/
public function generate_file() {
if ( 1 === $this->get_page() ) {
@unlink( $this->file );
@unlink( $this->get_file_path() );
}
$this->prepare_data_to_export();
$this->write_csv_data( $this->get_csv_data() );
@ -105,7 +104,7 @@ abstract class WC_CSV_Batch_Exporter extends WC_CSV_Exporter {
}
$file .= $data;
@file_put_contents( $this->file, $file );
@file_put_contents( $this->get_file_path(), $file );
}
/**

View File

@ -2,8 +2,6 @@
/**
* Handles CSV export.
*
* @author Automattic
* @category Admin
* @package WooCommerce/Export
* @version 3.1.0
*/
@ -24,6 +22,13 @@ abstract class WC_CSV_Exporter {
*/
protected $export_type = '';
/**
* Filename to export to.
*
* @var string
*/
protected $filename = 'wc-export.csv';
/**
* Batch limit.
*
@ -183,15 +188,23 @@ abstract class WC_CSV_Exporter {
header( 'Expires: 0' );
}
/**
* Set filename to export to.
*
* @param string $filename Filename to export to.
* @return string
*/
public function set_filename( $filename ) {
$this->filename = sanitize_file_name( str_replace( '.csv', '', $filename ) . '.csv' );
}
/**
* Generate and return a filename.
*
* @return string
*/
public function get_filename() {
$filename = 'wc-' . $this->export_type . '-export-' . date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) . '.csv';
return sanitize_file_name( apply_filters( "woocommerce_{$this->export_type}_export_get_filename", $filename ) );
return sanitize_file_name( apply_filters( "woocommerce_{$this->export_type}_export_get_filename", $this->filename ) );
}
/**
@ -365,9 +378,14 @@ abstract class WC_CSV_Exporter {
$data = $data ? 1 : 0;
}
$use_mb = function_exists( 'mb_convert_encoding' );
$data = (string) urldecode( $data );
$encoding = mb_detect_encoding( $data, 'UTF-8, ISO-8859-1', true );
$data = 'UTF-8' === $encoding ? $data : utf8_encode( $data );
if ( $use_mb ) {
$encoding = mb_detect_encoding( $data, 'UTF-8, ISO-8859-1', true );
$data = 'UTF-8' === $encoding ? $data : utf8_encode( $data );
}
return $this->escape_data( $data );
}

View File

@ -2,8 +2,6 @@
/**
* Handles product CSV export.
*
* @author Automattic
* @category Admin
* @package WooCommerce/Export
* @version 3.1.0
*/

View File

@ -391,6 +391,7 @@ function wc_scheduled_sales() {
// Sales which are due to start.
$product_ids = $data_store->get_starting_sales();
if ( $product_ids ) {
do_action( 'wc_before_products_starting_sales', $product_ids );
foreach ( $product_ids as $product_id ) {
if ( $product = wc_get_product( $product_id ) ) {
$sale_price = $product->get_sale_price();
@ -406,6 +407,7 @@ function wc_scheduled_sales() {
$product->save();
}
}
do_action( 'wc_after_products_starting_sales', $product_ids );
delete_transient( 'wc_products_onsale' );
}
@ -413,6 +415,7 @@ function wc_scheduled_sales() {
// Sales which are due to end.
$product_ids = $data_store->get_ending_sales();
if ( $product_ids ) {
do_action( 'wc_before_products_ending_sales', $product_ids );
foreach ( $product_ids as $product_id ) {
if ( $product = wc_get_product( $product_id ) ) {
$regular_price = $product->get_regular_price();
@ -423,6 +426,7 @@ function wc_scheduled_sales() {
$product->save();
}
}
do_action( 'wc_after_products_ending_sales', $product_ids );
WC_Cache_Helper::get_transient_version( 'product', true );
delete_transient( 'wc_products_onsale' );
@ -829,16 +833,24 @@ function wc_get_product_backorder_options() {
* @return array
*/
function wc_get_related_products( $product_id, $limit = 5, $exclude_ids = array() ) {
$product_id = absint( $product_id );
$limit = $limit > 0 ? $limit : 5;
$exclude_ids = array_merge( array( 0, $product_id ), $exclude_ids );
$transient_name = 'wc_related_' . $product_id;
$related_posts = get_transient( $transient_name );
$limit = $limit > 0 ? $limit : 5;
$query_args = http_build_query( array(
'limit' => $limit,
'exclude_ids' => $exclude_ids,
) );
$transient = get_transient( $transient_name );
$related_posts = $transient && isset( $transient[ $query_args ] ) ? $transient[ $query_args ] : false;
// We want to query related posts if they are not cached, or we don't have enough.
if ( false === $related_posts || count( $related_posts ) < $limit ) {
$cats_array = apply_filters( 'woocommerce_product_related_posts_relate_by_category', true, $product_id ) ? apply_filters( 'woocommerce_get_related_product_cat_terms', wc_get_product_term_ids( $product_id, 'product_cat' ), $product_id ) : array();
$tags_array = apply_filters( 'woocommerce_product_related_posts_relate_by_tag', true, $product_id ) ? apply_filters( 'woocommerce_get_related_product_tag_terms', wc_get_product_term_ids( $product_id, 'product_tag' ), $product_id ) : array();
$tags_array = apply_filters( 'woocommerce_product_related_posts_relate_by_tag', true, $product_id ) ? apply_filters( 'woocommerce_get_related_product_tag_terms', wc_get_product_term_ids( $product_id, 'product_tag' ), $product_id ) : array();
// Don't bother if none are set, unless woocommerce_product_related_posts_force_display is set to true in which case all products are related.
if ( empty( $cats_array ) && empty( $tags_array ) && ! apply_filters( 'woocommerce_product_related_posts_force_display', false, $product_id ) ) {
@ -848,9 +860,20 @@ function wc_get_related_products( $product_id, $limit = 5, $exclude_ids = array(
$related_posts = $data_store->get_related_products( $cats_array, $tags_array, $exclude_ids, $limit + 10, $product_id );
}
set_transient( $transient_name, $related_posts, DAY_IN_SECONDS );
if ( $transient ) {
$transient[ $query_args ] = $related_posts;
} else {
$transient = array( $query_args => $related_posts );
}
set_transient( $transient_name, $transient, DAY_IN_SECONDS );
}
$related_posts = apply_filters( 'woocommerce_related_products', $related_posts, $product_id, array(
'limit' => $limit,
'excluded_ids' => $exclude_ids,
) );
shuffle( $related_posts );
return array_slice( $related_posts, 0, $limit );

View File

@ -371,7 +371,7 @@ function wc_reset_product_grid_settings() {
delete_option( 'woocommerce_catalog_rows' );
}
if ( isset( $theme_support['product_grid']['default_rows'] ) ) {
if ( isset( $theme_support['product_grid']['default_columns'] ) ) {
update_option( 'woocommerce_catalog_columns', absint( $theme_support['product_grid']['default_columns'] ) );
} else {
delete_option( 'woocommerce_catalog_columns' );
@ -778,7 +778,11 @@ if ( ! function_exists( 'woocommerce_template_loop_product_link_open' ) ) {
* Insert the opening anchor tag for products in the loop.
*/
function woocommerce_template_loop_product_link_open() {
echo '<a href="' . esc_url( get_the_permalink() ) . '" class="woocommerce-LoopProduct-link woocommerce-loop-product__link">';
global $product;
$link = apply_filters( 'woocommerce_loop_product_link', get_the_permalink(), $product );
echo '<a href="' . esc_url( $link ) . '" class="woocommerce-LoopProduct-link woocommerce-loop-product__link">';
}
}

View File

@ -47,7 +47,7 @@
"istanbul": "^1.0.0-alpha",
"mocha": "^3.0.2",
"stylelint": "~8.2.0",
"wc-e2e-page-objects": "0.5.0"
"wc-e2e-page-objects": "0.6.0"
},
"engines": {
"node": ">=8.9.3",

View File

@ -4,6 +4,10 @@
"admin": {
"username": "admin",
"password": "password"
},
"customer": {
"username": "Customer",
"password": "password"
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,122 @@
/**
* External dependencies
*/
import config from 'config';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import test from 'selenium-webdriver/testing';
import { WebDriverManager, WebDriverHelper as helper } from 'wp-e2e-webdriver';
import { CustomerFlow, MyAccountPage, PageMap } from 'wc-e2e-page-objects';
chai.use( chaiAsPromised );
const assert = chai.assert;
let manager;
let driver;
test.describe( 'My account page', function() {
const loginAsCustomer = () => {
return new CustomerFlow( driver, {
baseUrl: config.get( 'url' ),
username: config.get( 'users.customer.username' ),
password: config.get( 'users.customer.password' )
} );
};
const getMyAccountSubPageUrl = path => {
return PageMap.getPageUrl( config.get( 'url' ), {
path: '/my-account/%s'
}, path );
};
const untrailingslashit = url => {
return url.endsWith( '/' ) ? url.substring( 0, url.length - 1 ) : url;
};
// open browser
test.before( function() {
this.timeout( config.get( 'startBrowserTimeoutMs' ) );
manager = new WebDriverManager( 'chrome', { baseUrl: config.get( 'url' ) } );
driver = manager.getDriver();
helper.clearCookiesAndDeleteLocalStorage( driver );
} );
this.timeout( config.get( 'mochaTimeoutMs' ) );
test.it( 'allows customer to login', () => {
loginAsCustomer();
const myAccount = new MyAccountPage( driver, {
baseUrl: config.get( 'url' ),
visit: false
} );
assert.eventually.ok( myAccount.hasText( 'Hello Customer' ), 'see "Hello Customer" text' );
assert.eventually.ok( myAccount.hasMenu( 'Dashboard' ), 'see Dashboard menu.' );
assert.eventually.ok( myAccount.hasMenu( 'Orders' ), 'see Orders menu' );
} );
test.it( 'allows customer to see orders', () => {
loginAsCustomer();
const myAccount = new MyAccountPage( driver, {
baseUrl: config.get( 'url' ),
visit: false
} );
myAccount.clickMenu( 'Orders' );
assert.eventually.equal(
driver.getCurrentUrl().then( untrailingslashit ),
untrailingslashit( getMyAccountSubPageUrl( 'orders' ) )
);
assert.eventually.ok( myAccount.hasText( 'Orders' ), 'see "Orders" text' );
} );
test.it( 'allows customer to see downloads', () => {
loginAsCustomer();
const myAccount = new MyAccountPage( driver, {
baseUrl: config.get( 'url' ),
visit: false
} );
myAccount.clickMenu( 'Downloads' );
assert.eventually.equal(
driver.getCurrentUrl().then( untrailingslashit ),
untrailingslashit( getMyAccountSubPageUrl( 'downloads' ) )
);
assert.eventually.ok( myAccount.hasText( 'Downloads' ), 'see "Downloads" text' );
} );
test.it( 'allows customer to edit addresses', () => {
loginAsCustomer();
const myAccount = new MyAccountPage( driver, {
baseUrl: config.get( 'url' ),
visit: false
} );
myAccount.clickMenu( 'Addresses' );
assert.eventually.equal(
driver.getCurrentUrl().then( untrailingslashit ),
untrailingslashit( getMyAccountSubPageUrl( 'edit-address' ) )
);
assert.eventually.ok( myAccount.hasText( 'Addresses' ), 'see "Addresses" text' );
} );
test.it( 'allows customer to edit account details', () => {
loginAsCustomer();
const myAccount = new MyAccountPage( driver, {
baseUrl: config.get( 'url' ),
visit: false
} );
myAccount.clickMenu( 'Account details' );
assert.eventually.equal(
driver.getCurrentUrl().then( untrailingslashit ),
untrailingslashit( getMyAccountSubPageUrl( 'edit-account' ) )
);
assert.eventually.ok( myAccount.hasText( 'Account details' ), 'see "Account details" text' );
} );
// quit browser
test.after( () => {
manager.quitBrowser();
} );
} );

View File

@ -16,6 +16,13 @@ let manager;
let driver;
test.describe( 'Single Product Page', function() {
const visitProductByPath = path => {
return new SingleProductPage( driver, { url: manager.getPageUrl( path ) } );
};
const visitCart = () => {
return new CartPage( driver, { url: manager.getPageUrl( '/cart' ) } );
};
// open browser
test.before( function() {
this.timeout( config.get( 'startBrowserTimeoutMs' ) );
@ -29,28 +36,29 @@ test.describe( 'Single Product Page', function() {
this.timeout( config.get( 'mochaTimeoutMs' ) );
test.it( 'should be able to add simple products to the cart', () => {
const productPage = new SingleProductPage( driver, { url: manager.getPageUrl( '/product/t-shirt' ) } );
const productPage = visitProductByPath( '/product/t-shirt' );
productPage.setQuantity( 5 );
productPage.addToCart();
const cartPage = new CartPage( driver, { url: manager.getPageUrl( '/cart' ) } );
assert.eventually.equal( cartPage.hasItem( 'T-Shirt', { qty: 5 } ), true );
assert.eventually.equal( visitCart().hasItem( 'T-Shirt', { qty: 5 } ), true );
} );
test.it( 'should be able to add variation products to the cart', () => {
const variableProductPage = new SingleProductPage( driver, { url: manager.getPageUrl( '/product/hoodie' ) } );
let variableProductPage;
variableProductPage = visitProductByPath( '/product/hoodie' );
variableProductPage.selectVariation( 'Color', 'Blue' );
variableProductPage.addToCart();
// Pause for a half-second. Driver goes too fast and makes wrong selections otherwise.
variableProductPage.selectVariation( 'Logo', 'Yes' );
driver.sleep( 500 );
variableProductPage.selectVariation( 'Color', 'Green' );
variableProductPage.addToCart();
assert.eventually.ok( visitCart().hasItem( 'Hoodie - Blue, Yes' ), '"Hoodie - Blue, Yes" in the cart' );
const cartPage = new CartPage( driver, { url: manager.getPageUrl( '/cart' ) } );
assert.eventually.equal( cartPage.hasItem( 'Hoodie - Blue' ), true );
assert.eventually.equal( cartPage.hasItem( 'Hoodie - Green' ), true );
variableProductPage = visitProductByPath( '/product/hoodie' );
variableProductPage.selectVariation( 'Color', 'Green' );
variableProductPage.selectVariation( 'Logo', 'No' );
driver.sleep( 500 );
variableProductPage.addToCart();
assert.eventually.ok( visitCart().hasItem( 'Hoodie - Green, No' ), '"Hoodie - Green, No" in the cart' );
} );
// quit browser

View File

@ -330,6 +330,92 @@ class WC_Tests_API_Orders extends WC_REST_Unit_Test_Case {
WC_Helper_Order::delete_order( $order->get_id() );
}
/**
* Tests updating an order and adding a coupon.
*
* @since 3.3.0
*/
public function test_update_order_add_coupons() {
wp_set_current_user( $this->user );
$order = WC_Helper_Order::create_order();
$order_item = current( $order->get_items() );
$coupon = WC_Helper_Coupon::create_coupon( 'fake-coupon' );
$coupon->set_amount( 5 );
$coupon->save();
$request = new WP_REST_Request( 'PUT', '/wc/v2/orders/' . $order->get_id() );
$request->set_body_params( array(
'coupon_lines' => array(
array(
'code' => 'fake-coupon',
'discount_total' => '5',
'discount_tax' => '0',
),
),
'line_items' => array(
array(
'id' => $order_item->get_id(),
'product_id' => $order_item->get_product_id(),
'total' => '35.00',
),
),
) );
$response = $this->server->dispatch( $request );
$data = $response->get_data();
$this->assertEquals( 200, $response->get_status() );
$this->assertCount( 1, $data['coupon_lines'] );
$this->assertEquals( '45.00', $data['total'] );
WC_Helper_Order::delete_order( $order->get_id() );
}
/**
* Tests updating an order and removing a coupon.
*
* @since 3.3.0
*/
public function test_update_order_remove_coupons() {
wp_set_current_user( $this->user );
$order = WC_Helper_Order::create_order();
$order_item = current( $order->get_items() );
$coupon = WC_Helper_Coupon::create_coupon( 'fake-coupon' );
$coupon->set_amount( 5 );
$coupon->save();
$order->apply_coupon( $coupon );
$order->save();
// Check that the coupon is applied.
$this->assertEquals( '45.00', $order->get_total() );
$request = new WP_REST_Request( 'PUT', '/wc/v2/orders/' . $order->get_id() );
$coupon_data = current( $order->get_items( 'coupon' ) );
$request->set_body_params( array(
'coupon_lines' => array(
array(
'id' => $coupon_data->get_id(),
'code' => null,
),
),
'line_items' => array(
array(
'id' => $order_item->get_id(),
'product_id' => $order_item->get_product_id(),
'total' => '40.00',
),
),
) );
$response = $this->server->dispatch( $request );
$data = $response->get_data();
$this->assertEquals( 200, $response->get_status() );
$this->assertTrue( empty( $data['coupon_lines'] ) );
$this->assertEquals( '50.00', $data['total'] );
WC_Helper_Order::delete_order( $order->get_id() );
}
/**
* Tests updating an order without the correct permissions.
*