PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 1.5.20
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v1.5.20
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
fluent-booking / app / Services / CalendarService.php

CalendarService.php in Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution 1.5.20, at app/Services/CalendarService.php

436 lines 17.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBooking\App\Services;
4
5 use FluentBooking\App\Models\Calendar;
6 use FluentBooking\App\Models\CalendarSlot;
7 use FluentBooking\App\Models\Availability;
8 use FluentBooking\App\Services\Helper;
9 use FluentBooking\Framework\Support\Arr;
10
11 class CalendarService
12 {
13 public static function createCalendar($data, $useCurrentUser = false)
14 {
15 $calendarData = self::prepareCalendarData($data, $useCurrentUser);
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);
97
98 $event->event_metas()->createMany($eventMetasData);
99 }
100
101 return $createdEvents;
102 }
103
104 protected static function prepareCalendarData($calendarData, $useCurrentUser = 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')),
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 if ($isHostCalendar && Calendar::where('user_id', $preparedData['user_id'])->where('type', 'simple')->first()) {
147 return new \WP_Error('calendar_exists', esc_html__('The user already have a calendar. Please delete it first to create a new one', 'fluent-booking'));
148 }
149
150 return $preparedData;
151 }
152
153 protected static function prepareCalendarMetas($calendarMetas)
154 {
155 $preparedCalendarMetas = [];
156
157 foreach ($calendarMetas as $calendarMeta) {
158 if (empty($calendarMeta['key']) || empty($calendarMeta['value'])) {
159 continue;
160 }
161
162 $value = $calendarMeta['value'];
163
164 $preparedCalendarMetas[] = [
165 'key' => sanitize_text_field($calendarMeta['key']),
166 'value' => is_array($value) ? self::sanitize_mapped_data($value) : sanitize_text_field($value),
167 'object_type' => sanitize_text_field($calendarMeta['object_type'])
168 ];
169 }
170
171 return $preparedCalendarMetas;
172 }
173
174 protected static function prepareEventData($eventData, $calendar, $availabilities = [])
175 {
176 $preparedEventData = [
177 'title' => sanitize_text_field(Arr::get($eventData, 'title')),
178 'duration' => (int)Arr::get($eventData, 'duration', 30),
179 'description' => sanitize_textarea_field(Arr::get($eventData, 'description')),
180 'type' => sanitize_text_field(Arr::get($eventData, 'type')),
181 'status' => sanitize_text_field(Arr::get($eventData, 'status', 'active')),
182 'color_schema' => sanitize_text_field(Arr::get($eventData, 'color_schema', '#0099ff')),
183 'event_type' => sanitize_text_field(Arr::get($eventData, 'event_type')),
184 'availability_id' => (int)self::prepareAvailabilityId(Arr::get($eventData, 'availability_id', 0), $availabilities),
185 'availability_type' => sanitize_text_field(Arr::get($eventData, 'availability_type')),
186 'location_type' => sanitize_text_field(Arr::get($eventData, 'location_type')),
187 'location_settings' => SanitizeService::locationSettings(Arr::get($eventData, 'location_settings', [])),
188 'max_book_per_slot' => (int)Arr::get($eventData, 'max_book_per_slot', 1),
189 'is_display_spots' => (bool)Arr::get($eventData, 'is_display_spots', false),
190 ];
191
192 if (!empty($eventData['hash'])) {
193 $hash = sanitize_text_field($eventData['hash']);
194 if (!CalendarSlot::where('hash', $hash)->exists()) {
195 $preparedEventData['hash'] = $hash;
196 }
197 }
198
199 $eventSettings = Arr::get($eventData, 'settings', []);
200
201 if (!$eventSettings) {
202 return $preparedEventData;
203 }
204
205 $preparedEventData['settings'] = [
206 'schedule_type' => sanitize_text_field(Arr::get($eventSettings, 'schedule_type')),
207 'weekly_schedules' => SanitizeService::weeklySchedules(Arr::get($eventSettings, 'weekly_schedules', []), $calendar->author_timezone, 'UTC'),
208 'date_overrides' => SanitizeService::slotDateOverrides(Arr::get($eventSettings, 'date_overrides', []), $calendar->author_timezone, 'UTC'),
209 'range_type' => sanitize_text_field(Arr::get($eventSettings, 'range_type')),
210 'range_days' => (int)(Arr::get($eventSettings, 'range_days', 60)) ?: 60,
211 'range_date_between' => SanitizeService::rangeDateBetween(Arr::get($eventSettings, 'range_date_between', ['', ''])),
212 'schedule_conditions' => SanitizeService::scheduleConditions(Arr::get($eventSettings, 'schedule_conditions', [])),
213 'common_schedule' => Arr::isTrue($eventSettings, 'common_schedule', false),
214 'buffer_time_before' => sanitize_text_field(Arr::get($eventSettings, 'buffer_time_before', '0')),
215 'buffer_time_after' => sanitize_text_field(Arr::get($eventSettings, 'buffer_time_after', '0')),
216 'slot_interval' => sanitize_text_field(Arr::get($eventSettings, 'slot_interval', '')),
217 'team_members' => array_map('intval', Arr::get($eventSettings, 'team_members', [])),
218 'multi_duration' => [
219 'enabled' => Arr::isTrue($eventSettings, 'multi_duration.enabled'),
220 'default_duration' => Arr::get($eventSettings, 'multi_duration.default_duration', ''),
221 'available_durations' => array_map('sanitize_text_field', Arr::get($eventSettings, 'multi_duration.available_durations', []))
222 ],
223 'booking_frequency' => [
224 'enabled' => Arr::isTrue($eventSettings, 'booking_frequency.enabled'),
225 'limits' => self::sanitize_mapped_data(Arr::get($eventSettings, 'booking_frequency.limits', []))
226 ],
227 'booking_duration' => [
228 'enabled' => Arr::isTrue($eventSettings, 'booking_duration.enabled'),
229 'limits' => self::sanitize_mapped_data(Arr::get($eventSettings, 'booking_duration.limits', []))
230 ],
231 'lock_timezone' => [
232 'enabled' => Arr::isTrue($eventSettings, 'lock_timezone.enabled'),
233 'timezone' => sanitize_text_field(Arr::get($eventSettings, 'lock_timezone.timezone'))
234 ],
235 ];
236
237 return $preparedEventData;
238 }
239
240 protected static function prepareEventMetas($eventMetasData)
241 {
242 $preparedEventMetas = [];
243
244 foreach ($eventMetasData as $eventMeta) {
245 if (empty($eventMeta['key']) || empty($eventMeta['value'])) {
246 continue;
247 }
248
249 $value = $eventMeta['value'];
250
251 if ($eventMeta['key'] == 'email_notification') {
252 $value = self::updateNotificationImageUrl($value);
253 }
254
255 $preparedEventMetas[] = [
256 'key' => sanitize_text_field($eventMeta['key']),
257 'value' => is_array($value) ? self::sanitize_mapped_data($value) : sanitize_text_field($value),
258 'object_type' => sanitize_text_field($eventMeta['object_type'])
259 ];
260 }
261
262 return $preparedEventMetas;
263 }
264
265 protected static function prepareAvailabilityId($availabilityId, $availabilities)
266 {
267 if ($availabilityId && isset($availabilities[$availabilityId])) {
268 return $availabilities[$availabilityId];
269 }
270
271 if ($availabilities) {
272 $firstKey = array_key_first($availabilities);
273 return $availabilities[$firstKey];
274 }
275
276 return $availabilityId;
277 }
278
279 protected static function updateNotificationImageUrl($notifications)
280 {
281 $formattedNotifications = [];
282
283 foreach ($notifications as $key => $notification) {
284 $emailBody = Arr::get($notification, 'email.body');
285 if (!$emailBody) {
286 continue;
287 }
288
289 $newImageUrl = FLUENT_BOOKING_URL . 'assets/images';
290 $pattern = '/(https:\/\/[^"]*?' . preg_quote('assets/images', '/') . ')/';
291 $emailBody = preg_replace($pattern, $newImageUrl, $emailBody);
292 $notification['email']['body'] = $emailBody;
293
294 $formattedNotifications[$key] = $notification;
295 }
296
297 return $formattedNotifications;
298 }
299
300 public static function getSlotOptions($calendarId)
301 {
302 $calendarSlots = CalendarSlot::select(['id', 'title'])
303 ->where('calendar_id', $calendarId)
304 ->where('status', '!=', 'expired')
305 ->latest()
306 ->get();
307
308 $options = [];
309 foreach ($calendarSlots as $slot) {
310 $options[] = [
311 'id' => $slot->id,
312 'label' => $slot->title,
313 ];
314 }
315 return apply_filters('fluent_booking/calendar_event_options', $options, $calendarId);
316 }
317
318 public static function getCalendarOptionsByHost()
319 {
320 $calendars = Calendar::select(['id', 'title'])
321 ->when(!PermissionManager::hasAllCalendarAccess(true), function ($query) {
322 return $query->where('user_id', get_current_user_id());
323 })
324 ->with(['slots' => function ($query) {
325 $query->where('status', '!=', 'expired');
326 }])
327 ->latest()
328 ->get();
329
330 $formattedCalendars = [];
331 foreach ($calendars as $index => $calendar) {
332 $slots = Arr::get($calendar, 'slots');
333 if (!empty($slots)) {
334 $options = [];
335 foreach ($slots as $slot) {
336 $options[] = [
337 'label' => Arr::get($slot, 'title'),
338 'value' => Arr::get($slot, 'id')
339 ];
340 }
341 if (!empty($options)) {
342 $formattedCalendars[$index] = [
343 'label' => Arr::get($calendar, 'title'),
344 'options' => $options
345 ];
346 }
347 }
348 }
349 return $formattedCalendars;
350 }
351
352 public static function getCalendarOptionsByTitle($condition = '')
353 {
354 $calendarQuery = Calendar::select(['id', 'title'])
355 ->where('status', '!=', 'expired')
356 ->when(!PermissionManager::hasAllCalendarAccess(true), function ($query) {
357 return $query->where('user_id', get_current_user_id());
358 });
359
360 switch ($condition) {
361 case 'only_hosts':
362 $calendarQuery->where('type', 'simple');
363 break;
364 case 'only_teams':
365 $calendarQuery->where('type', 'team');
366 break;
367 case 'only_events':
368 $calendarQuery->where('type', 'event');
369 break;
370 }
371
372 $calendars = $calendarQuery->with(['slots' => function ($query) {
373 $query->where('status', '!=', 'expired');
374 }])->latest()->get();
375
376 $formattedCalendars = [];
377 foreach ($calendars as $index => $calendar) {
378 $slots = Arr::get($calendar, 'slots');
379 if (!empty($slots)) {
380 $options = [];
381 foreach ($slots as $slot) {
382 $options[] = [
383 'id' => Arr::get($slot, 'id'),
384 'title' => Arr::get($slot, 'title')
385 ];
386 }
387 if (!empty($options)) {
388 $formattedCalendars[$index] = [
389 'id' => Arr::get($calendar, 'id'),
390 'title' => Arr::get($calendar, 'title'),
391 'options' => $options
392 ];
393 }
394 }
395 }
396 return apply_filters('fluent_booking/calendar_options_by_title', $formattedCalendars);
397 }
398
399 public static function updateCalendarEventsSchedule($calendarId, $oldTimezone, $updatedTimezone)
400 {
401 $calendarEvents = CalendarSlot::query()->where('calendar_id', $calendarId)->get();
402
403 foreach ($calendarEvents as $event) {
404 if ($weeklySchedule = Arr::get($event->settings, 'weekly_schedules', [])) {
405 $originalSchedule = SanitizeService::weeklySchedules($weeklySchedule, 'UTC', $oldTimezone);
406 $weeklySchedule = SanitizeService::weeklySchedules($originalSchedule, $updatedTimezone, 'UTC');
407 }
408
409 if ($dateOverride = Arr::get($event->settings, 'date_overrides', [])) {
410 $originalOverride = SanitizeService::slotDateOverrides($dateOverride, 'UTC', $oldTimezone);
411 $dateOverride = SanitizeService::slotDateOverrides($originalOverride, $updatedTimezone, 'UTC');
412 }
413
414 $event->settings = [
415 'weekly_schedules' => $weeklySchedule,
416 'date_overrides' => $dateOverride
417 ];
418
419 $event->save();
420 }
421 }
422
423 private static function sanitize_mapped_data($settings)
424 {
425 $sanitizerMap = [
426 'value' => 'intval',
427 'unit' => 'sanitize_text_field',
428 'subject' => 'sanitize_text_field',
429 'body' => 'fcal_sanitize_html',
430 'additional_recipients' => 'sanitize_text_field'
431 ];
432
433 return Helper::fcal_backend_sanitizer($settings, $sanitizerMap);
434 }
435 }
436