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 |