| @@ -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 ) ) { |
| @@ -162,8 +164,9 @@ | ||
| 162 | 164 | |
| 163 | 165 | foreach ( $this->sub_fields as $name => $sub_field ) { |
| 164 | 166 | $sub_field['name'] = $name; |
| 165 | 167 | $wrapper_classes = 'frm_grid_container frm_sub_field_options frm_sub_field_options-' . $sub_field['name']; |
| 168 | + | |
| 166 | 169 | if ( ! isset( $processed_sub_fields[ $name ] ) ) { |
| 167 | 170 | // Options for this subfield should be hidden. |
| 168 | 171 | $wrapper_classes .= ' frm_hidden'; |
| 169 | 172 | } |
| @@ -221,9 +224,9 @@ | ||
| 221 | 224 | * |
| 222 | 225 | * @return array |
| 223 | 226 | */ |
| 224 | 227 | protected function field_settings_for_type() { |
| 225 | - $settings = array( | |
| 228 | + return array( | |
| 226 | 229 | 'description' => false, |
| 227 | 230 | 'default' => false, |
| 228 | 231 | // Don't use the regular placeholder option. |
| 229 | 232 | 'clear_on_focus' => false, |
| @@ -229,10 +232,8 @@ | ||
| 229 | 232 | 'clear_on_focus' => false, |
| 230 | 233 | 'logic' => true, |
| 231 | 234 | 'visibility' => true, |
| 232 | 235 | ); |
| 233 | - | |
| 234 | - return $settings; | |
| 235 | 236 | } |
| 236 | 237 | |
| 237 | 238 | /** |
| 238 | 239 | * Shows field on the form builder. |
| @@ -266,8 +267,9 @@ | ||
| 266 | 267 | * Shows field in the frontend. |
| 267 | 268 | * |
| 268 | 269 | * @param array $args Arguments. |
| 269 | 270 | * @param array $shortcode_atts Shortcode attributes. |
| 271 | + * | |
| 270 | 272 | * @return string |
| 271 | 273 | */ |
| 272 | 274 | public function front_field_input( $args, $shortcode_atts ) { |
| 273 | 275 | $field = (array) $this->field; |
| @@ -272,8 +274,9 @@ | ||
| 272 | 274 | public function front_field_input( $args, $shortcode_atts ) { |
| 273 | 275 | $field = (array) $this->field; |
| 274 | 276 | |
| 275 | 277 | $field['default_value'] = $this->get_default_value(); |
| 278 | + | |
| 276 | 279 | if ( empty( $field['value'] ) ) { |
| 277 | 280 | $field['value'] = $field['default_value']; |
| 278 | 281 | } |
| 279 | 282 | |
| @@ -281,11 +284,10 @@ | ||
| 281 | 284 | $args['shortcode_atts'] = $shortcode_atts; |
| 282 | 285 | |
| 283 | 286 | ob_start(); |
| 284 | 287 | $this->load_field_output( $args ); |
| 285 | - $input_html = ob_get_clean(); | |
| 286 | 288 | |
| 287 | - return $input_html; | |
| 289 | + return ob_get_clean(); | |
| 288 | 290 | } |
| 289 | 291 | |
| 290 | 292 | /** |
| 291 | 293 | * Loads field output. |
| @@ -374,8 +376,9 @@ | ||
| 374 | 376 | |
| 375 | 377 | // Placeholder. |
| 376 | 378 | if ( in_array( 'placeholder', $sub_field['options'], true ) ) { |
| 377 | 379 | $placeholders = FrmField::get_option( $field, 'placeholder' ); |
| 380 | + | |
| 378 | 381 | if ( ! empty( $placeholders[ $sub_field['name'] ] ) ) { |
| 379 | 382 | $field['placeholder'] = $placeholders[ $sub_field['name'] ]; |
| 380 | 383 | } |
| 381 | 384 | } |
| @@ -381,8 +384,9 @@ | ||
| 381 | 384 | } |
| 382 | 385 | |
| 383 | 386 | // Add optional class. |
| 384 | 387 | $classes = $sub_field['classes'] ?? ''; |
| 388 | + | |
| 385 | 389 | if ( is_array( $classes ) ) { |
| 386 | 390 | $classes = implode( ' ', $classes ); |
| 387 | 391 | } |
| 388 | 392 | |
| @@ -399,16 +403,19 @@ | ||
| 399 | 403 | |
| 400 | 404 | if ( ! empty( $sub_field['name'] ) ) { |
| 401 | 405 | $field['subfield_name'] = $sub_field['name']; |
| 402 | 406 | } |
| 407 | + | |
| 403 | 408 | do_action( 'frm_field_input_html', $field ); |
| 404 | 409 | |
| 405 | 410 | // Print custom attributes. |
| 406 | - if ( ! empty( $sub_field['atts'] ) && is_array( $sub_field['atts'] ) ) { | |
| 407 | - foreach ( $sub_field['atts'] as $att_name => $att_value ) { | |
| 408 | - echo esc_attr( trim( $att_name ) ) . '="' . esc_attr( trim( $att_value ) ) . '" '; | |
| 409 | - } | |
| 411 | + if ( empty( $sub_field['atts'] ) || ! is_array( $sub_field['atts'] ) ) { | |
| 412 | + return; | |
| 410 | 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 | + } | |
| 411 | 418 | } |
| 412 | 419 | |
| 413 | 420 | /** |
| 414 | 421 | * Validate field. |
| @@ -413,8 +420,9 @@ | ||
| 413 | 420 | /** |
| 414 | 421 | * Validate field. |
| 415 | 422 | * |
| 416 | 423 | * @param array $args Arguments. Includes `errors`, `value`. |
| 424 | + * | |
| 417 | 425 | * @return array Errors array. |
| 418 | 426 | */ |
| 419 | 427 | public function validate( $args ) { |
| 420 | 428 | $errors = $args['errors'] ?? array(); |
| @@ -426,22 +434,19 @@ | ||
| 426 | 434 | if ( class_exists( 'FrmProEntryMeta' ) && FrmProEntryMeta::skip_required_validation( $this->field ) ) { |
| 427 | 435 | return $errors; |
| 428 | 436 | } |
| 429 | 437 | |
| 430 | - if ( class_exists( 'FrmProFieldsHelper' ) && ! FrmProFieldsHelper::is_field_visible_to_user( $this->field ) ) { | |
| 431 | - return $errors; | |
| 432 | - } | |
| 433 | - | |
| 434 | - $blank_msg = FrmFieldsHelper::get_error_msg( $this->field, 'blank' ); | |
| 435 | - | |
| 438 | + $blank_msg = FrmFieldsHelper::get_error_msg( $this->field, 'blank' ); | |
| 436 | 439 | $sub_fields = $this->get_processed_sub_fields(); |
| 437 | 440 | |
| 438 | 441 | // Validate not empty. |
| 439 | 442 | foreach ( $sub_fields as $name => $sub_field ) { |
| 440 | - if ( empty( $sub_field['optional'] ) && empty( $args['value'][ $name ] ) ) { | |
| 441 | - $errors[ 'field' . $args['id'] . '-' . $name ] = ''; | |
| 442 | - $errors[ 'field' . $args['id'] ] = $blank_msg; | |
| 443 | + if ( ! empty( $sub_field['optional'] ) || ! empty( $args['value'][ $name ] ) ) { | |
| 444 | + continue; | |
| 443 | 445 | } |
| 446 | + | |
| 447 | + $errors[ 'field' . $args['id'] . '-' . $name ] = ''; | |
| 448 | + $errors[ 'field' . $args['id'] ] = $blank_msg; | |
| 444 | 449 | } |
| 445 | 450 | |
| 446 | 451 | return $errors; |
| 447 | 452 | } |
| @@ -452,14 +457,15 @@ | ||
| 452 | 457 | * @return array |
| 453 | 458 | */ |
| 454 | 459 | public function get_export_headings() { |
| 455 | 460 | $headings = array(); |
| 456 | - $field_id = $this->field->id ?? $this->field['id']; | |
| 457 | - $field_name = $this->field->name ?? $this->field['name']; | |
| 458 | - $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']; | |
| 459 | 463 | $sub_fields = $this->get_processed_sub_fields(); |
| 464 | + | |
| 465 | + // Include sub-field headings. | |
| 460 | 466 | foreach ( $sub_fields as $name => $sub_field ) { |
| 461 | - $headings[ $field_id . '_' . $name ] = $field_name . ' (' . $field_key . ') - ' . $sub_field['label']; | |
| 467 | + $headings[ $field_id . '_' . $name ] = $field_name . ' - ' . $sub_field['label']; | |
| 462 | 468 | } |
| 463 | 469 | |
| 464 | 470 | return $headings; |
| 465 | 471 | } |
| @@ -497,11 +503,40 @@ | ||
| 497 | 503 | * Gets inputs container attributes. |
| 498 | 504 | * |
| 499 | 505 | * @return array |
| 500 | 506 | */ |
| 501 | - protected function get_inputs_container_attrs() { | |
| 507 | + public function get_inputs_container_attrs() { | |
| 502 | 508 | return array( |
| 503 | 509 | 'class' => 'frm_combo_inputs_container', |
| 504 | 510 | 'id' => 'frm_combo_inputs_container_' . $this->field_id, |
| 505 | 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; | |
| 506 | 541 | } |
| 507 | 542 | } |