| @@ -1,18 +1,369 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | namespace FluentBooking\App\Services; |
| 4 | 4 | |
| 5 | -use FluentBooking\Framework\Support\Arr; | |
| 6 | 5 | use FluentBooking\App\Models\Calendar; |
| 7 | 6 | use FluentBooking\App\Models\CalendarSlot; |
| 7 | +use FluentBooking\App\Models\Availability; | |
| 8 | +use FluentBooking\App\Services\Helper; | |
| 9 | +use FluentBooking\Framework\Support\Arr; | |
| 8 | 10 | |
| 9 | 11 | class CalendarService |
| 10 | 12 | { |
| 11 | - public static function getSlotOptions($calendarId) | |
| 13 | + public static function createCalendar($data, $useCurrentUser = false, $isFileInput = false) | |
| 12 | 14 | { |
| 15 | + $calendarData = self::prepareCalendarData($data, $useCurrentUser, $isFileInput); | |
| 16 | + | |
| 17 | + if (is_wp_error($calendarData)) { | |
| 18 | + return new \WP_Error($calendarData->get_error_code(), $calendarData->get_error_message()); | |
| 19 | + } | |
| 20 | + | |
| 21 | + $calendarData['slug'] = sanitize_title($calendarData['title']); | |
| 22 | + | |
| 23 | + if (!Helper::isCalendarSlugAvailable($calendarData['slug'], true)) { | |
| 24 | + $calendarData['slug'] .= '-' . time(); | |
| 25 | + } | |
| 26 | + | |
| 27 | + $calendar = Calendar::create($calendarData); | |
| 28 | + | |
| 29 | + $calendarMetas = self::prepareCalendarMetas(Arr::get($data, 'metas', [])); | |
| 30 | + | |
| 31 | + $calendar->metas()->createMany($calendarMetas); | |
| 32 | + | |
| 33 | + $availabilitiesData = Arr::get($data, 'availabilities', []); | |
| 34 | + | |
| 35 | + $createdAvailabilities = self::createAvailabilities($calendar, $availabilitiesData); | |
| 36 | + | |
| 37 | + $eventsData = Arr::get($data, 'events', []); | |
| 38 | + | |
| 39 | + self::createCalendarEvents($calendar, $eventsData, $createdAvailabilities); | |
| 40 | + | |
| 41 | + do_action('fluent_booking/after_create_calendar', $calendar); | |
| 42 | + | |
| 43 | + return [ | |
| 44 | + 'calendar' => $calendar | |
| 45 | + ]; | |
| 46 | + } | |
| 47 | + | |
| 48 | + public static function createAvailabilities($calendar, $availabilitiesData) | |
| 49 | + { | |
| 50 | + $createdAvailabilities = []; | |
| 51 | + | |
| 52 | + foreach ($availabilitiesData as $existingId => $availabilityData) { | |
| 53 | + $availability = Arr::only($availabilityData, ['key', 'value']); | |
| 54 | + $availability['value']['timezone'] = $calendar->author_timezone; | |
| 55 | + $availability['object_id'] = $calendar->user_id; | |
| 56 | + $availabilityModel = Availability::create($availability); | |
| 57 | + $createdAvailabilities[$existingId] = $availabilityModel->id; | |
| 58 | + } | |
| 59 | + | |
| 60 | + return $createdAvailabilities; | |
| 61 | + } | |
| 62 | + | |
| 63 | + public static function createCalendarEvents($calendar, $eventsData, $availabilities = []) | |
| 64 | + { | |
| 65 | + $defaultEventData = (new CalendarSlot())->getEventDefaultData($calendar); | |
| 66 | + | |
| 67 | + if (empty($eventsData)) { | |
| 68 | + $eventsData = [$defaultEventData]; | |
| 69 | + } | |
| 70 | + | |
| 71 | + $createEventsData = []; | |
| 72 | + | |
| 73 | + $createEventMetasData = []; | |
| 74 | + | |
| 75 | + foreach ($eventsData as $eventData) { | |
| 76 | + $eventMetas = Arr::get($eventData, 'event_metas', []); | |
| 77 | + | |
| 78 | + $eventData = self::prepareEventData($eventData, $calendar, $availabilities); | |
| 79 | + | |
| 80 | + $createEventData = wp_parse_args($eventData, $defaultEventData); | |
| 81 | + | |
| 82 | + $createEventData['slug'] = Helper::generateSlotSlug((int)$createEventData['duration'] . 'min', $calendar); | |
| 83 | + | |
| 84 | + $createEventData['settings'] = wp_parse_args($createEventData['settings'], $defaultEventData['settings']); | |
| 85 | + | |
| 86 | + $createEventsData[] = Arr::only($createEventData, (new CalendarSlot())->getFillable()); | |
| 87 | + | |
| 88 | + $createEventMetasData[] = $eventMetas; | |
| 89 | + } | |
| 90 | + | |
| 91 | + $createdEvents = $calendar->events()->createMany($createEventsData); | |
| 92 | + | |
| 93 | + foreach ($createdEvents as $index => $event) { | |
| 94 | + $eventMetasData = Arr::get($createEventMetasData, $index, []); | |
| 95 | + | |
| 96 | + $eventMetasData = self::prepareEventMetas($eventMetasData, $event); | |
| 97 | + | |
| 98 | + $event->event_metas()->createMany($eventMetasData); | |
| 99 | + } | |
| 100 | + | |
| 101 | + return $createdEvents; | |
| 102 | + } | |
| 103 | + | |
| 104 | + protected static function prepareCalendarData($calendarData, $useCurrentUser = false, $isFileInput = false) | |
| 105 | + { | |
| 106 | + if (!$calendarData) { | |
| 107 | + return new \WP_Error('invalid_data', esc_html__('Invalid JSON Data', 'fluent-booking')); | |
| 108 | + } | |
| 109 | + | |
| 110 | + $preparedData = [ | |
| 111 | + 'title' => sanitize_text_field(Arr::get($calendarData, 'title')), | |
| 112 | + 'description' => sanitize_textarea_field(Arr::get($calendarData, 'description')), | |
| 113 | + 'user_id' => intval(Arr::get($calendarData, 'user_id')), | |
| 114 | + 'status' => sanitize_text_field(Arr::get($calendarData, 'status', 'active')), | |
| 115 | + 'type' => sanitize_text_field(Arr::get($calendarData, 'type', 'simple')), | |
| 116 | + 'event_type' => sanitize_text_field(Arr::get($calendarData, 'event_type')), | |
| 117 | + 'account_type' => sanitize_text_field(Arr::get($calendarData, 'account_type')), | |
| 118 | + 'visibility' => sanitize_text_field(Arr::get($calendarData, 'visibility')), | |
| 119 | + 'author_timezone' => sanitize_text_field(Arr::get($calendarData, 'author_timezone')), | |
| 120 | + ]; | |
| 121 | + | |
| 122 | + if ($useCurrentUser || !Arr::get($preparedData, 'user_id')) { | |
| 123 | + $preparedData['user_id'] = get_current_user_id(); | |
| 124 | + } | |
| 125 | + | |
| 126 | + if (!Arr::get($preparedData, 'author_timezone')) { | |
| 127 | + $preparedData['author_timezone'] = wp_timezone_string(); | |
| 128 | + } | |
| 129 | + | |
| 130 | + if (!in_array(Arr::get($preparedData, 'type'), ['simple', 'team', 'event'])) { | |
| 131 | + $preparedData['type'] = 'simple'; | |
| 132 | + } | |
| 133 | + | |
| 134 | + $user = get_user_by('ID', $preparedData['user_id']); | |
| 135 | + | |
| 136 | + if (!$user) { | |
| 137 | + return new \WP_Error('invalid_user', esc_html__('Invalid User ID', 'fluent-booking')); | |
| 138 | + } | |
| 139 | + | |
| 140 | + $isHostCalendar = $preparedData['type'] == 'simple' ? true : false; | |
| 141 | + | |
| 142 | + if ($isHostCalendar || !$preparedData['title']) { | |
| 143 | + $preparedData['title'] = is_email($user->user_login) ? explode('@', $user->user_login)[0] : $user->user_login; | |
| 144 | + } | |
| 145 | + | |
| 146 | + $firstCalendar = Calendar::where('user_id', $preparedData['user_id'])->where('type', 'simple')->first(); | |
| 147 | + | |
| 148 | + if ($isHostCalendar && $firstCalendar) { | |
| 149 | + if ($isFileInput) { | |
| 150 | + return new \WP_Error('calendar_exists', esc_html__('The user already has a calendar. Please delete it first to create a new one', 'fluent-booking')); | |
| 151 | + } | |
| 152 | + return $firstCalendar; | |
| 153 | + } | |
| 154 | + | |
| 155 | + return $preparedData; | |
| 156 | + } | |
| 157 | + | |
| 158 | + protected static function prepareCalendarMetas($calendarMetas) | |
| 159 | + { | |
| 160 | + $preparedCalendarMetas = []; | |
| 161 | + | |
| 162 | + foreach ($calendarMetas as $calendarMeta) { | |
| 163 | + if (empty($calendarMeta['key']) || empty($calendarMeta['value'])) { | |
| 164 | + continue; | |
| 165 | + } | |
| 166 | + | |
| 167 | + $value = $calendarMeta['value']; | |
| 168 | + | |
| 169 | + $preparedCalendarMetas[] = [ | |
| 170 | + 'key' => sanitize_text_field($calendarMeta['key']), | |
| 171 | + 'value' => is_array($value) ? self::sanitize_mapped_data($value) : sanitize_text_field($value), | |
| 172 | + // Bound to the imported calendar; never let the payload pick the type | |
| 173 | + // (e.g. a user_meta / _access_permissions row). | |
| 174 | + 'object_type' => 'Calendar' | |
| 175 | + ]; | |
| 176 | + } | |
| 177 | + | |
| 178 | + return $preparedCalendarMetas; | |
| 179 | + } | |
| 180 | + | |
| 181 | + protected static function prepareEventData($eventData, $calendar, $availabilities = []) | |
| 182 | + { | |
| 183 | + $preparedEventData = [ | |
| 184 | + 'title' => sanitize_text_field(Arr::get($eventData, 'title')), | |
| 185 | + 'duration' => (int)Arr::get($eventData, 'duration', 30), | |
| 186 | + 'description' => wp_kses_post(Arr::get($eventData, 'description')), | |
| 187 | + 'type' => sanitize_text_field(Arr::get($eventData, 'type')), | |
| 188 | + 'status' => sanitize_text_field(Arr::get($eventData, 'status', 'active')), | |
| 189 | + 'color_schema' => sanitize_text_field(Arr::get($eventData, 'color_schema', '#0099ff')), | |
| 190 | + 'event_type' => sanitize_text_field(Arr::get($eventData, 'event_type')), | |
| 191 | + 'availability_id' => (int)self::prepareAvailabilityId(Arr::get($eventData, 'availability_id', 0), $availabilities), | |
| 192 | + 'availability_type' => sanitize_text_field(Arr::get($eventData, 'availability_type')), | |
| 193 | + 'location_type' => sanitize_text_field(Arr::get($eventData, 'location_type')), | |
| 194 | + 'location_settings' => SanitizeService::locationSettings(Arr::get($eventData, 'location_settings', [])), | |
| 195 | + 'max_book_per_slot' => (int)Arr::get($eventData, 'max_book_per_slot', 1), | |
| 196 | + 'is_display_spots' => (bool)Arr::get($eventData, 'is_display_spots', false), | |
| 197 | + ]; | |
| 198 | + | |
| 199 | + if (!empty($eventData['hash'])) { | |
| 200 | + $hash = sanitize_text_field($eventData['hash']); | |
| 201 | + if (!CalendarSlot::where('hash', $hash)->exists()) { | |
| 202 | + $preparedEventData['hash'] = $hash; | |
| 203 | + } | |
| 204 | + } | |
| 205 | + | |
| 206 | + $eventSettings = Arr::get($eventData, 'settings', []); | |
| 207 | + | |
| 208 | + if (!$eventSettings) { | |
| 209 | + return $preparedEventData; | |
| 210 | + } | |
| 211 | + | |
| 212 | + $preparedEventData['settings'] = [ | |
| 213 | + 'schedule_type' => sanitize_text_field(Arr::get($eventSettings, 'schedule_type')), | |
| 214 | + 'weekly_schedules' => SanitizeService::weeklySchedules(Arr::get($eventSettings, 'weekly_schedules', []), $calendar->author_timezone, 'UTC', true), | |
| 215 | + 'date_overrides' => SanitizeService::slotDateOverrides(Arr::get($eventSettings, 'date_overrides', []), $calendar->author_timezone, 'UTC', null, true), | |
| 216 | + 'range_type' => sanitize_text_field(Arr::get($eventSettings, 'range_type')), | |
| 217 | + 'range_days' => (int)(Arr::get($eventSettings, 'range_days', 60)) ?: 60, | |
| 218 | + 'range_date_between' => SanitizeService::rangeDateBetween(Arr::get($eventSettings, 'range_date_between', ['', ''])), | |
| 219 | + 'schedule_conditions' => SanitizeService::scheduleConditions(Arr::get($eventSettings, 'schedule_conditions', [])), | |
| 220 | + 'common_schedule' => Arr::isTrue($eventSettings, 'common_schedule', false), | |
| 221 | + 'buffer_time_before' => sanitize_text_field(Arr::get($eventSettings, 'buffer_time_before', '0')), | |
| 222 | + 'buffer_time_after' => sanitize_text_field(Arr::get($eventSettings, 'buffer_time_after', '0')), | |
| 223 | + 'slot_interval' => sanitize_text_field(Arr::get($eventSettings, 'slot_interval', '')), | |
| 224 | + 'team_members' => array_map('intval', Arr::get($eventSettings, 'team_members', [])), | |
| 225 | + 'multi_duration' => [ | |
| 226 | + 'enabled' => Arr::isTrue($eventSettings, 'multi_duration.enabled'), | |
| 227 | + 'default_duration' => Arr::get($eventSettings, 'multi_duration.default_duration', ''), | |
| 228 | + 'available_durations' => array_map('sanitize_text_field', Arr::get($eventSettings, 'multi_duration.available_durations', [])) | |
| 229 | + ], | |
| 230 | + 'booking_frequency' => [ | |
| 231 | + 'enabled' => Arr::isTrue($eventSettings, 'booking_frequency.enabled'), | |
| 232 | + 'limits' => self::sanitize_mapped_data(Arr::get($eventSettings, 'booking_frequency.limits', [])) | |
| 233 | + ], | |
| 234 | + 'booking_duration' => [ | |
| 235 | + 'enabled' => Arr::isTrue($eventSettings, 'booking_duration.enabled'), | |
| 236 | + 'limits' => self::sanitize_mapped_data(Arr::get($eventSettings, 'booking_duration.limits', [])) | |
| 237 | + ], | |
| 238 | + 'lock_timezone' => [ | |
| 239 | + 'enabled' => Arr::isTrue($eventSettings, 'lock_timezone.enabled'), | |
| 240 | + 'timezone' => sanitize_text_field(Arr::get($eventSettings, 'lock_timezone.timezone')) | |
| 241 | + ], | |
| 242 | + 'booking_title' => sanitize_text_field(Arr::get($eventSettings, 'booking_title')), | |
| 243 | + 'submit_button_text' => sanitize_text_field(Arr::get($eventSettings, 'submit_button_text')), | |
| 244 | + 'custom_redirect' => [ | |
| 245 | + 'enabled' => Arr::isTrue($eventSettings, 'custom_redirect.enabled'), | |
| 246 | + 'redirect_url' => sanitize_url(Arr::get($eventSettings, 'custom_redirect.redirect_url')), | |
| 247 | + 'is_query_string' => Arr::get($eventSettings, 'custom_redirect.is_query_string') == 'yes' ? 'yes' : 'no', | |
| 248 | + 'query_string' => sanitize_text_field(Arr::get($eventSettings, 'custom_redirect.query_string')), | |
| 249 | + ], | |
| 250 | + 'requires_confirmation' => [ | |
| 251 | + 'enabled' => Arr::isTrue($eventSettings, 'requires_confirmation.enabled'), | |
| 252 | + 'type' => sanitize_text_field(Arr::get($eventSettings, 'requires_confirmation.type')), | |
| 253 | + 'condition' => [ | |
| 254 | + 'unit' => sanitize_text_field(Arr::get($eventSettings, 'requires_confirmation.condition.unit')), | |
| 255 | + 'value' => intval(Arr::get($eventSettings, 'requires_confirmation.condition.value')) | |
| 256 | + ] | |
| 257 | + ], | |
| 258 | + 'multiple_booking' => [ | |
| 259 | + 'enabled' => Arr::isTrue($eventSettings, 'multiple_booking.enabled'), | |
| 260 | + 'limit' => intval(Arr::get($eventSettings, 'multiple_booking.limit')) | |
| 261 | + ], | |
| 262 | + 'can_not_cancel' => [ | |
| 263 | + 'enabled' => Arr::isTrue($eventSettings, 'can_not_cancel.enabled'), | |
| 264 | + 'type' => sanitize_text_field(Arr::get($eventSettings, 'can_not_cancel.type')), | |
| 265 | + 'message' => sanitize_text_field(Arr::get($eventSettings, 'can_not_cancel.message')), | |
| 266 | + 'condition' => [ | |
| 267 | + 'unit' => sanitize_text_field(Arr::get($eventSettings, 'can_not_cancel.condition.unit')), | |
| 268 | + 'value' => intval(Arr::get($eventSettings, 'can_not_cancel.condition.value')) | |
| 269 | + ] | |
| 270 | + ], | |
| 271 | + 'can_not_reschedule' => [ | |
| 272 | + 'enabled' => Arr::isTrue($eventSettings, 'can_not_reschedule.enabled'), | |
| 273 | + 'type' => sanitize_text_field(Arr::get($eventSettings, 'can_not_reschedule.type')), | |
| 274 | + 'message' => sanitize_text_field(Arr::get($eventSettings, 'can_not_reschedule.message')), | |
| 275 | + 'condition' => [ | |
| 276 | + 'unit' => sanitize_text_field(Arr::get($eventSettings, 'can_not_reschedule.condition.unit')), | |
| 277 | + 'value' => intval(Arr::get($eventSettings, 'can_not_reschedule.condition.value')) | |
| 278 | + ] | |
| 279 | + ] | |
| 280 | + ]; | |
| 281 | + | |
| 282 | + return $preparedEventData; | |
| 283 | + } | |
| 284 | + | |
| 285 | + protected static function prepareEventMetas($eventMetasData, $event = null) | |
| 286 | + { | |
| 287 | + $preparedEventMetas = []; | |
| 288 | + | |
| 289 | + foreach ($eventMetasData as $eventMeta) { | |
| 290 | + if (empty($eventMeta['key']) || empty($eventMeta['value'])) { | |
| 291 | + continue; | |
| 292 | + } | |
| 293 | + | |
| 294 | + $value = $eventMeta['value']; | |
| 295 | + | |
| 296 | + // Only the two types an event actually stores; anything else in the | |
| 297 | + // payload (e.g. user_meta / _access_permissions) is dropped. | |
| 298 | + $objectType = sanitize_text_field(Arr::get($eventMeta, 'object_type')); | |
| 299 | + if (!in_array($objectType, ['calendar_event', 'integration'], true)) { | |
| 300 | + continue; | |
| 301 | + } | |
| 302 | + | |
| 303 | + if ($eventMeta['key'] == 'email_notification') { | |
| 304 | + $value = self::updateNotificationImageUrl($value); | |
| 305 | + } | |
| 306 | + | |
| 307 | + // Rendered on the public form via {@html}; sanitize as the admin path does. | |
| 308 | + if ($eventMeta['key'] == 'booking_fields' && is_array($value)) { | |
| 309 | + $value = BookingFieldService::sanitizeBookingFields($value, $event); | |
| 310 | + } | |
| 311 | + | |
| 312 | + $preparedEventMetas[] = [ | |
| 313 | + 'key' => sanitize_text_field($eventMeta['key']), | |
| 314 | + 'value' => is_array($value) ? self::sanitize_mapped_data($value) : sanitize_text_field($value), | |
| 315 | + 'object_type' => $objectType | |
| 316 | + ]; | |
| 317 | + } | |
| 318 | + | |
| 319 | + return $preparedEventMetas; | |
| 320 | + } | |
| 321 | + | |
| 322 | + protected static function prepareAvailabilityId($availabilityId, $availabilities) | |
| 323 | + { | |
| 324 | + if ($availabilityId && isset($availabilities[$availabilityId])) { | |
| 325 | + return $availabilities[$availabilityId]; | |
| 326 | + } | |
| 327 | + | |
| 328 | + if ($availabilities) { | |
| 329 | + $firstKey = array_key_first($availabilities); | |
| 330 | + return $availabilities[$firstKey]; | |
| 331 | + } | |
| 332 | + | |
| 333 | + return $availabilityId; | |
| 334 | + } | |
| 335 | + | |
| 336 | + protected static function updateNotificationImageUrl($notifications) | |
| 337 | + { | |
| 338 | + $formattedNotifications = []; | |
| 339 | + | |
| 340 | + foreach ($notifications as $key => $notification) { | |
| 341 | + $emailBody = Arr::get($notification, 'email.body'); | |
| 342 | + if (!$emailBody) { | |
| 343 | + continue; | |
| 344 | + } | |
| 345 | + | |
| 346 | + $newImageUrl = FLUENT_BOOKING_URL . 'assets/images'; | |
| 347 | + $pattern = '/(https:\/\/[^"]*?' . preg_quote('assets/images', '/') . ')/'; | |
| 348 | + $emailBody = preg_replace($pattern, $newImageUrl, $emailBody); | |
| 349 | + $notification['email']['body'] = $emailBody; | |
| 350 | + | |
| 351 | + $formattedNotifications[$key] = $notification; | |
| 352 | + } | |
| 353 | + | |
| 354 | + return $formattedNotifications; | |
| 355 | + } | |
| 356 | + | |
| 357 | + public static function getSlotOptions($calendarId = null, $userId = null) | |
| 358 | + { | |
| 13 | 359 | $calendarSlots = CalendarSlot::select(['id', 'title']) |
| 14 | - ->where('calendar_id', $calendarId) | |
| 360 | + ->when($calendarId, function ($query) use ($calendarId) { | |
| 361 | + return $query->where('calendar_id', $calendarId); | |
| 362 | + }) | |
| 363 | + ->when($userId, function ($query) use ($userId) { | |
| 364 | + return $query->where('user_id', $userId); | |
| 365 | + }) | |
| 15 | 366 | ->where('status', '!=', 'expired') |
| 16 | 367 | ->latest() |
| 17 | 368 | ->get(); |
| 18 | 369 | |
| @@ -31,9 +382,9 @@ | ||
| 31 | 382 | $calendars = Calendar::select(['id', 'title']) |
| 32 | 383 | ->when(!PermissionManager::hasAllCalendarAccess(true), function ($query) { |
| 33 | 384 | return $query->where('user_id', get_current_user_id()); |
| 34 | 385 | }) |
| 35 | - ->with(['slots' => function($query) { | |
| 386 | + ->with(['slots' => function ($query) { | |
| 36 | 387 | $query->where('status', '!=', 'expired'); |
| 37 | 388 | }]) |
| 38 | 389 | ->latest() |
| 39 | 390 | ->get(); |
| @@ -38,9 +389,9 @@ | ||
| 38 | 389 | ->latest() |
| 39 | 390 | ->get(); |
| 40 | 391 | |
| 41 | 392 | $formattedCalendars = []; |
| 42 | - foreach ($calendars as $index => $calendar) { | |
| 393 | + foreach ($calendars as $calendar) { | |
| 43 | 394 | $slots = Arr::get($calendar, 'slots'); |
| 44 | 395 | if (!empty($slots)) { |
| 45 | 396 | $options = []; |
| 46 | 397 | foreach ($slots as $slot) { |
| @@ -49,9 +400,9 @@ | ||
| 49 | 400 | 'value' => Arr::get($slot, 'id') |
| 50 | 401 | ]; |
| 51 | 402 | } |
| 52 | 403 | if (!empty($options)) { |
| 53 | - $formattedCalendars[$index] = [ | |
| 404 | + $formattedCalendars[] = [ | |
| 54 | 405 | 'label' => Arr::get($calendar, 'title'), |
| 55 | 406 | 'options' => $options |
| 56 | 407 | ]; |
| 57 | 408 | } |
| @@ -61,32 +412,35 @@ | ||
| 61 | 412 | } |
| 62 | 413 | |
| 63 | 414 | public static function getCalendarOptionsByTitle($condition = '') |
| 64 | 415 | { |
| 65 | - $calendarQuery = Calendar::select(['id', 'title']) | |
| 416 | + $calendarsQuery = Calendar::select(['id', 'title', 'user_id']) | |
| 66 | 417 | ->where('status', '!=', 'expired') |
| 67 | - ->when(!PermissionManager::hasAllCalendarAccess(true), function ($query) { | |
| 68 | - return $query->where('user_id', get_current_user_id()); | |
| 69 | - }); | |
| 418 | + ->with(['slots' => function ($query) { | |
| 419 | + $query->where('status', '!=', 'expired'); | |
| 420 | + }]); | |
| 70 | 421 | |
| 71 | 422 | switch ($condition) { |
| 72 | 423 | case 'only_hosts': |
| 73 | - $calendarQuery->where('type', 'simple'); | |
| 424 | + $calendarsQuery->where('type', 'simple'); | |
| 74 | 425 | break; |
| 75 | 426 | case 'only_teams': |
| 76 | - $calendarQuery->where('type', 'team'); | |
| 427 | + $calendarsQuery->where('type', 'team'); | |
| 77 | 428 | break; |
| 78 | 429 | case 'only_events': |
| 79 | - $calendarQuery->where('type', 'event'); | |
| 430 | + $calendarsQuery->where('type', 'event'); | |
| 80 | 431 | break; |
| 81 | 432 | } |
| 82 | 433 | |
| 83 | - $calendars = $calendarQuery->with(['slots' => function($query) { | |
| 84 | - $query->where('status', '!=', 'expired'); | |
| 85 | - }])->latest()->get(); | |
| 434 | + if (!PermissionManager::hasAllCalendarAccess(true)) { | |
| 435 | + $attachedCalendarIds = self::getAttachedCalendarIds($calendarsQuery); | |
| 436 | + $calendarsQuery->whereIn('id', $attachedCalendarIds); | |
| 437 | + } | |
| 86 | 438 | |
| 439 | + $calendars = $calendarsQuery->latest()->get(); | |
| 440 | + | |
| 87 | 441 | $formattedCalendars = []; |
| 88 | - foreach ($calendars as $index => $calendar) { | |
| 442 | + foreach ($calendars as $calendar) { | |
| 89 | 443 | $slots = Arr::get($calendar, 'slots'); |
| 90 | 444 | if (!empty($slots)) { |
| 91 | 445 | $options = []; |
| 92 | 446 | foreach ($slots as $slot) { |
| @@ -95,9 +449,9 @@ | ||
| 95 | 449 | 'title' => Arr::get($slot, 'title') |
| 96 | 450 | ]; |
| 97 | 451 | } |
| 98 | 452 | if (!empty($options)) { |
| 99 | - $formattedCalendars[$index] = [ | |
| 453 | + $formattedCalendars[] = [ | |
| 100 | 454 | 'id' => Arr::get($calendar, 'id'), |
| 101 | 455 | 'title' => Arr::get($calendar, 'title'), |
| 102 | 456 | 'options' => $options |
| 103 | 457 | ]; |
| @@ -106,22 +460,65 @@ | ||
| 106 | 460 | } |
| 107 | 461 | return apply_filters('fluent_booking/calendar_options_by_title', $formattedCalendars); |
| 108 | 462 | } |
| 109 | 463 | |
| 464 | + public static function getAttachedCalendarIds($calendarsQuery) | |
| 465 | + { | |
| 466 | + $userId = get_current_user_id(); | |
| 467 | + | |
| 468 | + $calendars = $calendarsQuery->get(); | |
| 469 | + | |
| 470 | + $calendarIds = []; | |
| 471 | + foreach ($calendars as $calendar) { | |
| 472 | + if ($calendar->user_id == $userId) { | |
| 473 | + $calendarIds[] = $calendar->id; | |
| 474 | + continue; | |
| 475 | + } | |
| 476 | + | |
| 477 | + $events = Arr::get($calendar, 'slots', []); | |
| 478 | + foreach ($events as $event) { | |
| 479 | + $teamMembers = Arr::get($event, 'settings.team_members', []); | |
| 480 | + if (in_array($userId, $teamMembers)) { | |
| 481 | + $calendarIds[] = $calendar->id; | |
| 482 | + } | |
| 483 | + } | |
| 484 | + } | |
| 485 | + | |
| 486 | + return $calendarIds; | |
| 487 | + } | |
| 488 | + | |
| 489 | + public static function isSharedCalendar($calendar) | |
| 490 | + { | |
| 491 | + $calendarEvents = $calendar->events; | |
| 492 | + | |
| 493 | + $userId = get_current_user_id(); | |
| 494 | + | |
| 495 | + foreach ($calendarEvents as $event) { | |
| 496 | + if ($event->user_id == $userId) { | |
| 497 | + return true; | |
| 498 | + } | |
| 499 | + $teamMembers = Arr::get($event, 'settings.team_members', []); | |
| 500 | + if (in_array($userId, $teamMembers)) { | |
| 501 | + return true; | |
| 502 | + } | |
| 503 | + } | |
| 504 | + | |
| 505 | + return false; | |
| 506 | + } | |
| 507 | + | |
| 110 | 508 | public static function updateCalendarEventsSchedule($calendarId, $oldTimezone, $updatedTimezone) |
| 111 | 509 | { |
| 112 | 510 | $calendarEvents = CalendarSlot::query()->where('calendar_id', $calendarId)->get(); |
| 113 | 511 | |
| 114 | - foreach ($calendarEvents as $event) | |
| 115 | - { | |
| 512 | + foreach ($calendarEvents as $event) { | |
| 116 | 513 | if ($weeklySchedule = Arr::get($event->settings, 'weekly_schedules', [])) { |
| 117 | 514 | $originalSchedule = SanitizeService::weeklySchedules($weeklySchedule, 'UTC', $oldTimezone); |
| 118 | - $weeklySchedule = SanitizeService::weeklySchedules($originalSchedule, $updatedTimezone, 'UTC'); | |
| 515 | + $weeklySchedule = SanitizeService::weeklySchedules($originalSchedule, $updatedTimezone, 'UTC', true); | |
| 119 | 516 | } |
| 120 | 517 | |
| 121 | 518 | if ($dateOverride = Arr::get($event->settings, 'date_overrides', [])) { |
| 122 | 519 | $originalOverride = SanitizeService::slotDateOverrides($dateOverride, 'UTC', $oldTimezone); |
| 123 | - $dateOverride = SanitizeService::slotDateOverrides($originalOverride, $updatedTimezone, 'UTC'); | |
| 520 | + $dateOverride = SanitizeService::slotDateOverrides($originalOverride, $updatedTimezone, 'UTC', null, true); | |
| 124 | 521 | } |
| 125 | 522 | |
| 126 | 523 | $event->settings = [ |
| 127 | 524 | 'weekly_schedules' => $weeklySchedule, |
| @@ -129,6 +526,19 @@ | ||
| 129 | 526 | ]; |
| 130 | 527 | |
| 131 | 528 | $event->save(); |
| 132 | 529 | } |
| 530 | + } | |
| 531 | + | |
| 532 | + private static function sanitize_mapped_data($settings) | |
| 533 | + { | |
| 534 | + $sanitizerMap = [ | |
| 535 | + 'value' => 'intval', | |
| 536 | + 'unit' => 'sanitize_text_field', | |
| 537 | + 'subject' => 'sanitize_text_field', | |
| 538 | + 'body' => 'fcal_sanitize_html', | |
| 539 | + 'additional_recipients' => 'sanitize_text_field' | |
| 540 | + ]; | |
| 541 | + | |
| 542 | + return Helper::fcal_backend_sanitizer($settings, $sanitizerMap); | |
| 133 | 543 | } |
| 134 | 544 | } |