PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.14
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.14
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
← All changes | app/Modules/Form/FormDataParser.php +230 -136 3.6.406.2.14 View file →
@@ -2,57 +2,82 @@
2 2
3 3 namespace FluentForm\App\Modules\Form;
4 4
5 5 use FluentForm\Framework\Helpers\ArrayHelper;
6 -use WpFluent\Exception;
7 6
8 7 class FormDataParser
9 8 {
10 - protected static $data = null;
9 + protected static $data = null;
10 + protected static $submissionId = null;
11 11
12 - public static function parseFormEntries($entries, $form, $fields = null)
13 - {
14 - $fields = $fields ? $fields : FormFieldsParser::getEntryInputs($form);
12 + public static function parseFormEntries($entries, $form, $fields = null)
13 + {
14 + $fields = $fields ? $fields : FormFieldsParser::getEntryInputs($form);
15 15
16 - foreach ($entries as $entry) {
17 - static::parseFormEntry($entry, $form, $fields);
16 + foreach ($entries as $entry) {
17 + static::parseFormEntry($entry, $form, $fields);
18 18 }
19 19
20 20 return $entries;
21 - }
21 + }
22 22
23 - public static function parseFormEntry($entry, $form, $fields = null, $isHtml = false)
24 - {
25 - $fields = $fields ? $fields : FormFieldsParser::getEntryInputs($form);
23 + public static function parseFormEntry($entry, $form, $fields = null, $isHtml = false)
24 + {
25 + $fields = $fields ? $fields : FormFieldsParser::getEntryInputs($form);
26 26
27 - $entry->user_inputs = static::parseData(
28 - json_decode($entry->response), $fields, $form->id, $isHtml
29 - );
27 + $entry->user_inputs = static::parseData(
28 + json_decode($entry->response),
29 + $fields,
30 + $form->id,
31 + $isHtml
32 + );
30 33
31 34 return $entry;
32 - }
35 + }
33 36
34 - public static function parseFormSubmission($submission, $form, $fields, $isHtml = false)
35 - {
36 - if (is_null(static::$data)) {
37 - static::$data = static::parseData(
38 - json_decode($submission->response), $fields, $form->id, $isHtml
39 - );
40 - }
41 -
42 - $submission->user_inputs = static::$data;
37 + public static function parseFormSubmission($submission, $form, $fields, $isHtml = false)
38 + {
39 + // Sometimes submission will change inside loop. So we need to parse submission data for new one
40 + $newSubmission = $submission->id != static::$submissionId;
43 41
42 + if (is_null(static::$data) || $newSubmission) {
43 + static::$data = static::parseData(
44 + json_decode($submission->response),
45 + $fields,
46 + $form->id,
47 + $isHtml
48 + );
49 + static::$submissionId = $submission->id;
50 + }
51 +
52 + $submission->user_inputs = static::$data;
53 +
44 54 return $submission;
45 - }
55 + }
46 56
47 - public static function parseData( $response, $fields, $formId, $isHtml = false )
48 - {
49 - $trans = [];
57 + public static function parseData($response, $fields, $formId, $isHtml = false)
58 + {
59 + $trans = [];
50 60 foreach ($fields as $field_key => $field) {
51 61 if (isset($response->{$field_key})) {
62 + $value = $response->{$field_key};
63 +
64 + $value = apply_filters_deprecated(
65 + 'fluentform_response_render_' . $field['element'],
66 + [
67 + $value,
68 + $field,
69 + $formId,
70 + $isHtml
71 + ],
72 + FLUENTFORM_FRAMEWORK_UPGRADE,
73 + 'fluentform/response_render_' . $field['element'],
74 + 'Use fluentform/response_render_' . $field['element'] . ' instead of fluentform_response_render_' . $field['element']
75 + );
76 +
52 77 $value = apply_filters(
53 - 'fluentform_response_render_'.$field['element'],
54 - $response->{$field_key},
78 + 'fluentform/response_render_' . $field['element'],
79 + $value,
55 80 $field,
56 81 $formId,
57 82 $isHtml
58 83 );
@@ -62,39 +87,46 @@
62 87 }
63 88 }
64 89
65 90 return $trans;
66 - }
91 + }
67 92
68 - public static function formatValue($value)
69 - {
70 - if (is_array($value) || is_object($value)) {
93 + public static function formatValue($value)
94 + {
95 + if (is_array($value) || is_object($value)) {
71 96 return fluentImplodeRecursive(', ', array_filter(array_values((array) $value)));
72 97 }
73 98
74 99 return $value;
75 - }
100 + }
76 101
77 - public static function formatFileValues($values, $isHtml)
102 + public static function formatFileValues($values, $isHtml, $form_id = null)
78 103 {
79 - if(!$values) {
104 + if (!$values) {
80 105 return $values;
81 106 }
82 107
83 - if(is_string($values)) {
108 + if (is_string($values)) {
84 109 return $values;
85 110 }
86 111
87 - if(!$isHtml) {
112 + if (!$isHtml) {
88 113 return fluentImplodeRecursive(', ', array_filter(array_values((array) $values)));
89 114 }
115 + if ($form_id && \FluentForm\App\Helpers\Helper::isEntryAutoDeleteEnabled($form_id)) {
116 + return '';
117 + }
90 118
91 119 $html = '<ul class="ff_entry_list">';
92 120 foreach ($values as $value) {
93 - if(!$value) {
121 + if (!$value) {
94 122 continue;
95 123 }
96 - $html .= '<li><a href="'.$value.'" target="_blank">'.basename($value).'</a></li>';
124 + // SECURITY (FINDING-23): escape the submitted upload value. It reaches this HTML sink
125 + // via an unauthenticated submission and is only sanitize_text_field'd (keeps " and :),
126 + // so a javascript: URL or an " onmouseover=" attribute breakout would otherwise render
127 + // in the admin entry view, notification email and PDF. esc_url enforces a safe scheme.
128 + $html .= '<li><a href="' . esc_url($value) . '" target="_blank">' . esc_html(basename($value)) . '</a></li>';
97 129 }
98 130
99 131 $html .= '</ul>';
100 132 return $html;
@@ -99,36 +131,43 @@
99 131 $html .= '</ul>';
100 132 return $html;
101 133 }
102 134
103 - public static function formatImageValues($values, $isHtml)
135 + public static function formatImageValues($values, $isHtml, $form_id = null)
104 136 {
105 - if(!$values) {
137 + if (!$values) {
106 138 return $values;
107 139 }
108 140
109 - if(is_string($values)) {
141 + if (is_string($values)) {
110 142 return $values;
111 143 }
144 +
145 + $isHtml = apply_filters('fluentform/render_field_as_html', $isHtml, $values, $form_id);
112 146
113 - if(!$isHtml) {
147 +
148 + if (!$isHtml) {
114 149 return fluentImplodeRecursive(', ', array_filter(array_values((array) $values)));
115 150 }
116 -
117 - if(count($values) == 1) {
151 + if ($form_id && \FluentForm\App\Helpers\Helper::isEntryAutoDeleteEnabled($form_id)) {
152 + return '';
153 + }
154 + if (1 == count($values)) {
118 155 $value = $values[0];
119 - if(!$value) {
156 + if (!$value) {
120 157 return '';
121 158 }
122 - return '<a href="'.$value.'" target="_blank"><img style="max-width:180px" src="'.$value.'" /></a>';
159 + // SECURITY (FINDING-23): escape the submitted upload value (see formatFileValues).
160 + return '<a href="' . esc_url($value) . '" target="_blank"><img style="max-width:180px" src="' . esc_url($value) . '" /></a>';
123 161 }
124 162
125 163 $html = '<ul class="ff_entry_list ff_entry_images">';
126 164 foreach ($values as $value) {
127 - if(!$value) {
165 + if (!$value) {
128 166 continue;
129 167 }
130 - $html .= '<li style="margin: 20px 20px 20px 0px; display: inline-block; margin-right: 20px;"><a href="'.$value.'" target="_blank"><img style="max-width:180px" src="'.$value.'" /></a></li>';
168 + // SECURITY (FINDING-23): escape the submitted upload value (see formatFileValues).
169 + $html .= '<li style="margin: 20px 20px 20px 0px; display: inline-block; margin-right: 20px;"><a href="' . esc_url($value) . '" target="_blank"><img style="max-width:180px" src="' . esc_url($value) . '" /></a></li>';
131 170 }
132 171
133 172 $html .= '</ul>';
134 173 return $html;
@@ -133,115 +172,114 @@
133 172 $html .= '</ul>';
134 173 return $html;
135 174 }
136 175
137 - public static function formatRepeatFieldValue($value, $field, $form_id) {
176 + public static function formatRepeatFieldValue($value, $field, $form_id)
177 + {
138 178
139 - if(defined('FLUENTFORM_RENDERING_ENTRIES')) {
140 - return __('....', 'fluentform');
141 - }
179 + if (defined('FLUENTFORM_RENDERING_ENTRIES')) {
180 + return __('....', 'fluentform');
181 + }
142 182
143 - if(is_string($value)) {
144 - return $value;
145 - }
183 + if (is_string($value)) {
184 + return $value;
185 + }
146 186
147 - try {
148 - $repeatColumns = ArrayHelper::get($field, 'raw.fields');
149 - $rows = count($value[0]);
150 - $columns = count($value);
187 + try {
188 + $repeatColumns = ArrayHelper::get($field, 'raw.fields');
189 + $rows = count($value[0]);
190 + $columns = count($value);
151 191
152 - ob_start();
153 - if($repeatColumns) {
192 + ob_start();
193 + if ($repeatColumns) {
154 194 ?>
155 195 <div class="ff_entry_table_wrapper">
156 196 <table class="ff_entry_table_field ff-table">
157 197 <thead>
158 - <tr>
159 - <?php foreach ($repeatColumns as $repeatColumn) : ?>
160 - <th><?php echo ArrayHelper::get($repeatColumn, 'settings.label'); ?></th>
161 - <?php endforeach; ?>
162 - </tr>
198 + <tr>
199 + <?php foreach ($repeatColumns as $repeatColumn) : ?>
200 + <th><?php echo fluentform_sanitize_html(ArrayHelper::get($repeatColumn, 'settings.label')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- fluentform_sanitize_html() removes XSS vectors and uses wp_kses() with allowed tags ?>
201 + </th>
202 + <?php endforeach; ?>
203 + </tr>
163 204 </thead>
164 205
165 206 <tbody>
166 - <?php for ($i = 0; $i < $rows; $i++) : ?>
207 + <?php for ($i = 0; $i < $rows; $i++) : ?>
167 208 <tr>
168 209 <?php for ($j = 0; $j < $columns; $j++) : ?>
169 - <td>
170 - <?php echo $value[$j][$i] ?>
171 - </td>
210 + <td>
211 + <?php echo fluentform_sanitize_html($value[$j][$i]); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- fluentform_sanitize_html() removes XSS vectors and uses wp_kses() with allowed tags ?>
212 + </td>
172 213 <?php endfor; ?>
173 214 </tr>
174 - <?php endfor; ?>
215 + <?php endfor; ?>
175 216 </tbody>
176 217 </table>
177 218 </div>
178 219 <?php
179 220 }
180 - return ob_get_clean();
181 - } catch (Exception $e) {
221 + return ob_get_clean();
222 + } catch (\Exception $e) {
223 + }
182 224
183 - }
225 + return $value;
226 + }
184 227
185 - return $value;
186 - }
228 + public static function formatTabularGridFieldValue($value, $field, $form_id, $isHtml = false)
229 + {
230 + if (defined('FLUENTFORM_RENDERING_ENTRIES')) {
231 + return __('....', 'fluentform');
232 + }
187 233
188 - public static function formatTabularGridFieldValue($value, $field, $form_id, $isHtml = false)
189 - {
190 - if(defined('FLUENTFORM_RENDERING_ENTRIES')) {
191 - return __('....', 'fluentform');
192 - }
234 + if (is_string($value)) {
235 + return $value;
236 + }
193 237
194 - if(is_string($value)) {
195 - return $value;
196 - }
197 -
198 - if(is_array($value)) {
238 + if (is_array($value)) {
199 239 $value = (object) $value;
200 240 }
201 - try {
202 - if(empty($field['raw'])) {
203 - return $value;
241 + try {
242 + if (empty($field['raw'])) {
243 + return $value;
204 244 }
205 - $columnLabels = $field['raw']['settings']['grid_columns'];
206 - $fieldType = $field['raw']['settings']['tabular_field_type'];
207 - $columnHeaders = implode('</th><th>', array_values($columnLabels));
245 + $columnLabels = $field['raw']['settings']['grid_columns'];
246 + $fieldType = $field['raw']['settings']['tabular_field_type'];
247 + $columnHeaders = implode('</th><th style="text-align: center;">', array_values($columnLabels));
208 248
209 - $elMarkup = "<table class='ff-table'><thead><tr><th></th><th>{$columnHeaders}</th></tr></thead><tbody>";
249 + $elMarkup = "<table class='ff-table'><thead><tr><th></th><th style='text-align: center;'>{$columnHeaders}</th></tr></thead><tbody>";
210 250
211 - foreach (static::makeTabularData($field['raw']) as $row) {
212 -
213 - $elMarkup .= "<tr>";
214 - $elMarkup .= "<td>{$row['label']}</td>";
215 - foreach ($row['columns'] as $column) {
251 + foreach (static::makeTabularData($field['raw']) as $row) {
252 + $elMarkup .= '<tr>';
253 + $elMarkup .= "<td>{$row['label']}</td>";
254 + foreach ($row['columns'] as $column) {
216 255 $isChecked = '';
217 - if ($fieldType == 'radio') {
218 - if(isset($value->{$row['name']})) {
256 + if ('radio' == $fieldType) {
257 + if (isset($value->{$row['name']})) {
219 258 $isChecked = $value->{$row['name']} == $column['name'] ? 'checked' : '';
220 259 }
221 260 } else {
222 - if(isset($value->{$row['name']})) {
261 + if (isset($value->{$row['name']})) {
223 262 $isChecked = in_array($column['name'], $value->{$row['name']}) ? 'checked' : '';
224 263 }
225 264 }
226 265 $icon = "<input disabled type='{$fieldType}' {$isChecked}>";
227 - if($isChecked) {
266 + if ($isChecked) {
228 267 $icon = '✔';
229 268 }
230 - $elMarkup .= "<td>".$icon."</td>";
231 - }
232 - $elMarkup .= "</tr>";
233 - }
269 + $elMarkup .= "<td style='text-align: center;'>" . $icon . '</td>';
270 + }
271 + $elMarkup .= '</tr>';
272 + }
234 273
235 - $elMarkup .= "</tbody></table>";
274 + $elMarkup .= '</tbody></table>';
236 275
237 - return $elMarkup;
238 - } catch (Exception $e) {
276 + return $elMarkup;
277 + } catch (\Exception $e) {
278 + }
279 + return '';
280 + }
239 281
240 - }
241 - return '';
242 - }
243 -
244 282 public static function makeTabularData($data)
245 283 {
246 284 $table = [];
247 285 $rows = $data['settings']['grid_rows'];
@@ -247,37 +285,93 @@
247 285 $rows = $data['settings']['grid_rows'];
248 286 $columns = $data['settings']['grid_columns'];
249 287
250 288 foreach ($rows as $rowKey => $rowValue) {
289 + $rowKey = trim(sanitize_text_field($rowKey));
251 290 $table[$rowKey] = [
252 - 'name' => $rowKey,
253 - 'label' => $rowValue,
254 - 'columns' => []
291 + 'name' => $rowKey,
292 + 'label' => $rowValue,
293 + 'columns' => [],
255 294 ];
256 295
257 296 foreach ($columns as $columnKey => $columnValue) {
258 297 $table[$rowKey]['columns'][] = [
259 - 'name' => $columnKey,
260 - 'label' => $columnValue
298 + 'name' => trim(sanitize_text_field($columnKey)),
299 + 'label' => $columnValue,
261 300 ];
262 301 }
263 302 }
303 + return $table;
304 + }
264 305
265 - return $table;
306 + /**
307 + * Format input_name field value by concatenating all name fields.
308 + *
309 + * @param array|object $value
310 + *
311 + * @return string $value
312 + */
313 + public static function formatName($value)
314 + {
315 + if (is_array($value) || is_object($value)) {
316 + $value = (array) $value;
317 + $order = ['first_name', 'middle_name', 'last_name'];
318 + uksort($value, function($a, $b) use ($order) {
319 + $posA = array_search($a, $order);
320 + $posB = array_search($b, $order);
321 + return $posA - $posB;
322 + });
323 + return fluentImplodeRecursive(' ', array_filter(array_values($value)));
324 + }
325 +
326 + return $value;
266 327 }
267 328
268 - /**
269 - * Format input_name field value by concatenating all name fields.
270 - *
271 - * @param array|object $value
272 - *
273 - * @return string $value
274 - */
275 - public static function formatName($value)
276 - {
277 - if (is_array($value) || is_object($value)) {
278 - return fluentImplodeRecursive(' ', array_filter(array_values((array) $value)));
279 - }
329 + public static function formatCheckBoxValues($values, $field, $isHtml = false)
330 + {
331 + if (!$isHtml) {
332 + if (
333 + defined('FLUENTFORM_RENDERING_ENTRIES') &&
334 + $values && is_array($values) &&
335 + $options = ArrayHelper::get($field, 'raw.settings.advanced_options', [])
336 + ) {
337 + $options = \FluentForm\App\Helpers\Helper::advancedOptionsValueLabelMap($options);
338 + foreach ($values as &$value) {
339 + if ($label = ArrayHelper::get($options, $value)) {
340 + $value = $label;
341 + }
342 + }
343 + }
344 + return self::formatValue($values);
345 + }
280 346
281 - return $value;
282 - }
347 + if (!is_array($values)) {
348 + return $values;
349 + }
350 +
351 + if (empty($values)) {
352 + return '';
353 + }
354 +
355 + if (!isset($field['options'])) {
356 + $field['options'] = \FluentForm\App\Helpers\Helper::advancedOptionsValueLabelMap(
357 + ArrayHelper::get($field, 'raw.settings.advanced_options', [])
358 + );
359 + }
360 +
361 + $html = '<ul style="white-space: normal;">';
362 + foreach ($values as $value) {
363 + $item = $value;
364 + if ($itemLabel = ArrayHelper::get($field, 'options.' . $item)) {
365 + $item = $itemLabel;
366 + }
367 + $html .= '<li>' . $item . '</li>';
368 + }
369 +
370 + return $html . '</ul>';
371 + }
372 +
373 + public static function resetData()
374 + {
375 + static::$data = null;
376 + }
283 377 }