PluginProbe
Easy Hotel – Powerful Hotel Booking / 2.0.4
Easy Hotel – Powerful Hotel Booking v2.0.4
2.0.8 2.0.7 2.0.6 2.0.5 2.0.4 2.0.3 2.0.2 2.0.1 2.0.0 1.9.9 1.9.8 1.9.7 1.9.6 1.9.5 1.9.4 1.9.3 1.9.2 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 All 110 releases
easy-hotel / admin / includes / classes / class.helper.php

class.helper.php in Easy Hotel – Powerful Hotel Booking 2.0.4, at admin/includes/classes/class.helper.php

941 lines 37.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3 class ESHB_Helper {
4
5 public static function eshb_nonce_field( $action = 'eshb_action', $field = 'eshb_nonce', $echo = true ) {
6 // Generate site-specific action using home_url()
7 $nonce_action = sanitize_key( $action ) . '_' . md5( home_url() );
8
9 // Create the nonce field HTML
10 $nonce_field = wp_nonce_field( $nonce_action, sanitize_key( $field ), true, false );
11
12 // Whitelist only the safe HTML tags and attributes
13 $allowed_tags = array(
14 'input' => array(
15 'type' => true,
16 'id' => true,
17 'name' => true,
18 'value' => true,
19 ),
20 );
21
22 // Sanitize output
23 $safe_nonce_field = wp_kses( $nonce_field, $allowed_tags );
24
25 if ( $echo ) {
26 // Escaping required by Plugin Check (escape before output)
27 echo wp_kses( $safe_nonce_field, $allowed_tags );
28 return null;
29 }
30
31 // Return sanitized version
32 return $safe_nonce_field;
33 }
34
35 public static function generate_secure_nonce_action($action) {
36 $nonce_action = $action . '_' . md5( home_url() );
37 return $nonce_action;
38 }
39
40 public static function eshb_insert_booking($order_id, $booking_status, $cart_item_data) {
41
42 if(empty($booking_status) || empty($cart_item_data) || $cart_item_data === null) return false;
43
44 $post_id = wp_insert_post(array(
45 'post_type' => 'eshb_booking',
46 'post_title' => 'Booking',
47 'post_status' => $booking_status,
48 'meta_input' => array(
49 'eshb_booking_metaboxes' => $cart_item_data
50 )
51 ));
52
53 // Check if the post was inserted successfully
54 $accomodation_id = !empty($cart_item_data['booking_accomodation_id']) ? $cart_item_data['booking_accomodation_id'] : 0;
55 $accomodation_title = get_the_title( $accomodation_id );
56
57 if (!is_wp_error($post_id)) {
58 // Update the post title to include the post ID
59 wp_update_post(array(
60 'ID' => $post_id,
61 'post_title' => 'Booking #' . $post_id . ' for: ' . $accomodation_title,
62 'post_status' => $booking_status, // Update status to 'publish'
63 ));
64
65
66 // Update Available Rooms Count For This Accomodation
67 $accomodation_metaboxes = get_post_meta( $accomodation_id, 'eshb_accomodation_metaboxes', true );
68 $total_rooms = floatval($accomodation_metaboxes['total_rooms']);
69 $current_available_rooms = floatval($accomodation_metaboxes['available_rooms']);
70 $room_quantity = isset($cart_item_data['room_quantity']) ? $cart_item_data['room_quantity'] : 1;
71
72 if(!empty($current_available_rooms)){
73 $available_rooms = $current_available_rooms - floatval($room_quantity);
74 }else{
75 $available_rooms = $total_rooms - floatval($room_quantity);
76 }
77
78 $accomodation_metaboxes['available_rooms'] = $available_rooms;
79 update_post_meta($accomodation_id, 'eshb_accomodation_metaboxes', $accomodation_metaboxes);
80
81 // Update Order Status
82 $new_status = $booking_status;
83 update_post_meta($order_id, '_booking_post_created', $post_id);
84
85
86 do_action( 'eshb_after_booking_created', $post_id, $order_id );
87
88 return $post_id;
89
90 }else{
91 $error_message = $post_id->get_error_message();
92 return false;
93 }
94 }
95
96 public static function get_clean_number($num){
97 if (intval($num) == $num) {
98 return intval($num);
99 }
100 return $num;
101 }
102
103 public static function get_product_id_for_cart($accomodation_id, $booking_type) {
104 $product_id = 0;
105
106 if ($booking_type == 'woocommerce') {
107 $thumbnail_id = get_post_thumbnail_id($accomodation_id);
108 $product_id = self::get_or_create_woocommerce_product($accomodation_id, $thumbnail_id);
109 }
110 return $product_id;
111 }
112
113 public static function get_main_post_id_for_translated ($post_id) {
114 $main_post_id = $post_id;
115 if ( function_exists( 'pll_get_post' )) {
116 $default_lang = pll_default_language() ? pll_default_language() : 'en';
117 $main_post_id = pll_get_post( $post_id, $default_lang ) ? pll_get_post( $post_id, $default_lang ) : $post_id ;
118
119 }elseif ( function_exists( 'apply_filters' ) && function_exists( 'icl_object_id' ) ) {
120 $main_post_id = apply_filters( 'wpml_original_element_id', NULL, $post_id, 'post_post' );
121 }
122 return $main_post_id;
123 }
124
125 public static function eshb_get_booking_statuses(){
126
127 $statuses = array(
128 'pending' => 'Pending payment',
129 'deposit-payment' => 'Deposit payment',
130 'processing' => 'Processing',
131 'on-hold' => 'On hold',
132 'completed' => 'Completed',
133 'cancelled' => 'Cancelled',
134 'refunded' => 'Refunded',
135 'failed' => 'Failed'
136 );
137 return apply_filters( 'eshb_booking_statuses', $statuses );
138
139 }
140
141 public static function eshb_get_payment_statuses(){
142
143 $order_status = array(
144 'pending' => 'Pending',
145 'processing' => 'Processing',
146 'on-hold' => 'On hold',
147 'completed' => 'Completed',
148 'cancelled' => 'Cancelled',
149 'refunded' => 'Refunded',
150 'failed' => 'Failed'
151 );
152 return $order_status;
153
154 }
155
156 public static function get_or_create_woocommerce_product($accomodation_id, $thumbnail_id) {
157
158 $product_id = get_post_meta($accomodation_id, '_woocommerce_product_id', true);
159 $product = (!empty($product_id) && get_post_status($product_id) === 'publish') ? wc_get_product($product_id) : false;
160
161 if (!$product) {
162 $product = new WC_Product();
163 $product->set_name(get_the_title($accomodation_id));
164 $product->set_price(1);
165 $product->set_regular_price(1);
166 $product->set_virtual(true);
167 $product->set_image_id($thumbnail_id);
168 $product->save();
169
170 $product_id = $product->get_id();
171
172 update_post_meta($accomodation_id, '_woocommerce_product_id', $product_id);
173 update_post_meta($accomodation_id, '_regular_price', 1);
174 }
175
176 return $product_id;
177 }
178
179 public static function update_product_price_by_id($product_id, $new_price) {
180 // Update the regular price
181 update_post_meta($product_id, '_regular_price', $new_price);
182
183 // Update the price (current price)
184 update_post_meta($product_id, '_price', $new_price);
185
186 // Clear WooCommerce product cache
187 wc_delete_product_transients($product_id);
188 }
189
190 public static function get_current_booking_metadata($booking_id, $key) {
191 if (empty($booking_id)) {
192 $booking_id = get_the_ID();
193 }
194 if (empty($booking_id)) {
195 return '';
196 }
197 $eshb_booking_metaboxes = get_post_meta($booking_id, 'eshb_booking_metaboxes', true);
198 return isset($eshb_booking_metaboxes[$key]) ? $eshb_booking_metaboxes[$key] : '';
199 }
200
201 public static function get_current_booking_customer_metadata($booking_id, $key = '', $default_value = 'default') {
202 if (empty($booking_id)) {
203 $booking_id = get_the_ID();
204 }
205
206 $address = get_post_meta($booking_id, 'eshb_booking_customer_details_metaboxes', true);
207 $value = $address ?? $default_value;
208
209 if(!empty($key)){
210 $value = $address[$key] ?? $default_value;
211 }
212 return $value;
213 }
214
215 public static function get_current_payment_customer_metadata($id, $key) {
216 if (empty($id)) {
217 $id = get_the_ID();
218 }
219 if (empty($id)) return '';
220
221 $address = get_post_meta($id, 'eshb_payment_customer_details_metaboxes', true);
222 return isset($address[$key]) ? $address[$key] : 'default';
223 }
224
225 public static function eshb_get_payment_ids() {
226
227 $args = array(
228 'post_type' => 'eshb_payment',
229 'posts_per_page' => -1,
230 'fields' => 'ids',
231 'post_status' => array( 'completed' ),
232 'orderby' => 'ID',
233 'order' => 'DESC',
234 );
235
236 $payments = get_posts($args);
237 $payment_ids = [];
238
239 foreach ($payments as $payment_id) {
240 if(get_post_status( $payment_id ) == 'completed'){
241 $payment_ids[$payment_id] = get_the_title($payment_id);
242 }
243 }
244
245 return $payment_ids;
246 }
247
248 public static function eshb_get_booking_ids() {
249
250 $booking_statuses = self::eshb_get_booking_statuses();
251 $booking_status_keys = array_keys( $booking_statuses );
252
253 $args = array(
254 'post_type' => 'eshb_booking',
255 'posts_per_page' => -1,
256 'fields' => 'ids',
257 'post_status' => 'any'
258 );
259
260 $bookings = get_posts($args);
261 $booking_ids = [];
262
263 foreach ($bookings as $booking_id) {
264 $booking_ids[$booking_id] = get_the_title($booking_id);
265 }
266
267 return $booking_ids;
268 }
269
270 public static function eshb_get_payment_gateways() {
271 $gateways = [
272 'manual' => 'Manual Payment',
273 'cod' => 'Cash On Delivey'
274 ];
275 return $gateways;
276 }
277
278 public static function get_total_price_from_order_meta($order){
279 $total_price = 0;
280 foreach ( $order->get_items() as $item ) {
281 $item_total_price = $item->get_meta('Total Price');
282 if (!empty($item_total_price)) {
283 $total_price += floatval($item_total_price);
284 }
285 }
286 return $total_price;
287 }
288
289 public static function eshb_get_wc_state_city_name ($country_code, $code) {
290 $states_file = WP_PLUGIN_DIR . '/woocommerce/i18n/states.php';
291 $state_city_name = '';
292 if ( file_exists( $states_file ) ) {
293 $all_states = include $states_file; // returns array of all countries' states
294 $all_states = apply_filters( 'woocommerce_states', $all_states );
295 $state_city_name = isset( $all_states[$country_code][ $code ] ) ? $all_states[$country_code][ $code ] : '';
296 }
297 return $state_city_name;
298 }
299
300 public static function eshb_get_extra_services () {
301
302 $services = [];
303
304 $posts = get_posts([
305 'post_type' => 'eshb_service',
306 'posts_per_page' => -1
307 ]);
308
309 foreach ( $posts as $post ) {
310 $services[ $post->ID ] = $post->post_title;
311 }
312 return $services;
313 }
314
315 // Record a payment (deposit or subsequent) against an order.
316 public static function eshb_assign_payment_to_booking( $payment_id, $order_id, $booking_id, $amount_paid, $update = false, $args = [] ) {
317
318 if ( $amount_paid <= 0 ) {
319 return false;
320 }
321
322 $eshb_settings = get_option('eshb_settings');
323 $booking_type = isset($eshb_settings['booking-type']) ? $eshb_settings['booking-type'] : '';
324 $eshb_booking_metaboxes = get_post_meta( $booking_id, 'eshb_booking_metaboxes', true);
325 $eshb_payment_metaboxes = get_post_meta( $payment_id, 'eshb_payment_metaboxes', true);
326 $total_price = !empty($eshb_booking_metaboxes['total_price']) ? $eshb_booking_metaboxes['total_price'] : 0;
327 $total_paid = !empty($eshb_booking_metaboxes['total_paid']) ? $eshb_booking_metaboxes['total_paid'] : 0;
328 $due = (float) get_post_meta( $order_id, 'eshb_booking_due_amount', true );
329 $booking_new_status = 'deposit-payment';
330
331 $payment_ids = !empty($eshb_booking_metaboxes['payment_ids']) ? $eshb_booking_metaboxes['payment_ids'] : [];
332 if(!in_array($payment_id, $payment_ids)){
333 array_push($payment_ids, $payment_id);
334 }
335
336 if($booking_type == 'woocommerce' && class_exists( 'woocommerce' )){
337 $order = wc_get_order($order_id);
338 if(!$order) return;
339 $total_price = $order->get_subtotal();
340 $total_paid = $order->get_total();
341 }
342
343 $total_paid = 0;
344
345 foreach ( $payment_ids as $payment_id ) {
346 $metabox = get_post_meta( $payment_id, 'eshb_payment_metaboxes', true );
347
348 if ( ! empty( $metabox['amount'] ) ) {
349 $total_paid += floatval( $metabox['amount'] );
350 }
351 }
352
353 $new_due = (float) $total_price - (float)$total_paid;
354 $eshb_booking_metaboxes['payment_ids'] = $payment_ids;
355 $eshb_booking_metaboxes['total_paid'] = $total_paid;
356
357 if($new_due > 0){
358 $eshb_booking_metaboxes['due_amount'] = $new_due;
359 update_post_meta($order_id, 'eshb_booking_due_amount', $new_due);
360 }else{
361
362 if (!empty($eshb_settings['booking-auto-approval']) && $eshb_settings['booking-auto-approval'] == true) {
363 $booking_new_status = 'completed';
364 }else{
365 $booking_new_status = 'processing';
366 }
367 delete_post_meta( $order_id, 'eshb_booking_due_amount');
368 }
369
370 $eshb_booking_metaboxes['booking_status'] = $booking_new_status;
371
372
373 // update booking metabox
374 update_post_meta($booking_id, 'eshb_booking_metaboxes', $eshb_booking_metaboxes);
375
376
377 // update payment metabox
378 if(empty($eshb_payment_metaboxes['transaction_id'])){
379 $transaction_id = 'TXN-' . str_pad( $payment_id, 8, '0', STR_PAD_LEFT );
380 $eshb_payment_metaboxes['transaction_id'] = $transaction_id;
381 update_post_meta($payment_id, 'eshb_payment_metaboxes', $eshb_payment_metaboxes);
382 }
383
384 // update order
385 if($booking_type == 'woocommerce' && class_exists( 'woocommerce' )){
386 // update woocommerce order
387 $order = wc_get_order($order_id);
388 if($order){
389 $order->set_total($total_paid);
390 $order->update_status($booking_new_status);
391 if($new_due <= 0){
392 //$order->payment_complete();
393 $order->add_order_note('Payment received manually.');
394 }
395 $txn = $order->get_transaction_id();
396
397 $order->save();
398 }
399 }else{
400 // update booking status
401 wp_update_post( [
402 'ID' => $booking_id,
403 'post_status' => $booking_new_status
404 ]);
405 }
406
407 return $booking_id;
408 }
409
410 public static function get_eshb_default_start_end_date(){
411 $today_date = gmdate('Y-m-d'); // Get today's date
412
413 // Create a DateTime object from today's date
414 $date = new DateTime($today_date);
415
416 // Add one day
417 $date->modify('+1 day');
418
419 // Get the new date in 'Y-m-d' format
420 $tomorrow_date = $date->format('Y-m-d');
421
422 return array(
423 'start_date' => $today_date,
424 'end_date' => $tomorrow_date
425 );
426 }
427
428 public static function get_eshb_default_start_end_time(){
429 return array(
430 'start_time' => '10:00',
431 'end_time' => '11:00'
432 );
433 }
434
435 public static function eshb_capture_payment ($order_id){
436
437 if(!$order_id) return;
438
439 $order = wc_get_order( $order_id );
440 if ( !$order ) return;
441
442 $order_status = $order->get_status();
443 $due = (float) ( get_post_meta( $order_id, 'eshb_booking_due_amount', true ) ?: 0 );
444 $last_payment_type = get_post_meta( $order_id, 'last_payment_type', true );
445
446 if($due < 1 && $order_status == 'completed'){
447 $last_payment_type = 'full_payment';
448 }
449
450 if(!$last_payment_type) return;
451
452 $booking_id = get_post_meta($order_id, '_booking_post_created', true);
453 if(!$booking_id) return;
454
455 $payment_status = get_post_meta($booking_id, 'payment_status', true);
456
457 $eshb_booking_metaboxes = get_post_meta($booking_id, 'eshb_booking_metaboxes', true);
458 $total_price = ESHB_Helper::get_total_price_from_order_meta($order);
459 $total_paid = !empty($eshb_booking_metaboxes['total_paid']) ? $eshb_booking_metaboxes['total_paid'] : 0;
460
461 if($payment_status == 'completed' && $total_paid > 0) {
462 return;
463 }
464
465 if(in_array($last_payment_type, ['initial_deposit', 'remaining_payment']) && $total_paid >= $total_price) {
466 return;
467 };
468
469
470
471 $gateway = $order->get_payment_method();
472 $currency = $order->get_currency();
473 $fee = 0;
474 $amount = $total_paid;
475 $payment_type = 'Full Payment';
476 $initial_deposit = get_post_meta( $order_id, 'initial_deposit', true );
477 $payment_status = 'completed';
478 $new_due = 0;
479
480
481 if($last_payment_type == 'initial_deposit'){
482 $amount = $initial_deposit;
483 $payment_type = 'Initial Deposit';
484 $status = 'deposit-payment';
485 $new_due = $total_price - $initial_deposit;
486 $total_paid = $initial_deposit;
487 }elseif($last_payment_type == 'remaining_payment'){
488 $amount = $due;
489 $total_paid = $total_price;
490 $payment_type = 'Remaining Payment';
491 }else{
492 $amount = $total_price;
493 $total_paid = $total_price;
494 $payment_type = 'Full Payment';
495 }
496
497 // create payment options
498 $payment_options = [
499 'booking_id' => $booking_id,
500 'transaction_id' => '',
501 'gateway' => $gateway,
502 'gateway_mode' => 'live',
503 'amount' => $amount,
504 'fee' => $fee,
505 'currency' => $currency,
506 'payment_type' => $payment_type,
507 ];
508
509
510 // load customer details from order
511 $first_name = $order->get_billing_first_name();
512 $last_name = $order->get_billing_last_name();
513 $email = $order->get_billing_email();
514 $phone = $order->get_billing_phone();
515 $country = $order->get_billing_country();
516 $state = $order->get_billing_state();
517 $city = $order->get_billing_city();
518 $address_1 = $order->get_billing_address_1();
519 $address_2 = $order->get_billing_address_2();
520 $postcode = $order->get_billing_postcode();
521
522 // create customer details
523 $customer_details = [
524 'first_name' => $first_name,
525 'last_name' => $last_name,
526 'email' => $email,
527 'phone' => $phone,
528 'country' => $country,
529 'state' => $state,
530 'city' => $city,
531 'address_1' => $address_1,
532 'address_2' => $address_2,
533 'postcode' => $postcode,
534 ];
535
536 $post_title = 'Payment for Booking #' . $booking_id;
537
538 // insert payment to eshb_payment posttype
539 $payment_id = wp_insert_post(
540 [
541 'post_title' => $post_title,
542 'post_type' => 'eshb_payment',
543 'post_status' => $payment_status,
544 ]
545 );
546
547 // update payment metadata if payment success
548 if($payment_id){
549 $transaction_id = 'TXN-' . str_pad( $payment_id, 8, '0', STR_PAD_LEFT );
550 $payment_options['transaction_id'] = $transaction_id;
551 update_post_meta($payment_id, 'eshb_payment_metaboxes', $payment_options);
552 update_post_meta($payment_id, 'eshb_payment_customer_details_metaboxes', $customer_details);
553 update_post_meta($booking_id, 'eshb_booking_customer_details_metaboxes', $customer_details);
554
555 // update booking metaboxes
556 $payment_ids = !empty($eshb_booking_metaboxes['payment_ids']) ? $eshb_booking_metaboxes['payment_ids'] : [];
557 if(!in_array($payment_id, $payment_ids)){
558 array_push($payment_ids, $payment_id);
559 }
560
561
562 $eshb_booking_metaboxes['payment_ids'] = $payment_ids;
563 $eshb_booking_metaboxes['total_paid'] = $total_paid;
564
565
566 // update due meta
567 if($last_payment_type == 'initial_deposit'){
568 $eshb_booking_metaboxes['due_amount'] = $new_due;
569 update_post_meta($order_id, 'eshb_booking_due_amount', $new_due);
570 update_post_meta($order_id, 'last_payment_type', $last_payment_type);
571 }elseif($last_payment_type == 'remaining_payment'){
572 delete_post_meta( $order_id, 'eshb_booking_due_amount');
573 delete_post_meta( $order_id, 'last_payment_type');
574 }
575
576 if($new_due < 1 || !$new_due){
577 update_post_meta($booking_id, 'payment_status', 'completed');
578 }
579
580 // update booking
581 update_post_meta($booking_id, 'eshb_booking_metaboxes', $eshb_booking_metaboxes);
582
583
584 // allow other plugins to hook after capture payment
585 do_action('eshb_after_capture_payment', $order_id);
586
587
588 // delete last payment type
589 delete_post_meta( $order_id, 'last_payment_type');
590 delete_post_meta($order_id, 'last_requested_payment_amount');
591 }
592 return $payment_id;
593
594 }
595
596 public static function eshb_calculate_time_diff ($start_date, $end_date, $start_time, $end_time) {
597 // Require times; if missing return 0 hours
598 if (empty($start_time) || empty($end_time)) {
599 return 0;
600 }
601
602 // Default end date to start date when empty
603 $start_date = !empty($start_date) ? $start_date : gmdate('Y-m-d');
604 $end_date = !empty($end_date) ? $end_date : $start_date;
605
606 // Normalize special case where end time can be provided as 24:00
607 $is_end_24 = ($end_time === '24:00' || $end_time === '24:00:00');
608 $normalized_end_time = $is_end_24 ? '00:00' : $end_time;
609
610 $start_dt = DateTime::createFromFormat('Y-m-d H:i:s', $start_date . ' ' . $start_time);
611 if(!$start_dt){
612 $start_dt = DateTime::createFromFormat('Y-m-d H:i', $start_date . ' ' . $start_time);
613 }
614
615 $end_dt = DateTime::createFromFormat('Y-m-d H:i:s', $end_date . ' ' . $normalized_end_time);
616 if(!$end_dt){
617 $end_dt = DateTime::createFromFormat('Y-m-d H:i', $end_date . ' ' . $normalized_end_time);
618 }
619
620 if(!$start_dt || !$end_dt){
621 return 0;
622 }
623
624 // If the textual times are equal (ignoring seconds) and not a 24:00 case, treat as 0 hours for same-day
625 if(!$is_end_24 && $start_dt->format('H:i') === $end_dt->format('H:i') && $end_date === $start_date){
626 return 0;
627 }
628
629 // If end is not after start, or explicitly 24:00, assume it crosses midnight to the next day
630 if($is_end_24 || $end_dt <= $start_dt){
631 $end_dt->modify('+1 day');
632 }
633
634 $duration_seconds = $end_dt->getTimestamp() - $start_dt->getTimestamp();
635 $hours_count = (int) ceil($duration_seconds / 3600);
636
637 return max(0, (int) $hours_count);
638 }
639
640 public static function get_available_times_by_date($accommodation_id, $date, $excludes = []) {
641 // Get booking settings (working hours)
642 $settings = get_option('eshb_single_day_settings', []);
643 $start_time = !empty($settings['start_time']) ? $settings['start_time'] : '10:00';
644 $end_time = !empty($settings['end_time']) ? $settings['end_time'] : '22:00';
645
646 $date_str = gmdate('Y-m-d', strtotime($date));
647
648 // Fetch all bookings
649 $bookings = get_posts([
650 'post_type' => 'eshb_booking',
651 'posts_per_page' => -1,
652 'post_status' => ['publish','completed','processing','deposit-payment','pending'],
653 'fields' => 'ids',
654 ]);
655
656 $booked_slots = [];
657 $slot_keys = []; // Track unique slot keys
658
659 foreach ($bookings as $booking_id) {
660 $meta = get_post_meta($booking_id, 'eshb_booking_metaboxes', true);
661 if (!is_array($meta)) continue;
662
663 // Skip if accommodation does not match
664 if ((int)($meta['booking_accomodation_id'] ?? 0) !== (int)$accommodation_id) continue;
665
666 // Skip if booking date does not match
667 if (($meta['booking_start_date'] ?? '') !== $date_str) continue;
668
669 $b_start = $meta['booking_start_time'] ?? '';
670 $b_end = $meta['booking_end_time'] ?? '';
671
672 // skip if invalid
673 if (!$b_start || !$b_end) continue;
674
675 $slot_key = $b_start.'-'.$b_end;
676
677 // Add unique slot only once
678 if (!isset($slot_keys[$slot_key])) {
679 $slot_keys[$slot_key] = true;
680
681 $booked_slots[] = [$b_start, $b_end];
682 }
683 }
684
685 // Build available slots based on working window
686 $available_slots = [];
687
688 // skip for excludes
689 if (count($excludes) > 0) {
690 array_push($available_slots, $excludes);
691 }
692
693 $window_start_ts = strtotime("$date_str $start_time");
694 $window_end_ts = strtotime("$date_str $end_time");
695
696 if (empty($booked_slots)) {
697 // If no bookings, full window is available
698 $available_slots[] = [$start_time, $end_time];
699 } else {
700 // Sort booked slots by start time
701 usort($booked_slots, function($a, $b) use ($date_str) {
702 return strtotime("$date_str {$a[0]}") <=> strtotime("$date_str {$b[0]}");
703 });
704
705 $pointer = $window_start_ts;
706
707 foreach ($booked_slots as $slot) {
708 $slot_start_ts = strtotime("$date_str {$slot[0]}");
709 $slot_end_ts = strtotime("$date_str {$slot[1]}");
710
711 // Gap before the booking = available slot
712 if ($slot_start_ts > $pointer) {
713 $available_slots[] = [gmdate('H:i', $pointer), $slot[0]];
714 }
715
716 // Move pointer to end of current booked slot
717 $pointer = max($pointer, $slot_end_ts);
718 }
719
720 // After last booking, remaining time is available
721 if ($pointer < $window_end_ts) {
722 $available_slots[] = [gmdate('H:i', $pointer), $end_time];
723 }
724 }
725
726 return [
727 'booked_slots' => $booked_slots,
728 'available_slots' => $available_slots,
729 'next_start_time' => $available_slots[0][0] ?? '',
730 'next_end_time' => $available_slots[0][1] ?? '',
731 ];
732 }
733
734 public static function format_to_wp_time($time_string){
735 $timestamp = strtotime( $time_string );
736 return date_i18n( get_option('time_format'), $timestamp );
737 }
738
739 public static function eshb_set_accomodation_localize($accomodation_id = null) {
740 $cart_url = site_url('/cart');
741 $checkout_url = site_url('/checkout');
742 if(class_exists('woocommerce')) {
743 $cart_url = wc_get_cart_url();
744 $checkout_url = wc_get_checkout_url();
745 }
746
747 $min_max_settings = [
748 'calendar_start_date_buffer' => 0,
749 'required_min_nights' => 1,
750 'required_max_nights' => 999,
751 ];
752
753 $eshb_settings = get_option('eshb_settings');
754 $search_capacity_settings = [];
755 if(!empty($eshb_settings)){
756 $search_capacity_settings = [
757 'min_adult_quantity' => !empty($eshb_settings['min-adult-capacity']) ? $eshb_settings['min-adult-capacity'] : 1,
758 'min_children_quantity' => !empty($eshb_settings['min-children-capacity']) ? $eshb_settings['min-children-capacity'] : 0,
759 'max_adult_quantity' => !empty($eshb_settings['max-adult-capacity']) ? $eshb_settings['max-adult-capacity'] : 1000,
760 'max_children_quantity' => !empty($eshb_settings['max-children-capacity']) ? $eshb_settings['max-children-capacity'] : 1000,
761 ];
762 }
763 $booking_capacity_settings = [];
764 if(!empty($eshb_settings)){
765 $booking_capacity_settings = [
766 'min_adult_quantity' => !empty($eshb_settings['booking-min-adult-capacity']) ? $eshb_settings['booking-min-adult-capacity'] : 1,
767 'min_children_quantity' => !empty($eshb_settings['booking-min-children-capacity']) ? $eshb_settings['booking-min-children-capacity'] : 0,
768 ];
769 }
770
771 $eshb_min_max_settings = apply_filters( 'eshb_min_max_global_settings_localize', $min_max_settings);
772 $calendar_start_date_buffer = !empty($eshb_min_max_settings['calendar_start_date_buffer']) ? $eshb_min_max_settings['calendar_start_date_buffer'] : 0;
773 $required_min_nights = !empty($eshb_min_max_settings['required_min_nights']) ? $eshb_min_max_settings['required_min_nights'] : 1;
774 $required_max_nights = !empty($eshb_min_max_settings['required_max_nights']) ? $eshb_min_max_settings['required_max_nights'] : 999;
775
776 $eshb_week_settings = apply_filters( 'eshb_week_settings', [] );
777 $string_check_in_day_error_msg = !empty($eshb_week_settings['string_check_in_day_error_msg']) ? $eshb_week_settings['string_check_in_day_error_msg'] : '';
778
779
780 // accomodation details metadata
781 $eshb_accomodation_metaboxes = false;
782 $is_it_single_day_booking_accomodation = false;
783
784 if(is_singular( 'eshb_accomodation' ) && ($accomodation_id == null || empty($accomodation_id))){
785 $accomodation_id = get_the_ID();
786 }
787
788
789 $eshb_booking = new ESHB_Booking();
790 $start_date = '';
791 $end_date = '';
792
793 if (isset($_GET['nonce']) && wp_verify_nonce( sanitize_text_field(wp_unslash($_GET['nonce'])), ESHB_Helper::generate_secure_nonce_action('eshb_global_nonce_action'))) {
794 $start_date = !empty($_POST['start_date']) ? sanitize_text_field( wp_unslash($_POST['start_date']) ) : '';
795 $end_date = !empty($_POST['end_date']) ? sanitize_text_field( wp_unslash($_POST['end_date']) ) : '';
796 }
797
798 if($accomodation_id) {
799 $eshb_accomodation_metaboxes = get_post_meta($accomodation_id, 'eshb_accomodation_metaboxes', true);
800 $available_rooms = $eshb_booking->get_available_room_count_by_date_range($accomodation_id, $start_date, $end_date);
801 $available_rooms = $available_rooms > 0 ? $available_rooms : 0;
802 $eshb_accomodation_metaboxes['available_rooms'] = $available_rooms;
803
804
805 $is_global_source_for_min_max = !empty($eshb_accomodation_metaboxes['is_global_source_for_min_max']) ? true : false;
806 if($is_global_source_for_min_max != true) {
807 $required_min_nights = !empty($eshb_accomodation_metaboxes['required_min_nights']) ? $eshb_accomodation_metaboxes['required_min_nights'] : 1;
808 $required_max_nights = !empty($eshb_accomodation_metaboxes['required_max_nights']) ? $eshb_accomodation_metaboxes['required_max_nights'] : 999;
809 }
810 }
811
812 $eshb_translations = [
813 'maximumCapacity' => __('Maximum Capacity', 'easy-hotel'),
814 'availableCapacity' => __('Available Capacity', 'easy-hotel'),
815 'availableRoom' => __('Available Room', 'easy-hotel'),
816 'maximumAdultAndChildrenCapacity' => __('Maximum Adult and Children Capacity', 'easy-hotel'),
817 'maximumTimeSlot' => __('Allowed max time for this slot is', 'easy-hotel'),
818 'minimumTimeSlot' => __('Allowed min time for this slot is', 'easy-hotel'),
819 'minNightsErrorMsg' => __('Ops! This Reservation has been failed. Requried Minimum', 'easy-hotel'),
820 'maxNightsErrorMsg' => __('Ops! This Reservation has been failed. Requried Maximum', 'easy-hotel'),
821 'minNightsErrorMsgAvCal' => __('Requried Minimum Nights:', 'easy-hotel'),
822 'maxNightsErrorMsgAvCal' => __('Requried Maximum Nights:', 'easy-hotel'),
823 ];
824
825 $eshb_translations = apply_filters( 'eshb_booking_action_messages', $eshb_translations, [$accomodation_id, $eshb_settings] );
826 $show_count = apply_filters( 'eshb_booking_capacity_count_show', true, [$accomodation_id, $eshb_settings] );
827
828 $nonce_action = ESHB_Helper::generate_secure_nonce_action('eshb_global_nonce_action');
829 wp_localize_script(
830 'eshb-public-script',
831 'eshb_ajax',
832 [
833 'root' => esc_url(rest_url()),
834 'ajaxurl' => admin_url( 'admin-ajax.php' ),
835 'adminURL' => admin_url(),
836 'wooCartUrl' => $cart_url,
837 'wooCheckoutUrl' => $checkout_url,
838 'direct_booking' => !empty($eshb_settings['direct-booking']),
839 'is_admin' => is_admin(),
840 'nonce' => wp_create_nonce($nonce_action),
841 'eshb_add_to_cart_reservation_nonce' => wp_create_nonce('eshb_eshb_add_to_cart_reservation_nonce'),
842 'reservation_request_nonce' => wp_create_nonce('eshb_reservation_request_nonce'),
843 'version' => ESHB_VERSION,
844 'pluginURL' => ESHB_DIR_URL,
845 'dateFormat' => get_option( 'date_format' ),
846 'requiredMinNights' => $required_min_nights,
847 'requiredMaxNights' => $required_max_nights,
848 'calendar_start_date_buffer' => $calendar_start_date_buffer,
849 'checkInDayErrorMsg' => $string_check_in_day_error_msg,
850 'currentAccomodationMeta' => $eshb_accomodation_metaboxes,
851 'search_capacities' => $search_capacity_settings,
852 'booking_capacities' => $booking_capacity_settings,
853 'booking_capacity_count_show' => $show_count,
854 'translations' => $eshb_translations,
855 'cart_blocking_enabled' => ! empty( $eshb_settings['cart-blocking-switcher'] ),
856 'cart_blocking_time' => ! empty( $eshb_settings['cart-blocking-time'] ) ? (int) $eshb_settings['cart-blocking-time'] : 5,
857 'cart_blocking_color' => ! empty( $eshb_settings['cart-blocking-color'] ) ? esc_attr( $eshb_settings['cart-blocking-color'] ) : '#720eec',
858 'cart_blocking_notice_msg' => ! empty( $eshb_settings['cart-blocking-notice-msg'] ) ? esc_html( $eshb_settings['cart-blocking-notice-msg'] ) : esc_html__( 'Your reservation is held for', 'easy-hotel' ),
859 ]
860 );
861 }
862
863 public static function get_eshb_min_stay_night_by_session($accomodation_id, $start_date, $end_date) {
864 $metaboxes = get_post_meta($accomodation_id, 'eshb_accomodation_metaboxes', true);
865 $min_stay_night = 0;
866
867
868 // Get all sessions
869 $qargs = array(
870 'post_type' => 'eshb_session',
871 'post_status' => 'publish',
872 'posts_per_page' => -1,
873 );
874 $query = new WP_Query($qargs);
875
876 $sessions = [];
877
878 if ($query->have_posts()) {
879 while ($query->have_posts()) {
880 $query->the_post();
881 $post_id = get_the_ID();
882 $metaboxes = maybe_unserialize(get_post_meta($post_id, 'eshb_session_metaboxes', true));
883
884 if (empty($metaboxes['start_date']) || empty($metaboxes['end_date'])) continue;
885
886 $sessions[] = [
887 'id' => $post_id,
888 'start_date' => $metaboxes['start_date'],
889 'end_date' => $metaboxes['end_date'],
890 'min_nights' => $metaboxes['min_nights'] ?? 0,
891 'accomodation_ids' => $metaboxes['accomodation_ids'] ?? [],
892 'days' => $metaboxes['days'] ?? [],
893 ];
894 }
895 wp_reset_postdata();
896 }
897
898 // Create date range (exclude checkout date if multi-day)
899 $period = new DatePeriod(
900 new DateTime($start_date),
901 new DateInterval('P1D'),
902 (new DateTime($start_date) == new DateTime($end_date))
903 ? (new DateTime($end_date))->modify('+1 day') // same-day booking
904 : new DateTime($end_date) // exclude checkout
905 );
906
907 $total_nights = iterator_count($period);
908 $per_day_prices = [];
909
910
911 foreach ($period as $day) {
912 $current_date = $day->format('Y-m-d');
913 $current_day_name = strtolower($day->format('l'));
914
915 foreach ($sessions as $session) {
916
917
918 if(empty($session['accomodation_ids'])){
919 continue;
920 }
921
922 if (!empty($session['accomodation_ids']) && !in_array($accomodation_id, $session['accomodation_ids'])) {
923 continue;
924 }
925
926 $session_days = !empty($session['days']) ? $session['days'] : ['saturday', 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday'];
927 if(in_array('all', $session_days)){
928 $session_days = ['saturday', 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday'];
929 }
930
931 if ($current_date >= $session['start_date'] && $current_date <= $session['end_date'] && in_array($current_day_name, $session_days)) {
932
933 // 1. Get min night
934 $min_stay_night = $session['min_nights'];
935 break;
936 }
937 }
938 }
939 return $min_stay_night;
940 }
941 }