PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 2.1.2
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v2.1.2
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 / AvailabilityService.php

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

480 lines 19.2 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\Availability;
6 use FluentBooking\App\Models\Calendar;
7 use FluentBooking\App\Models\CalendarSlot;
8 use FluentBooking\App\Services\DateTimeHelper;
9 use FluentBooking\Framework\Support\Arr;
10
11 class AvailabilityService
12 {
13 public static function maybeCreateAvailability($calendar, $scheduleData)
14 {
15 $userId = $calendar->user_id;
16
17 $availability = self::getDefaultSchedule($userId);
18
19 if ($availability) {
20 return $availability;
21 }
22
23 $timezone = $calendar->author_timezone;
24
25 $defaultSchedule = self::createScheduleSchema($userId, 'Weekly Hours', true, $timezone, 'UTC', $scheduleData);
26
27 return Availability::create($defaultSchedule);
28 }
29
30 public static function availabilitySchedules()
31 {
32 $permissions = ['manage_all_data', 'read_and_use_other_availabilities', 'manage_other_availabilities', 'read_other_calendars', 'manage_other_calendars'];
33
34 $availabilities = Availability::when(
35 !PermissionManager::userCan($permissions),
36 function ($query) {
37 return $query->where('object_id', get_current_user_id());
38 })->get()->toArray();
39
40 $formattedSchedules = [];
41 foreach ($availabilities as $availability) {
42 $toTimezone = Arr::get($availability, 'value.timezone', 'UTC');
43 $formattedSchedules[] = [
44 'id' => Arr::get($availability, 'id'),
45 'object_id' => Arr::get($availability, 'object_id'),
46 'title' => Arr::get($availability, 'key'),
47 'settings' => [
48 'default' => Arr::isTrue($availability, 'value.default'),
49 'timezone' => $toTimezone,
50 'date_overrides' => SanitizeService::slotDateOverrides(Arr::get($availability, 'value.date_overrides', []), 'UTC', $toTimezone),
51 'weekly_schedules' => SanitizeService::weeklySchedules(Arr::get($availability, 'value.weekly_schedules', []), 'UTC', $toTimezone),
52 ]
53 ];
54 }
55 return $formattedSchedules;
56 }
57
58 public static function getFormattedSchedule($schedule)
59 {
60 $timezone = Arr::get($schedule, 'value.timezone', 'UTC');
61
62 $author = $schedule->getAuthor();
63
64 return [
65 'id' => $schedule->id,
66 'host_name' => $author['name'],
67 'host_avatar' => $author['avatar'],
68 'title' => $schedule->key,
69 'created_at' => $schedule->created_at->format('Y-m-d H:i:s'),
70 'usage_count' => self::getAvailabilityUsageCount($schedule->id),
71 'settings' => [
72 'default' => Arr::isTrue($schedule, 'value.default'),
73 'timezone' => $timezone,
74 'date_overrides' => SanitizeService::slotDateOverrides(Arr::get($schedule, 'value.date_overrides', []), 'UTC', $timezone),
75 'weekly_schedules' => SanitizeService::weeklySchedules(Arr::get($schedule, 'value.weekly_schedules'), 'UTC', $timezone)
76 ],
77 ];
78 }
79
80 public static function isTitleAlreadyExist($title, $userId, $currentTitle = '')
81 {
82 if ($title == $currentTitle) {
83 return false;
84 }
85
86 $scheduleTitles = Availability::where('object_id', $userId)
87 ->pluck('key')
88 ->toArray();
89
90 if (in_array($title, $scheduleTitles)) {
91 return true;
92 }
93
94 return false;
95 }
96
97 public static function updateOtherDefaultStatus($schedule, $id)
98 {
99 $schedules = Availability::where('object_id', $schedule->object_id)
100 ->where('id', '!=', $id)
101 ->get();
102
103 foreach ($schedules as $schedule) {
104 $schedule = Availability::findOrFail($schedule->id);
105 $updatedSettings = [
106 'default' => false,
107 'timezone' => Arr::get($schedule, 'value.timezone', 'UTC'),
108 'date_overrides' => Arr::get($schedule, 'value.data_overrides', []),
109 'weekly_schedules' => Arr::get($schedule, 'value.weekly_schedules'),
110 ];
111
112 $schedule->value = $updatedSettings;
113 $schedule->save();
114 }
115 }
116
117 public static function getScheduleOptions()
118 {
119 $permissions = ['manage_all_data', 'read_and_use_other_availabilities', 'manage_other_availabilities', 'read_other_calendars', 'manage_other_calendars'];
120
121 $availabilities = Availability::when(
122 !PermissionManager::userCan($permissions),
123 function ($query) {
124 return $query->where('object_id', get_current_user_id());
125 })->get();
126
127 $scheduleOptions = [];
128 foreach ($availabilities as $availability) {
129 $calendar = Calendar::with(['user'])
130 ->where('type', 'simple')
131 ->where('user_id', $availability->object_id)
132 ->first();
133
134 if ($calendar) {
135 $hostName = $calendar->user->full_name;
136 if ($calendar->user_id == get_current_user_id()) {
137 $hostName = __('My Schedules', 'fluent-booking');
138 }
139 } else {
140 $hostName = __('Deleted User', 'fluent-booking');
141 }
142
143 $scheduleOptions[$hostName] = $scheduleOptions[$hostName] ?? [];
144
145 $scheduleOptions[$hostName][] = [
146 'label' => Arr::get($availability, 'key'),
147 'value' => Arr::get($availability, 'id'),
148 'default' => Arr::isTrue($availability, 'value.default')
149 ];
150 }
151
152 return apply_filters('fluent_booking/availability_schedule_options', $scheduleOptions);
153 }
154
155 public static function getDefaultSchedule($userId)
156 {
157 return Availability::where('object_id', $userId)
158 ->get()
159 ->first(function ($schedule) {
160 return Arr::isTrue($schedule, 'value.default');
161 });
162 }
163
164 public static function createScheduleSchema($userId, $title, $default, $fromTimezone, $toTimezone = 'UTC', $weeklySchedule = [], $dateOverrides = [])
165 {
166 $weeklySchedule = $weeklySchedule ? $weeklySchedule : Helper::getWeeklyScheduleSchema();
167
168 $dateOverrides = $dateOverrides ? SanitizeService::slotDateOverrides($dateOverrides, $fromTimezone, $toTimezone, null, true) : [];
169
170 $defaultSchedule = [
171 'object_id' => $userId,
172 'key' => sanitize_text_field($title),
173 'value' => [
174 'default' => (bool)$default,
175 'timezone' => sanitize_text_field($fromTimezone),
176 'date_overrides' => $dateOverrides,
177 'weekly_schedules' => SanitizeService::weeklySchedules($weeklySchedule, $fromTimezone, $toTimezone, true),
178 ]
179 ];
180 return $defaultSchedule;
181 }
182
183 public static function getAvailabilityUsageCount($scheduleId)
184 {
185 return CalendarSlot::where('availability_type', 'existing_schedule')
186 ->where('availability_id', $scheduleId)
187 ->count();
188 }
189
190 public static function getUtcWeeklySchedules($schedules, $fromTimeZone = false, $toTimeZone = 'UTC')
191 {
192 if (!$schedules) {
193 return [];
194 }
195
196 if (!$fromTimeZone || !$toTimeZone || $fromTimeZone == $toTimeZone) {
197 return $schedules;
198 }
199
200 $weekDays = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
201
202 $dstTimeZone = $fromTimeZone == 'UTC' ? $toTimeZone : $fromTimeZone;
203
204 $dateWithoutDST = DateTimeHelper::getDateWithoutDST($dstTimeZone);
205
206 foreach ($schedules as $day => &$schedule) {
207 $schedule['enabled'] = Arr::isTrue($schedule, 'enabled');
208 if (!$schedule['enabled'] || empty($schedule['slots'])) {
209 $schedule['slots'] = [];
210 $schedule['enabled'] = false;
211 continue;
212 }
213
214 $schedule['enabled'] = true;
215
216 $nextDay = null;
217 $nextDayIndex = 0;
218 $dayIndex = array_search($day, $weekDays);
219 foreach ($schedule['slots'] as $index => $slot) {
220 if (!$slot['start'] || !$slot['end']) {
221 unset($schedule['slots'][$index]);
222 continue;
223 }
224
225 if (!empty(Arr::get($slot, 'type', ''))) {
226 continue;
227 }
228
229 $dayDiff = DateTimeHelper::getDayDifference($slot['start'], $fromTimeZone, $toTimeZone, $dateWithoutDST);
230 $slot['start'] = DateTimeHelper::convertToTimeZone($slot['start'], $fromTimeZone, $toTimeZone, 'H:i', $dateWithoutDST);
231 $slot['end'] = DateTimeHelper::convertToTimeZone($slot['end'], $fromTimeZone, $toTimeZone, 'H:i', $dateWithoutDST);
232
233 if ($nextDayIndex) {
234 array_splice($schedules[$nextDay]['slots'], $nextDayIndex, 0, [[
235 'start' => $slot['start'],
236 'end' => $slot['end'],
237 'type' => 'next_day'
238 ]]);
239 unset($schedule['slots'][$index]);
240 $nextDayIndex++;
241 continue;
242 }
243
244 if ($dayDiff > 0) {
245 $nextDayIndex = 1;
246 $nextDay = $weekDays[($dayIndex + $dayDiff) % 7];
247 $schedules[$nextDay]['enabled'] = true;
248 if (strtotime($slot['start']) < strtotime($slot['end'])) {
249 array_unshift($schedules[$nextDay]['slots'], [
250 'start' => $slot['start'],
251 'end' => $slot['end'],
252 'type' => 'next_day'
253 ]);
254 unset($schedule['slots'][$index]);
255 continue;
256 }
257 if ($slot['end'] != '00:00') {
258 array_unshift($schedules[$nextDay]['slots'], [
259 'start' => '00:00',
260 'end' => $slot['end'],
261 'type' => 'next_day'
262 ]);
263 $slot['end'] = '00:00';
264 }
265 } else if ($dayDiff < 0) {
266 $prevDay = $weekDays[($dayIndex + $dayDiff + 7) % 7];
267 $schedules[$prevDay]['enabled'] = true;
268 if (strtotime($slot['start']) < strtotime($slot['end'])) {
269 array_push($schedules[$prevDay]['slots'], [
270 'start' => $slot['start'],
271 'end' => $slot['end'],
272 'type' => 'prev_day'
273 ]);
274 unset($schedule['slots'][$index]);
275 continue;
276 }
277 if ($slot['start'] != '00:00') {
278 array_push($schedules[$prevDay]['slots'], [
279 'start' => $slot['start'],
280 'end' => '00:00',
281 'type' => 'prev_day'
282 ]);
283 $slot['start'] = '00:00';
284 }
285 } else {
286 if (strtotime($slot['start']) > strtotime($slot['end'])) {
287 if ($slot['end'] != '00:00') {
288 $nextDayIndex = 1;
289 $nextDay = $weekDays[($dayIndex + 1) % 7];
290 $schedules[$nextDay]['enabled'] = true;
291 array_unshift($schedules[$nextDay]['slots'], [
292 'start' => '00:00',
293 'end' => $slot['end'],
294 'type' => 'next_day'
295 ]);
296 $slot['end'] = '00:00';
297 }
298 }
299 }
300
301 if ($slot['start'] == '00:00' && $slot['end'] == '00:00') {
302 unset($schedule['slots'][$index]);
303 continue;
304 }
305
306 $schedule['slots'][$index] = $slot;
307 }
308
309 $schedule['slots'] = array_values($schedule['slots']);
310 }
311
312 return $schedules;
313 }
314
315 public static function getUtcDateOverrides($overrides, $fromTimeZone = false, $toTimeZone = 'UTC')
316 {
317 if (!$overrides) {
318 return [];
319 }
320
321 if (!$fromTimeZone || !$toTimeZone) {
322 return $overrides;
323 }
324
325 $dstTimeZone = $fromTimeZone == 'UTC' ? $toTimeZone : $fromTimeZone;
326
327 $dateWithoutDST = DateTimeHelper::getDateWithoutDST($dstTimeZone);
328
329 $todayTimeStamp = strtotime(gmdate('Y-m-d')); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
330
331 $validOverrides = [];
332 foreach ($overrides as $date => $slots) {
333 $dateTimestamp = strtotime($date);
334 if ($dateTimestamp < $todayTimeStamp) {
335 continue;
336 }
337
338 $nextDayIndex = 0;
339 foreach ($slots as $index => $slot) {
340 if (empty($slot['start']) || empty($slot['end']) || $slot['start'] == $slot['end']) {
341 unset($slots[$index]);
342 continue;
343 }
344
345 $dayDiff = DateTimeHelper::getDayDifference($slot['start'], $fromTimeZone, $toTimeZone, $dateWithoutDST);
346 $slot['start'] = DateTimeHelper::convertToTimeZone($slot['start'], $fromTimeZone, $toTimeZone, 'H:i', $dateWithoutDST);
347 $slot['end'] = DateTimeHelper::convertToTimeZone($slot['end'], $fromTimeZone, $toTimeZone, 'H:i', $dateWithoutDST);
348
349 if ($nextDayIndex) {
350 $nextDay = gmdate('Y-m-d', ($dateTimestamp + 86400 * $dayDiff)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
351 array_splice($validOverrides[$nextDay], $nextDayIndex, 0, [[
352 'start' => $slot['start'],
353 'end' => $slot['end']
354 ]]);
355 unset($slots[$index]);
356 $nextDayIndex++;
357 continue;
358 }
359
360 if ($dayDiff > 0) {
361 $nextDayIndex = 1;
362 $nextDay = gmdate('Y-m-d', ($dateTimestamp + 86400 * $dayDiff)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
363 $validOverrides[$nextDay] = $validOverrides[$nextDay] ?? [];
364 if (strtotime($slot['start']) < strtotime($slot['end'])) {
365 array_unshift($validOverrides[$nextDay], [
366 'start' => $slot['start'],
367 'end' => $slot['end']
368 ]);
369 unset($slots[$index]);
370 continue;
371 }
372 if ($slot['end'] != '00:00') {
373 array_unshift($validOverrides[$nextDay], [
374 'start' => '00:00',
375 'end' => $slot['end']
376 ]);
377 $slot['end'] = '00:00';
378 }
379 } else if ($dayDiff < 0) {
380 $prevDay = gmdate('Y-m-d', ($dateTimestamp - 86400 * abs($dayDiff))); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
381 $validOverrides[$prevDay] = $validOverrides[$prevDay] ?? [];
382 if (strtotime($slot['start']) < strtotime($slot['end'])) {
383 array_push($validOverrides[$prevDay], [
384 'start' => $slot['start'],
385 'end' => $slot['end']
386 ]);
387 unset($slots[$index]);
388 continue;
389 }
390 if ($slot['start'] != '00:00') {
391 array_push($validOverrides[$prevDay], [
392 'start' => $slot['start'],
393 'end' => '00:00'
394 ]);
395 $slot['start'] = '00:00';
396 }
397 } else {
398 if (strtotime($slot['start']) > strtotime($slot['end'])) {
399 if ($slot['end'] != '00:00') {
400 $nextDayIndex = 1;
401 $nextDay = gmdate('Y-m-d', ($dateTimestamp + 86400)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
402 $validOverrides[$nextDay] = $validOverrides[$nextDay] ?? [];
403 array_unshift($validOverrides[$nextDay], [
404 'start' => '00:00',
405 'end' => $slot['end']
406 ]);
407 $slot['end'] = '00:00';
408 }
409 }
410 }
411
412 if ($slot['start'] == '00:00' && $slot['end'] == '00:00') {
413 unset($slots[$index]);
414 continue;
415 }
416 $slots[$index] = $slot;
417 }
418
419 if ($slots) {
420 $validOverrides[$date] = $validOverrides[$date] ?? [];
421 $validOverrides[$date] = array_merge($validOverrides[$date], $slots);
422 }
423 }
424
425 return $validOverrides;
426 }
427
428 public static function getDateOverrideDays($overrides, $fromTimeZone, $toTimeZone = 'UTC')
429 {
430 if (!$overrides || !$fromTimeZone || !$toTimeZone) {
431 return [];
432 }
433
434 $overrideDays = [];
435 foreach ($overrides as $date => $slots) {
436 $dayStart = gmdate('Y-m-d 00:00:00', strtotime($date));
437 $dayEnd = gmdate('Y-m-d 24:00:00', strtotime($date));
438
439 $convertedStart = DateTimeHelper::convertToTimeZone($dayStart, $fromTimeZone, $toTimeZone);
440 $convertedEnd = DateTimeHelper::convertToTimeZone($dayEnd, $fromTimeZone, $toTimeZone);
441
442 $startDate = gmdate('Y-m-d', strtotime($convertedStart));
443 $endDate = gmdate('Y-m-d', strtotime($convertedEnd));
444
445 if (strtotime($dayStart) == strtotime($convertedStart)) {
446 $overrideDays[$startDate] = [
447 'start' => '00:00',
448 'end' => '24:00'
449 ];
450 continue;
451 }
452
453 if (isset($overrideDays[$startDate])) {
454 $overrideDays[$startDate] = [
455 'start' =>'00:00',
456 'end' =>'24:00'
457 ];
458 } else {
459 $overrideDays[$startDate] = [
460 'start' => gmdate('H:i', strtotime($convertedStart)),
461 'end' => '24:00'
462 ];
463 }
464
465 if (isset($overrideDays[$endDate])) {
466 $overrideDays[$endDate] = [
467 'start' => '00:00',
468 'end' => '24:00'
469 ];
470 } else {
471 $overrideDays[$endDate] = [
472 'start' => '00:00',
473 'end' => gmdate('H:i', strtotime($convertedEnd))
474 ];
475 }
476 }
477 return $overrideDays;
478 }
479 }
480