PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.15
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.15
6.2.15 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 All 197 releases
← All changes | app/Services/FormBuilder/FormBuilder.php +359 -76 3.6.31 → 6.2.15 View file →
@@ -8,23 +8,28 @@
8 8 class FormBuilder
9 9 {
10 10 /**
11 11 * The Applivcation instance
12 - * @var Framework\Foundation\Application
12 + *
13 + * @var \FluentForm\Framework\Foundation\Application
13 14 */
14 15 protected $app = null;
15 16
17 + protected $form = null;
18 +
16 19 /**
17 20 * Conditional logic for elements
21 + *
18 22 * @var array
19 23 */
20 - public $conditions = array();
24 + public $conditions = [];
21 25
22 26 /**
23 27 * Validation rules for elements
28 + *
24 29 * @var array
25 30 */
26 - public $validationRules = array();
31 + public $validationRules = [];
27 32
28 33 public $tabIndex = 1;
29 34
30 35 public $fieldLists = [];
@@ -32,9 +37,10 @@
32 37 public $containerCounter;
33 38
34 39 /**
35 40 * Construct the form builder instance
36 - * @param Framework\Foundation\Application $app
41 + *
42 + * @param \FluentForm\Framework\Foundation\Application $app
37 43 */
38 44 public function __construct($app)
39 45 {
40 46 $this->app = $app;
@@ -42,20 +48,25 @@
42 48 }
43 49
44 50 /**
45 51 * Render the form
46 - * @param \StdClass $form [Form entry from database]
52 + *
53 + * @param \StdClass $form [Form entry from database]
54 + *
47 55 * @return mixed
48 56 */
49 - public function build($form, $extraCssClass = '', $instanceCssClass = '')
57 + public function build($form, $extraCssClass = '', $instanceCssClass = '', $atts = [])
50 58 {
59 + $this->form = $form;
51 60 $hasStepWrapper = isset($form->fields['stepsWrapper']) && $form->fields['stepsWrapper'];
52 -
53 - $labelPlacement = $form->settings['layout']['labelPlacement'];
54 61
62 + $labelPlacement = ArrayHelper::get($form->settings, 'layout.labelPlacement', 'top');
63 +
55 64 $formClass = "frm-fluent-form fluent_form_{$form->id} ff-el-form-{$labelPlacement}";
56 65
57 - if ($extraCssClass) $formClass .= " {$extraCssClass}";
66 + if ($extraCssClass) {
67 + $formClass .= " {$extraCssClass}";
68 + }
58 69
59 70 if ($hasStepWrapper) {
60 71 $formClass .= ' ff-form-has-steps';
61 72 }
@@ -63,37 +74,123 @@
63 74 if ($extraFormClass = Helper::formExtraCssClass($form)) {
64 75 $formClass .= ' ' . $extraFormClass;
65 76 }
66 77
78 + $themeStyle = ArrayHelper::get($atts, 'theme');
79 +
80 + if (!$themeStyle) {
81 + $selectedStyle = Helper::getFormMeta($form->id, '_ff_selected_style');
82 +
83 + $selectedStyle = $selectedStyle ? $selectedStyle : 'ffs_default';
84 +
85 + $themeStyle = $selectedStyle;
86 +
87 + $atts['theme'] = $selectedStyle;
88 + }
89 +
90 + $form->theme = $themeStyle;
91 +
67 92 $formBody = $this->buildFormBody($form);
68 93
69 - $formClass = apply_filters('fluentform_form_class', $formClass, $form);
94 + if (strpos($formBody, '{dynamic.')) {
95 + $formClass .= ' ff_has_dynamic_smartcode';
96 + wp_enqueue_script('fluentform-advanced');
97 + }
98 +
99 + $formClass = apply_filters_deprecated(
100 + 'fluentform_form_class',
101 + [
102 + $formClass,
103 + $form
104 + ],
105 + FLUENTFORM_FRAMEWORK_UPGRADE,
106 + 'fluentform/form_class',
107 + 'Use fluentform/form_class instead of fluentform_form_class.'
108 + );
70 109
110 + $formClass = apply_filters('fluentform/form_class', $formClass, $form);
111 +
71 112 if ($form->has_payment) {
72 113 $formClass .= ' fluentform_has_payment';
73 114 }
74 115
75 - $formAttributes = apply_filters('fluent_form_html_attributes', [
76 - 'data-form_id' => $form->id,
77 - 'id' => 'fluentform_'.$form->id,
78 - 'class' => $formClass,
79 - 'data-form_instance' => $instanceCssClass
80 - ], $form);
116 + if ($themeStyle) {
117 + $formClass .= ' ' . $themeStyle;
118 + }
81 119
120 + $data = [
121 + 'data-form_id' => $form->id,
122 + 'id' => 'fluentform_' . $form->id,
123 + 'class' => $formClass,
124 + 'data-form_instance' => $instanceCssClass,
125 + 'method' => 'POST',
126 + ];
127 +
128 + $data = apply_filters_deprecated(
129 + 'fluent_form_html_attributes',
130 + [
131 + $data,
132 + $form
133 + ],
134 + FLUENTFORM_FRAMEWORK_UPGRADE,
135 + 'fluentform/html_attributes',
136 + 'Use fluentform/html_attributes instead of fluent_form_html_attributes.'
137 + );
138 +
139 + $formAttributes = apply_filters('fluentform/html_attributes', $data, $form);
140 +
82 141 $formAtts = $this->buildAttributes($formAttributes);
142 +
83 143
144 + $wrapperClasses = trim('fluentform ff-default fluentform_wrapper_' . $form->id . ' ' . ArrayHelper::get($atts, 'css_classes'));
145 +
146 + if ($themeStyle === 'ffs_inherit_theme') {
147 + $wrapperClasses = str_replace("ff-default", "ff-inherit-theme-style", $wrapperClasses);
148 + } else if ($themeStyle) {
149 + $wrapperClasses .= ' ' . $themeStyle . '_wrap';
150 + }
151 +
152 + $wrapperClasses = apply_filters('fluentform/form_wrapper_classes', $wrapperClasses, $form);
84 153 ob_start();
154 +
155 + echo "<div class='" . esc_attr($wrapperClasses) . "'>";
156 +
157 + do_action_deprecated(
158 + 'fluentform_before_form_render',
159 + [
160 + $form
161 + ],
162 + FLUENTFORM_FRAMEWORK_UPGRADE,
163 + 'fluentform/before_form_render',
164 + 'Use fluentform/before_form_render instead of fluentform_before_form_render.'
165 + );
85 166
86 - echo "<div class='fluentform fluentform_wrapper_".$form->id."'>";
167 + do_action('fluentform/before_form_render', $form, $atts);
168 + echo '<form ' . $formAtts . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $formAtts is escaped before being passed in.
169 +
170 + $isAccessible = apply_filters_deprecated(
171 + 'fluentform_disable_accessibility_fieldset',
172 + [
173 + true,
174 + $form
175 + ],
176 + FLUENTFORM_FRAMEWORK_UPGRADE,
177 + 'fluentform/disable_accessibility_fieldset',
178 + 'Use fluentform/disable_accessibility_fieldset instead of fluentform_disable_accessibility_fieldset.'
179 + );
180 + $isAccessible = apply_filters('fluentform/disable_accessibility_fieldset', true, $form);
181 +
182 + if ($isAccessible) {
183 + echo $this->fieldsetHtml($form); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $form is escaped before being passed in.
184 + }
87 185
88 - do_action('fluentform_before_form_render', $form);
89 -
90 - echo "<form ".$formAtts.">";
91 -
186 + /* This hook is deprecated & will be removed soon */
92 187 do_action('fluentform_form_element_start', $form);
93 188
94 - echo "<input type='hidden' name='__fluent_form_embded_post_id' value='" . get_the_ID() . "' />";
95 -
189 + do_action('fluentform/form_element_start', $form);
190 +
191 + echo "<input type='hidden' name='__fluent_form_embded_post_id' value='" . esc_attr(get_the_ID()) . "' />";
192 +
96 193 wp_nonce_field(
97 194 'fluentform-submit-form',
98 195 '_fluentform_' . $form->id . '_fluentformnonce',
99 196 true,
@@ -98,40 +195,77 @@
98 195 '_fluentform_' . $form->id . '_fluentformnonce',
99 196 true,
100 197 true
101 198 );
102 -
103 - echo $formBody;
104 -
105 - echo "</form><div id='fluentform_" . $form->id . "_errors' class='ff-errors-in-stack ";
106 199
107 - echo $extraCssClass . "_errors " . $instanceCssClass . "_errors'></div></div>";
108 -
109 - do_action('fluentform_after_form_render', $form);
110 -
200 + echo $formBody; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $formBody is escaped before being passed in.
201 +
202 + if ($isAccessible) {
203 + echo '</fieldset>';
204 + }
205 +
206 + echo "</form><div id='fluentform_" . (int) $form->id . "_errors' class='ff-errors-in-stack ";
207 +
208 + echo esc_attr($extraCssClass) . '_errors ' . esc_attr($instanceCssClass) . "_errors'></div></div>";
209 +
210 + do_action_deprecated(
211 + 'fluentform_after_form_render',
212 + [
213 + $form
214 + ],
215 + FLUENTFORM_FRAMEWORK_UPGRADE,
216 + 'fluentform/after_form_render',
217 + 'Use fluentform/after_form_render instead of fluentform_after_form_render.'
218 + );
219 + do_action('fluentform/after_form_render', $form);
220 +
111 221 return ob_get_clean();
112 222 }
113 223
114 -
224 + /**
225 + * @param \stdClass $form
226 + *
227 + * @return string form body
228 + */
115 229 public function buildFormBody($form)
116 230 {
117 231 $hasStepWrapper = isset($form->fields['stepsWrapper']) && $form->fields['stepsWrapper'];
118 -
232 +
119 233 ob_start();
120 -
234 +
121 235 $stepCounter = 1;
122 236
123 237 foreach ($form->fields['fields'] as $item) {
124 -
125 - if ($hasStepWrapper && $item['element'] == 'form_step') {
238 + if ($hasStepWrapper && 'form_step' == $item['element']) {
126 239 $stepCounter++;
127 240 }
128 241
129 242 $this->setUniqueIdentifier($item);
243 +
244 + $item = apply_filters_deprecated(
245 + 'fluentform_before_render_item',
246 + [
247 + $item,
248 + $form
249 + ],
250 + FLUENTFORM_FRAMEWORK_UPGRADE,
251 + 'fluentform/before_render_item',
252 + 'Use fluentform/before_render_item instead of fluentform_before_render_item.'
253 + );
254 + $item = apply_filters('fluentform/before_render_item', $item, $form);
130 255
131 - $item = apply_filters('fluentform_before_render_item', $item, $form);
256 + do_action_deprecated(
257 + 'fluentform_render_item_' . $item['element'],
258 + [
259 + $item,
260 + $form
261 + ],
262 + FLUENTFORM_FRAMEWORK_UPGRADE,
263 + 'fluentform/render_item_' . $item['element'],
264 + 'Use fluentform/render_item_' . $item['element'] . ' instead of fluentform_render_item_' . $item['element']
265 + );
132 266
133 - do_action('fluentform_render_item_' . $item['element'], $item, $form);
267 + do_action('fluentform/render_item_' . $item['element'], $item, $form);
134 268
135 269 $this->extractValidationRules($item);
136 270
137 271 $this->extractConditionalLogic($item);
@@ -137,11 +271,31 @@
137 271 $this->extractConditionalLogic($item);
138 272 }
139 273
140 274 if ($hasStepWrapper) {
141 - do_action('fluentform_render_item_step_end', $form->fields['stepsWrapper']['stepEnd'], $form);
275 + do_action_deprecated(
276 + 'fluentform_render_item_step_end',
277 + [
278 + $form->fields['stepsWrapper']['stepEnd'],
279 + $form
280 + ],
281 + FLUENTFORM_FRAMEWORK_UPGRADE,
282 + 'fluentform/render_item_step_end',
283 + 'Use fluentform/render_item_step_end instead of fluentform_render_item_step_end.'
284 + );
285 + do_action('fluentform/render_item_step_end', $form->fields['stepsWrapper']['stepEnd'], $form);
142 286 } else {
143 - do_action('fluentform_render_item_submit_button', $form->fields['submitButton'], $form);
287 + do_action_deprecated(
288 + 'fluentform_render_item_submit_button',
289 + [
290 + $form->fields['submitButton'],
291 + $form
292 + ],
293 + FLUENTFORM_FRAMEWORK_UPGRADE,
294 + 'fluentform/render_item_submit_button',
295 + 'Use fluentform/render_item_submit_button instead of fluentform_render_item_submit_button.'
296 + );
297 + do_action('fluentform/render_item_submit_button', $form->fields['submitButton'], $form);
144 298 }
145 299
146 300 $content = ob_get_clean();
147 301
@@ -146,11 +300,11 @@
146 300 $content = ob_get_clean();
147 301
148 302 if ($hasStepWrapper) {
149 303 $startElement = $form->fields['stepsWrapper']['stepStart'];
150 -
304 +
151 305 $steps = ArrayHelper::get($startElement, 'settings.step_titles');
152 -
306 +
153 307 // check if $stepCounter == count()
154 308 if ($stepCounter > count($steps)) {
155 309 $fillCount = $stepCounter - count($steps);
156 310 foreach (range(1, $fillCount) as $item) {
@@ -159,16 +313,26 @@
159 313 $startElement['settings']['step_titles'] = $steps;
160 314 }
161 315
162 316 $this->setUniqueIdentifier($startElement);
163 -
317 +
164 318 ob_start();
165 -
166 - do_action('fluentform_render_item_step_start', $startElement, $form);
167 -
319 +
320 + do_action_deprecated(
321 + 'fluentform_render_item_step_start',
322 + [
323 + $startElement,
324 + $form
325 + ],
326 + FLUENTFORM_FRAMEWORK_UPGRADE,
327 + 'fluentform/render_item_step_start',
328 + 'Use fluentform/render_item_step_start instead of fluentform_render_item_step_start.'
329 + );
330 + do_action('fluentform/render_item_step_start', $startElement, $form);
331 +
168 332 $stepStatrt = ob_get_clean();
169 -
170 - $content = $stepStatrt.$content;
333 +
334 + $content = $stepStatrt . $content;
171 335 }
172 336
173 337 return $content;
174 338 }
@@ -174,15 +338,17 @@
174 338 }
175 339
176 340 /**
177 341 * Set unique name/data-name for an element
342 + *
178 343 * @param array &$item
179 - * @return void
344 + *
345 + * @return void
180 346 */
181 347 protected function setUniqueIdentifier(&$item)
182 348 {
183 349 if (isset($item['columns'])) {
184 - $item['attributes']['data-name'] = 'ff_cn_id_'.$this->containerCounter;
350 + $item['attributes']['data-name'] = 'ff_cn_id_' . $this->containerCounter;
185 351 $this->containerCounter++;
186 352 foreach ($item['columns'] as &$column) {
187 353 foreach ($column['fields'] as &$field) {
188 354 $this->setUniqueIdentifier($field);
@@ -188,10 +354,21 @@
188 354 $this->setUniqueIdentifier($field);
189 355 }
190 356 }
191 357 } else {
192 - if (!isset($item['attributes']['name'])) {
193 - $item['attributes']['name'] = $item['element'] . '-' . uniqid(rand(), true);
358 + if (! isset($item['attributes']['name'])) {
359 + if ($this->form) {
360 + if (empty($this->form->attr_name_index)) {
361 + $this->form->attr_name_index = 1;
362 + } else {
363 + $this->form->attr_name_index += 1;
364 + }
365 + $uniqueId = $this->form->id . '_' . $this->form->attr_name_index;
366 + } else {
367 + $uniqueId = uniqid(wp_rand(), true);
368 + }
369 +
370 + $item['attributes']['name'] = $item['element'] . '-' . $uniqueId;
194 371 }
195 372 $item['attributes']['data-name'] = $item['attributes']['name'];
196 373 $this->fieldLists[] = $item['element'];
197 374 }
@@ -198,14 +375,16 @@
198 375 }
199 376
200 377 /**
201 378 * Recursively extract validation rules from a given element
379 + *
202 380 * @param array $item
381 + *
203 382 * @return void
204 383 */
205 384 protected function extractValidationRules($item)
206 385 {
207 - if (isset($item['columns'])) {
386 + if (isset($item['columns']) && 'repeater_container' !== $item['element']) {
208 387 foreach ($item['columns'] as $column) {
209 388 foreach ($column['fields'] as $field) {
210 389 $this->extractValidationRules($field);
211 390 }
@@ -212,14 +391,14 @@
212 391 }
213 392 } elseif (isset($item['fields'])) {
214 393 $rootName = $item['attributes']['name'];
215 394 foreach ($item['fields'] as $key => $innerItem) {
216 - if ($item['element'] == 'address' || $item['element'] == 'input_name') {
395 + if ('address' == $item['element'] || 'input_name' == $item['element']) {
217 396 $itemName = $innerItem['attributes']['name'];
218 397 $innerItem['attributes']['name'] = $rootName . '[' . $itemName . ']';
219 398 } else {
220 - if ($item['element'] == 'input_repeat' || $item['element'] == 'repeater_field') {
221 - if(!empty($innerItem['settings']['validation_rules']['email'])) {
399 + if ('input_repeat' == $item['element'] || 'repeater_field' == $item['element']) {
400 + if (empty($innerItem['settings']['validation_rules']['email'])) {
222 401 unset($innerItem['settings']['validation_rules']['email']);
223 402 }
224 403 $innerItem['attributes']['name'] = $rootName . '[' . $key . ']';
225 404 } else {
@@ -227,14 +406,15 @@
227 406 }
228 407 }
229 408 $this->extractValidationRule($innerItem);
230 409 }
231 - } elseif ($item['element'] == 'tabular_grid') {
410 + } elseif ('tabular_grid' == $item['element']) {
232 411 $gridName = $item['attributes']['name'];
233 412 $gridRows = $item['settings']['grid_rows'];
234 413 $gridType = $item['settings']['tabular_field_type'];
235 414 foreach ($gridRows as $rowKey => $rowValue) {
236 - if ($gridType == 'radio') {
415 + $rowKey = trim(sanitize_text_field($rowKey));
416 + if ('radio' == $gridType) {
237 417 $item['attributes']['name'] = $gridName . '[' . $rowKey . ']';
238 418 $this->extractValidationRule($item);
239 419 } else {
240 420 $item['attributes']['name'] = $gridName . '[' . $rowKey . ']';
@@ -240,14 +420,26 @@
240 420 $item['attributes']['name'] = $gridName . '[' . $rowKey . ']';
241 421 $this->extractValidationRule($item);
242 422 }
243 423 }
244 - } elseif ($item['element'] == 'chained_select') {
424 + } elseif ('chained_select' == $item['element']) {
245 425 $chainedSelectName = $item['attributes']['name'];
246 426 foreach ($item['settings']['data_source']['headers'] as $select) {
247 427 $item['attributes']['name'] = $chainedSelectName . '[' . $select . ']';
248 428 $this->extractValidationRule($item);
249 429 }
430 + } elseif ('repeater_container' == $item['element']) {
431 + $rootName = $item['attributes']['name'];
432 + $ruleIndex = 0;
433 + foreach ($item['columns'] as $key => $innerItem) {
434 + foreach ($innerItem['fields'] as &$innerField) {
435 + $innerField['attributes']['name'] = $rootName . '[' . $key . ']';
436 + $rules = ArrayHelper::get($innerField, 'settings.validation_rules', []);
437 + $innerField['settings']['validation_rules'] = $rules;
438 + $this->extractValidationRule($innerField, $rootName . '[' . $ruleIndex . ']');
439 + $ruleIndex++;
440 + }
441 + }
250 442 } else {
251 443 $this->extractValidationRule($item);
252 444 }
253 445 }
@@ -253,63 +445,134 @@
253 445 }
254 446
255 447 /**
256 448 * Extract validation rules from a given element
449 + *
257 450 * @param array $item
451 + *
258 452 * @return void
259 453 */
260 - protected function extractValidationRule($item)
454 + protected function extractValidationRule($item, $name = '')
261 455 {
262 456 if (isset($item['settings']['validation_rules'])) {
263 457 $rules = $item['settings']['validation_rules'];
264 458 foreach ($rules as $ruleName => $rule) {
265 - if(isset($rule['message'])) {
266 - $rules[$ruleName]['message'] = apply_filters('fluentform_validation_message_'.$ruleName, $rule['message'], $item);
267 - $rules[$ruleName]['message'] = apply_filters('fluentform_validation_message_'.$item['element'].'_'.$ruleName, $rule['message'], $item);
459 + if (isset($rule['message'])) {
460 + if (isset($rule['global']) && $rule['global']) {
461 + $rule['message'] = apply_filters('fluentform/get_global_message_' . $ruleName, $rule['message']);
462 + }
463 + // Shortcode parse on validation message
464 + $rule['message'] = Helper::shortCodeParseOnValidationMessage($rule['message'], $this->form, ArrayHelper::get($item, 'attributes.name'));
465 + $rules[$ruleName]['message'] = apply_filters_deprecated(
466 + 'fluentform_validation_message_' . $ruleName,
467 + [
468 + $rule['message'],
469 + $item
470 + ],
471 + FLUENTFORM_FRAMEWORK_UPGRADE,
472 + 'fluentform/validation_message_' . $ruleName,
473 + 'Use fluentform/validation_message_' . $ruleName . ' instead of fluentform_validation_message_' . $ruleName
474 + );
475 + $rules[$ruleName]['message'] = apply_filters('fluentform/validation_message_' . $ruleName, $rule['message'], $item);
476 +
477 + $rules[$ruleName]['message'] = apply_filters_deprecated(
478 + 'fluentform_validation_message_' . $item['element'] . '_' . $ruleName,
479 + [
480 + $rules[$ruleName]['message'],
481 + $item
482 + ],
483 + FLUENTFORM_FRAMEWORK_UPGRADE,
484 + 'fluentform/validation_message_' . $item['element'] . '_' . $ruleName,
485 + 'Use fluentform/validation_message_' . $item['element'] . '_' . $ruleName . ' instead of fluentform_validation_message_' . $item['element'] . '_' . $ruleName
486 + );
487 + $rules[$ruleName]['message'] = apply_filters('fluentform/validation_message_' . $item['element'] . '_' . $ruleName, $rules[$ruleName]['message'], $item);
268 488 }
269 489 }
270 - $rules = apply_filters('fluentform_item_rules_' . $item['element'], $rules, $item);
271 - $this->validationRules[ $item['attributes']['name'] ] = $rules;
490 +
491 + $rules = apply_filters_deprecated(
492 + 'fluentform_item_rules_' . $item['element'],
493 + [
494 + $rules,
495 + $item
496 + ],
497 + FLUENTFORM_FRAMEWORK_UPGRADE,
498 + 'fluentform/item_rules_' . $item['element'],
499 + 'Use fluentform/item_rules_' . $item['element'] . ' instead of fluentform_item_rules_' . $item['element']
500 + );
501 +
502 + $rules = apply_filters('fluentform/item_rules_' . $item['element'], $rules, $item);
503 + if (!$name) {
504 + $name = $item['attributes']['name'];
505 + }
506 + $this->validationRules[$name] = $rules;
272 507 }
273 508 }
274 509
275 510 /**
276 - * Extract conditipnal logic from a given element
511 + * Extract conditional logic from a given element
512 + *
277 513 * @param array $item
514 + *
278 515 * @return void
279 516 */
280 - protected function extractConditionalLogic($item)
517 + protected function extractConditionalLogic($item, $itemName = '')
281 518 {
282 519 // If container like element, then recurse
283 520 if (isset($item['columns'])) {
284 -
521 + $containerConditions = false;
285 522 if (isset($item['settings']['conditional_logics'])) {
286 523 $conditionals = $item['settings']['conditional_logics'];
287 524 if (isset($conditionals['status'])) {
288 525 if ($conditionals['status'] && $conditionals['conditions']) {
289 - $this->conditions[$item['attributes']['data-name']] = $item['settings']['conditional_logics'];
526 + $containerConditions = $item['settings']['conditional_logics'];
527 + $this->conditions[$item['attributes']['data-name']] = $containerConditions;
290 528 }
291 529 }
292 530 }
531 + $parentName = ArrayHelper::get($item, 'attributes.name');
532 + $isRepeaterContainer = ArrayHelper::get($item, 'element') === 'repeater_container';
293 533
294 - foreach ($item['columns'] as $column) {
295 - foreach ($column['fields'] as $field) {
296 - $this->extractConditionalLogic($field);
534 + foreach ($item['columns'] as $colIndex => $column) {
535 + foreach ($column['fields'] as $fieldIndex => $field) {
536 + if ($containerConditions) {
537 + $field['container_conditions'] = $containerConditions;
538 + }
539 + if ($isRepeaterContainer) {
540 + $conditionName = $parentName . '_condition_' . $colIndex . '_' . $fieldIndex;
541 + $this->extractConditionalLogic($field, $conditionName);
542 + } else {
543 + $this->extractConditionalLogic($field);
544 + }
297 545 }
298 546 }
299 547 } elseif (isset($item['settings']['conditional_logics'])) {
300 548 $conditionals = $item['settings']['conditional_logics'];
549 + if (!$itemName) {
550 + $itemName = $item['attributes']['data-name'];
551 + }
301 552 if (isset($conditionals['status'])) {
302 553 if ($conditionals['status'] && $conditionals['conditions']) {
303 - $this->conditions[$item['attributes']['data-name']] = $item['settings']['conditional_logics'];
554 + $this->conditions[$itemName] = $item['settings']['conditional_logics'];
304 555 }
305 556 }
557 + if (isset($item['container_conditions'])) {
558 + if (! isset($this->conditions[$item['attributes']['data-name']])) {
559 + $this->conditions[$itemName] = [
560 + 'conditions' => [],
561 + 'status' => false,
562 + 'type' => 'any',
563 + ];
564 + }
565 + $this->conditions[$itemName]['container_condition'] = $item['container_conditions'];
566 + }
306 567 }
307 568 }
308 569
309 570 /**
310 571 * Build attributes for any html element
311 - * @param array $attributes
572 + *
573 + * @param array $attributes
574 + *
312 575 * @return string [Compiled key='value' attributes]
313 576 */
314 577 protected function buildAttributes($attributes, $form = null)
315 578 {
@@ -314,12 +577,32 @@
314 577 protected function buildAttributes($attributes, $form = null)
315 578 {
316 579 $atts = '';
317 580 foreach ($attributes as $key => $value) {
318 - if ($value || $value === 0 || $value === '0') {
581 + // SECURITY (FINDING-06): drop event-handler attribute keys and escape the key name
582 + // (author-controlled attribute keys are not allowlisted at save).
583 + if (preg_match('/^on[a-z]/i', (string) $key)) {
584 + continue;
585 + }
586 + if ($value || 0 === $value || '0' === $value) {
319 587 $value = htmlspecialchars($value);
320 - $atts .= $key.'="'.$value.'" ';
588 + $atts .= esc_attr($key) . '="' . $value . '" ';
321 589 }
322 590 }
323 591 return $atts;
592 + }
593 +
594 + /**
595 + * Get hidden fieldset html
596 + *
597 + * @param $form
598 + *
599 + * @return string
600 + */
601 + private function fieldsetHtml($form)
602 + {
603 + return '<fieldset style="border: none!important;margin: 0!important;padding: 0!important;background-color: transparent!important;box-shadow: none!important;outline: none!important; min-inline-size: 100%;">
604 + <legend class="ff_screen_reader_title" style="display: block; margin: 0!important;padding: 0!important;height: 0!important;text-indent: -999999px;width: 0!important;overflow:hidden;">'
605 + .esc_html($form->title).
606 + '</legend>';
324 607 }
325 608 }