PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.9
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.9
6.2.14 6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 3.6.65 All 196 releases
fluentform / app / Services / Form / Updater.php

Updater.php in Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder 6.2.9, at app/Services/Form/Updater.php

430 lines 16.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentForm\App\Services\Form;
4
5 use Exception;
6 use FluentForm\App\Helpers\Helper;
7 use FluentForm\App\Models\Form;
8 use FluentForm\App\Models\FormMeta;
9 use FluentForm\App\Services\FormBuilder\RatingIcon;
10 use FluentForm\Framework\Support\Arr;
11 use FluentForm\App\Modules\Form\FormFieldsParser;
12
13 class Updater
14 {
15 public function update($attributes = [])
16 {
17 $formId = (int) Arr::get($attributes, 'form_id');
18 $formFields = Arr::get($attributes, 'formFields');
19 $status = sanitize_text_field(Arr::get($attributes, 'status', 'published'));
20 $title = sanitize_text_field(Arr::get($attributes, 'title'));
21
22 $this->validate([
23 'title' => $title,
24 'formFields' => $formFields,
25 ]);
26
27 try {
28 $form = Form::findOrFail($formId);
29 } catch (Exception $e) {
30 throw new \Exception("The form couldn't be found.");
31 }
32
33 $data = [
34 'title' => $title,
35 'status' => $status,
36 'updated_at' => current_time('mysql'),
37 ];
38
39 if ($formFields) {
40 $formFields = apply_filters_deprecated(
41 'fluentform_form_fields_update',
42 [
43 $formFields,
44 $formId,
45 ],
46 FLUENTFORM_FRAMEWORK_UPGRADE,
47 'fluentform/form_fields_update',
48 'Use fluentform/form_fields_update instead of fluentform_form_fields_update.'
49 );
50 $formFields = apply_filters('fluentform/form_fields_update', $formFields, $formId);
51 $formFields = $this->sanitizeFields($formFields);
52 $data['form_fields'] = $formFields;
53 /**
54 * Fires before a Form is updated.
55 *
56 * @since 5.2.1
57 */
58 do_action('fluentform/before_updating_form', $form, $data);
59
60 $form->fill($data);
61
62 if (FormFieldsParser::hasPaymentFields($form)) {
63 $data['has_payment'] = 1;
64 } elseif ($form->has_payment) {
65 $data['has_payment'] = 0;
66 }
67
68 $this->updatePrimaryEmail($form);
69
70 }
71
72 $form->fill($data)->save();
73
74 return $form;
75 }
76
77 private function validate($attributes)
78 {
79 if ($attributes['formFields']) {
80 $duplicates = Helper::getDuplicateFieldNames($attributes['formFields']);
81
82 if ($duplicates) {
83 $duplicateString = implode(', ', $duplicates);
84
85 throw new Exception(
86 sprintf('Name attribute %s has duplicate value.', esc_html($duplicateString))
87 );
88 }
89
90 $duplicateRankingFields = Helper::getRankingFieldsWithDuplicateOptionValues($attributes['formFields']);
91
92 if ($duplicateRankingFields) {
93 $duplicateRankingFields = implode(', ', array_unique($duplicateRankingFields));
94
95 throw new Exception(
96 sprintf(
97 // translators: %s is the ranking field name(s) with duplicate option values.
98 esc_html__('Ranking field %s has duplicate option values. Please make each option value unique.', 'fluentform'),
99 esc_html($duplicateRankingFields)
100 )
101 );
102 }
103 }
104
105 if (!$attributes['title']) {
106 throw new Exception('The title field is required.');
107 }
108 }
109
110 private function sanitizeFields($formFields)
111 {
112 if (fluentformCanUnfilteredHTML()) {
113 return $formFields;
114 }
115
116 $fieldsArray = json_decode($formFields, true);
117
118 if (isset($fieldsArray['submitButton'])) {
119 $fieldsArray['submitButton']['settings']['button_ui']['text'] = fluentform_sanitize_html(
120 $fieldsArray['submitButton']['settings']['button_ui']['text']
121 );
122
123 if (!empty($fieldsArray['submitButton']['settings']['button_ui']['img_url'])) {
124 $fieldsArray['submitButton']['settings']['button_ui']['img_url'] = sanitize_url(
125 $fieldsArray['submitButton']['settings']['button_ui']['img_url']
126 );
127 }
128 }
129 $fieldsArray['fields'] = $this->sanitizeFieldMaps($fieldsArray['fields']);
130 $fieldsArray['fields'] = $this->sanitizeCustomSubmit($fieldsArray['fields']);
131 if ($stepsWrapper = Arr::get($fieldsArray, 'stepsWrapper')) {
132 $fieldsArray['stepsWrapper'] = $this->sanitizeStepsWrapper($stepsWrapper);
133 }
134
135 return json_encode($fieldsArray);
136 }
137
138 private function sanitizeFieldMaps($fields)
139 {
140 if (!is_array($fields)) {
141 return $fields;
142 }
143
144 $attributesMap = [
145 'name' => 'sanitize_key',
146 'value' => 'sanitize_textarea_field',
147 'id' => 'sanitize_key',
148 'class' => 'sanitize_text_field',
149 'placeholder' => 'sanitize_text_field',
150 ];
151
152 $attributesKeys = array_keys($attributesMap);
153
154 $settingsMap = [
155 'container_class' => 'sanitize_text_field',
156 'label' => 'fluentform_sanitize_html',
157 'tnc_html' => 'fluentform_sanitize_html',
158 'label_placement' => 'sanitize_text_field',
159 'help_message' => 'wp_kses_post',
160 'admin_field_label' => 'sanitize_text_field',
161 'prefix_label' => 'sanitize_text_field',
162 'suffix_label' => 'sanitize_text_field',
163 'icon_source' => 'sanitize_key',
164 'icon_type' => 'sanitize_key',
165 'custom_icon_svg' => [RatingIcon::class, 'sanitizeCustomSvg'],
166 'inactive_color' => [RatingIcon::class, 'sanitizeColor'],
167 'active_color' => [RatingIcon::class, 'sanitizeColor'],
168 'unique_validation_message' => 'sanitize_text_field',
169 'advanced_options' => 'fluentform_options_sanitize',
170 'html_codes' => 'fluentform_sanitize_html',
171 'description' => 'fluentform_sanitize_html',
172 'grid_columns' => [Helper::class, 'sanitizeArrayKeysAndValues'],
173 'grid_rows' => [Helper::class, 'sanitizeArrayKeysAndValues'],
174 'date_config' => 'fluentform_sanitize_json_object',
175 'enable_crop' => 'sanitize_text_field',
176 'crop_mode' => 'sanitize_text_field',
177 'crop_ratio' => 'sanitize_text_field',
178 'crop_width' => 'absint',
179 'crop_height' => 'absint',
180 'enforce_image_dimensions' => 'sanitize_text_field',
181 ];
182
183 $settingsKeys = array_keys($settingsMap);
184
185 $stylePrefMap = [
186 'layout' => 'sanitize_key',
187 'media' => 'sanitize_url',
188 'alt_text' => 'sanitize_text_field',
189 ];
190 $stylePrefKeys = array_keys($stylePrefMap);
191
192 foreach ($fields as $fieldIndex => &$field) {
193 $element = Arr::get($field, 'element');
194
195 if ('container' == $element) {
196 $columns = $field['columns'];
197 foreach ($columns as $columnIndex => $column) {
198 $fields[$fieldIndex]['columns'][$columnIndex]['fields'] = $this->sanitizeFieldMaps($column['fields']);
199 }
200 continue;
201 }
202
203 if ('welcome_screen' == $element) {
204 if ($value = Arr::get($field, 'settings.button_ui.text')) {
205 $field['settings']['button_ui']['text'] = fluentform_sanitize_html($value);
206 }
207 }
208
209 if ('form_step' == $element) {
210 foreach (['next_btn', 'prev_btn'] as $buttonKey) {
211 $buttonSettings = Arr::get($field, 'settings.' . $buttonKey);
212 if (!is_array($buttonSettings)) {
213 continue;
214 }
215
216 if (isset($buttonSettings['type'])) {
217 $field['settings'][$buttonKey]['type'] = sanitize_text_field($buttonSettings['type']);
218 }
219
220 if (isset($buttonSettings['text'])) {
221 $field['settings'][$buttonKey]['text'] = fluentform_sanitize_html($buttonSettings['text']);
222 }
223
224 if (isset($buttonSettings['img_url'])) {
225 $field['settings'][$buttonKey]['img_url'] = esc_url_raw($buttonSettings['img_url']);
226 }
227
228 if (isset($buttonSettings['img_alt'])) {
229 $field['settings'][$buttonKey]['img_alt'] = sanitize_text_field($buttonSettings['img_alt']);
230 }
231 }
232 }
233
234 if ('save_progress_button' == $element) {
235 $buttonUi = Arr::get($field, 'settings.button_ui');
236 if (is_array($buttonUi)) {
237 if (isset($buttonUi['type'])) {
238 $field['settings']['button_ui']['type'] = sanitize_text_field($buttonUi['type']);
239 }
240
241 if (isset($buttonUi['text'])) {
242 $field['settings']['button_ui']['text'] = fluentform_sanitize_html($buttonUi['text']);
243 }
244
245 if (isset($buttonUi['img_url'])) {
246 $field['settings']['button_ui']['img_url'] = esc_url_raw($buttonUi['img_url']);
247 }
248 }
249 }
250
251 if (!empty($field['attributes'])) {
252 $attributes = array_filter(Arr::only($field['attributes'], $attributesKeys));
253
254 foreach ($attributes as $key => $value) {
255 $fields[$fieldIndex]['attributes'][$key] = call_user_func($attributesMap[$key], $value);
256 }
257 }
258
259 if (!empty($field['settings'])) {
260 $settings = array_filter(Arr::only($field['settings'], array_values($settingsKeys)));
261 foreach ($settings as $key => $value) {
262 $fields[$fieldIndex]['settings'][$key] = call_user_func($settingsMap[$key], $value);
263 }
264 }
265 /*
266 * Handle Name or address fields
267 */
268 if (!empty($field['fields'])) {
269 $fields[$fieldIndex]['fields'] = $this->sanitizeFieldMaps($field['fields']);
270 continue;
271 }
272
273 if (!empty($field['style_pref'])) {
274 $settings = array_filter(Arr::only($field['style_pref'], $stylePrefKeys));
275
276 foreach ($settings as $key => $value) {
277 $fields[$fieldIndex]['style_pref'][$key] = call_user_func($stylePrefMap[$key], $value);
278 }
279 }
280
281 $validationRules = Arr::get($field, 'settings.validation_rules');
282 if (!empty($validationRules)) {
283 foreach ($validationRules as $key => $rule) {
284 if (isset($rule['message'])) {
285 $message = $rule['message'];
286 $field['settings']['validation_rules'][$key]['message'] = wp_kses_post($message);
287 continue;
288 }
289 }
290 }
291 }
292
293 return $fields;
294 }
295
296 private function updatePrimaryEmail($form)
297 {
298 $emailInputs = FormFieldsParser::getElement($form, ['input_email'], ['element', 'attributes']);
299
300 if ($emailInputs) {
301 $emailInput = array_shift($emailInputs);
302 $emailInputName = Arr::get($emailInput, 'attributes.name');
303 } else {
304 $emailInputName = '';
305 }
306
307 FormMeta::persist($form->id, '_primary_email_field', $emailInputName);
308 }
309
310 private function sanitizeCustomSubmit($fields)
311 {
312 $customSubmitSanitizationMap = [
313 'hover_styles' => [
314 'backgroundColor' => [$this, 'sanitizeRgbColor'],
315 'borderColor' => [$this, 'sanitizeRgbColor'],
316 'color' => [$this, 'sanitizeRgbColor'],
317 'borderRadius' => 'sanitize_text_field',
318 'minWidth' => [$this, 'sanitizeMinWidth'],
319 ],
320 'normal_styles' => [
321 'backgroundColor' => [$this, 'sanitizeRgbColor'],
322 'borderColor' => [$this, 'sanitizeRgbColor'],
323 'color' => [$this, 'sanitizeRgbColor'],
324 'borderRadius' => 'sanitize_text_field',
325 'minWidth' => [$this, 'sanitizeMinWidth'],
326 ],
327 'button_ui' => [
328 'type' => 'sanitize_text_field',
329 'text' => 'fluentform_sanitize_html',
330 'img_url' => 'esc_url_raw',
331 ],
332 ];
333 foreach ($fields as $fieldIndex => $field) {
334 $element = Arr::get($field, 'element');
335
336 if ('custom_submit_button' == $element) {
337 $styleAttr = ['hover_styles', 'normal_styles', 'button_ui'];
338 foreach ($styleAttr as $attr) {
339 if ($styleConfigs = Arr::get($field, 'settings.' . $attr)) {
340 foreach ($styleConfigs as $key => $value) {
341 if (isset($customSubmitSanitizationMap[$attr][$key])) {
342 $sanitizeFunction = $customSubmitSanitizationMap[$attr][$key];
343 $fields[$fieldIndex]['settings'][$attr][$key] = $sanitizeFunction($value);
344 }
345 }
346 }
347 }
348 } elseif ('container' == $element) {
349 $columns = $field['columns'];
350 foreach ($columns as $columnIndex => $column) {
351 $fields[$fieldIndex]['columns'][$columnIndex]['fields'] = $this->sanitizeCustomSubmit($column['fields']);
352 }
353 return $fields;
354 }
355 }
356 return $fields;
357 }
358
359 private function sanitizeStepsWrapper($stepWrapper)
360 {
361 $stepsSanitizationMap = [
362 'prev_btn' => [
363 'type' => 'sanitize_text_field',
364 'text' => 'fluentform_sanitize_html',
365 'img_url' => 'esc_url_raw',
366 ],
367 ];
368
369 foreach ($stepWrapper as $fieldIndex => $field) {
370 $element = Arr::get($field, 'element');
371
372 if ('step_start' === $element || 'step_end' === $element) {
373 if (!empty($field['settings']['step_titles']) && is_array($field['settings']['step_titles'])) {
374 foreach ($field['settings']['step_titles'] as $index => $title) {
375 $field['settings']['step_titles'][$index] = fluentform_sanitize_html($title);
376 }
377 }
378
379 if (isset($field['settings']['tabs_show_progress_bar'])) {
380 $field['settings']['tabs_show_progress_bar'] = sanitize_text_field($field['settings']['tabs_show_progress_bar']) === 'yes' ? 'yes' : 'no';
381 }
382
383 if (isset($field['settings']['progress_layout'])) {
384 $progressLayout = sanitize_text_field($field['settings']['progress_layout']);
385 $field['settings']['progress_layout'] = in_array($progressLayout, ['top', 'left'], true) ? $progressLayout : 'top';
386 }
387
388 if (!empty($field['settings']['prev_btn']) && is_array($field['settings']['prev_btn'])) {
389 foreach ($field['settings']['prev_btn'] as $key => $value) {
390 if (isset($stepsSanitizationMap['prev_btn'][$key])) {
391 $sanitizeFunction = $stepsSanitizationMap['prev_btn'][$key];
392 $field['settings']['prev_btn'][$key] = $sanitizeFunction($value);
393 }
394 }
395 }
396
397 if (!empty($field['attributes']['class'])) {
398 $field['attributes']['class'] = sanitize_text_field($field['attributes']['class']);
399 }
400 if (!empty($field['attributes']['id'])) {
401 $field['attributes']['id'] = sanitize_text_field($field['attributes']['id']);
402 }
403 }
404
405 if ('step_start' === $element && isset($field['fields'])) {
406 $field['fields'] = $this->sanitizeStepsWrapper($field['fields']);
407 }
408
409 $stepWrapper[$fieldIndex] = $field;
410 }
411
412 return $stepWrapper;
413 }
414
415 public function sanitizeMinWidth($value)
416 {
417 if (is_string($value) && preg_match('/^\d+%$/', $value)) {
418 return $value;
419 }
420 return '';
421 }
422
423 public function sanitizeRgbColor($value) {
424 if (preg_match('/^rgba?\((\d{1,3}\s*,\s*){2,3}(0|1|0?\.\d+)\)$/', $value)) {
425 return $value;
426 }
427 return '';
428 }
429 }
430