WooCommerce Multilingual – run WooCommerce with WPML - Version 4.11.5

Version Description

  • Temporary fix for dashboard widget.
Download this release

Release Info

Developer andrew.p
Plugin Icon 128x128 WooCommerce Multilingual – run WooCommerce with WPML
Version 4.11.5
Comparing to
See all releases

Code changes from version 4.11.3.2 to 4.11.5

changelog/4.11.3.md DELETED
@@ -1,6 +0,0 @@
1
- # Fixes
2
- * [wcml-3540] Fixed a regression when a non-logged user switches language and each language has a default currency.
3
-
4
- # Compatibility
5
- * [wcml-3553] Fixed a bug in PHP 8 showing a 404 page for endpoints.
6
- * [wcml-3545] Fixed the "new order" email not sent to all the recipients.
 
 
 
 
 
 
changelog/4.11.5.md ADDED
@@ -0,0 +1,2 @@
 
 
1
+ # Fixes
2
+ * [wcml-3598] Temporary fix for dashboard widget.
classes/Rest/Generic.php CHANGED
@@ -4,19 +4,6 @@ namespace WCML\Rest;
4
 
5
  class Generic {
6
 
7
- /**
8
- * Enforces the language of request as the current language to be able to filter items by language
9
- *
10
- * @param WP_REST_Server $wp_rest_server
11
- */
12
- public static function setLanguageForRequest( \WP_REST_Server $wp_rest_server ) {
13
- if ( isset( $_GET['lang'] ) ) {
14
- if ( apply_filters( 'wpml_language_is_active', false, $_GET['lang'] ) ) {
15
- wpml_switch_language_action( $_GET['lang'] );
16
- }
17
- }
18
- }
19
-
20
  /**
21
  * Prevent WPML redirection when using the default language as a parameter in the url
22
  */
@@ -46,4 +33,4 @@ class Generic {
46
  }
47
  }
48
 
49
- }
4
 
5
  class Generic {
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  /**
8
  * Prevent WPML redirection when using the default language as a parameter in the url
9
  */
33
  }
34
  }
35
 
36
+ }
classes/Rest/Hooks.php CHANGED
@@ -10,7 +10,8 @@ class Hooks {
10
 
11
  Generic::preventDefaultLangUrlRedirect();
12
 
13
- add_action( 'rest_api_init', [ Generic::class, 'setLanguageForRequest' ] );
 
14
  add_action( 'parse_query', [ Generic::class, 'autoAdjustIncludedIds' ] );
15
 
16
  foreach ( [ 'product', 'shop_order', 'product_variation' ] as $type ) {
@@ -53,4 +54,4 @@ class Hooks {
53
  }
54
  }
55
 
56
- }
10
 
11
  Generic::preventDefaultLangUrlRedirect();
12
 
13
+ add_action( 'rest_api_init', [ Language\Set::class, 'fromUrlQueryVar' ] );
14
+ add_filter( 'rest_request_before_callbacks', [ Language\Set::class, 'beforeCallbacks' ], 10, 3 );
15
  add_action( 'parse_query', [ Generic::class, 'autoAdjustIncludedIds' ] );
16
 
17
  foreach ( [ 'product', 'shop_order', 'product_variation' ] as $type ) {
54
  }
55
  }
56
 
57
+ }
classes/Rest/Language/Set.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WCML\Rest\Language;
4
+
5
+ use WPML\FP\Obj;
6
+
7
+ class Set {
8
+
9
+ public static function fromUrlQueryVar() {
10
+ $lang = self::sanitize( Obj::prop( 'lang', $_GET ) );
11
+
12
+ if ( $lang ) {
13
+ wpml_switch_language_action( $lang );
14
+ }
15
+ }
16
+
17
+ /**
18
+ * @param \WP_REST_Response|\WP_HTTP_Response|\WP_Error|mixed $response
19
+ * @param array $handler
20
+ * @param \WP_REST_Request $request
21
+ *
22
+ * @return \WP_REST_Response|\WP_HTTP_Response|\WP_Error|mixed
23
+ */
24
+ public static function beforeCallbacks( $response, $handler, \WP_REST_Request $request ) {
25
+ $lang = self::getFromRequestParams( $request )
26
+ ?: self::getFromProduct( $handler, $request );
27
+
28
+ if ( $lang ) {
29
+ wpml_switch_language_action( $lang );
30
+ }
31
+
32
+ return $response;
33
+ }
34
+
35
+ /**
36
+ * @param \WP_REST_Request $request
37
+ *
38
+ * @return string
39
+ */
40
+ private static function getFromRequestParams( \WP_REST_Request $request ) {
41
+ return self::sanitize( $request->get_param( 'lang' ) );
42
+ }
43
+
44
+ /**
45
+ * @param array $handler
46
+ * @param \WP_REST_Request $request
47
+ *
48
+ * @return string
49
+ */
50
+ private static function getFromProduct( $handler, \WP_REST_Request $request ) {
51
+ if (
52
+ Obj::path( [ 'callback', 0 ], $handler ) instanceof \WC_REST_Products_Controller
53
+ && $request->get_param( 'id' )
54
+ ) {
55
+ return (string) Obj::prop(
56
+ 'language_code',
57
+ apply_filters( 'wpml_post_language_details', [], $request->get_param( 'id' ) )
58
+ );
59
+ }
60
+
61
+ return '';
62
+ }
63
+
64
+ /**
65
+ * @param string $lang
66
+ *
67
+ * @return string
68
+ */
69
+ private static function sanitize( $lang ) {
70
+ return filter_var( $lang, FILTER_SANITIZE_STRING );
71
+ }
72
+ }
classes/Rest/ProductSaveActions.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WCML\Rest;
4
+
5
+ /**
6
+ * We need to reuse the `after_save_post` protected method
7
+ * of the \WPML_Post_Translation and the only way is by inheritance.
8
+ * The abstract methods are not used in the parent, but
9
+ * we need implement it to respect the contract.
10
+ * We are also including the WCML logic to `synchronize_products`.
11
+ */
12
+ class ProductSaveActions extends \WPML_Post_Translation {
13
+
14
+ /** @var \SitePress $sitepress */
15
+ private $sitepress;
16
+
17
+ /** @var \WCML_Synchronize_Product_Data $productDataSync */
18
+ private $productDataSync;
19
+
20
+ public function __construct(
21
+ array $settings,
22
+ \wpdb $wpdb,
23
+ \SitePress $sitepress,
24
+ \WCML_Synchronize_Product_Data $productDataSync
25
+ ) {
26
+ parent::__construct( $settings, $wpdb );
27
+ $this->sitepress = $sitepress;
28
+ $this->productDataSync = $productDataSync;
29
+ }
30
+
31
+ /**
32
+ * @param object|\WC_Abstract_Legacy_Product $product
33
+ * @param int|null $trid
34
+ * @param string $langCode
35
+ * @param int|null $translationOf
36
+ */
37
+ public function run( $product, $trid, $langCode, $translationOf ) {
38
+ $productId = $product->get_id();
39
+ $trid = $trid ? $trid : $this->get_save_post_trid( $productId, null );
40
+ $langCode = $langCode ? $langCode : parent::get_save_post_lang( $productId, $this->sitepress );
41
+ $sourceLangCode = $this->get_element_lang_code( $translationOf );
42
+
43
+ $this->after_save_post( $trid, get_post( $productId, ARRAY_A ), $langCode, $sourceLangCode );
44
+ $this->productDataSync->synchronize_products( $productId, get_post( $productId ), true );
45
+ }
46
+
47
+ public function save_post_actions( $postId, $post ) {
48
+ throw new \Exception( 'This method should not be called, use `run` instead.' );
49
+ }
50
+
51
+ /**
52
+ * @inheritDoc
53
+ */
54
+ function get_save_post_trid( $postId, $post_status ) {
55
+ return $this->get_element_trid( $postId );
56
+ }
57
+
58
+ /**
59
+ * @inheritDoc
60
+ */
61
+ protected function get_save_post_source_lang( $trid, $language_code, $default_language ) {
62
+ return $this->get_source_lang_code( $this->get_element_id( $language_code, $trid ) );
63
+ }
64
+ }
classes/Rest/Wrapper/Factory.php CHANGED
@@ -2,23 +2,31 @@
2
 
