PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 1.5.22
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v1.5.22
2.4.0 2.3.0 2.2.5 2.2.0 2.1.2 2.1.1 trunk 1.10.0 1.10.01 1.10.02 1.5.0 1.5.01 1.5.02 1.5.1 1.5.10 1.5.20 1.5.21 1.5.22 1.5.23 1.5.24 1.5.25 1.6.0 1.7.0 1.7.1 1.7.2 All 33 releases
fluent-booking / app / Models / CalendarSlot.php

CalendarSlot.php in Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution 1.5.22, at app/Models/CalendarSlot.php

1,124 lines 35.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBooking\App\Models;
4
5 use FluentBooking\App\Models\Model;
6 use FluentBooking\App\Services\SanitizeService;
7 use FluentBooking\App\Services\AvailabilityService;
8 use FluentBooking\App\Services\BookingFieldService;
9 use FluentBooking\App\Services\DateTimeHelper;
10 use FluentBooking\App\Services\Helper;
11 use FluentBooking\App\Services\BookingService;
12 use FluentBooking\App\Services\CurrenciesHelper;
13 use FluentBooking\App\Services\EditorShortCodeParser;
14 use FluentBooking\App\Services\LandingPage\LandingPageHandler;
15 use FluentBooking\App\Services\LandingPage\LandingPageHelper;
16 use FluentBooking\App\Services\LocationService;
17 use FluentBooking\Framework\Support\Arr;
18
19 class CalendarSlot extends Model
20 {
21 protected $table = 'fcal_calendar_events';
22
23 protected $guarded = ['id'];
24
25 protected $fillable = [
26 'user_id',
27 'hash',
28 'calendar_id',
29 'duration',
30 'title',
31 'slug',
32 'media_id',
33 'description',
34 'settings',
35 'availability_type',
36 'availability_id',
37 'status',
38 'type',
39 'color_schema',
40 'location_type',
41 'location_heading',
42 'location_settings',
43 'event_type',
44 'is_display_spots',
45 'max_book_per_slot',
46 'created_at',
47 'updated_at',
48 ];
49
50 public static function boot()
51 {
52 parent::boot();
53
54 static::creating(function ($model) {
55 if (empty($model->user_id)) {
56 $model->user_id = get_current_user_id();
57 }
58
59 if (empty($model->hash)) {
60 $model->hash = md5(wp_generate_uuid4() . time());
61 }
62 });
63 }
64
65 public function setSettingsAttribute($settings)
66 {
67 $originalSettings = $this->getOriginal('settings');
68
69 $originalSettings = \maybe_unserialize($originalSettings);
70
71 foreach ($settings as $key => $value) {
72 $originalSettings[$key] = $value;
73 }
74
75 $this->attributes['settings'] = \maybe_serialize($originalSettings);
76 }
77
78 public function getSettingsAttribute($settings)
79 {
80 return \maybe_unserialize($settings);
81 }
82
83 public function setLocationSettingsAttribute($locationSettings)
84 {
85 $this->attributes['location_settings'] = \maybe_serialize($locationSettings);
86 }
87
88 public function getLocationSettingsAttribute($locationSettings)
89 {
90 return \maybe_unserialize($locationSettings);
91 }
92
93 public function calendar()
94 {
95 return $this->belongsTo(Calendar::class, 'calendar_id');
96 }
97
98 public function bookings()
99 {
100 return $this->hasMany(Booking::class, 'event_id');
101 }
102
103 public function user()
104 {
105 return $this->belongsTo(User::class, 'user_id');
106 }
107
108 public function event_metas()
109 {
110 return $this->hasMany(Meta::class, 'object_id', 'id')
111 ->whereIn('object_type', ['calendar_event', 'integration']);
112 }
113
114 public function isGroup()
115 {
116 return $this->event_type == 'group';
117 }
118
119 public function isSingleEvent()
120 {
121 return $this->event_type == 'single_event';
122 }
123
124 public function isGroupEvent()
125 {
126 return $this->event_type == 'group_event';
127 }
128
129 public function isRoundRobin()
130 {
131 return $this->event_type == 'round_robin';
132 }
133
134 public function isCollective()
135 {
136 return $this->event_type == 'collective';
137 }
138
139 public function isTeamEvent()
140 {
141 return $this->isRoundRobin() || $this->isCollective();
142 }
143
144 public function isOneOffEvent()
145 {
146 return $this->isSingleEvent() || $this->isGroupEvent();
147 }
148
149 public function isMultiHostEvent()
150 {
151 return $this->isTeamEvent() || $this->isOneOffEvent();
152 }
153
154 public function isMultiHostsEvent()
155 {
156 return $this->isCollective() || $this->isOneOffEvent();
157 }
158
159 public function isMultiGuestEvent()
160 {
161 return $this->isGroup() || $this->isGroupEvent();
162 }
163
164 public function isProEvent()
165 {
166 return $this->isGroup() || $this->isTeamEvent() || $this->isOneOffEvent();
167 }
168
169 public function allowMultiBooking()
170 {
171 return Arr::isTrue($this->settings, 'multiple_booking.enabled');
172 }
173
174 public function multiBookingLimit()
175 {
176 return Arr::get($this->settings, 'multiple_booking.limit', 5);
177 }
178
179 public function getAuthorProfile($public = true, $userID = null)
180 {
181 $userID = $userID ?: $this->user_id;
182
183 $user = get_user_by('id', $userID);
184 if (!$user) {
185 return false;
186 }
187
188 $name = trim($user->first_name . ' ' . $user->last_name);
189
190 if (!$name) {
191 $name = $user->display_name;
192 }
193
194 $data = [
195 'ID' => $user->ID,
196 'name' => $name,
197 'avatar' => Helper::fluentBookingUserAvatar($user->ID, $user)
198 ];
199
200 if (!$public) {
201 $data['email'] = $user->user_email;
202 }
203
204 return $data;
205 }
206
207 public function getAuthorProfiles($public = true)
208 {
209 $teamMembers = [];
210 $teamMemberIds = $this->getHostIds();
211
212 foreach ($teamMemberIds as $teamMemberId) {
213 $calendar = Calendar::where('user_id', $teamMemberId)->where('type', 'simple')->first();
214 if ($calendar) {
215 $teamMembers[] = $calendar->getAuthorProfile($public);
216 }
217 }
218
219 return $teamMembers;
220 }
221
222 public function isLocationFieldRequired()
223 {
224 $locationSettings = Arr::get($this, 'location_settings');
225
226 if (count($locationSettings) > 1) {
227 return true;
228 }
229
230 return false;
231 }
232
233 public function isPhoneRequired()
234 {
235 if (count($this->location_settings) == 1) {
236 return Arr::get($this->location_settings, '0.type') == 'phone_guest';
237 }
238
239 return false;
240 }
241
242 public function isAddressRequired()
243 {
244 if (count($this->location_settings) == 1) {
245 return Arr::get($this->location_settings, '0.type') == 'in_person_guest';
246 }
247
248 return false;
249 }
250
251 public function isGuestFieldRequired()
252 {
253 return true;
254 }
255
256 public function getEventDefaultData($calendar)
257 {
258 $weeklySchedule = Helper::getWeeklyScheduleSchema();
259
260 $availability = AvailabilityService::maybeCreateAvailability($calendar, $weeklySchedule);
261
262 $defaultData = [
263 'title' => '',
264 'calendar_id' => $calendar->id,
265 'user_id' => $calendar->user_id,
266 'status' => 'active',
267 'event_type' => 'single',
268 'description' => '',
269 'duration' => '30',
270 'color_schema' => '#0099ff',
271 'availability_type' => 'existing_schedule',
272 'availability_id' => (int)$availability->id,
273 'max_book_per_slot' => 1,
274 'is_display_spots' => false,
275 'location_settings' => [
276 [
277 'type' => '',
278 'title' => '',
279 'description' => '',
280 'host_phone_number' => ''
281 ]
282 ],
283 'settings' => [
284 'schedule_type' => 'weekly_schedules',
285 'weekly_schedules' => $weeklySchedule,
286 'date_overrides' => [],
287 'range_type' => 'range_days',
288 'range_days' => 60,
289 'range_date_between' => ['', ''],
290 'schedule_conditions' => [
291 'value' => 4,
292 'unit' => 'hours'
293 ]
294 ]
295 ];
296
297 return $defaultData;
298 }
299
300 public function getEventSchema($calendar)
301 {
302 $userCalendarId = $calendar->type == 'simple' ? $calendar->id : null;
303
304 $settingsSchema = $this->getSlotSettingsSchema($userCalendarId);
305
306 $schema = [
307 'title' => '',
308 'status' => 'active',
309 'description' => '',
310 'duration' => '30',
311 'color_schema' => '#0099ff',
312 'calendar' => $calendar,
313 'settings' => $settingsSchema,
314 'max_book_per_slot' => 1,
315 'location_settings' => [
316 [
317 'type' => '',
318 'title' => '',
319 'description' => '',
320 'host_phone_number' => ''
321 ]
322 ]
323 ];
324
325 return $schema;
326 }
327
328 public function getSlotSettingsSchema($calendarId = null)
329 {
330 $calendarEvent = $calendarId ? CalendarSlot::where('calendar_id', $calendarId)->first() : null;
331
332 return [
333 'schedule_type' => 'weekly_schedules',
334 'weekly_schedules' => Helper::getWeeklyScheduleSchema(),
335 'date_overrides' => [],
336 'range_type' => 'range_days',
337 'range_days' => 60,
338 'range_date_between' => ['', ''],
339 'schedule_conditions' => [
340 'value' => 4,
341 'unit' => 'hours'
342 ],
343 'location_fields' => $this->getLocationFields($calendarEvent)
344 ];
345 }
346
347 public function getNotifications($isEdit = false)
348 {
349 $statuses = $this->getMeta('email_notifications');
350
351 if ($statuses) {
352
353 $defaults = Helper::getDefaultEmailNotificationSettings();
354
355 foreach ($defaults as $key => $default) {
356 if (isset($statuses[$key])) {
357 if ($isEdit) {
358 $statuses[$key]['title'] = $default['title'];
359 }
360 $emailBody = str_replace('fluent-booking-pro/core', 'fluent-booking', $statuses[$key]['email']['body']);
361 $statuses[$key]['email']['body'] = $emailBody;
362 }
363 }
364
365 if (!Arr::get($statuses, 'rescheduled_by_host')) {
366 $statuses['rescheduled_by_host'] = $defaults['rescheduled_by_host'];
367 }
368
369 if (!Arr::get($statuses, 'rescheduled_by_attendee')) {
370 $statuses['rescheduled_by_attendee'] = $defaults['rescheduled_by_attendee'];
371 }
372
373 if (!Arr::get($statuses, 'booking_request_host')) {
374 $statuses['booking_request_host'] = $defaults['booking_request_host'];
375 }
376
377 if (!Arr::get($statuses, 'booking_request_attendee')) {
378 $statuses['booking_request_attendee'] = $defaults['booking_request_attendee'];
379 }
380
381 if (!Arr::get($statuses, 'declined_by_host')) {
382 $statuses['declined_by_host'] = $defaults['declined_by_host'];
383 }
384
385 return $statuses;
386 }
387
388 return Helper::getDefaultEmailNotificationSettings();
389 }
390
391 public function setNotifications($notifications)
392 {
393 $this->updateMeta('email_notifications', $notifications);
394 }
395
396 public function getBookingFields()
397 {
398 return BookingFieldService::getBookingFields($this);
399 }
400
401 public function setBookingFields($bookingFields)
402 {
403 return $this->updateMeta('booking_fields', $bookingFields);
404 }
405
406 public function getScheduleTimezone($hostId = null)
407 {
408 if ($hostId && !$this->isTeamCommonSchedule()) {
409 $schedule = AvailabilityService::getDefaultSchedule($hostId);
410 return Arr::get($schedule, 'value.timezone', 'UTC');
411 }
412
413 if ($this->availability_type == 'existing_schedule') {
414 $schedule = Availability::findOrFail($this->availability_id);
415 return Arr::get($schedule, 'value.timezone', 'UTC');
416 }
417
418 return $this->calendar->author_timezone;
419 }
420
421 public function isMultiDurationEnabled()
422 {
423 return Arr::isTrue($this->settings, 'multi_duration.enabled');
424 }
425
426 public function getDuration($duration = null)
427 {
428 if ($this->isMultiDurationEnabled()) {
429 if (in_array($duration, Arr::get($this->settings, 'multi_duration.available_durations', []))) {
430 return $duration;
431 } else {
432 return Arr::get($this->settings, 'multi_duration.default_duration', '');
433 }
434 }
435
436 return $this->duration;
437 }
438
439 public function getDefaultDuration()
440 {
441 if ($this->isMultiDurationEnabled()) {
442 return Arr::get($this->settings, 'multi_duration.default_duration', '');
443 }
444
445 return $this->duration;
446 }
447
448 public function getAvailableDurations()
449 {
450 if ($this->isMultiDurationEnabled()) {
451 $durationLookup = Helper::getDurationLookup(true);
452 $availableDurations = Arr::get($this->settings, 'multi_duration.available_durations', []);
453
454 return array_map(function ($duration) use ($durationLookup) {
455 return $durationLookup[$duration];
456 }, $availableDurations);
457 }
458
459 $durationLookup = Helper::getDurationLookup();
460
461 $duration = $durationLookup[$this->duration] ?? Helper::formatDuration($this->duration);
462
463 return [$duration];
464 }
465
466 public function getDescription()
467 {
468 if ($this->description) {
469 return $this->description;
470 }
471
472 if ($this->isMultiDurationEnabled()) {
473 return __('Choose your duration and book a meeting with me', 'fluent-booking');
474 }
475
476 // translators: %d is the duration of the meeting in minutes
477 return sprintf(__('Book a meeting with me for %d minutes', 'fluent-booking'), $this->duration);
478 }
479
480 public function getSlotInterval($duration = null)
481 {
482 $duration = $duration ?: $this->duration;
483
484 $interval = Arr::get($this->settings, 'slot_interval', '');
485
486 $slotInterval = empty($interval) ? $duration : intval($interval);
487
488 return $slotInterval;
489 }
490
491 public function isReserveTime()
492 {
493 return Arr::isTrue($this->settings, 'reserve_time', false);
494 }
495
496 public function getAvailableTimes()
497 {
498 return Arr::get($this->settings, 'available_times', []);
499 }
500
501 public function getTotalBufferTime()
502 {
503 $bufferTimeBefore = Arr::get($this->settings, 'buffer_time_before', 0);
504 $bufferTimeAfter = Arr::get($this->settings, 'buffer_time_after', 0);
505
506 return $bufferTimeBefore + $bufferTimeAfter;
507 }
508
509 public function getMaxBookableDateTime($startDate, $timeZone = 'UTC', $format = 'Y-m-d 23:59:59')
510 {
511 $rangeType = Arr::get($this->settings, 'range_type', 'range_days');
512
513 $lastDay = gmdate('Y-m-t 23:59:59', strtotime($startDate)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
514
515 if ($timeZone != 'UTC') {
516 $lastDay = DateTimeHelper::convertToTimeZone($lastDay, $timeZone, 'UTC', $format);
517 }
518
519 if ($rangeType == 'range_indefinite') {
520 return $lastDay;
521 }
522
523 $maxLookupDate = $this->getMaxLookUpDate();
524
525 $maxDate = DateTimeHelper::convertToTimeZone($maxLookupDate, $this->calendar->author_timezone, 'UTC');
526
527 if (strtotime($maxDate) > strtotime($lastDay)) {
528 return $lastDay;
529 }
530
531 return $maxDate;
532 }
533
534 public function getMinBookableDateTime($startDate = null, $timeZone = null)
535 {
536 $startDate = $startDate ?: gmdate('Y-m-d H:i:s'); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
537
538 $rangeType = Arr::get($this->settings, 'range_type', 'range_days');
539
540 if ($rangeType == 'range_date_between') {
541 $range = Arr::get($this->settings, 'range_date_between', []);
542 if (is_array($range) && count(array_filter($range)) == 2) {
543 if (strtotime($range[0]) >= strtotime($startDate)) {
544 $startDate = gmdate('Y-m-d H:i:s', strtotime($range[0])); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
545 }
546 }
547 }
548
549 $totalCutStamp = DateTimeHelper::getTimestamp() + $this->getCutoutSeconds();
550
551 if (strtotime($startDate) < $totalCutStamp) {
552 $startDate = gmdate('Y-m-d H:i:s', $totalCutStamp); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
553 }
554
555 return $startDate;
556 }
557
558 public function getMaxLookUpDate()
559 {
560 $rangeType = Arr::get($this->settings, 'range_type', 'range_days');
561
562 if ($rangeType == 'range_indefinite') {
563 return false;
564 }
565
566 if ($rangeType == 'range_date_between') {
567 $range = Arr::get($this->settings, 'range_date_between', []);
568 if (is_array($range) && count(array_filter($range)) == 2) {
569 return gmdate('Y-m-d 23:59:59', strtotime($range[1])); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
570 }
571 }
572
573 $rangeDays = Arr::get($this->settings, 'range_days', 60) ?: 60;
574
575 return gmdate('Y-m-d 23:59:59', time() + $rangeDays * DAY_IN_SECONDS); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
576 }
577
578 public function getMinLookUpDate($timeZone = 'UTC')
579 {
580 $rangeType = Arr::get($this->settings, 'range_type', 'range_days');
581
582 $minDate = gmdate('Y-m-d H:i:s'); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
583
584 if ($rangeType == 'range_date_between') {
585 $range = Arr::get($this->settings, 'range_date_between', []);
586 if (is_array($range) && count(array_filter($range)) == 2) {
587 $minDate = gmdate('Y-m-d H:i:s', strtotime($range[0])); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
588 }
589 }
590
591 if ($timeZone != 'UTC') {
592 $minDate = DateTimeHelper::convertToTimeZone($minDate, $timeZone, 'UTC');
593 }
594
595 return $minDate;
596 }
597
598 public function getCutoutSeconds()
599 {
600 $conditions = Arr::get($this->settings, 'schedule_conditions', []);
601
602 if (!$conditions || empty($conditions['unit'])) {
603 return 0;
604 }
605
606 return strtotime('+' . $conditions['value'] . ' ' . $conditions['unit'], 0) - strtotime('+0 seconds', 0);
607 }
608
609 public function getHostIds($hostId = null)
610 {
611 if ($hostId) {
612 return [$hostId];
613 }
614
615 if ($this->isMultiHostEvent()) {
616 return Arr::get($this->settings, 'team_members', []);
617 }
618
619 return [$this->user_id];
620 }
621
622 public function getMaxBookingPerSlot()
623 {
624 return $this->max_book_per_slot;
625 }
626
627 public function getPublicUrl()
628 {
629 $calendar = $this->calendar;
630 if (!$calendar) {
631 return false;
632 }
633
634 $baseUr = $calendar->getLandingPageUrl();
635
636 if (!$baseUr) {
637 return '';
638 }
639
640 if (defined('FLUENT_BOOKING_LANDING_SLUG')) {
641 return $baseUr . '/' . $this->slug;
642 }
643
644 return $baseUr . '&event=' . $this->slug;
645 }
646
647 public function getMeta($key, $default = null)
648 {
649 $meta = Meta::where('object_type', 'calendar_event')
650 ->where('object_id', $this->id)
651 ->where('key', $key)
652 ->first();
653
654 if (!$meta) {
655 return $default;
656 }
657
658 return $meta->value;
659 }
660
661 public function updateMeta($key, $value)
662 {
663 $exist = Meta::where('object_type', 'calendar_event')
664 ->where('object_id', $this->id)
665 ->where('key', $key)
666 ->first();
667
668 if ($exist) {
669 $exist->value = $value;
670 $exist->save();
671 } else {
672 $exist = Meta::create([
673 'object_type' => 'calendar_event',
674 'object_id' => $this->id,
675 'key' => $key,
676 'value' => $value
677 ]);
678 }
679
680 return $exist;
681 }
682
683 public function getLocationFields($calendarEvent = null)
684 {
685 return apply_filters('fluent_booking/get_location_fields', [
686 'conferencing' => [
687 'label' => __('Conferencing', 'fluent-booking'),
688 'options' => [
689 'google_meet' => [
690 'title' => __('Google Meet (Pro)', 'fluent-booking'),
691 'disabled' => true,
692 'location_type' => 'conferencing'
693 ],
694 'ms_teams' => [
695 'title' => __('MS Teams (Pro)', 'fluent-booking'),
696 'disabled' => true,
697 'location_type' => 'conferencing'
698 ],
699 'zoom_meeting' => [
700 'title' => __('Zoom Video (Pro)', 'fluent-booking'),
701 'disabled' => true,
702 'location_type' => 'conferencing'
703 ],
704 ],
705 ],
706 'in_person' => [
707 'label' => __('In Person', 'fluent-booking'),
708 'options' => [
709 'in_person_guest' => [
710 'title' => __('In Person (Attendee Address)', 'fluent-booking'),
711 ],
712 'in_person_organizer' => [
713 'title' => __('In Person (Organizer Address)', 'fluent-booking'),
714 ],
715 ],
716 ],
717 'phone' => [
718 'label' => __('Phone', 'fluent-booking'),
719 'options' => [
720 'phone_guest' => [
721 'title' => __('Attendee Phone Number', 'fluent-booking'),
722 ],
723 'phone_organizer' => [
724 'title' => __('Organizer Phone Number', 'fluent-booking'),
725 ],
726 ],
727 ],
728 'online' => [
729 'label' => __('Online', 'fluent-booking'),
730 'options' => [
731 'online_meeting' => [
732 'title' => __('Online Meeting', 'fluent-booking'),
733 ],
734 ],
735 ],
736 'other' => [
737 'label' => __('Other', 'fluent-booking'),
738 'options' => [
739 'custom' => [
740 'title' => __('Custom', 'fluent-booking'),
741 ],
742 ],
743 ],
744 ], $calendarEvent ?: $this);
745 }
746
747 public function isDisplaySpots()
748 {
749 return $this->is_display_spots == true;
750 }
751
752 public function isAdditionalGuestEnabled()
753 {
754 $guestField = BookingFieldService::getBookingFieldByName($this, 'guests');
755
756 return Arr::isTrue($guestField, 'enabled', false);
757 }
758
759 public function isConfirmationEnabled()
760 {
761 return Arr::isTrue($this->settings, 'requires_confirmation.enabled');
762 }
763
764 public function isConfirmationRequired($bookingStartTime, $bookingCreatedTime = null)
765 {
766 if (!$this->isConfirmationEnabled() || !is_string($bookingStartTime)) {
767 return false;
768 }
769
770 $type = Arr::get($this->settings, 'requires_confirmation.type', 'always');
771 if ($type == 'always') {
772 return true;
773 }
774
775 $bookingStartTime = strtotime($bookingStartTime);
776 $bookingCreatedTime = $bookingCreatedTime ? strtotime($bookingCreatedTime) : time();
777
778 $conditionUnit = Arr::get($this->settings, 'requires_confirmation.condition.unit', 'minutes');
779 $conditionValue = Arr::get($this->settings, 'requires_confirmation.condition.value', 0);
780
781 $conditionTime = $conditionValue * 60;
782 if ($conditionUnit == 'hours') {
783 $conditionTime = $conditionTime * 60;
784 }
785
786 return $bookingStartTime - $bookingCreatedTime < $conditionTime;
787 }
788
789 public function getCanNotCancelSettings()
790 {
791 if (!isset($this->settings['can_not_cancel'])) {
792 $enabled = Arr::get($this->settings, 'can_cancel') == 'no' ? true : false;
793 return [
794 'enabled' => $enabled,
795 'type' => 'always',
796 ];
797 }
798 return Arr::get($this->settings, 'can_not_cancel', []);
799 }
800
801 public function getCanNotRescheduleSettings()
802 {
803 if (!isset($this->settings['can_not_reschedule'])) {
804 $enabled = Arr::get($this->settings, 'can_reschedule') == 'no' ? true : false;
805 return [
806 'enabled' => $enabled,
807 'type' => 'always',
808 ];
809 }
810 return Arr::get($this->settings, 'can_not_reschedule', []);
811 }
812
813 public function defaultLocationHtml()
814 {
815 if (empty($this->location_settings)) {
816 return '';
817 }
818
819 $default = Arr::get($this, 'location_settings');
820 if (!$default) {
821 return '';
822 }
823
824 return LocationService::getLocationIconHeadingHtml($default, $this);
825 }
826
827 public function defaultPaymentIcon($currency, $amount)
828 {
829 $svg = '<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-ea893728=""><path fill="currentColor" d="M256 640v192h640V384H768v-64h150.976c14.272 0 19.456 1.472 24.64 4.288a29.056 29.056 0 0 1 12.16 12.096c2.752 5.184 4.224 10.368 4.224 24.64v493.952c0 14.272-1.472 19.456-4.288 24.64a29.056 29.056 0 0 1-12.096 12.16c-5.184 2.752-10.368 4.224-24.64 4.224H233.024c-14.272 0-19.456-1.472-24.64-4.288a29.056 29.056 0 0 1-12.16-12.096c-2.688-5.184-4.224-10.368-4.224-24.576V640h64z"></path><path fill="currentColor" d="M768 192H128v448h640V192zm64-22.976v493.952c0 14.272-1.472 19.456-4.288 24.64a29.056 29.056 0 0 1-12.096 12.16c-5.184 2.752-10.368 4.224-24.64 4.224H105.024c-14.272 0-19.456-1.472-24.64-4.288a29.056 29.056 0 0 1-12.16-12.096C65.536 682.432 64 677.248 64 663.04V169.024c0-14.272 1.472-19.456 4.288-24.64a29.056 29.056 0 0 1 12.096-12.16C85.568 129.536 90.752 128 104.96 128h685.952c14.272 0 19.456 1.472 24.64 4.288a29.056 29.056 0 0 1 12.16 12.096c2.752 5.184 4.224 10.368 4.224 24.64z"></path><path fill="currentColor" d="M448 576a160 160 0 1 1 0-320 160 160 0 0 1 0 320zm0-64a96 96 0 1 0 0-192 96 96 0 0 0 0 192z"></path></svg>';
830 $html = '<span class="fcal_slot_payment_icon">' . $svg . $currency . $amount . '</span>';
831 return $html;
832 }
833
834 public function isPaymentEnabled($duration = null)
835 {
836 if ($this->isMultiDurationEnabled()) {
837 $paymentSettings = $this->getPaymentSettings();
838 if (Arr::get($paymentSettings, 'multi_payment_enabled') == 'yes') {
839 $duration = $duration ?? $this->getDefaultDuration();
840 if (!Arr::get($paymentSettings, 'multi_payment_items.' . $duration . '.value')) {
841 return false;
842 }
843 }
844 }
845
846 return $this->type == 'paid' && Helper::isPaymentEnabled();
847 }
848
849 public function isWooEnabled()
850 {
851 return $this->type == 'woo' && defined('WC_PLUGIN_FILE');
852 }
853
854 public function getPaymentItems($duration = null)
855 {
856 $paymentSettings = $this->getPaymentSettings();
857
858 $isMultiEnabled = Arr::get($paymentSettings, 'multi_payment_enabled') == 'yes';
859
860 if ($this->isMultiDurationEnabled() && $isMultiEnabled) {
861 $duration = $duration ?? $this->getDefaultDuration();
862 return [Arr::get($paymentSettings, 'multi_payment_items.' . $duration)];
863 }
864
865 return Arr::get($paymentSettings, 'items', []);
866 }
867
868 public function getPricingTotal()
869 {
870 if (!$this->isPaymentEnabled()) {
871 return 0;
872 }
873
874 $total = 0;
875 $items = $this->getPaymentItems();
876 foreach ($items as $item) {
877 $total += (int)$item['value'];
878 }
879
880 return $total;
881 }
882
883 public function getWooProductPrice()
884 {
885 $paymentSettings = $this->getPaymentSettings();
886
887 $productId = $paymentSettings['woo_product_id'];
888
889 if (Arr::get($paymentSettings, 'multi_payment_enabled') == 'yes') {
890 $duration = $this->getDefaultDuration();
891 $productId = Arr::get($paymentSettings, 'multi_payment_woo_ids.' . $duration);
892 }
893
894 $price = 0;
895 $product = wc_get_product($productId);
896 if ($product) {
897 $price = $product->get_price();
898 }
899
900 return $price;
901 }
902
903 public function getWooProductPriceByDuration($productIds = [])
904 {
905 $productPrices = [];
906
907 foreach ($productIds as $duration => $productId) {
908 $product = wc_get_product($productId);
909 if ($product) {
910 $productPrices[$duration] = [
911 'value' => $product->get_price()
912 ];
913 }
914 }
915
916 return $productPrices;
917 }
918
919 public function getPaymentHtml()
920 {
921 $paymentHtml = '';
922
923 $driver = Arr::get($this->getPaymentSettings(), 'driver');
924
925 if ($driver == 'native' && $this->isPaymentEnabled()) {
926 $currency = CurrenciesHelper::getGlobalCurrencySign();
927 $totalPayment = $this->getPricingTotal();
928 $paymentHtml = $this->defaultPaymentIcon($currency, $totalPayment);
929 }
930
931 if ($driver == 'woo' && $this->isWooEnabled()) {
932 $currency = get_woocommerce_currency_symbol();
933 $totalPayment = $this->getWooProductPrice();
934 $paymentHtml = $this->defaultPaymentIcon($currency, $totalPayment);
935 }
936
937 return $paymentHtml;
938 }
939
940 public function isTeamDefaultSchedule()
941 {
942 if ($this->isTeamEvent()) {
943 if (!Arr::isTrue($this->settings, 'common_schedule', false)) {
944 return true;
945 }
946 }
947 return false;
948 }
949
950 public function isTeamCommonSchedule()
951 {
952 if ($this->isTeamEvent()) {
953 if (Arr::isTrue($this->settings, 'common_schedule', false)) {
954 return true;
955 }
956 }
957 return false;
958 }
959
960 public function isRoundRobinDefaultSchedule()
961 {
962 return $this->isRoundRobin() && $this->isTeamDefaultSchedule();
963 }
964
965 public function isRoundRobinCommonSchedule()
966 {
967 return $this->isRoundRobin() && $this->isTeamCommonSchedule();
968 }
969
970 private function getProcessedWeeklySlots($schedule)
971 {
972 $scheduleData = Arr::get($schedule, 'value.weekly_schedules', []);
973 $scheduleTimezone = Arr::get($schedule, 'value.timezone', 'UTC');
974 $schedule = SanitizeService::weeklySchedules($scheduleData, 'UTC', $scheduleTimezone);
975 return AvailabilityService::getUtcWeeklySchedules($schedule, $scheduleTimezone);
976 }
977
978 private function getProcessedDateOverrides($schedule)
979 {
980 $scheduleData = Arr::get($schedule, 'value.date_overrides', []);
981 $scheduleTimezone = Arr::get($schedule, 'value.timezone', 'UTC');
982 $schedule = SanitizeService::slotDateOverrides($scheduleData, 'UTC', $scheduleTimezone);
983 $overrideSlots = AvailabilityService::getUtcDateOverrides($schedule, $scheduleTimezone);
984 $overrideDays = AvailabilityService::getDateOverrideDays($schedule, $scheduleTimezone);
985 return [$overrideSlots, $overrideDays];
986 }
987
988 public function getWeeklySlots($hostId = null)
989 {
990 if ($hostId && !$this->isTeamCommonSchedule()) {
991 $schedule = AvailabilityService::getDefaultSchedule($hostId);
992 return $this->getProcessedWeeklySlots($schedule);
993 }
994
995 if ($this->availability_type === 'existing_schedule') {
996 $schedule = Availability::findOrFail($this->availability_id);
997 return $this->getProcessedWeeklySlots($schedule);
998 }
999
1000 $scheduleData = Arr::get($this->settings, 'weekly_schedules', []);
1001 $schedule = SanitizeService::weeklySchedules($scheduleData, 'UTC', $this->calendar->author_timezone);
1002 return AvailabilityService::getUtcWeeklySchedules($schedule, $this->calendar->author_timezone);
1003 }
1004
1005 public function getDateOverrides($hostId = null)
1006 {
1007 if ($hostId && !$this->isTeamCommonSchedule()) {
1008 $schedule = AvailabilityService::getDefaultSchedule($hostId);
1009 return $this->getProcessedDateOverrides($schedule);
1010 }
1011
1012 if ($this->availability_type === 'existing_schedule') {
1013 $schedule = Availability::findOrFail($this->availability_id);
1014 return $this->getProcessedDateOverrides($schedule);
1015 }
1016
1017 $scheduleData = Arr::get($this->settings, 'date_overrides', []);
1018 $schedule = SanitizeService::slotDateOverrides($scheduleData, 'UTC', $this->calendar->author_timezone);
1019 $overrideSlots = AvailabilityService::getUtcDateOverrides($schedule, $this->calendar->author_timezone);
1020 $overrideDays = AvailabilityService::getDateOverrideDays($schedule, $this->calendar->author_timezone);
1021 return [$overrideSlots, $overrideDays];
1022 }
1023
1024 public function getHostIdsSortedByBookings($startDate, $hostId = null)
1025 {
1026 $hostIds = $this->getHostIds($hostId);
1027
1028 if (count($hostIds) <= 1) {
1029 return $hostIds;
1030 }
1031
1032 $hostBookings = [];
1033 foreach ($hostIds as $hostId) {
1034 $dayStart = gmdate('Y-m-d 00:00:00', strtotime($startDate)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
1035 $dayEnd = gmdate('Y-m-d 23:59:59', strtotime($startDate)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
1036 $hostBookings[$hostId] = Booking::getHostTotalBooking($this->id, [$hostId], [$dayStart, $dayEnd]);
1037 }
1038 usort($hostIds, function ($a, $b) use ($hostBookings) {
1039 return $hostBookings[$a] - $hostBookings[$b];
1040 });
1041
1042 return $hostIds;
1043 }
1044
1045 public function getPaymentSettings()
1046 {
1047 $settings = $this->getMeta('payment_settings', []);
1048
1049 $duration = $this->getDefaultDuration();
1050
1051 $defaults = [
1052 'enabled' => 'no',
1053 'multi_payment_enabled' => 'no',
1054 'stripe_enabled' => 'no',
1055 'paypal_enabled' => 'no',
1056 'driver' => 'native',
1057 'items' => [
1058 [
1059 'title' => __('Booking Fee', 'fluent-booking'),
1060 'value' => 100
1061 ]
1062 ],
1063 'woo_product_id' => '',
1064 'multi_payment_items' => [
1065 $duration => [
1066 'title' => __('Booking Fee', 'fluent-booking'),
1067 'value' => 0
1068 ]
1069 ],
1070 'multi_payment_woo_ids' => [
1071 $duration => ''
1072 ]
1073 ];
1074
1075 if (!$settings) {
1076 $settings = $defaults;
1077 }
1078
1079 return wp_parse_args($settings, $defaults);
1080 }
1081
1082 public function getCalendarEventsMeta()
1083 {
1084 $eventsMeta = Meta::where('object_id', $this->id)
1085 ->where('object_type', 'calendar_event')
1086 ->get();
1087
1088 return $eventsMeta;
1089 }
1090
1091 public function getIntegrationsMeta()
1092 {
1093 $integrationsMeta = Meta::where('object_id', $this->id)
1094 ->where('object_type', 'integration')
1095 ->get();
1096
1097 return $integrationsMeta;
1098 }
1099
1100 /**
1101 * Get the attributes that have been changed since last sync.
1102 *
1103 * @return array
1104 */
1105 public function getDirty()
1106 {
1107 $dirty = [];
1108 foreach ($this->attributes as $key => $value) {
1109 if (!in_array($key, $this->fillable)) {
1110 continue;
1111 }
1112
1113 if (!array_key_exists($key, $this->original)) {
1114 $dirty[$key] = $value;
1115 } elseif ($value !== $this->original[$key] &&
1116 !$this->originalIsNumericallyEquivalent($key)) {
1117 $dirty[$key] = $value;
1118 }
1119 }
1120
1121 return $dirty;
1122 }
1123 }
1124