PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 2.3.0
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v2.3.0
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 / Integrations / Calendars / RemoteCalendarHelper.php

RemoteCalendarHelper.php in Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution 2.3.0, at app/Services/Integrations/Calendars/RemoteCalendarHelper.php

252 lines 7.8 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\Integrations\Calendars;
4
5 use FluentBooking\App\App;
6 use FluentBooking\App\Models\Meta;
7 use FluentBooking\App\Services\DateTimeHelper;
8 use FluentBooking\App\Services\Helper;
9 use FluentBooking\Framework\Support\Arr;
10
11 class RemoteCalendarHelper
12 {
13 /**
14 * Remote calendar and webhook URLs are set by roles below `manage_options`, so by
15 * default they are held to WordPress' safe URL rules (no loopback/private/link-local
16 * address, and only ports 80, 443, 8080). Filter to allow an internal host or port.
17 *
18 * Callbacks get the URL under check, so compare the host, not the whole string.
19 */
20 public static function shouldRejectUnsafeUrl($url, $context = 'remote_calendar')
21 {
22 return (bool) apply_filters('fluent_booking/reject_unsafe_remote_urls', true, $url, $context);
23 }
24
25 /**
26 * Sanitize an operator supplied remote URL, returning false when it is not a plain
27 * http/https address this site is allowed to call. Callers own the error message.
28 *
29 * @param string $url
30 * @param string $context
31 * @return string|false
32 */
33 public static function sanitizeRemoteUrl($url, $context = 'remote_calendar')
34 {
35 $url = esc_url_raw(trim((string) $url), ['http', 'https']);
36
37 if (!$url) {
38 return false;
39 }
40
41 // esc_url_raw() and wp_http_validate_url() both accept a scheme relative `//host`.
42 $scheme = strtolower((string) wp_parse_url($url, PHP_URL_SCHEME));
43
44 if (!in_array($scheme, ['http', 'https'], true)) {
45 return false;
46 }
47
48 if (self::shouldRejectUnsafeUrl($url, $context) && !wp_http_validate_url($url)) {
49 return false;
50 }
51
52 return $url;
53 }
54
55 public static function getUserRemoteCreatableCalendarSettings($userId)
56 {
57 $exist = Meta::where('object_type', '_calendar_user_meta')
58 ->where('object_id', $userId)
59 ->where('key', 'create_remote_calendar')
60 ->first();
61
62 if ($exist) {
63 $value = $exist->value;
64 if (!is_array($value)) {
65 return [];
66 }
67 return $value;
68 }
69
70 return [];
71 }
72
73 public static function updateUserRemoteCreatableCalendarSettings($userId, $settings)
74 {
75 if (!$settings) {
76 $settings = [];
77 }
78
79 $exist = Meta::where('object_type', '_calendar_user_meta')
80 ->where('object_id', $userId)
81 ->where('key', 'create_remote_calendar')
82 ->first();
83
84 if ($exist) {
85 $exist->value = $settings;
86 $exist->save();
87 return $exist;
88 }
89
90 return Meta::create([
91 'object_type' => '_calendar_user_meta',
92 'object_id' => $userId,
93 'key' => 'create_remote_calendar',
94 'value' => $settings
95 ]);
96 }
97
98 public static function getRemoteCalendarConfig($userId)
99 {
100 $settings = self::getUserRemoteCreatableCalendarSettings($userId);
101
102 if (!$settings) {
103 return null;
104 }
105
106 $idConfig = Arr::get($settings, 'id');
107 $driver = Arr::get($settings, 'driver');
108
109 if (!$idConfig || !$driver) {
110 return null;
111 }
112
113 $idArr = explode('__||__', $idConfig);
114
115 if (count($idArr) < 2) {
116 return null;
117 }
118
119 $metaId = (int)array_shift($idArr);
120
121 if (!$metaId) {
122 return null;
123 }
124
125 $remoteCalendarId = implode('__||__', $idArr);
126
127 return [
128 'db_id' => $metaId,
129 'remote_calendar_id' => $remoteCalendarId,
130 'driver' => $driver
131 ];
132 }
133
134 public static function showGeneralError($data = [])
135 {
136 $defaults = [
137 'title' => __('Unknown error', 'fluent-booking'),
138 'body' => __('Something went wrong. Please try again later.', 'fluent-booking'),
139 'btn_url' => Helper::getAppBaseUrl(),
140 'btn_text' => __('Back to dashboard', 'fluent-booking')
141 ];
142
143 $data = array_merge($defaults, $data);
144
145 $app = App::getInstance();
146
147 header('Content-Type: text/html; charset=utf-8');
148 $app->view->render('admin.general_error', $data);
149 exit();
150 }
151
152 public static function getRruleDates($rules, $sampleRange, $minDate, $maxDate, $args = [], $timezone = 'UTC')
153 {
154 try {
155 $durationSeconds = strtotime($sampleRange[1]) - strtotime($sampleRange[0]);
156 $timezone = DateTimeHelper::getValidatedTimeZone($timezone);
157 // Define the time range you're interested in
158 $minDate = new \DateTime($minDate, new \DateTimeZone($timezone));
159 $maxDate = new \DateTime($maxDate, new \DateTimeZone($timezone));
160 $dtStart = new \DateTime($sampleRange[0], new \DateTimeZone($timezone));
161
162 // Create a rule set
163 $rset = new \FluentBooking\App\Services\Libs\RRule\RSet();
164
165 foreach ($rules as $recurrence) {
166 if (strpos($recurrence, 'RRULE') === 0) {
167 // Create the RRule object with the actual DTSTART
168 $rrule = new \FluentBooking\App\Services\Libs\RRule\RRule($recurrence, $dtStart);
169 $rset->addRRule($rrule);
170 } elseif (strpos($recurrence, 'EXDATE') === 0) {
171 $exDates = \FluentBooking\App\Services\Libs\RRule\RfcParser::parseExDate($recurrence);
172 foreach ($exDates as $exDate) {
173 $rset->addExDate($exDate);
174 }
175 }
176 }
177
178 $occurrences = $rset->getOccurrencesBetween($minDate, $maxDate);
179
180 return array_map(function ($date) use ($durationSeconds, $args) {
181
182 $start = $date->format('Y-m-d H:i:s');
183 $endDateTime = gmdate('Y-m-d H:i:s', strtotime($start) + $durationSeconds); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
184
185 if (!$args) {
186 return [
187 'start' => $start,
188 'end' => $endDateTime
189 ];
190 }
191
192 return wp_parse_args([
193 'start' => $start,
194 'end' => $endDateTime
195 ], $args);
196 }, $occurrences);
197
198 } catch (\Exception $exception) {
199 Helper::debugLog([
200 'message' => $exception->getMessage(),
201 'method' => __METHOD__,
202 'type' => 'rrule_error'
203 ]);
204 return [];
205 }
206 }
207
208 public static function convertToTimeZoneOffset($dateTime, $toTimeZone, $refernceDate = null)
209 {
210 if ($toTimeZone === 'UTC') {
211 return gmdate('Y-m-d H:i:s', strtotime($dateTime));
212 }
213
214 if (!$refernceDate) {
215 return DateTimeHelper::convertFromUtc($dateTime, $toTimeZone);
216 }
217
218 $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC'));
219 $offset = self::getTimeOffset($refernceDate, $toTimeZone);
220 if ($offset > 0) {
221 $dateTime->add(new \DateInterval('PT' . $offset . 'S'));
222 } else {
223 $dateTime->sub(new \DateInterval('PT' . abs($offset) . 'S'));
224 }
225
226 return $dateTime->format('Y-m-d H:i:s');
227 }
228
229 public static function getTimeOffset($refDate, $timezone)
230 {
231 static $cache = [];
232
233 $cacheKey = $refDate . '_' . $timezone;
234
235 if (isset($cache[$cacheKey])) {
236 return $cache[$cacheKey];
237 }
238
239 $timezone = DateTimeHelper::getValidatedTimeZone($timezone);
240
241 $refDate = new \DateTime($refDate, new \DateTimeZone('UTC'));
242 $refDate->setTimezone(new \DateTimeZone($timezone));
243
244 $offset = $refDate->getOffset();
245
246 $cache[$cacheKey] = $offset;
247
248 return $offset;
249 }
250
251 }
252