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

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

439 lines 16.0 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\Booking;
6 use FluentBooking\App\Models\CalendarSlot;
7 use FluentBooking\Framework\Support\Arr;
8
9 class BookingFieldService
10 {
11 public static function getCustomFieldsData($postedData, CalendarSlot $slot)
12 {
13 $customFields = self::getCustomFields($slot, true);
14
15 $errors = [];
16
17 $formattedValues = [];
18
19 foreach ($customFields as $fieldKey => $customField) {
20 $value = wp_unslash(Arr::get($postedData, $fieldKey));
21 if (Arr::isTrue($customField, 'required')) {
22 $isTerms = $customField['type'] === 'terms-and-conditions';
23 $isCheckbox = $customField['type'] === 'checkbox';
24 if (!$value || ($isCheckbox && $value !== 'Yes') || ($isTerms && $value !== 'Accepted')) {
25 /* translators: %s: Field label */
26 $errors[$fieldKey . '.required'] = sprintf(__('%s is required', 'fluent-booking'), $customField['label']);
27 continue;
28 }
29 }
30
31 if (is_array($value)) {
32 if ($customField['type'] === 'multi-select') {
33 $value = array_map(function ($item) {
34 $val = is_array($item) ? Arr::get($item, 'value') : $item;
35 return sanitize_text_field($val);
36 },$value);
37 } else if ($customField['type'] === 'file') {
38 $maxField = Arr::get($customField, 'max_file_allow', 1);
39 $value = array_slice($value, 0, $maxField);
40 $value = array_map('sanitize_text_field', $value);
41 } else {
42 $value = array_map('sanitize_text_field', $value);
43 }
44 } else if ($customField['type'] == 'textarea') {
45 $value = sanitize_textarea_field($value);
46 } else {
47 $value = sanitize_text_field($value);
48 }
49
50 if ($customField['type'] === 'phone' && $value && !Helper::isValidPhoneNumber($value)) {
51 /* translators: %s: Field label */
52 $errors[$fieldKey . '.valid_phone_number'] = sprintf(__('%s is not a valid phone number', 'fluent-booking'), $customField['label']);
53 continue;
54 }
55
56 $formattedValues[$fieldKey] = $value;
57 }
58
59 if ($errors) {
60 return new \WP_Error('required_field', __('Please fill up the required data', 'fluent-booking'), $errors);
61 }
62
63 return $formattedValues;
64 }
65
66 public static function getBookingFields(CalendarSlot $calendarSlot, $cached = false)
67 {
68 static $bookingFields = null;
69
70 if ($cached && $bookingFields) {
71 return $bookingFields;
72 }
73
74 $requiredIndexes = ['name', 'email', 'message', 'cancellation_reason', 'rescheduling_reason'];
75
76 $defaultFields = [
77 'name' => [
78 'index' => 1,
79 'type' => 'text',
80 'name' => 'name',
81 'label' => __('Your Name', 'fluent-booking'),
82 'required' => true,
83 'enabled' => true,
84 'system_defined' => true,
85 'disable_alter' => true,
86 'is_visible' => true,
87 'placeholder' => __('Your Name', 'fluent-booking'),
88 'help_text' => ''
89 ],
90 'email' => [
91 'index' => 2,
92 'type' => 'email',
93 'name' => 'email',
94 'label' => __('Your Email', 'fluent-booking'),
95 'required' => true,
96 'enabled' => true,
97 'system_defined' => true,
98 'disable_alter' => true,
99 'is_visible' => true,
100 'placeholder' => __('Your Email', 'fluent-booking'),
101 'help_text' => ''
102 ],
103 'message' => [
104 'index' => 3,
105 'type' => 'textarea',
106 'name' => 'message',
107 'label' => __('What is this meeting about?', 'fluent-booking'),
108 'required' => false,
109 'enabled' => true,
110 'system_defined' => true,
111 'disable_alter' => false,
112 'help_text' => ''
113 ],
114 'cancellation_reason' => [
115 'index' => 4,
116 'type' => 'textarea',
117 'name' => 'cancellation_reason',
118 'label' => __('Reason for cancellation', 'fluent-booking'),
119 'placeholder' => __('Why are you cancelling?', 'fluent-booking'),
120 'required' => true,
121 'enabled' => true,
122 'system_defined' => true,
123 'disable_alter' => false,
124 'help_text' => ''
125 ],
126 'rescheduling_reason' => [
127 'index' => 5,
128 'type' => 'textarea',
129 'name' => 'rescheduling_reason',
130 'label' => __('Reason for reschedule', 'fluent-booking'),
131 'placeholder' => __('Let others know why you need to reschedule', 'fluent-booking'),
132 'required' => true,
133 'enabled' => true,
134 'system_defined' => true,
135 'disable_alter' => false,
136 'help_text' => ''
137 ],
138 ];
139
140 if ($calendarSlot->isGuestFieldRequired()) {
141 $requiredIndexes[] = 'guests';
142 $defaultFields['guests'] = [
143 'index' => 6,
144 'type' => 'multi-guests',
145 'name' => 'guests',
146 'label' => __('Additional Guests', 'fluent-booking'),
147 'limit' => 10,
148 'required' => false,
149 'enabled' => false,
150 'system_defined' => true,
151 'disable_alter' => false
152 ];
153 }
154
155 if ($calendarSlot->isLocationFieldRequired()) {
156 $requiredIndexes[] = 'location';
157 $defaultFields['location'] = [
158 'index' => 7,
159 'type' => 'radio',
160 'name' => 'location',
161 'label' => __('Location', 'fluent-booking'),
162 'options' => LocationService::getLocationOptions($calendarSlot),
163 'required' => true,
164 'enabled' => true,
165 'system_defined' => true,
166 'disable_alter' => true,
167 'placeholder' => esc_attr__('Location', 'fluent-booking')
168 ];
169 } else if ($calendarSlot->isPhoneRequired()) {
170 $requiredIndexes[] = 'phone_number';
171 $defaultFields['phone_number'] = [
172 'index' => 8,
173 'type' => 'phone',
174 'name' => 'phone_number',
175 'label' => __('Your Phone Number', 'fluent-booking'),
176 'required' => true,
177 'enabled' => true,
178 'system_defined' => true,
179 'disable_alter' => true,
180 'is_sms_number' => true,
181 'help_text' => ''
182 ];
183 } else if ($calendarSlot->isAddressRequired()) {
184 $requiredIndexes[] = 'address';
185 $defaultFields['address'] = [
186 'index' => 9,
187 'type' => 'text',
188 'name' => 'address',
189 'label' => __('Your Address', 'fluent-booking'),
190 'required' => true,
191 'enabled' => true,
192 'system_defined' => true,
193 'disable_alter' => true,
194 'placeholder' => esc_attr__('Address', 'fluent-booking'),
195 'help_text' => ''
196 ];
197 }
198
199 $dbFields = $calendarSlot->getMeta('booking_fields', []);
200
201 if (!$dbFields) {
202 $dbFields = array_values($defaultFields);
203 }
204
205 $existingFields = [];
206 foreach ($dbFields as $dbField) {
207 $name = $dbField['name'];
208 $existingFields[$name] = $dbField;
209 }
210
211 if (empty($defaultFields['location'])) {
212 unset($existingFields['location']);
213 } else {
214 if (empty($existingFields['location'])) {
215 $existingFields['location'] = $defaultFields['location'];
216 } else {
217 $existingFields['location']['options'] = $defaultFields['location']['options'];
218 }
219 }
220
221 if (empty($defaultFields['phone_number'])) {
222 unset($existingFields['phone_number']);
223 }
224
225 if (empty($defaultFields['address'])) {
226 unset($existingFields['address']);
227 }
228
229 foreach ($requiredIndexes as $index) {
230 if (empty($existingFields[$index]) && !empty($defaultFields[$index])) {
231 $existingFields[$index] = $defaultFields[$index];
232 }
233 }
234
235 $paymentField = apply_filters('fluent_booking/payment_booking_field', [], $calendarSlot, $existingFields);
236
237 if (!$paymentField) {
238 unset($existingFields['payment_method']);
239 } else {
240 $existingFields['payment_method'] = $paymentField;
241 }
242
243 $existingFields['email']['disabled'] = false;
244
245 $bookingFields = apply_filters('fluent_booking/booking_fields', array_values($existingFields), $calendarSlot);
246
247 return $bookingFields;
248 }
249
250 public static function getBookingFieldLabels(CalendarSlot $calendarSlot, $enabledOnly = false)
251 {
252 $fields = self::getBookingFields($calendarSlot, true);
253
254 $labels = [];
255 foreach ($fields as $field) {
256 if ($enabledOnly && !Arr::isTrue($field, 'enabled')) {
257 continue;
258 }
259
260 $labels[$field['name']] = $field['label'];
261 }
262
263 return $labels;
264 }
265
266 public static function generateFieldName($calendarEvent, $fieldLabel)
267 {
268 $fieldLabel = preg_replace('/[^A-Za-z0-9]/', '_', $fieldLabel);
269 $fieldName = 'custom_' . strtolower($fieldLabel);
270 $bookingFields = self::getBookingFields($calendarEvent);
271
272 $matched = 0;
273 foreach ($bookingFields as $field) {
274 if (strpos($field['name'], $fieldName) !== false) {
275 $matched++;
276 }
277 }
278
279 if ($matched) {
280 $fieldName .= '_' . $matched;
281 }
282 return $fieldName;
283 }
284
285 public static function maybeGenerateFieldName($calendarEvent, $fieldValue)
286 {
287 $bookingFields = self::getBookingFields($calendarEvent);
288
289 foreach ($bookingFields as $field) {
290 if ($field['name'] == $fieldValue['name'] && $field['index'] != $fieldValue['index']) {
291 return self::generateFieldName($calendarEvent, $fieldValue['label']);
292 }
293 }
294
295 return sanitize_text_field($fieldValue['name']);
296 }
297
298 public static function getFormattedCustomBookingData(Booking $booking, $htmlSupport = true, $isPublic = false)
299 {
300 $customFormData = $booking->getMeta('custom_fields_data', []);
301 if (!$customFormData) {
302 return [];
303 }
304
305 $labels = self::getBookingFieldLabels($booking->calendar_event);
306
307 $formattedData = [];
308
309 foreach ($customFormData as $dataKey => $value) {
310 $label = $labels[$dataKey] ?? $dataKey;
311
312 $formattedValue = is_array($value) ? implode(', ', $value) : $value;
313
314 $field = self::getBookingFieldByName($booking->calendar_event, $dataKey);
315
316 $fieldType = Arr::get($field, 'type');
317
318 if ($fieldType == 'file' && is_array($value)) {
319 $formattedValue = self::getUploadedFiles($value, $htmlSupport);
320 }
321
322 if ($fieldType == 'hidden') {
323 if ($isPublic) continue;
324 $formattedValue = EditorShortcodeParser::parse($formattedValue, $booking);
325 }
326
327 $formattedData[$dataKey] = [
328 'label' => $label,
329 'type' => $fieldType,
330 'value' => $formattedValue
331 ];
332 }
333
334 return $formattedData;
335 }
336
337 public static function getCustomFields($calendarEvent, $withConfig = false)
338 {
339 $existingFields = $calendarEvent->getMeta('booking_fields', []);
340
341 if (!$existingFields) {
342 return [];
343 }
344
345 $customFields = [];
346
347 foreach ($existingFields as $existingField) {
348 if (Arr::get($existingField, 'system_defined') || !Arr::isTrue($existingField, 'enabled')) {
349 continue;
350 }
351 if ($withConfig) {
352 $customFields[$existingField['name']] = $existingField;
353 } else {
354 $customFields[$existingField['name']] = $existingField['label'];
355 }
356 }
357
358 return $customFields;
359 }
360
361 public static function getBookingFieldByName($calendarEvent, $name)
362 {
363 $fields = self::getBookingFields($calendarEvent, true);
364
365 foreach ($fields as $field) {
366 if (Arr::get($field, 'name') == $name) {
367 return $field;
368 }
369 }
370
371 return [];
372 }
373
374 public static function hasPhoneNumberField($fields)
375 {
376 if(!$fields) {
377 return false;
378 }
379
380 foreach ($fields as $field) {
381 if ($field['type'] == 'phone') {
382 return true;
383 } else if ($field['name'] == 'location') {
384 if (!empty($field['options'])) {
385 foreach ($field['options'] as $option) {
386 if (Arr::get($option, 'type') == 'phone_guest') {
387 return true;
388 }
389 }
390 }
391 }
392 }
393 return false;
394 }
395
396 public static function getUploadedFiles($fieldValue, $htmlSupport = true)
397 {
398 if (empty($fieldValue)) {
399 return '';
400 }
401
402 $files = array_map(function($file) use ($htmlSupport) {
403 if ($htmlSupport) {
404 return '<a href="' . esc_url($file) . '" target="_blank" download="' . esc_attr(basename($file)) . '">' . esc_html(basename($file)) . '</a>';
405 }
406 return $file;
407 }, $fieldValue);
408
409 $separator = $htmlSupport ? '<br>' : PHP_EOL;
410
411 return implode($separator, $files);
412 }
413
414 public static function validateDateFields($customFieldsData, $calendarEvent)
415 {
416 foreach ($customFieldsData as $fieldKey => $fieldValue) {
417 $field = self::getBookingFieldByName($calendarEvent, $fieldKey);
418 if ($fieldValue && Arr::get($field, 'type') == 'date') {
419 $minDate = Arr::get($field, 'min_date');
420 $maxDate = Arr::get($field, 'max_date');
421
422 $fieldValue = DateTimeHelper::getFormattedDate($fieldValue, Arr::get($field, 'date_format'));
423 $minDate = gmdate('Y-m-d', strtotime($minDate ?: '1900-01-01'));
424 $maxDate = gmdate('Y-m-d', strtotime($maxDate ?: gmdate('Y-12-31')));
425
426 if ($minDate && $fieldValue < $minDate) {
427 /* translators: %1$s: Field label, %2$s: Minimum date */
428 return new \WP_Error('invalid_date', sprintf(__('The date for %1$s cannot be earlier than %2$s.', 'fluent-booking'), $field['label'], $minDate));
429 }
430 if ($maxDate && $fieldValue > $maxDate) {
431 /* translators: %1$s: Field label, %2$s: Maximum date */
432 return new \WP_Error('invalid_date', sprintf(__('The date for %1$s cannot be later than %2$s.', 'fluent-booking'), $field['label'], $maxDate));
433 }
434 }
435 }
436 return true;
437 }
438 }
439