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 +255 -122 1.5.10trunk View file →
@@ -1,19 +1,19 @@
1 1 <?php
2 2
3 3 namespace FluentBooking\App\Http\Controllers;
4 4
5 -use FluentBooking\App\Models\Booking;
5 +use FluentBooking\App\Models\Availability;
6 6 use FluentBooking\App\Models\Calendar;
7 7 use FluentBooking\App\Models\CalendarSlot;
8 -use FluentBooking\App\Models\Availability;
9 8 use FluentBooking\App\Services\Helper;
10 -use FluentBooking\App\Services\CurrenciesHelper;
11 9 use FluentBooking\App\Services\LandingPage\LandingPageHelper;
12 10 use FluentBooking\App\Services\PermissionManager;
13 11 use FluentBooking\App\Services\AvailabilityService;
14 12 use FluentBooking\App\Services\SanitizeService;
15 13 use FluentBooking\App\Services\CalendarService;
14 +use FluentBooking\App\Services\OnboardingService;
15 +use FluentBooking\App\Services\CalendarEventService;
16 16 use FluentBooking\App\Services\BookingFieldService;
17 17 use FluentBooking\App\Hooks\Handlers\AdminMenuHandler;
18 18 use FluentBooking\Framework\Http\Request\Request;
19 19 use FluentBooking\Framework\Support\Arr;
@@ -33,23 +33,28 @@
33 33 $query->where('title', 'LIKE', '%' . $search . '%');
34 34 }
35 35 };
36 36
37 - $calendarsQuery = Calendar::with(['slots' => function($query) use ($applySearchFilter) {
38 - $query->where('status', '!=', 'expired')
39 - ->where($applySearchFilter);
40 - }])
41 - ->where('status', '!=', 'expired')
42 - ->whereHas('slots', $applySearchFilter);
37 + $calendarsQuery = Calendar::with(['metas', 'slots' => function($query) use ($applySearchFilter) {
38 + $query->where($applySearchFilter);
39 + }])
40 + ->where('status', '!=', 'expired');
43 41
42 + if (!empty($search)) {
43 + $calendarsQuery->whereHas('slots', $applySearchFilter);
44 + }
45 +
44 46 if (!empty($calendarType) && $calendarType != 'all') {
45 - $calendarsQuery = $calendarsQuery->where('type', $calendarType);
47 + $calendarsQuery->where('type', $calendarType);
46 48 }
47 49
48 50 $calendarsQuery = $calendarsQuery->latest();
49 51
50 - if (!PermissionManager::hasAllCalendarAccess(true)) {
51 - $calendarsQuery->where('user_id', get_current_user_id());
52 + $hasPermission = PermissionManager::hasAllCalendarAccess(true);
53 +
54 + if (!$hasPermission) {
55 + $attachedCalendarIds = CalendarService::getAttachedCalendarIds($calendarsQuery);
56 + $calendarsQuery->whereIn('id', $attachedCalendarIds);
52 57 }
53 58
54 59 $calendars = $calendarsQuery->paginate();
55 60
@@ -55,20 +60,30 @@
55 60
56 61 foreach ($calendars as $calendar) {
57 62 $calendar->author_profile = $calendar->getAuthorProfile();
58 63 $calendar->public_url = $calendar->getLandingPageUrl();
64 + $calendar->event_order = $calendar->getMeta('event_order');
65 +
66 + if (!$hasPermission) {
67 + $calendar->setRelation('slots', $calendar->slots->filter(function ($slot) {
68 + return CalendarEventService::isSharedCalendarEvent($slot);
69 + })->values());
70 + }
71 +
59 72 foreach ($calendar->slots as $slot) {
73 + $slot->setRelation('calendar', $calendar);
60 74 $slot->shortcode = '[fluent_booking id="' . $slot->id . '"]';
61 75 $slot->public_url = $slot->getPublicUrl();
62 76 $slot->duration = $slot->getDefaultDuration();
63 - $slot->price_total = $slot->getPricingTotal();
77 + $slot->price_total = $slot->getEventPrice();
64 78 $slot->location_fields = $slot->getLocationFields();
65 79 $slot->author_profiles = $slot->isMultiHostEvent() ? $slot->getAuthorProfiles() : [];
66 80 do_action_ref_array('fluent_booking/calendar_slot', [&$slot]);
81 + $slot->unsetRelation('calendar');
67 82 }
68 83
69 84 if(empty($calendar->author_profile['ID'])) {
70 - $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>';
71 86 }
72 87
73 88 do_action_ref_array('fluent_booking/calendar', [&$calendar, 'lists']);
74 89 }
@@ -98,9 +113,10 @@
98 113 ], 422);
99 114 }
100 115
101 116 return [
102 - 'status' => true
117 + 'status' => true,
118 + 'message' => __('The provided slug is available', 'fluent-booking')
103 119 ];
104 120 }
105 121
106 122 public function createCalendar(Request $request)
@@ -139,9 +155,9 @@
139 155 $this->validate($data, $validationConfig['rules'], $validationConfig['messages']);
140 156
141 157 do_action('fluent_booking/before_create_calendar', $data, $this);
142 158
143 - if (!empty($data['user_id']) && PermissionManager::userCan('invite_team_members')) {
159 + if (!empty($data['user_id']) && PermissionManager::userCan(['manage_all_data', 'invite_team_members'])) {
144 160 $user = get_user_by('ID', $data['user_id']);
145 161 } else {
146 162 $user = get_user_by('ID', get_current_user_id());
147 163 }
@@ -151,15 +167,25 @@
151 167 'message' => __('User not found', 'fluent-booking')
152 168 ], 422);
153 169 }
154 170
155 - $type = sanitize_text_field(Arr::get($data, 'type', 'simple'));
171 + $onboardinFeatures = $request->get('features');
172 + if (!empty($onboardinFeatures)) {
173 + $installableAddons = SanitizeService::sanitizeAddons($onboardinFeatures);
174 + OnboardingService::installAddons($installableAddons);
175 + }
156 176
177 + $type = SanitizeService::checkCollection(
178 + sanitize_text_field(Arr::get($data, 'type', 'simple')),
179 + ['simple', 'team', 'event'],
180 + 'simple'
181 + );
182 +
157 183 $isHostCalendar = $type == 'simple' ? true : false;
158 184
159 185 if ($isHostCalendar && Calendar::where('user_id', $user->ID)->where('type', 'simple')->first()) {
160 186 return $this->sendError([
161 - 'message' => __('The user already have a calendar. Please delete it first to create a new one', 'fluent-booking')
187 + 'message' => __('The user already has a calendar. Please delete it first to create a new one', 'fluent-booking')
162 188 ], 422);
163 189 }
164 190
165 191 if ($isHostCalendar) {
@@ -175,10 +201,30 @@
175 201
176 202 if (!$isHostCalendar) {
177 203 $title = sanitize_text_field(Arr::get($data, 'title', ''));
178 204 $data['slug'] = sanitize_title($title, '', 'display');
179 - $teamMembers = array_map('intval', Arr::get($slot, 'settings.team_members', []));
180 - 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 +
181 227 $user = get_user_by('ID', reset($teamMembers));
182 228 if (!$user) {
183 229 return $this->sendError([
184 230 'message' => __('Invalid Team Member', 'fluent-booking')
@@ -221,33 +267,25 @@
221 267 'message' => __('Calendar could not be found. Please try again', 'fluent-booking')
222 268 ], 422);
223 269 }
224 270
225 - $availability = AvailabilityService::getDefaultSchedule($calendar->user_id);
271 + $weeklySchedule = Arr::get($data, 'slot.weekly_schedules', []);
226 272
227 - if (!$availability) {
228 - $weeklySchedule = Arr::get($data, 'slot.weekly_schedules', []);
273 + $availability = AvailabilityService::maybeCreateAvailability($calendar, $weeklySchedule);
229 274
230 - $defaultSchedule = AvailabilityService::createScheduleSchema(
231 - $calendar->user_id, 'Weekly Hours', true, $calendar->author_timezone, 'UTC', $weeklySchedule
232 - );
233 -
234 - $availability = Availability::create($defaultSchedule);
235 - }
236 -
237 275 $title = (!empty($slot['title'])) ? sanitize_text_field($slot['title']) : $slot['duration'] . ' Minute Meeting';
238 276
239 277 $slotData = [
240 278 'title' => $title,
241 - 'slug' => Helper::generateSlotSlug($slot['duration'] . 'min', $calendar),
279 + 'slug' => Helper::generateSlotSlug((int)$slot['duration'] . 'min', $calendar),
242 280 'calendar_id' => $calendar->id,
243 281 'user_id' => $calendar->user_id,
244 282 'duration' => (int)$slot['duration'],
245 - 'description' => sanitize_textarea_field(Arr::get($slot, 'description')),
283 + 'description' => wp_kses_post(Arr::get($slot, 'description')),
246 284 'settings' => [
247 285 'team_members' => !$isHostCalendar ? $teamMembers : [],
248 286 'schedule_type' => sanitize_text_field($slot['schedule_type']),
249 - 'weekly_schedules' => SanitizeService::weeklySchedules($slot['weekly_schedules'], $calendar->author_timezone, 'UTC')
287 + 'weekly_schedules' => SanitizeService::weeklySchedules($slot['weekly_schedules'], $calendar->author_timezone, 'UTC', true)
250 288 ],
251 289 'status' => SanitizeService::checkCollection($slot['status'], ['active', 'draft']),
252 290 'color_schema' => sanitize_text_field(Arr::get($slot, 'color_schema', '#0099ff')),
253 291 'event_type' => sanitize_text_field(Arr::get($slot, 'event_type')),
@@ -281,9 +319,28 @@
281 319 $query->where('status', '!=', 'expired');
282 320 }])->findOrFail($calendarId);
283 321
284 322 $calendar->author_profile = $calendar->getAuthorProfile();
323 + $calendar->event_order = $calendar->getMeta('event_order');
285 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 +
286 343 $data = [
287 344 'calendar' => $calendar
288 345 ];
289 346
@@ -290,8 +347,12 @@
290 347 if (in_array('settings_menu', $request->get('with', []))) {
291 348 $data['settings_menu'] = AdminMenuHandler::getCalendarSettingsMenuItems($calendar);
292 349 }
293 350
351 + if (in_array('public_url', $request->get('with', []))) {
352 + $data['public_url'] = $calendar->getLandingPageUrl();
353 + }
354 +
294 355 return $data;
295 356 }
296 357
297 358 public function getSharingSettings(Request $request, $calendarId)
@@ -312,9 +373,10 @@
312 373
313 374 if ($calendarDataItems) {
314 375 $this->validate($calendarDataItems, [
315 376 'title' => 'required',
316 - 'calendar_avatar' => 'url'
377 + 'calendar_avatar' => 'nullable|url',
378 + 'featured_image' => 'nullable|url'
317 379 ]);
318 380
319 381 $updatedTimezone = sanitize_text_field(Arr::get($calendarDataItems, 'timezone'));
320 382 if ($updatedTimezone && $updatedTimezone != $calendar->author_timezone) {
@@ -323,11 +385,11 @@
323 385 }
324 386
325 387 $calendar->title = sanitize_text_field(Arr::get($calendarDataItems, 'title'));
326 388 $calendar->description = wp_kses_post(Arr::get($calendarDataItems, 'description'));
327 - $calendar->save();
328 389 $calendar->updateMeta('profile_photo_url', sanitize_url(Arr::get($calendarDataItems, 'calendar_avatar')));
329 390 $calendar->updateMeta('featured_image_url', sanitize_url(Arr::get($calendarDataItems, 'featured_image')));
391 + $calendar->save();
330 392
331 393 if ($calendar->user) {
332 394 $calendar->user->updateMeta('host_phone', sanitize_text_field(Arr::get($calendarDataItems, 'phone')));
333 395 }
@@ -362,11 +424,11 @@
362 424 'message' => __('Calendar has been updated successfully', 'fluent-booking')
363 425 ];
364 426 }
365 427
366 - public function getEvent(Request $request, $calendarId, $slotId)
428 + public function getEvent(Request $request, $calendarId, $eventId)
367 429 {
368 - $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->with(['calendar.user'])->findOrFail($slotId);
430 + $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->with(['calendar.user'])->findOrFail($eventId);
369 431
370 432 $calendarEvent->author_profile = $calendarEvent->getAuthorProfile();
371 433
372 434 $calendarEvent->calendar->author_profile = $calendarEvent->calendar->getAuthorProfile();
@@ -380,8 +442,10 @@
380 442 $eventSettings['date_overrides'] = (object)SanitizeService::slotDateOverrides(Arr::get($eventSettings, 'date_overrides', []), 'UTC', $calendarEvent->calendar->author_timezone, $calendarEvent);
381 443
382 444 $eventSettings['location_fields'] = $calendarEvent->getLocationFields();
383 445
446 + $eventSettings['hosts_schedules'] = $calendarEvent->getHostsSchedules();
447 +
384 448 $calendarEvent->settings = apply_filters('fluent_booking/get_calendar_event_settings', $eventSettings, $calendarEvent, $calendarEvent->calendar);
385 449
386 450 $data = [
387 451 'calendar_event' => $calendarEvent
@@ -414,37 +478,16 @@
414 478 public function getEventSchema(Request $request, $calendarId)
415 479 {
416 480 $calendar = Calendar::findOrFail($calendarId);
417 481
418 - $userCalendarId = $calendar->type == 'simple' ? $calendarId : null;
482 + $schema = (new CalendarSlot())->getEventSchema($calendar);
419 483
420 - $settingsSchema = (new CalendarSlot())->getSlotSettingsSchema($userCalendarId);
421 -
422 - $schema = [
423 - 'title' => '',
424 - 'status' => 'active',
425 - 'description' => '',
426 - 'duration' => '30',
427 - 'color_schema' => '#0099ff',
428 - 'calendar' => $calendar,
429 - 'settings' => $settingsSchema,
430 - 'max_book_per_slot' => 1,
431 - 'location_settings' => [
432 - [
433 - 'type' => '',
434 - 'title' => '',
435 - 'description' => '',
436 - 'host_phone_number' => ''
437 - ]
438 - ]
439 - ];
440 -
441 484 return [
442 485 'slot' => $schema
443 486 ];
444 487 }
445 488
446 - public function getAvailabilitySettings(Request $request, $calendarId, $slotId)
489 + public function getAvailabilitySettings(Request $request, $calendarId, $eventId)
447 490 {
448 491 $availableSchedules = AvailabilityService::availabilitySchedules();
449 492
450 493 $scheduleOptions = AvailabilityService::getScheduleOptions();
@@ -492,18 +535,18 @@
492 535
493 536 $availability = AvailabilityService::getDefaultSchedule($calendar->user_id);
494 537
495 538 $slotData = [
496 - 'title' => $slot['title'],
539 + 'title' => sanitize_text_field($slot['title']),
497 540 'slug' => Helper::generateSlotSlug($slot['duration'] . 'min', $calendar),
498 541 'calendar_id' => $calendar->id,
499 542 'user_id' => $calendar->user_id,
500 543 'duration' => (int)$slot['duration'],
501 - 'description' => sanitize_textarea_field(Arr::get($slot, 'description')),
544 + 'description' => wp_kses_post(Arr::get($slot, 'description')),
502 545 'settings' => [
503 546 'schedule_type' => sanitize_text_field($slot['settings']['schedule_type']),
504 - 'weekly_schedules' => SanitizeService::weeklySchedules($slot['settings']['weekly_schedules'], $calendar->author_timezone, 'UTC'),
505 - 'date_overrides' => SanitizeService::slotDateOverrides(Arr::get($slot['settings'], 'date_overrides', []), $calendar->author_timezone, 'UTC'),
547 + 'weekly_schedules' => SanitizeService::weeklySchedules($slot['settings']['weekly_schedules'], $calendar->author_timezone, 'UTC', true),
548 + 'date_overrides' => SanitizeService::slotDateOverrides(Arr::get($slot['settings'], 'date_overrides', []), $calendar->author_timezone, 'UTC', null, true),
506 549 'range_type' => sanitize_text_field(Arr::get($slot['settings'], 'range_type')),
507 550 'range_days' => (int)(Arr::get($slot['settings'], 'range_days', 60)) ?: 60,
508 551 'range_date_between' => SanitizeService::rangeDateBetween(Arr::get($slot['settings'], 'range_date_between', ['', ''])),
509 552 'schedule_conditions' => SanitizeService::scheduleConditions(Arr::get($slot['settings'], 'schedule_conditions', [])),
@@ -530,8 +573,10 @@
530 573 $createdSlot = CalendarSlot::create($slotData);
531 574
532 575 do_action('fluent_booking/after_create_event', $calendar, $createdSlot);
533 576
577 + $calendar->updateEventOrder($createdSlot->id);
578 +
534 579 return [
535 580 'message' => __('New Event Type has been created successfully', 'fluent-booking'),
536 581 'slot' => $createdSlot
537 582 ];
@@ -589,9 +634,9 @@
589 634 $event->title = sanitize_text_field($data['title']);
590 635 $event->duration = (int)$data['duration'];
591 636 $event->status = SanitizeService::checkCollection($data['status'], ['active', 'draft']);
592 637 $event->color_schema = sanitize_text_field(Arr::get($data, 'color_schema', '#0099ff'));
593 - $event->description = sanitize_textarea_field(Arr::get($data, 'description'));
638 + $event->description = wp_kses_post(Arr::get($data, 'description'));
594 639 $event->max_book_per_slot = (int)Arr::get($data, 'max_book_per_slot');
595 640 $event->is_display_spots = (bool)Arr::get($data, 'is_display_spots');
596 641 $event->location_settings = SanitizeService::locationSettings(Arr::get($data, 'location_settings', []));
597 642
@@ -618,12 +663,12 @@
618 663 $data = $request->all();
619 664
620 665 $event = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);
621 666
622 - $event->settings = [
667 + $eventSettings = [
623 668 'schedule_type' => sanitize_text_field(Arr::get($data, 'schedule_type')),
624 - 'weekly_schedules' => SanitizeService::weeklySchedules(Arr::get($data, 'weekly_schedules'), $event->calendar->author_timezone, 'UTC'),
625 - 'date_overrides' => SanitizeService::slotDateOverrides(Arr::get($data, 'date_overrides', []), $event->calendar->author_timezone, 'UTC'),
669 + 'weekly_schedules' => SanitizeService::weeklySchedules(Arr::get($data, 'weekly_schedules'), $event->calendar->author_timezone, 'UTC', true),
670 + 'date_overrides' => SanitizeService::slotDateOverrides(Arr::get($data, 'date_overrides', []), $event->calendar->author_timezone, 'UTC', null, true),
626 671 'range_type' => sanitize_text_field(Arr::get($data, 'range_type')),
627 672 'range_days' => (int)(Arr::get($data, 'range_days', 60)) ?: 60,
628 673 'range_date_between' => SanitizeService::rangeDateBetween(Arr::get($data, 'range_date_between', ['', ''])),
629 674 'common_schedule' => Arr::isTrue($data, 'common_schedule', false)
@@ -628,11 +673,70 @@
628 673 'range_date_between' => SanitizeService::rangeDateBetween(Arr::get($data, 'range_date_between', ['', ''])),
629 674 'common_schedule' => Arr::isTrue($data, 'common_schedule', false)
630 675 ];
631 676
632 - $event->availability_id = (int)Arr::get($data, 'availability_id');
633 - $event->availability_type = SanitizeService::checkCollection(Arr::get($data, 'availability_type'), ['existing_schedule', 'custom']);
677 + $hostsSchedules = [];
634 678
679 + if ($event->isTeamEvent()) {
680 + $hostsSchedules = array_map('intval', array_combine(
681 + array_map('intval', array_keys(Arr::get($data, 'hosts_schedules', []))),
682 + array_map('intval', Arr::get($data, 'hosts_schedules', []))
683 + ));
684 + }
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 +
734 + $event->settings = $eventSettings;
735 +
736 + $event->availability_id = $availabilityId;
737 + $event->availability_type = $availabilityType;
738 +
635 739 $event->save();
636 740
637 741 return [
638 742 'message' => __('Data has been updated', 'fluent-booking'),
@@ -672,11 +776,11 @@
672 776 'event' => $event
673 777 ];
674 778 }
675 779
676 - public function patchCalendarEvent(Request $request, $calendarId, $slotId)
780 + public function patchCalendarEvent(Request $request, $calendarId, $eventId)
677 781 {
678 - $slot = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($slotId);
782 + $slot = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);
679 783
680 784 $status = $request->get('status');
681 785
682 786 if ($status) {
@@ -693,14 +797,22 @@
693 797 public function cloneCalendarEvent(Request $request, $calendarId, $eventId)
694 798 {
695 799 $newCalendarId = intval($request->get('new_calendar_id')) ?: $calendarId;
696 800
801 + if (!PermissionManager::canWriteCalendar($newCalendarId)) {
802 + return $this->sendError([
803 + 'message' => __('You do not have permission to write to the destination calendar.', 'fluent-booking')
804 + ], 403);
805 + }
806 +
697 807 $calendar = Calendar::findOrFail($newCalendarId);
698 808
699 - $originalEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);
809 + $originalEvent = CalendarSlot::with('event_metas')->where('calendar_id', $calendarId)->findOrFail($eventId);
700 810
701 811 $clonedEvent = $originalEvent->replicate();
702 812
813 + $clonedEvent->hash = null;
814 +
703 815 $clonedEvent->calendar_id = $calendar->id;
704 816
705 817 $clonedEvent->user_id = $calendar->user_id;
706 818
@@ -709,11 +821,11 @@
709 821 $clonedEvent->slug = Helper::generateSlotSlug($clonedEvent->duration . 'min', $calendar);
710 822
711 823 $clonedEvent->save();
712 824
713 - $eventsMeta = $originalEvent->getCalendarEventsMeta();
825 + $calendar->updateEventOrder($clonedEvent->id);
714 826
715 - $integrationsMeta = $originalEvent->getIntegrationsMeta();
827 + $eventsMeta = $originalEvent->event_metas;
716 828
717 829 foreach ($eventsMeta as $meta) {
718 830 $clonedMeta = $meta->replicate();
719 831 $clonedMeta->object_id = $clonedEvent->id;
@@ -719,17 +831,25 @@
719 831 $clonedMeta->object_id = $clonedEvent->id;
720 832 $clonedMeta->save();
721 833 }
722 834
723 - foreach ($integrationsMeta as $meta) {
724 - $clonedMeta = $meta->replicate();
725 - $clonedMeta->object_id = $clonedEvent->id;
726 - $clonedMeta->save();
727 - }
835 + return [
836 + 'slot' => $clonedEvent,
837 + 'message' => __('The Event Type has been cloned successfully', 'fluent-booking')
838 + ];
839 + }
728 840
841 + public function saveCalendarEventOrder(Request $request, $calendarId)
842 + {
843 + $calendar = Calendar::findOrFail($calendarId);
844 +
845 + $eventOrder = array_map('intval', $request->get('event_order', []));
846 +
847 + $calendar->updateMeta('event_order', array_filter($eventOrder));
848 +
729 849 return [
730 - 'message' => __('The Event Type has been cloned successfully', 'fluent-booking'),
731 - 'slot' => $clonedEvent
850 + 'calendar' => $calendar,
851 + 'message' => __('Event order has been updated', 'fluent-booking')
732 852 ];
733 853 }
734 854
735 855 public function cloneEventEmailNotification(Request $request, $calendarId, $eventId)
@@ -737,8 +857,14 @@
737 857 $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);
738 858
739 859 $fromEventId = intval($request->get('from_event_id'));
740 860
861 + if (!$fromEventId || !PermissionManager::canUpdateCalendarEvent($fromEventId)) {
862 + return $this->sendError([
863 + 'message' => __('You do not have permission to clone from the selected event.', 'fluent-booking')
864 + ], 403);
865 + }
866 +
741 867 $fromCalendarEvent = CalendarSlot::findOrFail($fromEventId);
742 868
743 869 $notification = $fromCalendarEvent->getNotifications(true);
744 870
@@ -751,11 +877,11 @@
751 877 'notifications' => $notification
752 878 ];
753 879 }
754 880
755 - public function getEventEmailNotifications(Request $request, $calendarId, $slotId)
881 + public function getEventEmailNotifications(Request $request, $calendarId, $eventId)
756 882 {
757 - $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($slotId);
883 + $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);
758 884
759 885 /*
760 886 * Confirmation Email to Attendee
761 887 * Confirmation Email to Organizer
@@ -776,11 +902,11 @@
776 902
777 903 return $data;
778 904 }
779 905
780 - public function saveEventEmailNotifications(Request $request, $calendarId, $slotId)
906 + public function saveEventEmailNotifications(Request $request, $calendarId, $eventId)
781 907 {
782 - $slot = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($slotId);
908 + $slot = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);
783 909
784 910 $notifications = $request->get('notifications', []);
785 911
786 912 $formattedNotifications = [];
@@ -800,15 +926,24 @@
800 926 'message' => __('Notifications has been saved', 'fluent-booking')
801 927 ];
802 928 }
803 929
804 - public function getEventBookingFields(Request $request, $calendarId, $slotId)
930 + public function getEventBookingFields(Request $request, $calendarId, $eventId)
805 931 {
806 - $slot = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($slotId);
932 + $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);
807 933
808 - return [
809 - 'fields' => $slot->getBookingFields()
934 + $data = [
935 + 'fields' => $calendarEvent->getBookingFields()
810 936 ];
937 +
938 + if (in_array('smart_codes', $request->get('with', []))) {
939 + $data['smart_codes'] = [
940 + 'texts' => Helper::getEditorShortCodes($calendarEvent),
941 + 'html' => Helper::getEditorShortCodes($calendarEvent, true)
942 + ];
943 + }
944 +
945 + return $data;
811 946 }
812 947
813 948 public function saveEventBookingFields(Request $request, $calendarId, $eventId)
814 949 {
@@ -815,55 +950,53 @@
815 950 $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);
816 951
817 952 $bookingFields = $request->get('booking_fields');
818 953
819 - $optionRequiredFields = ['dropdown', 'radio', 'checkbox-group', 'multi-select'];
954 + $formattedFields = BookingFieldService::sanitizeBookingFields($bookingFields, $calendarEvent);
820 955
821 - $formattedFields = [];
956 + $calendarEvent->setBookingFields($formattedFields);
822 957
823 - $textFields = ['type', 'name', 'label', 'placeholder', 'limit', 'help_text', 'date_format'];
824 - $booleanFields = ['enabled', 'required', 'system_defined', 'disable_alter', 'is_sms_number'];
958 + return [
959 + 'message' => __('Fields has been updated', 'fluent-booking')
960 + ];
961 + }
825 962
826 - foreach ($bookingFields as $value) {
827 - if (empty($value['name'])) {
828 - $value['name'] = BookingFieldService::generateFieldName($calendarEvent, $value['label']);
829 - }
963 + public function getEventPaymentSettings($calendarId, $eventId)
964 + {
965 + $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);
830 966
831 - $textValues = array_map('sanitize_text_field', Arr::only($value, $textFields));
967 + $config = [
968 + 'native_enabled' => Helper::isPaymentEnabled(),
969 + 'stripe_configured' => Helper::isPaymentConfigured('stripe'),
970 + 'paypal_configured' => Helper::isPaymentConfigured('paypal'),
971 + 'offline_configured' => Helper::isPaymentConfigured('offline'),
972 + 'native_config_link' => Helper::getAppBaseUrl('settings/payment-methods/stripe'),
973 + 'woo_config_link' => Helper::getAppBaseUrl('settings/configure-integrations/global-modules'),
974 + 'has_cart' => defined('FLUENTCART_VERSION'),
975 + 'has_woo' => defined('WC_PLUGIN_FILE'),
976 + 'woo_enabled' => defined('WC_PLUGIN_FILE') && Helper::isModuleEnabled('woo')
977 + ];
832 978
833 - $booleanValues = array_map(function ($valueItem) {
834 - return $valueItem === true || $valueItem === 'true' || $valueItem == 1;
835 - }, Arr::only($value, $booleanFields));
979 + $data = apply_filters('fluent_booking/payment/get_payment_settings', [
980 + 'settings' => $calendarEvent->getPaymentSettings(),
981 + 'config' => $config
982 + ], $calendarEvent);
836 983
837 - $formattedField = array_merge($textValues, $booleanValues);
838 -
839 - $formattedField['index'] = (int)Arr::get($value, 'index');
840 - if ($value['type'] == 'payment' && $calendarEvent->type === 'paid') {
841 - $formattedField['payment_items'] = Arr::get($value, 'payment_items');
842 - $formattedField['currency_sign'] = CurrenciesHelper::getGlobalCurrencySign();
843 - }
844 - if (in_array(Arr::get($value, 'type'), $optionRequiredFields)) {
845 - $sanitizedOptions = array_map('sanitize_text_field', Arr::get($value, 'options'));
846 - $formattedField['options'] = $sanitizedOptions;
847 - }
848 -
849 - $formattedFields[] = $formattedField;
850 - }
851 -
852 - $calendarEvent->setBookingFields($formattedFields);
853 -
854 - return [
855 - 'message' => __('Fields has been updated', 'fluent-booking')
856 - ];
984 + return $data;
857 985 }
858 986
859 987 public function deleteCalendarEvent(Request $request, $calendarId, $calendarEventId)
860 988 {
861 989 $calendar = Calendar::query()->findOrFail($calendarId);
990 +
862 991 $calendarEvent = CalendarSlot::query()->where('calendar_id', $calendar->id)->findOrFail($calendarEventId);
863 992
993 + $calendar->updateEventOrder($calendarEvent->id);
994 +
864 995 do_action('fluent_booking/before_delete_calendar_event', $calendarEvent, $calendar);
996 +
865 997 $calendarEvent->delete();
998 +
866 999 do_action('fluent_booking/after_delete_calendar_event', $calendarEventId, $calendar);
867 1000
868 1001 return [
869 1002 'message' => __('Calendar Event has been deleted', 'fluent-booking')