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 +127 -29 1.6.0 → 2.5.0 View file →
@@ -2,8 +2,9 @@
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;
@@ -9,9 +10,9 @@
9 10 use FluentBooking\App\Services\DateTimeHelper;
10 11 use FluentBooking\App\Services\Helper;
11 12 use FluentBooking\App\Services\CurrenciesHelper;
12 13 use FluentBooking\App\Services\LocationService;
13 -use FluentBooking\Framework\Support\Arr;
14 +use FluentBooking\App\Services\Integrations\FluentCart\CartHelper;
14 15
15 16 class CalendarSlot extends Model
16 17 {
17 18 protected $table = 'fcal_calendar_events';
@@ -89,9 +90,13 @@
89 90 public function getShortDescriptionAttribute()
90 91 {
91 92 $description = preg_replace('/<[^>]*>/', ' ', $this->getDescription());
92 93
93 - return Helper::excerpt($description);
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);
94 99 }
95 100
96 101 public function calendar()
97 102 {
@@ -113,8 +118,13 @@
113 118 return $this->hasMany(Meta::class, 'object_id', 'id')
114 119 ->whereIn('object_type', ['calendar_event', 'integration']);
115 120 }
116 121
122 + public function isOneToOne()
123 + {
124 + return $this->event_type == 'single';
125 + }
126 +
117 127 public function isGroup()
118 128 {
119 129 return $this->event_type == 'group';
120 130 }
@@ -128,8 +138,18 @@
128 138 {
129 139 return $this->event_type == 'group_event';
130 140 }
131 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 +
132 152 public function isRoundRobin()
133 153 {
134 154 return $this->event_type == 'round_robin';
135 155 }
@@ -163,16 +183,26 @@
163 183 {
164 184 return $this->isGroup() || $this->isGroupEvent();
165 185 }
166 186
187 + public function isRecurringEvent()
188 + {
189 + return Arr::isTrue($this->getRecurringConfig(), 'enabled');
190 + }
191 +
167 192 public function isProEvent()
168 193 {
169 - return $this->isGroup() || $this->isTeamEvent() || $this->isOneOffEvent();
194 + return $this->isGroup() || $this->isTeamEvent() || $this->isOneOffEvent() || $this->allowMultiBooking();
170 195 }
171 196
197 + public function isMultiBooking()
198 + {
199 + return Arr::isTrue($this->settings, 'multiple_booking.enabled');
200 + }
201 +
172 202 public function allowMultiBooking()
173 203 {
174 - return Arr::isTrue($this->settings, 'multiple_booking.enabled');
204 + return $this->isMultiBooking() || $this->isRecurringEvent();
175 205 }
176 206
177 207 public function multiBookingLimit()
178 208 {
@@ -211,11 +241,19 @@
211 241 {
212 242 $teamMembers = [];
213 243 $teamMemberIds = $this->getHostIds();
214 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 +
215 254 foreach ($teamMemberIds as $teamMemberId) {
216 - $calendar = Calendar::where('user_id', $teamMemberId)->where('type', 'simple')->first();
217 - if ($calendar) {
255 + if ($calendar = $calendars->get($teamMemberId)) {
218 256 $teamMembers[] = $calendar->getAuthorProfile($public);
219 257 }
220 258 }
221 259
@@ -221,8 +259,13 @@
221 259
222 260 return $teamMembers;
223 261 }
224 262
263 + public function getRecurringConfig()
264 + {
265 + return Arr::get($this->settings, 'recurring_config', []);
266 + }
267 +
225 268 public function isLocationFieldRequired()
226 269 {
227 270 $locationSettings = Arr::get($this, 'location_settings');
228 271
@@ -413,10 +456,12 @@
413 456 return Arr::get($schedule, 'value.timezone', 'UTC');
414 457 }
415 458
416 459 if ($this->availability_type == 'existing_schedule') {
417 - $schedule = Availability::findOrFail($this->availability_id);
418 - 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 + }
419 464 }
420 465
421 466 return $this->calendar->author_timezone;
422 467 }
@@ -537,8 +582,12 @@
537 582 public function getMinBookableDateTime($startDate = null, $timeZone = null)
538 583 {
539 584 $startDate = $startDate ?: gmdate('Y-m-d H:i:s'); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
540 585
586 + if ($timeZone) {
587 + $startDate = DateTimeHelper::convertToTimeZone($startDate, $timeZone, 'UTC');
588 + }
589 +
541 590 $rangeType = Arr::get($this->settings, 'range_type', 'range_days');
542 591
543 592 if ($rangeType == 'range_date_between') {
544 593 $range = Arr::get($this->settings, 'range_date_between', []);
@@ -544,8 +593,9 @@
544 593 $range = Arr::get($this->settings, 'range_date_between', []);
545 594 if (is_array($range) && count(array_filter($range)) == 2) {
546 595 if (strtotime($range[0]) >= strtotime($startDate)) {
547 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');
548 598 }
549 599 }
550 600 }
551 601
@@ -605,9 +655,11 @@
605 655 if (!$conditions || empty($conditions['unit'])) {
606 656 return 0;
607 657 }
608 658
609 - 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);
610 662 }
611 663
612 664 public function getHostIds($hostId = null)
613 665 {
@@ -783,8 +835,10 @@
783 835
784 836 $conditionTime = $conditionValue * 60;
785 837 if ($conditionUnit == 'hours') {
786 838 $conditionTime = $conditionTime * 60;
839 + } elseif ($conditionUnit == 'days') {
840 + $conditionTime = $conditionTime * 60 * 24;
787 841 }
788 842
789 843 return $bookingStartTime - $bookingCreatedTime < $conditionTime;
790 844 }
@@ -826,12 +880,13 @@
826 880
827 881 return LocationService::getLocationIconHeadingHtml($default, $this);
828 882 }
829 883
830 - public function defaultPaymentIcon($currency, $amount)
884 + public function defaultPaymentIcon($amount, $currencySettings = [])
831 885 {
886 + $formattedAmount = $currencySettings ? fluentbookingFormattedAmount((float)$amount * 100, $currencySettings) : $amount;
832 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>';
833 - $html = '<span class="fcal_slot_payment_icon">' . $svg . $currency . $amount . '</span>';
888 + $html = '<span class="fcal_slot_payment_icon">' . $svg . $formattedAmount . '</span>';
834 889 return $html;
835 890 }
836 891
837 892 public function isPaymentEnabled($duration = null)
@@ -848,13 +903,25 @@
848 903
849 904 return $this->type == 'paid' && Helper::isPaymentEnabled();
850 905 }
851 906
907 + public function isPaidEvent()
908 + {
909 + $paymentSettings = $this->getPaymentSettings();
910 +
911 + return $this->type != 'free' && Arr::get($paymentSettings, 'enabled') == 'yes';
912 + }
913 +
852 914 public function isWooEnabled()
853 915 {
854 916 return $this->type == 'woo' && defined('WC_PLUGIN_FILE');
855 917 }
856 918
919 + public function isCartEnabled()
920 + {
921 + return $this->type == 'cart' && defined('FLUENTCART_VERSION');
922 + }
923 +
857 924 public function getPaymentItems($duration = null)
858 925 {
859 926 $paymentSettings = $this->getPaymentSettings();
860 927
@@ -860,9 +927,9 @@
860 927
861 928 $isMultiEnabled = Arr::get($paymentSettings, 'multi_payment_enabled') == 'yes';
862 929
863 930 if ($this->isMultiDurationEnabled() && $isMultiEnabled) {
864 - $duration = $duration ?? $this->getDefaultDuration();
931 + $duration = $duration ?: $this->getDefaultDuration();
865 932 return [Arr::get($paymentSettings, 'multi_payment_items.' . $duration)];
866 933 }
867 934
868 935 return Arr::get($paymentSettings, 'items', []);
@@ -867,24 +934,43 @@
867 934
868 935 return Arr::get($paymentSettings, 'items', []);
869 936 }
870 937
871 - public function getPricingTotal()
938 + public function getEventPrice($duration = null)
872 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 + {
873 959 if (!$this->isPaymentEnabled()) {
874 960 return 0;
875 961 }
876 962
877 963 $total = 0;
878 - $items = $this->getPaymentItems();
964 + $items = $this->getPaymentItems($duration);
879 965 foreach ($items as $item) {
880 - $total += (int)$item['value'];
966 + $total += $item['value'];
881 967 }
882 968
883 969 return $total;
884 970 }
885 971
886 - public function getWooProductPrice()
972 + public function getWooProductPrice($duration = null)
887 973 {
888 974 $paymentSettings = $this->getPaymentSettings();
889 975
890 976 $productId = $paymentSettings['woo_product_id'];
@@ -889,9 +975,9 @@
889 975
890 976 $productId = $paymentSettings['woo_product_id'];
891 977
892 978 if (Arr::get($paymentSettings, 'multi_payment_enabled') == 'yes') {
893 - $duration = $this->getDefaultDuration();
979 + $duration = $duration ?? $this->getDefaultDuration();
894 980 $productId = Arr::get($paymentSettings, 'multi_payment_woo_ids.' . $duration);
895 981 }
896 982
897 983 $price = 0;
@@ -910,9 +996,9 @@
910 996 foreach ($productIds as $duration => $productId) {
911 997 $product = wc_get_product($productId);
912 998 if ($product) {
913 999 $productPrices[$duration] = [
914 - 'value' => $product->get_price()
1000 + 'value' => wc_price($product->get_price())
915 1001 ];
916 1002 }
917 1003 }
918 1004
@@ -925,17 +1011,16 @@
925 1011
926 1012 $driver = Arr::get($this->getPaymentSettings(), 'driver');
927 1013
928 1014 if ($driver == 'native' && $this->isPaymentEnabled()) {
929 - $currency = CurrenciesHelper::getGlobalCurrencySign();
1015 + $currencySettings = CurrenciesHelper::getGlobalCurrencySettings();
930 1016 $totalPayment = $this->getPricingTotal();
931 - $paymentHtml = $this->defaultPaymentIcon($currency, $totalPayment);
1017 + $paymentHtml = $this->defaultPaymentIcon($totalPayment, $currencySettings);
932 1018 }
933 1019
934 1020 if ($driver == 'woo' && $this->isWooEnabled()) {
935 - $currency = get_woocommerce_currency_symbol();
936 - $totalPayment = $this->getWooProductPrice();
937 - $paymentHtml = $this->defaultPaymentIcon($currency, $totalPayment);
1021 + $totalPayment = wc_price($this->getWooProductPrice());
1022 + $paymentHtml = $this->defaultPaymentIcon($totalPayment);
938 1023 }
939 1024
940 1025 return $paymentHtml;
941 1026 }
@@ -1000,10 +1085,12 @@
1000 1085 return $this->getProcessedWeeklySlots($schedule);
1001 1086 }
1002 1087
1003 1088 if ($this->availability_type === 'existing_schedule') {
1004 - $schedule = Availability::findOrFail($this->availability_id);
1005 - return $this->getProcessedWeeklySlots($schedule);
1089 + $schedule = Availability::find($this->availability_id);
1090 + if ($schedule) {
1091 + return $this->getProcessedWeeklySlots($schedule);
1092 + }
1006 1093 }
1007 1094
1008 1095 $scheduleData = Arr::get($this->settings, 'weekly_schedules', []);
1009 1096 $schedule = SanitizeService::weeklySchedules($scheduleData, 'UTC', $this->calendar->author_timezone);
@@ -1017,10 +1104,12 @@
1017 1104 return $this->getProcessedDateOverrides($schedule);
1018 1105 }
1019 1106
1020 1107 if ($this->availability_type === 'existing_schedule') {
1021 - $schedule = Availability::findOrFail($this->availability_id);
1022 - return $this->getProcessedDateOverrides($schedule);
1108 + $schedule = Availability::find($this->availability_id);
1109 + if ($schedule) {
1110 + return $this->getProcessedDateOverrides($schedule);
1111 + }
1023 1112 }
1024 1113
1025 1114 $scheduleData = Arr::get($this->settings, 'date_overrides', []);
1026 1115 $schedule = SanitizeService::slotDateOverrides($scheduleData, 'UTC', $this->calendar->author_timezone);
@@ -1060,8 +1149,9 @@
1060 1149 'enabled' => 'no',
1061 1150 'multi_payment_enabled' => 'no',
1062 1151 'stripe_enabled' => 'no',
1063 1152 'paypal_enabled' => 'no',
1153 + 'offline_enabled' => 'no',
1064 1154 'driver' => 'native',
1065 1155 'items' => [
1066 1156 [
1067 1157 'title' => __('Booking Fee', 'fluent-booking'),
@@ -1068,8 +1158,9 @@
1068 1158 'value' => 100
1069 1159 ]
1070 1160 ],
1071 1161 'woo_product_id' => '',
1162 + 'cart_product_id' => '',
1072 1163 'multi_payment_items' => [
1073 1164 $duration => [
1074 1165 'title' => __('Booking Fee', 'fluent-booking'),
1075 1166 'value' => 0
@@ -1076,19 +1167,26 @@
1076 1167 ]
1077 1168 ],
1078 1169 'multi_payment_woo_ids' => [
1079 1170 $duration => ''
1171 + ],
1172 + 'multi_payment_cart_ids' => [
1173 + $duration => ''
1080 1174 ]
1081 1175 ];
1082 1176
1177 + $defaults = apply_filters('fluent_booking/event_payment_settings_defaults', $defaults, $this);
1178 +
1083 1179 if (!$settings) {
1084 1180 $settings = $defaults;
1085 1181 }
1086 1182
1087 - 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);
1088 1186 }
1089 1187
1090 - public function getHostSchedule($hostId)
1188 + public function getHostSchedule($hostId)
1091 1189 {
1092 1190 $hostSchedules = Arr::get($this->settings, 'hosts_schedules', []);
1093 1191 if (isset($hostSchedules[$hostId])) {
1094 1192 return Availability::find($hostSchedules[$hostId]);