get_field_identifier($item); $context->add_render_attribute( [ 'field-group' . $i => [ 'class' => [ 'afeb-form-field-type-' . $item['field_type'], 'afeb-field-group', 'afeb-column', 'afeb-field-group-' . esc_attr($field_identifier), ], ], 'input' . $i => [ 'type' => ('acceptance' === $item['field_type']) ? 'checkbox' : (($item['field_type'] === 'upload') ? 'file' : $item['field_type']), 'name' => $this->get_attribute_name($item), 'id' => $this->get_attribute_id($item), 'class' => [ 'afeb-form-field', empty($item['css_classes']) ? '' : esc_attr($item['css_classes']), ], ], 'label' . $i => [ 'for' => $this->get_attribute_id($item), 'class' => 'afeb-form-field-label', ], ] ); if (empty($item['width'])) $item['width'] = '100'; $context->add_render_attribute('field-group' . $i, 'class', 'elementor-repeater-item-' . esc_attr($item['_id'])); if ($settings['show_placeholders'] == 'true' && !Utils::is_empty($item['placeholder'])) $context->add_render_attribute('input' . $i, 'placeholder', $item['placeholder']); if (!empty($item['field_value'])) $context->add_render_attribute('input' . $i, 'value', $item['field_value']); if (!$settings['show_labels']) $context->add_render_attribute('label' . $i, 'class', 'afeb-hidden-element'); if (!empty($item['required'])) { $class = 'afeb-form-field-required'; if (!empty($settings['mark_required'])) $class .= ' afeb-mark-required'; $context->add_render_attribute('field-group' . $i, 'class', $class); $this->add_required_attribute($context, 'input' . $i); } } public function get_attribute_name($item): string { $field_identifier = $this->get_field_identifier($item); if ($field_identifier === '') return 'form_fields[]'; return "form_fields[$field_identifier]"; } private function get_field_identifier($item) { if (!empty($item['field_id'])) return $item['field_id']; return $item['_id'] ?? ''; } public function get_attribute_id($item): string { $id_suffix = !empty($item['field_id']) ? $item['field_id'] : $item['_id']; return 'form-field-' . $id_suffix; } private function add_required_attribute($context, $element) { $context->add_render_attribute($element, 'required', 'required'); $context->add_render_attribute($element, 'aria-required', 'true'); } public function make_textarea_field($context, $item, $item_index): string { $context->add_render_attribute('textarea' . $item_index, [ 'class' => [ 'afeb-form-field-textual', 'afeb-form-field', 'elementor-field-textual', esc_attr($item['css_classes']), ], 'name' => $this->get_attribute_name($item), 'id' => $this->get_attribute_id($item), 'rows' => $item['rows'], ]); if ($context->get_settings_for_display()['show_placeholders'] == 'true' && !Utils::is_empty($item['placeholder'])) { $context->add_render_attribute('textarea' . $item_index, 'placeholder', $item['placeholder']); } if ($item['required']) { $this->add_required_attribute($context, 'textarea' . $item_index); } $value = empty($item['field_value']) ? '' : $item['field_value']; return ''; } public function make_select_field($context, $item) { ob_start(); ?>
$option): $option_label = $option; $option_value = $option; if (strpos($option, '|') !== false) { [$option_label, $option_value] = explode('|', $option); } if (isset($options_temp[$option_value]) || !trim($option_value)) { continue; } ?> >
print_render_attribute_string('icon-align'); ?>> 'true']); ?> esc_html__('Text', 'addons-for-elementor-builder'), 'textarea' => esc_html__('Textarea', 'addons-for-elementor-builder'), 'email' => esc_html__('Email', 'addons-for-elementor-builder'), 'url' => esc_html__('URL', 'addons-for-elementor-builder'), 'number' => esc_html__('Number', 'addons-for-elementor-builder'), 'tel' => esc_html__('Tel', 'addons-for-elementor-builder'), 'radio' => esc_html__('Radio', 'addons-for-elementor-builder'), 'select' => esc_html__('Select', 'addons-for-elementor-builder'), 'checkbox' => esc_html__('Checkbox', 'addons-for-elementor-builder'), 'date' => esc_html__('Date', 'addons-for-elementor-builder'), 'time' => esc_html__('Time', 'addons-for-elementor-builder'), 'upload' => esc_html__('File Upload', 'addons-for-elementor-builder'), 'password' => esc_html__('Password', 'addons-for-elementor-builder'), 'html' => esc_html__('HTML', 'addons-for-elementor-builder'), 'recaptcha_v3' => esc_html__('reCAPTCHA V3', 'addons-for-elementor-builder'), 'hidden' => esc_html__('Hidden', 'addons-for-elementor-builder'), 'step' => esc_html__('Step', 'addons-for-elementor-builder'), ]; } /** * Get the default fields structure for a new form. * * @return array */ public static function get_default_fields(): array { return [ [ '_id' => 'field_' . wp_generate_password(8, false), 'type' => 'text', 'label' => esc_html__('Untitled', 'addons-for-elementor-builder'), 'placeholder' => '', 'required' => false, 'value' => '', 'column_width' => 100, ], ]; } /** * Whitelisted file types for upload fields. * * @return array */ public static function get_whitelisted_file_types(): array { $defaults = [ 'extensions' => ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'zip'], 'mime_types' => [ 'image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', ], 'max_size' => 5 * 1024 * 1024, // 5MB ]; return apply_filters('afeb/form_builder/whitelisted_file_types', $defaults); } public static function validate_uploaded_files($files, $rules = [], $is_required = false): array { $normalized_files = []; $errors = []; // Convert MB to bytes if needed (common case where max_size is in MB) $max_size = !empty($rules['max_size']) ? (is_numeric($rules['max_size']) ? (int) $rules['max_size'] * 1024 * 1024 : wp_max_upload_size()) : wp_max_upload_size(); $allowed_exts = !empty($rules['exts']) ? array_map('strtolower', (array) $rules['exts']) : []; $allowed_mimes = !empty($rules['mime_types']) ? (array) $rules['mime_types'] : get_allowed_mime_types(); if (!is_array($files)) { $files = $files ? [$files] : []; } foreach ($files as $file) { // Handle newly uploaded files if (is_array($file) && isset($file['tmp_name'])) { // Skip empty files if (empty($file['name']) || empty($file['tmp_name'])) continue; // Basic security check if (!is_uploaded_file($file['tmp_name'])) { $errors[] = __('Security verification failed for uploaded file.', 'addons-for-elementor-builder'); continue; } // Check for upload errors if ($file['error'] !== UPLOAD_ERR_OK) { $errors[] = self::get_upload_error_message($file['error'], $file['name']); continue; } // Validate file type $filetype = wp_check_filetype_and_ext($file['tmp_name'], $file['name'], $allowed_mimes); if (!$filetype['ext'] || !$filetype['type']) { $errors[] = sprintf(__('"%s" is not a valid file type.', 'addons-for-elementor-builder'), esc_html($file['name'])); continue; } // Check against allowed extensions if (!empty($allowed_exts) && !in_array(strtolower($filetype['ext']), $allowed_exts, true)) { $errors[] = sprintf( __('"%s" is not an allowed file type. Allowed: %s', 'addons-for-elementor-builder'), esc_html($file['name']), esc_html(implode(', ', $allowed_exts)) ); continue; } // Check file size if ($file['size'] > $max_size) { $errors[] = sprintf( __('"%s" exceeds maximum file size of %s.', 'addons-for-elementor-builder'), esc_html($file['name']), size_format($max_size) ); continue; } if (!function_exists('wp_handle_upload')) require_once ABSPATH . 'wp-admin/includes/file.php'; // Handle the upload $upload = wp_handle_upload($file, [ 'test_form' => false, 'test_type' => true, 'mimes' => $allowed_mimes, ]); if (isset($upload['error'])) { $errors[] = sprintf(__('Error uploading "%s": %s', 'addons-for-elementor-builder'), esc_html($file['name']), esc_html($upload['error'])); continue; } $normalized_files[] = esc_url_raw($upload['url']); } // Handle existing URLs (for multi-step forms) else if (is_string($file) && filter_var($file, FILTER_VALIDATE_URL)) $normalized_files[] = esc_url_raw($file); } // Required field check if ($is_required && empty($normalized_files)) $errors[] = __('This field is required.', 'addons-for-elementor-builder'); return [ 'valid' => empty($errors), 'files' => $normalized_files, 'errors' => $errors, ]; } /** * Get user-friendly upload error message */ private static function get_upload_error_message($error_code, $filename): string { $messages = [ UPLOAD_ERR_INI_SIZE => __('"%s" exceeds server upload limit.', 'addons-for-elementor-builder'), UPLOAD_ERR_FORM_SIZE => __('"%s" exceeds form upload limit.', 'addons-for-elementor-builder'), UPLOAD_ERR_PARTIAL => __('"%s" was only partially uploaded.', 'addons-for-elementor-builder'), UPLOAD_ERR_NO_FILE => __('No file was uploaded for "%s".', 'addons-for-elementor-builder'), UPLOAD_ERR_NO_TMP_DIR => __('Missing temporary folder for "%s".', 'addons-for-elementor-builder'), UPLOAD_ERR_CANT_WRITE => __('Failed to write "%s" to disk.', 'addons-for-elementor-builder'), UPLOAD_ERR_EXTENSION => __('Server extension stopped "%s" upload.', 'addons-for-elementor-builder'), ]; return sprintf( $messages[$error_code] ?? __('Upload error for "%s".', 'addons-for-elementor-builder'), esc_html($filename) ); } }