WooCommerce Multilingual – run WooCommerce with WPML - Version 4.6.5

Version Description

  • The Events Calendar: convert currency for event_cost.
  • Wrong product price set after purchase in combination with custom prices for secondary currency and enabled stock
  • Translated attributes not saving if original one contains umlauts and original product language is German or Danish
  • Price not auto-calculated if you selected using custom prices and don't set a price
  • After quick edit variable product variations incremented their IDs
  • Added new wcml_translate_shipping_method_in_package filter
  • Products not filtered by current language while search Upsells/Cross-sells on product edit screen
  • Use default language if admin user not exists while sending "New Order" email to admins
  • Variation description not saved on installs with 300+ variations for product
Download this release

Release Info

Developer sergey.r
Plugin Icon 128x128 WooCommerce Multilingual – run WooCommerce with WPML
Version 4.6.5
Comparing to
See all releases

Code changes from version 4.6.3 to 4.6.5

changelog/4.6.5.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Features
2
+ * [wcml-2780] The Events Calendar: convert currency for event_cost.
3
+
4
+ # Fixes
5
+ * [wcml-2817] Wrong product price set after purchase in combination with custom prices for secondary currency and enabled stock
6
+ * [wcml-2815] Translated attributes not saving if original one contains umlauts and original product language is German or Danish
7
+ * [wcml-2813] Price not auto-calculated if you selected using custom prices and don't set a price
8
+ * [wcml-2812] After quick edit variable product variations incremented their IDs
9
+ * [wcml-2811] Added new wcml_translate_shipping_method_in_package filter
10
+ * [wcml-2810] Products not filtered by current language while search Upsells/Cross-sells on product edit screen
11
+ * [wcml-2798] added comp. class to cover price update when products are edited with WOOBE plugin
12
+ * [wcml-2797] Use default language if admin user not exists while sending "New Order" email to admins
13
+ * [wcml-2791] Variation description not saved on installs with 300+ variations for product
14
+ * [wcml-2784] "Hide completed" switcher from Translation Editor
inc/class-wcml-attributes.php CHANGED
@@ -235,7 +235,7 @@ class WCML_Attributes{
235
  $trnsl_labels = $this->get_attr_label_translations( $tr_product_id );
236
 
237
  foreach ( $orig_product_attrs as $key => $orig_product_attr ) {
238
- $sanitized_key = sanitize_title( $orig_product_attr[ 'name' ] );
239
  if( $sanitized_key != $key ) {
240
  $orig_product_attrs_buff = $orig_product_attrs[ $key ];
241
  unset( $orig_product_attrs[ $key ] );
@@ -537,7 +537,7 @@ class WCML_Attributes{
537
  */
538
  function filter_attribute_name( $attribute_name, $product_id, $return_sanitized = false ) {
539
 
540
- if ( !is_admin() && $product_id ) {
541
  $orig_lang = $this->woocommerce_wpml->products->get_original_product_language( $product_id );
542
  $current_language = $this->sitepress->get_current_language();
543
 
235
  $trnsl_labels = $this->get_attr_label_translations( $tr_product_id );
236
 
237
  foreach ( $orig_product_attrs as $key => $orig_product_attr ) {
238
+ $sanitized_key = $this->filter_attribute_name( $orig_product_attr[ 'name' ], $original_product_id, true );
239
  if( $sanitized_key != $key ) {
240
  $orig_product_attrs_buff = $orig_product_attrs[ $key ];
241
  unset( $orig_product_attrs[ $key ] );
537
  */
538
  function filter_attribute_name( $attribute_name, $product_id, $return_sanitized = false ) {
539
 
540
+ if ( $product_id ) {
541
  $orig_lang = $this->woocommerce_wpml->products->get_original_product_language( $product_id );
542
  $current_language = $this->sitepress->get_current_language();
543
 
inc/class-wcml-emails.php CHANGED
@@ -308,7 +308,7 @@ class WCML_Emails{
308
  if($user){
309
  $user_lang = $this->sitepress->get_user_admin_language($user->ID, true );
310
  }else{
311
- $user_lang = get_post_meta($order_id, 'wpml_language', true );
312
  }
313
 
314
  $this->change_email_language( $user_lang );
308
  if($user){
309
  $user_lang = $this->sitepress->get_user_admin_language($user->ID, true );
310
  }else{
311
+ $user_lang = $this->sitepress->get_default_language();
312
  }
313
 
314
  $this->change_email_language( $user_lang );
inc/class-wcml-orders.php CHANGED
@@ -2,6 +2,9 @@
2
 
3
  class WCML_Orders {
4
 
 
 
 
5
  private $woocommerce_wpml;
6
  private $sitepress;
7
 
@@ -305,7 +308,7 @@ class WCML_Orders {
305
  function order_language_dropdown( $order_id ){
306
  if( !get_post_meta( $order_id, '_order_currency') ) {
307
  $languages = apply_filters( 'wpml_active_languages', array(), array( 'skip_missing' => 0, 'orderby' => 'code' ) );
308
- $selected_lang = isset( $_COOKIE [ '_wcml_dashboard_order_language' ] ) ? $_COOKIE [ '_wcml_dashboard_order_language' ] : $this->sitepress->get_default_language();
309
  ?>
310
  <li class="wide">
311
  <label><?php _e('Order language:'); ?></label>
@@ -351,6 +354,8 @@ class WCML_Orders {
351
  });
352
 
353
  ");
 
 
354
  }
355
  }
356
 
@@ -361,11 +366,11 @@ class WCML_Orders {
361
  die();
362
  }
363
 
364
- $cookie_name = '_wcml_dashboard_order_language';
365
- // @todo uncomment or delete when #wpmlcore-5796 is resolved
366
- //do_action( 'wpsc_add_cookie', $cookie_name );
367
- setcookie( $cookie_name, filter_input( INPUT_POST, 'lang', FILTER_SANITIZE_FULL_SPECIAL_CHARS ), time() + 86400, COOKIEPATH, COOKIE_DOMAIN );
368
 
 
 
369
  }
370
 
371
  function set_order_language_backend( $post_id, $post ){
2
 
3
  class WCML_Orders {
4
 
5
+ const DASHBOARD_COOKIE_NAME = '_wcml_dashboard_order_language';
6
+ const COOKIE_TTL = 86400;
7
+
8
  private $woocommerce_wpml;
9
  private $sitepress;
10
 
308
  function order_language_dropdown( $order_id ){
309
  if( !get_post_meta( $order_id, '_order_currency') ) {
310
  $languages = apply_filters( 'wpml_active_languages', array(), array( 'skip_missing' => 0, 'orderby' => 'code' ) );
311
+ $selected_lang = isset( $_COOKIE [ self::DASHBOARD_COOKIE_NAME ] ) ? $_COOKIE [ self::DASHBOARD_COOKIE_NAME ] : $this->sitepress->get_default_language();
312
  ?>
313
  <li class="wide">
314
  <label><?php _e('Order language:'); ?></label>
354
  });
355
 
356
  ");
357
+ }else{
358
+ $this->remove_dashboard_order_language_cookie();
359
  }
360
  }
361
 
366
  die();
367
  }
368
 
369
+ setcookie( self::DASHBOARD_COOKIE_NAME, filter_input( INPUT_POST, 'lang', FILTER_SANITIZE_FULL_SPECIAL_CHARS ), time() + self::COOKIE_TTL, COOKIEPATH, COOKIE_DOMAIN );
370
+ }
 
 
371
 
372
+ private function remove_dashboard_order_language_cookie(){
373
+ setcookie( self::DASHBOARD_COOKIE_NAME, '', time() - self::COOKIE_TTL, COOKIEPATH, COOKIE_DOMAIN );
374
  }
375
 
376
  function set_order_language_backend( $post_id, $post ){
inc/class-wcml-products.php CHANGED
@@ -37,17 +37,13 @@ class WCML_Products{
37
 
38
  if ( is_admin() ) {
39
 
40
- add_filter( 'woocommerce_json_search_found_products', array(
41
- $this,
42
- 'woocommerce_json_search_found_products'
43
- ) );
44
-
45
  add_filter( 'post_row_actions', array( $this, 'filter_product_actions' ), 10, 2 );
46
 
47
  add_action( 'wp_ajax_wpml_switch_post_language', array( $this, 'switch_product_variations_language' ), 9 );
48
  add_filter( 'woocommerce_product_type_query', array( $this, 'override_product_type_query' ), 10, 2 );
49
  } else {
50
- add_filter( 'woocommerce_json_search_found_products', array( $this, 'filter_found_products_by_language' ) );
51
  add_filter( 'woocommerce_related_products_args', array( $this, 'filter_related_products_args' ) );
52
  add_filter( 'woocommerce_product_related_posts_query', array( $this, 'filter_related_products_query' ) );
53
  add_filter( 'woocommerce_shortcode_products_query', array(
@@ -76,6 +72,7 @@ class WCML_Products{
76
 
77
  if ( $this->sitepress->get_wp_api()->version_compare( $this->sitepress->get_wp_api()->constant( 'WC_VERSION' ), '3.6.0', '>=' ) ) {
78
  add_filter( 'get_post_metadata', array( $this, 'filter_product_data' ), 10, 3 );
 
79
  }
80
  }
81
 
@@ -320,30 +317,6 @@ class WCML_Products{
320
  return $actions;
321
  }
322
 
323
- /**
324
- * Filters upsell/crosell products in the correct language.
325
- */
326
- public function filter_found_products_by_language( $found_products ){
327
- $current_page_language = $this->sitepress->get_current_language();
328
-
329
- foreach( $found_products as $product_id => $output_v ){
330
- $post_data = $this->wpdb->get_row(
331
- $this->wpdb->prepare(
332
- "SELECT * FROM {$this->wpdb->prefix}icl_translations
333
- WHERE element_id = %d AND element_type LIKE 'post_%'", $product_id
334
- )
335
- );
336
-
337
- $product_language = $post_data->language_code;
338
-
339
- if( $product_language !== $current_page_language ){
340
- unset( $found_products[ $product_id ] );
341
- }
342
- }
343
-
344
- return $found_products;
345
- }
346
-
347
  /**
348
  * Takes off translated products from the Up-sells/Cross-sells tab.
349
  */
@@ -365,26 +338,54 @@ class WCML_Products{
365
  return $posts;
366
  }
367
 
368
- public function woocommerce_json_search_found_products( $found_products ){
369
 
370
- foreach( $found_products as $post => $formatted_product_name ) {
371
- $parent = wp_get_post_parent_id( $post );
372
- if( ( isset( $_COOKIE [ '_wcml_dashboard_order_language' ] )
373
- && ( ( !$parent && $this->post_translations->get_element_lang_code( $post ) == $_COOKIE [ '_wcml_dashboard_order_language' ] )
374
- || ( $parent && $this->post_translations->get_element_lang_code( $parent ) == $_COOKIE [ '_wcml_dashboard_order_language' ] ) )
375
- )
376
- ||
377
- ( ! isset( $_COOKIE [ '_wcml_dashboard_order_language' ] )
378
- && ( ( !$parent && $this->is_original_product($post) )
379
- || ( $parent && $this->is_original_product($parent) ) )
380
- )
381
- ) {
382
 
383
- $new_found_products[$post] = $formatted_product_name;
384
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
  }
386
 
387
- return isset ( $new_found_products ) ? $new_found_products : $found_products ;
388
  }
389
 
390
  //update menu_order fro translations after ordering original products
@@ -767,4 +768,16 @@ class WCML_Products{
767
 
768
  return $product_type;
769
  }
 
 
 
 
 
 
 
 
 
 
 
 
770
  }
37
 
38
  if ( is_admin() ) {
39
 
40
+ add_filter( 'woocommerce_json_search_found_products', array( $this, 'filter_wc_searched_products_on_admin' ) );
 
 
 
 
41
  add_filter( 'post_row_actions', array( $this, 'filter_product_actions' ), 10, 2 );
42
 
43
  add_action( 'wp_ajax_wpml_switch_post_language', array( $this, 'switch_product_variations_language' ), 9 );
44
  add_filter( 'woocommerce_product_type_query', array( $this, 'override_product_type_query' ), 10, 2 );
45
  } else {
46
+ add_filter( 'woocommerce_json_search_found_products', array( $this, 'filter_wc_searched_products_on_front' ) );
47
  add_filter( 'woocommerce_related_products_args', array( $this, 'filter_related_products_args' ) );
48
  add_filter( 'woocommerce_product_related_posts_query', array( $this, 'filter_related_products_query' ) );
49
  add_filter( 'woocommerce_shortcode_products_query', array(
72
 
73
  if ( $this->sitepress->get_wp_api()->version_compare( $this->sitepress->get_wp_api()->constant( 'WC_VERSION' ), '3.6.0', '>=' ) ) {
74
  add_filter( 'get_post_metadata', array( $this, 'filter_product_data' ), 10, 3 );
75
+ add_filter( 'woocommerce_can_reduce_order_stock', array( $this, 'remove_post_meta_data_filter_on_checkout_stock_update' ) );
76
  }
77
  }
78
 
317
  return $actions;
318
  }
319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  /**
321
  * Takes off translated products from the Up-sells/Cross-sells tab.
322
  */
338
  return $posts;
339
  }
340
 
 
341
 
342
+ /**
343
+ * Filters products by language
344
+ *
345
+ * @param array $found_products
346
+ * @param bool $language
347
+ *
348
+ * @return array
349
+ */
350
+ private function filter_found_products_by_language( $found_products, $language = false ){
 
 
 
351
 
352
+ if( !$language ){
353
+ $language = $this->sitepress->get_current_language();
354
+ }
355
+
356
+ foreach( $found_products as $product_id => $product_name ){
357
+
358
+ if( $this->post_translations->get_element_lang_code( $product_id ) !== $language ){
359
+ unset( $found_products[ $product_id ] );
360
+ }
361
+ }
362
+
363
+ return $found_products;
364
+ }
365
+
366
+ /**
367
+ * @param array $found_products
368
+ *
369
+ * @return array
370
+ */
371
+ public function filter_wc_searched_products_on_front( $found_products ){
372
+ return $this->filter_found_products_by_language( $found_products );
373
+ }
374
+
375
+ /**
376
+ * @param array $found_products
377
+ *
378
+ * @return array
379
+ */
380
+ public function filter_wc_searched_products_on_admin( $found_products ){
381
+
382
+ if ( isset( $_COOKIE['_wcml_dashboard_order_language'] ) ) {
383
+ $found_products = $this->filter_found_products_by_language( $found_products, $_COOKIE['_wcml_dashboard_order_language'] );
384
+ }else{
385
+ $found_products = $this->filter_found_products_by_language( $found_products );
386
  }
387
 
388
+ return $found_products;
389
  }
390
 
391
  //update menu_order fro translations after ordering original products
768
 
769
  return $product_type;
770
  }
771
+
772
+ /**
773
+ * @param bool $reduce_stock
774
+ *
775
+ * @return bool
776
+ */
777
+ public function remove_post_meta_data_filter_on_checkout_stock_update( $reduce_stock ){
778
+ if( isset( $_GET['wc-ajax'] ) && 'checkout' === $_GET['wc-ajax'] ){
779
+ remove_filter( 'get_post_metadata', array( $this, 'filter_product_data' ), 10, 3 );
780
+ }
781
+ return $reduce_stock;
782
+ }
783
  }
inc/class-wcml-wc-shipping.php CHANGED
@@ -98,7 +98,9 @@ class WCML_WC_Shipping{
98
  function translate_shipping_methods_in_package( $available_methods ){
99
 
100
  foreach($available_methods as $key => $method){
101
- $available_methods[$key]->label = $this->translate_shipping_method_title( $method->label, $key );
 
 
102
  }
103
 
104
  return apply_filters( 'wcml_translated_package_rates', $available_methods );
98
  function translate_shipping_methods_in_package( $available_methods ){
99
 
100
  foreach($available_methods as $key => $method){
101
+ if( apply_filters( 'wcml_translate_shipping_method_in_package', true, $key, $method ) ){
102
+ $available_methods[$key]->label = $this->translate_shipping_method_title( $method->label, $key );
103
+ }
104
  }
105
 
106
  return apply_filters( 'wcml_translated_package_rates', $available_methods );
inc/currencies/class-wcml-custom-prices.php CHANGED
@@ -2,13 +2,17 @@
2
 
3
  class WCML_Custom_Prices{
4
 
 
5
  private $woocommerce_wpml;
6
 
7
- public function __construct( &$woocommerce_wpml ){
8
- add_filter( 'init', array( $this, 'custom_prices_init' ) );
9
  $this->woocommerce_wpml = $woocommerce_wpml;
10
  }
11
 
 
 
 
 
12
  public function custom_prices_init(){
13
  if ( is_admin() ) {
14
  add_action( 'woocommerce_variation_options', array($this, 'add_individual_variation_nonce'), 10, 3 );
@@ -411,50 +415,69 @@ class WCML_Custom_Prices{
411
  }
412
  }
413
 
414
- public function update_custom_prices( $post_id, $custom_prices, $code ){
415
- $price = '';
416
 
417
- // initialization
418
- $keys = array(
419
- '_sale_price_dates_to', '_sale_price_dates_from',
420
- '_sale_price', '_sale_price_dates_to', '_sale_price_dates_from',
 
421
 
422
- );
423
- foreach( $keys as $key ){
424
- if( !isset( $custom_prices[$key] ) ){ $custom_prices[$key] = ''; }
425
- }
426
 
427
- foreach( $custom_prices as $custom_price_key => $custom_price_value ){
428
- update_post_meta( $post_id, $custom_price_key.'_'.$code, $custom_price_value );
429
- }
430
- if ( $custom_prices[ '_sale_price_dates_to' ] && ! $custom_prices[ '_sale_price_dates_from' ] ) {
431
- update_post_meta($post_id, '_sale_price_dates_from_' . $code, strtotime( 'NOW', current_time( 'timestamp' ) ) );
432
- }
433
- // Update price if on sale
434
- if ( $custom_prices[ '_sale_price' ] != '' && $custom_prices[ '_sale_price_dates_to' ] == '' && $custom_prices[ '_sale_price_dates_from' ] == '' ){
435
- $price = stripslashes( $custom_prices[ '_sale_price' ] );
436
- update_post_meta( $post_id, '_price_'.$code, stripslashes( $custom_prices[ '_sale_price' ] ) );
437
- }else{
438
- $price = stripslashes( $custom_prices[ '_regular_price' ] );
439
- update_post_meta( $post_id, '_price_'.$code, stripslashes( $custom_prices[ '_regular_price' ] ) );
440
- }
441
 
442
- if ( $custom_prices[ '_sale_price' ] != '' && $custom_prices[ '_sale_price_dates_from' ] < strtotime( 'NOW', current_time( 'timestamp' ) ) ){
443
- update_post_meta( $post_id, '_price_'.$code, stripslashes( $custom_prices[ '_sale_price' ] ) );
444
- $price = stripslashes( $custom_prices[ '_sale_price' ] );
445
- }
446
 
447
- if ( $custom_prices[ '_sale_price_dates_to' ] && $custom_prices[ '_sale_price_dates_to' ] < strtotime( 'NOW', current_time( 'timestamp' ) ) ) {
448
- update_post_meta( $post_id, '_price_'.$code, stripslashes( $custom_prices[ '_regular_price' ] ) );
449
- $price = stripslashes( $custom_prices[ '_regular_price' ] );
450
- update_post_meta( $post_id, '_sale_price_dates_from_'.$code, '' );
451
- update_post_meta( $post_id, '_sale_price_dates_to_'.$code, '' );
452
- }
453
 
454
- return $price;
455
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
 
457
- public function sync_product_variations_custom_prices( $product_id ){
458
 
459
  if( isset( $_POST[ '_wcml_custom_prices' ][ $product_id ] ) ){
460
 
2
 
3
  class WCML_Custom_Prices{
4
 
5
+ /** @var woocommerce_wpml */
6
  private $woocommerce_wpml;
7
 
8
+ public function __construct( woocommerce_wpml $woocommerce_wpml ){
 
9
  $this->woocommerce_wpml = $woocommerce_wpml;
10
  }
11
 
12
+ public function add_hooks(){
13
+ add_filter( 'init', array( $this, 'custom_prices_init' ) );
14
+ }
15
+
16
  public function custom_prices_init(){
17
  if ( is_admin() ) {
18
  add_action( 'woocommerce_variation_options', array($this, 'add_individual_variation_nonce'), 10, 3 );
415
  }
416
  }
417
 
418
+ public function update_custom_prices( $post_id, $custom_prices, $code ) {
419
+ $price = null;
420
 
421
+ $defaults = array(
422
+ '_sale_price_dates_to' => '',
423
+ '_sale_price_dates_from' => '',
424
+ '_sale_price' => ''
425
+ );
426
 
427
+ $custom_prices = array_merge( $defaults, $custom_prices );
 
 
 
428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
429
 
430
+ foreach ( $custom_prices as $custom_price_key => $custom_price_value ) {
431
+ update_post_meta( $post_id, $custom_price_key . '_' . $code, $custom_price_value );
432
+ }
 
433
 
434
+ if ( $this->is_sale_price_valid( $custom_prices ) ){
435
+ $price = stripslashes( $custom_prices['_sale_price'] );
436
+ } else {
437
+ $price = stripslashes( $custom_prices['_regular_price'] );
438
+ $this->validate_and_update_sale_price_dates( $post_id, $code, $custom_prices );
439
+ }
440
 
441
+ update_post_meta( $post_id, '_price_' . $code, $price ? $price : null );
442
+
443
+ return $price;
444
+ }
445
+
446
+ /**
447
+ * @param int $post_id
448
+ * @param string $code
449
+ * @param array $custom_prices
450
+ */
451
+ private function validate_and_update_sale_price_dates( $post_id, $code, array $custom_prices ) {
452
+ $date_from = $custom_prices['_sale_price_dates_from'];
453
+ $date_to = $custom_prices['_sale_price_dates_to'];
454
+ if ( $date_to ) {
455
+ if ( $date_to < strtotime( 'NOW', current_time( 'timestamp' ) ) ) {
456
+ update_post_meta( $post_id, '_sale_price_dates_from_' . $code, '' );
457
+ update_post_meta( $post_id, '_sale_price_dates_to_' . $code, '' );
458
+ } elseif ( ! $date_from ) {
459
+ update_post_meta( $post_id,
460
+ '_sale_price_dates_from_' . $code,
461
+ strtotime( 'NOW', current_time( 'timestamp' ) ) );
462
+ }
463
+ }
464
+ }
465
+
466
+ /**
467
+ * @param array $custom_prices
468
+ *
469
+ * @return bool
470
+ */
471
+ protected function is_sale_price_valid( array $custom_prices ) {
472
+ $sale_price_dates_from = $custom_prices['_sale_price_dates_from'];
473
+ $sale_price_dates_to = $custom_prices['_sale_price_dates_to'];
474
+ $not_depend_on_date = $sale_price_dates_to === '' && $sale_price_dates_from === '';
475
+ $valid_sale_date = $sale_price_dates_from < strtotime( 'NOW', current_time( 'timestamp' ) );
476
+
477
+ return $custom_prices['_sale_price'] !== '' && ( $not_depend_on_date || $valid_sale_date );
478
+ }
479
 
480
+ public function sync_product_variations_custom_prices( $product_id ){
481
 
482
  if( isset( $_POST[ '_wcml_custom_prices' ][ $product_id ] ) ){
483
 
inc/currencies/class-wcml-multi-currency.php CHANGED
@@ -123,6 +123,7 @@ class WCML_Multi_Currency{
123
  );
124
  $this->admin_currency_selector->add_hooks();
125
  $this->custom_prices = new WCML_Custom_Prices( $woocommerce_wpml );
 
126
  $this->currency_switcher = new WCML_Currency_Switcher( $woocommerce_wpml, $sitepress );
127
  $this->currency_switcher->add_hooks();
128
  $this->currency_switcher_ajax = new WCML_Currency_Switcher_Ajax( $woocommerce_wpml );
123
  );
124
  $this->admin_currency_selector->add_hooks();
125
  $this->custom_prices = new WCML_Custom_Prices( $woocommerce_wpml );
126
+ $this->custom_prices->add_hooks();
127
  $this->currency_switcher = new WCML_Currency_Switcher( $woocommerce_wpml, $sitepress );
128
  $this->currency_switcher->add_hooks();
129
  $this->currency_switcher_ajax = new WCML_Currency_Switcher_Ajax( $woocommerce_wpml );
inc/translation-editor/class-wcml-synchronize-product-data.php CHANGED
@@ -498,11 +498,11 @@ class WCML_Synchronize_Product_Data{
498
 
499
  if ( $translations ) {
500
  foreach ( $translations as $translation ) {
501
- if ( $is_original && $product_id !== $translation ) {
502
  $language_code = $this->post_translations->get_element_lang_code( $translation );
503
  $this->sync_product_data( $product_id, $translation, $language_code );
504
  $this->sync_date_and_parent( $product_id, $translation, $language_code );
505
- } elseif ( $original_product_id === $translation ) {
506
  $language_code = $this->post_translations->get_element_lang_code( $product_id );
507
  $this->sync_product_data( $translation, $product_id, $language_code );
508
  $this->sync_date_and_parent( $translation, $product_id, $language_code );
498
 
499
  if ( $translations ) {
500
  foreach ( $translations as $translation ) {
501
+ if ( $is_original && $product_id !== (int) $translation ) {
502
  $language_code = $this->post_translations->get_element_lang_code( $translation );
503
  $this->sync_product_data( $product_id, $translation, $language_code );
504
  $this->sync_date_and_parent( $product_id, $translation, $language_code );
505
+ } elseif ( ! $is_original && $original_product_id === (int) $translation ) {
506
  $language_code = $this->post_translations->get_element_lang_code( $product_id );
507
  $this->sync_product_data( $translation, $product_id, $language_code );
508
  $this->sync_date_and_parent( $translation, $product_id, $language_code );
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: CMS, woocommerce, commerce, ecommerce, e-commerce, products, WPML, multili
5
  License: GPLv2
6
  Requires at least: 4.7
7
  Tested up to: 5.2.1
8
- Stable tag: 4.6.3
9
 
10
  Allows running fully multilingual e-commerce sites using WooCommerce and WPML.
11
 
@@ -140,6 +140,17 @@ WooCommerce Multilingual is compatible with all major WooCommerce extensions. We
140
 
141
  == Changelog ==
142
 
 
 
 
 
 
 
 
 
 
 
 
143
  = 4.6.3 =
144
  * In some cases product translation can be converted to a simple product instead of correct type after saving
145
  * Fix not recalculated ratings after remove rating from admin
5
  License: GPLv2
6
  Requires at least: 4.7
7
  Tested up to: 5.2.1
8
+ Stable tag: 4.6.5
9
 
10
  Allows running fully multilingual e-commerce sites using WooCommerce and WPML.
11
 
140
 
141
  == Changelog ==
142
 
143
+ = 4.6.5 =
144
+ * The Events Calendar: convert currency for event_cost.
145
+ * Wrong product price set after purchase in combination with custom prices for secondary currency and enabled stock
146
+ * Translated attributes not saving if original one contains umlauts and original product language is German or Danish
147
+ * Price not auto-calculated if you selected using custom prices and don't set a price
148
+ * After quick edit variable product variations incremented their IDs
149
+ * Added new wcml_translate_shipping_method_in_package filter
150
+ * Products not filtered by current language while search Upsells/Cross-sells on product edit screen
151
+ * Use default language if admin user not exists while sending "New Order" email to admins
152
+ * Variation description not saved on installs with 300+ variations for product
153
+
154
  = 4.6.3 =
155
  * In some cases product translation can be converted to a simple product instead of correct type after saving
156
  * Fix not recalculated ratings after remove rating from admin
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit6de590272da4e9672c17db908f09e178::getLoader();
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInit2ba1fa2bed2b05e6935dfe8f0b8f9241::getLoader();
vendor/autoload_52.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once dirname(__FILE__) . '/composer'.'/autoload_real_52.php';
6
 
7
- return ComposerAutoloaderInitbbd0c134ac587a0753eb6f856bac0721::getLoader();
4
 
5
  require_once dirname(__FILE__) . '/composer'.'/autoload_real_52.php';
6
 
7
+ return ComposerAutoloaderInit5b128786b2c78c76b4e062ebf198a7b3::getLoader();
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit6de590272da4e9672c17db908f09e178
6
  {
7
  private static $loader;
8
 
@@ -19,15 +19,15 @@ class ComposerAutoloaderInit6de590272da4e9672c17db908f09e178
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInit6de590272da4e9672c17db908f09e178', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInit6de590272da4e9672c17db908f09e178', 'loadClassLoader'));
25
 
26
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
  if ($useStaticLoader) {
28
  require_once __DIR__ . '/autoload_static.php';
29
 
30
- call_user_func(\Composer\Autoload\ComposerStaticInit6de590272da4e9672c17db908f09e178::getInitializer($loader));
31
  } else {
32
  $map = require __DIR__ . '/autoload_namespaces.php';
33
  foreach ($map as $namespace => $path) {
@@ -48,19 +48,19 @@ class ComposerAutoloaderInit6de590272da4e9672c17db908f09e178
48
  $loader->register(true);
49
 
50
  if ($useStaticLoader) {
51
- $includeFiles = Composer\Autoload\ComposerStaticInit6de590272da4e9672c17db908f09e178::$files;
52
  } else {
53
  $includeFiles = require __DIR__ . '/autoload_files.php';
54
  }
55
  foreach ($includeFiles as $fileIdentifier => $file) {
56
- composerRequire6de590272da4e9672c17db908f09e178($fileIdentifier, $file);
57
  }
58
 
59
  return $loader;
60
  }
61
  }
62
 
63
- function composerRequire6de590272da4e9672c17db908f09e178($fileIdentifier, $file)
64
  {
65
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
66
  require $file;
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInit2ba1fa2bed2b05e6935dfe8f0b8f9241
6
  {
7
  private static $loader;
8
 
19
  return self::$loader;
20
  }
21
 
22
+ spl_autoload_register(array('ComposerAutoloaderInit2ba1fa2bed2b05e6935dfe8f0b8f9241', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInit2ba1fa2bed2b05e6935dfe8f0b8f9241', 'loadClassLoader'));
25
 
26
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
  if ($useStaticLoader) {
28
  require_once __DIR__ . '/autoload_static.php';
29
 
30
+ call_user_func(\Composer\Autoload\ComposerStaticInit2ba1fa2bed2b05e6935dfe8f0b8f9241::getInitializer($loader));
31
  } else {
32
  $map = require __DIR__ . '/autoload_namespaces.php';
33
  foreach ($map as $namespace => $path) {
48
  $loader->register(true);
49
 
50
  if ($useStaticLoader) {
51
+ $includeFiles = Composer\Autoload\ComposerStaticInit2ba1fa2bed2b05e6935dfe8f0b8f9241::$files;
52
  } else {
53
  $includeFiles = require __DIR__ . '/autoload_files.php';
54
  }
55
  foreach ($includeFiles as $fileIdentifier => $file) {
56
+ composerRequire2ba1fa2bed2b05e6935dfe8f0b8f9241($fileIdentifier, $file);
57
  }
58
 
59
  return $loader;
60
  }
61
  }
62
 
63
+ function composerRequire2ba1fa2bed2b05e6935dfe8f0b8f9241($fileIdentifier, $file)
64
  {
65
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
66
  require $file;
vendor/composer/autoload_real_52.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real_52.php generated by xrstf/composer-php52
4
 
5
- class ComposerAutoloaderInitbbd0c134ac587a0753eb6f856bac0721 {
6
  private static $loader;
7
 
8
  public static function loadClassLoader($class) {
@@ -19,9 +19,9 @@ class ComposerAutoloaderInitbbd0c134ac587a0753eb6f856bac0721 {
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInitbbd0c134ac587a0753eb6f856bac0721', 'loadClassLoader'), true /*, true */);
23
  self::$loader = $loader = new xrstf_Composer52_ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInitbbd0c134ac587a0753eb6f856bac0721', 'loadClassLoader'));
25
 
26
  $vendorDir = dirname(dirname(__FILE__));
27
  $baseDir = dirname($vendorDir);
2
 
3
  // autoload_real_52.php generated by xrstf/composer-php52
4
 
5
+ class ComposerAutoloaderInit5b128786b2c78c76b4e062ebf198a7b3 {
6
  private static $loader;
7
 
8
  public static function loadClassLoader($class) {
19
  return self::$loader;
20
  }
21
 
22
+ spl_autoload_register(array('ComposerAutoloaderInit5b128786b2c78c76b4e062ebf198a7b3', 'loadClassLoader'), true /*, true */);
23
  self::$loader = $loader = new xrstf_Composer52_ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInit5b128786b2c78c76b4e062ebf198a7b3', 'loadClassLoader'));
25
 
26
  $vendorDir = dirname(dirname(__FILE__));
27
  $baseDir = dirname($vendorDir);
vendor/composer/autoload_static.php CHANGED
@@ -4,7 +4,7 @@
4
 
5
  namespace Composer\Autoload;
6
 
7
- class ComposerStaticInit6de590272da4e9672c17db908f09e178
8
  {
9
  public static $files = array (
10
  'b45b351e6b6f7487d819961fef2fda77' => __DIR__ . '/..' . '/jakeasmith/http_build_url/src/http_build_url.php',
@@ -242,10 +242,10 @@ class ComposerStaticInit6de590272da4e9672c17db908f09e178
242
  public static function getInitializer(ClassLoader $loader)
243
  {
244
  return \Closure::bind(function () use ($loader) {
245
- $loader->prefixLengthsPsr4 = ComposerStaticInit6de590272da4e9672c17db908f09e178::$prefixLengthsPsr4;
246
- $loader->prefixDirsPsr4 = ComposerStaticInit6de590272da4e9672c17db908f09e178::$prefixDirsPsr4;
247
- $loader->prefixesPsr0 = ComposerStaticInit6de590272da4e9672c17db908f09e178::$prefixesPsr0;
248
- $loader->classMap = ComposerStaticInit6de590272da4e9672c17db908f09e178::$classMap;
249
 
250
  }, null, ClassLoader::class);
251
  }
4
 
5
  namespace Composer\Autoload;
6
 
7
+ class ComposerStaticInit2ba1fa2bed2b05e6935dfe8f0b8f9241
8
  {
9
  public static $files = array (
10
  'b45b351e6b6f7487d819961fef2fda77' => __DIR__ . '/..' . '/jakeasmith/http_build_url/src/http_build_url.php',
242
  public static function getInitializer(ClassLoader $loader)
243
  {
244
  return \Closure::bind(function () use ($loader) {
245
+ $loader->prefixLengthsPsr4 = ComposerStaticInit2ba1fa2bed2b05e6935dfe8f0b8f9241::$prefixLengthsPsr4;
246
+ $loader->prefixDirsPsr4 = ComposerStaticInit2ba1fa2bed2b05e6935dfe8f0b8f9241::$prefixDirsPsr4;
247
+ $loader->prefixesPsr0 = ComposerStaticInit2ba1fa2bed2b05e6935dfe8f0b8f9241::$prefixesPsr0;
248
+ $loader->classMap = ComposerStaticInit2ba1fa2bed2b05e6935dfe8f0b8f9241::$classMap;
249
 
250
  }, null, ClassLoader::class);
251
  }
wpml-woocommerce.php CHANGED
@@ -8,7 +8,7 @@
8
  Text Domain: woocommerce-multilingual
9
  Requires at least: 4.7
10
  Tested up to: 5.2.1
11
- Version: 4.6.3
12
  WC requires at least: 3.3.0
13
  WC tested up to: 3.6.3
14
  */
@@ -17,7 +17,7 @@ if ( defined( 'WCML_VERSION' ) ) {
17
  return;
18
  }
19
 
20
- define( 'WCML_VERSION', '4.6.3' );
21
  define( 'WCML_PLUGIN_PATH', dirname( __FILE__ ) );
22
  define( 'WCML_PLUGIN_FOLDER', basename( WCML_PLUGIN_PATH ) );
23
  define( 'WCML_LOCALE_PATH', WCML_PLUGIN_PATH . '/locale' );
8
  Text Domain: woocommerce-multilingual
9
  Requires at least: 4.7
10
  Tested up to: 5.2.1
11
+ Version: 4.6.5
12
  WC requires at least: 3.3.0
13
  WC tested up to: 3.6.3
14
  */
17
  return;
18
  }
19
 
20
+ define( 'WCML_VERSION', '4.6.5' );
21
  define( 'WCML_PLUGIN_PATH', dirname( __FILE__ ) );
22
  define( 'WCML_PLUGIN_FOLDER', basename( WCML_PLUGIN_PATH ) );
23
  define( 'WCML_LOCALE_PATH', WCML_PLUGIN_PATH . '/locale' );