PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.22
Timetics – Appointment Booking Calendar & Scheduling v1.0.22
1.0.61 1.0.60 1.0.59 1.0.58 1.0.57 1.0.56 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.2 1.0.20 1.0.21 1.0.22 1.0.23 1.0.24 All 62 releases
timetics / core / appointments / appointment.php

appointment.php in Timetics – Appointment Booking Calendar & Scheduling 1.0.22, at core/appointments/appointment.php

890 lines 21.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Apointment
4 *
5 * @since 1.0.0
6 *
7 * @package Timetics
8 */
9 namespace Timetics\Core\Appointments;
10
11 use DateTime;
12 use Timetics\Base\PostModel;
13 use Timetics\Core\Bookings\Booking;
14 use Timetics\Core\Bookings\Booking_Entry;
15 use Timetics\Core\Staffs\Staff;
16 use WP_Query;
17
18 /**
19 * Class Appointments.
20 *
21 * @since 1.0.0
22 */
23 class Appointment extends PostModel {
24 /**
25 * Store post type
26 *
27 * @since 1.0.0
28 *
29 * @var string $post_type
30 */
31 public $post_type = 'timetics-appointment';
32
33 /**
34 * Store timetics custom taxonomy
35 *
36 * @since 1.0.0
37 *
38 * @var string $taxonomy
39 */
40 protected $taxonomy = 'timetics-meeting-category';
41
42 /**
43 * Store id
44 *
45 * @since 1.0.0
46 *
47 * @var int $id
48 */
49 protected $id;
50
51 /**
52 * Store post metadata
53 *
54 * @since 1.0.0
55 *
56 * @var array $data
57 */
58 public $data = [
59 'name' => '',
60 'type' => '',
61 'description' => '',
62 'staff' => '',
63 'locations' => '',
64 'duration' => '',
65 'schedule' => '',
66 'blocked_schedule' => '',
67 'price' => '',
68 'buffer_time' => '',
69 'timezone' => '',
70 'availability' => '',
71 'visibility' => '',
72 'capacity' => '',
73 'seat_plan' => '',
74 'duplicate' => 0,
75 'custom_fields' => [],
76 'wc_product_id' => 0,
77 'seat_plan_settings' => '',
78 'fleunt_crm_webhook' => '',
79 'fluent_hook_overwrite' => '',
80 'pabbly_hook_overwrite' => '',
81 'zapier_hook_overwrite' => '',
82 'pabbly_webook' => '',
83 'zapier_webook' => '',
84 'min_notice_time' => '',
85 'notifications' => [
86 'booking_created_email_form' => '',
87 'booking_created_email_title' => '',
88 'booking_created_email_body' => '',
89 'booking_canceled_email_form' => '',
90 'booking_canceled_email_title' => '',
91 'booking_canceled_email_body' => '',
92 'booking_reschedule_email_form' => '',
93 'booking_reschedule_email_title' => '',
94 'booking_reschedule_email_body' => '',
95 'booking_reminder_email_form' => '',
96 'booking_reminder_email_title' => '',
97 'booking_reminder_email_body' => '',
98 'booking_reminder_time' => 30,
99 ],
100 'permalink' => '',
101 'recurring' => false,
102 'recurring_limit' => 0,
103 'recurring_interval' => 1,
104 'recurring_interval_name' => 'day',
105 'guest_enabled' => false,
106 'guest_limit' => 1,
107 ];
108
109 /**
110 * Store meta key prefix
111 *
112 * @since 1.0.0
113 *
114 * @var string $prefix
115 */
116 public $prefix = '_tt_apointment_';
117
118 /**
119 * Appointments Constructor
120 *
121 * @since 1.0.0
122 *
123 * @param int $appointment Optional. Default 0.
124 * @return void
125 */
126 public function __construct( $appointment = 0 ) {
127 if ( $appointment instanceof self ) {
128 $this->set_id( $appointment->get_id() );
129 } elseif ( ! empty( $appointment->ID ) ) {
130 $this->set_id( $appointment->ID );
131 } elseif ( is_numeric( $appointment ) && $appointment > 0 ) {
132 $this->set_id( $appointment );
133 }
134 }
135
136 /**
137 * Get appoint id.
138 *
139 * @since 1.0.0
140 *
141 * @return int
142 */
143 public function get_id() {
144 return $this->id;
145 }
146
147 /**
148 * Get appointment slug
149 *
150 * @return mixed
151 */
152 public function get_slug() {
153 $post = get_post( $this->get_id() );
154
155 if ( $post ) {
156 return $post->post_name;
157 }
158
159 return false;
160 }
161
162 /**
163 * Get woocommerce product id
164 *
165 * @return integer
166 */
167 public function get_wc_product_id() {
168 return $this->get_prop( 'wc_product_id' );
169 }
170
171 /**
172 * Get name.
173 *
174 * @since 1.0.0
175 *
176 * @return string
177 */
178 public function get_name() {
179 return $this->get_prop( 'name' );
180 }
181
182 /**
183 * Get coach
184 *
185 * @since 1.0.0
186 *
187 * @return int
188 */
189 public function get_staff() {
190 $staff_ids = $this->get_prop( 'staff' );
191 $staffs = [];
192
193 if ( $staff_ids ) {
194 foreach ( $staff_ids as $staff_id ) {
195 $staff = new Staff( $staff_id );
196
197 if ( ! $staff->is_staff() ) {
198 continue;
199 }
200
201 $staffs[] = $staff->get_data();
202 }
203 }
204 return $staffs;
205 }
206
207 /**
208 * Get staff ids
209 *
210 * @return array
211 */
212 public function get_staff_ids() {
213 return $this->get_prop( 'staff' );
214 }
215
216 /**
217 * Get duplicate number
218 *
219 * @return integer
220 */
221 public function get_duplicate_nuber() {
222 return $this->get_prop( 'duplicate' );
223 }
224
225 /**
226 * Get capacity
227 *
228 * @return integer
229 */
230 public function get_capacity() {
231 return $this->get_prop( 'capacity' );
232 }
233
234 /**
235 * Get location
236 *
237 * @since 1.0.0
238 *
239 * @return string
240 */
241 public function get_locations() {
242 return $this->get_prop( 'locations' );
243 }
244
245 /**
246 * Get Permalink
247 *
248 * @since 1.0.0
249 *
250 * @return string
251 */
252 public function get_appointment_permalink() {
253 return get_post_permalink( $this->id );
254 }
255
256 /**
257 * Get duration
258 *
259 * @since 1.0.0
260 *
261 * @return string
262 */
263 public function get_duration() {
264 return $this->get_prop( 'duration' );
265 }
266
267 /**
268 * Get interval for the duration
269 *
270 * @return integer
271 */
272 public function get_interval() {
273 $duration = $this->get_duration();
274 if ( $duration ) {
275 $duration = explode( ' ', $duration );
276 $duration = 'hr' === $duration[1] ? intval( $duration[0] ) * 60 * 60 : intval( $duration[0] ) * 60;
277 }
278
279 return $duration;
280 }
281
282 /**
283 * Get schedule
284 *
285 * @since 1.0.0
286 *
287 * @return array
288 */
289 public function get_schedule() {
290 return $this->get_prop( 'schedule' );
291 }
292
293 /**
294 * Get blocked schedule
295 *
296 * @since 1.0.0
297 *
298 * @return array
299 */
300 public function get_blocked_schedule() {
301 return $this->get_prop( 'blocked_schedule' );
302 }
303
304 /**
305 * Get price
306 *
307 * @since 1.0.0
308 *
309 * @return string
310 */
311 public function get_price() {
312 return $this->get_prop( 'price' );
313 }
314
315 /**
316 * Get type
317 *
318 * @since 1.0.0
319 *
320 * @return string
321 */
322 public function get_type() {
323 return $this->get_prop( 'type' );
324 }
325
326 /**
327 * Get description
328 *
329 * @since 1.0.0
330 *
331 * @return string
332 */
333 public function get_description() {
334 return $this->get_prop( 'description' );
335 }
336
337 public function get_buffer_time() {
338 return $this->get_prop( 'buffer_time' );
339 }
340
341 public function get_timezone() {
342 return $this->get_prop( 'timezone' );
343 }
344
345 public function get_availability() {
346 return $this->get_prop( 'availability' );
347 }
348
349 public function get_visibility() {
350 return $this->get_prop( 'visibility' );
351 }
352 public function get_fleunt_crm_webhook() {
353 return $this->get_prop( 'fleunt_crm_webhook' );
354 }
355 public function get_fluent_hook_overwrite() {
356 return $this->get_prop( 'fluent_hook_overwrite' );
357 }
358 public function get_pabbly_webook() {
359 return $this->get_prop( 'pabbly_webook' );
360 }
361 public function get_pabbly_hook_overwrite() {
362 return $this->get_prop( 'pabbly_hook_overwrite' );
363 }
364 public function get_zapier_webook() {
365 return $this->get_prop( 'zapier_webook' );
366 }
367 public function get_zapier_hook_overwrite() {
368 return $this->get_prop( 'zapier_hook_overwrite' );
369 }
370 public function get_min_notice_time() {
371 return $this->get_prop( 'min_notice_time' );
372 }
373 public function get_guest_enabled() {
374 return $this->get_prop( 'guest_enabled' );
375 }
376 public function get_guest_limit() {
377 return $this->get_prop( 'guest_limit' );
378 }
379
380 /**
381 * Get Custom fields
382 *
383 * @return array
384 */
385 public function get_custom_fields() {
386 return $this->get_prop( 'custom_fields' );
387 }
388
389 /**
390 * Get notifications
391 *
392 * @return array
393 */
394 public function get_notifications() {
395 return $this->get_prop( 'notifications' );
396 }
397
398 /**
399 * Get appointment categories
400 *
401 * @since 1.0.0
402 *
403 * @return array
404 */
405 public function get_categories() {
406 $categories = wp_get_post_terms( $this->id, $this->taxonomy );
407
408 if ( is_wp_error( $categories ) ) {
409 return [];
410 }
411
412 return $categories;
413 }
414
415 /**
416 * Get category ids
417 *
418 * @return array
419 */
420 public function get_category_ids() {
421 $categories = $this->get_categories();
422
423 $ids = [];
424
425 foreach ( $categories as $category ) {
426 $ids[] = $category->term_id;
427 }
428
429 return $ids;
430 }
431
432 /**
433 * Get post thumbnail.
434 *
435 * @param string $size Image size
436 *
437 * @return string
438 */
439 public function get_image( $size = 'post-thumbnail' ) {
440 return get_the_post_thumbnail_url( $size );
441 }
442
443 /**
444 * Get permalink
445 *
446 * @return string
447 */
448 public function get_link() {
449 return get_post_permalink( $this->id );
450 }
451
452 /**
453 * Get Seat plan for a meeting
454 *
455 * @return array
456 */
457 public function get_seats() {
458 return $this->get_prop( 'seat_plan' );
459 }
460
461 /**
462 * Get appointment author
463 *
464 * @return integer
465 */
466 public function get_author() {
467 $post = get_post( $this->id );
468
469 return $post->post_author;
470 }
471
472 /**
473 * Get seat plan settings
474 *
475 * @return array
476 */
477 public function get_seat_settings() {
478 return $this->get_prop( 'seat_plan_settings' );
479 }
480
481 // Recurring options.
482
483 /**
484 * Check it is recurring or not
485 *
486 * @return bool
487 */
488 public function is_recurring() {
489 return boolval( $this->get_prop( 'recurring' ) );
490 }
491
492 /**
493 * Get recurring limit
494 *
495 * @return integer
496 */
497 public function get_recurring_limit() {
498 return intval( $this->get_prop( 'recurring_limit' ) );
499 }
500
501 /**
502 * Get recurring interval
503 *
504 * @return integer
505 */
506 public function get_recurring_interval() {
507 return intval( $this->get_prop( 'recurring_interval' ) );
508 }
509
510 /**
511 * Get recurring interval name
512 *
513 * @return string
514 */
515 public function get_recurring_interval_name() {
516 return $this->get_prop( 'recurring_interval_name' );
517 }
518
519 /**
520 * Get appointment data
521 *
522 * @param string $prop
523 *
524 * @return mixed
525 */
526 public function get_prop( $prop = '' ) {
527 return $this->get_metadata( $prop );
528 }
529
530 /**
531 * Get metadata
532 *
533 * @since 1.0.0
534 *
535 * @param string $prop Optional. Default empty string.
536 *
537 * @return mixed
538 */
539 private function get_metadata( $prop = '' ) {
540 $meta_key = $this->prefix . $prop;
541 return get_post_meta( $this->id, $meta_key, true );
542 }
543
544 /**
545 * Set appointment id
546 *
547 * @since 1.0.0
548 *
549 * @param int $id
550 *
551 * @return void
552 */
553 public function set_id( $id ) {
554 $this->id = $id;
555 }
556
557 /**
558 * Set props
559 *
560 * @param array $data Metadata array. Default empty array.
561 *
562 * @return void
563 */
564 public function set_props( $data = [] ) {
565 $this->data = $data;
566 }
567
568 /**
569 * Save appointment
570 *
571 * @since 1.0.0
572 *
573 * @return void
574 */
575 public function save() {
576
577 $args = [
578 'post_title' => $this->data['name'],
579 'post_type' => $this->post_type,
580 'post_status' => 'publish',
581 'post_author' => get_current_user_id(),
582 ];
583
584 if ( ! empty( $this->id ) ) {
585 $args['ID'] = $this->id;
586 }
587
588 // Insert or Update appointment.
589 $appoint_id = wp_insert_post( $args );
590
591 if ( ! is_wp_error( $appoint_id ) ) {
592 $this->set_id( $appoint_id );
593 $this->save_metadata();
594 }
595 }
596
597 /**
598 * Update appointment meta data.
599 *
600 * @since 1.0.0
601 *
602 * @return void
603 */
604 private function save_metadata() {
605 foreach ( $this->data as $key => $value ) {
606 // Prepare meta key.
607 $meta_key = $this->prefix . $key;
608
609 // Update appointment meta data.
610 update_post_meta( $this->id, $meta_key, $value );
611 }
612 }
613
614 /**
615 * Update meeting data
616 *
617 * @param array $args meeting data
618 *
619 * @return bool
620 */
621 public function update( $args = [] ) {
622
623 foreach ( $args as $key => $value ) {
624 if ( ! array_key_exists( $key, $this->data ) ) {
625 continue;
626 }
627
628 // Prepare meta key.
629 $meta_key = $this->prefix . $key;
630
631 // Update appointment meta data.
632 update_post_meta( $this->id, $meta_key, $value );
633 }
634 }
635
636 /**
637 * Delete appointment
638 *
639 * @return bool | WP_Error
640 */
641 public function delete() {
642 return wp_delete_post( $this->id, true );
643 }
644
645 /**
646 * Check the appoint is valid or not
647 *
648 * @return bool
649 */
650 public function is_appointment() {
651 $post = get_post( $this->id );
652
653 if ( $post && $this->post_type === $post->post_type ) {
654 return true;
655 }
656
657 return false;
658 }
659
660 /**
661 * Get all appointments
662 *
663 * @param array $args Appointments args. Default empty array.
664 *
665 * @return array
666 */
667 public static function all( $args = [] ) {
668 $defaults = [
669 'post_type' => ( new self )->post_type,
670 'post_status' => 'publish',
671 'posts_per_page' => 20,
672 'paged' => 1,
673 'orderby' => 'ID',
674 'order' => 'DESC',
675 ];
676
677 $args = wp_parse_args( $args, $defaults );
678
679 if ( ! empty( $args['staff'] ) ) {
680 $args['meta_query'][] = [
681 'key' => '_tt_apointment_staff',
682 'value' => $args['staff'],
683 'compare' => 'LIKE',
684 ];
685 }
686
687 if ( ! empty( $args['visibility'] ) ) {
688 $args['meta_query'][] = [
689 'key' => '_tt_apointment_visibility',
690 'value' => $args['visibility'],
691 'compare' => 'LIKE',
692 ];
693 }
694
695 if ( ! empty( $args['type'] ) ) {
696 $args['meta_query'][] = [
697 'key' => '_tt_apointment_type',
698 'value' => $args['type'],
699 'compare' => 'LIKE',
700 ];
701 }
702
703 if ( ! empty( $args['category'] ) ) {
704 $args['tax_query'][] = [
705 'taxonomy' => 'timetics-meeting-category',
706 'terms' => $args['category'],
707 'include_children' => false,
708 ];
709 }
710
711 $post = new WP_Query( $args );
712
713 return [
714 'total' => $post->found_posts,
715 'items' => $post->posts,
716 ];
717 }
718
719 /**
720 * Duplicate appointment
721 *
722 * @since 1.0.0
723 *
724 * @return void
725 */
726 public function duplicate() {
727 $this->set_props( [
728 'name' => $this->get_name(),
729 'description' => $this->get_description(),
730 'type' => $this->get_type(),
731 'locations' => $this->get_locations(),
732 'staff' => $this->get_staff_ids(),
733 'duplicate' => intval( $this->get_duplicate_nuber() ) + 1,
734 'duration' => $this->get_duration(),
735 'price' => $this->get_price(),
736 'capacity' => $this->get_capacity(),
737 'schedule' => $this->get_schedule(),
738 'categories' => $this->get_categories(),
739 'timezone' => $this->get_timezone(),
740 'availability' => $this->get_availability(),
741 'visibility' => $this->get_visibility(),
742 'buffer_time' => $this->get_buffer_time(),
743 'notifications' => $this->get_notifications(),
744 'fluent_hook_overwrite' => $this->get_fluent_hook_overwrite(),
745 'fleunt_crm_webhook' => $this->get_fleunt_crm_webhook(),
746 'pabbly_hook_overwrite' => $this->get_pabbly_hook_overwrite(),
747 'pabbly_webook' => $this->get_pabbly_webook(),
748 'zapier_webook' => $this->get_zapier_webook(),
749 'min_notice_time' => $this->get_min_notice_time(),
750 'zapier_hook_overwrite' => $this->get_zapier_hook_overwrite(),
751 'guest_enabled' => $this->get_guest_enabled(),
752 'guest_limit' => $this->get_guest_limit(),
753 ] );
754
755 $this->set_id( 0 );
756 $this->save();
757 }
758
759 /**
760 * Prepare meeting schedule
761 *
762 * @param string $start
763 * @param string $end
764 * @param integer $staff_id
765 *
766 * @return array
767 */
768 public function prepare_schedule( $start, $end, $staff_id, $timze_zone = '' ) {
769 $start = new \DateTime( $start );
770 $end = new \DateTime( $end );
771 $schedule = [];
772
773 // Prepare schedule for the current meeting
774 for ( $date = $start; $date <= $end; $date->modify( '+1 day' ) ) {
775 $schedule[] = $this->get_schedule_by_date( $date->format( 'Y-m-d' ), $staff_id, $timze_zone );
776 }
777
778 return $schedule;
779 }
780
781 /**
782 * Get meeting schedule by date
783 *
784 * @param string $date [$date description]
785 * @param integer $staff_id [$staff_id description]
786 *
787 * @return array
788 */
789 private function get_schedule_by_date( $date, $staff_id, $timze_zone = '' ) {
790 $day_name = ucfirst( date( 'D', strtotime( $date ) ) );
791 $schedule = $this->get_schedule();
792 $interval = $this->get_interval();
793 $tartget_schedule = [];
794
795 $slots = [];
796
797 if ( ! empty( $schedule[$staff_id] ) ) {
798 $tartget_schedule = $schedule[$staff_id][$day_name];
799 }
800
801 foreach ( $tartget_schedule as $day ) {
802 $start = strtotime( $day['start'] );
803 $end = strtotime( $day['end'] );
804
805 for ( $time = $start; $time <= $end; $time += $interval ) {
806 $booked_entry = $this->get_booking_entries( $date, $time, $staff_id );
807 $status = 'available';
808 $capacity = $this->get_capacity();
809 $booked = 0;
810
811 if ( $booked_entry ) {
812 if ( $booked_entry->get_booked() >= $capacity ) {
813 $status = 'unavailable';
814 }
815
816 $booked = $booked_entry->get_booked();
817 }
818
819 $datetime = $this->convert_timezone( $time, $timze_zone );
820
821 $slot = [
822 'status' => $status,
823 'start_time' => $datetime->format( 'g:ia' ),
824 'booked' => $booked,
825 ];
826
827 $slot = apply_filters( 'timetics_booking_slot', $slot, $this, $booked_entry );
828
829 $slots[] = $slot;
830 }
831 }
832
833 return [
834 'date' => $date,
835 'status' => empty( $slots ) ? 'unavailable' : 'available',
836 'slots' => $slots,
837 ];
838 }
839
840 /**
841 * Get booked entry status
842 *
843 * @param string $date
844 * @param string $time
845 * @param interger $staff_id
846 *
847 * @return bool
848 */
849 private function get_booking_entries( $date, $time, $staff_id ) {
850 $time = gmdate( 'h:i a', $time );
851 $booking_entries = new Booking_Entry();
852 $entries = $booking_entries->find( [
853 'meeting_id' => $this->id,
854 'staff_id' => $staff_id,
855 'date' => $this->convert_timezone( $date, $this->get_timezone())->format('Y-m-d'),
856 ] );
857
858 foreach ( $entries as $entry ) {
859 $booking = new Booking( $entry->get_booking_id() );
860
861 if ( $entry->get_start() == $time ) {
862 return $entry;
863 }
864 }
865
866 return false;
867 }
868
869 /**
870 * Convert timestamp in target timezone
871 *
872 * @param integer $timestamp
873 * @param string $timze_zone
874 *
875 * @return DateTime
876 */
877 public function convert_timezone( $date, $timze_zone ) {
878 $date = is_int( $date ) ? gmdate( 'Y-m-d g:ia', $date ) : $date;
879
880 // Create a DateTime object with the provided timestamp and time zone
881 $datetime = new \DateTime( $date, new \DateTimeZone( $this->get_timezone() ) );
882
883 // Convert the time zone
884 $datetime->setTimezone( new \DateTimeZone( $timze_zone ) );
885
886 // Get the converted timestamp
887 return $datetime;
888 }
889 }
890