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

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

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