get('query'), 'search')); $calendarType = sanitize_text_field(Arr::get($request->get('query'), 'calendarType')); $applySearchFilter = function($query) use ($search) { $query->where('status', '!=', 'expired'); if (!empty($search)) { $query->where('title', 'LIKE', '%' . $search . '%'); } }; $calendarsQuery = Calendar::with(['metas', 'slots' => function($query) use ($applySearchFilter) { $query->where($applySearchFilter); }]) ->where('status', '!=', 'expired'); if (!empty($search)) { $calendarsQuery->whereHas('slots', $applySearchFilter); } if (!empty($calendarType) && $calendarType != 'all') { $calendarsQuery->where('type', $calendarType); } $calendarsQuery = $calendarsQuery->latest(); $hasPermission = PermissionManager::hasAllCalendarAccess(true); if (!$hasPermission) { $attachedCalendarIds = CalendarService::getAttachedCalendarIds($calendarsQuery); $calendarsQuery->whereIn('id', $attachedCalendarIds); } $calendars = $calendarsQuery->paginate(); foreach ($calendars as $calendar) { $calendar->author_profile = $calendar->getAuthorProfile(); $calendar->public_url = $calendar->getLandingPageUrl(); $calendar->event_order = $calendar->getMeta('event_order'); if (!$hasPermission) { $calendar->setRelation('slots', $calendar->slots->filter(function ($slot) { return CalendarEventService::isSharedCalendarEvent($slot); })->values()); } foreach ($calendar->slots as $slot) { $slot->setRelation('calendar', $calendar); $slot->shortcode = '[fluent_booking id="' . $slot->id . '"]'; $slot->public_url = $slot->getPublicUrl(); $slot->duration = $slot->getDefaultDuration(); $slot->price_total = $slot->getEventPrice(); $slot->location_fields = $slot->getLocationFields(); $slot->author_profiles = $slot->isMultiHostEvent() ? $slot->getAuthorProfiles() : []; do_action_ref_array('fluent_booking/calendar_slot', [&$slot]); $slot->unsetRelation('calendar'); } if(empty($calendar->author_profile['ID'])) { $calendar->generic_error = '

Connected Host user is missing

'; } do_action_ref_array('fluent_booking/calendar', [&$calendar, 'lists']); } $data = [ 'calendars' => $calendars ]; if (in_array('calendar_event_lists', $request->get('with', []))) { $data['calendar_event_lists'] = [ 'hosts' => CalendarService::getCalendarOptionsByTitle('only_hosts'), 'teams' => CalendarService::getCalendarOptionsByTitle('only_teams'), 'events' => CalendarService::getCalendarOptionsByTitle('only_events') ]; } return $data; } public function checkSlug(Request $request) { $slug = sanitize_text_field(trim($request->get('slug'))); if (!Helper::isCalendarSlugAvailable($slug, true)) { return $this->sendError([ 'message' => __('The provided slug is not available. Please choose a different one', 'fluent-booking') ], 422); } return [ 'status' => true, 'message' => __('The provided slug is available', 'fluent-booking') ]; } public function createCalendar(Request $request) { $data = $request->get('calendar'); $rules = [ 'author_timezone' => 'required', 'slot.duration' => 'required|numeric|min:5', 'slot.event_type' => 'required', 'slot.availability_type' => 'required', 'slot.schedule_type' => 'required', 'slot.title' => 'required', 'slot.weekly_schedules' => 'required_if:slot.schedule_type,weekly_schedules', 'slot.location_settings.*.type' => 'required', 'slot.location_settings.*.host_phone_number' => 'required_if:location_settings.*.type,phone_organizer' ]; $messages = [ 'author_timezone.required' => __('Author timezone field is required', 'fluent-booking'), 'slot.duration.required' => __('Event duration field is required', 'fluent-booking'), 'slot.event_type.required' => __('Event type field is required', 'fluent-booking'), 'slot.availability_type.required' => __('Event availability type field is required', 'fluent-booking'), 'slot.schedule_type.required' => __('Event schedule type field is required', 'fluent-booking'), 'slot.title.required' => __('Event title field is required', 'fluent-booking'), 'slot.weekly_schedules.required_if' => __('Event weekly schedules field is required', 'fluent-booking'), 'slot.location_settings.*.type.required' => __('Event location type field is required', 'fluent-booking'), 'slot.location_settings.*.host_phone_number.required_if' => __('Event location host phone number field is required', 'fluent-booking') ]; $validationConfig = apply_filters('fluent_booking/create_calendar_validation_rule', [ 'rules' => $rules, 'messages' => $messages ], $data); $this->validate($data, $validationConfig['rules'], $validationConfig['messages']); do_action('fluent_booking/before_create_calendar', $data, $this); if (!empty($data['user_id']) && PermissionManager::userCan(['manage_all_data', 'invite_team_members'])) { $user = get_user_by('ID', $data['user_id']); } else { $user = get_user_by('ID', get_current_user_id()); } if (!$user) { return $this->sendError([ 'message' => __('User not found', 'fluent-booking') ], 422); } $onboardinFeatures = $request->get('features'); if (!empty($onboardinFeatures)) { $installableAddons = SanitizeService::sanitizeAddons($onboardinFeatures); OnboardingService::installAddons($installableAddons); } $type = SanitizeService::checkCollection( sanitize_text_field(Arr::get($data, 'type', 'simple')), ['simple', 'team', 'event'], 'simple' ); $isHostCalendar = $type == 'simple' ? true : false; if ($isHostCalendar && Calendar::where('user_id', $user->ID)->where('type', 'simple')->first()) { return $this->sendError([ 'message' => __('The user already has a calendar. Please delete it first to create a new one', 'fluent-booking') ], 422); } if ($isHostCalendar) { $userName = $user->user_login; if (is_email($userName)) { $userName = explode('@', $userName); $userName = $userName[0] . '-' . time(); } $data['slug'] = sanitize_title($userName, '', 'display'); } $slot = $data['slot']; if (!$isHostCalendar) { $title = sanitize_text_field(Arr::get($data, 'title', '')); $data['slug'] = sanitize_title($title, '', 'display'); $teamMembers = array_values(array_filter( array_map('intval', (array) Arr::get($slot, 'settings.team_members', [])) )); cache_users($teamMembers); foreach ($teamMembers as $memberId) { if (!get_user_by('ID', $memberId)) { return $this->sendError([ 'message' => __('Invalid Team Member', 'fluent-booking') ], 422); } } if (!in_array($user->ID, $teamMembers, true)) { // Same privileged act as passing an explicit user_id above; gate it identically. if (!PermissionManager::userCan(['manage_all_data', 'invite_team_members'])) { return $this->sendError([ 'message' => __('You are not allowed to create a calendar for another user', 'fluent-booking') ], 403); } $user = get_user_by('ID', reset($teamMembers)); if (!$user) { return $this->sendError([ 'message' => __('Invalid Team Member', 'fluent-booking') ], 422); } } } if (!Helper::isCalendarSlugAvailable($data['slug'], true)) { $data['slug'] .= '-' . time(); } if (!empty($data['slug'])) { $slug = trim(sanitize_text_field($data['slug'])); if (!Helper::isCalendarSlugAvailable($slug, true)) { return $this->sendError([ 'message' => __('The provided slug is not available. Please choose a different one', 'fluent-booking') ], 422); } $personName = trim($user->first_name . ' ' . $user->last_name); if (!$personName) { $personName = $user->display_name; } $calendarData = [ 'slug' => $slug, 'user_id' => $user->ID, 'title' => $isHostCalendar ? $personName : $title, 'type' => $type, 'author_timezone' => sanitize_text_field($data['author_timezone']) ?: 'UTC', ]; $calendar = Calendar::create($calendarData); } else { $calendar = Calendar::where('user_id', $user->ID)->where('type', 'simple')->first(); } if (!$calendar) { return $this->sendError([ 'message' => __('Calendar could not be found. Please try again', 'fluent-booking') ], 422); } $weeklySchedule = Arr::get($data, 'slot.weekly_schedules', []); $availability = AvailabilityService::maybeCreateAvailability($calendar, $weeklySchedule); $title = (!empty($slot['title'])) ? sanitize_text_field($slot['title']) : $slot['duration'] . ' Minute Meeting'; $slotData = [ 'title' => $title, 'slug' => Helper::generateSlotSlug((int)$slot['duration'] . 'min', $calendar), 'calendar_id' => $calendar->id, 'user_id' => $calendar->user_id, 'duration' => (int)$slot['duration'], 'description' => wp_kses_post(Arr::get($slot, 'description')), 'settings' => [ 'team_members' => !$isHostCalendar ? $teamMembers : [], 'schedule_type' => sanitize_text_field($slot['schedule_type']), 'weekly_schedules' => SanitizeService::weeklySchedules($slot['weekly_schedules'], $calendar->author_timezone, 'UTC', true) ], 'status' => SanitizeService::checkCollection($slot['status'], ['active', 'draft']), 'color_schema' => sanitize_text_field(Arr::get($slot, 'color_schema', '#0099ff')), 'event_type' => sanitize_text_field(Arr::get($slot, 'event_type')), 'availability_type' => SanitizeService::checkCollection($slot['availability_type'], ['existing_schedule', 'custom'], 'existing_schedule'), 'availability_id' => (int)$availability->id, 'location_type' => sanitize_text_field(Arr::get($slot, 'location_type')), 'location_heading' => wp_kses_post(Arr::get($slot, 'location_heading')), 'location_settings' => SanitizeService::locationSettings(Arr::get($slot, 'location_settings', [])), ]; $slotData['settings'] = wp_parse_args($slotData['settings'], (new CalendarSlot())->getSlotSettingsSchema()); $slotData = apply_filters('fluent_booking/create_calendar_event_data', $slotData, $calendar); $slot = CalendarSlot::create($slotData); do_action('fluent_booking/after_create_calendar', $calendar); do_action('fluent_booking/after_create_calendar_slot', $slot, $calendar); return [ 'calendar' => $calendar, 'slot' => $slot, 'redirect_url' => Helper::getAppBaseUrl('calendars/' . $calendar->id . '/slot-settings/' . $slot->id) ]; } public function getCalendar(Request $request, $calendarId) { $calendar = Calendar::with(['slots' => function ($query) { $query->where('status', '!=', 'expired'); }])->findOrFail($calendarId); $calendar->author_profile = $calendar->getAuthorProfile(); $calendar->event_order = $calendar->getMeta('event_order'); if (!PermissionManager::hasAllCalendarAccess(true)) { $calendar->setRelation('slots', $calendar->slots->filter(function ($slot) { return CalendarEventService::isSharedCalendarEvent($slot); })->values()); } foreach ($calendar->slots as $slot) { $slot->setRelation('calendar', $calendar); $slot->shortcode = '[fluent_booking id="' . $slot->id . '"]'; $slot->public_url = $slot->getPublicUrl(); $slot->duration = $slot->getDefaultDuration(); $slot->price_total = $slot->getEventPrice(); $slot->location_fields = $slot->getLocationFields(); $slot->author_profiles = $slot->isMultiHostEvent() ? $slot->getAuthorProfiles() : []; do_action_ref_array('fluent_booking/calendar_slot', [&$slot]); $slot->unsetRelation('calendar'); } $data = [ 'calendar' => $calendar ]; if (in_array('settings_menu', $request->get('with', []))) { $data['settings_menu'] = AdminMenuHandler::getCalendarSettingsMenuItems($calendar); } if (in_array('public_url', $request->get('with', []))) { $data['public_url'] = $calendar->getLandingPageUrl(); } return $data; } public function getSharingSettings(Request $request, $calendarId) { $calendar = Calendar::findOrFail($calendarId); return [ 'settings' => LandingPageHelper::getSettings($calendar), 'share_url' => $calendar->getLandingPageUrl(true) ]; } public function saveSharingSettings(Request $request, $calendarId) { $calendar = Calendar::findOrFail($calendarId); $calendarDataItems = Arr::only($request->get('calendar_data', []), ['title', 'timezone', 'description', 'calendar_avatar', 'featured_image', 'phone']); if ($calendarDataItems) { $this->validate($calendarDataItems, [ 'title' => 'required', 'calendar_avatar' => 'nullable|url', 'featured_image' => 'nullable|url' ]); $updatedTimezone = sanitize_text_field(Arr::get($calendarDataItems, 'timezone')); if ($updatedTimezone && $updatedTimezone != $calendar->author_timezone) { CalendarService::updateCalendarEventsSchedule($calendarId, $calendar->author_timezone, $updatedTimezone); $calendar->author_timezone = $updatedTimezone; } $calendar->title = sanitize_text_field(Arr::get($calendarDataItems, 'title')); $calendar->description = wp_kses_post(Arr::get($calendarDataItems, 'description')); $calendar->updateMeta('profile_photo_url', sanitize_url(Arr::get($calendarDataItems, 'calendar_avatar'))); $calendar->updateMeta('featured_image_url', sanitize_url(Arr::get($calendarDataItems, 'featured_image'))); $calendar->save(); if ($calendar->user) { $calendar->user->updateMeta('host_phone', sanitize_text_field(Arr::get($calendarDataItems, 'phone'))); } } $sharingSettings = $request->get('landing_page_settings', []); LandingPageHelper::updateSettings($calendar, $sharingSettings); return [ 'message' => __('Landing Page settings has been updated', 'fluent-booking') ]; } public function updateCalendar(Request $request, $calendarId) { $data = $request->all(); $calendar = Calendar::findOrFail($calendarId); do_action_ref_array('fluent_booking/before_update_calendar', [&$calendar, $data]); $calendar->description = wp_kses_post($request->get('description')); $calendar->save(); do_action('fluent_booking/after_update_calendar', $calendar, $data); $calendar->author_profile = $calendar->getAuthorProfile(); do_action_ref_array('fluent_booking/calendar', [&$calendar, 'update']); return [ 'calendar' => $calendar, 'message' => __('Calendar has been updated successfully', 'fluent-booking') ]; } public function getEvent(Request $request, $calendarId, $eventId) { $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->with(['calendar.user'])->findOrFail($eventId); $calendarEvent->author_profile = $calendarEvent->getAuthorProfile(); $calendarEvent->calendar->author_profile = $calendarEvent->calendar->getAuthorProfile(); $calendarEvent->public_url = $calendarEvent->getPublicUrl(); $eventSettings = $calendarEvent->settings; $eventSettings['weekly_schedules'] = SanitizeService::weeklySchedules($eventSettings['weekly_schedules'], 'UTC', $calendarEvent->calendar->author_timezone); $eventSettings['date_overrides'] = (object)SanitizeService::slotDateOverrides(Arr::get($eventSettings, 'date_overrides', []), 'UTC', $calendarEvent->calendar->author_timezone, $calendarEvent); $eventSettings['location_fields'] = $calendarEvent->getLocationFields(); $eventSettings['hosts_schedules'] = $calendarEvent->getHostsSchedules(); $calendarEvent->settings = apply_filters('fluent_booking/get_calendar_event_settings', $eventSettings, $calendarEvent, $calendarEvent->calendar); $data = [ 'calendar_event' => $calendarEvent ]; if (in_array('calendar', $this->request->get('with', []))) { $calendar = $calendarEvent->calendar; $calendar->author_profile = $calendar->getAuthorProfile(); $data['calendar'] = $calendar; } if (in_array('smart_codes', $this->request->get('with', []))) { $data['smart_codes'] = [ 'texts' => Helper::getEditorShortCodes($calendarEvent), 'html' => Helper::getEditorShortCodes($calendarEvent, true) ]; } if (in_array('settings_menu', $this->request->get('with', []))) { $data['settings_menu'] = AdminMenuHandler::getEventSettingsMenuItems($calendarEvent); } if (in_array('calendar_event_lists', $this->request->get('with', []))) { $data['calendar_event_lists'] = CalendarService::getCalendarOptionsByTitle(); } return $data; } public function getEventSchema(Request $request, $calendarId) { $calendar = Calendar::findOrFail($calendarId); $schema = (new CalendarSlot())->getEventSchema($calendar); return [ 'slot' => $schema ]; } public function getAvailabilitySettings(Request $request, $calendarId, $eventId) { $availableSchedules = AvailabilityService::availabilitySchedules(); $scheduleOptions = AvailabilityService::getScheduleOptions(); return [ 'schedule_options' => $scheduleOptions, 'available_schedules' => $availableSchedules ]; } public function createCalendarEvent(Request $request, $calendarId) { $calendar = Calendar::findOrFail($calendarId); $slot = $request->all(); $rules = [ 'title' => 'required', 'duration' => 'required|numeric|min:5', 'status' => 'required', 'event_type' => 'required', 'location_settings.*.type' => 'required', 'location_settings.*.title' => 'required_if:location_settings.*.type,custom', 'location_settings.*.description' => 'required_if:location_settings.*.type,address_organizer', 'location_settings.*.host_phone_number' => 'required_if:location_settings.*.type,phone_organizer' ]; $messages = [ 'title.required' => __('Event title field is required', 'fluent-booking'), 'duration.required' => __('Event duration field is required', 'fluent-booking'), 'status.required' => __('Event status field is required', 'fluent-booking'), 'event_type.required' => __('Event type field is required', 'fluent-booking'), 'location_settings.*.type.required' => __('Event location type field is required', 'fluent-booking'), 'location_settings.*.title.required_if' => __('Event location title field is required', 'fluent-booking'), 'location_settings.*.description.required_if' => __('Event location description field is required', 'fluent-booking'), 'location_settings.*.host_phone_number.required_if' => __('Event location host phone number field is required', 'fluent-booking') ]; $validationConfig = apply_filters('fluent_booking/create_calendar_event_validation_rule', [ 'rules' => $rules, 'messages' => $messages ], $slot); $this->validate($slot, $validationConfig['rules'], $validationConfig['messages']); $availability = AvailabilityService::getDefaultSchedule($calendar->user_id); $slotData = [ 'title' => sanitize_text_field($slot['title']), 'slug' => Helper::generateSlotSlug($slot['duration'] . 'min', $calendar), 'calendar_id' => $calendar->id, 'user_id' => $calendar->user_id, 'duration' => (int)$slot['duration'], 'description' => wp_kses_post(Arr::get($slot, 'description')), 'settings' => [ 'schedule_type' => sanitize_text_field($slot['settings']['schedule_type']), 'weekly_schedules' => SanitizeService::weeklySchedules($slot['settings']['weekly_schedules'], $calendar->author_timezone, 'UTC', true), 'date_overrides' => SanitizeService::slotDateOverrides(Arr::get($slot['settings'], 'date_overrides', []), $calendar->author_timezone, 'UTC', null, true), 'range_type' => sanitize_text_field(Arr::get($slot['settings'], 'range_type')), 'range_days' => (int)(Arr::get($slot['settings'], 'range_days', 60)) ?: 60, 'range_date_between' => SanitizeService::rangeDateBetween(Arr::get($slot['settings'], 'range_date_between', ['', ''])), 'schedule_conditions' => SanitizeService::scheduleConditions(Arr::get($slot['settings'], 'schedule_conditions', [])), 'buffer_time_before' => sanitize_text_field(Arr::get($slot['settings'], 'buffer_time_before', '0')), 'buffer_time_after' => sanitize_text_field(Arr::get($slot['settings'], 'buffer_time_after', '0')), 'slot_interval' => sanitize_text_field(Arr::get($slot['settings'], 'slot_interval', '')), 'team_members' => array_map('intval', Arr::get($slot['settings'], 'team_members', [])) ], 'status' => SanitizeService::checkCollection($slot['status'], ['active', 'draft'], 'active'), 'color_schema' => sanitize_text_field(Arr::get($slot, 'color_schema', '#0099ff')), 'event_type' => sanitize_text_field(Arr::get($slot, 'event_type')), 'availability_type' => 'existing_schedule', 'availability_id' => $availability ? $availability->id : null, 'location_type' => sanitize_text_field(Arr::get($slot, 'location_type')), 'location_settings' => SanitizeService::locationSettings(Arr::get($slot, 'location_settings', [])), 'max_book_per_slot' => (int)Arr::get($slot, 'max_book_per_slot', 1), 'is_display_spots' => (bool)Arr::get($slot, 'is_display_spots', false), ]; $slotData = apply_filters('fluent_booking/create_calendar_event_data', $slotData, $calendar); do_action('fluent_booking/before_create_event', $calendar, $slotData); $createdSlot = CalendarSlot::create($slotData); do_action('fluent_booking/after_create_event', $calendar, $createdSlot); $calendar->updateEventOrder($createdSlot->id); return [ 'message' => __('New Event Type has been created successfully', 'fluent-booking'), 'slot' => $createdSlot ]; } public function updateEventDetails(Request $request, $calendarId, $eventId) { $data = $request->all(); $event = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId); $rules = [ 'title' => 'required', 'duration' => 'required|numeric', 'location_settings.*.type' => 'required', 'location_settings.*.title' => 'required_if:location_settings.*.type,in_person_organizer', 'location_settings.*.host_phone_number' => 'required_if:location_settings.*.type,phone_organizer' ]; $messages = [ 'title.required' => __('Event title field is required', 'fluent-booking'), 'duration.required' => __('Event duration field is required', 'fluent-booking'), 'location_settings.*.type.required' => __('Event location type field is required', 'fluent-booking'), 'location_settings.*.title.required_if' => __('Event location title field is required', 'fluent-booking'), 'location_settings.*.host_phone_number.required_if' => __('Event location host phone number field is required', 'fluent-booking') ]; if ('group' === $event->event_type) { $rules = array_merge($rules, [ 'max_book_per_slot' => 'required|numeric|min:1', 'is_display_spots' => 'required|min:0|max:1', ]); $messages = array_merge($messages, [ 'max_book_per_slot.required' => __('Event max book per slot field is required', 'fluent-booking'), 'is_display_spots.required' => __('Event is display spots field is required', 'fluent-booking') ]); } else { $rules = array_merge($rules, [ 'multi_duration.default_duration' => 'required_if:multi_duration.enabled,true', 'multi_duration.available_durations' => 'required_if:multi_duration.enabled,true' ]); $messages = array_merge($messages, [ 'multi_duration.default_duration.required_if' => __('Event default duration is required', 'fluent-booking'), 'multi_duration.available_durations.required_if' => __('Event available durations is required', 'fluent-booking') ]); } $validationConfig = apply_filters('fluent_booking/update_event_details_validation_rule', [ 'rules' => $rules, 'messages' => $messages ], $event); $this->validate($data, $validationConfig['rules'], $validationConfig['messages']); $event->title = sanitize_text_field($data['title']); $event->duration = (int)$data['duration']; $event->status = SanitizeService::checkCollection($data['status'], ['active', 'draft']); $event->color_schema = sanitize_text_field(Arr::get($data, 'color_schema', '#0099ff')); $event->description = wp_kses_post(Arr::get($data, 'description')); $event->max_book_per_slot = (int)Arr::get($data, 'max_book_per_slot'); $event->is_display_spots = (bool)Arr::get($data, 'is_display_spots'); $event->location_settings = SanitizeService::locationSettings(Arr::get($data, 'location_settings', [])); $event->settings = [ 'multi_duration' => [ 'enabled' => Arr::isTrue($data, 'multi_duration.enabled'), 'default_duration' => Arr::get($data, 'multi_duration.default_duration', ''), 'available_durations' => array_map('sanitize_text_field', Arr::get($data, 'multi_duration.available_durations', [])) ] ]; $event->save(); do_action('fluent_booking/after_update_event_details', $event); return [ 'message' => __('Data has been updated', 'fluent-booking'), 'event' => $event ]; } public function updateEventAvailability(Request $request, $calendarId, $eventId) { $data = $request->all(); $event = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId); $eventSettings = [ 'schedule_type' => sanitize_text_field(Arr::get($data, 'schedule_type')), 'weekly_schedules' => SanitizeService::weeklySchedules(Arr::get($data, 'weekly_schedules'), $event->calendar->author_timezone, 'UTC', true), 'date_overrides' => SanitizeService::slotDateOverrides(Arr::get($data, 'date_overrides', []), $event->calendar->author_timezone, 'UTC', null, true), 'range_type' => sanitize_text_field(Arr::get($data, 'range_type')), 'range_days' => (int)(Arr::get($data, 'range_days', 60)) ?: 60, 'range_date_between' => SanitizeService::rangeDateBetween(Arr::get($data, 'range_date_between', ['', ''])), 'common_schedule' => Arr::isTrue($data, 'common_schedule', false) ]; $hostsSchedules = []; if ($event->isTeamEvent()) { $hostsSchedules = array_map('intval', array_combine( array_map('intval', array_keys(Arr::get($data, 'hosts_schedules', []))), array_map('intval', Arr::get($data, 'hosts_schedules', [])) )); } $availabilityId = (int)Arr::get($data, 'availability_id'); $availabilityType = SanitizeService::checkCollection(Arr::get($data, 'availability_type'), ['existing_schedule', 'custom']); $submittedIds = array_values(array_filter(array_unique(array_merge( [$availabilityType === 'existing_schedule' ? $availabilityId : 0], array_values($hostsSchedules) )))); $usableIds = []; $scheduleOwners = []; if ($submittedIds) { $usableIds = array_map('intval', AvailabilityService::usableAvailabilityQuery() ->whereIn('id', $submittedIds)->pluck('id')->toArray()); $scheduleOwners = array_map('intval', Availability::whereIn('id', $submittedIds) ->pluck('object_id', 'id')->toArray()); } foreach ($hostsSchedules as $hostId => $scheduleId) { if (($scheduleOwners[$scheduleId] ?? 0) === (int)$hostId) { continue; } if (!in_array($scheduleId, $usableIds, true)) { return $this->sendError([ 'message' => __('You are not allowed to use the selected schedule', 'fluent-booking') ], 403); } } if ($hostsSchedules) { $eventSettings['hosts_schedules'] = $hostsSchedules; } $eventHostIds = array_map('intval', array_merge( $event->getHostIds(), [$event->user_id, $event->calendar->user_id] )); if ($availabilityType === 'existing_schedule' && $availabilityId && !in_array($availabilityId, $usableIds, true) && !in_array($scheduleOwners[$availabilityId] ?? 0, $eventHostIds, true)) { return $this->sendError([ 'message' => __('You are not allowed to use the selected schedule', 'fluent-booking') ], 403); } $event->settings = $eventSettings; $event->availability_id = $availabilityId; $event->availability_type = $availabilityType; $event->save(); return [ 'message' => __('Data has been updated', 'fluent-booking'), 'event' => $event ]; } public function updateEventLimits(Request $request, $calendarId, $eventId) { $data = $request->all(); $event = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId); $event->settings = [ 'schedule_conditions' => SanitizeService::scheduleConditions(Arr::get($data['settings'], 'schedule_conditions', [])), 'buffer_time_before' => sanitize_text_field(Arr::get($data, 'settings.buffer_time_before', '0')), 'buffer_time_after' => sanitize_text_field(Arr::get($data, 'settings.buffer_time_after', '0')), 'slot_interval' => sanitize_text_field(Arr::get($data, 'settings.slot_interval', '')), 'booking_frequency' => [ 'enabled' => Arr::isTrue($data, 'settings.booking_frequency.enabled'), 'limits' => $this->sanitize_mapped_data(Arr::get($data, 'settings.booking_frequency.limits')) ], 'booking_duration' => [ 'enabled' => Arr::isTrue($data, 'settings.booking_duration.enabled'), 'limits' => $this->sanitize_mapped_data(Arr::get($data, 'settings.booking_duration.limits')) ], 'lock_timezone' => [ 'enabled' => Arr::isTrue($data, 'settings.lock_timezone.enabled'), 'timezone' => sanitize_text_field(Arr::get($data, 'settings.lock_timezone.timezone')) ], ]; $event->save(); return [ 'message' => __('Data has been updated', 'fluent-booking'), 'event' => $event ]; } public function patchCalendarEvent(Request $request, $calendarId, $eventId) { $slot = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId); $status = $request->get('status'); if ($status) { $slot->status = $status; $slot->save(); } return [ 'message' => __('Data has been updated', 'fluent-booking') ]; } public function cloneCalendarEvent(Request $request, $calendarId, $eventId) { $newCalendarId = intval($request->get('new_calendar_id')) ?: $calendarId; if (!PermissionManager::canWriteCalendar($newCalendarId)) { return $this->sendError([ 'message' => __('You do not have permission to write to the destination calendar.', 'fluent-booking') ], 403); } $calendar = Calendar::findOrFail($newCalendarId); $originalEvent = CalendarSlot::with('event_metas')->where('calendar_id', $calendarId)->findOrFail($eventId); $clonedEvent = $originalEvent->replicate(); $clonedEvent->hash = null; $clonedEvent->calendar_id = $calendar->id; $clonedEvent->user_id = $calendar->user_id; $clonedEvent->title = $originalEvent->title . ' (clone)'; $clonedEvent->slug = Helper::generateSlotSlug($clonedEvent->duration . 'min', $calendar); $clonedEvent->save(); $calendar->updateEventOrder($clonedEvent->id); $eventsMeta = $originalEvent->event_metas; foreach ($eventsMeta as $meta) { $clonedMeta = $meta->replicate(); $clonedMeta->object_id = $clonedEvent->id; $clonedMeta->save(); } return [ 'slot' => $clonedEvent, 'message' => __('The Event Type has been cloned successfully', 'fluent-booking') ]; } public function saveCalendarEventOrder(Request $request, $calendarId) { $calendar = Calendar::findOrFail($calendarId); $eventOrder = array_map('intval', $request->get('event_order', [])); $calendar->updateMeta('event_order', array_filter($eventOrder)); return [ 'calendar' => $calendar, 'message' => __('Event order has been updated', 'fluent-booking') ]; } public function cloneEventEmailNotification(Request $request, $calendarId, $eventId) { $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId); $fromEventId = intval($request->get('from_event_id')); if (!$fromEventId || !PermissionManager::canUpdateCalendarEvent($fromEventId)) { return $this->sendError([ 'message' => __('You do not have permission to clone from the selected event.', 'fluent-booking') ], 403); } $fromCalendarEvent = CalendarSlot::findOrFail($fromEventId); $notification = $fromCalendarEvent->getNotifications(true); $calendarEvent->setNotifications($notification); $calendarEvent->save(); return [ 'message' => __('The Notification has been cloned successfully', 'fluent-booking'), 'notifications' => $notification ]; } public function getEventEmailNotifications(Request $request, $calendarId, $eventId) { $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId); /* * Confirmation Email to Attendee * Confirmation Email to Organizer * Reminder Email to Attendee [before 1 day, 1 hour, 30 minutes, 5 minutes] * Cancelled By Organizer to Attendee * Cancelled By Attendee to Organizer */ $data = [ 'notifications' => $calendarEvent->getNotifications(true) ]; if (in_array('smart_codes', $request->get('with', []))) { $data['smart_codes'] = [ 'texts' => Helper::getEditorShortCodes($calendarEvent), 'html' => Helper::getEditorShortCodes($calendarEvent, true) ]; } return $data; } public function saveEventEmailNotifications(Request $request, $calendarId, $eventId) { $slot = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId); $notifications = $request->get('notifications', []); $formattedNotifications = []; foreach ($notifications as $key => $value) { $formattedNotifications[$key] = [ 'title' => sanitize_text_field(Arr::get($value, 'title')), 'enabled' => Arr::isTrue($value, 'enabled'), 'email' => $this->sanitize_mapped_data(Arr::get($value, 'email')), 'is_host' => Arr::isTrue($value, 'is_host') ]; } $slot->setNotifications($formattedNotifications); return [ 'message' => __('Notifications has been saved', 'fluent-booking') ]; } public function getEventBookingFields(Request $request, $calendarId, $eventId) { $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId); $data = [ 'fields' => $calendarEvent->getBookingFields() ]; if (in_array('smart_codes', $request->get('with', []))) { $data['smart_codes'] = [ 'texts' => Helper::getEditorShortCodes($calendarEvent), 'html' => Helper::getEditorShortCodes($calendarEvent, true) ]; } return $data; } public function saveEventBookingFields(Request $request, $calendarId, $eventId) { $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId); $bookingFields = $request->get('booking_fields'); $formattedFields = BookingFieldService::sanitizeBookingFields($bookingFields, $calendarEvent); $calendarEvent->setBookingFields($formattedFields); return [ 'message' => __('Fields has been updated', 'fluent-booking') ]; } public function getEventPaymentSettings($calendarId, $eventId) { $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId); $config = [ 'native_enabled' => Helper::isPaymentEnabled(), 'stripe_configured' => Helper::isPaymentConfigured('stripe'), 'paypal_configured' => Helper::isPaymentConfigured('paypal'), 'offline_configured' => Helper::isPaymentConfigured('offline'), 'native_config_link' => Helper::getAppBaseUrl('settings/payment-methods/stripe'), 'woo_config_link' => Helper::getAppBaseUrl('settings/configure-integrations/global-modules'), 'has_cart' => defined('FLUENTCART_VERSION'), 'has_woo' => defined('WC_PLUGIN_FILE'), 'woo_enabled' => defined('WC_PLUGIN_FILE') && Helper::isModuleEnabled('woo') ]; $data = apply_filters('fluent_booking/payment/get_payment_settings', [ 'settings' => $calendarEvent->getPaymentSettings(), 'config' => $config ], $calendarEvent); return $data; } public function deleteCalendarEvent(Request $request, $calendarId, $calendarEventId) { $calendar = Calendar::query()->findOrFail($calendarId); $calendarEvent = CalendarSlot::query()->where('calendar_id', $calendar->id)->findOrFail($calendarEventId); $calendar->updateEventOrder($calendarEvent->id); do_action('fluent_booking/before_delete_calendar_event', $calendarEvent, $calendar); $calendarEvent->delete(); do_action('fluent_booking/after_delete_calendar_event', $calendarEventId, $calendar); return [ 'message' => __('Calendar Event has been deleted', 'fluent-booking') ]; } private function sanitize_mapped_data($settings) { $sanitizerMap = [ 'value' => 'intval', 'unit' => 'sanitize_text_field', 'subject' => 'sanitize_text_field', 'body' => 'fcal_sanitize_html', 'additional_recipients' => 'sanitize_text_field' ]; return Helper::fcal_backend_sanitizer($settings, $sanitizerMap); } public function deleteCalendar(Request $request, $calendarId) { $calendar = Calendar::findOrFail($calendarId); do_action('fluent_booking/before_delete_calendar', $calendar); $calendar->delete(); do_action('fluent_booking/after_delete_calendar', $calendarId); return [ 'message' => __('Calendar Deleted Successfully!', 'fluent-booking') ]; } }