PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / trunk
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution vtrunk
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
← All changes | app/Models/CalendarSlot.php +148 -34 1.5.21trunk 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 }
@@ -160,16 +173,26 @@
160 173 {
161 174 return $this->isGroup() || $this->isGroupEvent();
162 175 }
163 176
177 + public function isRecurringEvent()
178 + {
179 + return Arr::isTrue($this->getRecurringConfig(), 'enabled');
180 + }
181 +
164 182 public function isProEvent()
165 183 {
166 - return $this->isGroup() || $this->isTeamEvent() || $this->isOneOffEvent();
184 + return $this->isGroup() || $this->isTeamEvent() || $this->isOneOffEvent() || $this->allowMultiBooking();
167 185 }
168 186
187 + public function isMultiBooking()
188 + {
189 + return Arr::isTrue($this->settings, 'multiple_booking.enabled');
190 + }
191 +
169 192 public function allowMultiBooking()
170 193 {
171 - return Arr::isTrue($this->settings, 'multiple_booking.enabled');
194 + return $this->isMultiBooking() || $this->isRecurringEvent();
172 195 }
173 196
174 197 public function multiBookingLimit()
175 198 {
@@ -208,11 +231,19 @@
208 231 {
209 232 $teamMembers = [];
210 233 $teamMemberIds = $this->getHostIds();
211 234
235 + cache_users($teamMemberIds);
236 +
237 + $calendars = Calendar::whereIn('user_id', $teamMemberIds)
238 + ->where('type', 'simple')
239 + ->orderBy('id', 'desc')
240 + ->with(['metas', 'user', 'user.metas'])
241 + ->get()
242 + ->keyBy('user_id');
243 +
212 244 foreach ($teamMemberIds as $teamMemberId) {
213 - $calendar = Calendar::where('user_id', $teamMemberId)->where('type', 'simple')->first();
214 - if ($calendar) {
245 + if ($calendar = $calendars->get($teamMemberId)) {
215 246 $teamMembers[] = $calendar->getAuthorProfile($public);
216 247 }
217 248 }
218 249
@@ -218,8 +249,13 @@
218 249
219 250 return $teamMembers;
220 251 }
221 252
253 + public function getRecurringConfig()
254 + {
255 + return Arr::get($this->settings, 'recurring_config', []);
256 + }
257 +
222 258 public function isLocationFieldRequired()
223 259 {
224 260 $locationSettings = Arr::get($this, 'location_settings');
225 261
@@ -405,15 +441,17 @@
405 441
406 442 public function getScheduleTimezone($hostId = null)
407 443 {
408 444 if ($hostId && !$this->isTeamCommonSchedule()) {
409 - $schedule = AvailabilityService::getDefaultSchedule($hostId);
445 + $schedule = $this->getHostSchedule($hostId);
410 446 return Arr::get($schedule, 'value.timezone', 'UTC');
411 447 }
412 448
413 449 if ($this->availability_type == 'existing_schedule') {
414 - $schedule = Availability::findOrFail($this->availability_id);
415 - return Arr::get($schedule, 'value.timezone', 'UTC');
450 + $schedule = Availability::find($this->availability_id);
451 + if ($schedule) {
452 + return Arr::get($schedule, 'value.timezone', 'UTC');
453 + }
416 454 }
417 455
418 456 return $this->calendar->author_timezone;
419 457 }
@@ -534,8 +572,12 @@
534 572 public function getMinBookableDateTime($startDate = null, $timeZone = null)
535 573 {
536 574 $startDate = $startDate ?: gmdate('Y-m-d H:i:s'); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
537 575
576 + if ($timeZone) {
577 + $startDate = DateTimeHelper::convertToTimeZone($startDate, $timeZone, 'UTC');
578 + }
579 +
538 580 $rangeType = Arr::get($this->settings, 'range_type', 'range_days');
539 581
540 582 if ($rangeType == 'range_date_between') {
541 583 $range = Arr::get($this->settings, 'range_date_between', []);
@@ -541,8 +583,9 @@
541 583 $range = Arr::get($this->settings, 'range_date_between', []);
542 584 if (is_array($range) && count(array_filter($range)) == 2) {
543 585 if (strtotime($range[0]) >= strtotime($startDate)) {
544 586 $startDate = gmdate('Y-m-d H:i:s', strtotime($range[0])); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
587 + $startDate = DateTimeHelper::convertToTimeZone($startDate, $this->calendar->author_timezone, 'UTC');
545 588 }
546 589 }
547 590 }
548 591
@@ -602,9 +645,11 @@
602 645 if (!$conditions || empty($conditions['unit'])) {
603 646 return 0;
604 647 }
605 648
606 - return strtotime('+' . $conditions['value'] . ' ' . $conditions['unit'], 0) - strtotime('+0 seconds', 0);
649 + $value = max(0, (int) Arr::get($conditions, 'value', 0));
650 +
651 + return strtotime('+' . $value . ' ' . $conditions['unit'], 0) - strtotime('+0 seconds', 0);
607 652 }
608 653
609 654 public function getHostIds($hostId = null)
610 655 {
@@ -780,8 +825,10 @@
780 825
781 826 $conditionTime = $conditionValue * 60;
782 827 if ($conditionUnit == 'hours') {
783 828 $conditionTime = $conditionTime * 60;
829 + } elseif ($conditionUnit == 'days') {
830 + $conditionTime = $conditionTime * 60 * 24;
784 831 }
785 832
786 833 return $bookingStartTime - $bookingCreatedTime < $conditionTime;
787 834 }
@@ -823,12 +870,13 @@
823 870
824 871 return LocationService::getLocationIconHeadingHtml($default, $this);
825 872 }
826 873
827 - public function defaultPaymentIcon($currency, $amount)
874 + public function defaultPaymentIcon($amount, $currencySettings = [])
828 875 {
876 + $formattedAmount = $currencySettings ? fluentbookingFormattedAmount((float)$amount * 100, $currencySettings) : $amount;
829 877 $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>';
878 + $html = '<span class="fcal_slot_payment_icon">' . $svg . $formattedAmount . '</span>';
831 879 return $html;
832 880 }
833 881
834 882 public function isPaymentEnabled($duration = null)
@@ -845,13 +893,25 @@
845 893
846 894 return $this->type == 'paid' && Helper::isPaymentEnabled();
847 895 }
848 896
897 + public function isPaidEvent()
898 + {
899 + $paymentSettings = $this->getPaymentSettings();
900 +
901 + return $this->type != 'free' && Arr::get($paymentSettings, 'enabled') == 'yes';
902 + }
903 +
849 904 public function isWooEnabled()
850 905 {
851 906 return $this->type == 'woo' && defined('WC_PLUGIN_FILE');
852 907 }
853 908
909 + public function isCartEnabled()
910 + {
911 + return $this->type == 'cart' && defined('FLUENTCART_VERSION');
912 + }
913 +
854 914 public function getPaymentItems($duration = null)
855 915 {
856 916 $paymentSettings = $this->getPaymentSettings();
857 917
@@ -857,9 +917,9 @@
857 917
858 918 $isMultiEnabled = Arr::get($paymentSettings, 'multi_payment_enabled') == 'yes';
859 919
860 920 if ($this->isMultiDurationEnabled() && $isMultiEnabled) {
861 - $duration = $duration ?? $this->getDefaultDuration();
921 + $duration = $duration ?: $this->getDefaultDuration();
862 922 return [Arr::get($paymentSettings, 'multi_payment_items.' . $duration)];
863 923 }
864 924
865 925 return Arr::get($paymentSettings, 'items', []);
@@ -864,24 +924,43 @@
864 924
865 925 return Arr::get($paymentSettings, 'items', []);
866 926 }
867 927
868 - public function getPricingTotal()
928 + public function getEventPrice($duration = null)
869 929 {
930 + if (!$this->isPaidEvent()) {
931 + return 0;
932 + }
933 +
934 + $paymentSettings = $this->getPaymentSettings();
935 +
936 + if ($this->isCartEnabled()) {
937 + return CartHelper::getCartProductPrice($paymentSettings, $duration, false);
938 + }
939 +
940 + if ($this->isWooEnabled()) {
941 + return $this->getWooProductPrice($duration);
942 + }
943 +
944 + return $this->getPricingTotal($duration);
945 + }
946 +
947 + public function getPricingTotal($duration = null)
948 + {
870 949 if (!$this->isPaymentEnabled()) {
871 950 return 0;
872 951 }
873 952
874 953 $total = 0;
875 - $items = $this->getPaymentItems();
954 + $items = $this->getPaymentItems($duration);
876 955 foreach ($items as $item) {
877 - $total += (int)$item['value'];
956 + $total += $item['value'];
878 957 }
879 958
880 959 return $total;
881 960 }
882 961
883 - public function getWooProductPrice()
962 + public function getWooProductPrice($duration = null)
884 963 {
885 964 $paymentSettings = $this->getPaymentSettings();
886 965
887 966 $productId = $paymentSettings['woo_product_id'];
@@ -886,9 +965,9 @@
886 965
887 966 $productId = $paymentSettings['woo_product_id'];
888 967
889 968 if (Arr::get($paymentSettings, 'multi_payment_enabled') == 'yes') {
890 - $duration = $this->getDefaultDuration();
969 + $duration = $duration ?? $this->getDefaultDuration();
891 970 $productId = Arr::get($paymentSettings, 'multi_payment_woo_ids.' . $duration);
892 971 }
893 972
894 973 $price = 0;
@@ -907,9 +986,9 @@
907 986 foreach ($productIds as $duration => $productId) {
908 987 $product = wc_get_product($productId);
909 988 if ($product) {
910 989 $productPrices[$duration] = [
911 - 'value' => $product->get_price()
990 + 'value' => wc_price($product->get_price())
912 991 ];
913 992 }
914 993 }
915 994
@@ -922,17 +1001,16 @@
922 1001
923 1002 $driver = Arr::get($this->getPaymentSettings(), 'driver');
924 1003
925 1004 if ($driver == 'native' && $this->isPaymentEnabled()) {
926 - $currency = CurrenciesHelper::getGlobalCurrencySign();
1005 + $currencySettings = CurrenciesHelper::getGlobalCurrencySettings();
927 1006 $totalPayment = $this->getPricingTotal();
928 - $paymentHtml = $this->defaultPaymentIcon($currency, $totalPayment);
1007 + $paymentHtml = $this->defaultPaymentIcon($totalPayment, $currencySettings);
929 1008 }
930 1009
931 1010 if ($driver == 'woo' && $this->isWooEnabled()) {
932 - $currency = get_woocommerce_currency_symbol();
933 - $totalPayment = $this->getWooProductPrice();
934 - $paymentHtml = $this->defaultPaymentIcon($currency, $totalPayment);
1011 + $totalPayment = wc_price($this->getWooProductPrice());
1012 + $paymentHtml = $this->defaultPaymentIcon($totalPayment);
935 1013 }
936 1014
937 1015 return $paymentHtml;
938 1016 }
@@ -966,8 +1044,13 @@
966 1044 {
967 1045 return $this->isRoundRobin() && $this->isTeamCommonSchedule();
968 1046 }
969 1047
1048 + public function isCollectiveDefaultSchedule()
1049 + {
1050 + return $this->isCollective() && $this->isTeamDefaultSchedule();
1051 + }
1052 +
970 1053 private function getProcessedWeeklySlots($schedule)
971 1054 {
972 1055 $scheduleData = Arr::get($schedule, 'value.weekly_schedules', []);
973 1056 $scheduleTimezone = Arr::get($schedule, 'value.timezone', 'UTC');
@@ -987,15 +1070,17 @@
987 1070
988 1071 public function getWeeklySlots($hostId = null)
989 1072 {
990 1073 if ($hostId && !$this->isTeamCommonSchedule()) {
991 - $schedule = AvailabilityService::getDefaultSchedule($hostId);
1074 + $schedule = $this->getHostSchedule($hostId);
992 1075 return $this->getProcessedWeeklySlots($schedule);
993 1076 }
994 1077
995 1078 if ($this->availability_type === 'existing_schedule') {
996 - $schedule = Availability::findOrFail($this->availability_id);
997 - return $this->getProcessedWeeklySlots($schedule);
1079 + $schedule = Availability::find($this->availability_id);
1080 + if ($schedule) {
1081 + return $this->getProcessedWeeklySlots($schedule);
1082 + }
998 1083 }
999 1084
1000 1085 $scheduleData = Arr::get($this->settings, 'weekly_schedules', []);
1001 1086 $schedule = SanitizeService::weeklySchedules($scheduleData, 'UTC', $this->calendar->author_timezone);
@@ -1004,15 +1089,17 @@
1004 1089
1005 1090 public function getDateOverrides($hostId = null)
1006 1091 {
1007 1092 if ($hostId && !$this->isTeamCommonSchedule()) {
1008 - $schedule = AvailabilityService::getDefaultSchedule($hostId);
1093 + $schedule = $this->getHostSchedule($hostId);
1009 1094 return $this->getProcessedDateOverrides($schedule);
1010 1095 }
1011 1096
1012 1097 if ($this->availability_type === 'existing_schedule') {
1013 - $schedule = Availability::findOrFail($this->availability_id);
1014 - return $this->getProcessedDateOverrides($schedule);
1098 + $schedule = Availability::find($this->availability_id);
1099 + if ($schedule) {
1100 + return $this->getProcessedDateOverrides($schedule);
1101 + }
1015 1102 }
1016 1103
1017 1104 $scheduleData = Arr::get($this->settings, 'date_overrides', []);
1018 1105 $schedule = SanitizeService::slotDateOverrides($scheduleData, 'UTC', $this->calendar->author_timezone);
@@ -1052,8 +1139,9 @@
1052 1139 'enabled' => 'no',
1053 1140 'multi_payment_enabled' => 'no',
1054 1141 'stripe_enabled' => 'no',
1055 1142 'paypal_enabled' => 'no',
1143 + 'offline_enabled' => 'no',
1056 1144 'driver' => 'native',
1057 1145 'items' => [
1058 1146 [
1059 1147 'title' => __('Booking Fee', 'fluent-booking'),
@@ -1060,8 +1148,9 @@
1060 1148 'value' => 100
1061 1149 ]
1062 1150 ],
1063 1151 'woo_product_id' => '',
1152 + 'cart_product_id' => '',
1064 1153 'multi_payment_items' => [
1065 1154 $duration => [
1066 1155 'title' => __('Booking Fee', 'fluent-booking'),
1067 1156 'value' => 0
@@ -1068,16 +1157,41 @@
1068 1157 ]
1069 1158 ],
1070 1159 'multi_payment_woo_ids' => [
1071 1160 $duration => ''
1161 + ],
1162 + 'multi_payment_cart_ids' => [
1163 + $duration => ''
1072 1164 ]
1073 1165 ];
1074 1166
1167 + $defaults = apply_filters('fluent_booking/event_payment_settings_defaults', $defaults, $this);
1168 +
1075 1169 if (!$settings) {
1076 1170 $settings = $defaults;
1077 1171 }
1078 1172
1079 - return wp_parse_args($settings, $defaults);
1173 + $settings = wp_parse_args($settings, $defaults);
1174 +
1175 + return apply_filters('fluent_booking/get_event_payment_settings', $settings, $this);
1176 + }
1177 +
1178 + public function getHostSchedule($hostId)
1179 + {
1180 + $hostSchedules = Arr::get($this->settings, 'hosts_schedules', []);
1181 + if (isset($hostSchedules[$hostId])) {
1182 + return Availability::find($hostSchedules[$hostId]);
1183 + }
1184 + return AvailabilityService::getDefaultSchedule($hostId);
1185 + }
1186 +
1187 + public function getHostsSchedules() {
1188 + $hostIds = $this->getHostIds();
1189 + $hostSchedules = Arr::get($this->settings, 'hosts_schedules', []);
1190 + foreach ($hostIds as $hostId) {
1191 + $hostSchedules[$hostId] = $hostSchedules[$hostId] ?? AvailabilityService::getDefaultSchedule($hostId)['id'];
1192 + }
1193 + return $hostSchedules;
1080 1194 }
1081 1195
1082 1196 public function getCalendarEventsMeta()
1083 1197 {