PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.13
Timetics – Appointment Booking Calendar & Scheduling v1.0.13
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.13, at core/appointments/appointment.php

891 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
353 public function get_fleunt_crm_webhook() {
354 return $this->get_prop( 'fleunt_crm_webhook' );
355 }
356 public function get_fluent_hook_overwrite() {
357 return $this->get_prop( 'fluent_hook_overwrite' );
358 }
359 public function get_pabbly_webook() {
360 return $this->get_prop( 'pabbly_webook' );
361 }
362 public function get_pabbly_hook_overwrite() {
363 return $this->get_prop( 'pabbly_hook_overwrite' );
364 }
365 public function get_zapier_webook() {
366 return $this->get_prop( 'zapier_webook' );
367 }
368 public function get_zapier_hook_overwrite() {
369 return $this->get_prop( 'zapier_hook_overwrite' );
370 }
371 public function get_min_notice_time() {
372 return $this->get_prop( 'min_notice_time' );
373 }
374 public function get_guest_enabled() {
375 return $this->get_prop( 'guest_enabled' );
376 }
377 public function get_guest_limit() {
378 return $this->get_prop( 'guest_limit' );
379 }
380
381 /**
382 * Get Custom fields
383 *
384 * @return array
385 */
386 public function get_custom_fields() {
387 return $this->get_prop( 'custom_fields' );
388 }
389
390 /**
391 * Get notifications
392 *
393 * @return array
394 */
395 public function get_notifications() {
396 return $this->get_prop( 'notifications' );
397 }
398
399 /**
400 * Get appointment categories
401 *
402 * @since 1.0.0
403 *
404 * @return array
405 */
406 public function get_categories() {
407 $categories = wp_get_post_terms( $this->id, $this->taxonomy );
408
409 if ( is_wp_error( $categories ) ) {
410 return [];
411 }
412
413 return $categories;
414 }
415
416 /**
417 * Get category ids
418 *
419 * @return array
420 */
421 public function get_category_ids() {
422 $categories = $this->get_categories();
423
424 $ids = [];
425
426 foreach ( $categories as $category ) {
427 $ids[] = $category->term_id;
428 }
429
430 return $ids;
431 }
432
433 /**
434 * Get post thumbnail.
435 *
436 * @param string $size Image size
437 *
438 * @return string
439 */
440 public function get_image( $size = 'post-thumbnail' ) {
441 return get_the_post_thumbnail_url( $size );
442 }
443
444 /**
445 * Get permalink
446 *
447 * @return string
448 */
449 public function get_link() {
450 return get_post_permalink( $this->id );
451 }
452
453 /**
454 * Get Seat plan for a meeting
455 *
456 * @return array
457 */
458 public function get_seats() {
459 return $this->get_prop( 'seat_plan' );
460 }
461
462 /**
463 * Get appointment author
464 *
465 * @return integer
466 */
467 public function get_author() {
468 $post = get_post( $this->id );
469
470 return $post->post_author;
471 }
472
473 /**
474 * Get seat plan settings
475 *
476 * @return array
477 */
478 public function get_seat_settings() {
479 return $this->get_prop( 'seat_plan_settings' );
480 }
481
482 // Recurring options.
483
484 /**
485 * Check it is recurring or not
486 *
487 * @return bool
488 */
489 public function is_recurring() {
490 return boolval( $this->get_prop( 'recurring' ) );
491 }
492
493 /**
494 * Get recurring limit
495 *
496 * @return integer
497 */
498 public function get_recurring_limit() {
499 return intval( $this->get_prop( 'recurring_limit' ) );
500 }
501
502 /**
503 * Get recurring interval
504 *
505 * @return integer
506 */
507 public function get_recurring_interval() {
508 return intval( $this->get_prop( 'recurring_interval' ) );
509 }
510
511 /**
512 * Get recurring interval name
513 *
514 * @return string
515 */
516 public function get_recurring_interval_name() {
517 return $this->get_prop( 'recurring_interval_name' );
518 }
519
520 /**
521 * Get appointment data
522 *
523 * @param string $prop
524 *
525 * @return mixed
526 */
527 public function get_prop( $prop = '' ) {
528 return $this->get_metadata( $prop );
529 }
530
531 /**
532 * Get metadata
533 *
534 * @since 1.0.0
535 *
536 * @param string $prop Optional. Default empty string.
537 *
538 * @return mixed
539 */
540 private function get_metadata( $prop = '' ) {
541 $meta_key = $this->prefix . $prop;
542 return get_post_meta( $this->id, $meta_key, true );
543 }
544
545 /**
546 * Set appointment id
547 *
548 * @since 1.0.0
549 *
550 * @param int $id
551 *
552 * @return void
553 */
554 public function set_id( $id ) {
555 $this->id = $id;
556 }
557
558 /**
559 * Set props
560 *
561 * @param array $data Metadata array. Default empty array.
562 *
563 * @return void
564 */
565 public function set_props( $data = [] ) {
566 $this->data = $data;
567 }
568
569 /**
570 * Save appointment
571 *
572 * @since 1.0.0
573 *
574 * @return void
575 */
576 public function save() {
577
578 $args = [
579 'post_title' => $this->data['name'],
580 'post_type' => $this->post_type,
581 'post_status' => 'publish',
582 'post_author' => get_current_user_id(),
583 ];
584
585 if ( ! empty( $this->id ) ) {
586 $args['ID'] = $this->id;
587 }
588
589 // Insert or Update appointment.
590 $appoint_id = wp_insert_post( $args );
591
592 if ( ! is_wp_error( $appoint_id ) ) {
593 $this->set_id( $appoint_id );
594 $this->save_metadata();
595 }
596 }
597
598 /**
599 * Update appointment meta data.
600 *
601 * @since 1.0.0
602 *
603 * @return void
604 */
605 private function save_metadata() {
606 foreach ( $this->data as $key => $value ) {
607 // Prepare meta key.
608 $meta_key = $this->prefix . $key;
609
610 // Update appointment meta data.
611 update_post_meta( $this->id, $meta_key, $value );
612 }
613 }
614
615 /**
616 * Update meeting data
617 *
618 * @param array $args meeting data
619 *
620 * @return bool
621 */
622 public function update( $args = [] ) {
623
624 foreach ( $args as $key => $value ) {
625 if ( ! array_key_exists( $key, $this->data ) ) {
626 continue;
627 }
628
629 // Prepare meta key.
630 $meta_key = $this->prefix . $key;
631
632 // Update appointment meta data.
633 update_post_meta( $this->id, $meta_key, $value );
634 }
635 }
636
637 /**
638 * Delete appointment
639 *
640 * @return bool | WP_Error
641 */
642 public function delete() {
643 return wp_delete_post( $this->id, true );
644 }
645
646 /**
647 * Check the appoint is valid or not
648 *
649 * @return bool
650 */
651 public function is_appointment() {
652 $post = get_post( $this->id );
653
654 if ( $post && $this->post_type === $post->post_type ) {
655 return true;
656 }
657
658 return false;
659 }
660
661 /**
662 * Get all appointments
663 *
664 * @param array $args Appointments args. Default empty array.
665 *
666 * @return array
667 */
668 public static function all( $args = [] ) {
669 $defaults = [
670 'post_type' => ( new self )->post_type,
671 'post_status' => 'publish',
672 'posts_per_page' => 20,
673 'paged' => 1,
674 'orderby' => 'ID',
675 'order' => 'DESC',
676 ];
677
678 $args = wp_parse_args( $args, $defaults );
679
680 if ( ! empty( $args['staff'] ) ) {
681 $args['meta_query'][] = [
682 'key' => '_tt_apointment_staff',
683 'value' => $args['staff'],
684 'compare' => 'LIKE',
685 ];
686 }
687
688 if ( ! empty( $args['visibility'] ) ) {
689 $args['meta_query'][] = [
690 'key' => '_tt_apointment_visibility',
691 'value' => $args['visibility'],
692 'compare' => 'LIKE',
693 ];
694 }
695
696 if ( ! empty( $args['type'] ) ) {
697 $args['meta_query'][] = [
698 'key' => '_tt_apointment_type',
699 'value' => $args['type'],
700 'compare' => 'LIKE',
701 ];
702 }
703
704 if ( ! empty( $args['category'] ) ) {
705 $args['tax_query'][] = [
706 'taxonomy' => 'timetics-meeting-category',
707 'terms' => $args['category'],
708 'include_children' => false,
709 ];
710 }
711
712 $post = new WP_Query( $args );
713
714 return [
715 'total' => $post->found_posts,
716 'items' => $post->posts,
717 ];
718 }
719
720 /**
721 * Duplicate appointment
722 *
723 * @since 1.0.0
724 *
725 * @return void
726 */
727 public function duplicate() {
728 $this->set_props( [
729 'name' => $this->get_name(),
730 'description' => $this->get_description(),
731 'type' => $this->get_type(),
732 'locations' => $this->get_locations(),
733 'staff' => $this->get_staff_ids(),
734 'duplicate' => intval( $this->get_duplicate_nuber() ) + 1,
735 'duration' => $this->get_duration(),
736 'price' => $this->get_price(),
737 'capacity' => $this->get_capacity(),
738 'schedule' => $this->get_schedule(),
739 'categories' => $this->get_categories(),
740 'timezone' => $this->get_timezone(),
741 'availability' => $this->get_availability(),
742 'visibility' => $this->get_visibility(),
743 'buffer_time' => $this->get_buffer_time(),
744 'notifications' => $this->get_notifications(),
745 'fluent_hook_overwrite' => $this->get_fluent_hook_overwrite(),
746 'fleunt_crm_webhook' => $this->get_fleunt_crm_webhook(),
747 'pabbly_hook_overwrite' => $this->get_pabbly_hook_overwrite(),
748 'pabbly_webook' => $this->get_pabbly_webook(),
749 'zapier_webook' => $this->get_zapier_webook(),
750 'min_notice_time' => $this->get_min_notice_time(),
751 'zapier_hook_overwrite' => $this->get_zapier_hook_overwrite(),
752 'guest_enabled' => $this->get_guest_enabled(),
753 'guest_limit' => $this->get_guest_limit(),
754 ] );
755
756 $this->set_id( 0 );
757 $this->save();
758 }
759
760 /**
761 * Prepare meeting schedule
762 *
763 * @param string $start
764 * @param string $end
765 * @param integer $staff_id
766 *
767 * @return array
768 */
769 public function prepare_schedule( $start, $end, $staff_id, $timze_zone = '' ) {
770 $start = new \DateTime( $start );
771 $end = new \DateTime( $end );
772 $schedule = [];
773
774 // Prepare schedule for the current meeting
775 for ( $date = $start; $date <= $end; $date->modify( '+1 day' ) ) {
776 $schedule[] = $this->get_schedule_by_date( $date->format( 'Y-m-d' ), $staff_id, $timze_zone );
777 }
778
779 return $schedule;
780 }
781
782 /**
783 * Get meeting schedule by date
784 *
785 * @param string $date [$date description]
786 * @param integer $staff_id [$staff_id description]
787 *
788 * @return array
789 */
790 private function get_schedule_by_date( $date, $staff_id, $timze_zone = '' ) {
791 $day_name = ucfirst( date( 'D', strtotime( $date ) ) );
792 $schedule = $this->get_schedule();
793 $interval = $this->get_interval();
794 $tartget_schedule = [];
795
796 $slots = [];
797
798 if ( ! empty( $schedule[$staff_id] ) ) {
799 $tartget_schedule = $schedule[$staff_id][$day_name];
800 }
801
802 foreach ( $tartget_schedule as $day ) {
803 $start = strtotime( $day['start'] );
804 $end = strtotime( $day['end'] );
805
806 for ( $time = $start; $time <= $end; $time += $interval ) {
807 $booked_entry = $this->get_booking_entries( $date, $time, $staff_id );
808 $status = 'available';
809 $capacity = $this->get_capacity();
810 $booked = 0;
811
812 if ( $booked_entry ) {
813 if ( $booked_entry->get_booked() >= $capacity ) {
814 $status = 'unavailable';
815 }
816
817 $booked = $booked_entry->get_booked();
818 }
819
820 $datetime = $this->convert_timezone( $time, $timze_zone );
821
822 $slot = [
823 'status' => $status,
824 'start_time' => $datetime->format( 'g:ia' ),
825 'booked' => $booked,
826 ];
827
828 $slot = apply_filters( 'timetics_booking_slot', $slot, $this, $booked_entry );
829
830 $slots[] = $slot;
831 }
832 }
833
834 return [
835 'date' => $date,
836 'status' => empty( $slots ) ? 'unavailable' : 'available',
837 'slots' => $slots,
838 ];
839 }
840
841 /**
842 * Get booked entry status
843 *
844 * @param string $date
845 * @param string $time
846 * @param interger $staff_id
847 *
848 * @return bool
849 */
850 private function get_booking_entries( $date, $time, $staff_id ) {
851 $time = gmdate( 'h:i a', $time );
852 $booking_entries = new Booking_Entry();
853 $entries = $booking_entries->find( [
854 'meeting_id' => $this->id,
855 'staff_id' => $staff_id,
856 'date' => $this->convert_timezone( $date, $this->get_timezone())->format('Y-m-d'),
857 ] );
858
859 foreach ( $entries as $entry ) {
860 $booking = new Booking( $entry->get_booking_id() );
861
862 if ( $entry->get_start() == $time ) {
863 return $entry;
864 }
865 }
866
867 return false;
868 }
869
870 /**
871 * Convert timestamp in target timezone
872 *
873 * @param integer $timestamp
874 * @param string $timze_zone
875 *
876 * @return DateTime
877 */
878 public function convert_timezone( $date, $timze_zone ) {
879 $date = is_int( $date ) ? gmdate( 'Y-m-d g:ia', $date ) : $date;
880
881 // Create a DateTime object with the provided timestamp and time zone
882 $datetime = new \DateTime( $date, new \DateTimeZone( $this->get_timezone() ) );
883
884 // Convert the time zone
885 $datetime->setTimezone( new \DateTimeZone( $timze_zone ) );
886
887 // Get the converted timestamp
888 return $datetime;
889 }
890 }
891