PluginProbe
The Innovative Form Builder – IvyForms / 1.5
The Innovative Form Builder – IvyForms v1.5
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / backend / src / Common / Helpers / Import / WpFormsImportConverter.php

WpFormsImportConverter.php in The Innovative Form Builder – IvyForms 1.5, at backend/src/Common/Helpers/Import/WpFormsImportConverter.php

322 lines 10.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace IvyForms\Common\Helpers\Import;
4
5 /**
6 * Convert a WPForms JSON export into the IvyForms import payload structure.
7 */
8 class WpFormsImportConverter
9 {
10 /**
11 * WPForms field types that have direct IvyForms equivalents.
12 *
13 * @var array<string, string>
14 */
15 private const TYPE_MAP = [
16 'text' => 'text',
17 'textarea' => 'textarea',
18 'richtext' => 'textarea',
19 'email' => 'email',
20 'number' => 'number',
21 'number-slider' => 'slider',
22 'select' => 'select',
23 'radio' => 'radio',
24 'checkbox' => 'checkbox',
25 'gdpr-checkbox' => 'gdpr',
26 'phone' => 'phone',
27 'url' => 'website',
28 'date-time' => 'date',
29 'rating' => 'rating',
30 'file-upload' => 'file-upload',
31 'camera' => 'file-upload',
32 'html' => 'html',
33 'content' => 'html',
34 'divider' => 'section',
35 'pagebreak' => 'page',
36 'payment-single' => 'product',
37 'payment-checkbox' => 'product',
38 'payment-multiple' => 'product',
39 'payment-select' => 'product',
40 'payment-total' => 'total',
41 ];
42
43 /**
44 * Convert one or more exported WPForms forms.
45 *
46 * @param array<string|int, mixed> $rawData
47 * @return array<string, mixed>
48 */
49 public static function convert(array $rawData): array
50 {
51 $forms = [];
52 foreach (self::normalizeToList($rawData) as $formRaw) {
53 if (is_array($formRaw)) {
54 $forms[] = self::convertSingleForm($formRaw);
55 }
56 }
57
58 return ['forms' => $forms];
59 }
60
61 /**
62 * @param array<string|int, mixed> $rawData
63 * @return array<int, mixed>
64 */
65 private static function normalizeToList(array $rawData): array
66 {
67 return isset($rawData['fields']) ? [$rawData] : array_values($rawData);
68 }
69
70 /**
71 * @param array<string, mixed> $formRaw
72 * @return array<string, mixed>
73 */
74 private static function convertSingleForm(array $formRaw): array
75 {
76 $settings = self::child($formRaw, 'settings');
77 $description = sanitize_textarea_field((string) ($settings['form_desc'] ?? ''));
78 $submitLabel = sanitize_text_field((string) ($settings['submit_text'] ?? ''));
79
80 return [
81 'form' => [
82 'name' => sanitize_text_field((string) ($settings['form_title'] ?? '')),
83 'description' => $description,
84 'published' => true,
85 'showTitle' => true,
86 'showDescription' => ($description !== ''),
87 'storeEntries' => empty($settings['disable_entries']),
88 'formActionButtons' => [
89 'submitButtonSettings' => [
90 'label' => $submitLabel,
91 'position' => 'default',
92 ],
93 ],
94 ],
95 'fields' => self::extractFields($formRaw),
96 'notifications' => [],
97 'confirmation' => self::extractConfirmation($settings),
98 ];
99 }
100
101 /**
102 * @param array<string, mixed> $formRaw
103 * @return array<int, array<string, mixed>>
104 */
105 private static function extractFields(array $formRaw): array
106 {
107 $context = ['id' => 1, 'fieldIndex' => 1, 'position' => 1, 'rowIndex' => 0];
108 $fields = [];
109
110 foreach (self::child($formRaw, 'fields') as $rawField) {
111 if (!is_array($rawField)) {
112 continue;
113 }
114
115 if (($rawField['type'] ?? '') === 'name') {
116 WpFormsNameFieldMapper::append($rawField, $fields, $context);
117 continue;
118 }
119 if (($rawField['type'] ?? '') === 'address') {
120 WpFormsAddressFieldMapper::append($rawField, $fields, $context);
121 continue;
122 }
123
124 $field = self::convertField($rawField, $context);
125 if ($field !== null) {
126 $fields[] = $field;
127 }
128 }
129
130 return $fields;
131 }
132
133 /**
134 * @param array<string, mixed> $rawField
135 * @param array<string, int> $context
136 * @return array<string, mixed>|null
137 */
138 private static function convertField(array $rawField, array &$context): ?array
139 {
140 $wpFormsType = (string) ($rawField['type'] ?? '');
141 $ivyType = self::TYPE_MAP[$wpFormsType] ?? null;
142 if ($ivyType === null) {
143 return null;
144 }
145 $ivyType = WpFormsFieldSettingsMapper::resolveType($wpFormsType, $ivyType, $rawField);
146
147 $htmlContent = self::extractHtml($rawField);
148 if ($ivyType === 'html' && $htmlContent === '') {
149 return null;
150 }
151
152 $field = self::buildFieldBase($rawField, $ivyType, $context);
153 if ($ivyType === 'html') {
154 $field['htmlContent'] = $htmlContent;
155 }
156
157 $options = self::extractOptions($rawField, $wpFormsType);
158 if (!empty($options)) {
159 $field['fieldOptions'] = $options;
160 }
161 WpFormsFieldSettingsMapper::apply($field, $rawField, $wpFormsType, $ivyType, $options);
162
163 return $field;
164 }
165
166 /**
167 * @param array<string, mixed> $rawField
168 * @param string $ivyType
169 * @param array<string, int> $context
170 * @return array<string, mixed>
171 */
172 private static function buildFieldBase(array $rawField, string $ivyType, array &$context): array
173 {
174 $defaultValue = $rawField['default_value'] ?? '';
175 $label = (string) ($rawField['label'] ?? '');
176 if ($label === '' && $ivyType === 'html') {
177 $label = (string) ($rawField['name'] ?? '');
178 }
179
180 return [
181 'id' => $context['id']++,
182 'fieldIndex' => $context['fieldIndex']++,
183 'type' => $ivyType,
184 'label' => sanitize_text_field($label),
185 'placeholder' => sanitize_text_field((string) ($rawField['placeholder'] ?? '')),
186 'description' => sanitize_text_field((string) ($rawField['description'] ?? '')),
187 'required' => !empty($rawField['required']),
188 'requiredMessage' => '',
189 'defaultValue' => is_scalar($defaultValue) ? sanitize_text_field((string) $defaultValue) : '',
190 'hideLabel' => !empty($rawField['label_hide']) || !empty($rawField['label_disable']),
191 'parentId' => null,
192 'position' => $context['position']++,
193 'rowIndex' => $context['rowIndex']++,
194 'columnIndex' => 0,
195 'width' => 100,
196 ];
197 }
198
199 /**
200 * @param array<string, mixed> $rawField
201 * @param string $wpFormsType
202 * @return array<int, array<string, mixed>>
203 */
204 private static function extractOptions(array $rawField, string $wpFormsType): array
205 {
206 $rawOptions = $rawField['choices'] ?? [];
207 if (!is_array($rawOptions)) {
208 return [];
209 }
210
211 $options = [];
212 foreach ($rawOptions as $rawOption) {
213 if (!is_array($rawOption)) {
214 continue;
215 }
216
217 $label = sanitize_text_field((string) ($rawOption['label'] ?? ''));
218 $rawValue = strpos($wpFormsType, 'payment-') === 0
219 ? $label
220 : ($rawOption['value'] ?? $label);
221 $value = sanitize_text_field((string) $rawValue);
222 if ($label === '' && $value === '') {
223 continue;
224 }
225
226 $position = count($options) + 1;
227 $options[] = [
228 'id' => $position,
229 'label' => $label !== '' ? $label : $value,
230 'value' => $value !== '' ? $value : $label,
231 'isDefault' => !empty($rawOption['default']),
232 'position' => $position,
233 ];
234 }
235
236 return $options;
237 }
238
239 /**
240 * @param array<string, mixed> $rawField
241 * @return string
242 */
243 private static function extractHtml(array $rawField): string
244 {
245 foreach (['code', 'content'] as $key) {
246 if (!empty($rawField[$key]) && is_string($rawField[$key])) {
247 return wp_kses_post($rawField[$key]);
248 }
249 }
250
251 return '';
252 }
253
254 /**
255 * @param array<string, mixed> $settings
256 * @return array<string, mixed>
257 */
258 private static function extractConfirmation(array $settings): array
259 {
260 $confirmations = self::child($settings, 'confirmations');
261 $confirmation = reset($confirmations);
262 if (!is_array($confirmation)) {
263 return [];
264 }
265
266 $type = (string) ($confirmation['type'] ?? 'message');
267 if ($type === 'redirect' && !empty($confirmation['redirect'])) {
268 return self::redirectConfirmation(
269 'redirectToCustomUrl',
270 'url',
271 esc_url_raw((string) $confirmation['redirect'])
272 );
273 }
274
275 if ($type === 'page' && !empty($confirmation['page'])) {
276 return self::redirectConfirmation('redirectToPage', 'page', (string) $confirmation['page']);
277 }
278
279 $message = (string) ($confirmation['message'] ?? '');
280
281 return $message === '' ? [] : [
282 'type' => 'successMessage',
283 'enabled' => true,
284 'showForm' => false,
285 'message' => wp_kses_post($message),
286 'url' => '',
287 'page' => '',
288 ];
289 }
290
291 /**
292 * @param string $type
293 * @param string $targetKey
294 * @param string $targetValue
295 * @return array<string, mixed>
296 */
297 private static function redirectConfirmation(string $type, string $targetKey, string $targetValue): array
298 {
299 return array_merge(
300 [
301 'type' => $type,
302 'enabled' => true,
303 'showForm' => false,
304 'message' => '',
305 'url' => '',
306 'page' => '',
307 ],
308 [$targetKey => $targetValue]
309 );
310 }
311
312 /**
313 * @param array<string, mixed> $source
314 * @param string $key
315 * @return array<string, mixed>
316 */
317 private static function child(array $source, string $key): array
318 {
319 return is_array($source[$key] ?? null) ? $source[$key] : [];
320 }
321 }
322