$customField) { $value = wp_unslash(Arr::get($postedData, $fieldKey)); if (Arr::isTrue($customField, 'required')) { $isTerms = $customField['type'] === 'terms-and-conditions'; $isCheckbox = $customField['type'] === 'checkbox'; if (!$value || ($isCheckbox && $value !== 'Yes') || ($isTerms && $value !== 'Accepted')) { /* translators: %s: Field label */ $errors[$fieldKey . '.required'] = sprintf(__('%s is required', 'fluent-booking'), $customField['label']); continue; } } if (is_array($value)) { if ($customField['type'] === 'multi-select') { $value = array_map(function ($item) { $val = is_array($item) ? Arr::get($item, 'value') : $item; return sanitize_text_field($val); },$value); } else if ($customField['type'] === 'file') { $maxField = Arr::get($customField, 'max_file_allow', 1); $value = array_slice($value, 0, $maxField); $value = array_map('sanitize_text_field', $value); } else { $value = array_map('sanitize_text_field', $value); } } else if ($customField['type'] == 'textarea') { $value = sanitize_textarea_field($value); } else { $value = sanitize_text_field($value); } $formattedValues[$fieldKey] = $value; } if ($errors) { return new \WP_Error('required_field', __('Please fill up the required data', 'fluent-booking'), $errors); } return $formattedValues; } public static function getBookingFields(CalendarSlot $calendarSlot, $cached = false) { static $bookingFields = null; if ($cached && $bookingFields) { return $bookingFields; } $requiredIndexes = ['name', 'email', 'message', 'cancellation_reason', 'rescheduling_reason']; $defaultFields = [ 'name' => [ 'index' => 1, 'type' => 'text', 'name' => 'name', 'label' => __('Your Name', 'fluent-booking'), 'required' => true, 'enabled' => true, 'system_defined' => true, 'disable_alter' => true, 'is_visible' => true, 'placeholder' => __('Your Name', 'fluent-booking'), 'help_text' => '' ], 'email' => [ 'index' => 2, 'type' => 'email', 'name' => 'email', 'label' => __('Your Email', 'fluent-booking'), 'required' => true, 'enabled' => true, 'system_defined' => true, 'disable_alter' => true, 'is_visible' => true, 'placeholder' => __('Your Email', 'fluent-booking'), 'help_text' => '' ], 'message' => [ 'index' => 3, 'type' => 'textarea', 'name' => 'message', 'label' => __('What is this meeting about?', 'fluent-booking'), 'required' => false, 'enabled' => true, 'system_defined' => true, 'disable_alter' => false, 'help_text' => '' ], 'cancellation_reason' => [ 'index' => 4, 'type' => 'textarea', 'name' => 'cancellation_reason', 'label' => __('Reason for cancellation', 'fluent-booking'), 'placeholder' => __('Why are you cancelling?', 'fluent-booking'), 'required' => true, 'enabled' => true, 'system_defined' => true, 'disable_alter' => false, 'help_text' => '' ], 'rescheduling_reason' => [ 'index' => 5, 'type' => 'textarea', 'name' => 'rescheduling_reason', 'label' => __('Reason for reschedule', 'fluent-booking'), 'placeholder' => __('Let others know why you need to reschedule', 'fluent-booking'), 'required' => true, 'enabled' => true, 'system_defined' => true, 'disable_alter' => false, 'help_text' => '' ], ]; if ($calendarSlot->isGuestFieldRequired()) { $requiredIndexes[] = 'guests'; $defaultFields['guests'] = [ 'index' => 6, 'type' => 'multi-guests', 'name' => 'guests', 'label' => __('Additional Guests', 'fluent-booking'), 'limit' => 10, 'required' => false, 'enabled' => false, 'system_defined' => true, 'disable_alter' => false ]; } if ($calendarSlot->isLocationFieldRequired()) { $requiredIndexes[] = 'location'; $defaultFields['location'] = [ 'index' => 7, 'type' => 'radio', 'name' => 'location', 'label' => __('Location', 'fluent-booking'), 'options' => LocationService::getLocationOptions($calendarSlot), 'required' => true, 'enabled' => true, 'system_defined' => true, 'disable_alter' => true, 'placeholder' => esc_attr__('Location', 'fluent-booking') ]; } else if ($calendarSlot->isPhoneRequired()) { $requiredIndexes[] = 'phone_number'; $defaultFields['phone_number'] = [ 'index' => 8, 'type' => 'phone', 'name' => 'phone_number', 'label' => __('Your Phone Number', 'fluent-booking'), 'required' => true, 'enabled' => true, 'system_defined' => true, 'disable_alter' => true, 'is_sms_number' => true, 'help_text' => '' ]; } else if ($calendarSlot->isAddressRequired()) { $requiredIndexes[] = 'address'; $defaultFields['address'] = [ 'index' => 9, 'type' => 'text', 'name' => 'address', 'label' => __('Your Address', 'fluent-booking'), 'required' => true, 'enabled' => true, 'system_defined' => true, 'disable_alter' => true, 'placeholder' => esc_attr__('Address', 'fluent-booking'), 'help_text' => '' ]; } $dbFields = $calendarSlot->getMeta('booking_fields', []); if (!$dbFields) { $dbFields = array_values($defaultFields); } $existingFields = []; foreach ($dbFields as $dbField) { $name = $dbField['name']; $existingFields[$name] = $dbField; } if (empty($defaultFields['location'])) { unset($existingFields['location']); } else { if (empty($existingFields['location'])) { $existingFields['location'] = $defaultFields['location']; } else { $existingFields['location']['options'] = $defaultFields['location']['options']; } } if (empty($defaultFields['phone_number'])) { unset($existingFields['phone_number']); } if (empty($defaultFields['address'])) { unset($existingFields['address']); } foreach ($requiredIndexes as $index) { if (empty($existingFields[$index]) && !empty($defaultFields[$index])) { $existingFields[$index] = $defaultFields[$index]; } } $paymentField = apply_filters('fluent_booking/payment_booking_field', [], $calendarSlot, $existingFields); if (!$paymentField) { unset($existingFields['payment_method']); } else { $existingFields['payment_method'] = $paymentField; } $existingFields['email']['disabled'] = false; $bookingFields = apply_filters('fluent_booking/booking_fields', array_values($existingFields), $calendarSlot); return $bookingFields; } public static function getBookingFieldLabels(CalendarSlot $calendarSlot, $enabledOnly = false) { $fields = self::getBookingFields($calendarSlot, true); $labels = []; foreach ($fields as $field) { if ($enabledOnly && !Arr::isTrue($field, 'enabled')) { continue; } $labels[$field['name']] = $field['label']; } return $labels; } public static function generateFieldName($calendarEvent, $fieldLabel) { $fieldLabel = preg_replace('/[^A-Za-z0-9]/', '_', $fieldLabel); $fieldName = 'custom_' . strtolower($fieldLabel); $bookingFields = self::getBookingFields($calendarEvent); $matched = 0; foreach ($bookingFields as $field) { if (strpos($field['name'], $fieldName) !== false) { $matched++; } } if ($matched) { $fieldName .= '_' . $matched; } return $fieldName; } public static function maybeGenerateFieldName($calendarEvent, $fieldValue) { $bookingFields = self::getBookingFields($calendarEvent); foreach ($bookingFields as $field) { if ($field['name'] == $fieldValue['name'] && $field['index'] != $fieldValue['index']) { return self::generateFieldName($calendarEvent, $fieldValue['label']); } } return sanitize_text_field($fieldValue['name']); } public static function getFormattedCustomBookingData(Booking $booking, $htmlSupport = true, $isPublic = false) { $customFormData = $booking->getMeta('custom_fields_data', []); if (!$customFormData) { return []; } $labels = self::getBookingFieldLabels($booking->calendar_event); $formattedData = []; foreach ($customFormData as $dataKey => $value) { $label = $labels[$dataKey] ?? $dataKey; $formattedValue = is_array($value) ? implode(', ', $value) : $value; $field = self::getBookingFieldByName($booking->calendar_event, $dataKey); $fieldType = Arr::get($field, 'type'); if ($fieldType == 'file' && is_array($value)) { $formattedValue = self::getUploadedFiles($value, $htmlSupport); } if ($fieldType == 'hidden') { if ($isPublic) continue; $formattedValue = EditorShortcodeParser::parse($formattedValue, $booking); } $formattedData[$dataKey] = [ 'label' => $label, 'type' => $fieldType, 'value' => $formattedValue ]; } return $formattedData; } public static function getCustomFields($calendarEvent, $withConfig = false) { $existingFields = $calendarEvent->getMeta('booking_fields', []); if (!$existingFields) { return []; } $customFields = []; foreach ($existingFields as $existingField) { if (Arr::get($existingField, 'system_defined') || !Arr::isTrue($existingField, 'enabled')) { continue; } if ($withConfig) { $customFields[$existingField['name']] = $existingField; } else { $customFields[$existingField['name']] = $existingField['label']; } } return $customFields; } public static function getBookingFieldByName($calendarEvent, $name) { $fields = self::getBookingFields($calendarEvent, true); foreach ($fields as $field) { if (Arr::get($field, 'name') == $name) { return $field; } } return []; } public static function hasPhoneNumberField($fields) { if(!$fields) { return false; } foreach ($fields as $field) { if ($field['type'] == 'phone') { return true; } else if ($field['name'] == 'location') { if (!empty($field['options'])) { foreach ($field['options'] as $option) { if (Arr::get($option, 'type') == 'phone_guest') { return true; } } } } } return false; } public static function getUploadedFiles($fieldValue, $htmlSupport = true) { if (empty($fieldValue)) { return ''; } $files = array_map(function($file) use ($htmlSupport) { if ($htmlSupport) { return '' . esc_html(basename($file)) . ''; } return $file; }, $fieldValue); $separator = $htmlSupport ? '
' : PHP_EOL; return implode($separator, $files); } public static function validateDateFields($customFieldsData, $calendarEvent) { foreach ($customFieldsData as $fieldKey => $fieldValue) { $field = self::getBookingFieldByName($calendarEvent, $fieldKey); if ($fieldValue && Arr::get($field, 'type') == 'date') { $minDate = Arr::get($field, 'min_date'); $maxDate = Arr::get($field, 'max_date'); $fieldValue = DateTimeHelper::getFormattedDate($fieldValue, Arr::get($field, 'date_format')); $minDate = gmdate('Y-m-d', strtotime($minDate ?: '1900-01-01')); $maxDate = gmdate('Y-m-d', strtotime($maxDate ?: gmdate('Y-12-31'))); if ($minDate && $fieldValue < $minDate) { /* translators: %1$s: Field label, %2$s: Minimum date */ return new \WP_Error('invalid_date', sprintf(__('The date for %1$s cannot be earlier than %2$s.', 'fluent-booking'), $field['label'], $minDate)); } if ($maxDate && $fieldValue > $maxDate) { /* translators: %1$s: Field label, %2$s: Maximum date */ return new \WP_Error('invalid_date', sprintf(__('The date for %1$s cannot be later than %2$s.', 'fluent-booking'), $field['label'], $maxDate)); } } } return true; } }