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/Http/Controllers/CalendarController.php +112 -59 2.1.2trunk View file →
@@ -1,8 +1,9 @@
1 1 <?php
2 2
3 3 namespace FluentBooking\App\Http\Controllers;
4 4
5 +use FluentBooking\App\Models\Availability;
5 6 use FluentBooking\App\Models\Calendar;
6 7 use FluentBooking\App\Models\CalendarSlot;
7 8 use FluentBooking\App\Services\Helper;
8 9 use FluentBooking\App\Services\LandingPage\LandingPageHelper;
@@ -32,9 +33,9 @@
32 33 $query->where('title', 'LIKE', '%' . $search . '%');
33 34 }
34 35 };
35 36
36 - $calendarsQuery = Calendar::with(['slots' => function($query) use ($applySearchFilter) {
37 + $calendarsQuery = Calendar::with(['metas', 'slots' => function($query) use ($applySearchFilter) {
37 38 $query->where($applySearchFilter);
38 39 }])
39 40 ->where('status', '!=', 'expired');
40 41
@@ -60,12 +61,16 @@
60 61 foreach ($calendars as $calendar) {
61 62 $calendar->author_profile = $calendar->getAuthorProfile();
62 63 $calendar->public_url = $calendar->getLandingPageUrl();
63 64 $calendar->event_order = $calendar->getMeta('event_order');
64 - foreach ($calendar->slots as $key => $slot) {
65 - if (!$hasPermission && !CalendarEventService::isSharedCalendarEvent($slot)) {
66 - unset($calendar->slots[$key]);
67 - }
65 +
66 + if (!$hasPermission) {
67 + $calendar->setRelation('slots', $calendar->slots->filter(function ($slot) {
68 + return CalendarEventService::isSharedCalendarEvent($slot);
69 + })->values());
70 + }
71 +
72 + foreach ($calendar->slots as $slot) {
68 73 $slot->setRelation('calendar', $calendar);
69 74 $slot->shortcode = '[fluent_booking id="' . $slot->id . '"]';
70 75 $slot->public_url = $slot->getPublicUrl();
71 76 $slot->duration = $slot->getDefaultDuration();
@@ -76,9 +81,9 @@
76 81 $slot->unsetRelation('calendar');
77 82 }
78 83
79 84 if(empty($calendar->author_profile['ID'])) {
80 - $calendar->generic_error = '<p style="color: red; margin:0;">Connected Host user is missing</p>';
85 + $calendar->generic_error = '<p style="color: var(--fcal-danger-fg); margin:0;">Connected Host user is missing</p>';
81 86 }
82 87
83 88 do_action_ref_array('fluent_booking/calendar', [&$calendar, 'lists']);
84 89 }
@@ -168,9 +173,13 @@
168 173 $installableAddons = SanitizeService::sanitizeAddons($onboardinFeatures);
169 174 OnboardingService::installAddons($installableAddons);
170 175 }
171 176
172 - $type = sanitize_text_field(Arr::get($data, 'type', 'simple'));
177 + $type = SanitizeService::checkCollection(
178 + sanitize_text_field(Arr::get($data, 'type', 'simple')),
179 + ['simple', 'team', 'event'],
180 + 'simple'
181 + );
173 182
174 183 $isHostCalendar = $type == 'simple' ? true : false;
175 184
176 185 if ($isHostCalendar && Calendar::where('user_id', $user->ID)->where('type', 'simple')->first()) {
@@ -192,10 +201,30 @@
192 201
193 202 if (!$isHostCalendar) {
194 203 $title = sanitize_text_field(Arr::get($data, 'title', ''));
195 204 $data['slug'] = sanitize_title($title, '', 'display');
196 - $teamMembers = array_map('intval', Arr::get($slot, 'settings.team_members', []));
197 - if (!in_array($user->ID, $teamMembers)) {
205 + $teamMembers = array_values(array_filter(
206 + array_map('intval', (array) Arr::get($slot, 'settings.team_members', []))
207 + ));
208 +
209 + cache_users($teamMembers);
210 +
211 + foreach ($teamMembers as $memberId) {
212 + if (!get_user_by('ID', $memberId)) {
213 + return $this->sendError([
214 + 'message' => __('Invalid Team Member', 'fluent-booking')
215 + ], 422);
216 + }
217 + }
218 +
219 + if (!in_array($user->ID, $teamMembers, true)) {
220 + // Same privileged act as passing an explicit user_id above; gate it identically.
221 + if (!PermissionManager::userCan(['manage_all_data', 'invite_team_members'])) {
222 + return $this->sendError([
223 + 'message' => __('You are not allowed to create a calendar for another user', 'fluent-booking')
224 + ], 403);
225 + }
226 +
198 227 $user = get_user_by('ID', reset($teamMembers));
199 228 if (!$user) {
200 229 return $this->sendError([
201 230 'message' => __('Invalid Team Member', 'fluent-booking')
@@ -290,9 +319,28 @@
290 319 $query->where('status', '!=', 'expired');
291 320 }])->findOrFail($calendarId);
292 321
293 322 $calendar->author_profile = $calendar->getAuthorProfile();
323 + $calendar->event_order = $calendar->getMeta('event_order');
294 324
325 + if (!PermissionManager::hasAllCalendarAccess(true)) {
326 + $calendar->setRelation('slots', $calendar->slots->filter(function ($slot) {
327 + return CalendarEventService::isSharedCalendarEvent($slot);
328 + })->values());
329 + }
330 +
331 + foreach ($calendar->slots as $slot) {
332 + $slot->setRelation('calendar', $calendar);
333 + $slot->shortcode = '[fluent_booking id="' . $slot->id . '"]';
334 + $slot->public_url = $slot->getPublicUrl();
335 + $slot->duration = $slot->getDefaultDuration();
336 + $slot->price_total = $slot->getEventPrice();
337 + $slot->location_fields = $slot->getLocationFields();
338 + $slot->author_profiles = $slot->isMultiHostEvent() ? $slot->getAuthorProfiles() : [];
339 + do_action_ref_array('fluent_booking/calendar_slot', [&$slot]);
340 + $slot->unsetRelation('calendar');
341 + }
342 +
295 343 $data = [
296 344 'calendar' => $calendar
297 345 ];
298 346
@@ -487,9 +535,9 @@
487 535
488 536 $availability = AvailabilityService::getDefaultSchedule($calendar->user_id);
489 537
490 538 $slotData = [
491 - 'title' => $slot['title'],
539 + 'title' => sanitize_text_field($slot['title']),
492 540 'slug' => Helper::generateSlotSlug($slot['duration'] . 'min', $calendar),
493 541 'calendar_id' => $calendar->id,
494 542 'user_id' => $calendar->user_id,
495 543 'duration' => (int)$slot['duration'],
@@ -625,19 +673,69 @@
625 673 'range_date_between' => SanitizeService::rangeDateBetween(Arr::get($data, 'range_date_between', ['', ''])),
626 674 'common_schedule' => Arr::isTrue($data, 'common_schedule', false)
627 675 ];
628 676
677 + $hostsSchedules = [];
678 +
629 679 if ($event->isTeamEvent()) {
630 - $eventSettings['hosts_schedules'] = array_map('intval', array_combine(
680 + $hostsSchedules = array_map('intval', array_combine(
631 681 array_map('intval', array_keys(Arr::get($data, 'hosts_schedules', []))),
632 682 array_map('intval', Arr::get($data, 'hosts_schedules', []))
633 683 ));
634 684 }
635 685
686 + $availabilityId = (int)Arr::get($data, 'availability_id');
687 + $availabilityType = SanitizeService::checkCollection(Arr::get($data, 'availability_type'), ['existing_schedule', 'custom']);
688 +
689 + $submittedIds = array_values(array_filter(array_unique(array_merge(
690 + [$availabilityType === 'existing_schedule' ? $availabilityId : 0],
691 + array_values($hostsSchedules)
692 + ))));
693 +
694 + $usableIds = [];
695 + $scheduleOwners = [];
696 +
697 + if ($submittedIds) {
698 + $usableIds = array_map('intval', AvailabilityService::usableAvailabilityQuery()
699 + ->whereIn('id', $submittedIds)->pluck('id')->toArray());
700 +
701 + $scheduleOwners = array_map('intval', Availability::whereIn('id', $submittedIds)
702 + ->pluck('object_id', 'id')->toArray());
703 + }
704 +
705 + foreach ($hostsSchedules as $hostId => $scheduleId) {
706 + if (($scheduleOwners[$scheduleId] ?? 0) === (int)$hostId) {
707 + continue;
708 + }
709 +
710 + if (!in_array($scheduleId, $usableIds, true)) {
711 + return $this->sendError([
712 + 'message' => __('You are not allowed to use the selected schedule', 'fluent-booking')
713 + ], 403);
714 + }
715 + }
716 +
717 + if ($hostsSchedules) {
718 + $eventSettings['hosts_schedules'] = $hostsSchedules;
719 + }
720 +
721 + $eventHostIds = array_map('intval', array_merge(
722 + $event->getHostIds(),
723 + [$event->user_id, $event->calendar->user_id]
724 + ));
725 +
726 + if ($availabilityType === 'existing_schedule' && $availabilityId
727 + && !in_array($availabilityId, $usableIds, true)
728 + && !in_array($scheduleOwners[$availabilityId] ?? 0, $eventHostIds, true)) {
729 + return $this->sendError([
730 + 'message' => __('You are not allowed to use the selected schedule', 'fluent-booking')
731 + ], 403);
732 + }
733 +
636 734 $event->settings = $eventSettings;
637 735
638 - $event->availability_id = (int)Arr::get($data, 'availability_id');
639 - $event->availability_type = SanitizeService::checkCollection(Arr::get($data, 'availability_type'), ['existing_schedule', 'custom']);
736 + $event->availability_id = $availabilityId;
737 + $event->availability_type = $availabilityType;
640 738
641 739 $event->save();
642 740
643 741 return [
@@ -852,54 +950,9 @@
852 950 $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);
853 951
854 952 $bookingFields = $request->get('booking_fields');
855 953
856 - $optionRequiredFields = ['dropdown', 'radio', 'checkbox-group', 'multi-select'];
857 -
858 - $formattedFields = [];
859 -
860 - $textFields = ['type', 'name', 'label', 'placeholder', 'limit', 'help_text', 'date_format', 'min_date', 'max_date'];
861 - $booleanFields = ['enabled', 'required', 'system_defined', 'disable_alter', 'is_sms_number'];
862 -
863 - foreach ($bookingFields as $value) {
864 - if (empty($value['name'])) {
865 - $value['name'] = BookingFieldService::generateFieldName($calendarEvent, $value['label']);
866 - } else {
867 - $value['name'] = BookingFieldService::maybeGenerateFieldName($calendarEvent, $value);
868 - }
869 -
870 - $textValues = array_map('sanitize_text_field', Arr::only($value, $textFields));
871 -
872 - $booleanValues = array_map(function ($valueItem) {
873 - return $valueItem === true || $valueItem === 'true' || $valueItem == 1;
874 - }, Arr::only($value, $booleanFields));
875 -
876 - $formattedField = array_merge($textValues, $booleanValues);
877 -
878 - $fieldType = Arr::get($value, 'type');
879 -
880 - $formattedField['index'] = (int)Arr::get($value, 'index');
881 - if (in_array($fieldType, $optionRequiredFields)) {
882 - $sanitizedOptions = array_map('sanitize_text_field', Arr::get($value, 'options'));
883 - $formattedField['options'] = $sanitizedOptions;
884 - }
885 - if ($fieldType == 'file') {
886 - $formattedField['max_file_allow'] = intval(Arr::get($value, 'max_file_allow'));
887 - $formattedField['allow_file_types'] = array_map('sanitize_text_field', Arr::get($value, 'allow_file_types'));
888 - $formattedField['file_size_value'] = intval(Arr::get($value, 'file_size_value'));
889 - $formattedField['file_size_unit'] = SanitizeService::checkCollection(Arr::get($value, 'file_size_unit'), ['kb','mb']);
890 - }
891 - if ($fieldType == 'hidden') {
892 - $formattedField['default_value'] = sanitize_text_field(Arr::get($value, 'default_value'));
893 - }
894 - if ($fieldType == 'terms-and-conditions') {
895 - $formattedField['terms_and_conditions'] = wp_kses_post(Arr::get($value, 'terms_and_conditions'));
896 - }
897 -
898 - $formattedField = apply_filters('fluent_booking/save_event_booking_field_' . $fieldType, $formattedField, $value, $calendarEvent);
899 -
900 - $formattedFields[] = $formattedField;
901 - }
954 + $formattedFields = BookingFieldService::sanitizeBookingFields($bookingFields, $calendarEvent);
902 955
903 956 $calendarEvent->setBookingFields($formattedFields);
904 957
905 958 return [