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 / BookingFieldService.php

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

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