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 / WcBookings / class-wcml-bookings.php
woocommerce-multilingual / compatibility / WcBookings Last commit date
Templates 4 weeks ago TranslationEditor 4 weeks ago Calendar.php 1 year ago Emails.php 4 weeks ago Factory.php 4 weeks ago MulticurrencyHooks.php 4 weeks ago Prices.php 4 weeks ago SharedHooks.php 4 weeks ago Slots.php 4 weeks ago class-wcml-accommodation-bookings.php 4 weeks ago class-wcml-bookings.php 4 weeks ago
class-wcml-bookings.php
1517 lines
1 <?php
2
3 use WCML\Compatibility\WcBookings\Prices;
4 use WPML\FP\Fns;
5 use WPML\FP\Str;
6
7 class WCML_Bookings implements \IWPML_Action {
8
9 const POST_TYPE = 'wc_booking';
10
11 const PRIORITY_SAVE_POST_ACTION = 110;
12
13 const PERSON_FIELD_PREFIX = 'wc_bookings:person:';
14 const RESOURCE_FIELD_PREFIX = 'wc_bookings:resource:';
15
16 const BOOKING_ORDER_ITEM_ID_META = '_booking_order_item_id';
17 const BOOKING_COST_META = '_booking_cost';
18 const BOOKING_START_META = '_booking_start';
19 const BOOKING_END_META = '_booking_end';
20 const BOOKING_ALL_DAY_META = '_booking_all_day';
21 const BOOKING_CUSTOMER_ID_META = '_booking_customer_id';
22 const BOOKING_PRODUCT_ID_META = '_booking_product_id';
23 const BOOKING_RESOURCE_ID_META = '_booking_resource_id';
24 const BOOKING_PERSONS_META = '_booking_persons';
25 const BOOKING_DUPLICATE_OF_META = '_booking_duplicate_of';
26
27 private $tp;
28
29 private $sitepress;
30
31 private $woocommerce_wpml;
32
33 private $wpml_post_translations;
34
35 private $wpdb;
36
37 public function __construct( SitePress $sitepress, woocommerce_wpml $woocommerce_wpml, wpdb $wpdb, WPML_Element_Translation_Package $tp, WPML_Post_Translation $wpml_post_translations ) {
38 $this->sitepress = $sitepress;
39 $this->woocommerce_wpml = $woocommerce_wpml;
40 $this->wpdb = $wpdb;
41 $this->tp = $tp;
42 $this->wpml_post_translations = $wpml_post_translations;
43 }
44
45 public function add_hooks() {
46
47 add_filter( 'wcml_order_id_for_language', [ $this, 'order_id_for_language' ] );
48
49 add_action( 'save_post', Fns::withoutRecursion( Fns::noop(), [ $this, 'save_booking_action_handler' ] ), self::PRIORITY_SAVE_POST_ACTION );
50 add_action( 'wcml_bookings_resource_costs_updated', [ $this, 'sync_resource_costs_with_translations' ], 10, 2 );
51
52 add_action( 'wcml_before_sync_product_data', [ $this, 'sync_bookings' ], 10, 3 );
53 add_action( 'wcml_before_sync_product', [ $this, 'sync_booking_data' ], 10, 2 );
54
55 add_filter(
56 'wcml_cart_contents_not_changed',
57 [
58 $this,
59 'filter_bundled_product_in_cart_contents',
60 ],
61 10,
62 3
63 );
64
65 add_action( 'woocommerce_bookings_create_booking_page_add_order_item', [ $this, 'set_order_language_on_create_booking_page' ] );
66
67 add_filter( 'get_booking_products_args', [ $this, 'filter_get_booking_products_args' ] );
68
69 add_action( 'wcml_gui_additional_box_html', [ $this, 'custom_box_html' ], 10, 3 );
70 add_filter( 'wcml_gui_additional_box_data', [ $this, 'custom_box_html_data' ], 10, 4 );
71 add_filter( 'wcml_check_is_single', [ $this, 'show_custom_blocks_for_resources_and_persons' ], 10, 3 );
72 add_filter( 'wcml_do_not_display_custom_fields_for_product', [ $this, 'replace_tm_editor_custom_fields_with_own_sections' ] );
73 add_filter(
74 'wcml_not_display_single_fields_to_translate',
75 [
76 $this,
77 'remove_single_custom_fields_to_translate',
78 ]
79 );
80 add_filter( 'wcml_product_content_label', [ $this, 'product_content_resource_label' ], 10 );
81 add_action( 'wcml_update_extra_fields', [ $this, 'wcml_products_tab_sync_resources_and_persons' ], 10, 4 );
82
83 add_action( 'woocommerce_new_booking', [ $this, 'duplicate_booking_for_translations' ] );
84
85 add_action( 'woocommerce_booking_status_changed', [ $this, 'woocommerce_booking_status_changed_update_translations' ], 10, 4 );
86
87 add_filter( 'woocommerce_bookings_in_date_range_query', [ $this, 'bookings_in_date_range_query' ] );
88
89 add_action( 'before_delete_post', [ $this, 'delete_bookings' ] );
90 add_action( 'wp_trash_post', [ $this, 'trash_bookings' ] );
91 add_action( 'wpml_translation_job_saved', [ $this, 'save_booking_data_to_translation' ], 10, 3 );
92
93 add_action( 'wpml_pro_translation_completed', [ $this, 'synchronize_bookings_on_translation_completed' ], 10, 3 );
94
95 add_filter( 'wpml_tm_translation_job_data', [ $this, 'append_persons_to_translation_package' ], 10, 2 );
96 add_filter( 'wpml_tm_translation_job_data', [ $this, 'append_resources_to_translation_package' ], 10, 2 );
97 add_filter( 'wpml_tm_dashboard_translatable_types', [ $this, 'hide_bookings_type_on_tm_dashboard' ] );
98
99 if ( is_admin() ) {
100
101 add_filter( 'wcml_js_lock_fields_ids', [ $this, 'wcml_js_lock_fields_ids' ] );
102 add_filter( 'wcml_after_load_lock_fields_js', [ $this, 'localize_lock_fields_js' ] );
103
104 add_filter( 'get_booking_resources_args', [ $this, 'filter_get_booking_resources_args' ] );
105
106 add_filter( 'get_translatable_documents_all', [ $this, 'filter_translatable_documents' ] );
107
108 add_filter( 'pre_wpml_is_translated_post_type', [ $this, 'filter_is_translated_post_type' ] );
109
110 add_action( 'woocommerce_product_data_panels', [ $this, 'show_pointer_info' ] );
111
112 add_action( 'save_post', [ $this, 'sync_booking_status' ], 10, 3 );
113
114 add_filter( 'wcml_email_language', [ $this, 'booking_email_language' ] );
115
116 if ( $this->is_bookings_listing_page() ) {
117 $this->remove_language_switcher();
118 add_filter( 'wp_count_posts', [ $this, 'count_bookings_by_current_language' ], 10, 2 );
119 add_filter( 'views_edit-' . self::POST_TYPE, [ $this, 'unset_mine_from_bookings_views' ] );
120 }
121 }
122
123 add_filter( 'wpml_language_filter_extra_conditions_snippet', [ $this, 'extra_conditions_to_filter_bookings' ] );
124
125 add_filter( 'wcml_add_to_cart_sold_individually', [ $this, 'add_to_cart_sold_individually' ], 10, 4 );
126
127 add_filter( 'schedule_event', [ $this, 'prevent_events_on_duplicates' ] );
128
129 add_action( 'updated_post_meta', [ $this, 'sync_customer_created_during_checkout' ], 10, 4 );
130 }
131
132 public function order_id_for_language( $maybeBookingId ) {
133 if ( self::isWcBooking( $maybeBookingId ) ) {
134 return wp_get_post_parent_id( $maybeBookingId );
135 }
136
137 return $maybeBookingId;
138 }
139
140 public function save_booking_action_handler( $booking_id ) {
141
142 $this->maybe_set_booking_language( $booking_id );
143
144 $this->maybe_sync_updated_booking_meta( $booking_id );
145 }
146
147 public function sync_bookings( $original_product_id, $product_id, $language ) {
148 $all_bookings_for_product = $this->wpdb->get_results( $this->wpdb->prepare( "SELECT post_id as id FROM {$this->wpdb->postmeta} WHERE meta_key = '_booking_product_id' AND meta_value = %d", $original_product_id ) );
149
150 foreach ( $all_bookings_for_product as $booking ) {
151
152 $source_language_code = $this->wpml_post_translations->get_source_lang_code( $booking->id );
153
154 if ( $language === $source_language_code ) {
155 continue;
156 }
157 $booking_translations = $this->get_translated_bookings( $booking->id, false );
158
159 if ( ! isset( $booking_translations[ $language ] ) ) {
160 $this->duplicate_booking_for_translations( $booking->id, $language );
161 } elseif ( ! get_post_meta( $booking_translations[ $language ], self::BOOKING_PRODUCT_ID_META, true ) ) {
162 $this->update_translated_booking_meta( $booking_translations[ $language ], $booking->id, $language );
163 }
164 }
165 }
166
167 private function update_translated_booking_meta( $translated_booking_id, $original_booking_id, $language ) {
168 update_post_meta( $translated_booking_id, self::BOOKING_PRODUCT_ID_META, $this->get_translated_booking_product_id( $original_booking_id, $language ) );
169 update_post_meta( $translated_booking_id, self::BOOKING_RESOURCE_ID_META, $this->get_translated_booking_resource_id( $original_booking_id, $language ) );
170 update_post_meta( $translated_booking_id, self::BOOKING_PERSONS_META, $this->get_translated_booking_persons_ids( $original_booking_id, $language ) );
171 }
172
173 public function sync_booking_data( $original_product_id, $current_product_id ) {
174
175 if ( has_term( 'booking', 'product_type', $original_product_id ) ) {
176 $translations = $this->wpml_post_translations->get_element_translations( $original_product_id, false, true );
177 foreach ( $translations as $translation ) {
178 $language = $this->wpml_post_translations->get_element_lang_code( $translation );
179
180 $this->sync_resources( $original_product_id, $translation, $language );
181
182 $this->sync_persons( $original_product_id, $translation, $language );
183
184 $this->clear_bookable_product_cache( $translation );
185 }
186 }
187 }
188
189 public function sync_resources( $original_product_id, $translated_product_id, $lang_code, $duplicate = true ) {
190
191 $original_resources = $this->wpdb->get_results(
192 $this->wpdb->prepare(
193 "SELECT resource_id, sort_order FROM {$this->wpdb->prefix}wc_booking_relationships WHERE product_id = %d",
194 $original_product_id
195 )
196 );
197
198 $translated_resources = $this->wpdb->get_col(
199 $this->wpdb->prepare(
200 "SELECT resource_id FROM {$this->wpdb->prefix}wc_booking_relationships WHERE product_id = %d",
201 $translated_product_id
202 )
203 );
204
205 $used_translated_resources = [];
206
207 foreach ( $original_resources as $resource ) {
208
209 $translated_resource_id = apply_filters( 'wpml_object_id', $resource->resource_id, 'bookable_resource', false, $lang_code );
210 if ( ! is_null( $translated_resource_id ) ) {
211
212 if ( in_array( $translated_resource_id, $translated_resources ) ) {
213 $this->update_product_resource( $translated_product_id, $translated_resource_id, $resource );
214 } else {
215 $this->add_product_resource( $translated_product_id, $translated_resource_id, $resource );
216 }
217 $used_translated_resources[] = $translated_resource_id;
218 } else {
219 if ( $duplicate ) {
220 $this->duplicate_resource( $translated_product_id, $resource, $lang_code );
221 }
222 }
223 }
224
225 $removed_translated_resources_id = array_diff( $translated_resources, $used_translated_resources );
226 foreach ( $removed_translated_resources_id as $resource_id ) {
227 $this->remove_resource_from_product( $translated_product_id, $resource_id );
228 }
229
230 $this->sync_resource_costs( $original_product_id, $translated_product_id, '_resource_base_costs', $lang_code );
231 $this->sync_resource_costs( $original_product_id, $translated_product_id, '_resource_block_costs', $lang_code );
232
233 }
234
235 public function duplicate_resource( $tr_product_id, $resource, $lang_code ) {
236 global $iclTranslationManagement;
237
238 if ( method_exists( $this->sitepress, 'make_duplicate' ) ) {
239
240 $trns_resource_id = $this->sitepress->make_duplicate( $resource->resource_id, $lang_code );
241
242 } else {
243
244 if ( ! isset( $iclTranslationManagement ) ) {
245 $iclTranslationManagement = new TranslationManagement();
246 }
247
248 $trns_resource_id = $iclTranslationManagement->make_duplicate( $resource->resource_id, $lang_code );
249
250 }
251
252 $this->wpdb->insert(
253 $this->wpdb->prefix . 'wc_booking_relationships',
254 [
255 'product_id' => $tr_product_id,
256 'resource_id' => $trns_resource_id,
257 'sort_order' => $resource->sort_order,
258 ]
259 );
260
261 delete_post_meta( $trns_resource_id, '_icl_lang_duplicate_of' );
262
263 return $trns_resource_id;
264 }
265
266 public function add_product_resource( $product_id, $resource_id, $resource_data ) {
267
268 $this->wpdb->insert(
269 $this->wpdb->prefix . 'wc_booking_relationships',
270 [
271 'sort_order' => $resource_data->sort_order,
272 'product_id' => $product_id,
273 'resource_id' => $resource_id,
274 ]
275 );
276
277 update_post_meta( $resource_id, 'qty', get_post_meta( $resource_data->resource_id, 'qty', true ) );
278 update_post_meta( $resource_id, '_wc_booking_availability', get_post_meta( $resource_data->resource_id, '_wc_booking_availability', true ) );
279
280 }
281
282 public function remove_resource_from_product( $product_id, $resource_id ) {
283
284 $this->wpdb->delete(
285 $this->wpdb->prefix . 'wc_booking_relationships',
286 [
287 'product_id' => $product_id,
288 'resource_id' => $resource_id,
289 ]
290 );
291
292 }
293
294 public function update_product_resource( $product_id, $resource_id, $resource_data ) {
295
296 $this->wpdb->update(
297 $this->wpdb->prefix . 'wc_booking_relationships',
298 [
299 'sort_order' => $resource_data->sort_order,
300 ],
301 [
302 'product_id' => $product_id,
303 'resource_id' => $resource_id,
304 ]
305 );
306
307 update_post_meta( $resource_id, 'qty', get_post_meta( $resource_data->resource_id, 'qty', true ) );
308 update_post_meta( $resource_id, '_wc_booking_availability', get_post_meta( $resource_data->resource_id, '_wc_booking_availability', true ) );
309
310 }
311
312 public function sync_persons( $original_product_id, $tr_product_id, $lang_code, $duplicate = true ) {
313 $orig_persons = $this->wpdb->get_col( $this->wpdb->prepare( "SELECT ID FROM {$this->wpdb->posts} WHERE post_parent = %d AND post_type = 'bookable_person'", $original_product_id ) );
314
315 $trnsl_persons = $this->wpdb->get_col( $this->wpdb->prepare( "SELECT ID FROM {$this->wpdb->posts} WHERE post_parent = %d AND post_type = 'bookable_person'", $tr_product_id ) );
316
317 foreach ( $orig_persons as $person ) {
318
319 $trnsl_person_id = apply_filters( 'wpml_object_id', $person, 'bookable_person', false, $lang_code );
320
321 if ( ! is_null( $trnsl_person_id ) && in_array( $trnsl_person_id, $trnsl_persons ) ) {
322
323 $key = array_search( $trnsl_person_id, $trnsl_persons );
324 if ( false !== $key ) {
325
326 unset( $trnsl_persons[ $key ] );
327
328 update_post_meta( $trnsl_person_id, 'block_cost', get_post_meta( $person, 'block_cost', true ) );
329 update_post_meta( $trnsl_person_id, 'cost', get_post_meta( $person, 'cost', true ) );
330 update_post_meta( $trnsl_person_id, 'max', get_post_meta( $person, 'max', true ) );
331 update_post_meta( $trnsl_person_id, 'min', get_post_meta( $person, 'min', true ) );
332
333 if ( get_post_meta( $person, Prices::CUSTOM_COSTS_STATUS_KEY, true ) && $this->woocommerce_wpml->settings['enable_multi_currency'] == WCML_MULTI_CURRENCIES_INDEPENDENT ) {
334 $currencies = $this->woocommerce_wpml->multi_currency->get_currencies();
335
336 foreach ( $currencies as $code => $currency ) {
337
338 update_post_meta( $trnsl_person_id, 'block_cost_' . $code, get_post_meta( $person, 'block_cost_' . $code, true ) );
339 update_post_meta( $trnsl_person_id, 'cost_' . $code, get_post_meta( $person, 'cost_' . $code, true ) );
340
341 }
342 }
343 }
344 } else {
345
346 if ( $duplicate ) {
347
348 $this->duplicate_person( $tr_product_id, $person, $lang_code );
349
350 } else {
351
352 continue;
353
354 }
355 }
356 }
357
358 foreach ( $trnsl_persons as $trnsl_person ) {
359
360 wp_delete_post( $trnsl_person );
361
362 }
363
364 }
365
366 public function duplicate_person( $tr_product_id, $person_id, $lang_code ) {
367 global $iclTranslationManagement;
368
369 if ( method_exists( $this->sitepress, 'make_duplicate' ) ) {
370
371 $new_person_id = $this->sitepress->make_duplicate( $person_id, $lang_code );
372
373 } else {
374
375 if ( ! isset( $iclTranslationManagement ) ) {
376 $iclTranslationManagement = new TranslationManagement();
377 }
378
379 $new_person_id = $iclTranslationManagement->make_duplicate( $person_id, $lang_code );
380
381 }
382
383 $this->wpdb->update(
384 $this->wpdb->posts,
385 [
386 'post_parent' => $tr_product_id,
387 ],
388 [
389 'ID' => $new_person_id,
390 ]
391 );
392
393 delete_post_meta( $new_person_id, '_icl_lang_duplicate_of' );
394
395 return $new_person_id;
396 }
397
398 public function sync_resource_costs_with_translations( $object_id, $meta_key, $check = false ) {
399
400 $original_product_id = $this->woocommerce_wpml->products->get_original_product_id( $object_id );
401
402 if ( (int) $object_id === (int) $original_product_id ) {
403
404 $translations = $this->wpml_post_translations->get_element_translations( $object_id, false, true );
405
406 foreach ( $translations as $translation ) {
407 $this->sync_resource_costs(
408 $original_product_id,
409 $translation,
410 $meta_key,
411 $this->wpml_post_translations->get_element_lang_code( $translation )
412 );
413 }
414
415 return $check;
416 } else {
417 $this->sync_resource_costs(
418 $original_product_id,
419 $object_id,
420 $meta_key,
421 $this->wpml_post_translations->get_element_lang_code( $object_id )
422 );
423 return true;
424 }
425
426 }
427
428 public function sync_resource_costs( $original_product_id, $object_id, $meta_key, $language_code ) {
429
430 $original_costs = maybe_unserialize( get_post_meta( $original_product_id, $meta_key, true ) );
431
432 $wc_booking_resource_costs = [];
433 if ( ! empty( $original_costs ) ) {
434 foreach ( $original_costs as $resource_id => $costs ) {
435
436 if ( 'custom_costs' === $resource_id && isset( $costs['custom_costs'] ) ) {
437
438 foreach ( $costs['custom_costs'] as $code => $currencies ) {
439
440 foreach ( $currencies as $custom_costs_resource_id => $custom_cost ) {
441
442 $trns_resource_id = apply_filters( 'wpml_object_id', $custom_costs_resource_id, 'bookable_resource', true, $language_code );
443
444 $wc_booking_resource_costs['custom_costs'][ $code ][ $trns_resource_id ] = $custom_cost;
445
446 }
447 }
448 } else {
449
450 $trns_resource_id = apply_filters( 'wpml_object_id', $resource_id, 'bookable_resource', true, $language_code );
451
452 $wc_booking_resource_costs[ $trns_resource_id ] = $costs;
453
454 }
455 }
456 }
457
458 update_post_meta( $object_id, $meta_key, $wc_booking_resource_costs );
459
460 }
461
462 public function localize_lock_fields_js() {
463 wp_localize_script( 'wcml-bookings-js', 'lock_settings', [ 'lock_fields' => 1 ] );
464 }
465
466 public function filter_bundled_product_in_cart_contents( $cart_item, $key, $current_language ) {
467
468 if ( $cart_item['data'] instanceof WC_Product_Booking && isset( $cart_item['booking'] ) ) {
469
470 $current_id = apply_filters( 'wpml_object_id', $cart_item['product_id'], 'product', true, $current_language );
471 $cart_product_id = $cart_item['product_id'];
472
473 if ( $current_id !== $cart_product_id ) {
474
475 $cart_item['data'] = new WC_Product_Booking( $current_id );
476
477 }
478
479 if ( $this->woocommerce_wpml->settings['enable_multi_currency'] == WCML_MULTI_CURRENCIES_INDEPENDENT || $current_id != $cart_product_id ) {
480
481 $booking_info = [
482 'wc_bookings_field_start_date_year' => $cart_item['booking']['_year'],
483 'wc_bookings_field_start_date_month' => $cart_item['booking']['_month'],
484 'wc_bookings_field_start_date_day' => $cart_item['booking']['_day'],
485 'add-to-cart' => $current_id,
486 ];
487
488 if ( isset( $cart_item['booking']['_persons'] ) ) {
489 foreach ( $cart_item['booking']['_persons'] as $person_id => $value ) {
490 $booking_info[ 'wc_bookings_field_persons_' . apply_filters( 'wpml_object_id', $person_id, 'bookable_person', false, $current_language ) ] = $value;
491 }
492 }
493
494 if ( isset( $cart_item['booking']['_resource_id'] ) ) {
495 $booking_info['wc_bookings_field_resource'] = apply_filters( 'wpml_object_id', $cart_item['booking']['_resource_id'], 'bookable_resource', false, $current_language );
496 }
497
498 if ( isset( $cart_item['booking']['_duration'] ) ) {
499 $booking_info['wc_bookings_field_duration'] = $cart_item['booking']['_duration'];
500 }
501
502 if ( isset( $cart_item['booking']['_time'] ) ) {
503 $booking_info['wc_bookings_field_start_date_time'] = $cart_item['booking']['_time'];
504 }
505
506 $current_product = wc_get_product( $current_id );
507
508 $cost = $this->get_booking_cost( $booking_info, $current_product );
509
510 if ( ! is_wp_error( $cost ) ) {
511 $cart_item['data']->set_price( $cost );
512 }
513 }
514 }
515
516 return $cart_item;
517 }
518
519 private function get_booking_cost( $booking_info, $current_product ) {
520 if ( class_exists( 'WC_Bookings_Cost_Calculation' ) ) {
521 $cost = WC_Bookings_Cost_Calculation::calculate_booking_cost( wc_bookings_get_posted_data( $booking_info, $current_product ), $current_product );
522 } else {
523 $booking_form = new WC_Booking_Form( $current_product );
524 $cost = $booking_form->calculate_booking_cost( $booking_info );
525 }
526
527 return $cost;
528 }
529
530 public function set_order_language_on_create_booking_page( $order_id ) {
531 \WCML_Orders::setLanguage( $order_id, $this->sitepress->get_current_language() );
532 }
533
534 public function filter_get_booking_products_args( $args ) {
535 if ( isset( $args['suppress_filters'] ) ) {
536 $args['suppress_filters'] = false;
537 }
538
539 return $args;
540 }
541
542 public function custom_box_html( $obj, $product_id, $data ) {
543
544 if ( ! $this->is_booking( $product_id ) ) {
545 return;
546 }
547
548 $bookings_section = new WPML_Editor_UI_Field_Section( __( 'Bookings', 'woocommerce-multilingual' ) );
549
550 if ( 'yes' === get_post_meta( $product_id, '_wc_booking_has_resources', true ) ) {
551 $group = new WPML_Editor_UI_Field_Group( '', true );
552 $booking_field = new WPML_Editor_UI_Single_Line_Field( '_wc_booking_resouce_label', __( 'Resources Label', 'woocommerce-multilingual' ), $data, true );
553 $group->add_field( $booking_field );
554 $bookings_section->add_field( $group );
555 }
556
557 $orig_resources = maybe_unserialize( get_post_meta( $product_id, '_resource_base_costs', true ) );
558
559 if ( $orig_resources ) {
560 $group = new WPML_Editor_UI_Field_Group( __( 'Resources', 'woocommerce-multilingual' ) );
561 $group_title = __( 'Resources', 'woocommerce-multilingual' );
562 foreach ( $orig_resources as $resource_id => $cost ) {
563
564 if ( 'custom_costs' === $resource_id ) {
565 continue;
566 }
567
568 $group = new WPML_Editor_UI_Field_Group( $group_title );
569 $group_title = '';
570
571 $resource_field = new WPML_Editor_UI_Single_Line_Field( 'bookings-resource_' . $resource_id . '_title', __( 'Title', 'woocommerce-multilingual' ), $data, true );
572 $group->add_field( $resource_field );
573 $bookings_section->add_field( $group );
574 }
575 }
576
577 $original_persons = $this->get_original_persons( $product_id );
578 end( $original_persons );
579 $last_key = key( $original_persons );
580 $divider = true;
581 $group_title = __( 'Person Types', 'woocommerce-multilingual' );
582 foreach ( $original_persons as $person_id ) {
583 if ( $person_id === $last_key ) {
584 $divider = false;
585 }
586 $group = new WPML_Editor_UI_Field_Group( $group_title, $divider );
587 $group_title = '';
588
589 $person_field = new WPML_Editor_UI_Single_Line_Field( 'bookings-person_' . $person_id . '_title', __( 'Person Type Name', 'woocommerce-multilingual' ), $data, false );
590 $group->add_field( $person_field );
591 $person_field = new WPML_Editor_UI_Single_Line_Field( 'bookings-person_' . $person_id . '_description', __( 'Description', 'woocommerce-multilingual' ), $data, false );
592 $group->add_field( $person_field );
593 $bookings_section->add_field( $group );
594
595 }
596
597 if ( $orig_resources || $original_persons ) {
598 $obj->add_field( $bookings_section );
599 }
600
601 }
602
603 public function custom_box_html_data( $data, $product_id, $translation, $lang ) {
604
605 if ( ! $this->is_booking( $product_id ) ) {
606 return $data;
607 }
608
609 if ( 'yes' === get_post_meta( $product_id, '_wc_booking_has_resources', true ) ) {
610
611 $data['_wc_booking_resouce_label'] = [ 'original' => get_post_meta( $product_id, '_wc_booking_resouce_label', true ) ];
612 $data['_wc_booking_resouce_label']['translation'] = $translation ? get_post_meta( $translation->ID, '_wc_booking_resouce_label', true ) : '';
613 }
614
615 $orig_resources = $this->get_original_resources( $product_id );
616
617 if ( $orig_resources && is_array( $orig_resources ) ) {
618
619 foreach ( $orig_resources as $resource_id => $cost ) {
620
621 if ( 'custom_costs' === $resource_id ) {
622 continue;
623 }
624 $data[ 'bookings-resource_' . $resource_id . '_title' ] = [ 'original' => get_the_title( $resource_id ) ];
625
626 $trns_resource_id = apply_filters( 'wpml_object_id', $resource_id, 'bookable_resource', false, $lang );
627 $data[ 'bookings-resource_' . $resource_id . '_title' ]['translation'] = $trns_resource_id ? get_the_title( $trns_resource_id ) : '';
628 }
629 }
630
631 $original_persons = $this->get_original_persons( $product_id );
632
633 foreach ( $original_persons as $person_id ) {
634
635 $data[ 'bookings-person_' . $person_id . '_title' ] = [ 'original' => get_the_title( $person_id ) ];
636 $data[ 'bookings-person_' . $person_id . '_description' ] = [ 'original' => get_post( $person_id )->post_excerpt ];
637
638 $trnsl_person_id = apply_filters( 'wpml_object_id', $person_id, 'bookable_person', false, $lang );
639 $data[ 'bookings-person_' . $person_id . '_title' ]['translation'] = $trnsl_person_id ? get_the_title( $trnsl_person_id ) : '';
640 $data[ 'bookings-person_' . $person_id . '_description' ]['translation'] = $trnsl_person_id ? get_post( $trnsl_person_id )->post_excerpt : '';
641
642 }
643
644 return $data;
645 }
646
647
648 public function get_original_resources( $product_id ) {
649 $orig_resources = maybe_unserialize( get_post_meta( $product_id, '_resource_base_costs', true ) );
650
651 return $orig_resources;
652 }
653
654 public function get_original_persons( $product_id ) {
655 $original_persons = $this->wpdb->get_col( $this->wpdb->prepare( "SELECT ID FROM {$this->wpdb->posts} WHERE post_parent = %d AND post_type = 'bookable_person' AND post_status = 'publish'", $product_id ) );
656
657 return $original_persons;
658 }
659
660 public function show_custom_blocks_for_resources_and_persons( $check, $product_id, $product_content ) {
661 if ( in_array( $product_content, [ 'wc_booking_resources', 'wc_booking_persons' ], true ) ) {
662 return false;
663 }
664
665 return $check;
666 }
667
668 public function replace_tm_editor_custom_fields_with_own_sections( $fields ) {
669 $fields[] = '_resource_base_costs';
670 $fields[] = '_resource_block_costs';
671
672 return $fields;
673 }
674
675 public function remove_single_custom_fields_to_translate( $fields ) {
676 $fields[] = '_wc_booking_resouce_label';
677
678 return $fields;
679 }
680
681 public function product_content_resource_label( $meta_key ) {
682 if ( '_wc_booking_resouce_label' === $meta_key ) {
683 return __( 'Resources label', 'woocommerce-multilingual' );
684 }
685
686 return $meta_key;
687 }
688
689 public function wcml_products_tab_sync_resources_and_persons( $original_product_id, $tr_product_id, $data, $language ) {
690
691 remove_action( 'save_post', [ $this->wpml_post_translations, 'save_post_actions' ], 100 );
692
693 $orig_resources = $this->get_original_resources( $original_product_id );
694
695 if ( $orig_resources ) {
696
697 foreach ( $orig_resources as $orig_resource_id => $cost ) {
698
699 $resource_id = apply_filters( 'wpml_object_id', $orig_resource_id, 'bookable_resource', false, $language );
700
701 $orig_resource = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT resource_id, sort_order FROM {$this->wpdb->prefix}wc_booking_relationships WHERE resource_id = %d AND product_id = %d", $orig_resource_id, $original_product_id ), OBJECT );
702
703 if ( is_null( $resource_id ) ) {
704
705 if ( is_object( $orig_resource ) ) {
706 $resource_id = $this->duplicate_resource( $tr_product_id, $orig_resource, $language );
707 } else {
708 continue;
709 }
710 } else {
711 $exist = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT ID FROM {$this->wpdb->prefix}wc_booking_relationships WHERE resource_id = %d AND product_id = %d", $resource_id, $tr_product_id ) );
712
713 if ( ! $exist ) {
714
715 $this->wpdb->insert(
716 $this->wpdb->prefix . 'wc_booking_relationships',
717 [
718 'product_id' => $tr_product_id,
719 'resource_id' => $resource_id,
720 'sort_order' => $orig_resource->sort_order,
721 ]
722 );
723
724 }
725 }
726
727 $this->wpdb->update(
728 $this->wpdb->posts,
729 [
730 'post_title' => $data[ md5( 'bookings-resource_' . $orig_resource_id . '_title' ) ],
731 ],
732 [
733 'ID' => $resource_id,
734 ]
735 );
736
737 update_post_meta( $resource_id, 'wcml_is_translated', true );
738
739 }
740
741 $this->sync_resources( $original_product_id, $tr_product_id, $language, false );
742
743 }
744
745 $original_persons = $this->get_original_persons( $original_product_id );
746
747 if ( $original_persons ) {
748
749 foreach ( $original_persons as $original_person_id ) {
750
751 $person_id = apply_filters( 'wpml_object_id', $original_person_id, 'bookable_person', false, $language );
752
753 if ( is_null( $person_id ) ) {
754
755 $person_id = $this->duplicate_person( $tr_product_id, $original_person_id, $language );
756
757 } else {
758
759 $this->wpdb->update(
760 $this->wpdb->posts,
761 [
762 'post_parent' => $tr_product_id,
763 ],
764 [
765 'ID' => $person_id,
766 ]
767 );
768
769 }
770
771 $this->wpdb->update(
772 $this->wpdb->posts,
773 [
774 'post_title' => $data[ md5( 'bookings-person_' . $original_person_id . '_title' ) ],
775 'post_excerpt' => $data[ md5( 'bookings-person_' . $original_person_id . '_description' ) ],
776 ],
777 [
778 'ID' => $person_id,
779 ]
780 );
781
782 update_post_meta( $person_id, 'wcml_is_translated', true );
783
784 }
785
786 $this->sync_persons( $original_product_id, $tr_product_id, $language, false );
787
788 }
789
790 add_action( 'save_post', [ $this->wpml_post_translations, 'save_post_actions' ], 100, 2 );
791
792 $this->clear_bookable_product_cache( $tr_product_id );
793
794 }
795
796 public function duplicate_booking_for_translations( $booking_id, $lang = false ) {
797 $booking_object = get_post( $booking_id );
798
799 $booking_data = [
800 'post_type' => self::POST_TYPE,
801 'post_title' => $booking_object->post_title,
802 'post_status' => $booking_object->post_status,
803 'ping_status' => 'closed',
804 ];
805
806 $active_languages = $this->sitepress->get_active_languages();
807
808 foreach ( $active_languages as $language ) {
809
810 $booking_product_id = get_post_meta( $booking_id, self::BOOKING_PRODUCT_ID_META, true );
811
812 if ( ! $lang ) {
813 $booking_language = $this->sitepress->get_element_language_details( $booking_product_id, 'post_product' );
814 if ( $booking_language->language_code === $language['code'] ) {
815 continue;
816 }
817 } elseif ( $lang !== $language['code'] ) {
818 continue;
819 }
820
821 $booking_persons = maybe_unserialize( get_post_meta( $booking_id, self::BOOKING_PERSONS_META, true ) );
822 $trnsl_booking_persons = [];
823
824 if ( is_array( $booking_persons ) && ! empty( $booking_persons ) ) {
825 foreach ( $booking_persons as $person_id => $person_count ) {
826
827 $trnsl_person_id = apply_filters( 'wpml_object_id', $person_id, 'bookable_person', false, $language['code'] );
828
829 if ( is_null( $trnsl_person_id ) ) {
830 $trnsl_booking_persons[] = $person_count;
831 } else {
832 $trnsl_booking_persons[ $trnsl_person_id ] = $person_count;
833 }
834 }
835 }
836
837 $trnsl_booking_id = wp_insert_post( $booking_data );
838 $trid = $this->sitepress->get_element_trid( $booking_id );
839 $this->sitepress->set_element_language_details( $trnsl_booking_id, 'post_' . self::POST_TYPE, $trid, $language['code'] );
840
841 $meta_args = [
842 self::BOOKING_ORDER_ITEM_ID_META => 0,
843 self::BOOKING_PRODUCT_ID_META => $this->get_translated_booking_product_id( $booking_id, $language['code'] ),
844 self::BOOKING_RESOURCE_ID_META => $this->get_translated_booking_resource_id( $booking_id, $language['code'] ),
845 self::BOOKING_PERSONS_META => $this->get_translated_booking_persons_ids( $booking_id, $language['code'] ),
846 self::BOOKING_COST_META => get_post_meta( $booking_id, self::BOOKING_COST_META, true ),
847 self::BOOKING_START_META => get_post_meta( $booking_id, self::BOOKING_START_META, true ),
848 self::BOOKING_END_META => get_post_meta( $booking_id, self::BOOKING_END_META, true ),
849 self::BOOKING_ALL_DAY_META => intval( get_post_meta( $booking_id, self::BOOKING_ALL_DAY_META, true ) ),
850 self::BOOKING_CUSTOMER_ID_META => get_post_meta( $booking_id, self::BOOKING_CUSTOMER_ID_META, true ),
851 self::BOOKING_DUPLICATE_OF_META => $booking_id,
852 '_language_code' => $language['code'],
853 ];
854
855 foreach ( $meta_args as $key => $value ) {
856 update_post_meta( $trnsl_booking_id, $key, $value );
857 }
858
859 WC_Cache_Helper::get_transient_version( 'bookings', true );
860
861 }
862
863 }
864
865 public function get_translated_booking_product_id( $booking_id, $language ) {
866
867 $booking_product_id = get_post_meta( $booking_id, self::BOOKING_PRODUCT_ID_META, true );
868 $trnsl_booking_product_id = '';
869
870 if ( $booking_product_id ) {
871 $trnsl_booking_product_id = apply_filters( 'wpml_object_id', $booking_product_id, 'product', false, $language );
872 if ( is_null( $trnsl_booking_product_id ) ) {
873 $trnsl_booking_product_id = $booking_product_id;
874 }
875 }
876
877 return $trnsl_booking_product_id;
878
879 }
880
881 public function get_translated_booking_resource_id( $booking_id, $language ) {
882
883 $booking_resource_id = get_post_meta( $booking_id, self::BOOKING_RESOURCE_ID_META, true );
884 $trnsl_booking_resource_id = '';
885
886 if ( $booking_resource_id ) {
887 $trnsl_booking_resource_id = apply_filters( 'wpml_object_id', $booking_resource_id, 'bookable_resource', false, $language );
888
889 if ( is_null( $trnsl_booking_resource_id ) ) {
890 $trnsl_booking_resource_id = $booking_resource_id;
891 }
892 }
893
894 return $trnsl_booking_resource_id;
895 }
896
897 public function get_translated_booking_persons_ids( $booking_id, $language ) {
898
899 $booking_persons = maybe_unserialize( get_post_meta( $booking_id, self::BOOKING_PERSONS_META, true ) );
900 $trnsl_booking_persons = [];
901
902 if ( is_array( $booking_persons ) && ! empty( $booking_persons ) ) {
903 foreach ( $booking_persons as $person_id => $person_count ) {
904
905 $trnsl_person_id = apply_filters( 'wpml_object_id', $person_id, 'bookable_person', false, $language );
906
907 if ( is_null( $trnsl_person_id ) ) {
908 $trnsl_booking_persons[] = $person_count;
909 } else {
910 $trnsl_booking_persons[ $trnsl_person_id ] = $person_count;
911 }
912 }
913 }
914
915 return $trnsl_booking_persons;
916
917 }
918
919 public function woocommerce_booking_status_changed_update_translations( $from, $to, $booking_id, $wc_booking ) {
920 if ( 'in-cart' === $from && 'unpaid' === $to ) {
921 return;
922 }
923 $this->update_status_for_translations( $booking_id );
924 }
925
926 public function update_status_for_translations( $booking_id ) {
927
928 foreach ( $this->get_translated_bookings( $booking_id, false ) as $translated_booking_id ) {
929 if ( (int) $booking_id !== (int) $translated_booking_id ) {
930 $status = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT post_status FROM {$this->wpdb->posts} WHERE ID = %d", $booking_id ) );
931 $language = $this->sitepress->get_language_for_element( $translated_booking_id, 'post_' . self::POST_TYPE );
932
933 $this->wpdb->update(
934 $this->wpdb->posts,
935 [
936 'post_status' => $status,
937 'post_parent' => wp_get_post_parent_id( $booking_id ),
938 ],
939 [
940 'ID' => $translated_booking_id,
941 ]
942 );
943
944 $this->update_translated_booking_meta( $translated_booking_id, $booking_id, $language );
945
946 wp_cache_delete( $translated_booking_id, 'posts' );
947 }
948 }
949 }
950
951 public function get_translated_bookings( $booking_id, $actual_translations_only = true ) {
952
953 return $this->wpml_post_translations->get_element_translations( $booking_id, false, $actual_translations_only );
954 }
955
956 public function bookings_in_date_range_query( $booking_ids ) {
957 $current_language = $this->sitepress->get_current_language();
958 $default_language = $this->sitepress->get_default_language();
959
960 foreach ( $booking_ids as $key => $booking_id ) {
961 $language_code = $this->sitepress->get_language_for_element( get_post_meta( $booking_id, self::BOOKING_PRODUCT_ID_META, true ), 'post_product' );
962 $wc_booking_lang = get_post_meta( $booking_id, '_language_code', true );
963 $wc_booking_lang = $wc_booking_lang ?: $language_code ?: $default_language;
964
965 if ( $wc_booking_lang != $current_language ) {
966 unset( $booking_ids[ $key ] );
967 }
968
969 if ( $language_code != $current_language ) {
970 unset( $booking_ids[ $key ] );
971 }
972 }
973
974 return $booking_ids;
975 }
976
977 public function delete_bookings( $booking_id ) {
978
979 if (
980 ! $this->is_delete_all_action()
981 && $booking_id
982 && get_post_type( $booking_id ) === self::POST_TYPE
983 ) {
984 remove_action( 'before_delete_post', [ $this, 'delete_bookings' ] );
985
986 foreach ( $this->get_translated_bookings( $booking_id ) as $translated_booking_id ) {
987 $this->wpdb->update(
988 $this->wpdb->posts,
989 [
990 'post_parent' => 0,
991 ],
992 [
993 'ID' => $translated_booking_id,
994 ]
995 );
996
997 wp_delete_post( $translated_booking_id );
998
999 }
1000
1001 add_action( 'before_delete_post', [ $this, 'delete_bookings' ] );
1002 }
1003 }
1004
1005 private function is_delete_all_action() {
1006 return array_key_exists( 'delete_all', $_GET ) && $_GET['delete_all'];
1007 }
1008
1009 public function trash_bookings( $booking_id ) {
1010
1011 if ( $booking_id > 0 && get_post_type( $booking_id ) === self::POST_TYPE ) {
1012
1013 foreach ( $this->get_translated_bookings( $booking_id ) as $translated_booking_id ) {
1014
1015 $this->wpdb->update(
1016 $this->wpdb->posts,
1017 [
1018 'post_status' => 'trash',
1019 ],
1020 [
1021 'ID' => $translated_booking_id,
1022 ]
1023 );
1024
1025 }
1026 }
1027
1028 }
1029
1030 public function append_persons_to_translation_package( $package, $post ) {
1031 if ( 'product' === $post->post_type ) {
1032 if ( $this->is_booking( $post->ID ) ) {
1033
1034 $bookable_product = new WC_Product_Booking( $post->ID );
1035
1036 $person_types = $bookable_product->get_person_types();
1037
1038 foreach ( $person_types as $person_type ) {
1039
1040 $bookable_person = get_post( $person_type->ID );
1041
1042 $package['contents'][ self::PERSON_FIELD_PREFIX . $bookable_person->ID . ':name' ] = [
1043 'translate' => 1,
1044 'data' => $this->tp->encode_field_data( $bookable_person->post_title ),
1045 'format' => 'base64',
1046 ];
1047
1048 $package['contents'][ self::PERSON_FIELD_PREFIX . $bookable_person->ID . ':description' ] = [
1049 'translate' => 1,
1050 'data' => $this->tp->encode_field_data( $bookable_person->post_excerpt ),
1051 'format' => 'base64',
1052 ];
1053
1054 }
1055 }
1056 }
1057
1058 return $package;
1059 }
1060
1061 private function save_person_translation( $post_id, $data, $job ) {
1062 $person_translations = [];
1063
1064 foreach ( $data as $value ) {
1065
1066 if ( $value['finished'] && strpos( $value['field_type'], self::PERSON_FIELD_PREFIX ) === 0 ) {
1067
1068 $exp = explode( ':', $value['field_type'] );
1069
1070 $person_id = $exp[2];
1071 $field = $exp[3];
1072
1073 $person_translations[ $person_id ][ $field ] = $value['data'];
1074
1075 }
1076 }
1077
1078 if ( $person_translations ) {
1079
1080 foreach ( $person_translations as $person_id => $pt ) {
1081
1082 $person_trid = $this->sitepress->get_element_trid( $person_id, 'post_bookable_person' );
1083
1084 $person_id_translated = apply_filters( 'wpml_object_id', $person_id, 'bookable_person', false, $job->language_code );
1085
1086 if ( empty( $person_id_translated ) ) {
1087
1088 $person_post = [
1089
1090 'post_type' => 'bookable_person',
1091 'post_status' => 'publish',
1092 'post_title' => $pt['name'],
1093 'post_parent' => $post_id,
1094 'post_excerpt' => isset( $pt['description'] ) ? $pt['description'] : '',
1095
1096 ];
1097
1098 $person_id_translated = wp_insert_post( $person_post );
1099
1100 $this->sitepress->set_element_language_details( $person_id_translated, 'post_bookable_person', $person_trid, $job->language_code );
1101
1102 } else {
1103
1104 $person_post = [
1105 'ID' => $person_id_translated,
1106 'post_title' => $pt['name'],
1107 'post_excerpt' => isset( $pt['description'] ) ? $pt['description'] : '',
1108 ];
1109
1110 wp_update_post( $person_post );
1111
1112 }
1113 }
1114 }
1115 }
1116
1117 public function append_resources_to_translation_package( $package, $post ) {
1118
1119 if ( $post->post_type == 'product' ) {
1120 $product = wc_get_product( $post->ID );
1121 if ( $this->is_booking( $product ) && $product->has_resources() ) {
1122
1123 $resources = $product->get_resources();
1124
1125 foreach ( $resources as $resource ) {
1126
1127 $package['contents'][ self::RESOURCE_FIELD_PREFIX . $resource->ID . ':name' ] = [
1128 'translate' => 1,
1129 'data' => $this->tp->encode_field_data( $resource->post_title ),
1130 'format' => 'base64',
1131 ];
1132
1133 }
1134 }
1135 }
1136
1137 return $package;
1138
1139 }
1140
1141 private function save_resource_translation( $post_id, $data, $job ) {
1142 $resource_translations = [];
1143
1144 foreach ( $data as $value ) {
1145
1146 if ( $value['finished'] && strpos( $value['field_type'], self::RESOURCE_FIELD_PREFIX ) === 0 ) {
1147
1148 $exp = explode( ':', $value['field_type'] );
1149
1150 $resource_id = $exp[2];
1151 $field = $exp[3];
1152
1153 $resource_translations[ $resource_id ][ $field ] = $value['data'];
1154
1155 }
1156 }
1157
1158 if ( $resource_translations ) {
1159
1160 foreach ( $resource_translations as $resource_id => $rt ) {
1161
1162 $resource_trid = $this->sitepress->get_element_trid( $resource_id, 'post_bookable_resource' );
1163
1164 $resource_id_translated = apply_filters( 'wpml_object_id', $resource_id, 'bookable_resource', false, $job->language_code );
1165
1166 if ( empty( $resource_id_translated ) ) {
1167
1168 $resource_post = [
1169
1170 'post_type' => 'bookable_resource',
1171 'post_status' => 'publish',
1172 'post_title' => $rt['name'],
1173 'post_parent' => $post_id,
1174 ];
1175
1176 $resource_id_translated = wp_insert_post( $resource_post );
1177
1178 $this->sitepress->set_element_language_details( $resource_id_translated, 'post_bookable_resource', $resource_trid, $job->language_code );
1179
1180 $sort_order = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT sort_order FROM {$this->wpdb->prefix}wc_booking_relationships WHERE resource_id=%d", $resource_id ) );
1181 $relationship = [
1182 'product_id' => $post_id,
1183 'resource_id' => $resource_id_translated,
1184 'sort_order' => $sort_order,
1185 ];
1186 $this->wpdb->insert( $this->wpdb->prefix . 'wc_booking_relationships', $relationship );
1187
1188 } else {
1189
1190 $resource_post = [
1191 'ID' => $resource_id_translated,
1192 'post_title' => $rt['name'],
1193 ];
1194
1195 wp_update_post( $resource_post );
1196
1197 $sort_order = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT sort_order FROM {$this->wpdb->prefix}wc_booking_relationships WHERE resource_id=%d", $resource_id ) );
1198 $this->wpdb->update(
1199 $this->wpdb->prefix . 'wc_booking_relationships',
1200 [ 'sort_order' => $sort_order ],
1201 [
1202 'product_id' => $post_id,
1203 'resource_id' => $resource_id_translated,
1204 ]
1205 );
1206
1207 }
1208 }
1209 }
1210 }
1211
1212 public function wcml_js_lock_fields_ids( $ids ) {
1213 $ids = array_merge(
1214 $ids,
1215 [
1216 '_wc_booking_has_resources',
1217 '_wc_booking_has_persons',
1218 '_wc_booking_duration_type',
1219 '_wc_booking_duration',
1220 '_wc_booking_duration_unit',
1221 '_wc_booking_calendar_display_mode',
1222 '_wc_booking_requires_confirmation',
1223 '_wc_booking_user_can_cancel',
1224 '_wc_accommodation_booking_min_duration',
1225 '_wc_accommodation_booking_max_duration',
1226 '_wc_accommodation_booking_max_duration',
1227 '_wc_accommodation_booking_calendar_display_mode',
1228 '_wc_accommodation_booking_requires_confirmation',
1229 '_wc_accommodation_booking_user_can_cancel',
1230 '_wc_accommodation_booking_cancel_limit',
1231 '_wc_accommodation_booking_cancel_limit_unit',
1232 '_wc_accommodation_booking_qty',
1233 '_wc_accommodation_booking_min_date',
1234 '_wc_accommodation_booking_min_date_unit',
1235 '_wc_accommodation_booking_max_date',
1236 '_wc_accommodation_booking_max_date_unit',
1237 'bookings_pricing select',
1238 'bookings_resources select',
1239 'bookings_availability select',
1240 'bookings_persons input[type="checkbox"]',
1241 ]
1242 );
1243
1244 return $ids;
1245 }
1246
1247 public function filter_get_booking_resources_args( $args ) {
1248
1249 $screen = get_current_screen();
1250 if ( $screen->id == 'product' ) {
1251 $args['suppress_filters'] = false;
1252 }
1253
1254 return $args;
1255
1256 }
1257
1258 public function extra_conditions_to_filter_bookings( $extra_conditions ) {
1259
1260 if ( isset( $_GET['post_type'] ) && $_GET['post_type'] === self::POST_TYPE && ! isset( $_GET['post_status'] ) ) {
1261 $extra_conditions = str_replace( 'GROUP BY', " AND post_status = 'confirmed' GROUP BY", $extra_conditions );
1262 }
1263
1264 return $extra_conditions;
1265 }
1266
1267 public function hide_bookings_type_on_tm_dashboard( $types ) {
1268 unset( $types[ self::POST_TYPE ] );
1269
1270 return $types;
1271 }
1272
1273 public function show_pointer_info() {
1274 $pointerFactory = new WCML\PointerUi\Factory();
1275
1276 $pointerFactory
1277 ->create( [
1278 'content' => sprintf(
1279 /* translators: %1$s and %2$s are opening and closing HTML link tags */
1280 esc_html__( 'To translate Resources, go to the %1$sTranslation Dashboard%2$s and send the associated product for translation.', 'woocommerce-multilingual' ),
1281 '<a href="' . esc_url( \WCML\Utilities\AdminUrl::getWPMLTMDashboardProducts() ) . '">',
1282 '</a>'
1283 ),
1284 'selectorId' => 'bookings_resources .woocommerce_bookable_resources #message',
1285 'docLink' => WCML_Tracking_Link::getWcmlBookingsDoc(),
1286 ] )
1287 ->show();
1288
1289 $pointerFactory
1290 ->create( [
1291 'content' => sprintf(
1292 /* translators: %1$s and %2$s are opening and closing HTML link tags */
1293 esc_html__( 'To translate Person Types, go to the %1$sTranslation Dashboard%2$s and send the associated product for translation.', 'woocommerce-multilingual' ),
1294 '<a href="' . esc_url( \WCML\Utilities\AdminUrl::getWPMLTMDashboardProducts() ) . '">',
1295 '</a>'
1296 ),
1297 'selectorId' => 'bookings_persons #persons-types>div.toolbar',
1298 'docLink' => WCML_Tracking_Link::getWcmlBookingsDoc(),
1299 ] )
1300 ->show();
1301 }
1302
1303 public function add_to_cart_sold_individually( $sold_indiv, $cart_item_data, $product_id, $quantity ) {
1304
1305 if ( isset( $cart_item_data['booking'] ) ) {
1306 $sold_indiv = false;
1307 foreach ( WC()->cart->cart_contents as $cart_item ) {
1308 if (
1309 isset( $cart_item['booking'] ) && isset( $cart_item['booking']['_booking_id'] ) &&
1310 $cart_item['booking']['_start_date'] == $cart_item_data['booking']['_start_date'] &&
1311 $cart_item['booking']['_end_date'] == $cart_item_data['booking']['_end_date'] &&
1312 $cart_item['booking']['_booking_id'] == $cart_item_data['booking']['_booking_id']
1313 ) {
1314 $sold_indiv = true;
1315 }
1316 }
1317 }
1318
1319 return $sold_indiv;
1320 }
1321
1322 public function filter_translatable_documents( $icl_post_types ) {
1323
1324 if ( isset( $_GET['post'] ) && self::POST_TYPE === get_post_type( $_GET['post'] ) ) {
1325 unset( $icl_post_types[ self::POST_TYPE ] );
1326 }
1327
1328 return $icl_post_types;
1329 }
1330
1331 public function filter_is_translated_post_type( $type ) {
1332
1333 $getData = wpml_collect( $_GET );
1334
1335 if ( self::POST_TYPE === $getData->get('post_type') && 'create_booking' !== $getData->get( 'page' ) ) {
1336 return null;
1337 }
1338
1339 return $type;
1340 }
1341
1342 public function sync_booking_status( $post_id, $post, $update ) {
1343
1344 if ( $post->post_type === self::POST_TYPE && $update ) {
1345 foreach ( $this->get_translated_bookings( $post_id, false ) as $translated_booking_id ) {
1346 if ( (int) $translated_booking_id !== (int) $post_id ) {
1347 $this->wpdb->update(
1348 $this->wpdb->posts,
1349 [ 'post_status' => $post->post_status ],
1350 [ 'ID' => $translated_booking_id ]
1351 );
1352 }
1353 }
1354 }
1355 }
1356
1357 public function booking_email_language( $current_language ) {
1358
1359 if ( isset( $_POST['post_type'] ) && self::POST_TYPE === $_POST['post_type'] && isset( $_POST['_booking_order_id'] ) ) {
1360 $order_language = WCML_Orders::getLanguage( (int) $_POST['_booking_order_id'] );
1361 if ( $order_language ) {
1362 $current_language = $order_language;
1363 }
1364 }
1365
1366 return $current_language;
1367 }
1368
1369 public function maybe_set_booking_language( $booking_id ) {
1370
1371 if ( self::isWcBooking( $booking_id ) ) {
1372 $language_details = $this->sitepress->get_element_language_details( $booking_id, 'post_' . self::POST_TYPE );
1373 if ( ! is_object( $language_details ) ) {
1374 $current_language = $this->sitepress->get_current_language();
1375 $this->sitepress->set_element_language_details( $booking_id, 'post_' . self::POST_TYPE, false, $current_language );
1376 }
1377 }
1378
1379 }
1380
1381 private function is_booking( $product ) {
1382 if ( ! $product instanceof WC_Product ) {
1383 $product = wc_get_product( $product );
1384 }
1385
1386 return $product ? $product->get_type() === 'booking' : false;
1387 }
1388
1389 public function count_bookings_by_current_language( $counts, $type ) {
1390
1391 $query = "SELECT p.post_status, COUNT( * ) AS num_posts FROM {$this->wpdb->posts} as p
1392 LEFT JOIN {$this->wpdb->prefix}icl_translations as icl ON p.ID = icl.element_id
1393 WHERE p.post_type = %s AND icl.language_code = %s AND icl.element_type = %s GROUP BY p.post_status";
1394
1395 $results = $this->wpdb->get_results( $this->wpdb->prepare( $query, $type, $this->sitepress->get_current_language(), 'post_' . self::POST_TYPE ), ARRAY_A );
1396 $counts = array_fill_keys( get_post_stati(), 0 );
1397
1398 foreach ( $results as $row ) {
1399 $counts[ $row['post_status'] ] = $row['num_posts'];
1400 }
1401
1402 $counts = (object) $counts;
1403
1404 return $counts;
1405 }
1406
1407 public function unset_mine_from_bookings_views( $views ) {
1408 unset( $views['mine'] );
1409
1410 return $views;
1411 }
1412
1413 public function remove_language_switcher() {
1414 remove_action( 'wp_before_admin_bar_render', [ $this->sitepress, 'admin_language_switcher' ] );
1415 }
1416
1417 private function is_bookings_listing_page() {
1418 return isset( $_GET['post_type'] ) && self::POST_TYPE === $_GET['post_type'];
1419 }
1420
1421 public function save_booking_data_to_translation( $post_id, $data, $job ) {
1422 if ( $this->is_booking( $job->original_doc_id ) ) {
1423 $this->save_person_translation( $post_id, $data, $job );
1424 $this->save_resource_translation( $post_id, $data, $job );
1425 }
1426 }
1427
1428 public function synchronize_bookings_on_translation_completed( $new_post_id, $fields, $job ) {
1429 if (
1430 Str::startsWith( 'post_', $job->original_post_type )
1431 && $this->is_booking( $job->original_doc_id )
1432 ) {
1433 do_action( \WCML\Synchronization\Hooks::HOOK_SYNCHRONIZE_PRODUCT_TRANSLATIONS, get_post( $job->original_doc_id ), [], [] );
1434
1435 $this->clear_bookable_product_cache( $new_post_id );
1436 }
1437 }
1438
1439 public function prevent_events_on_duplicates( $event ) {
1440 if (
1441 isset( $event->hook, $event->args[0] )
1442 && in_array( $event->hook, [ 'wc-booking-reminder', 'wc-booking-complete' ], true )
1443 && get_post_meta( $event->args[0], self::BOOKING_DUPLICATE_OF_META, true )
1444 ) {
1445 return false;
1446 }
1447
1448 return $event;
1449 }
1450
1451 private function maybe_sync_updated_booking_meta( $booking_id ) {
1452 if ( self::isWcBooking( $booking_id ) ) {
1453
1454 $booking_translations = $this->get_translated_bookings( $booking_id, false );
1455
1456 $base_meta_args = [
1457 self::BOOKING_COST_META => get_post_meta( $booking_id, self::BOOKING_COST_META, true ),
1458 self::BOOKING_START_META => get_post_meta( $booking_id, self::BOOKING_START_META, true ),
1459 self::BOOKING_END_META => get_post_meta( $booking_id, self::BOOKING_END_META, true ),
1460 self::BOOKING_ALL_DAY_META => intval( get_post_meta( $booking_id, self::BOOKING_ALL_DAY_META, true ) ),
1461 self::BOOKING_CUSTOMER_ID_META => get_post_meta( $booking_id, self::BOOKING_CUSTOMER_ID_META, true ),
1462 ];
1463
1464 foreach ( $booking_translations as $language_code => $translated_booking_id ) {
1465 if ( (int) $translated_booking_id === (int) $booking_id ) {
1466 continue;
1467 }
1468 $meta_args = array_merge(
1469 $base_meta_args,
1470 [
1471 self::BOOKING_PRODUCT_ID_META => $this->get_translated_booking_product_id( $booking_id, $language_code ),
1472 self::BOOKING_RESOURCE_ID_META => $this->get_translated_booking_resource_id( $booking_id, $language_code ),
1473 self::BOOKING_PERSONS_META => $this->get_translated_booking_persons_ids( $booking_id, $language_code ),
1474 ]
1475 );
1476
1477 foreach ( $meta_args as $key => $value ) {
1478 update_post_meta( $translated_booking_id, $key, $value );
1479 }
1480
1481 $this->update_booking_order( $booking_id, $translated_booking_id );
1482 }
1483 }
1484 }
1485
1486 public function sync_customer_created_during_checkout( $meta_id, $object_id, $meta_key, $meta_value ) {
1487 if (
1488 self::BOOKING_CUSTOMER_ID_META === $meta_key
1489 && intval( $meta_value ) > 0
1490 && self::isWcBooking( $object_id )
1491 ) {
1492 $this->maybe_sync_updated_booking_meta( $object_id );
1493 }
1494 }
1495
1496 private function update_booking_order( $booking_id, $translated_booking_id ) {
1497 $order_id = wp_get_post_parent_id( $booking_id );
1498 $translation_order_id = wp_get_post_parent_id( $translated_booking_id );
1499
1500 if ( $order_id !== $translation_order_id ) {
1501 wp_update_post( [
1502 'ID' => $translated_booking_id,
1503 'post_parent' => $order_id,
1504 ] );
1505 }
1506 }
1507
1508 private function clear_bookable_product_cache( $product_id ) {
1509 wp_cache_delete( 'wc_product_' . $product_id . '_cache_prefix', 'product_' . $product_id );
1510 wp_cache_delete( 'wc_object_' . $product_id . '_cache_prefix', 'object_' . $product_id );
1511 }
1512
1513 public static function isWcBooking( $postId ) : bool {
1514 return self::POST_TYPE === get_post_type( $postId );
1515 }
1516 }
1517