| @@ -2,8 +2,9 @@ | ||
| 2 | 2 | /** |
| 3 | 3 | * Combo field - Field contains sub fields |
| 4 | 4 | * |
| 5 | 5 | * @package Formidable |
| 6 | + * | |
| 6 | 7 | * @since 4.11 |
| 7 | 8 | */ |
| 8 | 9 | |
| 9 | 10 | if ( ! defined( 'ABSPATH' ) ) { |
| @@ -15,8 +16,9 @@ | ||
| 15 | 16 | /** |
| 16 | 17 | * Does the html for this field label need to include "for"? |
| 17 | 18 | * |
| 18 | 19 | * @var bool |
| 20 | + * | |
| 19 | 21 | * @since 3.0 |
| 20 | 22 | */ |
| 21 | 23 | protected $has_for_label = false; |
| 22 | 24 | |
| @@ -53,9 +55,9 @@ | ||
| 53 | 55 | protected function register_sub_fields( array $sub_fields ) { |
| 54 | 56 | $defaults = $this->get_default_sub_field(); |
| 55 | 57 | |
| 56 | 58 | foreach ( $sub_fields as $name => $sub_field ) { |
| 57 | - if ( empty( $sub_field ) ) { | |
| 59 | + if ( ! $sub_field ) { | |
| 58 | 60 | continue; |
| 59 | 61 | } |
| 60 | 62 | |
| 61 | 63 | if ( is_array( $sub_field ) ) { |
| @@ -73,8 +75,24 @@ | ||
| 73 | 75 | ), |
| 74 | 76 | $defaults |
| 75 | 77 | ); |
| 76 | 78 | } |
| 79 | + }//end foreach | |
| 80 | + } | |
| 81 | + | |
| 82 | + /** | |
| 83 | + * Set the aria-invalid attribute for subfields. | |
| 84 | + * | |
| 85 | + * @since 6.16.3 | |
| 86 | + * | |
| 87 | + * @param array $shortcode_atts | |
| 88 | + * @param array $args | |
| 89 | + * | |
| 90 | + * @return void | |
| 91 | + */ | |
| 92 | + public function set_aria_invalid_error( &$shortcode_atts, $args ) { | |
| 93 | + foreach ( $this->get_sub_fields() as $sub_field ) { | |
| 94 | + $shortcode_atts[ 'aria-invalid-' . $sub_field['name'] ] = isset( $args['errors'][ 'field' . $this->field_id . '-' . $sub_field['name'] ] ) ? 'true' : 'false'; | |
| 77 | 95 | } |
| 78 | 96 | } |
| 79 | 97 | |
| 80 | 98 | /** |
| @@ -112,9 +130,10 @@ | ||
| 112 | 130 | continue; |
| 113 | 131 | } |
| 114 | 132 | |
| 115 | 133 | foreach ( $sub_field['options'] as $option ) { |
| 116 | - if ( 'default_value' === $option ) { // We parse default value from field column. | |
| 134 | + if ( 'default_value' === $option ) { | |
| 135 | + // We parse default value from field column. | |
| 117 | 136 | continue; |
| 118 | 137 | } |
| 119 | 138 | |
| 120 | 139 | if ( is_string( $option ) ) { |
| @@ -145,8 +164,9 @@ | ||
| 145 | 164 | |
| 146 | 165 | foreach ( $this->sub_fields as $name => $sub_field ) { |
| 147 | 166 | $sub_field['name'] = $name; |
| 148 | 167 | $wrapper_classes = 'frm_grid_container frm_sub_field_options frm_sub_field_options-' . $sub_field['name']; |
| 168 | + | |
| 149 | 169 | if ( ! isset( $processed_sub_fields[ $name ] ) ) { |
| 150 | 170 | // Options for this subfield should be hidden. |
| 151 | 171 | $wrapper_classes .= ' frm_hidden'; |
| 152 | 172 | } |
| @@ -181,9 +201,10 @@ | ||
| 181 | 201 | |
| 182 | 202 | return $default_value; |
| 183 | 203 | } |
| 184 | 204 | |
| 185 | - return json_decode( $default_value, true ); // We store default value as JSON string in db. | |
| 205 | + // We store default value as JSON string in db. | |
| 206 | + return json_decode( $default_value, true ); | |
| 186 | 207 | } |
| 187 | 208 | |
| 188 | 209 | /** |
| 189 | 210 | * Gets labels for built-in options of fields or sub fields. |
| @@ -203,17 +224,16 @@ | ||
| 203 | 224 | * |
| 204 | 225 | * @return array |
| 205 | 226 | */ |
| 206 | 227 | protected function field_settings_for_type() { |
| 207 | - $settings = array( | |
| 228 | + return array( | |
| 208 | 229 | 'description' => false, |
| 209 | 230 | 'default' => false, |
| 210 | - 'clear_on_focus' => false, // Don't use the regular placeholder option. | |
| 231 | + // Don't use the regular placeholder option. | |
| 232 | + 'clear_on_focus' => false, | |
| 211 | 233 | 'logic' => true, |
| 212 | 234 | 'visibility' => true, |
| 213 | 235 | ); |
| 214 | - | |
| 215 | - return $settings; | |
| 216 | 236 | } |
| 217 | 237 | |
| 218 | 238 | /** |
| 219 | 239 | * Shows field on the form builder. |
| @@ -247,8 +267,9 @@ | ||
| 247 | 267 | * Shows field in the frontend. |
| 248 | 268 | * |
| 249 | 269 | * @param array $args Arguments. |
| 250 | 270 | * @param array $shortcode_atts Shortcode attributes. |
| 271 | + * | |
| 251 | 272 | * @return string |
| 252 | 273 | */ |
| 253 | 274 | public function front_field_input( $args, $shortcode_atts ) { |
| 254 | 275 | $field = (array) $this->field; |
| @@ -253,8 +274,9 @@ | ||
| 253 | 274 | public function front_field_input( $args, $shortcode_atts ) { |
| 254 | 275 | $field = (array) $this->field; |
| 255 | 276 | |
| 256 | 277 | $field['default_value'] = $this->get_default_value(); |
| 278 | + | |
| 257 | 279 | if ( empty( $field['value'] ) ) { |
| 258 | 280 | $field['value'] = $field['default_value']; |
| 259 | 281 | } |
| 260 | 282 | |
| @@ -262,11 +284,10 @@ | ||
| 262 | 284 | $args['shortcode_atts'] = $shortcode_atts; |
| 263 | 285 | |
| 264 | 286 | ob_start(); |
| 265 | 287 | $this->load_field_output( $args ); |
| 266 | - $input_html = ob_get_clean(); | |
| 267 | 288 | |
| 268 | - return $input_html; | |
| 289 | + return ob_get_clean(); | |
| 269 | 290 | } |
| 270 | 291 | |
| 271 | 292 | /** |
| 272 | 293 | * Loads field output. |
| @@ -355,8 +376,9 @@ | ||
| 355 | 376 | |
| 356 | 377 | // Placeholder. |
| 357 | 378 | if ( in_array( 'placeholder', $sub_field['options'], true ) ) { |
| 358 | 379 | $placeholders = FrmField::get_option( $field, 'placeholder' ); |
| 380 | + | |
| 359 | 381 | if ( ! empty( $placeholders[ $sub_field['name'] ] ) ) { |
| 360 | 382 | $field['placeholder'] = $placeholders[ $sub_field['name'] ]; |
| 361 | 383 | } |
| 362 | 384 | } |
| @@ -361,9 +383,10 @@ | ||
| 361 | 383 | } |
| 362 | 384 | } |
| 363 | 385 | |
| 364 | 386 | // Add optional class. |
| 365 | - $classes = isset( $sub_field['classes'] ) ? $sub_field['classes'] : ''; | |
| 387 | + $classes = $sub_field['classes'] ?? ''; | |
| 388 | + | |
| 366 | 389 | if ( is_array( $classes ) ) { |
| 367 | 390 | $classes = implode( ' ', $classes ); |
| 368 | 391 | } |
| 369 | 392 | |
| @@ -374,18 +397,25 @@ | ||
| 374 | 397 | if ( $classes ) { |
| 375 | 398 | $field['input_class'] = esc_attr( $classes ); |
| 376 | 399 | } |
| 377 | 400 | |
| 378 | - $field['default_value'] = ''; // fake it to avoid printing frm-val attribute. | |
| 401 | + // Fake it to avoid printing frm-val attribute. | |
| 402 | + $field['default_value'] = ''; | |
| 379 | 403 | |
| 404 | + if ( ! empty( $sub_field['name'] ) ) { | |
| 405 | + $field['subfield_name'] = $sub_field['name']; | |
| 406 | + } | |
| 407 | + | |
| 380 | 408 | do_action( 'frm_field_input_html', $field ); |
| 381 | 409 | |
| 382 | 410 | // Print custom attributes. |
| 383 | - if ( ! empty( $sub_field['atts'] ) && is_array( $sub_field['atts'] ) ) { | |
| 384 | - foreach ( $sub_field['atts'] as $att_name => $att_value ) { | |
| 385 | - echo esc_attr( trim( $att_name ) ) . '="' . esc_attr( trim( $att_value ) ) . '" '; | |
| 386 | - } | |
| 411 | + if ( empty( $sub_field['atts'] ) || ! is_array( $sub_field['atts'] ) ) { | |
| 412 | + return; | |
| 387 | 413 | } |
| 414 | + | |
| 415 | + foreach ( $sub_field['atts'] as $att_name => $att_value ) { | |
| 416 | + echo esc_attr( trim( $att_name ) ) . '="' . esc_attr( trim( $att_value ) ) . '" '; | |
| 417 | + } | |
| 388 | 418 | } |
| 389 | 419 | |
| 390 | 420 | /** |
| 391 | 421 | * Validate field. |
| @@ -390,12 +420,13 @@ | ||
| 390 | 420 | /** |
| 391 | 421 | * Validate field. |
| 392 | 422 | * |
| 393 | 423 | * @param array $args Arguments. Includes `errors`, `value`. |
| 424 | + * | |
| 394 | 425 | * @return array Errors array. |
| 395 | 426 | */ |
| 396 | 427 | public function validate( $args ) { |
| 397 | - $errors = isset( $args['errors'] ) ? $args['errors'] : array(); | |
| 428 | + $errors = $args['errors'] ?? array(); | |
| 398 | 429 | |
| 399 | 430 | if ( ! $this->field->required ) { |
| 400 | 431 | return $errors; |
| 401 | 432 | } |
| @@ -403,22 +434,19 @@ | ||
| 403 | 434 | if ( class_exists( 'FrmProEntryMeta' ) && FrmProEntryMeta::skip_required_validation( $this->field ) ) { |
| 404 | 435 | return $errors; |
| 405 | 436 | } |
| 406 | 437 | |
| 407 | - if ( class_exists( 'FrmProFieldsHelper' ) && ! FrmProFieldsHelper::is_field_visible_to_user( $this->field ) ) { | |
| 408 | - return $errors; | |
| 409 | - } | |
| 410 | - | |
| 411 | - $blank_msg = FrmFieldsHelper::get_error_msg( $this->field, 'blank' ); | |
| 412 | - | |
| 438 | + $blank_msg = FrmFieldsHelper::get_error_msg( $this->field, 'blank' ); | |
| 413 | 439 | $sub_fields = $this->get_processed_sub_fields(); |
| 414 | 440 | |
| 415 | 441 | // Validate not empty. |
| 416 | 442 | foreach ( $sub_fields as $name => $sub_field ) { |
| 417 | - if ( empty( $sub_field['optional'] ) && empty( $args['value'][ $name ] ) ) { | |
| 418 | - $errors[ 'field' . $args['id'] . '-' . $name ] = ''; | |
| 419 | - $errors[ 'field' . $args['id'] ] = $blank_msg; | |
| 443 | + if ( ! empty( $sub_field['optional'] ) || ! empty( $args['value'][ $name ] ) ) { | |
| 444 | + continue; | |
| 420 | 445 | } |
| 446 | + | |
| 447 | + $errors[ 'field' . $args['id'] . '-' . $name ] = ''; | |
| 448 | + $errors[ 'field' . $args['id'] ] = $blank_msg; | |
| 421 | 449 | } |
| 422 | 450 | |
| 423 | 451 | return $errors; |
| 424 | 452 | } |
| @@ -429,14 +457,15 @@ | ||
| 429 | 457 | * @return array |
| 430 | 458 | */ |
| 431 | 459 | public function get_export_headings() { |
| 432 | 460 | $headings = array(); |
| 433 | - $field_id = isset( $this->field->id ) ? $this->field->id : $this->field['id']; | |
| 434 | - $field_name = isset( $this->field->name ) ? $this->field->name : $this->field['name']; | |
| 435 | - $field_key = isset( $this->field->field_key ) ? $this->field->field_key : $this->field['field_key']; | |
| 461 | + $field_id = is_object( $this->field ) ? $this->field->id : $this->field['id']; | |
| 462 | + $field_name = is_object( $this->field ) ? $this->field->name : $this->field['name']; | |
| 436 | 463 | $sub_fields = $this->get_processed_sub_fields(); |
| 464 | + | |
| 465 | + // Include sub-field headings. | |
| 437 | 466 | foreach ( $sub_fields as $name => $sub_field ) { |
| 438 | - $headings[ $field_id . '_' . $name ] = $field_name . ' (' . $field_key . ') - ' . $sub_field['label']; | |
| 467 | + $headings[ $field_id . '_' . $name ] = $field_name . ' - ' . $sub_field['label']; | |
| 439 | 468 | } |
| 440 | 469 | |
| 441 | 470 | return $headings; |
| 442 | 471 | } |
| @@ -441,9 +470,8 @@ | ||
| 441 | 470 | return $headings; |
| 442 | 471 | } |
| 443 | 472 | |
| 444 | 473 | /** |
| 445 | - * | |
| 446 | 474 | * Get a list of all field settings that should be translated |
| 447 | 475 | * on a multilingual site. |
| 448 | 476 | * |
| 449 | 477 | * @since 3.06.01 |
| @@ -475,11 +503,40 @@ | ||
| 475 | 503 | * Gets inputs container attributes. |
| 476 | 504 | * |
| 477 | 505 | * @return array |
| 478 | 506 | */ |
| 479 | - protected function get_inputs_container_attrs() { | |
| 507 | + public function get_inputs_container_attrs() { | |
| 480 | 508 | return array( |
| 481 | 509 | 'class' => 'frm_combo_inputs_container', |
| 482 | 510 | 'id' => 'frm_combo_inputs_container_' . $this->field_id, |
| 483 | 511 | ); |
| 512 | + } | |
| 513 | + | |
| 514 | + /** | |
| 515 | + * Gets subfield input attributes. | |
| 516 | + * | |
| 517 | + * @since 6.26 | |
| 518 | + * | |
| 519 | + * @param array $sub_field Subfield data. | |
| 520 | + * @param array $args Field output args. See {@see FrmFieldCombo::load_field_output()}. | |
| 521 | + * | |
| 522 | + * @return array | |
| 523 | + */ | |
| 524 | + protected function get_sub_field_input_attrs( $sub_field, $args ) { | |
| 525 | + $attrs = array( | |
| 526 | + 'type' => $sub_field['type'], | |
| 527 | + 'id' => $args['html_id'] . '_' . $sub_field['name'], | |
| 528 | + 'value' => '', | |
| 529 | + ); | |
| 530 | + | |
| 531 | + if ( ! empty( $args['field']['value'][ $sub_field['name'] ] ) ) { | |
| 532 | + $attrs['value'] = $args['field']['value'][ $sub_field['name'] ]; | |
| 533 | + $attrs['data-frmval'] = $args['field']['value'][ $sub_field['name'] ]; | |
| 534 | + } | |
| 535 | + | |
| 536 | + if ( empty( $args['remove_names'] ) ) { | |
| 537 | + $attrs['name'] = $args['field_name'] . '[' . $sub_field['name'] . ']'; | |
| 538 | + } | |
| 539 | + | |
| 540 | + return $attrs; | |
| 484 | 541 | } |
| 485 | 542 | } |