PluginProbe ʕ •ᴥ•ʔ
WPML Multilingual & Multicurrency for WooCommerce / 5.5.7
WPML Multilingual & Multicurrency for WooCommerce v5.5.7
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-products.php
woocommerce-multilingual / inc Last commit date
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