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 +371 -76 3.6.22 → 6.2.15 View file →
@@ -8,51 +8,65 @@
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 = [];
31 36
37 + public $containerCounter;
38 +
32 39 /**
33 40 * Construct the form builder instance
34 - * @param Framework\Foundation\Application $app
41 + *
42 + * @param \FluentForm\Framework\Foundation\Application $app
35 43 */
36 44 public function __construct($app)
37 45 {
38 46 $this->app = $app;
47 + $this->containerCounter = 1;
39 48 }
40 49
41 50 /**
42 51 * Render the form
43 - * @param \StdClass $form [Form entry from database]
52 + *
53 + * @param \StdClass $form [Form entry from database]
54 + *
44 55 * @return mixed
45 56 */
46 - public function build($form, $extraCssClass = '', $instanceCssClass = '')
57 + public function build($form, $extraCssClass = '', $instanceCssClass = '', $atts = [])
47 58 {
59 + $this->form = $form;
48 60 $hasStepWrapper = isset($form->fields['stepsWrapper']) && $form->fields['stepsWrapper'];
49 -
50 - $labelPlacement = $form->settings['layout']['labelPlacement'];
51 61
62 + $labelPlacement = ArrayHelper::get($form->settings, 'layout.labelPlacement', 'top');
63 +
52 64 $formClass = "frm-fluent-form fluent_form_{$form->id} ff-el-form-{$labelPlacement}";
53 65
54 - if ($extraCssClass) $formClass .= " {$extraCssClass}";
66 + if ($extraCssClass) {
67 + $formClass .= " {$extraCssClass}";
68 + }
55 69
56 70 if ($hasStepWrapper) {
57 71 $formClass .= ' ff-form-has-steps';
58 72 }
@@ -60,37 +74,123 @@
60 74 if ($extraFormClass = Helper::formExtraCssClass($form)) {
61 75 $formClass .= ' ' . $extraFormClass;
62 76 }
63 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 +
64 92 $formBody = $this->buildFormBody($form);
65 93
66 - $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 + );
67 109
110 + $formClass = apply_filters('fluentform/form_class', $formClass, $form);
111 +
68 112 if ($form->has_payment) {
69 113 $formClass .= ' fluentform_has_payment';
70 114 }
71 115
72 - $formAttributes = apply_filters('fluent_form_html_attributes', [
73 - 'data-form_id' => $form->id,
74 - 'id' => 'fluentform_'.$form->id,
75 - 'class' => $formClass,
76 - 'data-form_instance' => $instanceCssClass
77 - ], $form);
116 + if ($themeStyle) {
117 + $formClass .= ' ' . $themeStyle;
118 + }
78 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 +
79 141 $formAtts = $this->buildAttributes($formAttributes);
142 +
80 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);
81 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 + );
82 166
83 - 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 + }
84 185
85 - do_action('fluentform_before_form_render', $form);
86 -
87 - echo "<form ".$formAtts.">";
88 -
186 + /* This hook is deprecated & will be removed soon */
89 187 do_action('fluentform_form_element_start', $form);
90 188
91 - echo "<input type='hidden' name='__fluent_form_embded_post_id' value='" . get_the_ID() . "' />";
92 -
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 +
93 193 wp_nonce_field(
94 194 'fluentform-submit-form',
95 195 '_fluentform_' . $form->id . '_fluentformnonce',
96 196 true,
@@ -95,40 +195,77 @@
95 195 '_fluentform_' . $form->id . '_fluentformnonce',
96 196 true,
97 197 true
98 198 );
99 -
100 - echo $formBody;
101 -
102 - echo "</form><div id='fluentform_" . $form->id . "_errors' class='ff-errors-in-stack ";
103 199
104 - echo $extraCssClass . "_errors " . $instanceCssClass . "_errors'></div></div>";
105 -
106 - do_action('fluentform_after_form_render', $form);
107 -
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 +
108 221 return ob_get_clean();
109 222 }
110 223
111 -
224 + /**
225 + * @param \stdClass $form
226 + *
227 + * @return string form body
228 + */
112 229 public function buildFormBody($form)
113 230 {
114 231 $hasStepWrapper = isset($form->fields['stepsWrapper']) && $form->fields['stepsWrapper'];
115 -
232 +
116 233 ob_start();
117 -
234 +
118 235 $stepCounter = 1;
119 236
120 237 foreach ($form->fields['fields'] as $item) {
121 -
122 - if ($hasStepWrapper && $item['element'] == 'form_step') {
238 + if ($hasStepWrapper && 'form_step' == $item['element']) {
123 239 $stepCounter++;
124 240 }
125 241
126 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);
127 255
128 - $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 + );
129 266
130 - do_action('fluentform_render_item_' . $item['element'], $item, $form);
267 + do_action('fluentform/render_item_' . $item['element'], $item, $form);
131 268
132 269 $this->extractValidationRules($item);
133 270
134 271 $this->extractConditionalLogic($item);
@@ -134,23 +271,40 @@
134 271 $this->extractConditionalLogic($item);
135 272 }
136 273
137 274 if ($hasStepWrapper) {
138 - do_action(
139 - '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.'
140 284 );
285 + do_action('fluentform/render_item_step_end', $form->fields['stepsWrapper']['stepEnd'], $form);
286 + } else {
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);
141 298 }
142 299
143 - do_action('fluentform_render_item_submit_button', $form->fields['submitButton'], $form);
144 -
145 300 $content = ob_get_clean();
146 301
147 -
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,14 +338,18 @@
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'])) {
350 + $item['attributes']['data-name'] = 'ff_cn_id_' . $this->containerCounter;
351 + $this->containerCounter++;
184 352 foreach ($item['columns'] as &$column) {
185 353 foreach ($column['fields'] as &$field) {
186 354 $this->setUniqueIdentifier($field);
187 355 }
@@ -186,10 +354,21 @@
186 354 $this->setUniqueIdentifier($field);
187 355 }
188 356 }
189 357 } else {
190 - if (!isset($item['attributes']['name'])) {
191 - $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;
192 371 }
193 372 $item['attributes']['data-name'] = $item['attributes']['name'];
194 373 $this->fieldLists[] = $item['element'];
195 374 }
@@ -196,14 +375,16 @@
196 375 }
197 376
198 377 /**
199 378 * Recursively extract validation rules from a given element
379 + *
200 380 * @param array $item
381 + *
201 382 * @return void
202 383 */
203 384 protected function extractValidationRules($item)
204 385 {
205 - if (isset($item['columns'])) {
386 + if (isset($item['columns']) && 'repeater_container' !== $item['element']) {
206 387 foreach ($item['columns'] as $column) {
207 388 foreach ($column['fields'] as $field) {
208 389 $this->extractValidationRules($field);
209 390 }
@@ -210,14 +391,14 @@
210 391 }
211 392 } elseif (isset($item['fields'])) {
212 393 $rootName = $item['attributes']['name'];
213 394 foreach ($item['fields'] as $key => $innerItem) {
214 - if ($item['element'] == 'address' || $item['element'] == 'input_name') {
395 + if ('address' == $item['element'] || 'input_name' == $item['element']) {
215 396 $itemName = $innerItem['attributes']['name'];
216 397 $innerItem['attributes']['name'] = $rootName . '[' . $itemName . ']';
217 398 } else {
218 - if ($item['element'] == 'input_repeat' || $item['element'] == 'repeater_field') {
219 - 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'])) {
220 401 unset($innerItem['settings']['validation_rules']['email']);
221 402 }
222 403 $innerItem['attributes']['name'] = $rootName . '[' . $key . ']';
223 404 } else {
@@ -225,14 +406,15 @@
225 406 }
226 407 }
227 408 $this->extractValidationRule($innerItem);
228 409 }
229 - } elseif ($item['element'] == 'tabular_grid') {
410 + } elseif ('tabular_grid' == $item['element']) {
230 411 $gridName = $item['attributes']['name'];
231 412 $gridRows = $item['settings']['grid_rows'];
232 413 $gridType = $item['settings']['tabular_field_type'];
233 414 foreach ($gridRows as $rowKey => $rowValue) {
234 - if ($gridType == 'radio') {
415 + $rowKey = trim(sanitize_text_field($rowKey));
416 + if ('radio' == $gridType) {
235 417 $item['attributes']['name'] = $gridName . '[' . $rowKey . ']';
236 418 $this->extractValidationRule($item);
237 419 } else {
238 420 $item['attributes']['name'] = $gridName . '[' . $rowKey . ']';
@@ -238,14 +420,26 @@
238 420 $item['attributes']['name'] = $gridName . '[' . $rowKey . ']';
239 421 $this->extractValidationRule($item);
240 422 }
241 423 }
242 - } elseif ($item['element'] == 'chained_select') {
424 + } elseif ('chained_select' == $item['element']) {
243 425 $chainedSelectName = $item['attributes']['name'];
244 426 foreach ($item['settings']['data_source']['headers'] as $select) {
245 427 $item['attributes']['name'] = $chainedSelectName . '[' . $select . ']';
246 428 $this->extractValidationRule($item);
247 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 + }
248 442 } else {
249 443 $this->extractValidationRule($item);
250 444 }
251 445 }
@@ -251,53 +445,134 @@
251 445 }
252 446
253 447 /**
254 448 * Extract validation rules from a given element
449 + *
255 450 * @param array $item
451 + *
256 452 * @return void
257 453 */
258 - protected function extractValidationRule($item)
454 + protected function extractValidationRule($item, $name = '')
259 455 {
260 456 if (isset($item['settings']['validation_rules'])) {
261 457 $rules = $item['settings']['validation_rules'];
262 458 foreach ($rules as $ruleName => $rule) {
263 - if(isset($rule['message'])) {
264 - $rules[$ruleName]['message'] = apply_filters('fluentform_validation_message_'.$ruleName, $rule['message'], $item);
265 - $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);
266 488 }
267 489 }
268 - $rules = apply_filters('fluentform_item_rules_' . $item['element'], $rules, $item);
269 - $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;
270 507 }
271 508 }
272 509
273 510 /**
274 - * Extract conditipnal logic from a given element
511 + * Extract conditional logic from a given element
512 + *
275 513 * @param array $item
514 + *
276 515 * @return void
277 516 */
278 - protected function extractConditionalLogic($item)
517 + protected function extractConditionalLogic($item, $itemName = '')
279 518 {
280 519 // If container like element, then recurse
281 520 if (isset($item['columns'])) {
282 - foreach ($item['columns'] as $column) {
283 - foreach ($column['fields'] as $field) {
284 - $this->extractConditionalLogic($field);
521 + $containerConditions = false;
522 + if (isset($item['settings']['conditional_logics'])) {
523 + $conditionals = $item['settings']['conditional_logics'];
524 + if (isset($conditionals['status'])) {
525 + if ($conditionals['status'] && $conditionals['conditions']) {
526 + $containerConditions = $item['settings']['conditional_logics'];
527 + $this->conditions[$item['attributes']['data-name']] = $containerConditions;
528 + }
285 529 }
286 530 }
531 + $parentName = ArrayHelper::get($item, 'attributes.name');
532 + $isRepeaterContainer = ArrayHelper::get($item, 'element') === 'repeater_container';
533 +
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 + }
545 + }
546 + }
287 547 } elseif (isset($item['settings']['conditional_logics'])) {
288 548 $conditionals = $item['settings']['conditional_logics'];
549 + if (!$itemName) {
550 + $itemName = $item['attributes']['data-name'];
551 + }
289 552 if (isset($conditionals['status'])) {
290 553 if ($conditionals['status'] && $conditionals['conditions']) {
291 - $this->conditions[$item['attributes']['data-name']] = $item['settings']['conditional_logics'];
554 + $this->conditions[$itemName] = $item['settings']['conditional_logics'];
292 555 }
293 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 + }
294 567 }
295 568 }
296 569
297 570 /**
298 571 * Build attributes for any html element
299 - * @param array $attributes
572 + *
573 + * @param array $attributes
574 + *
300 575 * @return string [Compiled key='value' attributes]
301 576 */
302 577 protected function buildAttributes($attributes, $form = null)
303 578 {
@@ -302,12 +577,32 @@
302 577 protected function buildAttributes($attributes, $form = null)
303 578 {
304 579 $atts = '';
305 580 foreach ($attributes as $key => $value) {
306 - 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) {
307 587 $value = htmlspecialchars($value);
308 - $atts .= $key.'="'.$value.'" ';
588 + $atts .= esc_attr($key) . '="' . $value . '" ';
309 589 }
310 590 }
311 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>';
312 607 }
313 608 }