admin-menus
3 weeks ago
currencies
3 weeks ago
template-classes
3 weeks ago
translation-editor
3 weeks ago
class-wcml-ajax-setup.php
3 weeks ago
class-wcml-attributes.php
3 weeks ago
class-wcml-capabilities.php
3 weeks ago
class-wcml-cart-removed-items-widget.php
1 year ago
class-wcml-cart-switch-lang-functions.php
3 weeks ago
class-wcml-cart-sync-warnings.php
3 weeks ago
class-wcml-cart.php
3 weeks ago
class-wcml-comments.php
3 weeks ago
class-wcml-compatibility.php
3 weeks ago
class-wcml-coupons.php
3 weeks ago
class-wcml-dependencies.php
3 weeks ago
class-wcml-emails.php
3 weeks ago
class-wcml-endpoints.php
3 weeks ago
class-wcml-install.php
3 weeks ago
class-wcml-languages-upgrader.php
3 weeks ago
class-wcml-locale.php
3 weeks ago
class-wcml-orders.php
3 weeks ago
class-wcml-products-screen-options.php
3 weeks ago
class-wcml-products.php
3 weeks ago
class-wcml-reports.php
3 weeks ago
class-wcml-requests.php
3 weeks ago
class-wcml-resources.php
3 weeks ago
class-wcml-store-pages.php
3 weeks ago
class-wcml-terms.php
3 weeks ago
class-wcml-tp-support.php
3 weeks ago
class-wcml-troubleshooting.php
3 weeks ago
class-wcml-upgrade.php
3 weeks ago
class-wcml-url-translation.php
3 weeks ago
class-wcml-wc-gateways.php
3 weeks ago
class-wcml-wc-shipping.php
3 weeks ago
class-wcml-wc-strings.php
3 weeks ago
class-wcml-widgets.php
3 weeks ago
constants.php
3 weeks ago
functions-pure.php
3 weeks ago
functions.php
3 weeks ago
installer-loader.php
3 weeks ago
missing-php-functions.php
3 weeks ago
wcml-core-functions.php
3 weeks ago
wcml-switch-lang-request.php
3 weeks ago
class-wcml-products.php
728 lines
| 1 | <?php |
| 2 | |
| 3 | use WCML\Utilities\DB; |
| 4 | use WPML\Core\ISitePress; |
| 5 | use WPML\FP\Fns; |
| 6 | use WPML\FP\Obj; |
| 7 | use function WCML\functions\isStandAlone; |
| 8 | use function WPML\Container\make; |
| 9 | use function WPML\FP\partial; |
| 10 | use function WPML\FP\pipe; |
| 11 | |
| 12 | class WCML_Products { |
| 13 | |
| 14 | const PRODUCT_INVENTORY_META_TYPE_SKU = '_sku'; |
| 15 | const PRODUCT_INVENTORY_META_TYPE_GLOBAL_UID = '_global_unique_id'; |
| 16 | |
| 17 | private $woocommerce_wpml; |
| 18 | private $sitepress; |
| 19 | private $post_translations; |
| 20 | private $wpdb; |
| 21 | |
| 22 | public function __construct( woocommerce_wpml $woocommerce_wpml, ISitePress $sitepress, $post_translations = null, $wpdb = null ) { |
| 23 | $this->woocommerce_wpml = $woocommerce_wpml; |
| 24 | $this->sitepress = $sitepress; |
| 25 | $this->post_translations = $post_translations; |
| 26 | |
| 27 | if ( null === $wpdb ) { |
| 28 | global $wpdb; |
| 29 | } |
| 30 | $this->wpdb = $wpdb; |
| 31 | } |
| 32 | |
| 33 | public function add_hooks() { |
| 34 | if ( is_admin() ) { |
| 35 | if ( ! isStandAlone() ) { |
| 36 | add_filter( 'woocommerce_json_search_found_products', [ $this, 'filter_wc_searched_products_on_admin' ] ); |
| 37 | add_action( 'wp_ajax_wpml_switch_post_language', [ $this, 'switch_product_variations_language' ], 9 ); |
| 38 | add_filter( 'post_row_actions', [ $this, 'filter_product_actions' ], 10, 2 ); |
| 39 | add_filter( 'woocommerce_product_type_query', [ $this, 'override_product_type_query' ], 10, 2 ); |
| 40 | } |
| 41 | } else { |
| 42 | add_filter( 'woocommerce_related_products_args', [ $this, 'filter_related_products_args' ] ); |
| 43 | |
| 44 | if ( ! isStandAlone() ) { |
| 45 | add_filter( 'woocommerce_json_search_found_products', [ $this, 'filter_wc_searched_products_on_front' ] ); |
| 46 | add_filter( 'woocommerce_product_related_posts_query', [ $this, 'filter_related_products_query' ] ); |
| 47 | add_filter( 'woocommerce_shortcode_products_query', [ $this, 'add_lang_to_shortcode_products_query' ] ); |
| 48 | add_filter( 'woocommerce_product_file_download_path', [ $this, 'filter_file_download_path' ] ); |
| 49 | add_filter( 'woocommerce_product_add_to_cart_url', [ $this, 'maybe_add_language_parameter' ] ); |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | if ( ! isStandAlone() ) { |
| 54 | add_filter( 'woocommerce_upsell_crosssell_search_products', [ $this, 'filter_woocommerce_upsell_crosssell_posts_by_language' ] ); |
| 55 | add_action( 'woocommerce_after_product_ordering', [ $this, 'update_all_products_translations_ordering' ] ); |
| 56 | add_filter( 'wpml_copy_from_original_custom_fields', [ $this, 'filter_excerpt_field_content_copy' ] ); |
| 57 | add_filter( 'wpml_override_is_translator', [ $this, 'wcml_override_is_translator' ], 10, 3 ); |
| 58 | add_filter( 'wpml_user_can_translate', [ $this, 'wcml_user_can_translate' ], 10, 2 ); |
| 59 | add_filter( 'wc_product_has_unique_sku', [ $this, 'check_product_sku' ], 10, 3 ); |
| 60 | add_filter( 'wc_product_has_global_unique_id', [ $this, 'check_product_has_global_unique_id' ], 10, 3 ); |
| 61 | add_filter( 'get_product_search_form', [ $this->sitepress, 'get_search_form_filter' ] ); |
| 62 | add_filter( 'woocommerce_pre_customer_bought_product', Fns::withoutRecursion( Fns::identity(), [ $this, 'is_customer_bought_product' ] ), 10, 4 ); |
| 63 | } |
| 64 | |
| 65 | add_filter( 'get_post_metadata', [ $this, 'filter_product_data' ], PHP_INT_MAX, 3 ); |
| 66 | add_filter( 'woocommerce_can_reduce_order_stock', [ $this, 'remove_post_meta_data_filter_on_checkout_stock_update' ] ); |
| 67 | } |
| 68 | |
| 69 | public function is_original_product( $product_id ) { |
| 70 | return ! $this->post_translations || null === $this->post_translations->get_source_lang_code( $product_id ); |
| 71 | } |
| 72 | |
| 73 | public function get_original_product_language( $product_id ) { |
| 74 | return $this->post_translations |
| 75 | ? $this->post_translations->get_element_lang_code( $this->get_original_product_id( $product_id ) ) |
| 76 | : $this->sitepress->get_default_language(); |
| 77 | } |
| 78 | |
| 79 | public function get_original_product_id( $product_id ) { |
| 80 | |
| 81 | $original_product_id = $this->post_translations ? $this->post_translations->get_original_element( $product_id ) : null; |
| 82 | |
| 83 | return $original_product_id ?: $product_id; |
| 84 | } |
| 85 | |
| 86 | public function isVariableProductObject( $product ) { |
| 87 | if ( $product instanceOf WC_Product_Variable ) { |
| 88 | return true; |
| 89 | } |
| 90 | $productId = $product->get_id(); |
| 91 | return $this->is_variable_product( $productId ); |
| 92 | } |
| 93 | |
| 94 | public function is_variable_product( $product_id ) { |
| 95 | $cache_key = $product_id; |
| 96 | $cache_group = 'is_variable_product'; |
| 97 | $temp_is_variable = wp_cache_get( $cache_key, $cache_group ); |
| 98 | if ( $temp_is_variable ) { |
| 99 | return $temp_is_variable; |
| 100 | } |
| 101 | |
| 102 | $get_variation_term_taxonomy_ids = $this->wpdb->get_col( "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' AND tt.taxonomy = 'product_type'" ); |
| 103 | $get_variation_term_taxonomy_ids = apply_filters( 'wcml_variation_term_taxonomy_ids', (array) $get_variation_term_taxonomy_ids ); |
| 104 | |
| 105 | $is_variable_product = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT count(object_id) FROM {$this->wpdb->term_relationships} WHERE object_id = %d AND term_taxonomy_id IN (" . DB::prepareIn( $get_variation_term_taxonomy_ids, '%d' ) . ')', $product_id ) ); |
| 106 | $is_variable_product = apply_filters( 'wcml_is_variable_product', $is_variable_product, $product_id ); |
| 107 | |
| 108 | wp_cache_set( $cache_key, $is_variable_product, $cache_group ); |
| 109 | |
| 110 | return $is_variable_product; |
| 111 | } |
| 112 | |
| 113 | public function is_downloadable_product( $product ) { |
| 114 | return $this->woocommerce_wpml->downloadable->isDownloadableProduct( $product ); |
| 115 | } |
| 116 | |
| 117 | public function is_grouped_product( $product_id ) { |
| 118 | $get_variation_term_taxonomy_id = $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 = 'grouped'" ); |
| 119 | $is_grouped_product = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT count(object_id) FROM {$this->wpdb->term_relationships} WHERE object_id = %d AND term_taxonomy_id = %d ", $product_id, $get_variation_term_taxonomy_id ) ); |
| 120 | |
| 121 | return $is_grouped_product; |
| 122 | } |
| 123 | |
| 124 | public function get_translation_flags( $active_languages, $slang = false, $job_language = false ) { |
| 125 | $available_languages = []; |
| 126 | |
| 127 | foreach ( $active_languages as $key => $language ) { |
| 128 | if ( $job_language && $language['code'] != $job_language ) { |
| 129 | continue; |
| 130 | } elseif ( ! $slang || |
| 131 | ( |
| 132 | ( $slang != $language['code'] ) && |
| 133 | ( current_user_can( 'wpml_operate_woocommerce_multilingual' ) || |
| 134 | wpml_check_user_is_translator( $slang, $language['code'] ) ) && |
| 135 | ( ! isset( $_POST['translation_status_lang'] ) || |
| 136 | ( isset( $_POST['translation_status_lang'] ) && |
| 137 | ( $_POST['translation_status_lang'] == $language['code'] ) || |
| 138 | $_POST['translation_status_lang'] == '' ) |
| 139 | ) |
| 140 | ) |
| 141 | ) { |
| 142 | $available_languages[ $key ]['name'] = $language['english_name']; |
| 143 | $available_languages[ $key ]['flag_url'] = $this->sitepress->get_flag_url( $language['code'] ); |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | return $available_languages; |
| 148 | } |
| 149 | |
| 150 | public function get_translation_statuses( $original_product_id, $product_translations, $active_languages, $slang = false, $trid = false, $job_language = false ) { |
| 151 | $status_display_factory = new WPML_Post_Status_Display_Factory( $this->sitepress ); |
| 152 | $status_display = $status_display_factory->create(); |
| 153 | foreach ( $active_languages as $language ) { |
| 154 | if ( $job_language && $language['code'] != $job_language ) { |
| 155 | continue; |
| 156 | } elseif ( isset( $product_translations[ $language['code'] ] ) && $product_translations[ $language['code'] ]->original ) { ?> |
| 157 | <span title="<?php echo esc_attr( $language['english_name'] ) . ': ' . __( 'Original language', 'woocommerce-multilingual' ); ?>"> |
| 158 | <i class="otgs-ico-original"></i> |
| 159 | </span> |
| 160 | <?php |
| 161 | } elseif ( |
| 162 | $slang != $language['code'] && |
| 163 | ( ! isset( $_POST['translation_status_lang'] ) || |
| 164 | ( isset( $_POST['translation_status_lang'] ) && |
| 165 | $_POST['translation_status_lang'] == $language['code'] || |
| 166 | $_POST['translation_status_lang'] == '' |
| 167 | ) |
| 168 | ) |
| 169 | ) { |
| 170 | if ( isset( $product_translations[ $language['code'] ] ) ) { |
| 171 | $job_id = $this->wpdb->get_var( |
| 172 | $this->wpdb->prepare( |
| 173 | "SELECT MAX(tj.job_id) FROM {$this->wpdb->prefix}icl_translate_job AS tj |
| 174 | LEFT JOIN {$this->wpdb->prefix}icl_translation_status AS ts |
| 175 | ON tj.rid = ts.rid WHERE ts.translation_id=%d", |
| 176 | $product_translations[ $language['code'] ]->translation_id |
| 177 | ) |
| 178 | ); |
| 179 | } else { |
| 180 | $job_id = false; |
| 181 | } |
| 182 | |
| 183 | if ( ! current_user_can( 'wpml_manage_woocommerce_multilingual' ) && isset( $product_translations[ $language['code'] ] ) ) { |
| 184 | $tr_status = $this->wpdb->get_row( |
| 185 | $this->wpdb->prepare( |
| 186 | "SELECT status,translator_id FROM {$this->wpdb->prefix}icl_translation_status |
| 187 | WHERE translation_id = %d", |
| 188 | $product_translations[ $language['code'] ]->translation_id |
| 189 | ) |
| 190 | ); |
| 191 | |
| 192 | if ( is_object( $tr_status ) && get_current_user_id() != $tr_status->translator_id ) { |
| 193 | if ( $tr_status->status == ICL_TM_IN_PROGRESS ) { |
| 194 | ?> |
| 195 | <a title="<?php _e( 'Translation in progress', 'woocommerce-multilingual' ); ?>"><i |
| 196 | class="otgs-ico-in-progress"></i></a> |
| 197 | <?php |
| 198 | continue; |
| 199 | } elseif ( $tr_status->status == ICL_TM_WAITING_FOR_TRANSLATOR && ! $job_id ) { |
| 200 | $tr_job_id = $this->wpdb->get_var( |
| 201 | $this->wpdb->prepare( |
| 202 | "SELECT j.job_id FROM {$this->wpdb->prefix}icl_translate_job j |
| 203 | JOIN {$this->wpdb->prefix}icl_translation_status s ON j.rid = s.rid |
| 204 | WHERE s.translation_id = %d", |
| 205 | $product_translations[ $language['code'] ]->translation_id |
| 206 | ) |
| 207 | ); |
| 208 | $translation_queue_page = admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/translations-queue.php&job_id=' . $tr_job_id ); |
| 209 | $edit_url = apply_filters( 'icl_job_edit_url', $translation_queue_page, $tr_job_id ); |
| 210 | ?> |
| 211 | <a href="<?php echo esc_url( $edit_url ); ?>" title="<?php echo esc_attr( $language['english_name'] ) . ': ' . __( 'Take this and edit', 'woocommerce-multilingual' ); ?>"> |
| 212 | <i class="otgs-ico-add"></i> |
| 213 | </a> |
| 214 | <?php |
| 215 | continue; |
| 216 | } |
| 217 | } |
| 218 | wpml_tm_load_status_display_filter(); |
| 219 | } |
| 220 | echo $status_display->get_status_html( $original_product_id, $language['code'] ); |
| 221 | } |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | public function wcml_override_is_translator( $is_translator, $user_id, $args ) { |
| 226 | if ( current_user_can( 'wpml_operate_woocommerce_multilingual' ) ) { |
| 227 | if ( ! ( isset( $args['post_id'] ) && $args['post_id'] ) ) { |
| 228 | return true; |
| 229 | } |
| 230 | $wc_post_types = [ 'product', 'product_variation', 'shop_coupon', 'shop_order', 'shop_order_refund' ]; |
| 231 | $post_type = get_post_type( $args['post_id'] ); |
| 232 | if ( in_array( $post_type, $wc_post_types, true ) ) { |
| 233 | return true; |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | return $is_translator; |
| 238 | } |
| 239 | |
| 240 | public function wcml_user_can_translate( $user_can_translate, $user ) { |
| 241 | if ( user_can( $user, 'wpml_operate_woocommerce_multilingual' ) ) { |
| 242 | return true; |
| 243 | } |
| 244 | |
| 245 | return $user_can_translate; |
| 246 | } |
| 247 | |
| 248 | public function filter_product_actions( $actions, $post ) { |
| 249 | if ( |
| 250 | $post->post_type == 'product' && |
| 251 | ! $this->is_original_product( $post->ID ) && |
| 252 | isset( $actions['inline hide-if-no-js'] ) ) { |
| 253 | $new_actions = []; |
| 254 | foreach ( $actions as $key => $action ) { |
| 255 | if ( $key == 'inline hide-if-no-js' ) { |
| 256 | $new_actions['quick_hide'] = '<a href="#TB_inline?width=200&height=150&inlineId=quick_edit_notice" class="thickbox" title="' . |
| 257 | esc_attr( |
| 258 | sprintf( |
| 259 | /* translators: %s is the post title */ |
| 260 | __( 'Quick edit "%s" inline', 'woocommerce-multilingual' ), |
| 261 | $post->post_title |
| 262 | ) |
| 263 | ) . '">' . |
| 264 | esc_html__( 'Quick Edit', 'woocommerce-multilingual' ) . '</a>'; |
| 265 | } else { |
| 266 | $new_actions[ $key ] = $action; |
| 267 | } |
| 268 | } |
| 269 | return $new_actions; |
| 270 | } |
| 271 | return $actions; |
| 272 | } |
| 273 | |
| 274 | public function filter_woocommerce_upsell_crosssell_posts_by_language( $posts ) { |
| 275 | foreach ( $posts as $key => $post ) { |
| 276 | $post_id = $post->ID; |
| 277 | $post_data = $this->wpdb->get_row( |
| 278 | $this->wpdb->prepare( |
| 279 | "SELECT * FROM {$this->wpdb->prefix}icl_translations |
| 280 | WHERE element_id = %d ", |
| 281 | $post_id |
| 282 | ), |
| 283 | ARRAY_A |
| 284 | ); |
| 285 | |
| 286 | if ( $post_data['language_code'] !== $this->sitepress->get_current_language() ) { |
| 287 | unset( $posts[ $key ] ); |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | return $posts; |
| 292 | } |
| 293 | |
| 294 | |
| 295 | private function filter_found_products_by_language( $found_products, $language = false ) { |
| 296 | if ( null === $this->post_translations ) { |
| 297 | return $found_products; |
| 298 | } |
| 299 | |
| 300 | if ( ! $language ) { |
| 301 | $language = $this->sitepress->get_current_language(); |
| 302 | } |
| 303 | |
| 304 | foreach ( $found_products as $product_id => $product_name ) { |
| 305 | |
| 306 | if ( $this->post_translations->get_element_lang_code( $product_id ) !== $language ) { |
| 307 | unset( $found_products[ $product_id ] ); |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | return $found_products; |
| 312 | } |
| 313 | |
| 314 | public function filter_wc_searched_products_on_front( $found_products ) { |
| 315 | return $this->filter_found_products_by_language( $found_products ); |
| 316 | } |
| 317 | |
| 318 | public function filter_wc_searched_products_on_admin( $found_products ) { |
| 319 | |
| 320 | if ( isset( $_COOKIE['_wcml_dashboard_order_language'] ) ) { |
| 321 | $found_products = $this->filter_found_products_by_language( $found_products, $_COOKIE['_wcml_dashboard_order_language'] ); |
| 322 | } else { |
| 323 | $found_products = $this->filter_found_products_by_language( $found_products ); |
| 324 | } |
| 325 | |
| 326 | return $found_products; |
| 327 | } |
| 328 | |
| 329 | public function update_all_products_translations_ordering() { |
| 330 | if ( $this->woocommerce_wpml->settings['products_sync_order'] ) { |
| 331 | $current_language = $this->sitepress->get_current_language(); |
| 332 | $default_language = $this->sitepress->get_default_language(); |
| 333 | if ( $current_language === $default_language ) { |
| 334 | $this->wpdb->query( |
| 335 | $this->wpdb->prepare( " |
| 336 | UPDATE {$this->wpdb->posts} posts |
| 337 | JOIN {$this->wpdb->prefix}icl_translations trans ON posts.ID = trans.element_id AND trans.element_type = 'post_product' |
| 338 | SET posts.menu_order = ( |
| 339 | SELECT menu_order FROM {$this->wpdb->posts} |
| 340 | JOIN {$this->wpdb->prefix}icl_translations ON ID = element_id AND element_type = 'post_product' |
| 341 | WHERE language_code = '%s' AND trid = trans.trid |
| 342 | ) |
| 343 | WHERE trans.language_code <> %s |
| 344 | ", |
| 345 | $default_language, |
| 346 | $default_language |
| 347 | ) |
| 348 | ); |
| 349 | } |
| 350 | } |
| 351 | } |
| 352 | |
| 353 | public function update_order_for_product_translations( $product_id ) { |
| 354 | if ( null === $this->post_translations ) { |
| 355 | return; |
| 356 | } |
| 357 | |
| 358 | if ( isset( $this->woocommerce_wpml->settings['products_sync_order'] ) && $this->woocommerce_wpml->settings['products_sync_order'] ) { |
| 359 | $current_language = $this->sitepress->get_current_language(); |
| 360 | |
| 361 | if ( $current_language == $this->sitepress->get_default_language() ) { |
| 362 | $translations = array_diff( |
| 363 | $this->post_translations->get_element_translations( $product_id ), |
| 364 | [ $product_id ] |
| 365 | ); |
| 366 | |
| 367 | if ( $translations ) { |
| 368 | $menu_order = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT menu_order FROM {$this->wpdb->posts} WHERE ID = %d", $product_id ) ); |
| 369 | |
| 370 | $this->wpdb->query( |
| 371 | $this->wpdb->prepare( |
| 372 | "UPDATE {$this->wpdb->posts} SET menu_order = %d WHERE ID IN (" . wpml_prepare_in( $translations, '%d' ) . ')', |
| 373 | $menu_order |
| 374 | ) |
| 375 | ); |
| 376 | } |
| 377 | } |
| 378 | } |
| 379 | } |
| 380 | |
| 381 | public function filter_excerpt_field_content_copy( $elements ) { |
| 382 | |
| 383 | if ( $elements['post_type'] == 'product' ) { |
| 384 | $elements['excerpt'] ['editor_type'] = 'editor'; |
| 385 | } |
| 386 | if ( function_exists( 'format_for_editor' ) ) { |
| 387 | $elements['excerpt']['value'] = htmlspecialchars_decode( format_for_editor( $elements['excerpt']['value'], $_POST['excerpt_type'] ) ); |
| 388 | } else { |
| 389 | if ( $_POST['excerpt_type'] == 'rich' ) { |
| 390 | $elements['excerpt']['value'] = htmlspecialchars_decode( wp_richedit_pre( $elements['excerpt']['value'] ) ); |
| 391 | } else { |
| 392 | $elements['excerpt']['value'] = htmlspecialchars_decode( wp_htmledit_pre( $elements['excerpt']['value'] ) ); |
| 393 | } |
| 394 | } |
| 395 | return $elements; |
| 396 | } |
| 397 | |
| 398 | public function filter_related_products_args( $args ) { |
| 399 | if ( $this->woocommerce_wpml->settings['enable_multi_currency'] == WCML_MULTI_CURRENCIES_INDEPENDENT && |
| 400 | isset( $this->woocommerce_wpml->settings['display_custom_prices'] ) && |
| 401 | $this->woocommerce_wpml->settings['display_custom_prices'] ) { |
| 402 | |
| 403 | $client_currency = $this->woocommerce_wpml->multi_currency->get_client_currency(); |
| 404 | $woocommerce_currency = wcml_get_woocommerce_currency_option(); |
| 405 | |
| 406 | if ( $client_currency != $woocommerce_currency ) { |
| 407 | $args['meta_query'][] = [ |
| 408 | 'key' => '_wcml_custom_prices_status', |
| 409 | 'value' => 1, |
| 410 | 'compare' => '=', |
| 411 | ]; |
| 412 | } |
| 413 | } |
| 414 | return $args; |
| 415 | } |
| 416 | |
| 417 | public function filter_related_products_query( $query ) { |
| 418 | |
| 419 | $query['join'] .= " LEFT JOIN {$this->wpdb->prefix}icl_translations AS icl ON icl.element_id = p.ID "; |
| 420 | $query['where'] .= $this->wpdb->prepare( ' AND icl.language_code = %s ', $this->sitepress->get_current_language() ); |
| 421 | |
| 422 | return $query; |
| 423 | } |
| 424 | |
| 425 | public function get_mid_ids_by_key( $post_id, $meta_key ) { |
| 426 | $ids = $this->wpdb->get_col( $this->wpdb->prepare( "SELECT meta_id FROM {$this->wpdb->postmeta} WHERE post_id = %d AND meta_key = %s", $post_id, $meta_key ) ); |
| 427 | if ( $ids ) { |
| 428 | return $ids; |
| 429 | } |
| 430 | |
| 431 | return false; |
| 432 | } |
| 433 | |
| 434 | public function get_untranslated_products_count( $language ) { |
| 435 | |
| 436 | $count = 0; |
| 437 | |
| 438 | $products = $this->wpdb->get_results( |
| 439 | " |
| 440 | SELECT p.ID, t.trid, t.language_code |
| 441 | FROM {$this->wpdb->posts} AS p |
| 442 | LEFT JOIN {$this->wpdb->prefix}icl_translations AS t ON t.element_id = p.id |
| 443 | WHERE p.post_type = 'product' AND t.element_type = 'post_product' AND t.source_language_code IS NULL |
| 444 | " |
| 445 | ); |
| 446 | |
| 447 | foreach ( $products as $product ) { |
| 448 | if ( $product->language_code == $language ) { |
| 449 | continue; |
| 450 | } |
| 451 | |
| 452 | $translation_status = apply_filters( 'wpml_translation_status', null, $product->trid, $language ); |
| 453 | |
| 454 | if ( in_array( $translation_status, [ ICL_TM_NOT_TRANSLATED, ICL_TM_WAITING_FOR_TRANSLATOR, ICL_TM_IN_PROGRESS ] ) ) { |
| 455 | $count++; |
| 456 | } |
| 457 | } |
| 458 | |
| 459 | return $count; |
| 460 | } |
| 461 | |
| 462 | public function is_hide_resign_button() { |
| 463 | global $iclTranslationManagement; |
| 464 | |
| 465 | $hide_resign = false; |
| 466 | |
| 467 | if ( isset( $_GET['source_language_code'] ) && isset( $_GET['language_code'] ) ) { |
| 468 | |
| 469 | $from_lang = $_GET['source_language_code']; |
| 470 | $to_lang = $_GET['language_code']; |
| 471 | |
| 472 | } elseif ( isset( $_GET['job_id'] ) ) { |
| 473 | |
| 474 | $job = $iclTranslationManagement->get_translation_job( $_GET['job_id'] ); |
| 475 | if ( $job ) { |
| 476 | $from_lang = $job->source_language_code; |
| 477 | $to_lang = $job->language_code; |
| 478 | } |
| 479 | } |
| 480 | |
| 481 | if ( isset( $from_lang, $to_lang ) ) { |
| 482 | |
| 483 | $translators = $iclTranslationManagement->get_blog_translators( |
| 484 | [ |
| 485 | 'from' => $from_lang, |
| 486 | 'to' => $to_lang, |
| 487 | ] |
| 488 | ); |
| 489 | |
| 490 | if ( empty( $translators ) || ( sizeof( $translators ) == 1 && $translators[0]->ID == get_current_user_id() ) ) { |
| 491 | $hide_resign = true; |
| 492 | } |
| 493 | } |
| 494 | |
| 495 | return $hide_resign; |
| 496 | } |
| 497 | |
| 498 | public function switch_product_variations_language() { |
| 499 | if ( ! isset( $_POST['nonce'] ) ) { |
| 500 | return; |
| 501 | } |
| 502 | if ( ! wp_verify_nonce( $_POST['nonce'], \WPML_Post_Edit_Ajax::AJAX_ACTION_SWITCH_POST_LANGUAGE ) ) { |
| 503 | return; |
| 504 | } |
| 505 | if ( ! current_user_can( 'edit_products' ) ) { |
| 506 | return; |
| 507 | } |
| 508 | |
| 509 | $lang_to = false; |
| 510 | $post_id = false; |
| 511 | |
| 512 | if ( isset( $_POST['wpml_to'] ) ) { |
| 513 | $lang_to = $_POST['wpml_to']; |
| 514 | } |
| 515 | if ( isset( $_POST['wpml_post_id'] ) ) { |
| 516 | $post_id = $_POST['wpml_post_id']; |
| 517 | } |
| 518 | |
| 519 | if ( $post_id && $lang_to && get_post_type( $post_id ) == 'product' ) { |
| 520 | $product_variations = $this->woocommerce_wpml->sync_variations_data->get_product_variations( $post_id ); |
| 521 | foreach ( $product_variations as $product_variation ) { |
| 522 | $trid = $this->sitepress->get_element_trid( $product_variation->ID, 'post_product_variation' ); |
| 523 | $current_prod_variation_id = apply_filters( 'wpml_object_id', $product_variation->ID, 'product_variation', false, $lang_to ); |
| 524 | if ( is_null( $current_prod_variation_id ) ) { |
| 525 | $this->sitepress->set_element_language_details( $product_variation->ID, 'post_product_variation', $trid, $lang_to ); |
| 526 | |
| 527 | foreach ( get_post_custom( $product_variation->ID ) as $meta_key => $meta ) { |
| 528 | foreach ( $meta as $meta_value ) { |
| 529 | if ( substr( $meta_key, 0, 10 ) == 'attribute_' ) { |
| 530 | $trn_post_meta = $this->woocommerce_wpml->attributes->get_translated_variation_attribute_post_meta( $meta_value, $meta_key, $product_variation->ID, $product_variation->ID, $lang_to ); |
| 531 | update_post_meta( $product_variation->ID, $trn_post_meta['meta_key'], $trn_post_meta['meta_value'] ); |
| 532 | } |
| 533 | } |
| 534 | } |
| 535 | } |
| 536 | } |
| 537 | } |
| 538 | } |
| 539 | |
| 540 | public function check_product_sku( $has_unique_sku, $product_id, $sku ) { |
| 541 | return $this->check_product_inventory_uid( $has_unique_sku, $product_id, $sku, self::PRODUCT_INVENTORY_META_TYPE_SKU ); |
| 542 | } |
| 543 | |
| 544 | public function check_product_has_global_unique_id( $global_unique_id_found, $product_id, $global_unique_id ) { |
| 545 | return $this->check_product_inventory_uid( $global_unique_id_found, $product_id, $global_unique_id, self::PRODUCT_INVENTORY_META_TYPE_GLOBAL_UID ); |
| 546 | } |
| 547 | |
| 548 | private function check_product_inventory_uid( $is_product_inventory_uid_found, $product_id, $product_inventory_uid, $product_inventory_meta_type ) { |
| 549 | if ( null === $this->post_translations ) { |
| 550 | return $is_product_inventory_uid_found; |
| 551 | } |
| 552 | |
| 553 | if ( $is_product_inventory_uid_found ) { |
| 554 | |
| 555 | $product_trid = $this->post_translations->get_element_trid( $product_id ); |
| 556 | |
| 557 | $products = $this->wpdb->get_results( |
| 558 | $this->wpdb->prepare( |
| 559 | " |
| 560 | SELECT p.ID |
| 561 | FROM {$this->wpdb->posts} as p |
| 562 | LEFT JOIN {$this->wpdb->postmeta} as pm ON ( p.ID = pm.post_id ) |
| 563 | WHERE p.post_type IN ( 'product', 'product_variation' ) |
| 564 | AND p.post_status = 'publish' |
| 565 | AND pm.meta_key = '%s' AND pm.meta_value = '%s' |
| 566 | ", |
| 567 | $product_inventory_meta_type, |
| 568 | wp_slash( $product_inventory_uid ) |
| 569 | ) |
| 570 | ); |
| 571 | |
| 572 | $is_product_inventory_uid_found = false; |
| 573 | |
| 574 | foreach ( (array) $products as $product ) { |
| 575 | $trid = $this->post_translations->get_element_trid( $product->ID ); |
| 576 | if ( $product_trid !== $trid ) { |
| 577 | $is_product_inventory_uid_found = true; |
| 578 | break; |
| 579 | } |
| 580 | } |
| 581 | } |
| 582 | |
| 583 | return $is_product_inventory_uid_found; |
| 584 | } |
| 585 | |
| 586 | public function add_lang_to_shortcode_products_query( $query_args ) { |
| 587 | |
| 588 | $query_args['lang'] = $this->sitepress->get_current_language(); |
| 589 | |
| 590 | return $query_args; |
| 591 | } |
| 592 | |
| 593 | |
| 594 | public function filter_file_download_path( $file_path ) { |
| 595 | |
| 596 | $is_per_domain = $this->sitepress->get_wp_api()->constant( 'WPML_LANGUAGE_NEGOTIATION_TYPE_DOMAIN' ) === (int) $this->sitepress->get_setting( 'language_negotiation_type' ); |
| 597 | |
| 598 | if ( $is_per_domain ) { |
| 599 | $wpml_url_converter = make( \WPML_URL_Converter::class ); |
| 600 | |
| 601 | if ( strpos( trim( $file_path ), $wpml_url_converter->get_abs_home() ) === 0 ) { |
| 602 | $file_path = $this->sitepress->convert_url( $file_path ); |
| 603 | } |
| 604 | } |
| 605 | |
| 606 | return $file_path; |
| 607 | |
| 608 | } |
| 609 | |
| 610 | |
| 611 | public function is_product_display_as_translated_post_type() { |
| 612 | return apply_filters( 'wpml_is_display_as_translated_post_type', false, 'product' ); |
| 613 | } |
| 614 | |
| 615 | public function is_customer_bought_product( $value, $customer_email, $user_id, $product_id ) { |
| 616 | if ( $value ) { |
| 617 | return $value; |
| 618 | } |
| 619 | |
| 620 | $post_type = get_post_type( $product_id ); |
| 621 | $trid = apply_filters( 'wpml_element_trid', 0, $product_id, 'post_' . $post_type ); |
| 622 | |
| 623 | $has_bought_original_or_translation = pipe( |
| 624 | Obj::prop( 'element_id' ), |
| 625 | partial( 'wc_customer_bought_product', $customer_email, $user_id ) |
| 626 | ); |
| 627 | |
| 628 | return (bool) wpml_collect( |
| 629 | apply_filters( 'wpml_get_element_translations', [], $trid, $post_type ) |
| 630 | )->first( $has_bought_original_or_translation ); |
| 631 | } |
| 632 | |
| 633 | public function filter_product_data( $data, $product_id, $meta_key ) { |
| 634 | |
| 635 | if ( ! $meta_key ) { |
| 636 | |
| 637 | $post_type = get_post_type( $product_id ); |
| 638 | |
| 639 | if ( in_array( $post_type, [ 'product', 'product_variation' ], true ) ) { |
| 640 | remove_filter( 'get_post_metadata', [ $this, 'filter_product_data' ], PHP_INT_MAX ); |
| 641 | |
| 642 | $data = get_post_meta( $product_id ); |
| 643 | $meta_keys_to_filter = []; |
| 644 | $is_mc_enabled = (int) $this->woocommerce_wpml->settings['enable_multi_currency'] === (int) $this->sitepress->get_wp_api()->constant( 'WCML_MULTI_CURRENCIES_INDEPENDENT' ); |
| 645 | |
| 646 | if ( $is_mc_enabled ) { |
| 647 | $meta_keys_to_filter = wcml_price_custom_fields( $product_id ); |
| 648 | } |
| 649 | |
| 650 | if ( ! is_admin() && ! isStandAlone() ) { |
| 651 | if ( 'product' === $post_type ) { |
| 652 | $meta_keys_to_filter[] = WCML_Comments::WC_RATING_COUNT_KEY; |
| 653 | $meta_keys_to_filter[] = WCML_Comments::WC_REVIEW_COUNT_KEY; |
| 654 | $meta_keys_to_filter[] = WCML_Comments::WC_AVERAGE_RATING_KEY; |
| 655 | } |
| 656 | |
| 657 | $is_original_product = $this->woocommerce_wpml->products->is_original_product( $product_id ); |
| 658 | if ( ! $is_original_product ) { |
| 659 | $meta_keys_to_filter[] = '_thumbnail_id'; |
| 660 | if ( 'product' === $post_type && is_single() ) { |
| 661 | $meta_keys_to_filter[] = '_product_image_gallery'; |
| 662 | } |
| 663 | } |
| 664 | } |
| 665 | |
| 666 | foreach ( $meta_keys_to_filter as $meta_key ) { |
| 667 | $data[ $meta_key ][0] = get_post_meta( $product_id, $meta_key, true ); |
| 668 | } |
| 669 | |
| 670 | add_filter( 'get_post_metadata', [ $this, 'filter_product_data' ], PHP_INT_MAX, 3 ); |
| 671 | } |
| 672 | } |
| 673 | |
| 674 | return $data; |
| 675 | } |
| 676 | |
| 677 | public function get_product_price_from_db( $product_id ) { |
| 678 | |
| 679 | return $this->wpdb->get_var( $this->wpdb->prepare( "SELECT meta_value FROM {$this->wpdb->postmeta} WHERE `meta_key` = '_price' AND post_id = %d ", $product_id ) ); |
| 680 | } |
| 681 | |
| 682 | public function override_product_type_query( $product_type, $product_id ) { |
| 683 | |
| 684 | if ( 'product' === get_post_type( $product_id ) ) { |
| 685 | $product_type = 'simple'; |
| 686 | $terms = get_the_terms( $product_id, 'product_type' ); |
| 687 | if ( $terms ) { |
| 688 | $product_type = sanitize_title( current( $terms )->name ); |
| 689 | } |
| 690 | } |
| 691 | |
| 692 | return $product_type; |
| 693 | } |
| 694 | |
| 695 | public function remove_post_meta_data_filter_on_checkout_stock_update( $reduce_stock ) { |
| 696 | if ( isset( $_GET['wc-ajax'] ) && 'checkout' === $_GET['wc-ajax'] ) { |
| 697 | remove_filter( 'get_post_metadata', [ $this, 'filter_product_data' ], PHP_INT_MAX ); |
| 698 | } |
| 699 | return $reduce_stock; |
| 700 | } |
| 701 | |
| 702 | public function maybe_add_language_parameter( $url ) { |
| 703 | |
| 704 | if ( |
| 705 | 'no' === get_option( 'woocommerce_enable_ajax_add_to_cart' ) && |
| 706 | constant( 'WPML_LANGUAGE_NEGOTIATION_TYPE_PARAMETER' ) === (int) $this->sitepress->get_setting( 'language_negotiation_type' ) |
| 707 | ) { |
| 708 | $current_language = $this->sitepress->get_current_language(); |
| 709 | if ( $current_language !== $this->sitepress->get_default_language() ) { |
| 710 | $url = add_query_arg( 'lang', $current_language, $url ); |
| 711 | } |
| 712 | } |
| 713 | |
| 714 | return $url; |
| 715 | } |
| 716 | |
| 717 | public function update_stock_status( $product_id, $status ) { |
| 718 | update_post_meta( $product_id, '_stock_status', $status ); |
| 719 | $this->wpdb->query( |
| 720 | $this->wpdb->prepare( |
| 721 | "UPDATE {$this->wpdb->wc_product_meta_lookup} SET stock_status = %s WHERE product_id = %d", |
| 722 | $status, |
| 723 | $product_id |
| 724 | ) |
| 725 | ); |
| 726 | } |
| 727 | } |
| 728 |