| @@ -8,9 +8,10 @@ | ||
| 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 | |
| 16 | 17 | protected $form = null; |
| @@ -16,17 +17,19 @@ | ||
| 16 | 17 | protected $form = null; |
| 17 | 18 | |
| 18 | 19 | /** |
| 19 | 20 | * Conditional logic for elements |
| 21 | + * | |
| 20 | 22 | * @var array |
| 21 | 23 | */ |
| 22 | - public $conditions = array(); | |
| 24 | + public $conditions = []; | |
| 23 | 25 | |
| 24 | 26 | /** |
| 25 | 27 | * Validation rules for elements |
| 28 | + * | |
| 26 | 29 | * @var array |
| 27 | 30 | */ |
| 28 | - public $validationRules = array(); | |
| 31 | + public $validationRules = []; | |
| 29 | 32 | |
| 30 | 33 | public $tabIndex = 1; |
| 31 | 34 | |
| 32 | 35 | public $fieldLists = []; |
| @@ -34,9 +37,10 @@ | ||
| 34 | 37 | public $containerCounter; |
| 35 | 38 | |
| 36 | 39 | /** |
| 37 | 40 | * Construct the form builder instance |
| 38 | - * @param Framework\Foundation\Application $app | |
| 41 | + * | |
| 42 | + * @param \FluentForm\Framework\Foundation\Application $app | |
| 39 | 43 | */ |
| 40 | 44 | public function __construct($app) |
| 41 | 45 | { |
| 42 | 46 | $this->app = $app; |
| @@ -44,21 +48,25 @@ | ||
| 44 | 48 | } |
| 45 | 49 | |
| 46 | 50 | /** |
| 47 | 51 | * Render the form |
| 48 | - * @param \StdClass $form [Form entry from database] | |
| 52 | + * | |
| 53 | + * @param \StdClass $form [Form entry from database] | |
| 54 | + * | |
| 49 | 55 | * @return mixed |
| 50 | 56 | */ |
| 51 | - public function build($form, $extraCssClass = '', $instanceCssClass = '') | |
| 57 | + public function build($form, $extraCssClass = '', $instanceCssClass = '', $atts = []) | |
| 52 | 58 | { |
| 53 | 59 | $this->form = $form; |
| 54 | 60 | $hasStepWrapper = isset($form->fields['stepsWrapper']) && $form->fields['stepsWrapper']; |
| 55 | - | |
| 56 | - $labelPlacement = $form->settings['layout']['labelPlacement']; | |
| 57 | 61 | |
| 62 | + $labelPlacement = ArrayHelper::get($form->settings, 'layout.labelPlacement', 'top'); | |
| 63 | + | |
| 58 | 64 | $formClass = "frm-fluent-form fluent_form_{$form->id} ff-el-form-{$labelPlacement}"; |
| 59 | 65 | |
| 60 | - if ($extraCssClass) $formClass .= " {$extraCssClass}"; | |
| 66 | + if ($extraCssClass) { | |
| 67 | + $formClass .= " {$extraCssClass}"; | |
| 68 | + } | |
| 61 | 69 | |
| 62 | 70 | if ($hasStepWrapper) { |
| 63 | 71 | $formClass .= ' ff-form-has-steps'; |
| 64 | 72 | } |
| @@ -66,42 +74,123 @@ | ||
| 66 | 74 | if ($extraFormClass = Helper::formExtraCssClass($form)) { |
| 67 | 75 | $formClass .= ' ' . $extraFormClass; |
| 68 | 76 | } |
| 69 | 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 | + | |
| 70 | 92 | $formBody = $this->buildFormBody($form); |
| 71 | 93 | |
| 72 | - if(strpos($formBody, '{dynamic.')) { | |
| 94 | + if (strpos($formBody, '{dynamic.')) { | |
| 73 | 95 | $formClass .= ' ff_has_dynamic_smartcode'; |
| 74 | 96 | wp_enqueue_script('fluentform-advanced'); |
| 75 | 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 | + ); | |
| 76 | 109 | |
| 77 | - $formClass = apply_filters('fluentform_form_class', $formClass, $form); | |
| 110 | + $formClass = apply_filters('fluentform/form_class', $formClass, $form); | |
| 78 | 111 | |
| 79 | 112 | if ($form->has_payment) { |
| 80 | 113 | $formClass .= ' fluentform_has_payment'; |
| 81 | 114 | } |
| 82 | 115 | |
| 83 | - $formAttributes = apply_filters('fluent_form_html_attributes', [ | |
| 84 | - 'data-form_id' => $form->id, | |
| 85 | - 'id' => 'fluentform_'.$form->id, | |
| 86 | - 'class' => $formClass, | |
| 87 | - 'data-form_instance' => $instanceCssClass | |
| 88 | - ], $form); | |
| 116 | + if ($themeStyle) { | |
| 117 | + $formClass .= ' ' . $themeStyle; | |
| 118 | + } | |
| 89 | 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 | + | |
| 90 | 141 | $formAtts = $this->buildAttributes($formAttributes); |
| 142 | + | |
| 91 | 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); | |
| 92 | 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 | + ); | |
| 93 | 166 | |
| 94 | - 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 | + } | |
| 95 | 185 | |
| 96 | - do_action('fluentform_before_form_render', $form); | |
| 97 | - | |
| 98 | - echo "<form ".$formAtts.">"; | |
| 99 | - | |
| 186 | + /* This hook is deprecated & will be removed soon */ | |
| 100 | 187 | do_action('fluentform_form_element_start', $form); |
| 101 | 188 | |
| 102 | - echo "<input type='hidden' name='__fluent_form_embded_post_id' value='" . get_the_ID() . "' />"; | |
| 103 | - | |
| 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 | + | |
| 104 | 193 | wp_nonce_field( |
| 105 | 194 | 'fluentform-submit-form', |
| 106 | 195 | '_fluentform_' . $form->id . '_fluentformnonce', |
| 107 | 196 | true, |
| @@ -106,40 +195,77 @@ | ||
| 106 | 195 | '_fluentform_' . $form->id . '_fluentformnonce', |
| 107 | 196 | true, |
| 108 | 197 | true |
| 109 | 198 | ); |
| 110 | - | |
| 111 | - echo $formBody; | |
| 112 | - | |
| 113 | - echo "</form><div id='fluentform_" . $form->id . "_errors' class='ff-errors-in-stack "; | |
| 114 | 199 | |
| 115 | - echo $extraCssClass . "_errors " . $instanceCssClass . "_errors'></div></div>"; | |
| 116 | - | |
| 117 | - do_action('fluentform_after_form_render', $form); | |
| 118 | - | |
| 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 | + | |
| 119 | 221 | return ob_get_clean(); |
| 120 | 222 | } |
| 121 | 223 | |
| 122 | - | |
| 224 | + /** | |
| 225 | + * @param \stdClass $form | |
| 226 | + * | |
| 227 | + * @return string form body | |
| 228 | + */ | |
| 123 | 229 | public function buildFormBody($form) |
| 124 | 230 | { |
| 125 | 231 | $hasStepWrapper = isset($form->fields['stepsWrapper']) && $form->fields['stepsWrapper']; |
| 126 | - | |
| 232 | + | |
| 127 | 233 | ob_start(); |
| 128 | - | |
| 234 | + | |
| 129 | 235 | $stepCounter = 1; |
| 130 | 236 | |
| 131 | 237 | foreach ($form->fields['fields'] as $item) { |
| 132 | - | |
| 133 | - if ($hasStepWrapper && $item['element'] == 'form_step') { | |
| 238 | + if ($hasStepWrapper && 'form_step' == $item['element']) { | |
| 134 | 239 | $stepCounter++; |
| 135 | 240 | } |
| 136 | 241 | |
| 137 | 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); | |
| 138 | 255 | |
| 139 | - $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 | + ); | |
| 140 | 266 | |
| 141 | - do_action('fluentform_render_item_' . $item['element'], $item, $form); | |
| 267 | + do_action('fluentform/render_item_' . $item['element'], $item, $form); | |
| 142 | 268 | |
| 143 | 269 | $this->extractValidationRules($item); |
| 144 | 270 | |
| 145 | 271 | $this->extractConditionalLogic($item); |
| @@ -145,11 +271,31 @@ | ||
| 145 | 271 | $this->extractConditionalLogic($item); |
| 146 | 272 | } |
| 147 | 273 | |
| 148 | 274 | if ($hasStepWrapper) { |
| 149 | - 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); | |
| 150 | 286 | } else { |
| 151 | - 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); | |
| 152 | 298 | } |
| 153 | 299 | |
| 154 | 300 | $content = ob_get_clean(); |
| 155 | 301 | |
| @@ -154,11 +300,11 @@ | ||
| 154 | 300 | $content = ob_get_clean(); |
| 155 | 301 | |
| 156 | 302 | if ($hasStepWrapper) { |
| 157 | 303 | $startElement = $form->fields['stepsWrapper']['stepStart']; |
| 158 | - | |
| 304 | + | |
| 159 | 305 | $steps = ArrayHelper::get($startElement, 'settings.step_titles'); |
| 160 | - | |
| 306 | + | |
| 161 | 307 | // check if $stepCounter == count() |
| 162 | 308 | if ($stepCounter > count($steps)) { |
| 163 | 309 | $fillCount = $stepCounter - count($steps); |
| 164 | 310 | foreach (range(1, $fillCount) as $item) { |
| @@ -167,16 +313,26 @@ | ||
| 167 | 313 | $startElement['settings']['step_titles'] = $steps; |
| 168 | 314 | } |
| 169 | 315 | |
| 170 | 316 | $this->setUniqueIdentifier($startElement); |
| 171 | - | |
| 317 | + | |
| 172 | 318 | ob_start(); |
| 173 | - | |
| 174 | - do_action('fluentform_render_item_step_start', $startElement, $form); | |
| 175 | - | |
| 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 | + | |
| 176 | 332 | $stepStatrt = ob_get_clean(); |
| 177 | - | |
| 178 | - $content = $stepStatrt.$content; | |
| 333 | + | |
| 334 | + $content = $stepStatrt . $content; | |
| 179 | 335 | } |
| 180 | 336 | |
| 181 | 337 | return $content; |
| 182 | 338 | } |
| @@ -182,15 +338,17 @@ | ||
| 182 | 338 | } |
| 183 | 339 | |
| 184 | 340 | /** |
| 185 | 341 | * Set unique name/data-name for an element |
| 342 | + * | |
| 186 | 343 | * @param array &$item |
| 187 | - * @return void | |
| 344 | + * | |
| 345 | + * @return void | |
| 188 | 346 | */ |
| 189 | 347 | protected function setUniqueIdentifier(&$item) |
| 190 | 348 | { |
| 191 | 349 | if (isset($item['columns'])) { |
| 192 | - $item['attributes']['data-name'] = 'ff_cn_id_'.$this->containerCounter; | |
| 350 | + $item['attributes']['data-name'] = 'ff_cn_id_' . $this->containerCounter; | |
| 193 | 351 | $this->containerCounter++; |
| 194 | 352 | foreach ($item['columns'] as &$column) { |
| 195 | 353 | foreach ($column['fields'] as &$field) { |
| 196 | 354 | $this->setUniqueIdentifier($field); |
| @@ -196,18 +354,18 @@ | ||
| 196 | 354 | $this->setUniqueIdentifier($field); |
| 197 | 355 | } |
| 198 | 356 | } |
| 199 | 357 | } else { |
| 200 | - if (!isset($item['attributes']['name'])) { | |
| 201 | - if($this->form) { | |
| 202 | - if(empty($this->form->attr_name_index)) { | |
| 358 | + if (! isset($item['attributes']['name'])) { | |
| 359 | + if ($this->form) { | |
| 360 | + if (empty($this->form->attr_name_index)) { | |
| 203 | 361 | $this->form->attr_name_index = 1; |
| 204 | 362 | } else { |
| 205 | 363 | $this->form->attr_name_index += 1; |
| 206 | 364 | } |
| 207 | - $uniqueId = $this->form->id.'_'.$this->form->attr_name_index; | |
| 365 | + $uniqueId = $this->form->id . '_' . $this->form->attr_name_index; | |
| 208 | 366 | } else { |
| 209 | - $uniqueId = uniqid(rand(), true); | |
| 367 | + $uniqueId = uniqid(wp_rand(), true); | |
| 210 | 368 | } |
| 211 | 369 | |
| 212 | 370 | $item['attributes']['name'] = $item['element'] . '-' . $uniqueId; |
| 213 | 371 | } |
| @@ -217,14 +375,16 @@ | ||
| 217 | 375 | } |
| 218 | 376 | |
| 219 | 377 | /** |
| 220 | 378 | * Recursively extract validation rules from a given element |
| 379 | + * | |
| 221 | 380 | * @param array $item |
| 381 | + * | |
| 222 | 382 | * @return void |
| 223 | 383 | */ |
| 224 | 384 | protected function extractValidationRules($item) |
| 225 | 385 | { |
| 226 | - if (isset($item['columns'])) { | |
| 386 | + if (isset($item['columns']) && 'repeater_container' !== $item['element']) { | |
| 227 | 387 | foreach ($item['columns'] as $column) { |
| 228 | 388 | foreach ($column['fields'] as $field) { |
| 229 | 389 | $this->extractValidationRules($field); |
| 230 | 390 | } |
| @@ -231,14 +391,14 @@ | ||
| 231 | 391 | } |
| 232 | 392 | } elseif (isset($item['fields'])) { |
| 233 | 393 | $rootName = $item['attributes']['name']; |
| 234 | 394 | foreach ($item['fields'] as $key => $innerItem) { |
| 235 | - if ($item['element'] == 'address' || $item['element'] == 'input_name') { | |
| 395 | + if ('address' == $item['element'] || 'input_name' == $item['element']) { | |
| 236 | 396 | $itemName = $innerItem['attributes']['name']; |
| 237 | 397 | $innerItem['attributes']['name'] = $rootName . '[' . $itemName . ']'; |
| 238 | 398 | } else { |
| 239 | - if ($item['element'] == 'input_repeat' || $item['element'] == 'repeater_field') { | |
| 240 | - 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'])) { | |
| 241 | 401 | unset($innerItem['settings']['validation_rules']['email']); |
| 242 | 402 | } |
| 243 | 403 | $innerItem['attributes']['name'] = $rootName . '[' . $key . ']'; |
| 244 | 404 | } else { |
| @@ -246,14 +406,15 @@ | ||
| 246 | 406 | } |
| 247 | 407 | } |
| 248 | 408 | $this->extractValidationRule($innerItem); |
| 249 | 409 | } |
| 250 | - } elseif ($item['element'] == 'tabular_grid') { | |
| 410 | + } elseif ('tabular_grid' == $item['element']) { | |
| 251 | 411 | $gridName = $item['attributes']['name']; |
| 252 | 412 | $gridRows = $item['settings']['grid_rows']; |
| 253 | 413 | $gridType = $item['settings']['tabular_field_type']; |
| 254 | 414 | foreach ($gridRows as $rowKey => $rowValue) { |
| 255 | - if ($gridType == 'radio') { | |
| 415 | + $rowKey = trim(sanitize_text_field($rowKey)); | |
| 416 | + if ('radio' == $gridType) { | |
| 256 | 417 | $item['attributes']['name'] = $gridName . '[' . $rowKey . ']'; |
| 257 | 418 | $this->extractValidationRule($item); |
| 258 | 419 | } else { |
| 259 | 420 | $item['attributes']['name'] = $gridName . '[' . $rowKey . ']'; |
| @@ -259,14 +420,26 @@ | ||
| 259 | 420 | $item['attributes']['name'] = $gridName . '[' . $rowKey . ']'; |
| 260 | 421 | $this->extractValidationRule($item); |
| 261 | 422 | } |
| 262 | 423 | } |
| 263 | - } elseif ($item['element'] == 'chained_select') { | |
| 424 | + } elseif ('chained_select' == $item['element']) { | |
| 264 | 425 | $chainedSelectName = $item['attributes']['name']; |
| 265 | 426 | foreach ($item['settings']['data_source']['headers'] as $select) { |
| 266 | 427 | $item['attributes']['name'] = $chainedSelectName . '[' . $select . ']'; |
| 267 | 428 | $this->extractValidationRule($item); |
| 268 | 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 | + } | |
| 269 | 442 | } else { |
| 270 | 443 | $this->extractValidationRule($item); |
| 271 | 444 | } |
| 272 | 445 | } |
| @@ -272,32 +445,77 @@ | ||
| 272 | 445 | } |
| 273 | 446 | |
| 274 | 447 | /** |
| 275 | 448 | * Extract validation rules from a given element |
| 449 | + * | |
| 276 | 450 | * @param array $item |
| 451 | + * | |
| 277 | 452 | * @return void |
| 278 | 453 | */ |
| 279 | - protected function extractValidationRule($item) | |
| 454 | + protected function extractValidationRule($item, $name = '') | |
| 280 | 455 | { |
| 281 | 456 | if (isset($item['settings']['validation_rules'])) { |
| 282 | 457 | $rules = $item['settings']['validation_rules']; |
| 283 | 458 | foreach ($rules as $ruleName => $rule) { |
| 284 | - if(isset($rule['message'])) { | |
| 285 | - $rules[$ruleName]['message'] = apply_filters('fluentform_validation_message_'.$ruleName, $rule['message'], $item); | |
| 286 | - $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); | |
| 287 | 488 | } |
| 288 | 489 | } |
| 289 | - $rules = apply_filters('fluentform_item_rules_' . $item['element'], $rules, $item); | |
| 290 | - $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; | |
| 291 | 507 | } |
| 292 | 508 | } |
| 293 | 509 | |
| 294 | 510 | /** |
| 295 | - * Extract conditipnal logic from a given element | |
| 511 | + * Extract conditional logic from a given element | |
| 512 | + * | |
| 296 | 513 | * @param array $item |
| 514 | + * | |
| 297 | 515 | * @return void |
| 298 | 516 | */ |
| 299 | - protected function extractConditionalLogic($item) | |
| 517 | + protected function extractConditionalLogic($item, $itemName = '') | |
| 300 | 518 | { |
| 301 | 519 | // If container like element, then recurse |
| 302 | 520 | if (isset($item['columns'])) { |
| 303 | 521 | $containerConditions = false; |
| @@ -309,33 +527,43 @@ | ||
| 309 | 527 | $this->conditions[$item['attributes']['data-name']] = $containerConditions; |
| 310 | 528 | } |
| 311 | 529 | } |
| 312 | 530 | } |
| 531 | + $parentName = ArrayHelper::get($item, 'attributes.name'); | |
| 532 | + $isRepeaterContainer = ArrayHelper::get($item, 'element') === 'repeater_container'; | |
| 313 | 533 | |
| 314 | - foreach ($item['columns'] as $column) { | |
| 315 | - foreach ($column['fields'] as $field) { | |
| 316 | - if($containerConditions) { | |
| 534 | + foreach ($item['columns'] as $colIndex => $column) { | |
| 535 | + foreach ($column['fields'] as $fieldIndex => $field) { | |
| 536 | + if ($containerConditions) { | |
| 317 | 537 | $field['container_conditions'] = $containerConditions; |
| 318 | 538 | } |
| 319 | - $this->extractConditionalLogic($field); | |
| 539 | + if ($isRepeaterContainer) { | |
| 540 | + $conditionName = $parentName . '_condition_' . $colIndex . '_' . $fieldIndex; | |
| 541 | + $this->extractConditionalLogic($field, $conditionName); | |
| 542 | + } else { | |
| 543 | + $this->extractConditionalLogic($field); | |
| 544 | + } | |
| 320 | 545 | } |
| 321 | 546 | } |
| 322 | 547 | } elseif (isset($item['settings']['conditional_logics'])) { |
| 323 | 548 | $conditionals = $item['settings']['conditional_logics']; |
| 549 | + if (!$itemName) { | |
| 550 | + $itemName = $item['attributes']['data-name']; | |
| 551 | + } | |
| 324 | 552 | if (isset($conditionals['status'])) { |
| 325 | 553 | if ($conditionals['status'] && $conditionals['conditions']) { |
| 326 | - $this->conditions[$item['attributes']['data-name']] = $item['settings']['conditional_logics']; | |
| 554 | + $this->conditions[$itemName] = $item['settings']['conditional_logics']; | |
| 327 | 555 | } |
| 328 | 556 | } |
| 329 | - if(isset($item['container_conditions'])) { | |
| 330 | - if(!isset($this->conditions[$item['attributes']['data-name']])) { | |
| 331 | - $this->conditions[$item['attributes']['data-name']] = [ | |
| 557 | + if (isset($item['container_conditions'])) { | |
| 558 | + if (! isset($this->conditions[$item['attributes']['data-name']])) { | |
| 559 | + $this->conditions[$itemName] = [ | |
| 332 | 560 | 'conditions' => [], |
| 333 | - 'status' => false, | |
| 334 | - 'type' => 'any' | |
| 561 | + 'status' => false, | |
| 562 | + 'type' => 'any', | |
| 335 | 563 | ]; |
| 336 | 564 | } |
| 337 | - $this->conditions[$item['attributes']['data-name']]['container_condition'] = $item['container_conditions']; | |
| 565 | + $this->conditions[$itemName]['container_condition'] = $item['container_conditions']; | |
| 338 | 566 | } |
| 339 | 567 | } |
| 340 | 568 | } |
| 341 | 569 | |
| @@ -340,9 +568,11 @@ | ||
| 340 | 568 | } |
| 341 | 569 | |
| 342 | 570 | /** |
| 343 | 571 | * Build attributes for any html element |
| 344 | - * @param array $attributes | |
| 572 | + * | |
| 573 | + * @param array $attributes | |
| 574 | + * | |
| 345 | 575 | * @return string [Compiled key='value' attributes] |
| 346 | 576 | */ |
| 347 | 577 | protected function buildAttributes($attributes, $form = null) |
| 348 | 578 | { |
| @@ -347,12 +577,32 @@ | ||
| 347 | 577 | protected function buildAttributes($attributes, $form = null) |
| 348 | 578 | { |
| 349 | 579 | $atts = ''; |
| 350 | 580 | foreach ($attributes as $key => $value) { |
| 351 | - 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) { | |
| 352 | 587 | $value = htmlspecialchars($value); |
| 353 | - $atts .= $key.'="'.$value.'" '; | |
| 588 | + $atts .= esc_attr($key) . '="' . $value . '" '; | |
| 354 | 589 | } |
| 355 | 590 | } |
| 356 | 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>'; | |
| 357 | 607 | } |
| 358 | 608 | } |