WooCommerce Multilingual – run WooCommerce with WPML - Version 3.9.5

Version Description

  • Fixed a bug introduced in 3.9.4 that prevented translating variable products
Download this release

Release Info

Developer mihaimihai
Plugin Icon 128x128 WooCommerce Multilingual – run WooCommerce with WPML
Version 3.9.5
Comparing to
See all releases

Code changes from version 3.9.4 to 3.9.5

inc/translation-editor/class-wcml-synchronize-product-data.php CHANGED
@@ -1,627 +1,639 @@
1
- <?php
2
-
3
- class WCML_Synchronize_Product_Data{
4
-
5
- private $woocommerce_wpml;
6
- /**
7
- * @var SitePress
8
- */
9
- private $sitepress;
10
- private $wpdb;
11
-
12
- public function __construct( &$woocommerce_wpml, &$sitepress, &$wpdb ) {
13
- $this->woocommerce_wpml = $woocommerce_wpml;
14
- $this->sitepress = $sitepress;
15
- $this->wpdb = $wpdb;
16
-
17
- if( is_admin() ){
18
- // filters to sync variable products
19
- add_action( 'save_post', array( $this, 'sync_post_action' ), 110, 2 ); // After WPML
20
-
21
- add_action( 'icl_pro_translation_completed', array( $this, 'icl_pro_translation_completed' ) );
22
-
23
- add_filter( 'icl_make_duplicate', array( $this, 'icl_make_duplicate'), 110, 4 );
24
- add_action( 'woocommerce_duplicate_product', array( $this, 'woocommerce_duplicate_product' ), 10, 2 );
25
-
26
- //quick & bulk edit
27
- add_action( 'woocommerce_product_quick_edit_save', array( $this, 'woocommerce_product_quick_edit_save' ) );
28
- add_action( 'woocommerce_product_bulk_edit_save', array( $this, 'woocommerce_product_quick_edit_save' ) );
29
-
30
- add_action( 'init', array( $this, 'init' ) );
31
-
32
- add_action( 'deleted_term_relationships', array( $this, 'delete_term_relationships_update_term_count' ), 10, 2 );
33
- }
34
-
35
- add_action( 'woocommerce_reduce_order_stock', array( $this, 'sync_product_stocks_reduce' ) );
36
- add_action( 'woocommerce_restore_order_stock', array( $this, 'sync_product_stocks_restore' ) );
37
- add_action( 'woocommerce_product_set_stock_status', array($this, 'sync_stock_status_for_translations' ), 10, 2);
38
- add_action( 'woocommerce_variation_set_stock_status', array($this, 'sync_stock_status_for_translations' ), 10, 2);
39
-
40
- add_filter( 'future_product', array( $this, 'set_schedule_for_translations'), 10, 2 );
41
- }
42
-
43
- public function init(){
44
- $this->check_ajax_actions();
45
- }
46
-
47
- /**
48
- * This function takes care of synchronizing original product
49
- */
50
- public function sync_post_action( $post_id, $post ){
51
- global $pagenow, $wp;
52
-
53
- $original_language = $this->woocommerce_wpml->products->get_original_product_language( $post_id );
54
- $current_language = $this->sitepress->get_current_language();
55
- $duplicated_post_id = apply_filters( 'translate_object_id', $post_id, 'product', false, $original_language );
56
-
57
- $wpml_media_options = maybe_unserialize( get_option( '_wpml_media' ) );
58
-
59
- if( $wpml_media_options[ 'new_content_settings' ][ 'duplicate_media' ] ){
60
- //sync product gallery
61
- $this->woocommerce_wpml->media->sync_product_gallery( $duplicated_post_id );
62
- }
63
- // check its a product
64
- $post_type = get_post_type( $post_id );
65
- //set trid for variations
66
- if ( $post_type == 'product_variation' ) {
67
- $var_lang = $this->sitepress->get_language_for_element( wp_get_post_parent_id( $post_id ), 'post_product' );
68
- if( $this->woocommerce_wpml->products->is_original_product( wp_get_post_parent_id( $post_id ) ) ){
69
- $this->sitepress->set_element_language_details( $post_id, 'post_product_variation', false, $var_lang );
70
- }
71
- }
72
-
73
- if ( $post_type != 'product' ) {
74
- return;
75
- }
76
-
77
- // exceptions
78
- $ajax_call = ( !empty( $_POST[ 'icl_ajx_action' ] ) && $_POST[ 'icl_ajx_action' ] == 'make_duplicates' );
79
- $api_call = !empty( $wp->query_vars['wc-api-version'] );
80
- if ( ( empty( $duplicated_post_id ) || isset( $_POST[ 'autosave' ] ) ) ||
81
- ( $pagenow != 'post.php' && $pagenow != 'post-new.php' && $pagenow != 'admin.php' && !$ajax_call && !$api_call ) ||
82
- ( isset( $_GET[ 'action' ] ) && $_GET[ 'action' ] == 'trash' )
83
- ) {
84
- return;
85
- }
86
- // If we reach this point, we go ahead with sync.
87
- // Remove filter to avoid double sync
88
- remove_action( 'save_post', array( $this, 'sync_post_action' ), 110, 2 );
89
- do_action( 'wcml_before_sync_product', $duplicated_post_id, $post_id );
90
-
91
-
92
- //trnsl_interface option
93
- if ( !$this->woocommerce_wpml->settings['trnsl_interface'] && $original_language != $current_language ) {
94
- if( !isset( $_POST[ 'wp-preview' ] ) || empty( $_POST[ 'wp-preview' ] ) ){
95
- //make sure we sync post in current language
96
- $post_id = apply_filters( 'translate_object_id', $post_id, 'product', false, $current_language );
97
- do_action( 'wcml_before_sync_product_data', $duplicated_post_id, $post_id, $current_language );
98
- $this->sync_date_and_parent( $duplicated_post_id, $post_id, $current_language );
99
- $this->sync_product_data( $duplicated_post_id, $post_id, $current_language );
100
- }
101
- return;
102
- }
103
-
104
- // get language code
105
- $language_details = $this->sitepress->get_element_language_details( $post_id, 'post_product' );
106
- if ( $pagenow == 'admin.php' && empty( $language_details ) ) {
107
- //translation editor support: sidestep icl_translations_cache
108
- $language_details = $this->wpdb->get_row(
109
- $this->wpdb->prepare(
110
- "SELECT element_id, trid, language_code, source_language_code FROM {$this->wpdb->prefix}icl_translations
111
- WHERE element_id = %d AND element_type = 'post_product'",
112
- $post_id )
113
- );
114
- }
115
- if ( empty( $language_details ) ) {
116
- return;
117
- }
118
-
119
- //save files option
120
- $this->woocommerce_wpml->downloadable->save_files_option( $duplicated_post_id );
121
- // pick posts to sync
122
- $traslated_products = array();
123
- $translations = $this->sitepress->get_element_translations( $language_details->trid, 'post_product', false, true );
124
-
125
- foreach ( $translations as $translation ) {
126
- if ( $translation->original ) {
127
- $original_product_id = $translation->element_id;
128
- } else {
129
- $traslated_products[ $translation->element_id ] = $translation;
130
- }
131
- }
132
-
133
- foreach( $traslated_products as $translated_product_id => $translation ) {
134
- $lang = $translation->language_code;
135
- do_action( 'wcml_before_sync_product_data', $original_product_id, $translated_product_id, $lang );
136
- // Filter upsell products, crosell products and default attributes for translations
137
- $this->duplicate_product_post_meta( $original_product_id, $translated_product_id );
138
- if( $wpml_media_options[ 'new_content_settings' ][ 'duplicate_featured' ] ){
139
- //sync feature image
140
- $this->woocommerce_wpml->media->sync_thumbnail_id( $original_product_id, $translated_product_id, $lang );
141
- }
142
- $this->sync_date_and_parent( $original_product_id, $translated_product_id, $lang );
143
- $this->sync_product_taxonomies( $original_product_id, $translated_product_id, $lang );
144
- $this->woocommerce_wpml->attributes->sync_default_product_attr( $original_product_id, $translated_product_id, $lang );
145
- $this->woocommerce_wpml->attributes->sync_product_attr( $original_product_id, $translated_product_id );
146
- $this->woocommerce_wpml->products->update_order_for_product_translations( $original_product_id );
147
- // synchronize post variations
148
- $this->woocommerce_wpml->sync_variations_data->sync_product_variations( $original_product_id, $translated_product_id, $lang );
149
- $this->sync_linked_products( $original_product_id, $translated_product_id, $lang );
150
-
151
- // Clear any unwanted data
152
- wc_delete_product_transients( $translated_product_id );
153
- }
154
- if( $this->woocommerce_wpml->settings['enable_multi_currency'] == WCML_MULTI_CURRENCIES_INDEPENDENT ) {
155
- //save custom prices
156
- $this->woocommerce_wpml->multi_currency->custom_prices->save_custom_prices( $duplicated_post_id );
157
- $this->woocommerce_wpml->multi_currency->custom_prices->sync_product_variations_custom_prices($original_product_id);
158
- }
159
- }
160
-
161
- public function sync_product_data( $original_product_id, $tr_product_id, $lang ){
162
-
163
- $this->duplicate_product_post_meta( $original_product_id, $tr_product_id );
164
-
165
- $this->woocommerce_wpml->attributes->sync_product_attr( $original_product_id, $tr_product_id );
166
-
167
- $this->woocommerce_wpml->attributes->sync_default_product_attr( $original_product_id, $tr_product_id, $lang );
168
-
169
- $wpml_media_options = maybe_unserialize( get_option( '_wpml_media' ) );
170
- //sync media
171
- if( $wpml_media_options[ 'new_content_settings' ][ 'duplicate_featured' ] ){
172
- //sync feature image
173
- $this->woocommerce_wpml->media->sync_thumbnail_id( $original_product_id, $tr_product_id, $lang );
174
- }
175
-
176
- if( $wpml_media_options[ 'new_content_settings' ][ 'duplicate_media' ]){
177
- //sync product gallery
178
- $this->woocommerce_wpml->media->sync_product_gallery( $original_product_id );
179
- }
180
-
181
- //sync taxonomies
182
- $this->sync_product_taxonomies( $original_product_id, $tr_product_id, $lang );
183
-
184
- //duplicate variations
185
-
186
- $this->woocommerce_wpml->sync_variations_data->sync_product_variations( $original_product_id, $tr_product_id, $lang );
187
-
188
- $this->sync_linked_products( $original_product_id, $tr_product_id, $lang );
189
-
190
- // Clear any unwanted data
191
- wc_delete_product_transients( $tr_product_id );
192
- }
193
-
194
- public function sync_product_taxonomies( $original_product_id, $tr_product_id, $lang ){
195
- $taxonomies = get_object_taxonomies( 'product' );
196
-
197
- foreach( $taxonomies as $taxonomy ) {
198
- if( in_array( $taxonomy, array( 'product_cat', 'product_tag' ) ) ) {
199
-
200
- $terms = get_the_terms($original_product_id, $taxonomy);
201
- $tt_ids = array();
202
-
203
- if ($terms) {
204
- foreach ($terms as $term) {
205
- $tt_ids[] = $term->term_id;
206
- }
207
- $this->wcml_update_term_count_by_ids($tt_ids, $lang, $taxonomy);
208
- }
209
- }
210
- }
211
-
212
- }
213
- public function delete_term_relationships_update_term_count( $object_id, $tt_ids ){
214
-
215
- if( get_post_type( $object_id ) == 'product' ){
216
-
217
- $language_details = $this->sitepress->get_element_language_details( $object_id, 'post_product' );
218
- $translations = $this->sitepress->get_element_translations( $language_details->trid, 'post_product', false, true );
219
-
220
- foreach( $translations as $translation ) {
221
- if ( !$translation->original ) {
222
- $this->wcml_update_term_count_by_ids( $tt_ids, $translation->language_code );
223
- }
224
- }
225
- }
226
-
227
- }
228
- public function wcml_update_term_count_by_ids( $tt_ids, $language, $taxonomy = '' ){
229
- $terms_array = array();
230
-
231
- foreach( $tt_ids as $tt_id ){
232
-
233
- $tr_id = apply_filters( 'translate_object_id', $tt_id, 'product_cat', false, $language );
234
-
235
- if( !is_null( $tr_id ) ){
236
- // not using get_term - unfiltered get_term
237
- $translated_term = $this->wpdb->get_row( $this->wpdb->prepare( "
238
- SELECT * FROM {$this->wpdb->terms} t JOIN {$this->wpdb->term_taxonomy} x ON x.term_id = t.term_id WHERE t.term_id = %d", $tr_id ) );
239
- $terms_array[] = $translated_term->term_taxonomy_id;
240
- $taxonomy = $translated_term->taxonomy;
241
- }
242
-
243
- }
244
-
245
- if( is_taxonomy_hierarchical( $taxonomy ) ){
246
- $terms_array = array_unique( array_map( 'intval', $terms_array ) );
247
- }
248
-
249
- $this->sitepress->switch_lang( $language );
250
- wp_update_term_count( $terms_array, $taxonomy );
251
- $this->sitepress->switch_lang( );
252
-
253
- }
254
-
255
-
256
-
257
- public function sync_linked_products( $product_id, $translated_product_id, $lang ){
258
- //sync up-sells
259
- $original_up_sells = maybe_unserialize( get_post_meta( $product_id, '_upsell_ids', true ) );
260
- $trnsl_up_sells = array();
261
- if( $original_up_sells ){
262
- foreach( $original_up_sells as $original_up_sell_product ) {
263
- $trnsl_up_sells[] = apply_filters( 'translate_object_id', $original_up_sell_product, get_post_type( $original_up_sell_product ), false, $lang );
264
- }
265
- }
266
- update_post_meta( $translated_product_id, '_upsell_ids', $trnsl_up_sells );
267
- //sync cross-sells
268
- $original_cross_sells = maybe_unserialize( get_post_meta( $product_id, '_crosssell_ids', true ) );
269
- $trnsl_cross_sells = array();
270
- if( $original_cross_sells )
271
- foreach( $original_cross_sells as $original_cross_sell_product ){
272
- $trnsl_cross_sells[] = apply_filters( 'translate_object_id', $original_cross_sell_product, get_post_type( $original_cross_sell_product ), false, $lang );
273
- }
274
- update_post_meta( $translated_product_id, '_crosssell_ids', $trnsl_cross_sells );
275
- // refresh parent-children transients (e.g. this child goes to private or draft)
276
- $translated_product_parent_id = wp_get_post_parent_id( $translated_product_id );
277
- if ( $translated_product_parent_id ) {
278
- delete_transient( 'wc_product_children_' . $translated_product_parent_id );
279
- delete_transient( '_transient_wc_product_children_ids_' . $translated_product_parent_id );
280
- }
281
- }
282
-
283
-
284
-
285
- public function sync_product_stocks_reduce( $order ){
286
- return $this->sync_product_stocks( $order, 'reduce' );
287
- }
288
-
289
- public function sync_product_stocks_restore( $order ){
290
- return $this->sync_product_stocks( $order, 'restore' );
291
- }
292
-
293
- /**
294
- * @param $order WC_Order
295
- * @param $action
296
- */
297
- public function sync_product_stocks( $order, $action ){
298
- $order_id = method_exists( 'WC_Order', 'get_id' ) ? $order->get_id() : $order->id;
299
-
300
- foreach( $order->get_items() as $item ) {
301
-
302
- if( $item instanceof WC_Order_Item_Product ){
303
- $variation_id = $item->get_variation_id();
304
- $product_id = $item->get_product_id();
305
- $qty = $item->get_quantity();
306
- }else{
307
- $variation_id = isset( $item[ 'variation_id' ] ) ? $item[ 'variation_id' ] : 0;
308
- $product_id = $item[ 'product_id' ];
309
- $qty = $item[ 'qty' ];
310
- }
311
-
312
- if( $variation_id > 0 ){
313
- $trid = $this->sitepress->get_element_trid( $variation_id, 'post_product_variation' );
314
- $translations = $this->sitepress->get_element_translations( $trid, 'post_product_variation' );
315
- $ld = $this->sitepress->get_element_language_details( $variation_id, 'post_product_variation' );
316
- } else {
317
- $trid = $this->sitepress->get_element_trid( $product_id, 'post_product' );
318
- $translations = $this->sitepress->get_element_translations( $trid, 'post_product' );
319
- $ld = $this->sitepress->get_element_language_details( $product_id, 'post_product' );
320
- }
321
-
322
- // Process for non-current languages
323
- foreach( $translations as $translation ){
324
- if ( $ld->language_code != $translation->language_code ) {
325
- //check if product exist
326
- if( get_post_type( $translation->element_id ) == 'product_variation' && !get_post( wp_get_post_parent_id( $translation->element_id ) ) ){
327
- continue;
328
- }
329
- $_product = wc_get_product( $translation->element_id );
330
-
331
- if( $_product && $_product->exists() && $_product->managing_stock() ) {
332
- $total_sales = get_post_meta($_product->id, 'total_sales', true);
333
-
334
- if( $action == 'reduce'){
335
- $stock = $_product->reduce_stock( $qty );
336
- $total_sales += $qty;
337
- }else{
338
- $stock = $_product->increase_stock( $qty );
339
- $total_sales -= $qty;
340
- }
341
- update_post_meta( $translation->element_id, 'total_sales', $total_sales );
342
- }
343
- }
344
- }
345
- }
346
- }
347
-
348
- public function sync_stock_status_for_translations( $id, $status ){
349
-
350
- $type = get_post_type( $id );
351
- $trid = $this->sitepress->get_element_trid( $id, 'post_'.$type );
352
- $translations = $this->sitepress->get_element_translations( $trid, 'post_'.$type, true);
353
-
354
- foreach ( $translations as $translation ) {
355
- if ( !$translation->original ) {
356
- update_post_meta( $translation->element_id, '_stock_status', $status );
357
- }
358
- }
359
- }
360
-
361
-
362
-
363
- //sync product parent & post_status
364
- public function sync_date_and_parent( $duplicated_post_id, $post_id, $lang ){
365
- $tr_parent_id = apply_filters( 'translate_object_id', wp_get_post_parent_id( $duplicated_post_id ), 'product', false, $lang );
366
- $orig_product = get_post( $duplicated_post_id );
367
- $args = array();
368
- $args[ 'post_parent' ] = is_null( $tr_parent_id )? 0 : $tr_parent_id;
369
- //sync product date
370
-
371
- if( !empty( $this->woocommerce_wpml->settings[ 'products_sync_date' ] ) ){
372
- $args[ 'post_date' ] = $orig_product->post_date;
373
- }
374
- $this->wpdb->update(
375
- $this->wpdb->posts,
376
- $args,
377
- array( 'id' => $post_id )
378
- );
379
- }
380
-
381
- public function set_schedule_for_translations( $deprecated, $post ){
382
-
383
- if( $this->woocommerce_wpml->products->is_original_product( $post->ID ) ) {
384
- $trid = $this->sitepress->get_element_trid( $post->ID, 'post_product');
385
- $translations = $this->sitepress->get_element_translations( $trid, 'post_product', true);
386
- foreach( $translations as $translation ) {
387
- if( !$translation->original ){
388
- wp_clear_scheduled_hook( 'publish_future_post', array( $translation->element_id ) );
389
- wp_schedule_single_event( strtotime( get_gmt_from_date( $post->post_date) . ' GMT' ), 'publish_future_post', array( $translation->element_id ) );
390
- }
391
- }
392
- }
393
- }
394
-
395
-
396
-
397
- public function icl_pro_translation_completed( $tr_product_id ){
398
- $trid = $this->sitepress->get_element_trid( $tr_product_id, 'post_product' );
399
- $translations = $this->sitepress->get_element_translations( $trid, 'post_product' );
400
-
401
- foreach( $translations as $translation ){
402
- if( $translation->original ){
403
- $original_product_id = $translation->element_id;
404
- }
405
- }
406
-
407
- if( !isset( $original_product_id ) ){
408
- return;
409
- }
410
-
411
- $lang = $this->sitepress->get_language_for_element( $tr_product_id, 'post_product' );
412
- $this->sync_product_data( $original_product_id, $tr_product_id, $lang );
413
- }
414
-
415
- public function icl_make_duplicate( $master_post_id, $lang, $postarr, $id ){
416
- if( get_post_type( $master_post_id ) == 'product' ){
417
-
418
- $original_language = $this->woocommerce_wpml->products->get_original_product_language( $master_post_id );
419
- $master_post_id = apply_filters( 'translate_object_id', $master_post_id, 'product', false, $original_language );
420
-
421
- $this->sync_product_data( $master_post_id, $id, $lang );
422
- }
423
- }
424
-
425
- public function woocommerce_product_quick_edit_save( $product ){
426
- $is_original = $this->woocommerce_wpml->products->is_original_product( $product->id );
427
- $trid = $this->sitepress->get_element_trid( $product->id, 'post_product' );
428
-
429
- if( $trid ){
430
- $translations = $this->sitepress->get_element_translations( $trid, 'post_product' );
431
- if( $translations ){
432
- foreach( $translations as $translation ){
433
- if( $is_original ){
434
- if( !$translation->original ){
435
- $this->sync_product_data( $product->id, $translation->element_id, $translation->language_code );
436
- $this->sync_date_and_parent( $product->id, $translation->element_id, $translation->language_code );
437
- }
438
- }elseif( $translation->original ){
439
- $this->sync_product_data( $translation->element_id, $product->id, $this->sitepress->get_language_for_element( $product->id, 'post_product' ) );
440
- $this->sync_date_and_parent( $translation->element_id, $product->id, $this->sitepress->get_language_for_element( $product->id, 'post_product' ) );
441
- }
442
- }
443
- }
444
- }
445
- }
446
-
447
-
448
-
449
- //duplicate product post meta
450
- public function duplicate_product_post_meta( $original_product_id, $trnsl_product_id, $data = false ){
451
- global $iclTranslationManagement;
452
- $settings = $iclTranslationManagement->settings[ 'custom_fields_translation' ];
453
- $all_meta = get_post_custom( $original_product_id );
454
- $post_fields = null;
455
-
456
- unset( $all_meta[ '_thumbnail_id' ] );
457
-
458
- foreach ( $all_meta as $key => $meta ) {
459
- if ( !isset( $settings[ $key ] ) || $settings[ $key ] == WPML_IGNORE_CUSTOM_FIELD ) {
460
- continue;
461
- }
462
- foreach ( $meta as $meta_value ) {
463
- if( $key == '_downloadable_files' ){
464
- $this->woocommerce_wpml->downloadable->sync_files_to_translations( $original_product_id, $trnsl_product_id, $data );
465
- }elseif ( $data ) {
466
- if ( isset( $settings[ $key ] ) && $settings[ $key ] == WPML_TRANSLATE_CUSTOM_FIELD ) {
467
-
468
- $post_fields = $this->sync_custom_field_value( $key, $data, $trnsl_product_id, $post_fields );
469
- }
470
- }
471
- }
472
- }
473
-
474
- do_action( 'wcml_after_duplicate_product_post_meta', $original_product_id, $trnsl_product_id, $data );
475
- }
476
-
477
- public function sync_custom_field_value( $custom_field, $translation_data, $trnsl_product_id, $post_fields, $original_product_id = false, $is_variation = false ){
478
-
479
- if( is_null( $post_fields ) ){
480
- $post_fields = array();
481
- if( isset( $_POST['data'] ) ){
482
- $post_args = wp_parse_args( $_POST['data'] );
483
- $post_fields = $post_args[ 'fields' ];
484
- }
485
- }
486
-
487
-
488
- $custom_filed_key = $is_variation && $original_product_id ? $custom_field.$original_product_id : $custom_field;
489
-
490
- if( isset( $translation_data[ md5( $custom_filed_key ) ] ) ){
491
- $meta_value = $translation_data[ md5( $custom_filed_key ) ];
492
- $meta_value = apply_filters( 'wcml_meta_value_before_add', $meta_value, $custom_filed_key );
493
- update_post_meta( $trnsl_product_id, $custom_field, $meta_value );
494
- unset( $post_fields[ $custom_filed_key ] );
495
- }else{
496
- foreach( $post_fields as $post_field_key => $post_field ){
497
- $meta_value = $translation_data[ md5( $post_field_key ) ];
498
- $field_key = explode( ':', $post_field_key );
499
- if( $field_key[0] == $custom_filed_key ){
500
- if( substr( $field_key[1], 0, 3 ) == 'new' ){
501
- add_post_meta( $trnsl_product_id, $custom_field, $meta_value );
502
- }else{
503
- update_meta( $field_key[1], $custom_field, $meta_value );
504
- }
505
- unset( $post_fields[ $post_field_key ] );
506
- }
507
- }
508
- }
509
-
510
- return $post_fields;
511
- }
512
-
513
- public function woocommerce_duplicate_product( $new_id, $post ){
514
- $duplicated_products = array();
515
-
516
- //duplicate original first
517
- $trid = $this->sitepress->get_element_trid( $post->ID, 'post_' . $post->post_type );
518
- $orig_id = $this->sitepress->get_original_element_id_by_trid( $trid );
519
- $orig_lang = $this->woocommerce_wpml->products->get_original_product_language( $post->ID );
520
-
521
- $wc_admin = new WC_Admin_Duplicate_Product();
522
-
523
- if( $orig_id == $post->ID ){
524
- $this->sitepress->set_element_language_details( $new_id, 'post_' . $post->post_type, false, $orig_lang );
525
- $new_trid = $this->sitepress->get_element_trid( $new_id, 'post_' . $post->post_type );
526
- $new_orig_id = $new_id;
527
- }else{
528
- $post_to_duplicate = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT * FROM {$this->wpdb->posts} WHERE ID=%d", $orig_id ) );
529
- if ( ! empty( $post_to_duplicate ) ) {
530
- $new_orig_id = $wc_admin->duplicate_product( $post_to_duplicate );
531
- do_action( 'wcml_after_duplicate_product' , $new_id, $post_to_duplicate );
532
- $this->sitepress->set_element_language_details( $new_orig_id, 'post_' . $post->post_type, false, $orig_lang );
533
- $new_trid = $this->sitepress->get_element_trid( $new_orig_id, 'post_' . $post->post_type );
534
- if( get_post_meta( $orig_id, '_icl_lang_duplicate_of' ) ){
535
- update_post_meta( $new_id, '_icl_lang_duplicate_of', $new_orig_id );
536
- }
537
- $this->sitepress->set_element_language_details( $new_id, 'post_' . $post->post_type, $new_trid, $this->sitepress->get_current_language() );
538
- }
539
- }
540
-
541
- // Set language info for variations
542
- if ( $children_products = get_children( 'post_parent=' . $new_orig_id . '&post_type=product_variation' ) ) {
543
- foreach ( $children_products as $child ) {
544
- $this->sitepress->set_element_language_details( $child->ID, 'post_product_variation', false, $orig_lang );
545
- }
546
- }
547
-
548
- $translations = $this->sitepress->get_element_translations( $trid, 'post_' . $post->post_type );
549
- $duplicated_products[ 'translations' ] = array();
550
- if( $translations ){
551
- foreach( $translations as $translation ){
552
- if( !$translation->original && $translation->element_id != $post->ID ){
553
- $post_to_duplicate = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT * FROM {$this->wpdb->posts} WHERE ID=%d", $translation->element_id ) );
554
-
555
- if( ! empty( $post_to_duplicate ) ) {
556
- $new_id = $wc_admin->duplicate_product( $post_to_duplicate );
557
- $new_id_obj = get_post( $new_id );
558
- $new_slug = wp_unique_post_slug( sanitize_title( $new_id_obj->post_title ), $new_id, $post_to_duplicate->post_status, $post_to_duplicate->post_type, $new_id_obj->post_parent );
559
-
560
- $this->wpdb->update(
561
- $this->wpdb->posts,
562
- array(
563
- 'post_name' => $new_slug,
564
- 'post_status' => 'draft'
565
- ),
566
- array( 'ID' => $new_id )
567
- );
568
-
569
- do_action( 'wcml_after_duplicate_product' , $new_id, $post_to_duplicate );
570
- $this->sitepress->set_element_language_details( $new_id, 'post_' . $post->post_type, $new_trid, $translation->language_code );
571
- if( get_post_meta( $translation->element_id, '_icl_lang_duplicate_of' ) ){
572
- update_post_meta( $new_id, '_icl_lang_duplicate_of', $new_orig_id );
573
- }
574
- $duplicated_products[ 'translations' ][] = $new_id;
575
- }
576
- }
577
- }
578
- }
579
-
580
- $duplicated_products[ 'original' ] = $new_orig_id;
581
-
582
- return $duplicated_products;
583
- }
584
-
585
- public function check_ajax_actions(){
586
- if( isset( $_POST[ 'icl_ajx_action' ] ) && $_POST[ 'icl_ajx_action' ] == 'connect_translations' ){
587
- $this->icl_connect_translations_action();
588
- }
589
- }
590
-
591
- public function icl_connect_translations_action(){
592
- $new_trid = $_POST['new_trid'];
593
- $post_type = $_POST['post_type'];
594
- $post_id = $_POST['post_id'];
595
- $set_as_source = $_POST['set_as_source'];
596
- if( $post_type == 'product' ){
597
-
598
- $translations = $this->sitepress->get_element_translations( $new_trid, 'post_' . $post_type );
599
- if( $translations ) {
600
- foreach ( $translations as $translation ) {
601
- //current as original need sync translation
602
- if ( $translation->original ) {
603
- if( $set_as_source ) {
604
- $orig_id = $post_id;
605
- $trnsl_id = $translation->element_id;
606
- $lang = $translation->language_code;
607
- }else{
608
- $orig_id = $translation->element_id;
609
- $trnsl_id = $post_id;
610
- $lang = $this->sitepress->get_current_language();
611
- }
612
- $this->sync_product_data( $orig_id, $trnsl_id, $lang );
613
- $this->sync_date_and_parent( $orig_id, $trnsl_id, $lang );
614
- $this->sitepress->copy_custom_fields( $orig_id, $trnsl_id );
615
- }else {
616
- if( $set_as_source ) {
617
- $this->sync_product_data( $post_id, $translation->element_id, $translation->language_code );
618
- $this->sync_date_and_parent( $post_id, $translation->element_id, $translation->language_code );
619
- $this->sitepress->copy_custom_fields( $post_id, $translation->element_id );
620
- }
621
- }
622
- }
623
- }
624
- }
625
- }
626
-
 
 
 
 
 
 
 
 
 
 
 
 
627
  }
1
+ <?php
2
+
3
+ class WCML_Synchronize_Product_Data{
4
+
5
+ private $woocommerce_wpml;
6
+ /**
7
+ * @var SitePress
8
+ */
9
+ private $sitepress;
10
+ private $wpdb;
11
+
12
+ public function __construct( &$woocommerce_wpml, &$sitepress, &$wpdb ) {
13
+ $this->woocommerce_wpml = $woocommerce_wpml;
14
+ $this->sitepress = $sitepress;
15
+ $this->wpdb = $wpdb;
16
+
17
+ if( is_admin() ){
18
+ // filters to sync variable products
19
+ add_action( 'save_post', array( $this, 'sync_post_action' ), 110, 2 ); // After WPML
20
+
21
+ add_action( 'icl_pro_translation_completed', array( $this, 'icl_pro_translation_completed' ) );
22
+
23
+ add_filter( 'icl_make_duplicate', array( $this, 'icl_make_duplicate'), 110, 4 );
24
+ add_action( 'woocommerce_duplicate_product', array( $this, 'woocommerce_duplicate_product' ), 10, 2 );
25
+
26
+ //quick & bulk edit
27
+ add_action( 'woocommerce_product_quick_edit_save', array( $this, 'woocommerce_product_quick_edit_save' ) );
28
+ add_action( 'woocommerce_product_bulk_edit_save', array( $this, 'woocommerce_product_quick_edit_save' ) );
29
+
30
+ add_action( 'init', array( $this, 'init' ) );
31
+
32
+ add_action( 'deleted_term_relationships', array( $this, 'delete_term_relationships_update_term_count' ), 10, 2 );
33
+ }
34
+
35
+ add_action( 'woocommerce_reduce_order_stock', array( $this, 'sync_product_stocks_reduce' ) );
36
+ add_action( 'woocommerce_restore_order_stock', array( $this, 'sync_product_stocks_restore' ) );
37
+ add_action( 'woocommerce_product_set_stock_status', array($this, 'sync_stock_status_for_translations' ), 10, 2);
38
+ add_action( 'woocommerce_variation_set_stock_status', array($this, 'sync_stock_status_for_translations' ), 10, 2);
39
+
40
+ add_filter( 'future_product', array( $this, 'set_schedule_for_translations'), 10, 2 );
41
+ }
42
+
43
+ public function init(){
44
+ $this->check_ajax_actions();
45
+ }
46
+
47
+ /**
48
+ * This function takes care of synchronizing original product
49
+ */
50
+ public function sync_post_action( $post_id, $post ){
51
+ global $pagenow, $wp;
52
+
53
+ $original_language = $this->woocommerce_wpml->products->get_original_product_language( $post_id );
54
+ $current_language = $this->sitepress->get_current_language();
55
+ $duplicated_post_id = apply_filters( 'translate_object_id', $post_id, 'product', false, $original_language );
56
+
57
+ $wpml_media_options = maybe_unserialize( get_option( '_wpml_media' ) );
58
+
59
+ if( $wpml_media_options[ 'new_content_settings' ][ 'duplicate_media' ] ){
60
+ //sync product gallery
61
+ $this->woocommerce_wpml->media->sync_product_gallery( $duplicated_post_id );
62
+ }
63
+ // check its a product
64
+ $post_type = get_post_type( $post_id );
65
+ //set trid for variations
66
+ if ( $post_type == 'product_variation' ) {
67
+ $var_lang = $this->sitepress->get_language_for_element( wp_get_post_parent_id( $post_id ), 'post_product' );
68
+ if( $this->woocommerce_wpml->products->is_original_product( wp_get_post_parent_id( $post_id ) ) ){
69
+ $this->sitepress->set_element_language_details( $post_id, 'post_product_variation', false, $var_lang );
70
+ }
71
+ }
72
+
73
+ if ( $post_type != 'product' ) {
74
+ return;
75
+ }
76
+
77
+ // exceptions
78
+ $ajax_call = ( !empty( $_POST[ 'icl_ajx_action' ] ) && $_POST[ 'icl_ajx_action' ] == 'make_duplicates' );
79
+ $api_call = !empty( $wp->query_vars['wc-api-version'] );
80
+ if ( ( empty( $duplicated_post_id ) || isset( $_POST[ 'autosave' ] ) ) ||
81
+ ( $pagenow != 'post.php' && $pagenow != 'post-new.php' && $pagenow != 'admin.php' && !$ajax_call && !$api_call ) ||
82
+ ( isset( $_GET[ 'action' ] ) && $_GET[ 'action' ] == 'trash' )
83
+ ) {
84
+ return;
85
+ }
86
+ // If we reach this point, we go ahead with sync.
87
+ // Remove filter to avoid double sync
88
+ remove_action( 'save_post', array( $this, 'sync_post_action' ), 110, 2 );
89
+ do_action( 'wcml_before_sync_product', $duplicated_post_id, $post_id );
90
+
91
+
92
+ //trnsl_interface option
93
+ if ( !$this->woocommerce_wpml->settings['trnsl_interface'] && $original_language != $current_language ) {
94
+ if( !isset( $_POST[ 'wp-preview' ] ) || empty( $_POST[ 'wp-preview' ] ) ){
95
+ //make sure we sync post in current language
96
+ $post_id = apply_filters( 'translate_object_id', $post_id, 'product', false, $current_language );
97
+ do_action( 'wcml_before_sync_product_data', $duplicated_post_id, $post_id, $current_language );
98
+ $this->sync_date_and_parent( $duplicated_post_id, $post_id, $current_language );
99
+ $this->sync_product_data( $duplicated_post_id, $post_id, $current_language );
100
+ }
101
+ return;
102
+ }
103
+
104
+ // get language code
105
+ $language_details = $this->sitepress->get_element_language_details( $post_id, 'post_product' );
106
+ if ( $pagenow == 'admin.php' && empty( $language_details ) ) {
107
+ //translation editor support: sidestep icl_translations_cache
108
+ $language_details = $this->wpdb->get_row(
109
+ $this->wpdb->prepare(
110
+ "SELECT element_id, trid, language_code, source_language_code FROM {$this->wpdb->prefix}icl_translations
111
+ WHERE element_id = %d AND element_type = 'post_product'",
112
+ $post_id )
113
+ );
114
+ }
115
+ if ( empty( $language_details ) ) {
116
+ return;
117
+ }
118
+
119
+ //save files option
120
+ $this->woocommerce_wpml->downloadable->save_files_option( $duplicated_post_id );
121
+ // pick posts to sync
122
+ $traslated_products = array();
123
+ $translations = $this->sitepress->get_element_translations( $language_details->trid, 'post_product', false, true );
124
+
125
+ foreach ( $translations as $translation ) {
126
+ if ( $translation->original ) {
127
+ $original_product_id = $translation->element_id;
128
+ } else {
129
+ $traslated_products[ $translation->element_id ] = $translation;
130
+ }
131
+ }
132
+
133
+ foreach( $traslated_products as $translated_product_id => $translation ) {
134
+ $lang = $translation->language_code;
135
+ do_action( 'wcml_before_sync_product_data', $original_product_id, $translated_product_id, $lang );
136
+ // Filter upsell products, crosell products and default attributes for translations
137
+ $this->duplicate_product_post_meta( $original_product_id, $translated_product_id );
138
+ if( $wpml_media_options[ 'new_content_settings' ][ 'duplicate_featured' ] ){
139
+ //sync feature image
140
+ $this->woocommerce_wpml->media->sync_thumbnail_id( $original_product_id, $translated_product_id, $lang );
141
+ }
142
+ $this->sync_date_and_parent( $original_product_id, $translated_product_id, $lang );
143
+ $this->sync_product_taxonomies( $original_product_id, $translated_product_id, $lang );
144
+ $this->woocommerce_wpml->attributes->sync_default_product_attr( $original_product_id, $translated_product_id, $lang );
145
+ $this->woocommerce_wpml->attributes->sync_product_attr( $original_product_id, $translated_product_id );
146
+ $this->woocommerce_wpml->products->update_order_for_product_translations( $original_product_id );
147
+ // synchronize post variations
148
+ $this->woocommerce_wpml->sync_variations_data->sync_product_variations( $original_product_id, $translated_product_id, $lang );
149
+ $this->sync_linked_products( $original_product_id, $translated_product_id, $lang );
150
+
151
+ // Clear any unwanted data
152
+ wc_delete_product_transients( $translated_product_id );
153
+ }
154
+ if( $this->woocommerce_wpml->settings['enable_multi_currency'] == WCML_MULTI_CURRENCIES_INDEPENDENT ) {
155
+ //save custom prices
156
+ $this->woocommerce_wpml->multi_currency->custom_prices->save_custom_prices( $duplicated_post_id );
157
+ $this->woocommerce_wpml->multi_currency->custom_prices->sync_product_variations_custom_prices($original_product_id);
158
+ }
159
+ }
160
+
161
+ public function sync_product_data( $original_product_id, $tr_product_id, $lang ){
162
+
163
+ $this->duplicate_product_post_meta( $original_product_id, $tr_product_id );
164
+
165
+ $this->woocommerce_wpml->attributes->sync_product_attr( $original_product_id, $tr_product_id );
166
+
167
+ $this->woocommerce_wpml->attributes->sync_default_product_attr( $original_product_id, $tr_product_id, $lang );
168
+
169
+ $wpml_media_options = maybe_unserialize( get_option( '_wpml_media' ) );
170
+ //sync media
171
+ if( $wpml_media_options[ 'new_content_settings' ][ 'duplicate_featured' ] ){
172
+ //sync feature image
173
+ $this->woocommerce_wpml->media->sync_thumbnail_id( $original_product_id, $tr_product_id, $lang );
174
+ }
175
+
176
+ if( $wpml_media_options[ 'new_content_settings' ][ 'duplicate_media' ]){
177
+ //sync product gallery
178
+ $this->woocommerce_wpml->media->sync_product_gallery( $original_product_id );
179
+ }
180
+
181
+ //sync taxonomies
182
+ $this->sync_product_taxonomies( $original_product_id, $tr_product_id, $lang );
183
+
184
+ //duplicate variations
185
+
186
+ $this->woocommerce_wpml->sync_variations_data->sync_product_variations( $original_product_id, $tr_product_id, $lang );
187
+
188
+ $this->sync_linked_products( $original_product_id, $tr_product_id, $lang );
189
+
190
+ // Clear any unwanted data
191
+ wc_delete_product_transients( $tr_product_id );
192
+ }
193
+
194
+ public function sync_product_taxonomies( $original_product_id, $tr_product_id, $lang ){
195
+ $taxonomies = get_object_taxonomies( 'product' );
196
+
197
+ foreach( $taxonomies as $taxonomy ) {
198
+
199
+ $terms = wp_get_object_terms( $original_product_id, $taxonomy );
200
+ $tt_ids = array();
201
+ $tt_names = array();
202
+ if ($terms) {
203
+ foreach ($terms as $term) {
204
+ if( $term->taxonomy == 'product_type' ){
205
+ $tt_names[] = $term->name;
206
+ continue;
207
+ }
208
+ $tt_ids[] = $term->term_id;
209
+ }
210
+
211
+ if( $taxonomy == 'product_type' ) {
212
+ wp_set_post_terms( $tr_product_id, $tt_names, $taxonomy );
213
+ }else{
214
+ $this->wcml_update_term_count_by_ids( $tt_ids, $lang, $taxonomy, $tr_product_id );
215
+ }
216
+ }
217
+ }
218
+ }
219
+
220
+ public function delete_term_relationships_update_term_count( $object_id, $tt_ids ){
221
+
222
+ if( get_post_type( $object_id ) == 'product' ){
223
+
224
+ $language_details = $this->sitepress->get_element_language_details( $object_id, 'post_product' );
225
+ $translations = $this->sitepress->get_element_translations( $language_details->trid, 'post_product', false, true );
226
+
227
+ foreach( $translations as $translation ) {
228
+ if ( !$translation->original ) {
229
+ $this->wcml_update_term_count_by_ids( $tt_ids, $translation->language_code );
230
+ }
231
+ }
232
+ }
233
+
234
+ }
235
+ public function wcml_update_term_count_by_ids( $tt_ids, $language, $taxonomy = '', $tr_product_id = false ){
236
+ $terms_array = array();
237
+ $terms_ids_array = array();
238
+
239
+ foreach( $tt_ids as $tt_id ){
240
+
241
+ $tr_id = apply_filters( 'translate_object_id', $tt_id, $taxonomy, false, $language );
242
+
243
+ if( !is_null( $tr_id ) ){
244
+ // not using get_term - unfiltered get_term
245
+ $translated_term = $this->wpdb->get_row( $this->wpdb->prepare( "
246
+ SELECT * FROM {$this->wpdb->terms} t JOIN {$this->wpdb->term_taxonomy} x ON x.term_id = t.term_id WHERE t.term_id = %d", $tr_id ) );
247
+ $terms_ids_array[] = $translated_term->term_id;
248
+ $terms_array[] = $translated_term->term_taxonomy_id;
249
+ $taxonomy = $translated_term->taxonomy;
250
+ }
251
+
252
+ }
253
+
254
+ if( is_taxonomy_hierarchical( $taxonomy ) ){
255
+ $terms_array = array_unique( array_map( 'intval', $terms_array ) );
256
+ }
257
+
258
+
259
+ if( in_array( $taxonomy, array( 'product_cat', 'product_tag' ) ) ) {
260
+ $this->sitepress->switch_lang( $language );
261
+ wp_update_term_count( $terms_array, $taxonomy );
262
+ $this->sitepress->switch_lang( );
263
+ }elseif( $tr_product_id ){
264
+ wp_set_post_terms( $tr_product_id, $terms_ids_array, $taxonomy );
265
+ }
266
+
267
+ }
268
+
269
+ public function sync_linked_products( $product_id, $translated_product_id, $lang ){
270
+ //sync up-sells
271
+ $original_up_sells = maybe_unserialize( get_post_meta( $product_id, '_upsell_ids', true ) );
272
+ $trnsl_up_sells = array();
273
+ if( $original_up_sells ){
274
+ foreach( $original_up_sells as $original_up_sell_product ) {
275
+ $trnsl_up_sells[] = apply_filters( 'translate_object_id', $original_up_sell_product, get_post_type( $original_up_sell_product ), false, $lang );
276
+ }
277
+ }
278
+ update_post_meta( $translated_product_id, '_upsell_ids', $trnsl_up_sells );
279
+ //sync cross-sells
280
+ $original_cross_sells = maybe_unserialize( get_post_meta( $product_id, '_crosssell_ids', true ) );
281
+ $trnsl_cross_sells = array();
282
+ if( $original_cross_sells )
283
+ foreach( $original_cross_sells as $original_cross_sell_product ){
284
+ $trnsl_cross_sells[] = apply_filters( 'translate_object_id', $original_cross_sell_product, get_post_type( $original_cross_sell_product ), false, $lang );
285
+ }
286
+ update_post_meta( $translated_product_id, '_crosssell_ids', $trnsl_cross_sells );
287
+ // refresh parent-children transients (e.g. this child goes to private or draft)
288
+ $translated_product_parent_id = wp_get_post_parent_id( $translated_product_id );
289
+ if ( $translated_product_parent_id ) {
290
+ delete_transient( 'wc_product_children_' . $translated_product_parent_id );
291
+ delete_transient( '_transient_wc_product_children_ids_' . $translated_product_parent_id );
292
+ }
293
+ }
294
+
295
+
296
+
297
+ public function sync_product_stocks_reduce( $order ){
298
+ return $this->sync_product_stocks( $order, 'reduce' );
299
+ }
300
+
301
+ public function sync_product_stocks_restore( $order ){
302
+ return $this->sync_product_stocks( $order, 'restore' );
303
+ }
304
+
305
+ /**
306
+ * @param $order WC_Order
307
+ * @param $action
308
+ */
309
+ public function sync_product_stocks( $order, $action ){
310
+ $order_id = method_exists( 'WC_Order', 'get_id' ) ? $order->get_id() : $order->id;
311
+
312
+ foreach( $order->get_items() as $item ) {
313
+
314
+ if( $item instanceof WC_Order_Item_Product ){
315
+ $variation_id = $item->get_variation_id();
316
+ $product_id = $item->get_product_id();
317
+ $qty = $item->get_quantity();
318
+ }else{
319
+ $variation_id = isset( $item[ 'variation_id' ] ) ? $item[ 'variation_id' ] : 0;
320
+ $product_id = $item[ 'product_id' ];
321
+ $qty = $item[ 'qty' ];
322
+ }
323
+
324
+ if( $variation_id > 0 ){
325
+ $trid = $this->sitepress->get_element_trid( $variation_id, 'post_product_variation' );
326
+ $translations = $this->sitepress->get_element_translations( $trid, 'post_product_variation' );
327
+ $ld = $this->sitepress->get_element_language_details( $variation_id, 'post_product_variation' );
328
+ } else {
329
+ $trid = $this->sitepress->get_element_trid( $product_id, 'post_product' );
330
+ $translations = $this->sitepress->get_element_translations( $trid, 'post_product' );
331
+ $ld = $this->sitepress->get_element_language_details( $product_id, 'post_product' );
332
+ }
333
+
334
+ // Process for non-current languages
335
+ foreach( $translations as $translation ){
336
+ if ( $ld->language_code != $translation->language_code ) {
337
+ //check if product exist
338
+ if( get_post_type( $translation->element_id ) == 'product_variation' && !get_post( wp_get_post_parent_id( $translation->element_id ) ) ){
339
+ continue;
340
+ }
341
+ $_product = wc_get_product( $translation->element_id );
342
+
343
+ if( $_product && $_product->exists() && $_product->managing_stock() ) {
344
+ $total_sales = get_post_meta($_product->id, 'total_sales', true);
345
+
346
+ if( $action == 'reduce'){
347
+ $stock = $_product->reduce_stock( $qty );
348
+ $total_sales += $qty;
349
+ }else{
350
+ $stock = $_product->increase_stock( $qty );
351
+ $total_sales -= $qty;
352
+ }
353
+ update_post_meta( $translation->element_id, 'total_sales', $total_sales );
354
+ }
355
+ }
356
+ }
357
+ }
358
+ }
359
+
360
+ public function sync_stock_status_for_translations( $id, $status ){
361
+
362
+ $type = get_post_type( $id );
363
+ $trid = $this->sitepress->get_element_trid( $id, 'post_'.$type );
364
+ $translations = $this->sitepress->get_element_translations( $trid, 'post_'.$type, true);
365
+
366
+ foreach ( $translations as $translation ) {
367
+ if ( !$translation->original ) {
368
+ update_post_meta( $translation->element_id, '_stock_status', $status );
369
+ }
370
+ }
371
+ }
372
+
373
+
374
+
375
+ //sync product parent & post_status
376
+ public function sync_date_and_parent( $duplicated_post_id, $post_id, $lang ){
377
+ $tr_parent_id = apply_filters( 'translate_object_id', wp_get_post_parent_id( $duplicated_post_id ), 'product', false, $lang );
378
+ $orig_product = get_post( $duplicated_post_id );
379
+ $args = array();
380
+ $args[ 'post_parent' ] = is_null( $tr_parent_id )? 0 : $tr_parent_id;
381
+ //sync product date
382
+
383
+ if( !empty( $this->woocommerce_wpml->settings[ 'products_sync_date' ] ) ){
384
+ $args[ 'post_date' ] = $orig_product->post_date;
385
+ }
386
+ $this->wpdb->update(
387
+ $this->wpdb->posts,
388
+ $args,
389
+ array( 'id' => $post_id )
390
+ );
391
+ }
392
+
393
+ public function set_schedule_for_translations( $deprecated, $post ){
394
+
395
+ if( $this->woocommerce_wpml->products->is_original_product( $post->ID ) ) {
396
+ $trid = $this->sitepress->get_element_trid( $post->ID, 'post_product');
397
+ $translations = $this->sitepress->get_element_translations( $trid, 'post_product', true);
398
+ foreach( $translations as $translation ) {
399
+ if( !$translation->original ){
400
+ wp_clear_scheduled_hook( 'publish_future_post', array( $translation->element_id ) );
401
+ wp_schedule_single_event( strtotime( get_gmt_from_date( $post->post_date) . ' GMT' ), 'publish_future_post', array( $translation->element_id ) );
402
+ }
403
+ }
404
+ }
405
+ }
406
+
407
+
408
+
409
+ public function icl_pro_translation_completed( $tr_product_id ){
410
+ $trid = $this->sitepress->get_element_trid( $tr_product_id, 'post_product' );
411
+ $translations = $this->sitepress->get_element_translations( $trid, 'post_product' );
412
+
413
+ foreach( $translations as $translation ){
414
+ if( $translation->original ){
415
+ $original_product_id = $translation->element_id;
416
+ }
417
+ }
418
+
419
+ if( !isset( $original_product_id ) ){
420
+ return;
421
+ }
422
+
423
+ $lang = $this->sitepress->get_language_for_element( $tr_product_id, 'post_product' );
424
+ $this->sync_product_data( $original_product_id, $tr_product_id, $lang );
425
+ }
426
+
427
+ public function icl_make_duplicate( $master_post_id, $lang, $postarr, $id ){
428
+ if( get_post_type( $master_post_id ) == 'product' ){
429
+
430
+ $original_language = $this->woocommerce_wpml->products->get_original_product_language( $master_post_id );
431
+ $master_post_id = apply_filters( 'translate_object_id', $master_post_id, 'product', false, $original_language );
432
+
433
+ $this->sync_product_data( $master_post_id, $id, $lang );
434
+ }
435
+ }
436
+
437
+ public function woocommerce_product_quick_edit_save( $product ){
438
+ $is_original = $this->woocommerce_wpml->products->is_original_product( $product->id );
439
+ $trid = $this->sitepress->get_element_trid( $product->id, 'post_product' );
440
+
441
+ if( $trid ){
442
+ $translations = $this->sitepress->get_element_translations( $trid, 'post_product' );
443
+ if( $translations ){
444
+ foreach( $translations as $translation ){
445
+ if( $is_original ){
446
+ if( !$translation->original ){
447
+ $this->sync_product_data( $product->id, $translation->element_id, $translation->language_code );
448
+ $this->sync_date_and_parent( $product->id, $translation->element_id, $translation->language_code );
449
+ }
450
+ }elseif( $translation->original ){
451
+ $this->sync_product_data( $translation->element_id, $product->id, $this->sitepress->get_language_for_element( $product->id, 'post_product' ) );
452
+ $this->sync_date_and_parent( $translation->element_id, $product->id, $this->sitepress->get_language_for_element( $product->id, 'post_product' ) );
453
+ }
454
+ }
455
+ }
456
+ }
457
+ }
458
+
459
+
460
+
461
+ //duplicate product post meta
462
+ public function duplicate_product_post_meta( $original_product_id, $trnsl_product_id, $data = false ){
463
+ global $iclTranslationManagement;
464
+ $settings = $iclTranslationManagement->settings[ 'custom_fields_translation' ];
465
+ $all_meta = get_post_custom( $original_product_id );
466
+ $post_fields = null;
467
+
468
+ unset( $all_meta[ '_thumbnail_id' ] );
469
+
470
+ foreach ( $all_meta as $key => $meta ) {
471
+ if ( !isset( $settings[ $key ] ) || $settings[ $key ] == WPML_IGNORE_CUSTOM_FIELD ) {
472
+ continue;
473
+ }
474
+ foreach ( $meta as $meta_value ) {
475
+ if( $key == '_downloadable_files' ){
476
+ $this->woocommerce_wpml->downloadable->sync_files_to_translations( $original_product_id, $trnsl_product_id, $data );
477
+ }elseif ( $data ) {
478
+ if ( isset( $settings[ $key ] ) && $settings[ $key ] == WPML_TRANSLATE_CUSTOM_FIELD ) {
479
+
480
+ $post_fields = $this->sync_custom_field_value( $key, $data, $trnsl_product_id, $post_fields );
481
+ }
482
+ }
483
+ }
484
+ }
485
+
486
+ do_action( 'wcml_after_duplicate_product_post_meta', $original_product_id, $trnsl_product_id, $data );
487
+ }
488
+
489
+ public function sync_custom_field_value( $custom_field, $translation_data, $trnsl_product_id, $post_fields, $original_product_id = false, $is_variation = false ){
490
+
491
+ if( is_null( $post_fields ) ){
492
+ $post_fields = array();
493
+ if( isset( $_POST['data'] ) ){
494
+ $post_args = wp_parse_args( $_POST['data'] );
495
+ $post_fields = $post_args[ 'fields' ];
496
+ }
497
+ }
498
+
499
+
500
+ $custom_filed_key = $is_variation && $original_product_id ? $custom_field.$original_product_id : $custom_field;
501
+
502
+ if( isset( $translation_data[ md5( $custom_filed_key ) ] ) ){
503
+ $meta_value = $translation_data[ md5( $custom_filed_key ) ];
504
+ $meta_value = apply_filters( 'wcml_meta_value_before_add', $meta_value, $custom_filed_key );
505
+ update_post_meta( $trnsl_product_id, $custom_field, $meta_value );
506
+ unset( $post_fields[ $custom_filed_key ] );
507
+ }else{
508
+ foreach( $post_fields as $post_field_key => $post_field ){
509
+ $meta_value = $translation_data[ md5( $post_field_key ) ];
510
+ $field_key = explode( ':', $post_field_key );
511
+ if( $field_key[0] == $custom_filed_key ){
512
+ if( substr( $field_key[1], 0, 3 ) == 'new' ){
513
+ add_post_meta( $trnsl_product_id, $custom_field, $meta_value );
514
+ }else{
515
+ update_meta( $field_key[1], $custom_field, $meta_value );
516
+ }
517
+ unset( $post_fields[ $post_field_key ] );
518
+ }
519
+ }
520
+ }
521
+
522
+ return $post_fields;
523
+ }
524
+
525
+ public function woocommerce_duplicate_product( $new_id, $post ){
526
+ $duplicated_products = array();
527
+
528
+ //duplicate original first
529
+ $trid = $this->sitepress->get_element_trid( $post->ID, 'post_' . $post->post_type );
530
+ $orig_id = $this->sitepress->get_original_element_id_by_trid( $trid );
531
+ $orig_lang = $this->woocommerce_wpml->products->get_original_product_language( $post->ID );
532
+
533
+ $wc_admin = new WC_Admin_Duplicate_Product();
534
+
535
+ if( $orig_id == $post->ID ){
536
+ $this->sitepress->set_element_language_details( $new_id, 'post_' . $post->post_type, false, $orig_lang );
537
+ $new_trid = $this->sitepress->get_element_trid( $new_id, 'post_' . $post->post_type );
538
+ $new_orig_id = $new_id;
539
+ }else{
540
+ $post_to_duplicate = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT * FROM {$this->wpdb->posts} WHERE ID=%d", $orig_id ) );
541
+ if ( ! empty( $post_to_duplicate ) ) {
542
+ $new_orig_id = $wc_admin->duplicate_product( $post_to_duplicate );
543
+ do_action( 'wcml_after_duplicate_product' , $new_id, $post_to_duplicate );
544
+ $this->sitepress->set_element_language_details( $new_orig_id, 'post_' . $post->post_type, false, $orig_lang );
545
+ $new_trid = $this->sitepress->get_element_trid( $new_orig_id, 'post_' . $post->post_type );
546
+ if( get_post_meta( $orig_id, '_icl_lang_duplicate_of' ) ){
547
+ update_post_meta( $new_id, '_icl_lang_duplicate_of', $new_orig_id );
548
+ }
549
+ $this->sitepress->set_element_language_details( $new_id, 'post_' . $post->post_type, $new_trid, $this->sitepress->get_current_language() );
550
+ }
551
+ }
552
+
553
+ // Set language info for variations
554
+ if ( $children_products = get_children( 'post_parent=' . $new_orig_id . '&post_type=product_variation' ) ) {
555
+ foreach ( $children_products as $child ) {
556
+ $this->sitepress->set_element_language_details( $child->ID, 'post_product_variation', false, $orig_lang );
557
+ }
558
+ }
559
+
560
+ $translations = $this->sitepress->get_element_translations( $trid, 'post_' . $post->post_type );
561
+ $duplicated_products[ 'translations' ] = array();
562
+ if( $translations ){
563
+ foreach( $translations as $translation ){
564
+ if( !$translation->original && $translation->element_id != $post->ID ){
565
+ $post_to_duplicate = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT * FROM {$this->wpdb->posts} WHERE ID=%d", $translation->element_id ) );
566
+
567
+ if( ! empty( $post_to_duplicate ) ) {
568
+ $new_id = $wc_admin->duplicate_product( $post_to_duplicate );
569
+ $new_id_obj = get_post( $new_id );
570
+ $new_slug = wp_unique_post_slug( sanitize_title( $new_id_obj->post_title ), $new_id, $post_to_duplicate->post_status, $post_to_duplicate->post_type, $new_id_obj->post_parent );
571
+
572
+ $this->wpdb->update(
573
+ $this->wpdb->posts,
574
+ array(
575
+ 'post_name' => $new_slug,
576
+ 'post_status' => 'draft'
577
+ ),
578
+ array( 'ID' => $new_id )
579
+ );
580
+
581
+ do_action( 'wcml_after_duplicate_product' , $new_id, $post_to_duplicate );
582
+ $this->sitepress->set_element_language_details( $new_id, 'post_' . $post->post_type, $new_trid, $translation->language_code );
583
+ if( get_post_meta( $translation->element_id, '_icl_lang_duplicate_of' ) ){
584
+ update_post_meta( $new_id, '_icl_lang_duplicate_of', $new_orig_id );
585
+ }
586
+ $duplicated_products[ 'translations' ][] = $new_id;
587
+ }
588
+ }
589
+ }
590
+ }
591
+
592
+ $duplicated_products[ 'original' ] = $new_orig_id;
593
+
594
+ return $duplicated_products;
595
+ }
596
+
597
+ public function check_ajax_actions(){
598
+ if( isset( $_POST[ 'icl_ajx_action' ] ) && $_POST[ 'icl_ajx_action' ] == 'connect_translations' ){
599
+ $this->icl_connect_translations_action();
600
+ }
601
+ }
602
+
603
+ public function icl_connect_translations_action(){
604
+ $new_trid = $_POST['new_trid'];
605
+ $post_type = $_POST['post_type'];
606
+ $post_id = $_POST['post_id'];
607
+ $set_as_source = $_POST['set_as_source'];
608
+ if( $post_type == 'product' ){
609
+
610
+ $translations = $this->sitepress->get_element_translations( $new_trid, 'post_' . $post_type );
611
+ if( $translations ) {
612
+ foreach ( $translations as $translation ) {
613
+ //current as original need sync translation
614
+ if ( $translation->original ) {
615
+ if( $set_as_source ) {
616
+ $orig_id = $post_id;
617
+ $trnsl_id = $translation->element_id;
618
+ $lang = $translation->language_code;
619
+ }else{
620
+ $orig_id = $translation->element_id;
621
+ $trnsl_id = $post_id;
622
+ $lang = $this->sitepress->get_current_language();
623
+ }
624
+ $this->sync_product_data( $orig_id, $trnsl_id, $lang );
625
+ $this->sync_date_and_parent( $orig_id, $trnsl_id, $lang );
626
+ $this->sitepress->copy_custom_fields( $orig_id, $trnsl_id );
627
+ }else {
628
+ if( $set_as_source ) {
629
+ $this->sync_product_data( $post_id, $translation->element_id, $translation->language_code );
630
+ $this->sync_date_and_parent( $post_id, $translation->element_id, $translation->language_code );
631
+ $this->sitepress->copy_custom_fields( $post_id, $translation->element_id );
632
+ }
633
+ }
634
+ }
635
+ }
636
+ }
637
+ }
638
+
639
  }
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: 3.9
7
- Tested up to: 4.6.1
8
- Stable tag: 3.9.4
9
 
10
  Allows running fully multilingual e-commerce sites using WooCommerce and WPML.
11
 
@@ -142,6 +142,9 @@ WooCommerce Multilingual is compatible with all major WooCommerce extensions. We
142
 
143
  == Changelog ==
144
 
 
 
 
145
  = 3.9.4 =
146
  * Fixed an issue with custom prices (secondary currency) overriding the price in the default currency in some conditions
147
  * Fixed an issue with WooCommerce Authorize.Net AIM Gateway happening when using the credit cart checkout
4
  Tags: CMS, woocommerce, commerce, ecommerce, e-commerce, products, WPML, multilingual, e-shop, shop
5
  License: GPLv2
6
  Requires at least: 3.9
7
+ Tested up to: 4.7
8
+ Stable tag: 3.9.5
9
 
10
  Allows running fully multilingual e-commerce sites using WooCommerce and WPML.
11
 
142
 
143
  == Changelog ==
144
 
145
+ = 3.9.5 =
146
+ * Fixed a bug introduced in 3.9.4 that prevented translating variable products
147
+
148
  = 3.9.4 =
149
  * Fixed an issue with custom prices (secondary currency) overriding the price in the default currency in some conditions
150
  * Fixed an issue with WooCommerce Authorize.Net AIM Gateway happening when using the credit cart checkout
wpml-woocommerce.php CHANGED
@@ -6,12 +6,12 @@
6
  Author: OnTheGoSystems
7
  Author URI: http://www.onthegosystems.com/
8
  Text Domain: woocommerce-multilingual
9
- Version: 3.9.4
10
  */
11
 
12
  if( defined( 'WCML_VERSION' ) ) return;
13
 
14
- define( 'WCML_VERSION', '3.9.4' );
15
  define( 'WCML_PLUGIN_PATH', dirname( __FILE__ ) );
16
  define( 'WCML_PLUGIN_FOLDER', basename( WCML_PLUGIN_PATH ) );
17
  define( 'WCML_LOCALE_PATH', WCML_PLUGIN_PATH . '/locale' );
6
  Author: OnTheGoSystems
7
  Author URI: http://www.onthegosystems.com/
8
  Text Domain: woocommerce-multilingual
9
+ Version: 3.9.5
10
  */
11
 
12
  if( defined( 'WCML_VERSION' ) ) return;
13
 
14
+ define( 'WCML_VERSION', '3.9.5' );
15
  define( 'WCML_PLUGIN_PATH', dirname( __FILE__ ) );
16
  define( 'WCML_PLUGIN_FOLDER', basename( WCML_PLUGIN_PATH ) );
17
  define( 'WCML_LOCALE_PATH', WCML_PLUGIN_PATH . '/locale' );