PluginProbe ʕ •ᴥ•ʔ
WPML Multilingual & Multicurrency for WooCommerce / trunk
WPML Multilingual & Multicurrency for WooCommerce vtrunk
5.5.7 5.3.8 5.3.9 5.4.3 5.4.4 5.4.5 5.5.1 5.5.1.1 5.5.2.2 5.5.2.3 5.5.3 5.5.3.1 5.5.4 5.5.5 5.5.6 trunk 0.9 1.0 1.1 1.2 1.3 1.4 1.5 2.0 2.2 2.3 2.3.1 2.3.2 3.0 3.0.1 3.1 3.2 3.2.1 3.2.2 3.3 3.3.1 3.3.2 3.3.3 3.3.4 3.4 3.4.1 3.4.2 3.4.3 3.5 3.5.1 3.5.2 3.5.3 3.5.4 3.5.5 3.6 3.6.1 3.6.10 3.6.11 3.6.2 3.6.3 3.6.4 3.6.5 3.6.5.1 3.6.6 3.6.7 3.6.8 3.6.9 3.7 3.7.1 3.7.10 3.7.11 3.7.12 3.7.13 3.7.14 3.7.15 3.7.16 3.7.2 3.7.3 3.7.4 3.7.5 3.7.6 3.7.7 3.7.8 3.7.9 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.9.0 3.9.1 3.9.1.1 3.9.2 3.9.3 3.9.4 3.9.5 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.10.0 4.10.1 4.10.2 4.10.3 4.10.4 4.11.2 4.11.3.2 4.11.5 4.11.6 4.12.1 4.12.4 4.12.5 4.12.6 4.2.0 4.2.0.1 4.2.1 4.2.1.1 4.2.10 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.2.7 4.2.7.1 4.2.8 4.2.8.1 4.2.9 4.3.0 4.3.1 4.3.2 4.3.2.1 4.3.3 4.3.4 4.3.5 4.3.6 4.3.7 4.4.0 4.4.1 4.4.2 4.4.2.1 4.5.0 4.6.0 4.6.1 4.6.2 4.6.2.1 4.6.3 4.6.5 4.6.6 4.6.7 4.7.0 4.7.1 4.7.2 4.7.3 4.7.4 4.7.5 4.7.6 4.7.7 4.7.8 4.7.9 4.8.0 4.9.0 4.9.1 5.0.1 5.0.2 5.1.1 5.1.2 5.1.3 5.2.0 5.2.1 5.3.2 5.3.3.1 5.3.4 5.3.5 5.3.6 5.3.7
woocommerce-multilingual / inc / class-wcml-troubleshooting.php
woocommerce-multilingual / inc Last commit date
admin-menus 1 month ago currencies 1 month ago template-classes 1 month ago translation-editor 1 month ago class-wcml-ajax-setup.php 1 month ago class-wcml-attributes.php 1 month ago class-wcml-capabilities.php 1 month ago class-wcml-cart-removed-items-widget.php 1 year ago class-wcml-cart-switch-lang-functions.php 1 month ago class-wcml-cart-sync-warnings.php 1 month ago class-wcml-cart.php 1 month ago class-wcml-comments.php 1 month ago class-wcml-compatibility.php 1 month ago class-wcml-coupons.php 1 month ago class-wcml-dependencies.php 1 month ago class-wcml-emails.php 1 month ago class-wcml-endpoints.php 1 month ago class-wcml-install.php 1 month ago class-wcml-languages-upgrader.php 1 month ago class-wcml-locale.php 1 month ago class-wcml-orders.php 1 month ago class-wcml-products-screen-options.php 1 month ago class-wcml-products.php 1 month ago class-wcml-reports.php 1 month ago class-wcml-requests.php 1 month ago class-wcml-resources.php 1 month ago class-wcml-store-pages.php 1 month ago class-wcml-terms.php 1 month ago class-wcml-tp-support.php 1 month ago class-wcml-troubleshooting.php 1 month ago class-wcml-upgrade.php 1 month ago class-wcml-url-translation.php 1 month ago class-wcml-wc-gateways.php 1 month ago class-wcml-wc-shipping.php 1 month ago class-wcml-wc-strings.php 1 month ago class-wcml-widgets.php 1 month ago constants.php 1 month ago functions-pure.php 1 month ago functions.php 1 month ago installer-loader.php 1 month ago missing-php-functions.php 1 month ago wcml-core-functions.php 1 month ago wcml-switch-lang-request.php 1 month ago
class-wcml-troubleshooting.php
603 lines
1 <?php
2
3 use WCML\Utilities\DB;
4 use WPML\FP\Obj;
5 use function WPML\Container\make;
6
7 class WCML_Troubleshooting {
8
9 const ITEMS_PER_AJAX_MIN = 3;
10 const ITEMS_PER_AJAX = 5;
11
12 const OPTION_PRODUCTS_WITH_VARIATIONS = 'wcml_products_to_sync';
13 const OPTION_PRODUCTS_AND_VARIATIONS_FOR_STOCK_SYNC = 'wcml_products_and_variations_for_stock_sync';
14 const OPTION_VARIATIONS_FOR_LANGUAGE_ASSIGNMENT = 'wcml_trbl_translated_variations';
15 const OPTION_PRODUCTS_AND_VARIATIONS_FOR_META_CLEANUP = 'wcml_trbl_products_needs_fix_postmeta';
16 const META_GALLERY_SYNC = 'wcml_gallery_sync';
17 const META_GALLERY_SYNC_LEGACY = 'gallery_sync';
18 const META_CAT_META_SYNC = 'wcml_cat_meta_sync';
19
20 private $woocommerce_wpml;
21 private $sitepress;
22 private $wpdb;
23
24 public function __construct( $woocommerce_wpml, $sitepress, $wpdb ) {
25
26 $this->woocommerce_wpml = $woocommerce_wpml;
27 $this->sitepress = $sitepress;
28 $this->wpdb = $wpdb;
29
30 add_action( 'init', [ $this, 'init' ] );
31 }
32
33 public function init() {
34 add_action( 'wp_ajax_trbl_sync_variations', [ $this, 'trbl_sync_variations' ] );
35 add_action( 'wp_ajax_trbl_gallery_images', [ $this, 'trbl_gallery_images' ] );
36 add_action( 'wp_ajax_trbl_sync_categories', [ $this, 'trbl_sync_categories' ] );
37 add_action( 'wp_ajax_trbl_sync_stock', [ $this, 'trbl_sync_stock' ] );
38 add_action( 'wp_ajax_fix_translated_variations_relationships', [ $this, 'fix_translated_variations_relationships' ] );
39 add_action( 'wp_ajax_trbl_fix_product_type_terms', [ $this, 'trbl_fix_product_type_terms' ] );
40 add_action( 'wp_ajax_trbl_duplicate_terms', [ $this, 'trbl_duplicate_terms' ] );
41 add_action( 'wp_ajax_register_reviews_in_st', [ $this, 'register_reviews_in_st' ] );
42 add_action( 'wp_ajax_sync_deleted_meta', [ $this, 'sync_deleted_meta' ] );
43 }
44
45 public function countProducts() {
46 return $this->wpdb->get_var(
47 "
48 SELECT COUNT( DISTINCT p.ID ) FROM {$this->wpdb->posts} AS p
49 LEFT JOIN {$this->wpdb->prefix}icl_translations AS tr
50 ON tr.element_id = p.ID
51 WHERE p.post_status = 'publish' AND p.post_type = 'product' AND tr.source_language_code is NULL
52 "
53 );
54 }
55
56 public function countVariations() {
57 return $this->wpdb->get_var(
58 "
59 SELECT COUNT( DISTINCT p.ID ) FROM {$this->wpdb->posts} AS p
60 LEFT JOIN {$this->wpdb->prefix}icl_translations AS tr
61 ON tr.element_id = p.ID
62 WHERE p.post_status = 'publish' AND p.post_type = 'product_variation' AND tr.source_language_code is NULL
63 "
64 );
65 }
66
67 public function getVariableProducts() {
68 $get_variation_term_taxonomy_ids = $this->wpdb->get_var( "SELECT tt.term_taxonomy_id FROM {$this->wpdb->terms} AS t LEFT JOIN {$this->wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE t.name = 'variable'" );
69 $get_variation_term_taxonomy_ids = apply_filters( 'wcml_variation_term_taxonomy_ids', (array) $get_variation_term_taxonomy_ids );
70
71 return $this->wpdb->get_results(
72 "
73 SELECT tr.element_id as id,tr.language_code as lang FROM {$this->wpdb->prefix}icl_translations AS tr LEFT JOIN {$this->wpdb->term_relationships} as t ON tr.element_id = t.object_id LEFT JOIN {$this->wpdb->posts} AS p ON tr.element_id = p.ID
74 WHERE p.post_status = 'publish' AND tr.source_language_code is NULL AND tr.element_type = 'post_product' AND t.term_taxonomy_id IN (" . DB::prepareIn( $get_variation_term_taxonomy_ids, '%d' ) . ") ORDER BY tr.element_id
75 ",
76 ARRAY_A
77 );
78 }
79
80 public function countVariableProducts() {
81 $variableProducts = $this->getVariableProducts();
82 return count( $variableProducts );
83 }
84
85 public function getRemainingVariableProducts() {
86 $variableProducts = get_option( self::OPTION_PRODUCTS_WITH_VARIATIONS );
87 if ( false === $variableProducts ) {
88 $variableProducts = $this->getVariableProducts();
89 }
90 return $variableProducts;
91 }
92
93 public function setRemainingVariableProducts( $variableProducts ) {
94 if ( empty( $variableProducts ) ) {
95 delete_option( 'wcml_products_to_sync' );
96 } else {
97 update_option( 'wcml_products_to_sync', $variableProducts );
98 }
99 }
100
101 public function trbl_sync_variations() {
102 self::checkNonce( 'trbl_sync_variations' );
103
104 $response = [
105 'processed' => 0,
106 'complete' => false,
107 ];
108
109 $all_active_lang = $this->sitepress->get_active_languages();
110 $variableProducts = $this->getRemainingVariableProducts();
111 $variableProductsForRound = array_slice( $variableProducts, 0, self::ITEMS_PER_AJAX_MIN, true );
112
113 foreach ( $variableProductsForRound as $key => $product ) {
114 $translationsLanguages = [];
115 foreach ( $all_active_lang as $language ) {
116 if ( $language['code'] != $product['lang'] ) {
117 $translationId = apply_filters( 'wpml_object_id', $product['id'], 'product', false, $language['code'] );
118 if ( ! is_null( $translationId ) ) {
119 $translationsLanguages[ $translationId ] = $language['code'];
120 }
121 }
122 unset( $variableProducts[ $key ] );
123 }
124 if ( ! empty( $translationsLanguages ) ) {
125 do_action(
126 \WCML\Synchronization\Hooks::HOOK_SYNCHRONIZE_PRODUCT_COMPONENT,
127 get_post( $product['id'] ),
128 array_keys( $translationsLanguages ),
129 $translationsLanguages,
130 \WCML\Synchronization\Store::COMPONENT_VARIATIONS
131 );
132 }
133 }
134
135 $this->setRemainingVariableProducts( $variableProducts );
136
137 $wcml_settings = get_option( '_wcml_settings' );
138 if ( isset( $wcml_settings['notifications']['varimages'] ) ) {
139 $wcml_settings['notifications']['varimages']['show'] = 0;
140 update_option( '_wcml_settings', $wcml_settings );
141 }
142
143 $response['processed'] = count( $variableProductsForRound );
144 $response['complete'] = empty( $variableProducts );
145 wp_send_json_success( $response );
146 }
147
148 public function getProductsForGallerySync( $limit = false ) {
149 $queryLimit= '';
150 if ( $limit ) {
151 $queryLimit = ' ORDER BY p.ID LIMIT ' . self::ITEMS_PER_AJAX;
152 }
153 return $this->wpdb->get_results(
154 $this->wpdb->prepare(
155 "
156 SELECT DISTINCT p.ID FROM {$this->wpdb->posts} AS p
157 LEFT JOIN {$this->wpdb->prefix}icl_translations AS tr
158 ON tr.element_id = p.ID
159 WHERE p.post_status = 'publish' AND p.post_type = 'product' AND tr.source_language_code is NULL
160 AND ( SELECT COUNT( pm.meta_key ) FROM {$this->wpdb->postmeta} AS pm WHERE pm.post_id = p.ID AND pm.meta_key = %s ) = 0
161 {$queryLimit}
162 ",
163 self::META_GALLERY_SYNC
164 )
165 );
166 }
167
168 public function countProductsForGallerySync() {
169 $productsForGallerySync = $this->getProductsForGallerySync();
170 return count( $productsForGallerySync );
171 }
172
173 public function trbl_gallery_images() {
174 self::checkNonce( 'trbl_gallery_images' );
175
176 $productsForGallerySync = $this->getProductsForGallerySync( true );
177
178 foreach ( $productsForGallerySync as $product ) {
179 $this->woocommerce_wpml->media->sync_product_gallery_to_all_languages( $product->ID );
180 add_post_meta( $product->ID, self::META_GALLERY_SYNC, true );
181 }
182
183 $response = [
184 'processed' => count( $productsForGallerySync ),
185 'complete' => count( $productsForGallerySync ) < self::ITEMS_PER_AJAX,
186 ];
187
188 if ( $response ['complete'] ) {
189 $this->wpdb->delete ( $this->wpdb->postmeta, [ 'meta_key' => self::META_GALLERY_SYNC ] );
190 $this->wpdb->delete ( $this->wpdb->postmeta, [ 'meta_key' => self::META_GALLERY_SYNC_LEGACY ] );
191 }
192
193 wp_send_json_success( $response );
194 }
195
196 public function getProductCategoriesForTermMetaSync( $limit = false ) {
197 $queryLimit = '';
198 if ( $limit ) {
199 $queryLimit = ' ORDER BY t.term_taxonomy_id LIMIT ' . self::ITEMS_PER_AJAX;
200 }
201 return $this->wpdb->get_results(
202 $this->wpdb->prepare(
203 "
204 SELECT t.term_taxonomy_id,t.term_id,tr.language_code FROM {$this->wpdb->term_taxonomy} AS t
205 LEFT JOIN {$this->wpdb->prefix}icl_translations AS tr
206 ON tr.element_id = t.term_taxonomy_id
207 WHERE t.taxonomy = 'product_cat' AND tr.element_type = 'tax_product_cat' AND tr.source_language_code is NULL
208 AND ( SELECT COUNT( tm.meta_key ) FROM {$this->wpdb->termmeta} AS tm WHERE tm.term_id = t.term_id AND tm.meta_key = %s ) = 0
209 {$queryLimit}
210 ",
211 self::META_CAT_META_SYNC
212 )
213 );
214 }
215
216 public function countProductCategoriesForTermMetaSync() {
217 $productCategoriesForTermMetaSync = $this->getProductCategoriesForTermMetaSync();
218 return count( $productCategoriesForTermMetaSync );
219 }
220
221 public function trbl_sync_categories() {
222 self::checkNonce( 'trbl_sync_categories' );
223
224 $productCategoriesForTermMetaSync = $this->getProductCategoriesForTermMetaSync( true );
225
226 foreach ( $productCategoriesForTermMetaSync as $category ) {
227 update_term_meta( $category->term_id, self::META_CAT_META_SYNC, true );
228 $trid = $this->sitepress->get_element_trid( $category->term_taxonomy_id, 'tax_product_cat' );
229 $translations = $this->sitepress->get_element_translations( $trid, 'tax_product_cat' );
230 $type = get_term_meta( $category->term_id, 'display_type', true );
231 $thumbnail_id = get_term_meta( $category->term_id, 'thumbnail_id', true );
232 foreach ( $translations as $translation ) {
233 if ( $translation->language_code != $category->language_code ) {
234 update_term_meta( $translation->term_id, 'display_type', $type );
235 update_term_meta( $translation->term_id, 'thumbnail_id', apply_filters( 'wpml_object_id', $thumbnail_id, 'attachment', true, $translation->language_code ) );
236 }
237 }
238 }
239
240 $response = [
241 'processed' => count( $productCategoriesForTermMetaSync ),
242 'complete' => count( $productCategoriesForTermMetaSync ) < self::ITEMS_PER_AJAX,
243 ];
244
245 if ( $response ['complete'] ) {
246 $this->wpdb->delete ( $this->wpdb->termmeta, [ 'meta_key' => self::META_CAT_META_SYNC ] );
247 }
248
249 wp_send_json_success( $response );
250 }
251
252 public function getProductsAndVariationsForStockSync( $limit = false ) {
253 $queryLimit = '';
254 if ( $limit ) {
255 $queryLimit = ' ORDER BY p.ID LIMIT ' . self::ITEMS_PER_AJAX_MIN;
256 }
257 return $this->wpdb->get_results(
258 "
259 SELECT p.ID, t.trid, t.element_type
260 FROM {$this->wpdb->posts} p
261 JOIN {$this->wpdb->prefix}icl_translations t ON t.element_id = p.ID AND t.element_type IN ('post_product', 'post_product_variation')
262 WHERE p.post_type in ('product', 'product_variation') AND t.source_language_code IS NULL
263 {$queryLimit}
264 "
265 );
266 }
267
268 public function getRemainingProductsAndVariationsForStockSync() {
269 $itemsForStockSync = get_option( self::OPTION_PRODUCTS_AND_VARIATIONS_FOR_STOCK_SYNC );
270 if ( false === $itemsForStockSync ) {
271 $itemsForStockSync = $this->getProductsAndVariationsForStockSync();
272 }
273 return $itemsForStockSync;
274 }
275
276 public function setRemainingProductsAndVariationsForStockSync( $itemsForStockSync ) {
277 if ( empty( $itemsForStockSync ) ) {
278 delete_option( self::OPTION_PRODUCTS_AND_VARIATIONS_FOR_STOCK_SYNC );
279 } else {
280 update_option( self::OPTION_PRODUCTS_AND_VARIATIONS_FOR_STOCK_SYNC, $itemsForStockSync );
281 }
282 }
283
284 public function trbl_sync_stock() {
285 self::checkNonce( 'trbl_sync_stock' );
286
287 $response = [
288 'processed' => 0,
289 'complete' => false,
290 ];
291
292 $itemsForStockSync = $this->getRemainingProductsAndVariationsForStockSync();
293 $itemsForStockSyncInRound = array_slice( $itemsForStockSync, 0, self::ITEMS_PER_AJAX_MIN, true );
294
295 foreach ( $itemsForStockSyncInRound as $key => $product ) {
296 $translations = $this->sitepress->get_element_translations( $product->trid, $product->element_type );
297 $translationsIds = [];
298 $translationsLanguages = [];
299 foreach ( $translations as $translation ) {
300 if ( (int) $product->ID !== (int) $translation->element_id ) {
301 $translationsIds[] = $translation->element_id;
302 $translationsLanguages[ $translation->element_id ] = $translation->language_code;
303 }
304 }
305 unset( $itemsForStockSync[ $key ] );
306 do_action(
307 \WCML\Synchronization\Hooks::HOOK_SYNCHRONIZE_PRODUCT_COMPONENT,
308 get_post( $product->ID ),
309 $translationsIds,
310 $translationsLanguages,
311 \WCML\Synchronization\Store::COMPONENT_STOCK
312 );
313 }
314
315 $this->setRemainingProductsAndVariationsForStockSync( $itemsForStockSync );
316
317 $response['processed'] = count( $itemsForStockSyncInRound );
318 $response['complete'] = empty( $itemsForStockSync );
319
320 wp_send_json_success( $response );
321 }
322
323 public function getVariationsForLanguageAssignment( $limit = false ) {
324 $queryLimit = '';
325 if ( $limit ) {
326 $queryLimit = ' ORDER BY post_id LIMIT ' . self::ITEMS_PER_AJAX;
327 }
328 return $this->wpdb->get_results(
329 "
330 SELECT post_id, meta_value FROM {$this->wpdb->postmeta}
331 WHERE meta_key ='_wcml_duplicate_of_variation'
332 {$queryLimit}
333 "
334 );
335 }
336
337 public function getRemainingVariationsForLanguageAssignment() {
338 $itemsForLanguageAssignment = get_option( self::OPTION_VARIATIONS_FOR_LANGUAGE_ASSIGNMENT );
339 if ( false === $itemsForLanguageAssignment ) {
340 $itemsForLanguageAssignment = $this->getVariationsForLanguageAssignment();
341 }
342 return $itemsForLanguageAssignment;
343 }
344
345 public function setRemainingVariationsForLanguageAssignment( $itemsForLanguageAssignment ) {
346 if ( empty( $itemsForLanguageAssignment ) ) {
347 delete_option( self::OPTION_VARIATIONS_FOR_LANGUAGE_ASSIGNMENT );
348 } else {
349 update_option( self::OPTION_VARIATIONS_FOR_LANGUAGE_ASSIGNMENT, $itemsForLanguageAssignment );
350 }
351 }
352
353 private function get_translation_info_for_element( $element_id, $element_type ) {
354 return $this->wpdb->get_row(
355 $this->wpdb->prepare(
356 "
357 SELECT trid, translation_id FROM {$this->wpdb->prefix}icl_translations
358 WHERE element_id = %d AND element_type = %s
359 ",
360 $element_id,
361 $element_type
362 )
363 );
364 }
365
366 public function fix_translated_variations_relationships() {
367 self::checkNonce( 'fix_relationships' );
368
369 if ( ! current_user_can( 'manage_options' ) ) {
370 wp_send_json_error( 'Access error' );
371 }
372
373 $response = [
374 'processed' => 0,
375 'complete' => false,
376 ];
377
378 $translatedVariations = $this->getRemainingVariationsForLanguageAssignment();
379 $translatedVariationsInRound = array_slice( $translatedVariations, 0, self::ITEMS_PER_AJAX, true );
380
381 foreach ( $translatedVariationsInRound as $key => $translated_variation ) {
382 $tr_info_for_original_variation = $this->get_translation_info_for_element( $translated_variation->meta_value, 'post_product_variation' );
383
384 $language = $this->sitepress->get_language_for_element( wp_get_post_parent_id( $translated_variation->meta_value ), 'post_product' );
385
386 if ( ! $language ) {
387 unset( $translatedVariations[ $key ] );
388 continue;
389 }
390
391 $language_current = $this->sitepress->get_language_for_element( wp_get_post_parent_id( $translated_variation->post_id ), 'post_product' );
392
393 $tr_info_for_current_variation = $this->get_translation_info_for_element( $translated_variation->post_id, 'post_product_variation' );
394
395 if ( ! $tr_info_for_current_variation ) {
396 $tr_info_for_current_variation = $this->get_translation_info_for_element( $translated_variation->post_id, 'post_product' );
397 if ( $tr_info_for_current_variation ) {
398 $this->wpdb->update( $this->wpdb->prefix . 'icl_translations', [ 'element_type' => 'post_product_variation' ], [ 'translation_id' => $tr_info_for_current_variation->translation_id ] );
399 }
400 }
401
402 $check_duplicated_post_type = $this->get_translation_info_for_element( $translated_variation->post_id, 'post_product' );
403 if ( $check_duplicated_post_type ) {
404 $this->wpdb->delete( $this->wpdb->prefix . 'icl_translations', [ 'translation_id' => $check_duplicated_post_type->translation_id ] );
405 }
406
407 if ( ! $tr_info_for_original_variation ) {
408
409 $tr_info_for_original_variation = $this->get_translation_info_for_element( $translated_variation->meta_value, 'post_product' );
410 if ( $tr_info_for_original_variation ) {
411 $this->wpdb->update( $this->wpdb->prefix . 'icl_translations', [ 'element_type' => 'post_product_variation' ], [ 'translation_id' => $tr_info_for_original_variation->translation_id ] );
412 } else {
413 $this->sitepress->set_element_language_details( $translated_variation->meta_value, 'post_product_variation', $tr_info_for_current_variation->trid, $language );
414 $tr_info_for_original_variation = $this->get_translation_info_for_element( $translated_variation->meta_value, 'post_product' );
415 }
416
417 $this->wpdb->update( $this->wpdb->prefix . 'icl_translations', [ 'source_language_code' => $language ], [ 'translation_id' => $tr_info_for_current_variation->translation_id ] );
418 }
419
420 if ( $tr_info_for_original_variation->trid != $tr_info_for_current_variation->trid ) {
421
422 $this->wpdb->update(
423 $this->wpdb->prefix . 'icl_translations',
424 [
425 'trid' => $tr_info_for_original_variation->trid,
426 'language_code' => $language_current,
427 'source_language_code' => $language,
428 ],
429 [ 'translation_id' => $tr_info_for_current_variation->translation_id ]
430 );
431
432 }
433
434 unset( $translatedVariations[ $key ] );
435 }
436
437 $this->setRemainingVariationsForLanguageAssignment( $translatedVariations );
438
439 $response['processed'] = count( $translatedVariationsInRound );
440 $response['complete'] = empty( $translatedVariations );
441
442 wp_send_json_success( $response );
443 }
444
445 public function trbl_fix_product_type_terms() {
446 self::checkNonce( 'trbl_product_type_terms' );
447
448 WCML_Install::check_product_type_terms();
449
450 $sync_settings = $this->sitepress->get_setting( 'taxonomies_sync_option', [] );
451 $sync_settings['product_type'] = 0;
452 $this->sitepress->set_setting( 'taxonomies_sync_option', $sync_settings, true );
453
454 $response = [
455 'complete' => true,
456 ];
457 wp_send_json_success( $response );
458 }
459
460 public function trbl_duplicate_terms() {
461 self::checkNonce( 'trbl_duplicate_terms' );
462
463 $attr = $_POST['attr'] ?? false;
464 $terms = [];
465
466 if ( $attr ) {
467 $terms = get_terms( $attr, 'hide_empty=0' );
468 $languages = $this->sitepress->get_active_languages();
469 foreach ( $terms as $term ) {
470 foreach ( $languages as $language ) {
471 $tr_id = apply_filters( 'wpml_object_id', $term->term_id, $attr, false, $language['code'] );
472
473 if ( is_null( $tr_id ) ) {
474 $term_args = [];
475 if ( is_taxonomy_hierarchical( $attr ) ) {
476 if ( $term->parent ) {
477 $original_parent_translated = apply_filters( 'wpml_object_id', $term->parent, $attr, false, $language['code'] );
478 if ( $original_parent_translated ) {
479 $term_args['parent'] = $original_parent_translated;
480 }
481 }
482 }
483
484 $term_name = $term->name;
485 $slug = $term->name . '-' . $language['code'];
486 $slug = WPML_Terms_Translations::term_unique_slug( $slug, $attr, $language['code'] );
487 $term_args['slug'] = $slug;
488
489 $new_term = wp_insert_term( $term_name, $attr, $term_args );
490 if ( is_wp_error( $new_term ) ) {
491 $tt_id = $this->sitepress->get_element_trid( $term->term_taxonomy_id, 'tax_' . $attr );
492 $this->sitepress->set_element_language_details( $new_term['term_taxonomy_id'], 'tax_' . $attr, $tt_id, $language['code'] );
493 }
494 }
495 }
496 }
497 }
498
499 $response = [
500 'processed' => count( $terms ),
501 'complete' => true,
502 ];
503 wp_send_json_success( $response );
504 }
505
506 public function register_reviews_in_st() {
507 self::checkNonce( 'register_reviews_in_st' );
508
509 make( \WCML\Reviews\Translations\Mapper::class )->registerMissingReviewStrings();
510
511 $response = [
512 'complete' => true,
513 ];
514 wp_send_json_success( $response );
515 }
516
517 public function getItemsForMetaCleanup( $limit = false ) {
518 $queryLimit = '';
519 if ( $limit ) {
520 $queryLimit = ' ORDER BY p.ID LIMIT ' . self::ITEMS_PER_AJAX_MIN;
521 }
522 return $this->wpdb->get_results(
523 "
524 SELECT p.ID, t.trid, t.element_type
525 FROM {$this->wpdb->posts} p
526 JOIN {$this->wpdb->prefix}icl_translations t ON t.element_id = p.ID AND t.element_type IN ('post_product', 'post_product_variation')
527 WHERE p.post_type in ('product', 'product_variation') AND t.source_language_code IS NULL
528 {$queryLimit}
529 "
530 );
531 }
532
533 public function getRemainingItemsForMetaCleanup() {
534 $itemsForMetaCleanup = get_option( self::OPTION_PRODUCTS_AND_VARIATIONS_FOR_META_CLEANUP );
535 if ( false === $itemsForMetaCleanup ) {
536 $itemsForMetaCleanup = $this->getItemsForMetaCleanup();
537 }
538 return $itemsForMetaCleanup;
539 }
540
541 public function setRemainingItemsForMetaCleanup( $itemsForMetaCleanup ) {
542 if ( empty( $itemsForMetaCleanup ) ) {
543 delete_option( self::OPTION_PRODUCTS_AND_VARIATIONS_FOR_META_CLEANUP );
544 } else {
545 update_option( self::OPTION_PRODUCTS_AND_VARIATIONS_FOR_META_CLEANUP, $itemsForMetaCleanup );
546 }
547 }
548
549 public function sync_deleted_meta() {
550 self::checkNonce( 'sync_deleted_meta' );
551
552 if ( ! current_user_can( 'manage_options' ) ) {
553 wp_send_json_error( 'Access error' );
554 }
555
556 $response = [
557 'processed' => 0,
558 'complete' => false,
559 ];
560
561 $itemsForMetaCleanup = $this->getRemainingItemsForMetaCleanup();
562 $getItemsForMetaCleanupInRound = array_slice( $itemsForMetaCleanup, 0, self::ITEMS_PER_AJAX, true );
563
564 $iclTranslationManagement = wpml_load_core_tm();
565 $settings_factory = new WPML_Custom_Field_Setting_Factory( $iclTranslationManagement );
566
567 foreach ( $getItemsForMetaCleanupInRound as $key => $product ) {
568
569 $translations = $this->sitepress->get_element_translations( $product->trid, $product->element_type );
570
571 foreach ( $translations as $translation ) {
572 if ( ! $translation->original ) {
573 $all_post_meta_keys = $this->wpdb->get_col( $this->wpdb->prepare( "SELECT meta_key FROM {$this->wpdb->postmeta} WHERE post_id = %d", $translation->element_id ) );
574 foreach ( $all_post_meta_keys as $meta_key ) {
575 $setting = $settings_factory->post_meta_setting( $meta_key );
576 if ( WPML_COPY_CUSTOM_FIELD === $setting->status() ) {
577 if ( ! metadata_exists( 'post', $product->ID, $meta_key ) ) {
578 delete_post_meta( $translation->element_id, $meta_key );
579 }
580 }
581 }
582 }
583 }
584
585 unset( $itemsForMetaCleanup[ $key ] );
586 }
587
588 $this->setRemainingItemsForMetaCleanup( $itemsForMetaCleanup );
589
590 $response['processed'] = count( $getItemsForMetaCleanupInRound );
591 $response['complete'] = empty( $itemsForMetaCleanup );
592
593 wp_send_json_success( $response );
594 }
595
596 private static function checkNonce( $action ) {
597 $nonce = filter_var( Obj::prop( 'wcml_nonce', $_POST ), FILTER_SANITIZE_FULL_SPECIAL_CHARS );
598 if ( ! $nonce || ! wp_verify_nonce( $nonce, $action ) ) {
599 wp_send_json_error( 'Invalid nonce' );
600 }
601 }
602 }
603