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 / compatibility / WcCompositeProducts / class-wcml-composite-products.php
woocommerce-multilingual / compatibility / WcCompositeProducts Last commit date
CompositedProductHooks.php 4 weeks ago Factory.php 4 weeks ago JobHooks.php 4 weeks ago MulticurrencyHooks.php 4 weeks ago class-wcml-composite-products.php 4 weeks ago
class-wcml-composite-products.php
468 lines
1 <?php
2
3 use WCML\Options\WPML;
4 use WPML\FP\Fns;
5 use WPML\FP\Obj;
6 use WPML\FP\Str;
7
8 class WCML_Composite_Products extends WCML_Compatibility_Helper implements \IWPML_Action {
9
10 const META_KEY_DATA = '_bto_data';
11 const META_KEY_SCENARIO = '_bto_scenario_data';
12 const FIELD_TYPE_PREFIX = 'wc_composite';
13
14 private $sitepress;
15
16 public function __construct( SitePress $sitepress ) {
17 $this->sitepress = $sitepress;
18 }
19
20 public function add_hooks(){
21
22 add_filter( 'woocommerce_composite_component_default_option', [ $this, 'woocommerce_composite_component_default_option' ], 10, 3 );
23 add_filter( 'wcml_cart_contents', [ $this, 'wpml_composites_compat' ], 11, 4 );
24 add_filter( 'woocommerce_composite_component_options_query_args', [ $this, 'wpml_composites_transients_cache_per_language' ], 10, 3 );
25 add_action( 'wcml_before_sync_product_data', [ $this, 'sync_composite_data_across_translations' ], 10, 2 );
26 add_action( 'wpml_translation_job_saved', [ $this, 'save_composite_data_translation' ], 10, 3 );
27 add_filter( 'wpml_tm_translation_job_data', [ $this, 'append_composite_data_translation_package' ], 10, 2 );
28
29 if( is_admin() ){
30 if ( ! WPML::useAte() ) {
31 add_action( 'wcml_gui_additional_box_html', [ $this, 'custom_box_html' ], 10, 3 );
32 add_filter( 'wcml_gui_additional_box_data', [ $this, 'custom_box_html_data' ], 10, 4 );
33 add_action( 'wcml_update_extra_fields', [ $this, 'update_component_strings' ], 10, 4 );
34 }
35
36 add_filter( 'woocommerce_json_search_found_products', [ $this, 'woocommerce_json_search_found_products' ] );
37
38 add_filter( 'wcml_js_lock_fields_input_names', [ $this, 'wcml_js_lock_fields_input_names' ] );
39 add_filter( 'wcml_js_lock_fields_ids', [ $this, 'wcml_js_lock_fields_ids' ] );
40 add_filter( 'wcml_after_load_lock_fields_js', [ $this, 'localize_lock_fields_js' ] );
41 add_action( 'init', [ $this, 'load_assets' ] );
42
43 add_filter( 'wcml_do_not_display_custom_fields_for_product', [ $this, 'replace_tm_editor_custom_fields_with_own_sections' ] );
44 }
45 }
46
47 public function woocommerce_composite_component_default_option($selected_value, $component_id, $object) {
48
49 if( !empty( $selected_value ) )
50 $selected_value = apply_filters( 'wpml_object_id', $selected_value, 'product', true );
51
52
53 return $selected_value;
54 }
55
56 public function wpml_composites_compat( $new_cart_data, $cart_contents, $key, $new_key ) {
57
58 if ( isset( $cart_contents[ $key ][ 'composite_children' ] ) || isset( $cart_contents[ $key ][ 'composite_parent' ] ) ) {
59
60 $buff = $new_cart_data[ $new_key ];
61
62 unset( $new_cart_data[ $new_key ] );
63
64 $new_cart_data[ $key ] = $buff;
65 }
66
67 return $new_cart_data;
68 }
69
70 public function wpml_composites_transients_cache_per_language( $args, $query_args, $component_data ) {
71
72 $args[ 'wpml_lang' ] = apply_filters( 'wpml_current_language', NULL );
73
74 return $args;
75 }
76
77 public function sync_composite_data_across_translations( $original_product_id, $current_product_id ){
78
79 if( $this->get_product_type( $original_product_id ) == 'composite' ){
80
81 $composite_data = $this->get_composite_data( $original_product_id );
82 $composite_scenarios_meta = $this->get_composite_scenarios_meta( $original_product_id );
83
84 $product_trid = $this->sitepress->get_element_trid( $original_product_id, 'post_product' );
85 $product_translations = $this->sitepress->get_element_translations( $product_trid, 'post_product' );
86
87 foreach ( $product_translations as $product_translation ) {
88
89 if ( empty($product_translation->original) ) {
90
91 $translated_composite_data = $this->get_composite_data( $product_translation->element_id );
92
93 foreach ( $composite_data as $component_id => $component ) {
94
95 if( isset( $translated_composite_data[$component_id]['title'] ) ){
96 $composite_data[$component_id]['title'] = $translated_composite_data[$component_id]['title'];
97 }
98
99 if( isset( $translated_composite_data[$component_id]['description'] ) ){
100 $composite_data[$component_id]['description'] = $translated_composite_data[$component_id]['description'];
101 }
102
103 if ( $component['query_type'] == 'product_ids' ) {
104
105 foreach ( $component['assigned_ids'] as $idx => $assigned_id ) {
106 $composite_data[$component_id]['assigned_ids'][$idx] =
107 apply_filters( 'wpml_object_id', $assigned_id, 'product', true, $product_translation->language_code );
108 }
109
110 } elseif( $component['query_type'] == 'category_ids' ){
111
112 foreach ( $component['assigned_category_ids'] as $idx => $assigned_id ) {
113 $composite_data[$component_id]['assigned_category_ids'][$idx] =
114 apply_filters( 'wpml_object_id', $assigned_id, 'product_cat', true, $product_translation->language_code );
115
116 }
117
118 }
119
120 if ( isset( $component['default_id'] ) && $component['default_id'] ) {
121 $translated_default_id = apply_filters( 'wpml_object_id', $component['default_id'], get_post_type( $component['default_id'] ), false, $product_translation->language_code );
122 if ( $translated_default_id ) {
123 $composite_data[ $component_id ]['default_id'] = $translated_default_id;
124 }
125 }
126
127 }
128
129 update_post_meta( $product_translation->element_id, self::META_KEY_DATA, $composite_data );
130
131 if ( $composite_scenarios_meta ) {
132 $translate_product_ids = function ( $component_data ) use ( $product_translation ) {
133 $translate_assigned_product_id = function( $assigned_product_id ) use ( $product_translation ) {
134 return apply_filters( 'wpml_object_id', $assigned_product_id, get_post_type( $assigned_product_id ), false, $product_translation->language_code ) ?: $assigned_product_id;
135 };
136
137 return wpml_collect( (array) $component_data )
138 ->map( Fns::map( $translate_assigned_product_id ) )
139 ->toArray();
140 };
141
142 $composite_scenarios_meta = wpml_collect( $composite_scenarios_meta )
143 ->map( Obj::over( Obj::lensPath( [ 'component_data' ] ), $translate_product_ids ) )
144 ->map( Obj::over( Obj::lensPath( [ 'scenario_actions', 'conditional_options', 'component_data' ] ), $translate_product_ids ) )
145 ->toArray();
146
147 update_post_meta( $product_translation->element_id, self::META_KEY_SCENARIO, $composite_scenarios_meta );
148 }
149 }
150 }
151
152 }
153 }
154
155 public function custom_box_html( $obj, $product_id, $data ){
156
157 if( $this->get_product_type( $product_id ) == 'composite' ){
158
159 $composite_data = $this->get_composite_data( $product_id );
160
161 $composite_section = new WPML_Editor_UI_Field_Section( __( 'Composite Products ( Components )', 'woocommerce-multilingual' ) );
162 end( $composite_data );
163 $last_key = key( $composite_data );
164 $divider = true;
165 foreach( $composite_data as $component_id => $component ) {
166 if( $component_id == $last_key ){
167 $divider = false;
168 }
169 $group = new WPML_Editor_UI_Field_Group( '', $divider );
170 $composite_field = new WPML_Editor_UI_Single_Line_Field( 'composite_'.$component_id.'_title', __( 'Name', 'woocommerce-multilingual' ), $data, false );
171 $group->add_field( $composite_field );
172 $composite_field = new WPML_Editor_UI_Single_Line_Field( 'composite_'.$component_id.'_description' , __( 'Description', 'woocommerce-multilingual' ), $data, false );
173 $group->add_field( $composite_field );
174 $composite_section->add_field( $group );
175
176 }
177
178 if( $composite_data ){
179 $obj->add_field( $composite_section );
180 }
181
182 $composite_scenarios_meta = $this->get_composite_scenarios_meta( $product_id );
183 if( $composite_scenarios_meta ){
184
185 $composite_scenarios = new WPML_Editor_UI_Field_Section( __( 'Composite Products ( Scenarios )', 'woocommerce-multilingual' ) );
186 end( $composite_scenarios_meta );
187 $last_key = key( $composite_scenarios_meta );
188 $divider = true;
189 foreach( $composite_scenarios_meta as $scenario_key => $scenario_meta ) {
190 if( $scenario_key == $last_key ){
191 $divider = false;
192 }
193 $group = new WPML_Editor_UI_Field_Group( '', $divider );
194 $composite_scenario_field = new WPML_Editor_UI_Single_Line_Field( 'composite_scenario_'.$scenario_key.'_title', __( 'Name', 'woocommerce-multilingual' ), $data, false );
195 $group->add_field( $composite_scenario_field );
196 $composite_scenario_field = new WPML_Editor_UI_Single_Line_Field( 'composite_scenario_'.$scenario_key.'_description' , __( 'Description', 'woocommerce-multilingual' ), $data, false );
197 $group->add_field( $composite_scenario_field );
198 $composite_scenarios->add_field( $group );
199
200 }
201
202 $obj->add_field( $composite_scenarios );
203
204 }
205
206 }
207
208 }
209
210 public function custom_box_html_data( $data, $product_id, $translation, $lang ){
211
212 if( $this->get_product_type( $product_id ) == 'composite' ){
213
214 $composite_data = $this->get_composite_data( $product_id );
215
216 foreach( $composite_data as $component_id => $component ) {
217
218 $data['composite_'.$component_id.'_title'] = [ 'original' =>
219 isset( $composite_data[$component_id]['title'] ) ? $composite_data[$component_id]['title'] : '' ];
220
221 $data['composite_'.$component_id.'_description'] = [ 'original' =>
222 isset( $composite_data[$component_id]['description'] ) ? $composite_data[$component_id]['description'] : '' ];
223
224 }
225
226 $composite_scenarios_meta = $this->get_composite_scenarios_meta( $product_id );
227 if( $composite_scenarios_meta ){
228 foreach( $composite_scenarios_meta as $scenario_key => $scenario_meta ){
229 $data[ 'composite_scenario_'.$scenario_key.'_title' ] = [
230 'original' => isset( $scenario_meta['title'] ) ? $scenario_meta['title'] : '',
231 'translation' => ''
232 ];
233
234 $data[ 'composite_scenario_'.$scenario_key.'_description' ] = [
235 'original' => isset( $scenario_meta['description'] ) ? $scenario_meta['description'] : '',
236 'translation' => ''
237 ];
238 }
239 }
240
241 if ( is_object( $translation ) ) {
242 $translated_composite_data = $this->get_composite_data( $translation->ID );
243
244 foreach( $composite_data as $component_id => $component ){
245
246 $data['composite_'.$component_id.'_title'][ 'translation' ] =
247 isset( $translated_composite_data[$component_id]['title'] ) ? $translated_composite_data[$component_id]['title'] : '';
248
249 $data['composite_'.$component_id.'_description'][ 'translation' ] =
250 isset( $translated_composite_data[$component_id]['description'] ) ? $translated_composite_data[$component_id]['description'] : '';
251
252 }
253
254 $translated_composite_scenarios_meta = $this->get_composite_scenarios_meta( $translation->ID );
255 if( $translated_composite_scenarios_meta ){
256 foreach( $translated_composite_scenarios_meta as $scenario_key => $translated_scenario_meta ){
257 $data[ 'composite_scenario_'.$scenario_key.'_title' ][ 'translation' ] =
258 isset( $translated_scenario_meta['title'] ) ? $translated_scenario_meta['title'] : '';
259
260 $data[ 'composite_scenario_'.$scenario_key.'_description' ][ 'translation' ] =
261 isset( $translated_scenario_meta['description'] ) ? $translated_scenario_meta['description'] : '';
262 }
263 }
264
265 }
266
267 }
268
269 return $data;
270 }
271
272 public function update_component_strings( $original_product_id, $product_id, $data, $language ){
273
274 $composite_data = $this->get_composite_data( $product_id );
275
276 foreach( $composite_data as $component_id => $component ) {
277
278 if(!empty($data[ md5( 'composite_'.$component_id.'_title' ) ] ) ){
279 $composite_data[$component_id]['title'] = $data[ md5( 'composite_'.$component_id.'_title' ) ];
280 }
281
282 if(!empty($data[ md5( 'composite_'.$component_id.'_description' ) ])) {
283 $composite_data[$component_id]['description'] = $data[ md5( 'composite_'.$component_id.'_description' ) ];
284 }
285
286 }
287
288 update_post_meta( $product_id, self::META_KEY_DATA, $composite_data );
289
290 $composite_scenarios_meta = $this->get_composite_scenarios_meta( $product_id );
291 if( $composite_scenarios_meta ){
292 foreach( $composite_scenarios_meta as $scenario_key => $scenario_meta ){
293 if( !empty( $data[ md5( 'composite_scenario_'.$scenario_key.'_title' ) ] ) ){
294 $composite_scenarios_meta[ $scenario_key ][ 'title' ] = $data[ md5( 'composite_scenario_'.$scenario_key.'_title' ) ];
295 }
296
297 if( !empty( $data[ md5( 'composite_scenario_'.$scenario_key.'_description' ) ])) {
298 $composite_scenarios_meta[ $scenario_key ][ 'description' ] = $data[ md5( 'composite_scenario_'.$scenario_key.'_description' ) ];
299 }
300 }
301 }
302
303 update_post_meta( $product_id, self::META_KEY_SCENARIO, $composite_scenarios_meta );
304 }
305
306 public function append_composite_data_translation_package( $package, $post ){
307 if( 'product' === $post->post_type ) {
308 $add_fields_to_package = function( $data, $fields, $subtype ) use ( &$package ) {
309 if ( $data ) {
310 foreach( $data as $key => $meta ){
311 foreach( $fields as $field ) {
312 if ( ! empty( $meta[ $field ] ) ) {
313 $package['contents'][ self::get_field_name( $key, $field, $subtype ) ] = [
314 'translate' => 1,
315 'data' => base64_encode( $meta[ $field ] ),
316 'format' => 'base64',
317 ];
318 }
319 }
320 }
321 }
322 };
323
324 $add_fields_to_package( $this->get_composite_data( $post->ID ), [ 'title', 'description' ], '' );
325 $add_fields_to_package( $this->get_composite_scenarios_meta( $post->ID ), [ 'title', 'description' ], 'scenario' );
326 }
327
328 return $package;
329 }
330
331 private static function get_field_name( $component_id, $field, $subtype ) {
332 $subtype = $subtype ? $subtype . ':' : $subtype;
333 return self::FIELD_TYPE_PREFIX . ':' . $subtype . $component_id . ':' . $field;
334 }
335
336 public function save_composite_data_translation( $post_id, $data, $job ){
337 if (
338 Str::startsWith( 'post_', $job->original_post_type )
339 && 'product' === get_post_type( $job->original_doc_id )
340 ) {
341 $composite_data = $this->get_composite_data( $job->original_doc_id );
342
343 if ( $composite_data ) {
344 $get_translation = function ( $name ) use ( $data ) {
345 return Obj::path( [ $name, 'data' ], $data );
346 };
347
348 $apply_translations = function ( $data, $fields, $subtype ) use ( $get_translation ) {
349 foreach ( $data as $key => $meta ) {
350 foreach ( $fields as $field ) {
351 $translation = $get_translation( self::get_field_name( $key, $field, $subtype ) );
352
353 if ( $translation ) {
354 $data[ $key ][ $field ] = $translation;
355 }
356 }
357 }
358
359 return $data;
360 };
361
362 $adjust_ids = function ( $data, $lang ) {
363 foreach ( $data as $key => $meta ) {
364 $ids_key = $cpt = null;
365
366 if ( 'product_ids' === $meta['query_type'] ) {
367 $ids_key = 'assigned_ids';
368 $cpt = 'product';
369 } elseif ( 'category_ids' === $meta['query_type'] ) {
370 $ids_key = 'assigned_category_ids';
371 $cpt = 'product_cat';
372 }
373
374 if ( $ids_key && $cpt ) {
375 foreach ( $meta[ $ids_key ] as $idx => $assigned_id ) {
376 $data[ $key ][ $ids_key ][ $idx ] = apply_filters( 'wpml_object_id', $assigned_id, $cpt, true, $lang );
377 }
378 }
379 }
380
381 return $data;
382 };
383
384 update_post_meta(
385 $post_id,
386 self::META_KEY_DATA,
387 $adjust_ids( $apply_translations( $composite_data, [ 'title', 'description' ], '' ), $job->language_code )
388 );
389
390 update_post_meta(
391 $post_id,
392 self::META_KEY_SCENARIO,
393 $apply_translations( $this->get_composite_scenarios_meta( $job->original_doc_id ), [ 'title', 'description' ], 'scenario' )
394 );
395 }
396 }
397 }
398
399 public function wcml_js_lock_fields_input_names( $names ){
400
401 $names[] = '_base_regular_price';
402 $names[] = '_base_sale_price';
403 $names[] = 'bto_style';
404
405 return $names;
406 }
407
408 public function wcml_js_lock_fields_ids( $names ){
409
410 $names[] = '_per_product_pricing_bto';
411 $names[] = '_per_product_shipping_bto';
412 $names[] = '_bto_hide_shop_price';
413
414 return $names;
415 }
416
417 public function localize_lock_fields_js(){
418 wp_localize_script( 'wcml-composite-js', 'lock_settings' , [ 'lock_fields' => 1 ] );
419 }
420
421 public function load_assets( ){
422 global $pagenow;
423
424 $is_composite_edit_page = false;
425
426 if( $pagenow == 'post.php' && isset( $_GET[ 'post' ] ) ){
427 $wc_product = wc_get_product( $_GET[ 'post' ] );
428 if( $wc_product && $wc_product->get_type() === 'composite' ){
429 $is_composite_edit_page = true;
430 }
431 }
432
433 if( $is_composite_edit_page || $pagenow == 'post-new.php' ){
434 wp_register_script( 'wcml-composite-js', WCML_PLUGIN_URL . '/compatibility/res/js/wcml-composite.js', [ 'jquery' ], WCML_VERSION, true );
435 wp_enqueue_script( 'wcml-composite-js' );
436
437 }
438
439 }
440
441 public function woocommerce_json_search_found_products( $found_products ){
442 global $wpml_post_translations;
443
444 foreach( $found_products as $id => $product_name ){
445 if( $wpml_post_translations->get_element_lang_code ( $id ) != $this->sitepress->get_current_language() ){
446 unset( $found_products[ $id ] );
447 }
448 }
449
450 return $found_products;
451 }
452
453 public function get_composite_scenarios_meta( $product_id ){
454 return get_post_meta( $product_id, self::META_KEY_SCENARIO, true );
455 }
456
457 public function get_composite_data( $product_id ){
458 return get_post_meta( $product_id, self::META_KEY_DATA, true ) ?: [];
459 }
460
461 public function replace_tm_editor_custom_fields_with_own_sections( $fields ){
462 $fields[] = self::META_KEY_DATA;
463 $fields[] = self::META_KEY_SCENARIO;
464
465 return $fields;
466 }
467 }
468