3
  namespace WCML\Rest\Wrapper;
4
 
5
- use WCML\Rest\Wrapper\Products\Images as ProductsImages;
6
- use WCML\Rest\Wrapper\Products\Languages as ProductsLanguages;
7
- use WCML\Rest\Wrapper\Products\Prices as ProductsPrices;
8
 
9
  use WCML\Rest\Wrapper\Orders\Languages as OrdersLanguages;
10
  use WCML\Rest\Wrapper\Orders\Prices as OrdersPrices;
 
11
  use WCML\Rest\Wrapper\Reports\ProductsCount;
12
  use WCML\Rest\Wrapper\Reports\ProductsSales;
13
  use WCML\Rest\Wrapper\Reports\TopSeller;
14
- use WPML\FP\Obj;
15
 
16
  class Factory {
17
 
18
  /**
 
 
19
  * @return Handler
20
  */
21
  public static function create( $objectType ) {
 
 
 
 
 
 
 
 
22
  global $woocommerce_wpml, $wpml_post_translations, $wpml_term_translations, $sitepress, $wpml_query_filter, $wpdb;
23
 
24
  $isMultiCurrencyOn = wcml_is_multi_currency_on();
@@ -33,13 +41,12 @@ class Factory {
33
  return new Composite( $objects );
34
  case 'product_variation':
35
  case 'product':
36
- $objects[] = new ProductsLanguages( $sitepress, $wpml_post_translations, $wpml_query_filter, $woocommerce_wpml->sync_variations_data, $woocommerce_wpml->attributes );
37
- $objects[] = new ProductsImages( $woocommerce_wpml->products, $woocommerce_wpml->media );
38
- if ( $isMultiCurrencyOn ) {
39
- $objects[] = new ProductsPrices( $woocommerce_wpml->multi_currency, (array) Obj::propOr( [], 'currencies_order', $woocommerce_wpml->settings ), $wpml_post_translations );
40
- }
41
-
42
- return new Composite( $objects );
43
  case 'term':
44
  return new ProductTerms( $sitepress, $wpml_term_translations, $woocommerce_wpml->terms );
45
  case 'reports_top_seller':
@@ -53,4 +60,4 @@ class Factory {
53
  return new Handler();
54
  }
55
 
56
- }
2
 
3
  namespace WCML\Rest\Wrapper;
4
 
5
+ use WCML\Rest\ProductSaveActions;
 
 
6
 
7
  use WCML\Rest\Wrapper\Orders\Languages as OrdersLanguages;
8
  use WCML\Rest\Wrapper\Orders\Prices as OrdersPrices;
9
+ use WCML\Rest\Wrapper\Products\Products;
10
  use WCML\Rest\Wrapper\Reports\ProductsCount;
11
  use WCML\Rest\Wrapper\Reports\ProductsSales;
12
  use WCML\Rest\Wrapper\Reports\TopSeller;
 
13
 
14
  class Factory {
15
 
16
  /**
17
+ * @param string $objectType
18
+ *
19
  * @return Handler
20
  */
21
  public static function create( $objectType ) {
22
+ /**
23
+ * @var \woocommerce_wpml $woocommerce_wpml
24
+ * @var \WPML_Post_Translation $wpml_post_translations
25
+ * @var \WPML_Term_Translation $wpml_term_translations
26
+ * @var \SitePress $sitepress
27
+ * @var \WPML_Query_Filter $wpml_query_filter
28
+ * @var \wpdb $wpdb
29
+ */
30
  global $woocommerce_wpml, $wpml_post_translations, $wpml_term_translations, $sitepress, $wpml_query_filter, $wpdb;
31
 
32
  $isMultiCurrencyOn = wcml_is_multi_currency_on();
41
  return new Composite( $objects );
42
  case 'product_variation':
43
  case 'product':
44
+ return new Products(
45
+ $sitepress,
46
+ $wpml_post_translations,
47
+ $wpml_query_filter,
48
+ new ProductSaveActions( $sitepress->get_settings(), $wpdb, $sitepress, $woocommerce_wpml->sync_product_data )
49
+ );
 
50
  case 'term':
51
  return new ProductTerms( $sitepress, $wpml_term_translations, $woocommerce_wpml->terms );
52
  case 'reports_top_seller':
60
  return new Handler();
61
  }
62
 
63
+ }
classes/Rest/Wrapper/ProductTerms.php CHANGED
@@ -2,6 +2,7 @@
2
 
3
  namespace WCML\Rest\Wrapper;
4
 
 
5
  use WPML\FP\Obj;
6
  use WCML\Rest\Exceptions\InvalidLanguage;
7
  use WCML\Rest\Exceptions\InvalidTerm;
@@ -9,11 +10,11 @@ use WCML\Rest\Exceptions\MissingLanguage;
9
 
10
  class ProductTerms extends Handler {
11
 
12
- /** @var Sitepress */
13
  private $sitepress;
14
- /** @var WPML_Term_Translation */
15
  private $wpmlTermTranslations;
16
- /** @var WCML_Terms */
17
  private $wcmlTerms;
18
 
19
  public function __construct(
@@ -27,8 +28,8 @@ class ProductTerms extends Handler {
27
  }
28
 
29
  /**
30
- * @param array $args
31
- * @param WP_REST_Request $request Request object.
32
  *
33
  * @return array
34
  *
@@ -52,57 +53,72 @@ class ProductTerms extends Handler {
52
  /**
53
  * Appends the language and translation information to the get_product response
54
  *
55
- * @param WP_REST_Response $response
56
- * @param object $object
57
- * @param WP_REST_Request $request
58
  *
59
- * @return WP_REST_Response
60
  */
61
  public function prepare( $response, $object, $request ) {
62
 
63
  $response->data['translations'] = [];
64
 
65
- $id = Obj::prop( 'id', $response->data );
66
- $trid = $this->wpmlTermTranslations->get_element_trid( $id );
67
 
68
  if ( $trid ) {
69
- $response->data['translations'] = $this->wpmlTermTranslations->get_element_translations( $id, $trid );
70
- $response->data['lang'] = $this->wpmlTermTranslations->get_element_lang_code( $id );
 
 
 
 
 
 
 
 
 
71
  }
72
 
73
  return $response;
74
  }
75
 
76
-
77
  /**
78
  * Sets the product information according to the provided language
79
  *
80
- * @param WP_Term $term
81
- * @param WP_REST_Request $request
82
- * @param bool $creating
83
  *
84
  * @throws InvalidLanguage
85
  * @throws InvalidTerm
86
  *
87
  */
88
  public function insert( $term, $request, $creating ) {
89
- $language = Obj::prop( 'lang', $request->get_params() );
90
- $translationOf = Obj::prop( 'translation_of', $request->get_params() );
91
 
92
- if ( $language && in_array( $request->get_method(), array( 'POST', 'PUT' ), true ) ) {
 
 
 
93
 
94
  $this->checkLanguage( $language );
95
 
96
  if ( $translationOf ) {
97
- $trid = $this->wpmlTermTranslations->get_element_trid( $translationOf );
98
- if ( empty( $trid ) ) {
 
 
 
 
 
99
  throw new InvalidTerm( $translationOf );
100
  }
101
  } else {
102
  $trid = null;
103
  }
104
 
105
- $this->sitepress->set_element_language_details( $term->term_id, 'tax_' . $term->taxonomy, $trid, $language );
106
 
107
  $this->wcmlTerms->update_terms_translated_status( $term->taxonomy );
108
  } elseif ( $translationOf ) {
@@ -110,10 +126,14 @@ class ProductTerms extends Handler {
110
  }
111
  }
112
 
 
 
 
 
 
113
  private function checkLanguage( $language ) {
114
  if ( ! $this->sitepress->is_active_language( $language ) ) {
115
  throw new InvalidLanguage( $language );
116
  }
117
  }
118
-
119
- }
2
 
3
  namespace WCML\Rest\Wrapper;
4
 
5
+ use WPML\FP\Fns;
6
  use WPML\FP\Obj;
7
  use WCML\Rest\Exceptions\InvalidLanguage;
8
  use WCML\Rest\Exceptions\InvalidTerm;
10
 
11
  class ProductTerms extends Handler {
12
 
13
+ /** @var \Sitepress */
14
  private $sitepress;
15
+ /** @var \WPML_Term_Translation */
16
  private $wpmlTermTranslations;
17
+ /** @var \WCML_Terms */
18
  private $wcmlTerms;
19
 
20
  public function __construct(
28
  }
29
 
30
  /**
31
+ * @param array $args
32
+ * @param \WP_REST_Request $request Request object.
33
  *
34
  * @return array
35
  *
53
  /**
54
  * Appends the language and translation information to the get_product response
55
  *
56
+ * @param \WP_REST_Response $response
57
+ * @param object|\WP_Term $object
58
+ * @param \WP_REST_Request $request
59
  *
60
+ * @return \WP_REST_Response
61
  */
62
  public function prepare( $response, $object, $request ) {
63
 
64
  $response->data['translations'] = [];
65
 
66
+ $termTaxonomyId = (int) Obj::prop( 'term_taxonomy_id', $object );
67
+ $trid = $this->wpmlTermTranslations->get_element_trid( $termTaxonomyId );
68
 
69
  if ( $trid ) {
70
+ $getTermId = function( $termTaxonomyId ) {
71
+ $term = get_term_by( 'term_taxonomy_id', $termTaxonomyId );
72
+ return isset( $term->term_id ) ? $term->term_id : null;
73
+ };
74
+
75
+ $response->data['translations'] = Fns::map(
76
+ $getTermId,
77
+ $this->wpmlTermTranslations->get_element_translations( $termTaxonomyId, $trid )
78
+ );
79
+
80
+ $response->data['lang'] = $this->wpmlTermTranslations->get_element_lang_code( $termTaxonomyId );
81
  }
82
 
83
  return $response;
84
  }
85
 
 
86
  /**
87
  * Sets the product information according to the provided language
88
  *
89
+ * @param \WP_Term $term
90
+ * @param \WP_REST_Request $request
91
+ * @param bool $creating
92
  *
93
  * @throws InvalidLanguage
94
  * @throws InvalidTerm
95
  *
96
  */
97
  public function insert( $term, $request, $creating ) {
98
+ $getParam = Obj::prop( Fns::__, $request->get_params() );
 
99
 
100
+ $language = $getParam( 'lang' );
101
+ $translationOf = $getParam( 'translation_of' );
102
+
103
+ if ( $language ) {
104
 
105
  $this->checkLanguage( $language );
106
 
107
  if ( $translationOf ) {
108
+ $translationOfTerm = get_term( $translationOf, $term->taxonomy );
109
+
110
+ $trid = isset( $translationOfTerm->term_taxonomy_id )
111
+ ? $this->wpmlTermTranslations->get_element_trid( $translationOfTerm->term_taxonomy_id )
112
+ : null;
113
+
114
+ if ( ! $trid ) {
115
  throw new InvalidTerm( $translationOf );
116
  }
117
  } else {
118
  $trid = null;
119
  }
120
 
121
+ $this->sitepress->set_element_language_details( $term->term_taxonomy_id, 'tax_' . $term->taxonomy, $trid, $language );
122
 
123
  $this->wcmlTerms->update_terms_translated_status( $term->taxonomy );
124
  } elseif ( $translationOf ) {
126
  }
127
  }
128
 
129
+ /**
130
+ * @param string $language
131
+ *
132
+ * @throws InvalidLanguage
133
+ */
134
  private function checkLanguage( $language ) {
135
  if ( ! $this->sitepress->is_active_language( $language ) ) {
136
  throw new InvalidLanguage( $language );
137
  }
138
  }
139
+ }
 
classes/Rest/Wrapper/Products/Images.php DELETED
@@ -1,42 +0,0 @@
1
- <?php
2
-
3
- namespace WCML\Rest\Wrapper\Products;
4
-
5
- use WCML\Rest\Wrapper\Handler;
6
-
7
- class Images extends Handler {
8
-
9
- /** @var WCML_Products */
10
- private $wcmlProduct;
11
-
12
- /** @var Translatable|NonTranslatable */
13
- private $wcmlMedia;
14
-
15
- public function __construct(
16
- \WCML_Products $wcmlProduct,
17
- $wcmlMedia
18
- ) {
19
- $this->wcmlProduct = $wcmlProduct;
20
- $this->wcmlMedia = $wcmlMedia;
21
- }
22
-
23
- /**
24
- * @param object $object Inserted object.
25
- * @param WP_REST_Request $request Request object.
26
- * @param boolean $creating True when creating object, false when updating.
27
- */
28
- public function insert( $object, $request, $creating ) {
29
- $data = $request->get_params();
30
-
31
- if ( isset( $data['translation_of'] ) ) {
32
- $this->wcmlMedia->sync_thumbnail_id( $data['translation_of'], $object->get_id(), $data['lang'] );
33
- $this->wcmlMedia->sync_product_gallery( $data['translation_of'] );
34
-
35
- if ( isset( $data['variations'] ) ) {
36
- foreach ( $data['variations'] as $variation_id ) {
37
- $this->wcmlMedia->sync_variation_thumbnail_id( $this->wcmlProduct->get_original_product_id( $variation_id ), $variation_id, $data['lang'] );
38
- }
39
- }
40
- }
41
- }
42
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/Rest/Wrapper/Products/Languages.php DELETED
@@ -1,141 +0,0 @@
1
- <?php
2
-
3
- namespace WCML\Rest\Wrapper\Products;
4
-
5
- use WCML\Rest\Wrapper\Handler;
6
- use WCML\Rest\Exceptions\InvalidLanguage;
7
- use WCML\Rest\Exceptions\InvalidProduct;
8
- use WCML\Rest\Exceptions\Generic;
9
-
10
-
11
- class Languages extends Handler {
12
-
13
- /** @var Sitepress */
14
- private $sitepress;
15
- /** @var WPML_Post_Translation */
16
- private $wpmlPostTranslations;
17
- /** @var WPML_Query_Filter */
18
- private $wpmlQueryFilter;
19
- /** @var WCML_Synchronize_Variations_Data */
20
- private $wcmlSyncVariationsData;
21
- /** @var WCML_Attributes */
22
- private $wcmlAttributes;
23
-
24
- public function __construct(
25
- \Sitepress $sitepress,
26
- \WPML_Post_Translation $wpmlPostTranslations,
27
- \WPML_Query_Filter $wpmlQueryFilter,
28
- \WCML_Synchronize_Variations_Data $wcmlSyncVariationsData,
29
- \WCML_Attributes $wcmlAttributes
30
- ) {
31
- $this->sitepress = $sitepress;
32
- $this->wpmlPostTranslations = $wpmlPostTranslations;
33
- $this->wpmlQueryFilter = $wpmlQueryFilter;
34
- $this->wcmlSyncVariationsData = $wcmlSyncVariationsData;
35
- $this->wcmlAttributes = $wcmlAttributes;
36
- }
37
-
38
- /**
39
- * @param array $args
40
- * @param WP_REST_Request $request Request object.
41
- *
42
- * @return array
43
- */
44
- public function query( $args, $request ) {
45
- $data = $request->get_params();
46
- if ( isset( $data['lang'] ) && $data['lang'] === 'all' ) {
47
- remove_filter( 'posts_join', [ $this->wpmlQueryFilter, 'posts_join_filter' ] );
48
- remove_filter( 'posts_where', [ $this->wpmlQueryFilter, 'posts_where_filter' ] );
49
- }
50
-
51
- return $args;
52
- }
53
-
54
-
55
- /**
56
- * Appends the language and translation information to the get_product response
57
- *
58
- * @param WP_REST_Response $response
59
- * @param object $object
60
- * @param WP_REST_Request $request
61
- *
62
- * @return WP_REST_Response
63
- */
64
- public function prepare( $response, $object, $request ) {
65
-
66
- $response->data['translations'] = array();
67
-
68
- $trid = $this->wpmlPostTranslations->get_element_trid( $response->data['id'] );
69
-
70
- if ( $trid ) {
71
- $translations = $this->wpmlPostTranslations->get_element_translations( $response->data['id'], $trid );
72
- foreach ( $translations as $translation ) {
73
- $response->data['translations'][ $this->wpmlPostTranslations->get_element_lang_code( $translation ) ] = $translation;
74
- }
75
- $response->data['lang'] = $this->wpmlPostTranslations->get_element_lang_code( $response->data['id'] );
76
- }
77
-
78
- return $response;
79
- }
80
-
81
-
82
- /**
83
- * Sets the product information according to the provided language
84
- *
85
- * @param object $object
86
- * @param WP_REST_Request $request
87
- * @param bool $creating
88
- *
89
- * @throws InvalidLanguage
90
- * @throws InvalidProduct
91
- * @throws Generic
92
- *
93
- */
94
- public function insert( $object, $request, $creating ) {
95
- $data = $request->get_params();
96
-
97
- if ( isset( $data['lang'] ) && in_array( $request->get_method(), array( 'POST', 'PUT' ), true ) ) {
98
-
99
- if ( ! apply_filters( 'wpml_language_is_active', false, $data['lang'] ) ) {
100
- throw new InvalidLanguage( $data['lang'] );
101
- }
102
- if ( isset( $data['translation_of'] ) ) {
103
- $trid = $this->wpmlPostTranslations->get_element_trid( $data['translation_of'] );
104
- if ( empty( $trid ) ) {
105
- throw new InvalidProduct( $data['translation_of'] );
106
- }
107
-
108
- $this->sitepress->copy_custom_fields( $data['translation_of'], $object->get_id() );
109
- } else {
110
- $trid = null;
111
- }
112
-
113
- $this->sitepress->set_element_language_details( $object->get_id(), 'post_'.get_post_type( $object->get_id() ), $trid, $data['lang'] );
114
- wpml_tm_save_post( $object->get_id(), get_post( $object->get_id() ), ICL_TM_COMPLETE );
115
-
116
- if ( isset( $data['translation_of'] ) ) {
117
- // needs run after set_element_language_details
118
- $this->syncVariableProduct( $object, $data['translation_of'], $data['lang'] );
119
- }
120
- } else {
121
- if ( isset( $data['translation_of'] ) ) {
122
- throw new Generic( __( 'Using "translation_of" requires providing a "lang" parameter too', 'woocommerce-multilingual' ) );
123
- }
124
- }
125
- }
126
-
127
- /**
128
- * @param object $object
129
- * @param string $translationOf
130
- * @param string $lang
131
- */
132
- private function syncVariableProduct( $object, $translationOf, $lang ) {
133
-
134
- if ( 'variable' === $object->get_type() ) {
135
- $this->wcmlAttributes->sync_default_product_attr( $translationOf, $object->get_id(), $lang );
136
- $this->wcmlSyncVariationsData->sync_product_variations( $translationOf, $object->get_id(), $lang );
137
- }
138
- }
139
-
140
-
141
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/Rest/Wrapper/Products/Prices.php DELETED
@@ -1,90 +0,0 @@
1
- <?php
2
-
3
- namespace WCML\Rest\Wrapper\Products;
4
-
5
- use WCML\Rest\Wrapper\Handler;
6
-
7
- class Prices extends Handler {
8
-
9
- /** @var WCML_Multi_Currency */
10
- private $wcmlMultiCurrency;
11
- /** @var array */
12
- private $currenciesOrderSettings;
13
- /** @var WPML_Post_Translation */
14
- private $wpmlPostTranslations;
15
-
16
- public function __construct(
17
- \WCML_Multi_Currency $wcmlMultiCurrency,
18
- array $currenciesOrderSettings,
19
- \WPML_Post_Translation $wpmlPostTranslations
20
- ) {
21
- $this->wcmlMultiCurrency = $wcmlMultiCurrency;
22
- $this->currenciesOrderSettings = $currenciesOrderSettings;
23
- $this->wpmlPostTranslations = $wpmlPostTranslations;
24
- }
25
-
26
- /**
27
- * @param WP_REST_Response $response The response object.
28
- * @param object $object Object data.
29
- * @param WP_REST_Request $request Request object.
30
- *
31
- * @return WP_REST_Response
32
- */
33
- public function prepare( $response, $object, $request ) {
34
-
35
- if ( ! empty( $this->currenciesOrderSettings ) ) {
36
-
37
- $response->data['multi-currency-prices'] = [];
38
- $isCustomPricesOn = get_post_meta( $response->data['id'], '_wcml_custom_prices_status', true );
39
-
40
- foreach ( $this->currenciesOrderSettings as $currency ) {
41
- if ( $currency !== wcml_get_woocommerce_currency_option() ) {
42
- if ( $isCustomPricesOn ) {
43
- $customPrices = (array) $this->wcmlMultiCurrency->custom_prices->get_product_custom_prices( $response->data['id'], $currency );
44
- foreach ( $customPrices as $key => $price ) {
45
- $response->data['multi-currency-prices'][ $currency ][ preg_replace( '#^_#', '', $key ) ] = $price;
46
- }
47
- } else {
48
- $response->data['multi-currency-prices'][ $currency ]['regular_price'] =
49
- $this->wcmlMultiCurrency->prices->raw_price_filter( $response->data['regular_price'], $currency );
50
- if ( ! empty( $response->data['sale_price'] ) ) {
51
- $response->data['multi-currency-prices'][ $currency ]['sale_price'] =
52
- $this->wcmlMultiCurrency->prices->raw_price_filter( $response->data['sale_price'], $currency );
53
- }
54
- }
55
- }
56
- }
57
- }
58
-
59
- return $response;
60
- }
61
-
62
- /**
63
- * @param object $object Inserted object.
64
- * @param WP_REST_Request $request Request object.
65
- * @param boolean $creating True when creating object, false when updating.
66
- */
67
- public function insert( $object, $request, $creating ) {
68
- $data = $request->get_params();
69
-
70
- if ( ! empty( $data['custom_prices'] ) ) {
71
-
72
- $trid = $this->wpmlPostTranslations->get_element_trid( $object->get_id() );
73
- $originalProductId = $this->wpmlPostTranslations->get_original_post_ID( $trid );
74
-
75
- update_post_meta( $originalProductId, '_wcml_custom_prices_status', 1 );
76
-
77
- foreach ( $data['custom_prices'] as $currency => $prices ) {
78
-
79
- $pricesUscore = array();
80
- foreach ( $prices as $k => $p ) {
81
- $pricesUscore[ '_' . $k ] = $p;
82
- }
83
- $this->wcmlMultiCurrency->custom_prices->update_custom_prices( $originalProductId, $pricesUscore, $currency );
84
-
85
- }
86
-
87
- }
88
- }
89
-
90
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/Rest/Wrapper/Products/Products.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WCML\Rest\Wrapper\Products;
4
+
5
+ use WCML\Rest\Exceptions\Generic;
6
+ use WCML\Rest\Exceptions\InvalidLanguage;
7
+ use WCML\Rest\Exceptions\InvalidProduct;
8
+ use WCML\Rest\ProductSaveActions;
9
+ use WCML\Rest\Wrapper\Handler;
10
+ use WPML\FP\Fns;
11
+ use WPML\FP\Obj;
12
+
13
+ class Products extends Handler {
14
+
15
+ /** @var \Sitepress */
16
+ private $sitepress;
17
+ /** @var \WPML_Post_Translation */
18
+ private $wpmlPostTranslations;
19
+ /** @var \WPML_Query_Filter */
20
+ private $wpmlQueryFilter;
21
+ /** @var ProductSaveActions $productSaveActions */
22
+ private $productSaveActions;
23
+
24
+ public function __construct(
25
+ \Sitepress $sitepress,
26
+ \WPML_Post_Translation $wpmlPostTranslations,
27
+ \WPML_Query_Filter $wpmlQueryFilter,
28
+ ProductSaveActions $productSaveActions
29
+ ) {
30
+ $this->sitepress = $sitepress;
31
+ $this->wpmlPostTranslations = $wpmlPostTranslations;
32
+ $this->wpmlQueryFilter = $wpmlQueryFilter;
33
+ $this->productSaveActions = $productSaveActions;
34
+ }
35
+
36
+ /**
37
+ * @param array $args
38
+ * @param \WP_REST_Request $request Request object.
39
+ *
40
+ * @return array
41
+ */
42
+ public function query( $args, $request ) {
43
+ $data = $request->get_params();
44
+ if ( isset( $data['lang'] ) && $data['lang'] === 'all' ) {
45
+ remove_filter( 'posts_join', [ $this->wpmlQueryFilter, 'posts_join_filter' ] );
46
+ remove_filter( 'posts_where', [ $this->wpmlQueryFilter, 'posts_where_filter' ] );
47
+ }
48
+
49
+ return $args;
50
+ }
51
+
52
+
53
+ /**
54
+ * Appends the language and translation information to the get_product response
55
+ *
56
+ * @param \WP_REST_Response $response
57
+ * @param object $object
58
+ * @param \WP_REST_Request $request
59
+ *
60
+ * @return \WP_REST_Response
61
+ */
62
+ public function prepare( $response, $object, $request ) {
63
+ $response->data['translations'] = array();
64
+
65
+ $trid = $this->wpmlPostTranslations->get_element_trid( $response->data['id'] );
66
+
67
+ if ( $trid ) {
68
+ $translations = $this->wpmlPostTranslations->get_element_translations( $response->data['id'], $trid );
69
+ foreach ( $translations as $translation ) {
70
+ $response->data['translations'][ $this->wpmlPostTranslations->get_element_lang_code( $translation ) ] = $translation;
71
+ }
72
+ $response->data['lang'] = $this->wpmlPostTranslations->get_element_lang_code( $response->data['id'] );
73
+ }
74
+
75
+ return $response;
76
+ }
77
+
78
+
79
+ /**
80
+ * Sets the product information according to the provided language
81
+ *
82
+ * @param object|\WC_Abstract_Legacy_Product $object
83
+ * @param \WP_REST_Request $request
84
+ * @param bool $creating
85
+ *
86
+ * @throws InvalidLanguage
87
+ * @throws InvalidProduct
88
+ * @throws Generic
89
+ *
90
+ */
91
+ public function insert( $object, $request, $creating ) {
92
+ $getParam = Obj::prop( Fns::__, $request->get_params() );
93
+
94
+ $langCode = $getParam( 'lang' );
95
+ $translationOf = $getParam( 'translation_of' );
96
+ $trid = null;
97
+ $sourceLangCode = null;
98
+
99
+ if ( $langCode ) {
100
+
101
+ if ( ! $this->sitepress->is_active_language( $langCode ) ) {
102
+ throw new InvalidLanguage( $langCode );
103
+ }
104
+
105
+ if ( $translationOf ) {
106
+ $trid = $this->wpmlPostTranslations->get_element_trid( $translationOf );
107
+
108
+ if ( ! $trid ) {
109
+ throw new InvalidProduct( $translationOf );
110
+ }
111
+ }
112
+
113
+ } elseif ( $translationOf ) {
114
+ throw new Generic( __( 'Using "translation_of" requires providing a "lang" parameter too', 'woocommerce-multilingual' ) );
115
+ }
116
+
117
+ $this->productSaveActions->run( $object, $trid, $langCode, $translationOf );
118
+ }
119
+ }
classes/class-woocommerce-wpml.php CHANGED
@@ -184,12 +184,13 @@ class woocommerce_wpml {
184
  $this->currencies = new WCML_Currencies( $this );
185
  $this->currencies->add_hooks();
186
 
 
 
187
  if ( is_admin() || wpml_is_rest_request() ) {
188
  $this->translation_editor = new WCML_Translation_Editor( $this, $sitepress, $wpdb );
189
  $this->translation_editor->add_hooks();
190
  $tp_support = new WCML_TP_Support( $this, $wpdb, new WPML_Element_Translation_Package(), $sitepress->get_setting( 'translation-management', [] ) );
191
  $tp_support->add_hooks();
192
- $this->sync_variations_data = new WCML_Synchronize_Variations_Data( $this, $sitepress, $wpdb );
193
  }
194
 
195
  if ( is_admin() ) {
184
  $this->currencies = new WCML_Currencies( $this );
185
  $this->currencies->add_hooks();
186
 
187
+ $this->sync_variations_data = new WCML_Synchronize_Variations_Data( $this, $sitepress, $wpdb );
188
+
189
  if ( is_admin() || wpml_is_rest_request() ) {
190
  $this->translation_editor = new WCML_Translation_Editor( $this, $sitepress, $wpdb );
191
  $this->translation_editor->add_hooks();
192
  $tp_support = new WCML_TP_Support( $this, $wpdb, new WPML_Element_Translation_Package(), $sitepress->get_setting( 'translation-management', [] ) );
193
  $tp_support->add_hooks();
 
194
  }
195
 
196
  if ( is_admin() ) {
classes/order-property-filter/class-wcml-payment-method-filter.php CHANGED
@@ -1,14 +1,18 @@
1
  <?php
2
 
 
 
3
  class WCML_Payment_Method_Filter {
4
  /** @var array */
5
  private $payment_gateway_cache = [];
6
 
7
- /** @var array */
8
- private $post_type_cache = [];
9
-
10
  public function add_hooks() {
11
- add_filter( 'woocommerce_order_get_payment_method_title', [ $this, 'payment_method_string' ], 10, 2 );
 
 
 
 
 
12
  }
13
 
14
  public function payment_method_string( $title, $object ) {
1
  <?php
2
 
3
+ use WPML\FP\Fns;
4
+
5
  class WCML_Payment_Method_Filter {
6
  /** @var array */
7
  private $payment_gateway_cache = [];
8
 
 
 
 
9
  public function add_hooks() {
10
+ add_filter(
11
+ 'woocommerce_order_get_payment_method_title',
12
+ Fns::withoutRecursion( Fns::identity(), [ $this, 'payment_method_string' ] ),
13
+ 10,
14
+ 2
15
+ );
16
  }
17
 
18
  public function payment_method_string( $title, $object ) {
inc/class-wcml-attributes.php CHANGED
@@ -91,6 +91,7 @@ class WCML_Attributes {
91
  10,
92
  4
93
  );
 
94
  add_filter(
95
  'woocommerce_product_get_default_attributes',
96
  [
@@ -750,7 +751,7 @@ class WCML_Attributes {
750
 
751
  if ( '' === $meta_key && 'product_variation' === get_post_type( $object_id ) ) {
752
 
753
- $cache_key = $this->sitepress->get_current_language() . $object_id;
754
  $cached_value = wp_cache_get( $cache_key, self::CACHE_GROUP_VARIATION );
755
 
756
  if ( $cached_value ) {
@@ -780,7 +781,7 @@ class WCML_Attributes {
780
 
781
  if ( $all_meta ) {
782
  foreach ( $all_meta as $meta_key => $meta_value ) {
783
- if ( 'attribute_' === substr( $meta_key, 0, 10 ) ) {
784
  foreach ( $meta_value as $key => $value ) {
785
  $all_meta[ $meta_key ][ $key ] = $this->get_attribute_term_translation_in_current_language( substr( $meta_key, 10 ), $value );
786
  }
@@ -797,6 +798,34 @@ class WCML_Attributes {
797
 
798
  }
799
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
800
 
801
  /**
802
  * @param array $default_attributes
91
  10,
92
  4
93
  );
94
+ add_action( 'added_post_meta', [ $this, 'invalidateVariationMetaCache' ], 10, 3 );
95
  add_filter(
96
  'woocommerce_product_get_default_attributes',
97
  [
751
 
752
  if ( '' === $meta_key && 'product_variation' === get_post_type( $object_id ) ) {
753
 
754
+ $cache_key = $this->getCacheKey( $object_id );
755
  $cached_value = wp_cache_get( $cache_key, self::CACHE_GROUP_VARIATION );
756
 
757
  if ( $cached_value ) {
781
 
782
  if ( $all_meta ) {
783
  foreach ( $all_meta as $meta_key => $meta_value ) {
784
+ if ( self::isAttributeMeta( $meta_key ) ) {
785
  foreach ( $meta_value as $key => $value ) {
786
  $all_meta[ $meta_key ][ $key ] = $this->get_attribute_term_translation_in_current_language( substr( $meta_key, 10 ), $value );
787
  }
798
 
799
  }
800
 
801
+ /**
802
+ * @param int $mid
803
+ * @param int $objectId
804
+ * @param string $key
805
+ */
806
+ public function invalidateVariationMetaCache( $mid, $objectId, $key ) {
807
+ if ( self::isAttributeMeta( $key ) ) {
808
+ wp_cache_delete( $this->getCacheKey( $objectId ), self::CACHE_GROUP_VARIATION );
809
+ }
810
+ }
811
+
812
+ /**
813
+ * @param string $key
814
+ *
815
+ * @return bool
816
+ */
817
+ private static function isAttributeMeta( $key ) {
818
+ return 'attribute_' === substr( $key, 0, 10 );
819
+ }
820
+
821
+ /**
822
+ * @param int $variationId
823
+ *
824
+ * @return string
825
+ */
826
+ private function getCacheKey( $variationId ) {
827
+ return $this->sitepress->get_current_language() . $variationId;
828
+ }
829
 
830
  /**
831
  * @param array $default_attributes
inc/class-wcml-endpoints.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php
2
 
 
 
3
  class WCML_Endpoints {
4
 
5
  /** @var woocommerce_wpml */
@@ -37,9 +39,6 @@ class WCML_Endpoints {
37
 
38
  add_filter( 'wpml_sl_blacklist_requests', array( $this, 'reserved_requests' ) );
39
  add_filter( 'woocommerce_get_endpoint_url', array( $this, 'filter_get_endpoint_url' ), 10, 4 );
40
- if ( ! is_admin() ) {
41
- add_filter( 'pre_get_posts', array( $this, 'check_if_endpoint_exists' ) );
42
- }
43
  }
44
 
45
  public function migrate_ones_string_translations() {
@@ -200,41 +199,6 @@ class WCML_Endpoints {
200
  return array( $link, $endpoint );
201
  }
202
 
203
- /*
204
- * We need check special case - when you manually put in URL default not translated endpoint it not generated 404 error
205
- */
206
- public function check_if_endpoint_exists( $q ) {
207
- global $wp_query;
208
-
209
- $my_account_id = wc_get_page_id( 'myaccount' );
210
-
211
- $current_id = $q->query_vars['page_id'];
212
- if ( ! $current_id ) {
213
- $current_id = $q->queried_object_id;
214
- }
215
-
216
- if ( ! $q->is_404 && $current_id == $my_account_id && $q->is_page ) {
217
-
218
- $uri_vars = array_filter( explode( '/', $_SERVER['REQUEST_URI'] ) );
219
- $endpoints = WC()->query->get_query_vars();
220
- $endpoint_in_url = urldecode( end( $uri_vars ) );
221
-
222
- $endpoints['shipping'] = urldecode( $this->get_translated_edit_address_slug( 'shipping' ) );
223
- $endpoints['billing'] = urldecode( $this->get_translated_edit_address_slug( 'billing' ) );
224
-
225
- $endpoint_not_pagename = isset( $q->query['pagename'] ) && urldecode( $q->query['pagename'] ) != $endpoint_in_url;
226
- $endpoint_url_not_in_endpoints = ! in_array( $endpoint_in_url, $endpoints );
227
- $uri_vars_not_in_query_vars = ! array_key_exists( urldecode( prev( $uri_vars ) ), $q->query_vars );
228
-
229
- if ( $endpoint_not_pagename && $endpoint_url_not_in_endpoints && is_numeric( $endpoint_in_url ) && $uri_vars_not_in_query_vars ) {
230
- $wp_query->set_404();
231
- status_header( 404 );
232
- include( get_query_template( '404' ) );
233
- die();
234
- }
235
- }
236
- }
237
-
238
  private function get_translated_edit_address_slug( $slug, $language = false ) {
239
 
240
  $strings_language = $this->woocommerce_wpml->strings->get_string_language( $slug, 'woocommerce', 'edit-address-slug: ' . $slug );
1
  <?php
2
 
3
+ use WPML\FP\Obj;
4
+
5
  class WCML_Endpoints {
6
 
7
  /** @var woocommerce_wpml */
39
 
40
  add_filter( 'wpml_sl_blacklist_requests', array( $this, 'reserved_requests' ) );
41
  add_filter( 'woocommerce_get_endpoint_url', array( $this, 'filter_get_endpoint_url' ), 10, 4 );
 
 
 
42
  }
43
 
44
  public function migrate_ones_string_translations() {
199
  return array( $link, $endpoint );
200
  }
201
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  private function get_translated_edit_address_slug( $slug, $language = false ) {
203
 
204
  $strings_language = $this->woocommerce_wpml->strings->get_string_language( $slug, 'woocommerce', 'edit-address-slug: ' . $slug );
inc/currencies/class-wcml-multi-currency-reports.php CHANGED
@@ -73,6 +73,9 @@ class WCML_Multi_Currency_Reports {
73
  $this,
74
  'filter_dashboard_status_widget_sales_query'
75
  ) ); // woocommerce 2.6
 
 
 
76
  }
77
 
78
  }
73
  $this,
74
  'filter_dashboard_status_widget_sales_query'
75
  ) ); // woocommerce 2.6
76
+
77
+ // Temporary fix for dashboard widget (see wcml-3598).
78
+ add_filter( 'woocommerce_admin_disabled', '__return_true' );
79
  }
80
 
81
  }
inc/translation-editor/class-wcml-synchronize-product-data.php CHANGED
@@ -66,8 +66,12 @@ class WCML_Synchronize_Product_Data {
66
 
67
  /**
68
  * This function takes care of synchronizing products
 
 
 
 
69
  */
70
- public function synchronize_products( $post_id, $post ) {
71
  global $pagenow, $wp;
72
 
73
  $original_language = $this->woocommerce_wpml->products->get_original_product_language( $post_id );
@@ -87,15 +91,20 @@ class WCML_Synchronize_Product_Data {
87
  }
88
 
89
  // exceptions.
90
- $ajax_call = ( ! empty( $_POST['icl_ajx_action'] ) && 'make_duplicates' === $_POST['icl_ajx_action'] );
91
- $api_call = ! empty( $wp->query_vars['wc-api-version'] );
92
- $auto_draft = 'auto-draft' === $post->post_status;
93
- $trashing = isset( $_GET['action'] ) && 'trash' === $_GET['action'];
 
 
 
 
 
94
  if (
95
  $post_type !== 'product' ||
96
  empty( $original_product_id ) ||
97
  isset( $_POST['autosave'] ) ||
98
- ( $pagenow !== 'post.php' && $pagenow !== 'post-new.php' && $pagenow !== 'admin.php' && ! $ajax_call && ! $api_call ) ||
99
  $trashing ||
100
  $auto_draft
101
  ) {
66
 
67
  /**
68
  * This function takes care of synchronizing products
69
+ *
70
+ * @param int $post_id
71
+ * @param WP_Post $post
72
+ * @param bool $force_valid_context
73
  */
74
+ public function synchronize_products( $post_id, $post, $force_valid_context = false ) {
75
  global $pagenow, $wp;
76
 
77
  $original_language = $this->woocommerce_wpml->products->get_original_product_language( $post_id );
91
  }
92
 
93
  // exceptions.
94
+ $ajax_call = ( ! empty( $_POST['icl_ajx_action'] ) && 'make_duplicates' === $_POST['icl_ajx_action'] );
95
+ $api_call = ! empty( $wp->query_vars['wc-api-version'] );
96
+ $auto_draft = 'auto-draft' === $post->post_status;
97
+ $trashing = isset( $_GET['action'] ) && 'trash' === $_GET['action'];
98
+ $is_valid_context = $force_valid_context
99
+ || $ajax_call
100
+ || $api_call
101
+ || in_array( $pagenow, [ 'post.php', 'post-new.php', 'admin.php' ], true );
102
+
103
  if (
104
  $post_type !== 'product' ||
105
  empty( $original_product_id ) ||
106
  isset( $_POST['autosave'] ) ||
107
+ ! $is_valid_context ||
108
  $trashing ||
109
  $auto_draft
110
  ) {
readme.txt CHANGED
@@ -4,8 +4,8 @@ Donate link: http://wpml.org/documentation/related-projects/woocommerce-multilin
4
  Tags: CMS, woocommerce, commerce, ecommerce, e-commerce, products, WPML, multilingual, e-shop, shop
5
  License: GPLv2
6
  Requires at least: 4.7
7
- Tested up to: 5.6.1
8
- Stable tag: 4.11.3.1
9
  Requires PHP: 5.6
10
 
11
  Allows running fully multilingual e-commerce sites using WooCommerce and WPML.
@@ -138,10 +138,8 @@ WooCommerce Multilingual is compatible with all major WooCommerce extensions. We
138
 
139
  == Changelog ==
140
 
141
- = 4.11.3 =
142
- * Fixed a regression when a non-logged user switches language and each language has a default currency.
143
- * Fixed a bug in PHP 8 showing a 404 page for endpoints.
144
- * Fixed the "new order" email not sent to all the recipients.
145
 
146
  = 4.11.0 =
147
  * Fixed missing product tabs comming from WooCommerce Tab Manager plugin.
4
  Tags: CMS, woocommerce, commerce, ecommerce, e-commerce, products, WPML, multilingual, e-shop, shop
5
  License: GPLv2
6
  Requires at least: 4.7
7
+ Tested up to: 5.7.1
8
+ Stable tag: 4.11.5
9
  Requires PHP: 5.6
10
 
11
  Allows running fully multilingual e-commerce sites using WooCommerce and WPML.
138
 
139
  == Changelog ==
140
 
141
+ = 4.11.5 =
142
+ * Temporary fix for dashboard widget.
 
 
143
 
144
  = 4.11.0 =
145
  * Fixed missing product tabs comming from WooCommerce Tab Manager plugin.
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit793cca277ed682a0e0123624055ae0e3::getLoader();
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInite2cbce783747da1e56f5a1ab19fdb881::getLoader();
vendor/composer/autoload_classmap.php CHANGED
@@ -51,15 +51,15 @@ return array(
51
  'WCML\\Rest\\Functions' => $baseDir . '/classes/Rest/Functions.php',
52
  'WCML\\Rest\\Generic' => $baseDir . '/classes/Rest/Generic.php',
53
  'WCML\\Rest\\Hooks' => $baseDir . '/classes/Rest/Hooks.php',
 
 
54
  'WCML\\Rest\\Wrapper\\Composite' => $baseDir . '/classes/Rest/Wrapper/Composite .php',
55
  'WCML\\Rest\\Wrapper\\Factory' => $baseDir . '/classes/Rest/Wrapper/Factory.php',
56
  'WCML\\Rest\\Wrapper\\Handler' => $baseDir . '/classes/Rest/Wrapper/Handler.php',
57
  'WCML\\Rest\\Wrapper\\Orders\\Languages' => $baseDir . '/classes/Rest/Wrapper/Orders/Languages.php',
58
  'WCML\\Rest\\Wrapper\\Orders\\Prices' => $baseDir . '/classes/Rest/Wrapper/Orders/Prices.php',
59
  'WCML\\Rest\\Wrapper\\ProductTerms' => $baseDir . '/classes/Rest/Wrapper/ProductTerms.php',
60
- 'WCML\\Rest\\Wrapper\\Products\\Images' => $baseDir . '/classes/Rest/Wrapper/Products/Images.php',
61
- 'WCML\\Rest\\Wrapper\\Products\\Languages' => $baseDir . '/classes/Rest/Wrapper/Products/Languages.php',
62
- 'WCML\\Rest\\Wrapper\\Products\\Prices' => $baseDir . '/classes/Rest/Wrapper/Products/Prices.php',
63
  'WCML\\Rest\\Wrapper\\Reports\\ProductsCount' => $baseDir . '/classes/Rest/Wrapper/Reports/ProductsCount.php',
64
  'WCML\\Rest\\Wrapper\\Reports\\ProductsSales' => $baseDir . '/classes/Rest/Wrapper/Reports/ProductsSales.php',
65
  'WCML\\Rest\\Wrapper\\Reports\\TopSeller' => $baseDir . '/classes/Rest/Wrapper/Reports/TopSeller.php',
51
  'WCML\\Rest\\Functions' => $baseDir . '/classes/Rest/Functions.php',
52
  'WCML\\Rest\\Generic' => $baseDir . '/classes/Rest/Generic.php',
53
  'WCML\\Rest\\Hooks' => $baseDir . '/classes/Rest/Hooks.php',
54
+ 'WCML\\Rest\\Language\\Set' => $baseDir . '/classes/Rest/Language/Set.php',
55
+ 'WCML\\Rest\\ProductSaveActions' => $baseDir . '/classes/Rest/ProductSaveActions.php',
56
  'WCML\\Rest\\Wrapper\\Composite' => $baseDir . '/classes/Rest/Wrapper/Composite .php',
57
  'WCML\\Rest\\Wrapper\\Factory' => $baseDir . '/classes/Rest/Wrapper/Factory.php',
58
  'WCML\\Rest\\Wrapper\\Handler' => $baseDir . '/classes/Rest/Wrapper/Handler.php',
59
  'WCML\\Rest\\Wrapper\\Orders\\Languages' => $baseDir . '/classes/Rest/Wrapper/Orders/Languages.php',
60
  'WCML\\Rest\\Wrapper\\Orders\\Prices' => $baseDir . '/classes/Rest/Wrapper/Orders/Prices.php',
61
  'WCML\\Rest\\Wrapper\\ProductTerms' => $baseDir . '/classes/Rest/Wrapper/ProductTerms.php',
62
+ 'WCML\\Rest\\Wrapper\\Products\\Products' => $baseDir . '/classes/Rest/Wrapper/Products/Products.php',
 
 
63
  'WCML\\Rest\\Wrapper\\Reports\\ProductsCount' => $baseDir . '/classes/Rest/Wrapper/Reports/ProductsCount.php',
64
  'WCML\\Rest\\Wrapper\\Reports\\ProductsSales' => $baseDir . '/classes/Rest/Wrapper/Reports/ProductsSales.php',
65
  'WCML\\Rest\\Wrapper\\Reports\\TopSeller' => $baseDir . '/classes/Rest/Wrapper/Reports/TopSeller.php',
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit793cca277ed682a0e0123624055ae0e3
6
  {
7
  private static $loader;
8
 
@@ -22,15 +22,15 @@ class ComposerAutoloaderInit793cca277ed682a0e0123624055ae0e3
22
  return self::$loader;
23
  }
24
 
25
- spl_autoload_register(array('ComposerAutoloaderInit793cca277ed682a0e0123624055ae0e3', 'loadClassLoader'), true, true);
26
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
27
- spl_autoload_unregister(array('ComposerAutoloaderInit793cca277ed682a0e0123624055ae0e3', 'loadClassLoader'));
28
 
29
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
30
  if ($useStaticLoader) {
31
  require_once __DIR__ . '/autoload_static.php';
32
 
33
- call_user_func(\Composer\Autoload\ComposerStaticInit793cca277ed682a0e0123624055ae0e3::getInitializer($loader));
34
  } else {
35
  $map = require __DIR__ . '/autoload_namespaces.php';
36
  foreach ($map as $namespace => $path) {
@@ -51,19 +51,19 @@ class ComposerAutoloaderInit793cca277ed682a0e0123624055ae0e3
51
  $loader->register(true);
52
 
53
  if ($useStaticLoader) {
54
- $includeFiles = Composer\Autoload\ComposerStaticInit793cca277ed682a0e0123624055ae0e3::$files;
55
  } else {
56
  $includeFiles = require __DIR__ . '/autoload_files.php';
57
  }
58
  foreach ($includeFiles as $fileIdentifier => $file) {
59
- composerRequire793cca277ed682a0e0123624055ae0e3($fileIdentifier, $file);
60
  }
61
 
62
  return $loader;
63
  }
64
  }
65
 
66
- function composerRequire793cca277ed682a0e0123624055ae0e3($fileIdentifier, $file)
67
  {
68
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
69
  require $file;
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInite2cbce783747da1e56f5a1ab19fdb881
6
  {
7
  private static $loader;
8
 
22
  return self::$loader;
23
  }
24
 
25
+ spl_autoload_register(array('ComposerAutoloaderInite2cbce783747da1e56f5a1ab19fdb881', 'loadClassLoader'), true, true);
26
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
27
+ spl_autoload_unregister(array('ComposerAutoloaderInite2cbce783747da1e56f5a1ab19fdb881', 'loadClassLoader'));
28
 
29
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
30
  if ($useStaticLoader) {
31
  require_once __DIR__ . '/autoload_static.php';
32
 
33
+ call_user_func(\Composer\Autoload\ComposerStaticInite2cbce783747da1e56f5a1ab19fdb881::getInitializer($loader));
34
  } else {
35
  $map = require __DIR__ . '/autoload_namespaces.php';
36
  foreach ($map as $namespace => $path) {
51
  $loader->register(true);
52
 
53
  if ($useStaticLoader) {
54
+ $includeFiles = Composer\Autoload\ComposerStaticInite2cbce783747da1e56f5a1ab19fdb881::$files;
55
  } else {
56
  $includeFiles = require __DIR__ . '/autoload_files.php';
57
  }
58
  foreach ($includeFiles as $fileIdentifier => $file) {
59
+ composerRequiree2cbce783747da1e56f5a1ab19fdb881($fileIdentifier, $file);
60
  }
61
 
62
  return $loader;
63
  }
64
  }
65
 
66
+ function composerRequiree2cbce783747da1e56f5a1ab19fdb881($fileIdentifier, $file)
67
  {
68
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
69
  require $file;
vendor/composer/autoload_static.php CHANGED
@@ -4,7 +4,7 @@
4
 
5
  namespace Composer\Autoload;
6
 
7
- class ComposerStaticInit793cca277ed682a0e0123624055ae0e3
8
  {
9
  public static $files = array (
10
  'b45b351e6b6f7487d819961fef2fda77' => __DIR__ . '/..' . '/jakeasmith/http_build_url/src/http_build_url.php',
@@ -70,15 +70,15 @@ class ComposerStaticInit793cca277ed682a0e0123624055ae0e3
70
  'WCML\\Rest\\Functions' => __DIR__ . '/../..' . '/classes/Rest/Functions.php',
71
  'WCML\\Rest\\Generic' => __DIR__ . '/../..' . '/classes/Rest/Generic.php',
72
  'WCML\\Rest\\Hooks' => __DIR__ . '/../..' . '/classes/Rest/Hooks.php',
 
 
73
  'WCML\\Rest\\Wrapper\\Composite' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Composite .php',
74
  'WCML\\Rest\\Wrapper\\Factory' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Factory.php',
75
  'WCML\\Rest\\Wrapper\\Handler' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Handler.php',
76
  'WCML\\Rest\\Wrapper\\Orders\\Languages' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Orders/Languages.php',
77
  'WCML\\Rest\\Wrapper\\Orders\\Prices' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Orders/Prices.php',
78
  'WCML\\Rest\\Wrapper\\ProductTerms' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/ProductTerms.php',
79
- 'WCML\\Rest\\Wrapper\\Products\\Images' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Products/Images.php',
80
- 'WCML\\Rest\\Wrapper\\Products\\Languages' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Products/Languages.php',
81
- 'WCML\\Rest\\Wrapper\\Products\\Prices' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Products/Prices.php',
82
  'WCML\\Rest\\Wrapper\\Reports\\ProductsCount' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Reports/ProductsCount.php',
83
  'WCML\\Rest\\Wrapper\\Reports\\ProductsSales' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Reports/ProductsSales.php',
84
  'WCML\\Rest\\Wrapper\\Reports\\TopSeller' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Reports/TopSeller.php',
@@ -282,9 +282,9 @@ class ComposerStaticInit793cca277ed682a0e0123624055ae0e3
282
  public static function getInitializer(ClassLoader $loader)
283
  {
284
  return \Closure::bind(function () use ($loader) {
285
- $loader->prefixLengthsPsr4 = ComposerStaticInit793cca277ed682a0e0123624055ae0e3::$prefixLengthsPsr4;
286
- $loader->prefixDirsPsr4 = ComposerStaticInit793cca277ed682a0e0123624055ae0e3::$prefixDirsPsr4;
287
- $loader->classMap = ComposerStaticInit793cca277ed682a0e0123624055ae0e3::$classMap;
288
 
289
  }, null, ClassLoader::class);
290
  }
4
 
5
  namespace Composer\Autoload;
6
 
7
+ class ComposerStaticInite2cbce783747da1e56f5a1ab19fdb881
8
  {
9
  public static $files = array (
10
  'b45b351e6b6f7487d819961fef2fda77' => __DIR__ . '/..' . '/jakeasmith/http_build_url/src/http_build_url.php',
70
  'WCML\\Rest\\Functions' => __DIR__ . '/../..' . '/classes/Rest/Functions.php',
71
  'WCML\\Rest\\Generic' => __DIR__ . '/../..' . '/classes/Rest/Generic.php',
72
  'WCML\\Rest\\Hooks' => __DIR__ . '/../..' . '/classes/Rest/Hooks.php',
73
+ 'WCML\\Rest\\Language\\Set' => __DIR__ . '/../..' . '/classes/Rest/Language/Set.php',
74
+ 'WCML\\Rest\\ProductSaveActions' => __DIR__ . '/../..' . '/classes/Rest/ProductSaveActions.php',
75
  'WCML\\Rest\\Wrapper\\Composite' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Composite .php',
76
  'WCML\\Rest\\Wrapper\\Factory' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Factory.php',
77
  'WCML\\Rest\\Wrapper\\Handler' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Handler.php',
78
  'WCML\\Rest\\Wrapper\\Orders\\Languages' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Orders/Languages.php',
79
  'WCML\\Rest\\Wrapper\\Orders\\Prices' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Orders/Prices.php',
80
  'WCML\\Rest\\Wrapper\\ProductTerms' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/ProductTerms.php',
81
+ 'WCML\\Rest\\Wrapper\\Products\\Products' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Products/Products.php',
 
 
82
  'WCML\\Rest\\Wrapper\\Reports\\ProductsCount' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Reports/ProductsCount.php',
83
  'WCML\\Rest\\Wrapper\\Reports\\ProductsSales' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Reports/ProductsSales.php',
84
  'WCML\\Rest\\Wrapper\\Reports\\TopSeller' => __DIR__ . '/../..' . '/classes/Rest/Wrapper/Reports/TopSeller.php',
282
  public static function getInitializer(ClassLoader $loader)
283
  {
284
  return \Closure::bind(function () use ($loader) {
285
+ $loader->prefixLengthsPsr4 = ComposerStaticInite2cbce783747da1e56f5a1ab19fdb881::$prefixLengthsPsr4;
286
+ $loader->prefixDirsPsr4 = ComposerStaticInite2cbce783747da1e56f5a1ab19fdb881::$prefixDirsPsr4;
287
+ $loader->classMap = ComposerStaticInite2cbce783747da1e56f5a1ab19fdb881::$classMap;
288
 
289
  }, null, ClassLoader::class);
290
  }
wpml-woocommerce.php CHANGED
@@ -7,11 +7,11 @@
7
  * Author URI: http://www.onthegosystems.com/
8
  * Text Domain: woocommerce-multilingual
9
  * Requires at least: 4.7
10
- * Tested up to: 5.6.1
11
- * Version: 4.11.3
12
  * Plugin Slug: woocommerce-multilingual
13
- * WC requires at least: 3.3.0
14
- * WC tested up to: 5.1
15
  *
16
  * @package WCML
17
  * @author OnTheGoSystems
@@ -33,7 +33,7 @@ if ( ! $wpml_php_version_check->is_ok() ) {
33
  return;
34
  }
35
 
36
- define( 'WCML_VERSION', '4.11.3' );
37
  define( 'WCML_PLUGIN_PATH', dirname( __FILE__ ) );
38
  define( 'WCML_PLUGIN_FOLDER', basename( WCML_PLUGIN_PATH ) );
39
  define( 'WCML_LOCALE_PATH', WCML_PLUGIN_PATH . '/locale' );
7
  * Author URI: http://www.onthegosystems.com/
8
  * Text Domain: woocommerce-multilingual
9
  * Requires at least: 4.7
10
+ * Tested up to: 5.7.1
11
+ * Version: 4.11.5
12
  * Plugin Slug: woocommerce-multilingual
13
+ * WC requires at least: 3.9.0
14
+ * WC tested up to: 5.2
15
  *
16
  * @package WCML
17
  * @author OnTheGoSystems
33
  return;
34
  }
35
 
36
+ define( 'WCML_VERSION', '4.11.5' );
37
  define( 'WCML_PLUGIN_PATH', dirname( __FILE__ ) );
38
  define( 'WCML_PLUGIN_FOLDER', basename( WCML_PLUGIN_PATH ) );
39
  define( 'WCML_LOCALE_PATH', WCML_PLUGIN_PATH . '/locale' );