PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 2.5.0
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v2.5.0
2.5.0 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 All 34 releases
← All changes | app/Models/CalendarSlot.php +158 -34 1.5.22 → 2.5.0 View file →
@@ -2,20 +2,17 @@
2 2
3 3 namespace FluentBooking\App\Models;
4 4
5 5 use FluentBooking\App\Models\Model;
6 +use FluentBooking\Framework\Support\Arr;
6 7 use FluentBooking\App\Services\SanitizeService;
7 8 use FluentBooking\App\Services\AvailabilityService;
8 9 use FluentBooking\App\Services\BookingFieldService;
9 10 use FluentBooking\App\Services\DateTimeHelper;
10 11 use FluentBooking\App\Services\Helper;
11 -use FluentBooking\App\Services\BookingService;
12 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 13 use FluentBooking\App\Services\LocationService;
17 -use FluentBooking\Framework\Support\Arr;
14 +use FluentBooking\App\Services\Integrations\FluentCart\CartHelper;
18 15
19 16 class CalendarSlot extends Model
20 17 {
21 18 protected $table = 'fcal_calendar_events';
@@ -89,8 +86,19 @@
89 86 {
90 87 return \maybe_unserialize($locationSettings);
91 88 }
92 89
90 + public function getShortDescriptionAttribute()
91 + {
92 + $description = preg_replace('/<[^>]*>/', ' ', $this->getDescription());
93 +
94 + $maxLength = apply_filters('fluent_booking/event_short_description_length', 160, $this);
95 +
96 + $description = Helper::excerpt($description, $maxLength);
97 +
98 + return apply_filters('fluent_booking/event_short_description', $description, $this);
99 + }
100 +
93 101 public function calendar()
94 102 {
95 103 return $this->belongsTo(Calendar::class, 'calendar_id');
96 104 }
@@ -110,8 +118,13 @@
110 118 return $this->hasMany(Meta::class, 'object_id', 'id')
111 119 ->whereIn('object_type', ['calendar_event', 'integration']);
112 120 }
113 121
122 + public function isOneToOne()
123 + {
124 + return $this->event_type == 'single';
125 + }
126 +
114 127 public function isGroup()
115 128 {
116 129 return $this->event_type == 'group';
117 130 }
@@ -125,8 +138,18 @@
125 138 {
126 139 return $this->event_type == 'group_event';
127 140 }
128 141
142 + /**
143 + * Every value event_type can hold, on both fcal_calendar_slots and fcal_bookings.
144 + *
145 + * @return array
146 + */
147 + public static function getEventTypes()
148 + {
149 + return ['single', 'group', 'round_robin', 'collective', 'single_event', 'group_event'];
150 + }
151 +
129 152 public function isRoundRobin()
130 153 {
131 154 return $this->event_type == 'round_robin';
132 155 }
@@ -160,16 +183,26 @@
160 183 {
161 184 return $this->isGroup() || $this->isGroupEvent();
162 185 }
163 186
187 + public function isRecurringEvent()
188 + {
189 + return Arr::isTrue($this->getRecurringConfig(), 'enabled');
190 + }
191 +
164 192 public function isProEvent()
165 193 {
166 - return $this->isGroup() || $this->isTeamEvent() || $this->isOneOffEvent();
194 + return $this->isGroup() || $this->isTeamEvent() || $this->isOneOffEvent() || $this->allowMultiBooking();
167 195 }
168 196
197 + public function isMultiBooking()
198 + {
199 + return Arr::isTrue($this->settings, 'multiple_booking.enabled');
200 + }
201 +
169 202 public function allowMultiBooking()
170 203 {
171 - return Arr::isTrue($this->settings, 'multiple_booking.enabled');
204 + return $this->isMultiBooking() || $this->isRecurringEvent();
172 205 }
173 206
174 207 public function multiBookingLimit()
175 208 {
@@ -208,11 +241,19 @@
208 241 {
209 242 $teamMembers = [];
210 243 $teamMemberIds = $this->getHostIds();
211 244
245 + cache_users($teamMemberIds);
246 +
247 + $calendars = Calendar::whereIn('user_id', $teamMemberIds)
248 + ->where('type', 'simple')
249 + ->orderBy('id', 'desc')
250 + ->with(['metas', 'user', 'user.metas'])
251 + ->get()
252 + ->keyBy('user_id');
253 +
212 254 foreach ($teamMemberIds as $teamMemberId) {
213 - $calendar = Calendar::where('user_id', $teamMemberId)->where('type', 'simple')->first();
214 - if ($calendar) {
255 + if ($calendar = $calendars->get($teamMemberId)) {
215 256 $teamMembers[] = $calendar->getAuthorProfile($public);
216 257 }
217 258 }
218 259
@@ -218,8 +259,13 @@
218 259
219 260 return $teamMembers;
220 261 }
221 262
263 + public function getRecurringConfig()
264 + {
265 + return Arr::get($this->settings, 'recurring_config', []);
266 + }
267 +
222 268 public function isLocationFieldRequired()
223 269 {
224 270 $locationSettings = Arr::get($this, 'location_settings');
225 271
@@ -405,15 +451,17 @@
405 451
406 452 public function getScheduleTimezone($hostId = null)
407 453 {
408 454 if ($hostId && !$this->isTeamCommonSchedule()) {
409 - $schedule = AvailabilityService::getDefaultSchedule($hostId);
455 + $schedule = $this->getHostSchedule($hostId);
410 456 return Arr::get($schedule, 'value.timezone', 'UTC');
411 457 }
412 458
413 459 if ($this->availability_type == 'existing_schedule') {
414 - $schedule = Availability::findOrFail($this->availability_id);
415 - return Arr::get($schedule, 'value.timezone', 'UTC');
460 + $schedule = Availability::find($this->availability_id);
461 + if ($schedule) {
462 + return Arr::get($schedule, 'value.timezone', 'UTC');
463 + }
416 464 }
417 465
418 466 return $this->calendar->author_timezone;
419 467 }
@@ -534,8 +582,12 @@
534 582 public function getMinBookableDateTime($startDate = null, $timeZone = null)
535 583 {
536 584 $startDate = $startDate ?: gmdate('Y-m-d H:i:s'); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
537 585
586 + if ($timeZone) {
587 + $startDate = DateTimeHelper::convertToTimeZone($startDate, $timeZone, 'UTC');
588 + }
589 +
538 590 $rangeType = Arr::get($this->settings, 'range_type', 'range_days');
539 591
540 592 if ($rangeType == 'range_date_between') {
541 593 $range = Arr::get($this->settings, 'range_date_between', []);
@@ -541,8 +593,9 @@
541 593 $range = Arr::get($this->settings, 'range_date_between', []);
542 594 if (is_array($range) && count(array_filter($range)) == 2) {
543 595 if (strtotime($range[0]) >= strtotime($startDate)) {
544 596 $startDate = gmdate('Y-m-d H:i:s', strtotime($range[0])); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
597 + $startDate = DateTimeHelper::convertToTimeZone($startDate, $this->calendar->author_timezone, 'UTC');
545 598 }
546 599 }
547 600 }
548 601
@@ -602,9 +655,11 @@
602 655 if (!$conditions || empty($conditions['unit'])) {
603 656 return 0;
604 657 }
605 658
606 - return strtotime('+' . $conditions['value'] . ' ' . $conditions['unit'], 0) - strtotime('+0 seconds', 0);
659 + $value = max(0, (int) Arr::get($conditions, 'value', 0));
660 +
661 + return strtotime('+' . $value . ' ' . $conditions['unit'], 0) - strtotime('+0 seconds', 0);
607 662 }
608 663
609 664 public function getHostIds($hostId = null)
610 665 {
@@ -780,8 +835,10 @@
780 835
781 836 $conditionTime = $conditionValue * 60;
782 837 if ($conditionUnit == 'hours') {
783 838 $conditionTime = $conditionTime * 60;
839 + } elseif ($conditionUnit == 'days') {
840 + $conditionTime = $conditionTime * 60 * 24;
784 841 }
785 842
786 843 return $bookingStartTime - $bookingCreatedTime < $conditionTime;
787 844 }
@@ -823,12 +880,13 @@
823 880
824 881 return LocationService::getLocationIconHeadingHtml($default, $this);
825 882 }
826 883
827 - public function defaultPaymentIcon($currency, $amount)
884 + public function defaultPaymentIcon($amount, $currencySettings = [])
828 885 {
886 + $formattedAmount = $currencySettings ? fluentbookingFormattedAmount((float)$amount * 100, $currencySettings) : $amount;
829 887 $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>';
888 + $html = '<span class="fcal_slot_payment_icon">' . $svg . $formattedAmount . '</span>';
831 889 return $html;
832 890 }
833 891
834 892 public function isPaymentEnabled($duration = null)
@@ -845,13 +903,25 @@
845 903
846 904 return $this->type == 'paid' && Helper::isPaymentEnabled();
847 905 }
848 906
907 + public function isPaidEvent()
908 + {
909 + $paymentSettings = $this->getPaymentSettings();
910 +
911 + return $this->type != 'free' && Arr::get($paymentSettings, 'enabled') == 'yes';
912 + }
913 +
849 914 public function isWooEnabled()
850 915 {
851 916 return $this->type == 'woo' && defined('WC_PLUGIN_FILE');
852 917 }
853 918
919 + public function isCartEnabled()
920 + {
921 + return $this->type == 'cart' && defined('FLUENTCART_VERSION');
922 + }
923 +
854 924 public function getPaymentItems($duration = null)
855 925 {
856 926 $paymentSettings = $this->getPaymentSettings();
857 927
@@ -857,9 +927,9 @@
857 927
858 928 $isMultiEnabled = Arr::get($paymentSettings, 'multi_payment_enabled') == 'yes';
859 929
860 930 if ($this->isMultiDurationEnabled() && $isMultiEnabled) {
861 - $duration = $duration ?? $this->getDefaultDuration();
931 + $duration = $duration ?: $this->getDefaultDuration();
862 932 return [Arr::get($paymentSettings, 'multi_payment_items.' . $duration)];
863 933 }
864 934
865 935 return Arr::get($paymentSettings, 'items', []);
@@ -864,24 +934,43 @@
864 934
865 935 return Arr::get($paymentSettings, 'items', []);
866 936 }
867 937
868 - public function getPricingTotal()
938 + public function getEventPrice($duration = null)
869 939 {
940 + if (!$this->isPaidEvent()) {
941 + return 0;
942 + }
943 +
944 + $paymentSettings = $this->getPaymentSettings();
945 +
946 + if ($this->isCartEnabled()) {
947 + return CartHelper::getCartProductPrice($paymentSettings, $duration, false);
948 + }
949 +
950 + if ($this->isWooEnabled()) {
951 + return $this->getWooProductPrice($duration);
952 + }
953 +
954 + return $this->getPricingTotal($duration);
955 + }
956 +
957 + public function getPricingTotal($duration = null)
958 + {
870 959 if (!$this->isPaymentEnabled()) {
871 960 return 0;
872 961 }
873 962
874 963 $total = 0;
875 - $items = $this->getPaymentItems();
964 + $items = $this->getPaymentItems($duration);
876 965 foreach ($items as $item) {
877 - $total += (int)$item['value'];
966 + $total += $item['value'];
878 967 }
879 968
880 969 return $total;
881 970 }
882 971
883 - public function getWooProductPrice()
972 + public function getWooProductPrice($duration = null)
884 973 {
885 974 $paymentSettings = $this->getPaymentSettings();
886 975
887 976 $productId = $paymentSettings['woo_product_id'];
@@ -886,9 +975,9 @@
886 975
887 976 $productId = $paymentSettings['woo_product_id'];
888 977
889 978 if (Arr::get($paymentSettings, 'multi_payment_enabled') == 'yes') {
890 - $duration = $this->getDefaultDuration();
979 + $duration = $duration ?? $this->getDefaultDuration();
891 980 $productId = Arr::get($paymentSettings, 'multi_payment_woo_ids.' . $duration);
892 981 }
893 982
894 983 $price = 0;
@@ -907,9 +996,9 @@
907 996 foreach ($productIds as $duration => $productId) {
908 997 $product = wc_get_product($productId);
909 998 if ($product) {
910 999 $productPrices[$duration] = [
911 - 'value' => $product->get_price()
1000 + 'value' => wc_price($product->get_price())
912 1001 ];
913 1002 }
914 1003 }
915 1004
@@ -922,17 +1011,16 @@
922 1011
923 1012 $driver = Arr::get($this->getPaymentSettings(), 'driver');
924 1013
925 1014 if ($driver == 'native' && $this->isPaymentEnabled()) {
926 - $currency = CurrenciesHelper::getGlobalCurrencySign();
1015 + $currencySettings = CurrenciesHelper::getGlobalCurrencySettings();
927 1016 $totalPayment = $this->getPricingTotal();
928 - $paymentHtml = $this->defaultPaymentIcon($currency, $totalPayment);
1017 + $paymentHtml = $this->defaultPaymentIcon($totalPayment, $currencySettings);
929 1018 }
930 1019
931 1020 if ($driver == 'woo' && $this->isWooEnabled()) {
932 - $currency = get_woocommerce_currency_symbol();
933 - $totalPayment = $this->getWooProductPrice();
934 - $paymentHtml = $this->defaultPaymentIcon($currency, $totalPayment);
1021 + $totalPayment = wc_price($this->getWooProductPrice());
1022 + $paymentHtml = $this->defaultPaymentIcon($totalPayment);
935 1023 }
936 1024
937 1025 return $paymentHtml;
938 1026 }
@@ -966,8 +1054,13 @@
966 1054 {
967 1055 return $this->isRoundRobin() && $this->isTeamCommonSchedule();
968 1056 }
969 1057
1058 + public function isCollectiveDefaultSchedule()
1059 + {
1060 + return $this->isCollective() && $this->isTeamDefaultSchedule();
1061 + }
1062 +
970 1063 private function getProcessedWeeklySlots($schedule)
971 1064 {
972 1065 $scheduleData = Arr::get($schedule, 'value.weekly_schedules', []);
973 1066 $scheduleTimezone = Arr::get($schedule, 'value.timezone', 'UTC');
@@ -987,15 +1080,17 @@
987 1080
988 1081 public function getWeeklySlots($hostId = null)
989 1082 {
990 1083 if ($hostId && !$this->isTeamCommonSchedule()) {
991 - $schedule = AvailabilityService::getDefaultSchedule($hostId);
1084 + $schedule = $this->getHostSchedule($hostId);
992 1085 return $this->getProcessedWeeklySlots($schedule);
993 1086 }
994 1087
995 1088 if ($this->availability_type === 'existing_schedule') {
996 - $schedule = Availability::findOrFail($this->availability_id);
997 - return $this->getProcessedWeeklySlots($schedule);
1089 + $schedule = Availability::find($this->availability_id);
1090 + if ($schedule) {
1091 + return $this->getProcessedWeeklySlots($schedule);
1092 + }
998 1093 }
999 1094
1000 1095 $scheduleData = Arr::get($this->settings, 'weekly_schedules', []);
1001 1096 $schedule = SanitizeService::weeklySchedules($scheduleData, 'UTC', $this->calendar->author_timezone);
@@ -1004,15 +1099,17 @@
1004 1099
1005 1100 public function getDateOverrides($hostId = null)
1006 1101 {
1007 1102 if ($hostId && !$this->isTeamCommonSchedule()) {
1008 - $schedule = AvailabilityService::getDefaultSchedule($hostId);
1103 + $schedule = $this->getHostSchedule($hostId);
1009 1104 return $this->getProcessedDateOverrides($schedule);
1010 1105 }
1011 1106
1012 1107 if ($this->availability_type === 'existing_schedule') {
1013 - $schedule = Availability::findOrFail($this->availability_id);
1014 - return $this->getProcessedDateOverrides($schedule);
1108 + $schedule = Availability::find($this->availability_id);
1109 + if ($schedule) {
1110 + return $this->getProcessedDateOverrides($schedule);
1111 + }
1015 1112 }
1016 1113
1017 1114 $scheduleData = Arr::get($this->settings, 'date_overrides', []);
1018 1115 $schedule = SanitizeService::slotDateOverrides($scheduleData, 'UTC', $this->calendar->author_timezone);
@@ -1052,8 +1149,9 @@
1052 1149 'enabled' => 'no',
1053 1150 'multi_payment_enabled' => 'no',
1054 1151 'stripe_enabled' => 'no',
1055 1152 'paypal_enabled' => 'no',
1153 + 'offline_enabled' => 'no',
1056 1154 'driver' => 'native',
1057 1155 'items' => [
1058 1156 [
1059 1157 'title' => __('Booking Fee', 'fluent-booking'),
@@ -1060,8 +1158,9 @@
1060 1158 'value' => 100
1061 1159 ]
1062 1160 ],
1063 1161 'woo_product_id' => '',
1162 + 'cart_product_id' => '',
1064 1163 'multi_payment_items' => [
1065 1164 $duration => [
1066 1165 'title' => __('Booking Fee', 'fluent-booking'),
1067 1166 'value' => 0
@@ -1068,16 +1167,41 @@
1068 1167 ]
1069 1168 ],
1070 1169 'multi_payment_woo_ids' => [
1071 1170 $duration => ''
1171 + ],
1172 + 'multi_payment_cart_ids' => [
1173 + $duration => ''
1072 1174 ]
1073 1175 ];
1074 1176
1177 + $defaults = apply_filters('fluent_booking/event_payment_settings_defaults', $defaults, $this);
1178 +
1075 1179 if (!$settings) {
1076 1180 $settings = $defaults;
1077 1181 }
1078 1182
1079 - return wp_parse_args($settings, $defaults);
1183 + $settings = wp_parse_args($settings, $defaults);
1184 +
1185 + return apply_filters('fluent_booking/get_event_payment_settings', $settings, $this);
1186 + }
1187 +
1188 + public function getHostSchedule($hostId)
1189 + {
1190 + $hostSchedules = Arr::get($this->settings, 'hosts_schedules', []);
1191 + if (isset($hostSchedules[$hostId])) {
1192 + return Availability::find($hostSchedules[$hostId]);
1193 + }
1194 + return AvailabilityService::getDefaultSchedule($hostId);
1195 + }
1196 +
1197 + public function getHostsSchedules() {
1198 + $hostIds = $this->getHostIds();
1199 + $hostSchedules = Arr::get($this->settings, 'hosts_schedules', []);
1200 + foreach ($hostIds as $hostId) {
1201 + $hostSchedules[$hostId] = $hostSchedules[$hostId] ?? AvailabilityService::getDefaultSchedule($hostId)['id'];
1202 + }
1203 + return $hostSchedules;
1080 1204 }
1081 1205
1082 1206 public function getCalendarEventsMeta()
1083 1207 {