CMS Project Sync
This commit is contained in:
+4490
File diff suppressed because it is too large
Load Diff
+773
@@ -0,0 +1,773 @@
|
||||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checkbox field.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class WPForms_Field_Checkbox extends WPForms_Field {
|
||||
|
||||
/**
|
||||
* Primary class constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function init() {
|
||||
|
||||
// Define field type information.
|
||||
$this->name = esc_html__( 'Checkboxes', 'wpforms-lite' );
|
||||
$this->keywords = esc_html__( 'choice', 'wpforms-lite' );
|
||||
$this->type = 'checkbox';
|
||||
$this->icon = 'fa-check-square-o';
|
||||
$this->order = 110;
|
||||
$this->defaults = [
|
||||
1 => [
|
||||
'label' => esc_html__( 'First Choice', 'wpforms-lite' ),
|
||||
'value' => '',
|
||||
'image' => '',
|
||||
'icon' => '',
|
||||
'icon_style' => '',
|
||||
'default' => '',
|
||||
],
|
||||
2 => [
|
||||
'label' => esc_html__( 'Second Choice', 'wpforms-lite' ),
|
||||
'value' => '',
|
||||
'image' => '',
|
||||
'icon' => '',
|
||||
'icon_style' => '',
|
||||
'default' => '',
|
||||
],
|
||||
3 => [
|
||||
'label' => esc_html__( 'Third Choice', 'wpforms-lite' ),
|
||||
'value' => '',
|
||||
'image' => '',
|
||||
'icon' => '',
|
||||
'icon_style' => '',
|
||||
'default' => '',
|
||||
],
|
||||
];
|
||||
|
||||
$this->default_settings = [
|
||||
'choices' => $this->defaults,
|
||||
];
|
||||
|
||||
$this->hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks.
|
||||
*
|
||||
* @since 1.8.1
|
||||
*/
|
||||
private function hooks() {
|
||||
|
||||
// Customize HTML field values.
|
||||
add_filter( 'wpforms_html_field_value', [ $this, 'field_html_value' ], 10, 4 );
|
||||
add_filter( "wpforms_{$this->type}_field_html_value_images", [ $this, 'field_html_value_images' ], 10, 3 );
|
||||
|
||||
// Define additional field properties.
|
||||
add_filter( 'wpforms_field_properties_checkbox', [ $this, 'field_properties' ], 5, 3 );
|
||||
|
||||
// This field requires fieldset+legend instead of the field label.
|
||||
add_filter( "wpforms_frontend_modern_is_field_requires_fieldset_{$this->type}", '__return_true', PHP_INT_MAX, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Define additional field properties.
|
||||
*
|
||||
* @since 1.4.5
|
||||
*
|
||||
* @param array $properties Field properties.
|
||||
* @param array $field Field settings.
|
||||
* @param array $form_data Form data and settings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function field_properties( $properties, $field, $form_data ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded
|
||||
|
||||
// Define data.
|
||||
$form_id = absint( $form_data['id'] );
|
||||
$field_id = wpforms_validate_field_id( $field['id'] );
|
||||
$choices = $field['choices'];
|
||||
$dynamic = wpforms_get_field_dynamic_choices( $field, $form_id, $form_data );
|
||||
|
||||
if ( $dynamic !== false ) {
|
||||
$choices = $dynamic;
|
||||
$field['show_values'] = true;
|
||||
}
|
||||
|
||||
// Remove primary input, unset for attribute for label.
|
||||
unset( $properties['inputs']['primary'], $properties['label']['attr']['for'] );
|
||||
|
||||
// Set input container (ul) properties.
|
||||
$properties['input_container'] = [
|
||||
'class' => [ ! empty( $field['random'] ) ? 'wpforms-randomize' : '' ],
|
||||
'data' => [],
|
||||
'attr' => [],
|
||||
'id' => "wpforms-{$form_id}-field_{$field_id}",
|
||||
];
|
||||
|
||||
$is_choice_limit_set = ! empty( $field['choice_limit'] ) && (int) $field['choice_limit'] > 0;
|
||||
|
||||
if ( $is_choice_limit_set ) {
|
||||
$properties['input_container']['data']['choice-limit'] = $field['choice_limit'];
|
||||
}
|
||||
|
||||
// Set input properties.
|
||||
foreach ( $choices as $key => $choice ) {
|
||||
|
||||
// Used for dynamic choices.
|
||||
$depth = isset( $choice['depth'] ) ? absint( $choice['depth'] ) : 1;
|
||||
$label = isset( $choice['label'] ) ? $choice['label'] : '';
|
||||
|
||||
// Choice labels should not be left blank, but if they are we
|
||||
// provide a basic value.
|
||||
$value = isset( $field['show_values'] ) ? $choice['value'] : $label;
|
||||
|
||||
if ( '' === $value ) {
|
||||
if ( 1 === count( $choices ) ) {
|
||||
$value = esc_html__( 'Checked', 'wpforms-lite' );
|
||||
} else {
|
||||
/* translators: %s - choice number. */
|
||||
$value = sprintf( esc_html__( 'Choice %s', 'wpforms-lite' ), $key );
|
||||
}
|
||||
}
|
||||
|
||||
$properties['inputs'][ $key ] = [
|
||||
'container' => [
|
||||
'attr' => [],
|
||||
'class' => [ "choice-{$key}", "depth-{$depth}" ],
|
||||
'data' => [],
|
||||
'id' => '',
|
||||
],
|
||||
'label' => [
|
||||
'attr' => [
|
||||
'for' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
|
||||
],
|
||||
'class' => [ 'wpforms-field-label-inline' ],
|
||||
'data' => [],
|
||||
'id' => '',
|
||||
'text' => $label,
|
||||
],
|
||||
'attr' => [
|
||||
'name' => "wpforms[fields][{$field_id}][]",
|
||||
'value' => $value,
|
||||
],
|
||||
'class' => [],
|
||||
'data' => [],
|
||||
'id' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
|
||||
'icon' => isset( $choice['icon'] ) ? $choice['icon'] : '',
|
||||
'icon_style' => isset( $choice['icon_style'] ) ? $choice['icon_style'] : '',
|
||||
'image' => isset( $choice['image'] ) ? $choice['image'] : '',
|
||||
'required' => ! empty( $field['required'] ) ? 'required' : '',
|
||||
'default' => isset( $choice['default'] ),
|
||||
];
|
||||
|
||||
// Rule for validator only if needed.
|
||||
if ( $is_choice_limit_set ) {
|
||||
$properties['inputs'][ $key ]['data']['rule-check-limit'] = 'true';
|
||||
}
|
||||
}
|
||||
|
||||
// Required class for pagebreak validation.
|
||||
if ( ! empty( $field['required'] ) ) {
|
||||
$properties['input_container']['class'][] = 'wpforms-field-required';
|
||||
}
|
||||
|
||||
// Custom properties if image choices is enabled.
|
||||
if ( ! $dynamic && ! empty( $field['choices_images'] ) ) {
|
||||
|
||||
$properties['input_container']['class'][] = 'wpforms-image-choices';
|
||||
$properties['input_container']['class'][] = 'wpforms-image-choices-' . sanitize_html_class( $field['choices_images_style'] );
|
||||
|
||||
foreach ( $properties['inputs'] as $key => $inputs ) {
|
||||
$properties['inputs'][ $key ]['container']['class'][] = 'wpforms-image-choices-item';
|
||||
|
||||
if ( in_array( $field['choices_images_style'], [ 'modern', 'classic' ], true ) ) {
|
||||
$properties['inputs'][ $key ]['class'][] = 'wpforms-screen-reader-element';
|
||||
}
|
||||
}
|
||||
} elseif ( ! $dynamic && ! empty( $field['choices_icons'] ) ) {
|
||||
$properties = wpforms()->obj( 'icon_choices' )->field_properties( $properties, $field );
|
||||
}
|
||||
|
||||
// Custom properties for disclaimer format display.
|
||||
if ( ! empty( $field['disclaimer_format'] ) ) {
|
||||
|
||||
$properties['description']['class'][] = 'wpforms-disclaimer-description';
|
||||
$properties['description']['value'] = nl2br( $properties['description']['value'] );
|
||||
}
|
||||
|
||||
// Add selected class for choices with defaults.
|
||||
foreach ( $properties['inputs'] as $key => $inputs ) {
|
||||
if ( ! empty( $inputs['default'] ) ) {
|
||||
$properties['inputs'][ $key ]['container']['class'][] = 'wpforms-selected';
|
||||
}
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Field options panel inside the builder.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
*/
|
||||
public function field_options( $field ) {
|
||||
/*
|
||||
* Basic field options.
|
||||
*/
|
||||
|
||||
// Options open markup.
|
||||
$this->field_option(
|
||||
'basic-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'open',
|
||||
]
|
||||
);
|
||||
|
||||
// Label.
|
||||
$this->field_option( 'label', $field );
|
||||
|
||||
// Choices.
|
||||
$this->field_option( 'choices', $field );
|
||||
|
||||
// AI Feature.
|
||||
$this->field_option(
|
||||
'ai_modal_button',
|
||||
$field,
|
||||
[
|
||||
'value' => esc_html__( 'Generate Choices', 'wpforms-lite' ),
|
||||
'type' => 'choices',
|
||||
]
|
||||
);
|
||||
|
||||
// Choices Images.
|
||||
$this->field_option( 'choices_images', $field );
|
||||
|
||||
// Hide Choices Images.
|
||||
$this->field_option( 'choices_images_hide', $field );
|
||||
|
||||
// Choices Images Style (theme).
|
||||
$this->field_option( 'choices_images_style', $field );
|
||||
|
||||
// Choices Icons.
|
||||
$this->field_option( 'choices_icons', $field );
|
||||
|
||||
// Choices Icons Color.
|
||||
$this->field_option( 'choices_icons_color', $field );
|
||||
|
||||
// Choices Icons Size.
|
||||
$this->field_option( 'choices_icons_size', $field );
|
||||
|
||||
// Choices Icons Style.
|
||||
$this->field_option( 'choices_icons_style', $field );
|
||||
|
||||
// Description.
|
||||
$this->field_option( 'description', $field );
|
||||
|
||||
// Required toggle.
|
||||
$this->field_option( 'required', $field );
|
||||
|
||||
// Options close markup.
|
||||
$this->field_option(
|
||||
'basic-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'close',
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
* Advanced field options
|
||||
*/
|
||||
|
||||
// Options open markup.
|
||||
$this->field_option(
|
||||
'advanced-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'open',
|
||||
]
|
||||
);
|
||||
|
||||
// Randomize order of choices.
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'random',
|
||||
'content' => $this->field_element(
|
||||
'toggle',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'random',
|
||||
'value' => isset( $field['random'] ) ? '1' : '0',
|
||||
'desc' => esc_html__( 'Randomize Choices', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Check this option to randomize the order of the choices.', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
// Show Values toggle option. This option will only show if already used
|
||||
// or if manually enabled by a filter.
|
||||
if ( ! empty( $field['show_values'] ) || wpforms_show_fields_options_setting() ) {
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'show_values',
|
||||
'content' => $this->field_element(
|
||||
'toggle',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'show_values',
|
||||
'value' => isset( $field['show_values'] ) ? $field['show_values'] : '0',
|
||||
'desc' => esc_html__( 'Show Values', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Check this option to manually set form field values.', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Display format.
|
||||
$this->field_option( 'input_columns', $field );
|
||||
|
||||
// Choice Limit.
|
||||
$this->field_option( 'choice_limit', $field );
|
||||
|
||||
// Dynamic choice auto-populating toggle.
|
||||
$this->field_option( 'dynamic_choices', $field );
|
||||
|
||||
// Dynamic choice source.
|
||||
$this->field_option( 'dynamic_choices_source', $field );
|
||||
|
||||
// Custom CSS classes.
|
||||
$this->field_option( 'css', $field );
|
||||
|
||||
// Hide label.
|
||||
$this->field_option( 'label_hide', $field );
|
||||
|
||||
// Enable Disclaimer formatting.
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'disclaimer_format',
|
||||
'content' => $this->field_element(
|
||||
'toggle',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'disclaimer_format',
|
||||
'value' => isset( $field['disclaimer_format'] ) ? '1' : '0',
|
||||
'desc' => esc_html__( 'Enable Disclaimer / Terms of Service Display', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Check this option to adjust the field styling to support Disclaimers and Terms of Service type agreements.', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
// Options close markup.
|
||||
$this->field_option(
|
||||
'advanced-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'close',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Field preview inside the builder.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
*/
|
||||
public function field_preview( $field ) {
|
||||
|
||||
// Label.
|
||||
$this->field_preview_option( 'label', $field );
|
||||
|
||||
// Choices.
|
||||
$this->field_preview_option( 'choices', $field );
|
||||
|
||||
// Description.
|
||||
$this->field_preview_option(
|
||||
'description',
|
||||
$field,
|
||||
[
|
||||
'class' => ! empty( $field['disclaimer_format'] ) ? 'disclaimer nl2br' : false,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Field display on the form front-end and admin entry edit page.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
* @param array $deprecated Deprecated array.
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function field_display( $field, $deprecated, $form_data ) {
|
||||
|
||||
$using_image_choices = empty( $field['dynamic_choices'] ) && ! empty( $field['choices_images'] );
|
||||
$using_icon_choices = empty( $field['dynamic_choices'] ) && empty( $field['choices_images'] ) && ! empty( $field['choices_icons'] );
|
||||
|
||||
// Define data.
|
||||
$container = $field['properties']['input_container'];
|
||||
$choices = $field['properties']['inputs'];
|
||||
|
||||
// Do not display the field with empty choices on the frontend.
|
||||
if ( ! $choices && ! is_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Display a warning message on Entry Edit page.
|
||||
if ( ! $choices && is_admin() ) {
|
||||
$this->display_empty_dynamic_choices_message( $field );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$amp_state_id = '';
|
||||
|
||||
if ( wpforms_is_amp() && ( $using_image_choices || $using_icon_choices ) ) {
|
||||
$amp_state_id = str_replace( '-', '_', sanitize_key( $container['id'] ) ) . '_state';
|
||||
$state = [];
|
||||
|
||||
foreach ( $choices as $key => $choice ) {
|
||||
$state[ $choice['id'] ] = ! empty( $choice['default'] );
|
||||
}
|
||||
printf(
|
||||
'<amp-state id="%s"><script type="application/json">%s</script></amp-state>',
|
||||
esc_attr( $amp_state_id ),
|
||||
wp_json_encode( $state )
|
||||
);
|
||||
}
|
||||
|
||||
printf(
|
||||
'<ul %s>',
|
||||
wpforms_html_attributes( $container['id'], $container['class'], $container['data'], $container['attr'] )
|
||||
);
|
||||
|
||||
foreach ( $choices as $key => $choice ) {
|
||||
$label = $this->get_choices_label( $choice['label']['text'] ?? '', $key, $field );
|
||||
|
||||
if ( wpforms_is_amp() && ( $using_image_choices || $using_icon_choices ) ) {
|
||||
$choice['container']['attr']['[class]'] = sprintf(
|
||||
'%s + ( %s[%s] ? " wpforms-selected" : "")',
|
||||
wp_json_encode( implode( ' ', $choice['container']['class'] ) ),
|
||||
$amp_state_id,
|
||||
wp_json_encode( $choice['id'] )
|
||||
);
|
||||
}
|
||||
|
||||
// If the field is required, has the label hidden, and has
|
||||
// disclaimer mode enabled, so the required status in choice
|
||||
// label.
|
||||
$required = '';
|
||||
|
||||
if ( ! empty( $field['disclaimer_format'] ) && ! empty( $choice['required'] ) && ! empty( $field['label_hide'] ) ) {
|
||||
$required = wpforms_get_field_required_label();
|
||||
}
|
||||
|
||||
printf(
|
||||
'<li %s>',
|
||||
wpforms_html_attributes( $choice['container']['id'], $choice['container']['class'], $choice['container']['data'], $choice['container']['attr'] )
|
||||
);
|
||||
|
||||
// The required constraint in HTML5 form validation does not work with checkbox groups, so omit in AMP.
|
||||
$required_attr = wpforms_is_amp() && count( $choices ) > 1 ? '' : $choice['required'];
|
||||
|
||||
if ( $using_image_choices ) {
|
||||
|
||||
// Make sure the image choices are keyboard-accessible.
|
||||
$choice['label']['attr']['tabindex'] = 0;
|
||||
|
||||
if ( wpforms_is_amp() ) {
|
||||
$choice['label']['attr']['on'] = sprintf(
|
||||
'tap:AMP.setState({ %s: { %s: ! %s[%s] } })',
|
||||
wp_json_encode( $amp_state_id ),
|
||||
wp_json_encode( $choice['id'] ),
|
||||
$amp_state_id,
|
||||
wp_json_encode( $choice['id'] )
|
||||
);
|
||||
$choice['label']['attr']['role'] = 'button';
|
||||
}
|
||||
if ( is_array( $choice['label']['class'] ) && wpforms_is_empty_string( $label ) ) {
|
||||
$choice['label']['class'][] = 'wpforms-field-label-inline-empty';
|
||||
}
|
||||
|
||||
// Image choices.
|
||||
printf(
|
||||
'<label %s>',
|
||||
wpforms_html_attributes( $choice['label']['id'], $choice['label']['class'], $choice['label']['data'], $choice['label']['attr'] )
|
||||
);
|
||||
|
||||
echo '<span class="wpforms-image-choices-image">';
|
||||
|
||||
if ( ! empty( $choice['image'] ) ) {
|
||||
printf(
|
||||
'<img src="%s" alt="%s"%s>',
|
||||
esc_url( $choice['image'] ),
|
||||
esc_attr( $label ),
|
||||
! empty( $label ) ? ' title="' . esc_attr( $label ) . '"' : ''
|
||||
);
|
||||
}
|
||||
|
||||
echo '</span>';
|
||||
|
||||
if ( $field['choices_images_style'] === 'none' ) {
|
||||
echo '<br>';
|
||||
}
|
||||
|
||||
$choice['attr']['tabindex'] = '-1';
|
||||
|
||||
if ( wpforms_is_amp() ) {
|
||||
$choice['attr']['[checked]'] = sprintf(
|
||||
'%s[%s]',
|
||||
$amp_state_id,
|
||||
wp_json_encode( $choice['id'] )
|
||||
);
|
||||
}
|
||||
|
||||
printf(
|
||||
'<input type="checkbox" %s %s %s>',
|
||||
wpforms_html_attributes( $choice['id'], $choice['class'], $choice['data'], $choice['attr'] ),
|
||||
esc_attr( $required_attr ),
|
||||
checked( '1', $choice['default'], false )
|
||||
);
|
||||
|
||||
echo '<span class="wpforms-image-choices-label">' . wp_kses_post( $label ) . '</span>';
|
||||
|
||||
echo '</label>';
|
||||
|
||||
} elseif ( $using_icon_choices ) {
|
||||
|
||||
if ( wpforms_is_amp() ) {
|
||||
$choice['label']['attr']['on'] = sprintf(
|
||||
'tap:AMP.setState({ %s: { %s: ! %s[%s] } })',
|
||||
wp_json_encode( $amp_state_id ),
|
||||
wp_json_encode( $choice['id'] ),
|
||||
$amp_state_id,
|
||||
wp_json_encode( $choice['id'] )
|
||||
);
|
||||
$choice['label']['attr']['role'] = 'button';
|
||||
}
|
||||
|
||||
// Icon Choices.
|
||||
wpforms()->obj( 'icon_choices' )->field_display( $field, $choice, 'checkbox' );
|
||||
|
||||
} else {
|
||||
|
||||
// Normal display.
|
||||
printf(
|
||||
'<input type="checkbox" %s %s %s>',
|
||||
wpforms_html_attributes( $choice['id'], $choice['class'], $choice['data'], $choice['attr'] ),
|
||||
esc_attr( $required_attr ),
|
||||
checked( '1', $choice['default'], false )
|
||||
);
|
||||
|
||||
printf(
|
||||
'<label %s>%s%s</label>',
|
||||
wpforms_html_attributes( $choice['label']['id'], $choice['label']['class'], $choice['label']['data'], $choice['label']['attr'] ),
|
||||
wp_kses_post( $label ),
|
||||
wp_kses(
|
||||
$required,
|
||||
[
|
||||
'span' => [
|
||||
'class' => true,
|
||||
],
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
echo '</li>';
|
||||
}
|
||||
|
||||
echo '</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate field on form submit.
|
||||
*
|
||||
* @since 1.5.2
|
||||
*
|
||||
* @param int $field_id Field ID.
|
||||
* @param array $field_submit Submitted field value (raw data).
|
||||
* @param array $form_data Form data.
|
||||
*/
|
||||
public function validate( $field_id, $field_submit, $form_data ) {
|
||||
|
||||
$field_id = (int) $field_id;
|
||||
$field = $form_data['fields'][ $field_id ];
|
||||
|
||||
// Skip validation if field is dynamic and choices are empty.
|
||||
if ( $this->is_dynamic_choices_empty( $field, $form_data ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$field_submit = (array) $field_submit;
|
||||
|
||||
$this->validate_field_choice_limit( $field_id, $field_submit, $form_data );
|
||||
|
||||
// Basic required check - If field is marked as required, check for entry data.
|
||||
if (
|
||||
! empty( $form_data['fields'][ $field_id ]['required'] ) &&
|
||||
(
|
||||
empty( $field_submit ) ||
|
||||
(
|
||||
count( $field_submit ) === 1 &&
|
||||
( ! isset( $field_submit[0] ) || (string) $field_submit[0] === '' )
|
||||
)
|
||||
)
|
||||
) {
|
||||
$error = wpforms_get_required_label();
|
||||
}
|
||||
|
||||
if ( ! empty( $error ) ) {
|
||||
wpforms()->obj( 'process' )->errors[ $form_data['id'] ][ $field_id ] = $error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and sanitize field.
|
||||
*
|
||||
* @since 1.0.2
|
||||
*
|
||||
* @param int $field_id Field ID.
|
||||
* @param array $field_submit Submitted form data.
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function format( $field_id, $field_submit, $form_data ) {
|
||||
|
||||
$field_submit = (array) $field_submit;
|
||||
$field = $form_data['fields'][ $field_id ];
|
||||
$dynamic = ! empty( $field['dynamic_choices'] ) ? $field['dynamic_choices'] : false;
|
||||
$name = sanitize_text_field( $field['label'] );
|
||||
$value_raw = wpforms_sanitize_array_combine( $field_submit );
|
||||
|
||||
$data = [
|
||||
'name' => $name,
|
||||
'value' => '',
|
||||
'value_raw' => $value_raw,
|
||||
'id' => wpforms_validate_field_id( $field_id ),
|
||||
'type' => $this->type,
|
||||
];
|
||||
|
||||
if ( 'post_type' === $dynamic && ! empty( $field['dynamic_post_type'] ) ) {
|
||||
|
||||
// Dynamic population is enabled using post type.
|
||||
$value_raw = implode( ',', array_map( 'absint', $field_submit ) );
|
||||
$data['value_raw'] = $value_raw;
|
||||
$data['dynamic'] = 'post_type';
|
||||
$data['dynamic_items'] = $value_raw;
|
||||
$data['dynamic_post_type'] = $field['dynamic_post_type'];
|
||||
$posts = [];
|
||||
|
||||
foreach ( $field_submit as $id ) {
|
||||
$post = get_post( $id );
|
||||
|
||||
if ( ! is_wp_error( $post ) && ! empty( $post ) && $data['dynamic_post_type'] === $post->post_type ) {
|
||||
$posts[] = esc_html( wpforms_get_post_title( $post ) );
|
||||
}
|
||||
}
|
||||
|
||||
$data['value'] = ! empty( $posts ) ? wpforms_sanitize_array_combine( $posts ) : '';
|
||||
|
||||
}
|
||||
elseif ( 'taxonomy' === $dynamic && ! empty( $field['dynamic_taxonomy'] ) ) {
|
||||
|
||||
// Dynamic population is enabled using taxonomy.
|
||||
$value_raw = implode( ',', array_map( 'absint', $field_submit ) );
|
||||
$data['value_raw'] = $value_raw;
|
||||
$data['dynamic'] = 'taxonomy';
|
||||
$data['dynamic_items'] = $value_raw;
|
||||
$data['dynamic_taxonomy'] = $field['dynamic_taxonomy'];
|
||||
$terms = [];
|
||||
|
||||
foreach ( $field_submit as $id ) {
|
||||
$term = get_term( $id, $field['dynamic_taxonomy'] );
|
||||
|
||||
if ( ! is_wp_error( $term ) && ! empty( $term ) ) {
|
||||
$terms[] = esc_html( wpforms_get_term_name( $term ) );
|
||||
}
|
||||
}
|
||||
|
||||
$data['value'] = ! empty( $terms ) ? wpforms_sanitize_array_combine( $terms ) : '';
|
||||
|
||||
} else {
|
||||
|
||||
// Normal processing, dynamic population is off.
|
||||
$choice_keys = [];
|
||||
|
||||
// If show_values is true, that means values posted are the raw values
|
||||
// and not the labels. So we need to set label values. Also store
|
||||
// the choice keys.
|
||||
if ( ! empty( $field['show_values'] ) && (int) $field['show_values'] === 1 ) {
|
||||
|
||||
foreach ( $field_submit as $item ) {
|
||||
foreach ( $field['choices'] as $key => $choice ) {
|
||||
// Check if the submitted value is the same as the choice value or if the value is empty and the key matches.
|
||||
// Skip if the submitted value is empty.
|
||||
if ( ( ! empty( $item ) && $item === $choice['value'] ) || ( empty( $choice['value'] ) && (int) str_replace( 'Choice ', '', $item ) === $key ) ) {
|
||||
$value[] = $choice['label'];
|
||||
$choice_keys[] = $key;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data['value'] = ! empty( $value ) ? wpforms_sanitize_array_combine( $value ) : '';
|
||||
|
||||
} else {
|
||||
|
||||
$data['value'] = $value_raw;
|
||||
|
||||
// Determine choices keys, this is needed for image choices.
|
||||
foreach ( $field_submit as $item ) {
|
||||
foreach ( $field['choices'] as $key => $choice ) {
|
||||
/* translators: %s - choice number. */
|
||||
if ( $item === $choice['label'] || $item === sprintf( esc_html__( 'Choice %s', 'wpforms-lite' ), $key ) ) {
|
||||
$choice_keys[] = $key;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Images choices are enabled, lookup and store image URLs.
|
||||
if ( ! empty( $choice_keys ) && ! empty( $field['choices_images'] ) ) {
|
||||
|
||||
$data['images'] = [];
|
||||
|
||||
foreach ( $choice_keys as $key ) {
|
||||
$data['images'][] = ! empty( $field['choices'][ $key ]['image'] ) ? esc_url_raw( $field['choices'][ $key ]['image'] ) : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Push field details to be saved.
|
||||
wpforms()->obj( 'process' )->fields[ $field_id ] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
new WPForms_Field_Checkbox();
|
||||
+1397
File diff suppressed because it is too large
Load Diff
+329
@@ -0,0 +1,329 @@
|
||||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* GDPR Checkbox field.
|
||||
*
|
||||
* @since 1.4.6
|
||||
*/
|
||||
class WPForms_Field_GDPR_Checkbox extends WPForms_Field {
|
||||
|
||||
/**
|
||||
* Primary class constructor.
|
||||
*
|
||||
* @since 1.4.6
|
||||
*/
|
||||
public function init() {
|
||||
|
||||
// Define field type information.
|
||||
$this->name = esc_html__( 'GDPR Agreement', 'wpforms-lite' );
|
||||
$this->type = 'gdpr-checkbox';
|
||||
$this->icon = 'fa-check-square-o';
|
||||
$this->order = 500;
|
||||
$this->allow_read_only = false;
|
||||
$this->defaults = [
|
||||
1 => [
|
||||
'label' => esc_html__( 'I consent to having this website store my submitted information so they can respond to my inquiry.', 'wpforms-lite' ),
|
||||
'value' => '',
|
||||
'image' => '',
|
||||
'default' => '',
|
||||
],
|
||||
];
|
||||
|
||||
$this->default_settings = [
|
||||
'choices' => $this->defaults,
|
||||
];
|
||||
|
||||
// Set field to default to the required.
|
||||
add_filter( 'wpforms_field_new_required', [ $this, 'field_default_required' ], 10, 2 );
|
||||
|
||||
// Define additional field properties.
|
||||
add_filter( 'wpforms_field_properties_gdpr-checkbox', [ $this, 'field_properties' ], 5, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Field should default to being required.
|
||||
*
|
||||
* @since 1.4.6
|
||||
*
|
||||
* @param bool $required Required status, true is required.
|
||||
* @param array $field Field settings.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function field_default_required( $required, $field ) {
|
||||
|
||||
if ( $this->type === $field['type'] ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $required;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define additional field properties.
|
||||
*
|
||||
* @since 1.4.6
|
||||
*
|
||||
* @param array $properties Field properties.
|
||||
* @param array $field Field settings.
|
||||
* @param array $form_data Form data and settings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function field_properties( $properties, $field, $form_data ) {
|
||||
|
||||
// Define data.
|
||||
$form_id = absint( $form_data['id'] );
|
||||
$field_id = absint( $field['id'] );
|
||||
$choices = ! empty( $field['choices'] ) ? $field['choices'] : [];
|
||||
|
||||
// Remove primary input, unset for attribute for label.
|
||||
unset( $properties['inputs']['primary'], $properties['label']['attr']['for'] );
|
||||
|
||||
// Set input container (ul) properties.
|
||||
$properties['input_container'] = [
|
||||
'class' => [],
|
||||
'data' => [],
|
||||
'attr' => [],
|
||||
'id' => "wpforms-{$form_id}-field_{$field_id}",
|
||||
];
|
||||
|
||||
// Set input properties.
|
||||
foreach ( $choices as $key => $choice ) {
|
||||
|
||||
$properties['inputs'][ $key ] = [
|
||||
'container' => [
|
||||
'attr' => [],
|
||||
'class' => [ "choice-{$key}" ],
|
||||
'data' => [],
|
||||
'id' => '',
|
||||
],
|
||||
'label' => [
|
||||
'attr' => [
|
||||
'for' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
|
||||
],
|
||||
'class' => [ 'wpforms-field-label-inline' ],
|
||||
'data' => [],
|
||||
'id' => '',
|
||||
'text' => $choice['label'],
|
||||
],
|
||||
'attr' => [
|
||||
'name' => "wpforms[fields][{$field_id}][]",
|
||||
'value' => $choice['label'],
|
||||
],
|
||||
'class' => [],
|
||||
'data' => [],
|
||||
'id' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
|
||||
'image' => '',
|
||||
'required' => ! empty( $field['required'] ) ? 'required' : '',
|
||||
'default' => '',
|
||||
];
|
||||
}
|
||||
|
||||
// Required class for pagebreak validation.
|
||||
if ( ! empty( $field['required'] ) ) {
|
||||
$properties['input_container']['class'][] = 'wpforms-field-required';
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current field can be populated dynamically.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param array $properties Field properties.
|
||||
* @param array $field Current field specific data.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_dynamic_population_allowed( $properties, $field ): bool {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Field options panel inside the builder.
|
||||
*
|
||||
* @since 1.4.6
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
*/
|
||||
public function field_options( $field ) {
|
||||
|
||||
// Field is always required.
|
||||
$this->field_element(
|
||||
'text',
|
||||
$field,
|
||||
[
|
||||
'type' => 'hidden',
|
||||
'slug' => 'required',
|
||||
'value' => '1',
|
||||
]
|
||||
);
|
||||
|
||||
// -------------------------------------------------------------------//
|
||||
// Basic field options
|
||||
// -------------------------------------------------------------------//
|
||||
|
||||
// Options open markup.
|
||||
$this->field_option(
|
||||
'basic-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'open',
|
||||
]
|
||||
);
|
||||
|
||||
// Label.
|
||||
$this->field_option( 'label', $field );
|
||||
|
||||
// Choices.
|
||||
$this->field_option(
|
||||
'choices',
|
||||
$field,
|
||||
[
|
||||
'label' => esc_html__( 'Agreement', 'wpforms-lite' ),
|
||||
]
|
||||
);
|
||||
|
||||
// Description.
|
||||
$this->field_option( 'description', $field );
|
||||
|
||||
// Options close markup.
|
||||
$this->field_option(
|
||||
'basic-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'close',
|
||||
]
|
||||
);
|
||||
|
||||
// -------------------------------------------------------------------//
|
||||
// Advanced field options
|
||||
// -------------------------------------------------------------------//
|
||||
|
||||
// Options open markup.
|
||||
$this->field_option(
|
||||
'advanced-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'open',
|
||||
]
|
||||
);
|
||||
|
||||
// Custom CSS classes.
|
||||
$this->field_option( 'css', $field );
|
||||
|
||||
// Hide label.
|
||||
$this->field_option( 'label_hide', $field );
|
||||
|
||||
// Options close markup.
|
||||
$this->field_option(
|
||||
'advanced-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'close',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Field preview inside the builder.
|
||||
*
|
||||
* @since 1.4.6
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
*/
|
||||
public function field_preview( $field ) {
|
||||
|
||||
// Label.
|
||||
$this->field_preview_option( 'label', $field );
|
||||
|
||||
// Choices.
|
||||
$this->field_preview_option( 'choices', $field );
|
||||
|
||||
// Description.
|
||||
$this->field_preview_option( 'description', $field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Field display on the form front-end.
|
||||
*
|
||||
* @since 1.4.6
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
* @param array $deprecated Deprecated array.
|
||||
* @param array $form_data Form data and settings.
|
||||
*
|
||||
* @noinspection HtmlUnknownAttribute
|
||||
*/
|
||||
public function field_display( $field, $deprecated, $form_data ) {
|
||||
|
||||
// Define data.
|
||||
$container = $field['properties']['input_container'];
|
||||
$choices = $field['properties']['inputs'];
|
||||
|
||||
printf(
|
||||
'<ul %s>',
|
||||
wpforms_html_attributes( $container['id'], $container['class'], $container['data'], $container['attr'] )
|
||||
);
|
||||
|
||||
foreach ( $choices as $choice ) {
|
||||
$required = '';
|
||||
|
||||
if ( ! empty( $choice['required'] ) && ! empty( $field['label_hide'] ) ) {
|
||||
$required = wpforms_get_field_required_label();
|
||||
}
|
||||
|
||||
printf(
|
||||
'<li %s>',
|
||||
wpforms_html_attributes( $choice['container']['id'], $choice['container']['class'], $choice['container']['data'], $choice['container']['attr'] )
|
||||
);
|
||||
// Normal display.
|
||||
printf(
|
||||
'<input type="checkbox" %s %s %s>',
|
||||
wpforms_html_attributes( $choice['id'], $choice['class'], $choice['data'], $choice['attr'] ),
|
||||
esc_attr( $choice['required'] ),
|
||||
checked( '1', $choice['default'], false )
|
||||
);
|
||||
|
||||
printf(
|
||||
'<label %s>%s%s</label>',
|
||||
wpforms_html_attributes( $choice['label']['id'], $choice['label']['class'], $choice['label']['data'], $choice['label']['attr'] ),
|
||||
wp_kses_post( $choice['label']['text'] ),
|
||||
wp_kses_post( $required )
|
||||
);
|
||||
|
||||
echo '</li>';
|
||||
}
|
||||
|
||||
echo '</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and sanitize field.
|
||||
*
|
||||
* @since 1.4.6
|
||||
*
|
||||
* @param int $field_id Field ID.
|
||||
* @param array $field_submit Submitted form data.
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function format( $field_id, $field_submit, $form_data ) {
|
||||
|
||||
wpforms()->obj( 'process' )->fields[ $field_id ] = [
|
||||
'name' => ! empty( $form_data['fields'][ $field_id ]['label'] ) ? sanitize_text_field( $form_data['fields'][ $field_id ]['label'] ) : '',
|
||||
'value' => $form_data['fields'][ $field_id ]['choices'][1]['label'],
|
||||
'id' => wpforms_validate_field_id( $field_id ),
|
||||
'type' => $this->type,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
new WPForms_Field_GDPR_Checkbox();
|
||||
+942
@@ -0,0 +1,942 @@
|
||||
<?php
|
||||
|
||||
// phpcs:ignore Generic.Commenting.DocComment.MissingShort
|
||||
/** @noinspection AutoloadingIssuesInspection */
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal information field class.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*/
|
||||
class WPForms_Field_Internal_Information extends WPForms_Field {
|
||||
|
||||
/**
|
||||
* The key used to save form checkboxes in the post meta table.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private const CHECKBOX_META_KEY = 'wpforms_iif_checkboxes';
|
||||
|
||||
/**
|
||||
* Class initialization method.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*/
|
||||
public function init() {
|
||||
|
||||
$this->name = $this->is_editable() ? esc_html__( 'Internal Information', 'wpforms-lite' ) : esc_html__( 'This field is not editable', 'wpforms-lite' );
|
||||
$this->type = 'internal-information';
|
||||
$this->icon = 'fa fa-sticky-note-o';
|
||||
$this->order = 550;
|
||||
|
||||
$this->hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register hooks.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @noinspection PhpUnnecessaryCurlyVarSyntaxInspection
|
||||
*/
|
||||
private function hooks() {
|
||||
|
||||
add_filter( 'wpforms_entries_table_fields_disallow', [ $this, 'hide_column_in_entries_table' ], 10, 2 );
|
||||
add_filter( 'wpforms_field_preview_class', [ $this, 'add_css_class_for_field_wrapper' ], 10, 2 );
|
||||
add_filter( 'wpforms_field_new_class', [ $this, 'add_css_class_for_field_wrapper' ], 10, 2 );
|
||||
add_filter( "wpforms_pro_admin_entries_edit_is_field_displayable_{$this->type}", '__return_false' );
|
||||
add_filter( 'wpforms_builder_strings', [ $this, 'builder_strings' ], 10, 2 );
|
||||
add_filter( 'wpforms_frontend_form_data', [ $this, 'remove_internal_fields_on_front_end' ] );
|
||||
add_filter( 'wpforms_pro_fields_entry_preview_get_ignored_fields', [ $this, 'ignore_entry_preview' ] );
|
||||
add_filter( 'wpforms_process_before_form_data', [ $this, 'process_before_form_data' ], 10, 2 );
|
||||
add_filter( 'wpforms_field_preview_display_duplicate_button', [ $this, 'display_duplicate_button' ], 10, 3 );
|
||||
add_action( 'wpforms_builder_enqueues', [ $this, 'builder_enqueues' ] );
|
||||
add_action( 'wp_ajax_wpforms_builder_save_internal_information_checkbox', [ $this, 'save_internal_information_checkbox' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current field can be populated dynamically.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $properties Field properties.
|
||||
* @param array $field Current field specific data.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_dynamic_population_allowed( $properties, $field ): bool {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current field can be populated using a fallback.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $properties Field properties.
|
||||
* @param array $field Current field specific data.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_fallback_population_allowed( $properties, $field ): bool {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define field options to display in the left panel.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $field Field data and settings.
|
||||
*/
|
||||
public function field_options( $field ) {
|
||||
|
||||
$this->field_option(
|
||||
'basic-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'open',
|
||||
]
|
||||
);
|
||||
|
||||
$this->heading_option( $field );
|
||||
$this->field_option( 'description', $field );
|
||||
$this->expanded_description_option( $field );
|
||||
$this->cta_label_option( $field );
|
||||
$this->cta_link_option( $field );
|
||||
|
||||
$this->field_option(
|
||||
'basic-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'close',
|
||||
]
|
||||
);
|
||||
|
||||
$this->field_code( $field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Define field preview on the right side on builder.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $field Field data and settings.
|
||||
*/
|
||||
public function field_preview( $field ) {
|
||||
|
||||
$class = wpforms_sanitize_classes( $field['class'] ?? '' );
|
||||
|
||||
printf(
|
||||
'<div class="internal-information-wrap wpforms-clear %s">',
|
||||
esc_attr( $class )
|
||||
);
|
||||
|
||||
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo wpforms_render( 'fields/internal-information/icon-lightbulb' );
|
||||
|
||||
echo '<div class="internal-information-content">';
|
||||
|
||||
$this->render_preview( 'heading', $field );
|
||||
$this->render_preview( 'description', $field );
|
||||
$this->render_preview( 'expanded-description', $field );
|
||||
$this->render_preview( 'addon', $field );
|
||||
|
||||
if ( $this->is_button_displayable( $field ) ) {
|
||||
echo '<div class="wpforms-field-internal-information-row wpforms-field-internal-information-row-cta-button">';
|
||||
echo $this->render_custom_preview( 'cta-button', $field );
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the button is displayable.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $field Field data.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_button_displayable( $field ): bool {
|
||||
|
||||
return ! empty( $field['expanded-description'] ) ||
|
||||
( ! empty( $field['cta-label'] ) && ! empty( $field['cta-link'] ) ) ||
|
||||
$this->is_editable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stub to make the field not visible in the front-end.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $field Field data and settings.
|
||||
* @param array $deprecated Field attributes.
|
||||
* @param array $form_data Form data.
|
||||
*/
|
||||
public function field_display( $field, $deprecated, $form_data ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Heading option.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $field Field data and settings.
|
||||
*/
|
||||
private function heading_option( $field ) {
|
||||
|
||||
$output = $this->field_element(
|
||||
'label',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'heading',
|
||||
'value' => esc_html__( 'Heading', 'wpforms-lite' ),
|
||||
'tooltip' => esc_attr__( 'Enter text for the form field heading.', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$output .= $this->field_element(
|
||||
'text',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'label',
|
||||
'value' => ! empty( $field['label'] ) ? esc_attr( $field['label'] ) : '',
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'heading',
|
||||
'content' => $output,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expanded description option.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $field Field data and settings.
|
||||
*/
|
||||
private function expanded_description_option( $field ) {
|
||||
|
||||
$output = $this->field_element(
|
||||
'label',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'expanded-description',
|
||||
'value' => esc_html__( 'Expanded Content', 'wpforms-lite' ),
|
||||
'tooltip' => esc_attr__( 'Enter text for the form field expanded description.', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$output .= $this->field_element(
|
||||
'textarea',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'expanded-description',
|
||||
'value' => ! empty( $field['expanded-description'] ) ? esc_html( $field['expanded-description'] ) : '',
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$output .= sprintf(
|
||||
'<p class="note">%s</p>',
|
||||
esc_html__( 'Adds an expandable content area below the description.', 'wpforms-lite' )
|
||||
);
|
||||
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'expanded-description',
|
||||
'content' => $output,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* CTA label option.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $field Field data and settings.
|
||||
*/
|
||||
private function cta_label_option( $field ) {
|
||||
|
||||
$output = $this->field_element(
|
||||
'label',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'cta-label',
|
||||
'value' => esc_html__( 'CTA Label', 'wpforms-lite' ),
|
||||
'tooltip' => esc_attr__( 'Enter label for the form field call to action button. The label will be ignored if the field has extended description content: in that case button will be used to expand the description content.', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$output .= $this->field_element(
|
||||
'text',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'cta-label',
|
||||
'value' => ! empty( $field['cta-label'] ) ? esc_attr( $field['cta-label'] ) : esc_attr__( 'Learn More', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'cta-label',
|
||||
'content' => $output,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* CTA link option.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $field Field data and settings.
|
||||
*/
|
||||
private function cta_link_option( $field ) {
|
||||
|
||||
$output = $this->field_element(
|
||||
'label',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'cta-link',
|
||||
'value' => esc_html__( 'CTA Link', 'wpforms-lite' ),
|
||||
'tooltip' => esc_attr__( 'Enter the URL for the form field call to action button. URL will be ignored if the field has extended description content: in that case button will be used to expand the description content.', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$output .= $this->field_element(
|
||||
'text',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'cta-link',
|
||||
'value' => ! empty( $field['cta-link'] ) ? esc_url( $field['cta-link'] ) : '',
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$output .= sprintf(
|
||||
'<p class="note">%s</p>',
|
||||
esc_html__( 'CTA is hidden if Expanded Content is used.', 'wpforms-lite' )
|
||||
);
|
||||
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'cta-link',
|
||||
'content' => $output,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add hidden input with code identifier.
|
||||
*
|
||||
* @since 1.8.9
|
||||
*
|
||||
* @param array $field Field data and settings.
|
||||
*/
|
||||
private function field_code( $field ) {
|
||||
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'code',
|
||||
'content' => sprintf(
|
||||
'<input type="hidden" name="fields[%1$s][code]" value="%2$s">',
|
||||
$field['id'],
|
||||
! empty( $field['code'] ) ? esc_attr( $field['code'] ) : ''
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a CSS class to hide field settings when the field is not editable.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param string $option Field option to render.
|
||||
* @param array $field Field data and settings.
|
||||
* @param array $args Field preview arguments.
|
||||
* @param bool $do_echo Print or return the value. Print by default.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function field_element( $option, $field, $args = [], $do_echo = true ) {
|
||||
|
||||
if ( ! isset( $args['class'] ) ) {
|
||||
$args['class'] = '';
|
||||
}
|
||||
|
||||
if ( ! $this->is_editable() ) {
|
||||
$args['class'] .= ' wpforms-hidden ';
|
||||
}
|
||||
|
||||
return parent::field_element( $option, $field, $args, $do_echo );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a custom option preview on the right side of the builder.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param string $option Field option to render.
|
||||
* @param array $field Field data and settings.
|
||||
* @param array $args Field arguments.
|
||||
*
|
||||
* @return string
|
||||
* @noinspection HtmlUnknownTarget
|
||||
*/
|
||||
private function render_custom_preview( $option, $field, $args = [] ): string { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
|
||||
|
||||
$class = ! empty( $args['class'] ) ? wpforms_sanitize_classes( $args['class'] ) : '';
|
||||
$allowed_tags = $this->get_allowed_tags();
|
||||
|
||||
switch ( $option ) {
|
||||
case 'heading':
|
||||
$label = isset( $field['label'] ) && ! wpforms_is_empty_string( $field['label'] ) ? esc_html( $field['label'] ) : '';
|
||||
|
||||
if ( ! $label ) {
|
||||
$class .= ' hidden ';
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<label class="label-title heading %s"><span class="text">%s</span><span class="required">*</span></label>',
|
||||
esc_attr( $class ),
|
||||
esc_html( $label )
|
||||
);
|
||||
|
||||
case 'description': // phpcs:ignore WPForms.Formatting.Switch.AddEmptyLineBefore
|
||||
$description = ! empty( $field['description'] ) ? wp_kses( $field['description'], $allowed_tags ) : '';
|
||||
$description = wpautop( $this->replace_checkboxes( $description, $field ) );
|
||||
$description = $this->add_link_attributes( $description );
|
||||
|
||||
return sprintf( '<div class="description %s">%s</div>', $class, $description );
|
||||
|
||||
case 'expanded-description': // phpcs:ignore WPForms.Formatting.Switch.AddEmptyLineBefore
|
||||
$description = isset( $field['expanded-description'] ) && ! wpforms_is_empty_string( $field['expanded-description'] ) ? wp_kses( $field['expanded-description'], $allowed_tags ) : '';
|
||||
$description = wpautop( $this->replace_checkboxes( $description, $field ) );
|
||||
$description = $this->add_link_attributes( $description );
|
||||
|
||||
return sprintf( '<div class="expanded-description %s">%s</div>', esc_attr( $class ), wp_kses( $description, $allowed_tags ) );
|
||||
|
||||
case 'cta-button': // phpcs:ignore WPForms.Formatting.Switch.AddEmptyLineBefore
|
||||
$label = ! empty( $field['cta-label'] ) && empty( $field['expanded-description'] ) ? esc_attr( $field['cta-label'] ) : esc_attr__( 'Learn More', 'wpforms-lite' );
|
||||
|
||||
if ( ! empty( $field['expanded-description'] ) ) {
|
||||
return sprintf(
|
||||
'<div class="cta-button cta-expand-description not-expanded %s"><a href="#" target="_blank" rel="noopener noreferrer"><span class="button-label">%s</span> %s %s</a></div>',
|
||||
esc_attr( $class ),
|
||||
esc_html( $label ),
|
||||
wpforms_render( 'fields/internal-information/icon-not-expanded' ),
|
||||
wpforms_render( 'fields/internal-information/icon-expanded' )
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! empty( $field['cta-link'] ) ) {
|
||||
return sprintf( '<div class="cta-button cta-link-external %s"><a href="%s" target="_blank" rel="noopener noreferrer">%s</a></div>', esc_attr( $class ), esc_url( $this->add_url_utm( $field ) ), esc_html( $label ) );
|
||||
}
|
||||
|
||||
return sprintf( '<div class="cta-button cta-link-external %s"><a href="" target="_blank" rel="noopener noreferrer" class="hidden"><span class="button-label"></span></a></div>', esc_attr( $class ) );
|
||||
|
||||
case 'addon':
|
||||
if ( empty( $field['addon'] ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return sprintf( '<input type="hidden" name="fields[%1$s][addon]" value="%2$s">', esc_attr( $field['id'] ), esc_attr( $field['addon'] ) );
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the field button in the left panel only if the field is editable.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $fields All fields to display in the left panel.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function field_button( $fields ) {
|
||||
|
||||
if ( $this->is_editable() ) {
|
||||
return parent::field_button( $fields );
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* When the form is going to be displayed on the front-end, remove internal information fields.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $form_data Form data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function remove_internal_fields_on_front_end( $form_data ) {
|
||||
|
||||
if ( empty( $form_data['fields'] ) ) {
|
||||
return $form_data;
|
||||
}
|
||||
|
||||
foreach ( $form_data['fields'] as $id => $field ) {
|
||||
if ( $field['type'] === $this->type ) {
|
||||
unset( $form_data['fields'][ $id ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $form_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the internal information field to the list of ignored fields for entry preview.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @param array|mixed $ignored_fields Ignored fields.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function ignore_entry_preview( $ignored_fields ): array {
|
||||
|
||||
$ignored_fields = (array) $ignored_fields;
|
||||
$ignored_fields[] = $this->type;
|
||||
|
||||
return $ignored_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove field from form data before processing the form submit.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $form_data Form data.
|
||||
* @param array $entry Form submission raw data ($_POST).
|
||||
*
|
||||
* @return array
|
||||
* @noinspection PhpMissingParamTypeInspection
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
public function process_before_form_data( $form_data, $entry ) {
|
||||
|
||||
return $this->remove_internal_fields_on_front_end( $form_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not display the duplicate button.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param bool $is_visible If true, the duplicate button will be displayed.
|
||||
* @param array $field Field data and settings.
|
||||
* @param array $form_data Form data and settings.
|
||||
*
|
||||
* @return bool
|
||||
* @noinspection PhpMissingParamTypeInspection
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
public function display_duplicate_button( $is_visible, $field, $form_data ) {
|
||||
|
||||
if ( $this->is_internal_information_field( $field ) && ! $this->is_editable() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $is_visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the column from the entry list table.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array|mixed $disallowed Table columns.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function hide_column_in_entries_table( $disallowed ): array {
|
||||
|
||||
$disallowed = (array) $disallowed;
|
||||
$disallowed[] = $this->type;
|
||||
|
||||
return $disallowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a CSS class for the field parent div informing about mode (editable or not).
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param string $css CSS classes.
|
||||
* @param array $field Field data and settings.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function add_css_class_for_field_wrapper( $css, $field ) {
|
||||
|
||||
if ( ! $this->is_internal_information_field( $field ) ) {
|
||||
return $css;
|
||||
}
|
||||
|
||||
// If the Internal Information field is added by some add-ons, it will be hidden by default.
|
||||
// Add styles to the addon assets to display the field.
|
||||
// When the addon is disabled, the field is hidden.
|
||||
if ( ! empty( $field['addon'] ) ) {
|
||||
$css .= sprintf( ' wpforms-field-internal-information-%s-addon wpforms-hidden', $field['addon'] );
|
||||
}
|
||||
|
||||
if ( $this->is_editable() ) {
|
||||
$css .= ' internal-information-editable ';
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
$css .= ' ui-sortable-disabled internal-information-not-editable internal-information-not-draggable ';
|
||||
|
||||
return str_replace( 'ui-sortable-handle', '', $css );
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the checkbox state to the post meta table.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*/
|
||||
public function save_internal_information_checkbox(): void {
|
||||
|
||||
$form_id = isset( $_POST['formId'] ) ? absint( $_POST['formId'] ) : 0;
|
||||
|
||||
// Run several checks: required items, security, permissions.
|
||||
if (
|
||||
! $form_id ||
|
||||
! isset( $_POST['name'], $_POST['checked'] ) ||
|
||||
! check_ajax_referer( 'wpforms-builder', 'nonce', false ) ||
|
||||
! wpforms_current_user_can( 'edit_forms', $form_id )
|
||||
) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
$checked = (int) $_POST['checked'];
|
||||
$name = sanitize_text_field( wp_unslash( $_POST['name'] ) );
|
||||
$post_meta = get_post_meta( $form_id, self::CHECKBOX_META_KEY, true );
|
||||
$post_meta = ! empty( $post_meta ) ? (array) $post_meta : [];
|
||||
|
||||
if ( $checked ) {
|
||||
$post_meta[ $name ] = $checked;
|
||||
} else {
|
||||
unset( $post_meta[ $name ] );
|
||||
}
|
||||
|
||||
update_post_meta( $form_id, self::CHECKBOX_META_KEY, $post_meta );
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Localized strings for a wpforms-internal-information-field JS script.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $strings Localized strings.
|
||||
* @param array $form The form element.
|
||||
*
|
||||
* @return array
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
public function builder_strings( $strings, $form ) {
|
||||
|
||||
$strings['iif_redirect_url_field_error'] = esc_html__( 'You should enter a valid absolute address to the CTA Link field or leave it empty.', 'wpforms-lite' );
|
||||
$strings['iif_dismiss'] = esc_html__( 'Dismiss', 'wpforms-lite' );
|
||||
$strings['iif_more'] = esc_html__( 'Learn More', 'wpforms-lite' );
|
||||
|
||||
return $strings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue wpforms-internal-information-field script.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param string $view Current view.
|
||||
*
|
||||
* @noinspection PhpUnusedParameterInspection, PhpUnnecessaryCurlyVarSyntaxInspection
|
||||
*/
|
||||
public function builder_enqueues( $view ) {
|
||||
|
||||
$min = wpforms_get_min_suffix();
|
||||
|
||||
wp_enqueue_script(
|
||||
'wpforms-md5-hash',
|
||||
WPFORMS_PLUGIN_URL . 'assets/lib/md5.min.js',
|
||||
[ 'wpforms-builder' ],
|
||||
'2.19.0',
|
||||
false
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'wpforms-internal-information-field',
|
||||
WPFORMS_PLUGIN_URL . "assets/js/admin/builder/fields/internal-information{$min}.js",
|
||||
[ 'wpforms-builder', 'wpforms-md5-hash' ],
|
||||
WPFORMS_VERSION,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user is allowed to edit the field's content.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_editable(): bool {
|
||||
|
||||
/**
|
||||
* Allow changing a mode.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param bool $is_editable True if editable mode is allowed. Default: false.
|
||||
*/
|
||||
return (bool) apply_filters( 'wpforms_field_internal_information_is_editable', false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the field has type internal-information.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $field Field data.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_internal_information_field( $field ): bool {
|
||||
|
||||
return isset( $field['type'] ) && $field['type'] === $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the result of the field_preview_option into a custom div.
|
||||
*
|
||||
* If the field has no value, do not echo anything.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param string $label Field label.
|
||||
* @param array $field Field settings and data.
|
||||
* @param array $args Field arguments.
|
||||
*
|
||||
* @noinspection PhpSameParameterValueInspection
|
||||
*/
|
||||
private function render_preview( $label, $field, $args = [] ): void {
|
||||
|
||||
$key = $label === 'heading' ? 'label' : $label;
|
||||
|
||||
if ( empty( $field[ $key ] ) && ! $this->is_editable() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$allowed_tags = $this->get_allowed_tags();
|
||||
|
||||
printf(
|
||||
'<div class="wpforms-field-internal-information-row wpforms-field-internal-information-row-%s">%s</div>',
|
||||
esc_attr( $label ),
|
||||
wp_kses( $this->render_custom_preview( $label, $field, $args ), $allowed_tags )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace `[] some text` with checkboxes.
|
||||
*
|
||||
* Additionally, generates the input name by hashing the line of text where the checkbox is.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param string $description Expanded description.
|
||||
* @param array $field Field data and settings.
|
||||
*
|
||||
* @return string
|
||||
* @noinspection HtmlUnknownAttribute
|
||||
*/
|
||||
private function replace_checkboxes( string $description, array $field ): string {
|
||||
|
||||
if ( ! $this->form_id ) {
|
||||
return $description;
|
||||
}
|
||||
|
||||
$lines = explode( PHP_EOL, $description );
|
||||
$replaced = [];
|
||||
$post_meta = get_post_meta( $this->form_id, self::CHECKBOX_META_KEY, true );
|
||||
$post_meta = ! empty( $post_meta ) ? (array) $post_meta : [];
|
||||
$field_id = $field['id'] ?? 0;
|
||||
$needle = '[] ';
|
||||
|
||||
foreach ( $lines as $line_number => $line ) {
|
||||
$line = trim( $line );
|
||||
|
||||
if ( strpos( $line, $needle ) !== 0 ) {
|
||||
$replaced[] = $line . PHP_EOL;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$field_name = sprintf( 'iif-%d-%s-%d', $field_id, md5( $line ), $line_number );
|
||||
$checked = (int) isset( $post_meta[ $field_name ] );
|
||||
$attributes = [
|
||||
'name' => esc_attr( $field_name ),
|
||||
'value' => 1,
|
||||
];
|
||||
|
||||
if ( $this->is_editable() ) {
|
||||
$attributes['disabled'] = 'disabled';
|
||||
$attributes['title'] = esc_html__( 'This field is disabled in the editor mode.', 'wpforms-lite' );
|
||||
}
|
||||
|
||||
$html = sprintf(
|
||||
'<div class="wpforms-field-internal-information-checkbox-input"><input type="checkbox" %s %s /></div><div class="wpforms-field-internal-information-checkbox-label">',
|
||||
wpforms_html_attributes(
|
||||
'',
|
||||
[ 'wpforms-field-internal-information-checkbox' ],
|
||||
[],
|
||||
$attributes
|
||||
),
|
||||
! $this->is_editable() ? checked( $checked, 1, false ) : ''
|
||||
);
|
||||
|
||||
$line = substr_replace( $line, $html, 0, strlen( $needle ) );
|
||||
|
||||
$replaced[] = '<div class="wpforms-field-internal-information-checkbox-wrap">' . $line . '</div></div>';
|
||||
}
|
||||
|
||||
return implode( '', $replaced );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return allowed tags specific to internal information field content.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_allowed_tags(): array {
|
||||
|
||||
$allowed_tags = wpforms_builder_preview_get_allowed_tags();
|
||||
|
||||
$allowed_tags['input'] = [
|
||||
'type' => [],
|
||||
'name' => [],
|
||||
'value' => [],
|
||||
'class' => [],
|
||||
'checked' => [],
|
||||
'disabled' => [],
|
||||
'title' => [],
|
||||
];
|
||||
|
||||
return $allowed_tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds link parameters to all links in the provided content.
|
||||
*
|
||||
* @since 1.8.3
|
||||
*
|
||||
* @param string $content The content to modify.
|
||||
*
|
||||
* @return string The modified content with UTM parameters added to links.
|
||||
*/
|
||||
private function add_link_attributes( string $content ): string {
|
||||
|
||||
if ( empty( $content ) || ! class_exists( 'DOMDocument' ) ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$form_obj = wpforms()->obj( 'form' );
|
||||
$form_data = $form_obj ? $form_obj->get( $this->form_id, [ 'content_only' => true ] ) : [];
|
||||
$templates_obj = wpforms()->obj( 'builder_templates' );
|
||||
$template = $form_data['meta']['template'] ?? '';
|
||||
$template_data = $templates_obj && $template ? $templates_obj->get_template( $template ) : [];
|
||||
$template_name = $template_data['name'] ?? '';
|
||||
|
||||
$dom->loadHTML( htmlspecialchars_decode( htmlentities( $content ) ) );
|
||||
|
||||
$links = $dom->getElementsByTagName( 'a' );
|
||||
|
||||
foreach ( $links as $link ) {
|
||||
$href = $link->getAttribute( 'href' );
|
||||
$text = $link->textContent; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
|
||||
$modified_href = wpforms_utm_link( $href, 'Form Template Information Note', $template_name, $text );
|
||||
|
||||
$link->setAttribute( 'href', $modified_href );
|
||||
$link->setAttribute( 'target', '_blank' );
|
||||
$link->setAttribute( 'rel', 'noopener noreferrer' );
|
||||
}
|
||||
|
||||
// Remove the wrapper elements.
|
||||
$body = $dom->getElementsByTagName( 'body' )->item( 0 );
|
||||
$child_nodes = $body->childNodes ?? []; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
|
||||
$inner_html = '';
|
||||
|
||||
foreach ( $child_nodes as $node ) {
|
||||
$inner_html .= $dom->saveHTML( $node );
|
||||
}
|
||||
|
||||
return $inner_html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add UTM parameters to the CTA button link.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $field Field data.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function add_url_utm( array $field ): string {
|
||||
|
||||
$cta_link = (string) $field['cta-link'];
|
||||
|
||||
if ( strpos( $cta_link, 'https://wpforms.com' ) === 0 ) {
|
||||
return wpforms_utm_link( $cta_link, 'Template Documentation' );
|
||||
}
|
||||
|
||||
return $cta_link;
|
||||
}
|
||||
}
|
||||
|
||||
new WPForms_Field_Internal_Information();
|
||||
+681
@@ -0,0 +1,681 @@
|
||||
<?php
|
||||
|
||||
// phpcs:ignore Generic.Commenting.DocComment.MissingShort
|
||||
/** @noinspection AutoloadingIssuesInspection */
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Name text field.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class WPForms_Field_Name extends WPForms_Field {
|
||||
|
||||
/**
|
||||
* Primary class constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function init() {
|
||||
|
||||
// Define field type information.
|
||||
$this->name = esc_html__( 'Name', 'wpforms-lite' );
|
||||
$this->keywords = esc_html__( 'user, first, last', 'wpforms-lite' );
|
||||
$this->type = 'name';
|
||||
$this->icon = 'fa-user';
|
||||
$this->order = 150;
|
||||
|
||||
$this->hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks.
|
||||
*
|
||||
* @since 1.8.1
|
||||
*/
|
||||
private function hooks(): void {
|
||||
|
||||
// Define additional field properties.
|
||||
add_filter( 'wpforms_field_properties_name', [ $this, 'field_properties' ], 5, 3 );
|
||||
|
||||
// Set field to default required.
|
||||
add_filter( 'wpforms_field_new_required', [ $this, 'default_required' ], 10, 2 );
|
||||
|
||||
// This field requires fieldset+legend instead of the field label.
|
||||
add_filter( "wpforms_frontend_modern_is_field_requires_fieldset_{$this->type}", [ $this, 'is_field_requires_fieldset' ], PHP_INT_MAX, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Define additional field properties.
|
||||
*
|
||||
* @since 1.3.7
|
||||
*
|
||||
* @param array|mixed $properties Field properties.
|
||||
* @param array $field Field data and settings.
|
||||
* @param array $form_data Form data and settings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function field_properties( $properties, $field, $form_data ): array { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded
|
||||
|
||||
$properties = (array) $properties;
|
||||
|
||||
$format = ! empty( $field['format'] ) ? esc_attr( $field['format'] ) : 'first-last';
|
||||
|
||||
// Simple format.
|
||||
if ( $format === 'simple' ) {
|
||||
$properties['inputs']['primary']['attr']['placeholder'] = ! empty( $field['simple_placeholder'] )
|
||||
? $field['simple_placeholder'] :
|
||||
'';
|
||||
$properties['inputs']['primary']['attr']['value'] = ! empty( $field['simple_default'] )
|
||||
? wpforms_process_smart_tags( $field['simple_default'], $form_data, [], '', 'field-properties' )
|
||||
: '';
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
// Expanded formats.
|
||||
// Remove primary for expanded formats since we have first, middle, last.
|
||||
unset( $properties['inputs']['primary'] );
|
||||
|
||||
// Remove reference to an input element to prevent duplication.
|
||||
if ( empty( $field['sublabel_hide'] ) ) {
|
||||
unset( $properties['label']['attr']['for'] );
|
||||
}
|
||||
|
||||
$form_id = absint( $form_data['id'] );
|
||||
$field_id = wpforms_validate_field_id( $field['id'] );
|
||||
|
||||
$props = [
|
||||
'inputs' => [
|
||||
'first' => [
|
||||
'attr' => [
|
||||
'name' => "wpforms[fields][{$field_id}][first]",
|
||||
'value' => ! empty( $field['first_default'] )
|
||||
? wpforms_process_smart_tags( $field['first_default'], $form_data, [], '', 'field-properties' )
|
||||
: '',
|
||||
'placeholder' => ! empty( $field['first_placeholder'] ) ? $field['first_placeholder'] : '',
|
||||
],
|
||||
'block' => [
|
||||
'wpforms-field-row-block',
|
||||
'wpforms-first',
|
||||
],
|
||||
'class' => [
|
||||
'wpforms-field-name-first',
|
||||
],
|
||||
'data' => [],
|
||||
'id' => "wpforms-{$form_id}-field_{$field_id}",
|
||||
'required' => ! empty( $field['required'] ) ? 'required' : '',
|
||||
'sublabel' => [
|
||||
'hidden' => ! empty( $field['sublabel_hide'] ),
|
||||
'value' => esc_html__( 'First', 'wpforms-lite' ),
|
||||
],
|
||||
],
|
||||
'middle' => [
|
||||
'attr' => [
|
||||
'name' => "wpforms[fields][{$field_id}][middle]",
|
||||
'value' => ! empty( $field['middle_default'] )
|
||||
? wpforms_process_smart_tags( $field['middle_default'], $form_data, [], '', 'field-properties' )
|
||||
: '',
|
||||
'placeholder' => ! empty( $field['middle_placeholder'] ) ? $field['middle_placeholder'] : '',
|
||||
],
|
||||
'block' => [
|
||||
'wpforms-field-row-block',
|
||||
'wpforms-one-fifth',
|
||||
],
|
||||
'class' => [
|
||||
'wpforms-field-name-middle',
|
||||
],
|
||||
'data' => [],
|
||||
'id' => "wpforms-{$form_id}-field_{$field_id}-middle",
|
||||
'required' => '',
|
||||
'sublabel' => [
|
||||
'hidden' => ! empty( $field['sublabel_hide'] ),
|
||||
'value' => esc_html__( 'Middle', 'wpforms-lite' ),
|
||||
],
|
||||
],
|
||||
'last' => [
|
||||
'attr' => [
|
||||
'name' => "wpforms[fields][{$field_id}][last]",
|
||||
'value' => ! empty( $field['last_default'] )
|
||||
? wpforms_process_smart_tags( $field['last_default'], $form_data, [], '', 'field-properties' )
|
||||
: '',
|
||||
'placeholder' => ! empty( $field['last_placeholder'] ) ? $field['last_placeholder'] : '',
|
||||
],
|
||||
'block' => [
|
||||
'wpforms-field-row-block',
|
||||
|
||||
],
|
||||
'class' => [
|
||||
'wpforms-field-name-last',
|
||||
],
|
||||
'data' => [],
|
||||
'id' => "wpforms-{$form_id}-field_{$field_id}-last",
|
||||
'required' => ! empty( $field['required'] ) ? 'required' : '',
|
||||
'sublabel' => [
|
||||
'hidden' => ! empty( $field['sublabel_hide'] ),
|
||||
'value' => esc_html__( 'Last', 'wpforms-lite' ),
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$properties = array_merge_recursive( $properties, $props );
|
||||
|
||||
$has_common_error = ! empty( $properties['error']['value'] ) && is_string( $properties['error']['value'] );
|
||||
|
||||
// Input First: add error class if needed.
|
||||
if ( ! empty( $properties['error']['value']['first'] ) || $has_common_error ) {
|
||||
$properties['inputs']['first']['class'][] = 'wpforms-error';
|
||||
}
|
||||
|
||||
// Input First: add required class if needed.
|
||||
if ( ! empty( $field['required'] ) ) {
|
||||
$properties['inputs']['first']['class'][] = 'wpforms-field-required';
|
||||
}
|
||||
|
||||
// Input First: add column class.
|
||||
$properties['inputs']['first']['block'][] = $format === 'first-last' ? 'wpforms-one-half' : 'wpforms-two-fifths';
|
||||
|
||||
// Input Middle: add error class if needed.
|
||||
if ( $has_common_error ) {
|
||||
$properties['inputs']['middle']['class'][] = 'wpforms-error';
|
||||
}
|
||||
|
||||
// Input Last: add error class if needed.
|
||||
if ( ! empty( $properties['error']['value']['last'] ) || $has_common_error ) {
|
||||
$properties['inputs']['last']['class'][] = 'wpforms-error';
|
||||
}
|
||||
|
||||
// Input Last: add required class if needed.
|
||||
if ( ! empty( $field['required'] ) ) {
|
||||
$properties['inputs']['last']['class'][] = 'wpforms-field-required';
|
||||
}
|
||||
|
||||
// Input Last: add column class.
|
||||
$properties['inputs']['last']['block'][] = $format === 'first-last' ? 'wpforms-one-half' : 'wpforms-two-fifths';
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Name fields should default to being required.
|
||||
*
|
||||
* @since 1.0.8
|
||||
*
|
||||
* @param bool|mixed $required Whether the field is required.
|
||||
* @param array $field Field data.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function default_required( $required, $field ): bool {
|
||||
|
||||
if ( $field['type'] === 'name' ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (bool) $required;
|
||||
}
|
||||
|
||||
/**
|
||||
* Field options panel inside the builder.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field information.
|
||||
*/
|
||||
public function field_options( $field ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
|
||||
|
||||
// Define data.
|
||||
$format = ! empty( $field['format'] ) ? esc_attr( $field['format'] ) : 'first-last';
|
||||
|
||||
/*
|
||||
* Basic field options.
|
||||
*/
|
||||
|
||||
// Options open markup.
|
||||
$args = [
|
||||
'markup' => 'open',
|
||||
];
|
||||
|
||||
$this->field_option( 'basic-options', $field, $args );
|
||||
|
||||
// Label.
|
||||
$this->field_option( 'label', $field );
|
||||
|
||||
// Format.
|
||||
$lbl = $this->field_element(
|
||||
'label',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'format',
|
||||
'value' => esc_html__( 'Format', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Select format to use for the name form field', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$fld = $this->field_element(
|
||||
'select',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'format',
|
||||
'value' => $format,
|
||||
'options' => [
|
||||
'simple' => esc_html__( 'Simple', 'wpforms-lite' ),
|
||||
'first-last' => esc_html__( 'First Last', 'wpforms-lite' ),
|
||||
'first-middle-last' => esc_html__( 'First Middle Last', 'wpforms-lite' ),
|
||||
],
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$args = [
|
||||
'slug' => 'format',
|
||||
'content' => $lbl . $fld,
|
||||
];
|
||||
|
||||
$this->field_element( 'row', $field, $args );
|
||||
|
||||
// Description.
|
||||
$this->field_option( 'description', $field );
|
||||
|
||||
// Required toggle.
|
||||
$this->field_option( 'required', $field );
|
||||
|
||||
// Options close markup.
|
||||
$args = [
|
||||
'markup' => 'close',
|
||||
];
|
||||
|
||||
$this->field_option( 'basic-options', $field, $args );
|
||||
|
||||
/*
|
||||
* Advanced field options.
|
||||
*/
|
||||
|
||||
// Options open markup.
|
||||
$args = [
|
||||
'markup' => 'open',
|
||||
];
|
||||
|
||||
$this->field_option( 'advanced-options', $field, $args );
|
||||
|
||||
// Size.
|
||||
$this->field_option( 'size', $field );
|
||||
|
||||
echo '<div class="format-selected-' . esc_attr( $format ) . ' format-selected">';
|
||||
|
||||
// Simple.
|
||||
$simple_placeholder = ! empty( $field['simple_placeholder'] ) ? esc_attr( $field['simple_placeholder'] ) : '';
|
||||
$simple_default = ! empty( $field['simple_default'] ) ? esc_attr( $field['simple_default'] ) : '';
|
||||
|
||||
printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-simple" id="wpforms-field-option-row-%d-simple" data-subfield="simple" data-field-id="%d">', esc_attr( $field['id'] ), esc_attr( $field['id'] ) );
|
||||
$this->field_element(
|
||||
'label',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'simple_placeholder',
|
||||
'value' => esc_html__( 'Name', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Name field advanced options.', 'wpforms-lite' ),
|
||||
]
|
||||
);
|
||||
echo '<div class="wpforms-field-options-columns-2 wpforms-field-options-columns">';
|
||||
echo '<div class="placeholder wpforms-field-options-column">';
|
||||
printf( '<input type="text" class="placeholder" id="wpforms-field-option-%d-simple_placeholder" name="fields[%d][simple_placeholder]" value="%s">', (int) $field['id'], (int) $field['id'], esc_attr( $simple_placeholder ) );
|
||||
printf( '<label for="wpforms-field-option-%d-simple_placeholder" class="sub-label">%s</label>', (int) $field['id'], esc_html__( 'Placeholder', 'wpforms-lite' ) );
|
||||
echo '</div>';
|
||||
echo '<div class="default wpforms-field-options-column">';
|
||||
printf( '<input type="text" class="default wpforms-smart-tags-enabled" id="wpforms-field-option-%d-simple_default" name="fields[%d][simple_default]" data-type="other" value="%s">', (int) $field['id'], (int) $field['id'], esc_attr( $simple_default ) );
|
||||
printf( '<label for="wpforms-field-option-%d-simple_default" class="sub-label">%s</label>', (int) $field['id'], esc_html__( 'Default Value', 'wpforms-lite' ) );
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
|
||||
// First.
|
||||
$first_placeholder = ! empty( $field['first_placeholder'] ) ? esc_attr( $field['first_placeholder'] ) : '';
|
||||
$first_default = ! empty( $field['first_default'] ) ? esc_attr( $field['first_default'] ) : '';
|
||||
|
||||
printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-first" id="wpforms-field-option-row-%d-first" data-subfield="first-name" data-field-id="%d">', esc_attr( $field['id'] ), esc_attr( $field['id'] ) );
|
||||
$this->field_element(
|
||||
'label',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'first_placeholder',
|
||||
'value' => esc_html__( 'First Name', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'First name field advanced options.', 'wpforms-lite' ),
|
||||
]
|
||||
);
|
||||
echo '<div class="wpforms-field-options-columns-2 wpforms-field-options-columns">';
|
||||
echo '<div class="placeholder wpforms-field-options-column">';
|
||||
printf( '<input type="text" class="placeholder" id="wpforms-field-option-%1$d-first_placeholder" name="fields[%1$d][first_placeholder]" value="%2$s">', (int) $field['id'], esc_attr( $first_placeholder ) );
|
||||
printf( '<label for="wpforms-field-option-%d-first_placeholder" class="sub-label">%s</label>', (int) $field['id'], esc_html__( 'Placeholder', 'wpforms-lite' ) );
|
||||
echo '</div>';
|
||||
echo '<div class="default wpforms-field-options-column">';
|
||||
printf( '<input type="text" class="default wpforms-smart-tags-enabled" id="wpforms-field-option-%1$d-first_default" name="fields[%1$d][first_default]" data-type="other" value="%2$s">', (int) $field['id'], esc_attr( $first_default ) );
|
||||
printf( '<label for="wpforms-field-option-%d-first_default" class="sub-label">%s</label>', (int) $field['id'], esc_html__( 'Default Value', 'wpforms-lite' ) );
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
|
||||
// Middle.
|
||||
$middle_placeholder = ! empty( $field['middle_placeholder'] ) ? esc_attr( $field['middle_placeholder'] ) : '';
|
||||
$middle_default = ! empty( $field['middle_default'] ) ? esc_attr( $field['middle_default'] ) : '';
|
||||
|
||||
printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-middle" id="wpforms-field-option-row-%d-middle" data-subfield="middle-name" data-field-id="%d">', esc_attr( $field['id'] ), esc_attr( $field['id'] ) );
|
||||
$this->field_element(
|
||||
'label',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'middle_placeholder',
|
||||
'value' => esc_html__( 'Middle Name', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Middle name field advanced options.', 'wpforms-lite' ),
|
||||
]
|
||||
);
|
||||
echo '<div class="wpforms-field-options-columns-2 wpforms-field-options-columns">';
|
||||
echo '<div class="placeholder wpforms-field-options-column">';
|
||||
printf( '<input type="text" class="placeholder" id="wpforms-field-option-%1$d-middle_placeholder" name="fields[%1$d][middle_placeholder]" value="%2$s">', (int) $field['id'], esc_attr( $middle_placeholder ) );
|
||||
printf( '<label for="wpforms-field-option-%d-middle_placeholder" class="sub-label">%s</label>', (int) $field['id'], esc_html__( 'Placeholder', 'wpforms-lite' ) );
|
||||
echo '</div>';
|
||||
echo '<div class="default wpforms-field-options-column">';
|
||||
printf( '<input type="text" class="default wpforms-smart-tags-enabled" id="wpforms-field-option-%1$d-middle_default" name="fields[%1$d][middle_default]" data-type="other" value="%2$s">', (int) $field['id'], esc_attr( $middle_default ) );
|
||||
printf( '<label for="wpforms-field-option-%d-middle_default" class="sub-label">%s</label>', (int) $field['id'], esc_html__( 'Default Value', 'wpforms-lite' ) );
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
|
||||
// Last.
|
||||
$last_placeholder = ! empty( $field['last_placeholder'] ) ? esc_attr( $field['last_placeholder'] ) : '';
|
||||
$last_default = ! empty( $field['last_default'] ) ? esc_attr( $field['last_default'] ) : '';
|
||||
|
||||
printf( '<div class="wpforms-clear wpforms-field-option-row wpforms-field-option-row-last" id="wpforms-field-option-row-%d-last" data-subfield="last-name" data-field-id="%d">', esc_attr( $field['id'] ), esc_attr( $field['id'] ) );
|
||||
$this->field_element(
|
||||
'label',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'last_placeholder',
|
||||
'value' => esc_html__( 'Last Name', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Last name field advanced options.', 'wpforms-lite' ),
|
||||
]
|
||||
);
|
||||
echo '<div class="wpforms-field-options-columns-2 wpforms-field-options-columns">';
|
||||
echo '<div class="placeholder wpforms-field-options-column">';
|
||||
printf( '<input type="text" class="placeholder" id="wpforms-field-option-%1$d-last_placeholder" name="fields[%1$d][last_placeholder]" value="%2$s">', (int) $field['id'], esc_attr( $last_placeholder ) );
|
||||
printf( '<label for="wpforms-field-option-%d-last_placeholder" class="sub-label">%s</label>', (int) $field['id'], esc_html__( 'Placeholder', 'wpforms-lite' ) );
|
||||
echo '</div>';
|
||||
echo '<div class="default wpforms-field-options-column">';
|
||||
printf( '<input type="text" class="default wpforms-smart-tags-enabled" id="wpforms-field-option-%1$d-last_default" name="fields[%1$d][last_default]" data-type="other" value="%2$s">', (int) $field['id'], esc_attr( $last_default ) );
|
||||
printf( '<label for="wpforms-field-option-%d-last_default" class="sub-label">%s</label>', (int) $field['id'], esc_html__( 'Default Value', 'wpforms-lite' ) );
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
|
||||
echo '</div>';
|
||||
|
||||
// Custom CSS classes.
|
||||
$this->field_option( 'css', $field );
|
||||
|
||||
// Hide Label.
|
||||
$this->field_option( 'label_hide', $field );
|
||||
|
||||
// Hide sublabels.
|
||||
$sublabel_class = isset( $field['format'] ) && ! in_array( $field['format'], [ 'first-last', 'first-middle-last' ], true ) ? 'wpforms-hidden' : '';
|
||||
|
||||
$this->field_option( 'sublabel_hide', $field, [ 'class' => $sublabel_class ] );
|
||||
|
||||
// Options close markup.
|
||||
$args = [
|
||||
'markup' => 'close',
|
||||
];
|
||||
|
||||
$this->field_option( 'advanced-options', $field, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Field preview inside the builder.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field information.
|
||||
*/
|
||||
public function field_preview( $field ) {
|
||||
|
||||
// Define data.
|
||||
$simple_placeholder = ! empty( $field['simple_placeholder'] ) ? $field['simple_placeholder'] : '';
|
||||
$first_placeholder = ! empty( $field['first_placeholder'] ) ? $field['first_placeholder'] : '';
|
||||
$middle_placeholder = ! empty( $field['middle_placeholder'] ) ? $field['middle_placeholder'] : '';
|
||||
$last_placeholder = ! empty( $field['last_placeholder'] ) ? $field['last_placeholder'] : '';
|
||||
$simple_default = ! empty( $field['simple_default'] ) ? $field['simple_default'] : '';
|
||||
$first_default = ! empty( $field['first_default'] ) ? $field['first_default'] : '';
|
||||
$middle_default = ! empty( $field['middle_default'] ) ? $field['middle_default'] : '';
|
||||
$last_default = ! empty( $field['last_default'] ) ? $field['last_default'] : '';
|
||||
$format = ! empty( $field['format'] ) ? $field['format'] : 'first-last';
|
||||
|
||||
// Label.
|
||||
$this->field_preview_option( 'label', $field );
|
||||
?>
|
||||
|
||||
<div class="format-selected-<?php echo sanitize_html_class( $format ); ?> format-selected wpforms-clear">
|
||||
|
||||
<div class="wpforms-simple">
|
||||
<input type="text" placeholder="<?php echo esc_attr( $simple_placeholder ); ?>" value="<?php echo esc_attr( $simple_default ); ?>" class="primary-input" readonly>
|
||||
</div>
|
||||
|
||||
<div class="wpforms-first-name">
|
||||
<input type="text" placeholder="<?php echo esc_attr( $first_placeholder ); ?>" value="<?php echo esc_attr( $first_default ); ?>" class="primary-input" readonly>
|
||||
<label class="wpforms-sub-label"><?php esc_html_e( 'First', 'wpforms-lite' ); ?></label>
|
||||
</div>
|
||||
|
||||
<div class="wpforms-middle-name">
|
||||
<input type="text" placeholder="<?php echo esc_attr( $middle_placeholder ); ?>" value="<?php echo esc_attr( $middle_default ); ?>" class="primary-input" readonly>
|
||||
<label class="wpforms-sub-label"><?php esc_html_e( 'Middle', 'wpforms-lite' ); ?></label>
|
||||
</div>
|
||||
|
||||
<div class="wpforms-last-name">
|
||||
<input type="text" placeholder="<?php echo esc_attr( $last_placeholder ); ?>" value="<?php echo esc_attr( $last_default ); ?>" class="primary-input" readonly>
|
||||
<label class="wpforms-sub-label"><?php esc_html_e( 'Last', 'wpforms-lite' ); ?></label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php
|
||||
// Description.
|
||||
$this->field_preview_option( 'description', $field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Field display on the form front-end.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field information.
|
||||
* @param array $deprecated Deprecated parameter, not used anymore.
|
||||
* @param array $form_data Form data and settings.
|
||||
*
|
||||
* @noinspection HtmlUnknownAttribute
|
||||
*/
|
||||
public function field_display( $field, $deprecated, $form_data ) {
|
||||
|
||||
// Define data.
|
||||
$format = ! empty( $field['format'] ) ? esc_attr( $field['format'] ) : 'first-last';
|
||||
$primary = ! empty( $field['properties']['inputs']['primary'] ) ? $field['properties']['inputs']['primary'] : '';
|
||||
$first = ! empty( $field['properties']['inputs']['first'] ) ? $field['properties']['inputs']['first'] : '';
|
||||
$middle = ! empty( $field['properties']['inputs']['middle'] ) ? $field['properties']['inputs']['middle'] : '';
|
||||
$last = ! empty( $field['properties']['inputs']['last'] ) ? $field['properties']['inputs']['last'] : '';
|
||||
|
||||
// Simple format.
|
||||
if ( $format === 'simple' ) {
|
||||
|
||||
// Primary field (Simple).
|
||||
printf(
|
||||
'<input type="text" %s %s>',
|
||||
wpforms_html_attributes( $primary['id'], $primary['class'], $primary['data'], $primary['attr'] ),
|
||||
esc_attr( $primary['required'] )
|
||||
);
|
||||
|
||||
// Expanded formats.
|
||||
} else {
|
||||
|
||||
// Row wrapper.
|
||||
echo '<div class="wpforms-field-row wpforms-field-' . sanitize_html_class( $field['size'] ) . '">';
|
||||
|
||||
// First name.
|
||||
echo '<div ' . wpforms_html_attributes( false, $first['block'] ) . '>';
|
||||
$this->field_display_sublabel( 'first', 'before', $field );
|
||||
printf(
|
||||
'<input type="text" %s %s>',
|
||||
wpforms_html_attributes( $first['id'], $first['class'], $first['data'], $first['attr'] ),
|
||||
esc_attr( $first['required'] )
|
||||
);
|
||||
$this->field_display_sublabel( 'first', 'after', $field );
|
||||
$this->field_display_error( 'first', $field );
|
||||
echo '</div>';
|
||||
|
||||
// Middle name.
|
||||
if ( $format === 'first-middle-last' ) {
|
||||
echo '<div ' . wpforms_html_attributes( false, $middle['block'] ) . '>';
|
||||
$this->field_display_sublabel( 'middle', 'before', $field );
|
||||
printf(
|
||||
'<input type="text" %s %s>',
|
||||
wpforms_html_attributes( $middle['id'], $middle['class'], $middle['data'], $middle['attr'] ),
|
||||
esc_attr( $middle['required'] )
|
||||
);
|
||||
$this->field_display_sublabel( 'middle', 'after', $field );
|
||||
$this->field_display_error( 'middle', $field );
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
// Last name.
|
||||
echo '<div ' . wpforms_html_attributes( false, $last['block'] ) . '>';
|
||||
$this->field_display_sublabel( 'last', 'before', $field );
|
||||
printf(
|
||||
'<input type="text" %s %s>',
|
||||
wpforms_html_attributes( $last['id'], $last['class'], $last['data'], $last['attr'] ),
|
||||
esc_attr( $last['required'] )
|
||||
);
|
||||
$this->field_display_sublabel( 'last', 'after', $field );
|
||||
$this->field_display_error( 'last', $field );
|
||||
echo '</div>';
|
||||
|
||||
echo '</div>';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate field on submitting a form.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param int $field_id Field id.
|
||||
* @param array|string $field_submit Submitted field value (raw data).
|
||||
* @param array $form_data Form data.
|
||||
*/
|
||||
public function validate( $field_id, $field_submit, $form_data ) {
|
||||
|
||||
if ( empty( $form_data['fields'][ $field_id ]['required'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extended validation needed for the different name fields.
|
||||
$form_id = $form_data['id'];
|
||||
$format = $form_data['fields'][ $field_id ]['format'];
|
||||
$required = wpforms_get_required_label();
|
||||
$process = wpforms()->obj( 'process' );
|
||||
|
||||
if ( $format === 'simple' && wpforms_is_empty_string( $field_submit ) ) {
|
||||
$process->errors[ $form_id ][ $field_id ] = $required;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! ( $format === 'first-last' || $format === 'first-middle-last' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->validate_complicated_formats( $process, $form_id, $field_id, $field_submit, $required );
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and sanitize field.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param int $field_id Field ID.
|
||||
* @param mixed $field_submit Field value that was submitted.
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function format( $field_id, $field_submit, $form_data ) {
|
||||
// Define data.
|
||||
$name = isset( $form_data['fields'][ $field_id ]['label'] ) && ! wpforms_is_empty_string( $form_data['fields'][ $field_id ]['label'] ) ? $form_data['fields'][ $field_id ]['label'] : '';
|
||||
$first = isset( $field_submit['first'] ) && ! wpforms_is_empty_string( $field_submit['first'] ) ? $field_submit['first'] : '';
|
||||
$middle = isset( $field_submit['middle'] ) && ! wpforms_is_empty_string( $field_submit['middle'] ) ? $field_submit['middle'] : '';
|
||||
$last = isset( $field_submit['last'] ) && ! wpforms_is_empty_string( $field_submit['last'] ) ? $field_submit['last'] : '';
|
||||
|
||||
if ( is_array( $field_submit ) ) {
|
||||
$value = implode( ' ', array_filter( [ $first, $middle, $last ] ) );
|
||||
} else {
|
||||
$value = $field_submit;
|
||||
}
|
||||
|
||||
// Set final field details.
|
||||
wpforms()->obj( 'process' )->fields[ $field_id ] = [
|
||||
'name' => sanitize_text_field( $name ),
|
||||
'value' => sanitize_text_field( $value ),
|
||||
'id' => wpforms_validate_field_id( $field_id ),
|
||||
'type' => $this->type,
|
||||
'first' => sanitize_text_field( $first ),
|
||||
'middle' => sanitize_text_field( $middle ),
|
||||
'last' => sanitize_text_field( $last ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the field requires fieldset+legend instead of the regular field label.
|
||||
*
|
||||
* @since 1.8.1
|
||||
*
|
||||
* @param bool $requires_fieldset True if it requires fieldset.
|
||||
* @param array $field Field data.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
public function is_field_requires_fieldset( $requires_fieldset, $field ) {
|
||||
|
||||
return isset( $field['format'] ) && $field['format'] !== 'simple';
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate complicated formats.
|
||||
*
|
||||
* @since 1.8.2.3
|
||||
*
|
||||
* @param WPForms_Process $process Process class instance.
|
||||
* @param int|string $form_id Form id.
|
||||
* @param int|string $field_id Field id.
|
||||
* @param array $field_submit Field submit.
|
||||
* @param string $required Required message text.
|
||||
*/
|
||||
private function validate_complicated_formats( $process, $form_id, $field_id, $field_submit, $required ) {
|
||||
|
||||
// Prevent PHP Warning: Illegal string offset ‘first’ or 'last'.
|
||||
if ( isset( $process->errors[ $form_id ][ $field_id ] ) ) {
|
||||
$process->errors[ $form_id ][ $field_id ] = (array) $process->errors[ $form_id ][ $field_id ];
|
||||
}
|
||||
|
||||
if ( isset( $field_submit['first'] ) && wpforms_is_empty_string( $field_submit['first'] ) ) {
|
||||
$process->errors[ $form_id ][ $field_id ]['first'] = $required;
|
||||
}
|
||||
|
||||
if ( isset( $field_submit['last'] ) && wpforms_is_empty_string( $field_submit['last'] ) ) {
|
||||
$process->errors[ $form_id ][ $field_id ]['last'] = $required;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new WPForms_Field_Name();
|
||||
+452
@@ -0,0 +1,452 @@
|
||||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use WPForms\Forms\Fields\Traits\NumberField as NumberFieldTrait;
|
||||
|
||||
/**
|
||||
* Number Slider field.
|
||||
*
|
||||
* @since 1.5.7
|
||||
*/
|
||||
class WPForms_Field_Number_Slider extends WPForms_Field {
|
||||
|
||||
use NumberFieldTrait;
|
||||
|
||||
/**
|
||||
* Default minimum value of the field.
|
||||
*
|
||||
* @since 1.5.7
|
||||
*/
|
||||
const SLIDER_MIN = 0;
|
||||
|
||||
/**
|
||||
* Default maximum value of the field.
|
||||
*
|
||||
* @since 1.5.7
|
||||
*/
|
||||
const SLIDER_MAX = 10;
|
||||
|
||||
/**
|
||||
* Default step value of the field.
|
||||
*
|
||||
* @since 1.5.7
|
||||
*/
|
||||
const SLIDER_STEP = 1;
|
||||
|
||||
/**
|
||||
* Primary class constructor.
|
||||
*
|
||||
* @since 1.5.7
|
||||
*/
|
||||
public function init() {
|
||||
|
||||
// Define field type information.
|
||||
$this->name = esc_html__( 'Number Slider', 'wpforms-lite' );
|
||||
$this->type = 'number-slider';
|
||||
$this->icon = 'fa-sliders';
|
||||
$this->order = 180;
|
||||
|
||||
// Customize value format for HTML emails.
|
||||
add_filter( 'wpforms_html_field_value', [ $this, 'html_email_value' ], 10, 4 );
|
||||
|
||||
// Builder strings.
|
||||
add_filter( 'wpforms_builder_strings', [ $this, 'add_builder_strings' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Builder strings.
|
||||
*
|
||||
* @since 1.6.2.3
|
||||
*
|
||||
* @param array $strings Form Builder strings.
|
||||
*
|
||||
* @return array Form Builder strings.
|
||||
*/
|
||||
public function add_builder_strings( $strings ) {
|
||||
|
||||
$strings['error_number_slider_increment'] = esc_html__( 'Increment value should be greater than zero. Decimal fractions allowed.', 'wpforms-lite' );
|
||||
|
||||
return $strings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize format for HTML email notifications.
|
||||
*
|
||||
* @since 1.5.7
|
||||
*
|
||||
* @param string $val Field value.
|
||||
* @param array $field Field settings.
|
||||
* @param array $form_data Form data and settings.
|
||||
* @param string $context Value display context.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function html_email_value( $val, $field, $form_data = [], $context = '' ) {
|
||||
|
||||
if ( empty( $field['value_raw'] ) || $field['type'] !== $this->type ) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
$value = isset( $field['value_raw']['value'] ) ? (float) $field['value_raw']['value'] : 0;
|
||||
$min = isset( $field['value_raw']['min'] ) ? (float) $field['value_raw']['min'] : self::SLIDER_MIN;
|
||||
$max = isset( $field['value_raw']['max'] ) ? (float) $field['value_raw']['max'] : self::SLIDER_MAX;
|
||||
|
||||
$html_value = $value;
|
||||
if ( strpos( $field['value_raw']['value_display'], '{value}' ) !== false ) {
|
||||
$html_value = str_replace(
|
||||
'{value}',
|
||||
/* translators: %1$s - Number slider selected value, %2$s - its minimum value, %3$s - its maximum value. */
|
||||
sprintf( esc_html__( '%1$s (%2$s min / %3$s max)', 'wpforms-lite' ), $value, $min, $max ),
|
||||
$field['value_raw']['value_display']
|
||||
);
|
||||
}
|
||||
|
||||
return $html_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Field options panel inside the builder.
|
||||
*
|
||||
* @since 1.5.7
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
*/
|
||||
public function field_options( $field ) {
|
||||
/*
|
||||
* Basic field options.
|
||||
*/
|
||||
|
||||
// Set default values for Min, Max, Step, Default Value Options.
|
||||
$field = $this->set_default_field_args( $field );
|
||||
|
||||
// Options open markup.
|
||||
$args = [
|
||||
'markup' => 'open',
|
||||
];
|
||||
|
||||
$this->field_option( 'basic-options', $field, $args );
|
||||
|
||||
// Label.
|
||||
$this->field_option( 'label', $field );
|
||||
|
||||
// Description.
|
||||
$this->field_option( 'description', $field );
|
||||
|
||||
// Required toggle disabled.
|
||||
$this->field_element(
|
||||
'text',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'required',
|
||||
'value' => '',
|
||||
'type' => 'hidden',
|
||||
]
|
||||
);
|
||||
|
||||
// Min/Max.
|
||||
$min_max_args = [
|
||||
'class' => 'wpforms-number-slider',
|
||||
'label' => esc_html__( 'Value Range', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Define the minimum and the maximum values for the slider.', 'wpforms-lite' ),
|
||||
];
|
||||
$min_max = $this->field_number_option_min_max( $field, $min_max_args, false );
|
||||
|
||||
// Default value.
|
||||
$default_value_args = [
|
||||
'class' => 'wpforms-number-slider-default-value',
|
||||
];
|
||||
$default_value = $this->field_number_option_default_value( $field, $default_value_args, false );
|
||||
|
||||
// Increment.
|
||||
$step_args = [
|
||||
'class' => 'wpforms-number-slider-step',
|
||||
'tooltip' => esc_html__( 'Determines the increment between selectable values on the slider.', 'wpforms-lite' ),
|
||||
];
|
||||
$step = $this->field_number_option_step( $field, $step_args, false );
|
||||
|
||||
// Print of options markup: Minimum, Maximum, Increment, Default Value.
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'number_min_max_step_dependent',
|
||||
'content' => $min_max . $default_value . $step,
|
||||
'class' => 'wpforms-field-number-slider-option',
|
||||
],
|
||||
true
|
||||
);
|
||||
|
||||
// Options close markup.
|
||||
$args = [
|
||||
'markup' => 'close',
|
||||
];
|
||||
|
||||
$this->field_option( 'basic-options', $field, $args );
|
||||
|
||||
/*
|
||||
* Advanced field options.
|
||||
*/
|
||||
|
||||
// Options open markup.
|
||||
$args = [
|
||||
'markup' => 'open',
|
||||
];
|
||||
|
||||
$this->field_option( 'advanced-options', $field, $args );
|
||||
|
||||
// Size.
|
||||
$this->field_option( 'size', $field );
|
||||
|
||||
// Value display.
|
||||
$lbl = $this->field_element(
|
||||
'label',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'value_display',
|
||||
'value' => esc_html__( 'Value Display', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Displays the currently selected value below the slider.', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$fld = $this->field_element(
|
||||
'text',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'value_display',
|
||||
'class' => 'wpforms-number-slider-value-display',
|
||||
'value' => isset( $field['value_display'] ) ? $field['value_display'] : $this->get_default_display_value(),
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'value_display',
|
||||
'content' => $lbl . $fld,
|
||||
]
|
||||
);
|
||||
|
||||
// Custom CSS classes.
|
||||
$this->field_option( 'css', $field );
|
||||
|
||||
// Hide label.
|
||||
$this->field_option( 'label_hide', $field );
|
||||
|
||||
// Options close markup.
|
||||
$args = [
|
||||
'markup' => 'close',
|
||||
];
|
||||
|
||||
$this->field_option( 'advanced-options', $field, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default display value.
|
||||
*
|
||||
* @since 1.7.1
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_default_display_value() {
|
||||
|
||||
return sprintf( /* translators: %s - value. */
|
||||
esc_html__( 'Selected Value: %s', 'wpforms-lite' ),
|
||||
'{value}'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Field preview inside the builder.
|
||||
*
|
||||
* @since 1.5.7
|
||||
*
|
||||
* @param array $field Field data.
|
||||
*/
|
||||
public function field_preview( $field ) {
|
||||
|
||||
// Label.
|
||||
$this->field_preview_option( 'label', $field );
|
||||
|
||||
$value_display = isset( $field['value_display'] ) ? esc_attr( $field['value_display'] ) : $this->get_default_display_value();
|
||||
$default_value = ! empty( $field['default_value'] ) ? (float) $field['default_value'] : 0;
|
||||
|
||||
echo wpforms_render( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
'fields/number-slider/builder-preview',
|
||||
[
|
||||
'min' => isset( $field['min'] ) && is_numeric( $field['min'] ) ? (float) $field['min'] : self::SLIDER_MIN,
|
||||
'max' => isset( $field['max'] ) && is_numeric( $field['max'] ) ? (float) $field['max'] : self::SLIDER_MAX,
|
||||
'step' => isset( $field['step'] ) && is_numeric( $field['step'] ) ? (float) $field['step'] : self::SLIDER_STEP,
|
||||
'value_display' => $value_display,
|
||||
'default_value' => $default_value,
|
||||
'value_hint' => str_replace( '{value}', '<b>' . $default_value . '</b>', wp_kses( $value_display, wpforms_builder_preview_get_allowed_tags() ) ),
|
||||
'field_id' => $field['id'],
|
||||
],
|
||||
true
|
||||
);
|
||||
|
||||
// Description.
|
||||
$this->field_preview_option( 'description', $field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Field display on the form front-end.
|
||||
*
|
||||
* @since 1.5.7
|
||||
*
|
||||
* @param array $field Field data and settings.
|
||||
* @param array $deprecated Deprecated field attributes. Use $field['properties'] instead.
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function field_display( $field, $deprecated, $form_data ) {
|
||||
|
||||
// Define data.
|
||||
$primary = $field['properties']['inputs']['primary'];
|
||||
|
||||
$value_display = isset( $field['value_display'] ) ? esc_attr( $field['value_display'] ) : esc_html__( 'Selected Value: {value}', 'wpforms-lite' );
|
||||
$hint_value = ! empty( $primary['attr']['value'] ) ? (float) $primary['attr']['value'] : 0;
|
||||
|
||||
$hint = str_replace( '{value}', '<b>' . $hint_value . '</b>', $value_display );
|
||||
|
||||
// phpcs:ignore
|
||||
echo wpforms_render(
|
||||
'fields/number-slider/frontend',
|
||||
[
|
||||
'atts' => $primary['attr'],
|
||||
'class' => $primary['class'],
|
||||
'datas' => $primary['data'],
|
||||
'id' => $primary['id'],
|
||||
'max' => isset( $field['max'] ) && is_numeric( $field['max'] ) ? (float) $field['max'] : self::SLIDER_MAX,
|
||||
'min' => isset( $field['min'] ) && is_numeric( $field['min'] ) ? (float) $field['min'] : self::SLIDER_MIN,
|
||||
'required' => $primary['required'],
|
||||
'step' => isset( $field['step'] ) && is_numeric( $field['step'] ) ? (float) $field['step'] : self::SLIDER_STEP,
|
||||
'value_display' => $value_display,
|
||||
'value_hint' => $hint,
|
||||
],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate field on form submit.
|
||||
*
|
||||
* @since 1.5.7
|
||||
*
|
||||
* @param int $field_id Field ID.
|
||||
* @param int|float|string $field_submit Submitted field value (raw data).
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function validate( $field_id, $field_submit, $form_data ) {
|
||||
|
||||
$form_id = $form_data['id'];
|
||||
|
||||
$field_submit = (float) $this->sanitize_value( $field_submit );
|
||||
|
||||
// Basic required check - if field is marked as required, check for entry data.
|
||||
if (
|
||||
! empty( $form_data['fields'][ $field_id ]['required'] ) &&
|
||||
empty( $field_submit ) &&
|
||||
(string) $field_submit !== '0'
|
||||
) {
|
||||
wpforms()->obj( 'process' )->errors[ $form_id ][ $field_id ] = wpforms_get_required_label();
|
||||
}
|
||||
|
||||
// Check if value is numeric.
|
||||
if ( ! empty( $field_submit ) && ! is_numeric( $field_submit ) ) {
|
||||
/**
|
||||
* Filter the error message for the number field.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $message Error message.
|
||||
*/
|
||||
wpforms()->obj( 'process' )->errors[ $form_id ][ $field_id ] = apply_filters( 'wpforms_valid_number_label', esc_html__( 'Please provide a valid value.', 'wpforms-lite' ) ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and sanitize field.
|
||||
*
|
||||
* @since 1.5.7
|
||||
*
|
||||
* @param int $field_id Field ID.
|
||||
* @param int|string|float $field_submit Submitted field value.
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function format( $field_id, $field_submit, $form_data ) {
|
||||
|
||||
// Define data.
|
||||
$name = ! empty( $form_data['fields'][ $field_id ]['label'] ) ? $form_data['fields'][ $field_id ]['label'] : '';
|
||||
$value = (float) $this->sanitize_value( $field_submit );
|
||||
|
||||
$value_raw = [
|
||||
'value' => $value,
|
||||
'min' => (float) $form_data['fields'][ $field_id ]['min'],
|
||||
'max' => (float) $form_data['fields'][ $field_id ]['max'],
|
||||
'value_display' => wp_kses_post( $form_data['fields'][ $field_id ]['value_display'] ),
|
||||
];
|
||||
|
||||
// Set final field details.
|
||||
wpforms()->obj( 'process' )->fields[ $field_id ] = [
|
||||
'name' => sanitize_text_field( $name ),
|
||||
'value' => $value,
|
||||
'value_raw' => $value_raw,
|
||||
'id' => wpforms_validate_field_id( $field_id ),
|
||||
'type' => $this->type,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize the value.
|
||||
*
|
||||
* @since 1.5.7
|
||||
*
|
||||
* @param string $value The number field submitted value.
|
||||
*
|
||||
* @return float|int|string
|
||||
*/
|
||||
private function sanitize_value( $value ) {
|
||||
|
||||
// Some browsers allow other non-digit/decimal characters to be submitted
|
||||
// with the num input, which then trips the is_numeric validation below.
|
||||
// To get around this we remove all chars that are not expected.
|
||||
$signed_value = preg_replace( '/[^-0-9.]/', '', $value );
|
||||
|
||||
// If there's no number on the signed value we return zero.
|
||||
// We have to do that because since PHP 8.0, the abs() function is allowed an argument with int|float type.
|
||||
if ( ! is_numeric( $signed_value ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$abs_value = abs( $signed_value );
|
||||
$value = strpos( $signed_value, '-' ) === 0 ? '-' . $abs_value : $abs_value;
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default field settings.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
*
|
||||
* @return array Modified array.
|
||||
*/
|
||||
private function set_default_field_args( $field ) {
|
||||
|
||||
$field['min'] = empty( $field['min'] ) ? self::SLIDER_MIN : $field['min'];
|
||||
$field['max'] = empty( $field['max'] ) ? self::SLIDER_MAX : $field['max'];
|
||||
$field['step'] = empty( $field['step'] ) ? self::SLIDER_STEP : $field['step'];
|
||||
$field['default_value'] = empty( $field['default_value'] ) ? self::SLIDER_MIN : $field['default_value'];
|
||||
|
||||
return $field;
|
||||
}
|
||||
}
|
||||
|
||||
new WPForms_Field_Number_Slider();
|
||||
+278
@@ -0,0 +1,278 @@
|
||||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use WPForms\Forms\Fields\Traits\NumberField as NumberFieldTrait;
|
||||
|
||||
/**
|
||||
* Number text field.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class WPForms_Field_Number extends WPForms_Field {
|
||||
|
||||
use NumberFieldTrait;
|
||||
|
||||
/**
|
||||
* Primary class constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function init() {
|
||||
|
||||
// Define field type information.
|
||||
$this->name = esc_html__( 'Numbers', 'wpforms-lite' );
|
||||
$this->type = 'number';
|
||||
$this->icon = 'fa-hashtag';
|
||||
$this->order = 130;
|
||||
|
||||
$this->hooks();
|
||||
$this->number_hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*/
|
||||
private function hooks() {
|
||||
|
||||
// Define additional field properties.
|
||||
add_filter( 'wpforms_field_properties_number', [ $this, 'field_properties' ], 5, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Define additional field properties.
|
||||
*
|
||||
* @since 1.9.4
|
||||
*
|
||||
* @param array|mixed $properties Field properties.
|
||||
* @param array $field Field settings.
|
||||
* @param array $form_data Form data and settings.
|
||||
*
|
||||
* @return array
|
||||
* @noinspection PhpMissingParamTypeInspection
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
public function field_properties( $properties, $field, $form_data ): array {
|
||||
|
||||
$properties = (array) $properties;
|
||||
|
||||
if ( is_numeric( $field['min'] ?? null ) ) {
|
||||
$properties['inputs']['primary']['attr']['min'] = (float) $field['min'];
|
||||
}
|
||||
|
||||
if ( is_numeric( $field['max'] ?? null ) ) {
|
||||
$properties['inputs']['primary']['attr']['max'] = (float) $field['max'];
|
||||
}
|
||||
|
||||
$properties['inputs']['primary']['attr']['step'] = 'any';
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Field options panel inside the builder.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field data.
|
||||
*/
|
||||
public function field_options( $field ) {
|
||||
/*
|
||||
* Basic field options.
|
||||
*/
|
||||
|
||||
// Options open markup.
|
||||
$args = [
|
||||
'markup' => 'open',
|
||||
];
|
||||
|
||||
$this->field_option( 'basic-options', $field, $args );
|
||||
|
||||
// Label.
|
||||
$this->field_option( 'label', $field );
|
||||
|
||||
// Description.
|
||||
$this->field_option( 'description', $field );
|
||||
|
||||
// Required toggle.
|
||||
$this->field_option( 'required', $field );
|
||||
|
||||
// Options close markup.
|
||||
$args = [
|
||||
'markup' => 'close',
|
||||
];
|
||||
|
||||
$this->field_option( 'basic-options', $field, $args );
|
||||
|
||||
/*
|
||||
* Advanced field options.
|
||||
*/
|
||||
|
||||
// Options open markup.
|
||||
$args = [
|
||||
'markup' => 'open',
|
||||
];
|
||||
|
||||
$this->field_option( 'advanced-options', $field, $args );
|
||||
|
||||
// Size.
|
||||
$this->field_option( 'size', $field );
|
||||
|
||||
// Placeholder.
|
||||
$this->field_option( 'placeholder', $field );
|
||||
|
||||
// Min/Max.
|
||||
$this->field_number_option_min_max( $field, [ 'class' => 'wpforms-numbers' ] );
|
||||
|
||||
// Default value.
|
||||
$this->field_option( 'default_value', $field );
|
||||
|
||||
// Custom CSS classes.
|
||||
$this->field_option( 'css', $field );
|
||||
|
||||
// Hide label.
|
||||
$this->field_option( 'label_hide', $field );
|
||||
|
||||
// Options close markup.
|
||||
$args = [
|
||||
'markup' => 'close',
|
||||
];
|
||||
|
||||
$this->field_option( 'advanced-options', $field, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Field preview inside the builder.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field data.
|
||||
*/
|
||||
public function field_preview( $field ) {
|
||||
|
||||
// Define data.
|
||||
$placeholder = ! empty( $field['placeholder'] ) ? $field['placeholder'] : '';
|
||||
$default_value = ! empty( $field['default_value'] ) ? $field['default_value'] : '';
|
||||
|
||||
// Label.
|
||||
$this->field_preview_option( 'label', $field );
|
||||
|
||||
// Primary input.
|
||||
echo '<input type="text" placeholder="' . esc_attr( $placeholder ) . '" value="' . esc_attr( $default_value ) . '" class="primary-input" readonly>';
|
||||
|
||||
// Description.
|
||||
$this->field_preview_option( 'description', $field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Field display on the form front-end.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field data.
|
||||
* @param array $deprecated Deprecated, not used.
|
||||
* @param array $form_data Form data.
|
||||
*/
|
||||
public function field_display( $field, $deprecated, $form_data ) {
|
||||
|
||||
// Define data.
|
||||
$primary = $field['properties']['inputs']['primary'];
|
||||
|
||||
// Primary field.
|
||||
printf(
|
||||
'<input type="number" %s %s>',
|
||||
wpforms_html_attributes( $primary['id'], $primary['class'], $primary['data'], $primary['attr'] ),
|
||||
esc_attr( $primary['required'] )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate field on form submit.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param int $field_id Field id.
|
||||
* @param string $field_submit Submitted field value (raw data).
|
||||
* @param array $form_data Form data.
|
||||
*/
|
||||
public function validate( $field_id, $field_submit, $form_data ) {
|
||||
|
||||
$form_id = $form_data['id'];
|
||||
|
||||
$value = $this->sanitize_value( $field_submit );
|
||||
|
||||
// If field is marked as required, check for entry data.
|
||||
if (
|
||||
! empty( $form_data['fields'][ $field_id ]['required'] ) &&
|
||||
empty( $value ) &&
|
||||
! is_numeric( $value )
|
||||
) {
|
||||
wpforms()->obj( 'process' )->errors[ $form_id ][ $field_id ] = wpforms_get_required_label();
|
||||
}
|
||||
|
||||
// Check if value is numeric.
|
||||
if ( ! empty( $value ) && ! is_numeric( $value ) ) {
|
||||
/**
|
||||
* Filter the error message for the number field.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $message Error message.
|
||||
*/
|
||||
wpforms()->obj( 'process' )->errors[ $form_id ][ $field_id ] = apply_filters( 'wpforms_valid_number_label', esc_html__( 'Please enter a valid number.', 'wpforms-lite' ) ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and sanitize field.
|
||||
*
|
||||
* @since 1.3.5
|
||||
*
|
||||
* @param int $field_id Field id.
|
||||
* @param string $field_submit Submitted value.
|
||||
* @param array $form_data Form data.
|
||||
*/
|
||||
public function format( $field_id, $field_submit, $form_data ) {
|
||||
|
||||
// Define data.
|
||||
$name = ! empty( $form_data['fields'][ $field_id ]['label'] ) ? $form_data['fields'][ $field_id ]['label'] : '';
|
||||
|
||||
// Set final field details.
|
||||
wpforms()->obj( 'process' )->fields[ $field_id ] = [
|
||||
'name' => sanitize_text_field( $name ),
|
||||
'value' => $this->sanitize_value( $field_submit ),
|
||||
'id' => wpforms_validate_field_id( $field_id ),
|
||||
'type' => $this->type,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize the value.
|
||||
*
|
||||
* @since 1.5.7
|
||||
*
|
||||
* @param string $value The number field submitted value.
|
||||
*
|
||||
* @return float|int|string
|
||||
*/
|
||||
private function sanitize_value( $value ) {
|
||||
|
||||
if ( empty( $value ) && ! is_numeric( $value ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Some browsers allow other non-digit/decimal characters to be submitted
|
||||
// with the num input, which then trips the is_numeric validation below.
|
||||
// To get around this we remove all chars that are not expected.
|
||||
$signed_value = preg_replace( '/[^-0-9.]/', '', $value );
|
||||
$abs_value = str_replace( '-', '', $signed_value );
|
||||
|
||||
return $signed_value < 0 ? '-' . $abs_value : $abs_value;
|
||||
}
|
||||
}
|
||||
|
||||
new WPForms_Field_Number();
|
||||
+944
@@ -0,0 +1,944 @@
|
||||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiple Choice field.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class WPForms_Field_Radio extends WPForms_Field {
|
||||
|
||||
/**
|
||||
* Primary class constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function init() {
|
||||
|
||||
// Define field type information.
|
||||
$this->name = esc_html__( 'Multiple Choice', 'wpforms-lite' );
|
||||
$this->keywords = esc_html__( 'radio', 'wpforms-lite' );
|
||||
$this->type = 'radio';
|
||||
$this->icon = 'fa-dot-circle-o';
|
||||
$this->order = 110;
|
||||
$this->defaults = [
|
||||
1 => [
|
||||
'label' => esc_html__( 'First Choice', 'wpforms-lite' ),
|
||||
'value' => '',
|
||||
'image' => '',
|
||||
'icon' => '',
|
||||
'icon_style' => '',
|
||||
'default' => '',
|
||||
],
|
||||
2 => [
|
||||
'label' => esc_html__( 'Second Choice', 'wpforms-lite' ),
|
||||
'value' => '',
|
||||
'image' => '',
|
||||
'icon' => '',
|
||||
'icon_style' => '',
|
||||
'default' => '',
|
||||
],
|
||||
3 => [
|
||||
'label' => esc_html__( 'Third Choice', 'wpforms-lite' ),
|
||||
'value' => '',
|
||||
'image' => '',
|
||||
'icon' => '',
|
||||
'icon_style' => '',
|
||||
'default' => '',
|
||||
],
|
||||
];
|
||||
|
||||
$this->default_settings = [
|
||||
'choices' => $this->defaults,
|
||||
];
|
||||
|
||||
$this->hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks.
|
||||
*
|
||||
* @since 1.8.1
|
||||
*/
|
||||
private function hooks() {
|
||||
|
||||
// Customize HTML field values.
|
||||
add_filter( 'wpforms_html_field_value', [ $this, 'field_html_value' ], 10, 4 );
|
||||
add_filter( "wpforms_{$this->type}_field_html_value_images", [ $this, 'field_html_value_images' ], 10, 3 );
|
||||
|
||||
// Define additional field properties.
|
||||
add_filter( 'wpforms_field_properties_radio', [ $this, 'field_properties' ], 5, 3 );
|
||||
|
||||
// This field requires fieldset+legend instead of the field label.
|
||||
add_filter( "wpforms_frontend_modern_is_field_requires_fieldset_{$this->type}", '__return_true', PHP_INT_MAX, 2 );
|
||||
|
||||
// Load assets.
|
||||
add_action( 'wpforms_builder_enqueues', [ $this, 'builder_assets' ] );
|
||||
|
||||
// Modify an export data format for the Other option.
|
||||
add_filter( 'wpforms_pro_admin_entries_export_ajax_get_entry_fields_data_field', [ $this, 'export_entry_field_data' ] );
|
||||
|
||||
// Allow radio fields to be included in the Keyword Filter.
|
||||
add_filter( 'wpforms_pro_anti_spam_keyword_filter_get_filtered_fields', [ $this, 'add_field_to_anti_spam_keyword_filter' ] );
|
||||
|
||||
// Adjust entry field before saving to entry_fields DB table.
|
||||
add_filter( 'wpforms_entry_save_fields', [ $this, 'save_field' ], 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue assets for the builder.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*/
|
||||
public function builder_assets() {
|
||||
|
||||
$min = wpforms_get_min_suffix();
|
||||
|
||||
wp_enqueue_script(
|
||||
'wpforms-multiple-choices',
|
||||
WPFORMS_PLUGIN_URL . "assets/js/admin/builder/multiple-choices{$min}.js",
|
||||
[ 'jquery', 'wpforms-builder' ],
|
||||
WPFORMS_VERSION,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust builder preview container classes.
|
||||
*
|
||||
* Adds size-{small|medium|large} to the Radio field container in the Builder
|
||||
* when the "Add Other Choice" option is enabled.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @param string $css Existing class string.
|
||||
* @param array $field Field data and settings.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function preview_field_class( $css, $field ): string {
|
||||
|
||||
$css = parent::preview_field_class( $css, $field );
|
||||
|
||||
if ( $field['type'] !== $this->type ) {
|
||||
return $css;
|
||||
}
|
||||
|
||||
// Apply a size class to the field container when Other Choice is enabled.
|
||||
if ( $this->has_other_choice( $field ) ) {
|
||||
$size = ! empty( $field['other_size'] ) ? sanitize_html_class( $field['other_size'] ) : 'medium';
|
||||
$css .= ' size-' . $size;
|
||||
}
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define additional field properties.
|
||||
*
|
||||
* @since 1.4.5
|
||||
*
|
||||
* @param array $properties Field properties.
|
||||
* @param array $field Field settings.
|
||||
* @param array $form_data Form data and settings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function field_properties( $properties, $field, $form_data ) {
|
||||
|
||||
// Remove primary input, unset for attribute for label.
|
||||
unset( $properties['inputs']['primary'], $properties['label']['attr']['for'] );
|
||||
|
||||
// Define data.
|
||||
$form_id = absint( $form_data['id'] );
|
||||
$field_id = wpforms_validate_field_id( $field['id'] );
|
||||
$choices = $field['choices'];
|
||||
$dynamic = wpforms_get_field_dynamic_choices( $field, $form_id, $form_data );
|
||||
|
||||
if ( $dynamic !== false ) {
|
||||
$choices = $dynamic;
|
||||
$field['show_values'] = true;
|
||||
}
|
||||
|
||||
// Set input container (ul) properties.
|
||||
$properties['input_container'] = [
|
||||
'class' => [ ! empty( $field['random'] ) ? 'wpforms-randomize' : '' ],
|
||||
'data' => [],
|
||||
'attr' => [],
|
||||
'id' => "wpforms-{$form_id}-field_{$field_id}",
|
||||
];
|
||||
|
||||
// Set input properties.
|
||||
foreach ( $choices as $key => $choice ) {
|
||||
|
||||
// Used for dynamic choices.
|
||||
$depth = isset( $choice['depth'] ) ? absint( $choice['depth'] ) : 1;
|
||||
|
||||
$value = ! empty( $field['show_values'] ) ? $choice['value'] : $choice['label'];
|
||||
/* translators: %s - choice number. */
|
||||
$value = ( $value === '' ) ? sprintf( esc_html__( 'Choice %s', 'wpforms-lite' ), $key ) : $value;
|
||||
|
||||
// Check if this is the "Other" choice.
|
||||
$is_other_choice = isset( $choice['other'] ) && (bool) $choice['other'] === true;
|
||||
|
||||
$properties['inputs'][ $key ] = [
|
||||
'container' => [
|
||||
'attr' => [],
|
||||
'class' => [ "choice-{$key}", "depth-{$depth}", $is_other_choice ? 'wpforms-other-choice' : '' ],
|
||||
'data' => [],
|
||||
'id' => '',
|
||||
],
|
||||
'label' => [
|
||||
'attr' => [
|
||||
'for' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
|
||||
],
|
||||
'class' => [ 'wpforms-field-label-inline' ],
|
||||
'data' => [],
|
||||
'id' => '',
|
||||
'text' => $choice['label'],
|
||||
],
|
||||
'attr' => [
|
||||
'name' => "wpforms[fields][{$field_id}]",
|
||||
'value' => $value,
|
||||
],
|
||||
'class' => [],
|
||||
'data' => $is_other_choice ? [ 'other-choice' => 'true' ] : [],
|
||||
'id' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
|
||||
'icon' => isset( $choice['icon'] ) ? $choice['icon'] : '',
|
||||
'icon_style' => isset( $choice['icon_style'] ) ? $choice['icon_style'] : '',
|
||||
'image' => isset( $choice['image'] ) ? $choice['image'] : '',
|
||||
'required' => ! empty( $field['required'] ) ? 'required' : '',
|
||||
'default' => isset( $choice['default'] ),
|
||||
];
|
||||
}
|
||||
|
||||
// Required class for pagebreak validation.
|
||||
if ( ! empty( $field['required'] ) ) {
|
||||
$properties['input_container']['class'][] = 'wpforms-field-required';
|
||||
}
|
||||
|
||||
// Custom properties if image choices is enabled.
|
||||
if ( ! $dynamic && ! empty( $field['choices_images'] ) ) {
|
||||
|
||||
$properties['input_container']['class'][] = 'wpforms-image-choices';
|
||||
$properties['input_container']['class'][] = 'wpforms-image-choices-' . sanitize_html_class( $field['choices_images_style'] );
|
||||
|
||||
foreach ( $properties['inputs'] as $key => $inputs ) {
|
||||
$properties['inputs'][ $key ]['container']['class'][] = 'wpforms-image-choices-item';
|
||||
|
||||
if ( in_array( $field['choices_images_style'], [ 'modern', 'classic' ], true ) ) {
|
||||
$properties['inputs'][ $key ]['class'][] = 'wpforms-screen-reader-element';
|
||||
}
|
||||
}
|
||||
} elseif ( ! $dynamic && ! empty( $field['choices_icons'] ) ) {
|
||||
$properties = wpforms()->obj( 'icon_choices' )->field_properties( $properties, $field );
|
||||
}
|
||||
|
||||
// Add selected class for choices with defaults.
|
||||
foreach ( $properties['inputs'] as $key => $inputs ) {
|
||||
if ( ! empty( $inputs['default'] ) ) {
|
||||
$properties['inputs'][ $key ]['container']['class'][] = 'wpforms-selected';
|
||||
}
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Field options panel inside the builder.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
*/
|
||||
public function field_options( $field ) {
|
||||
/*
|
||||
* Basic field options.
|
||||
*/
|
||||
|
||||
// Options open markup.
|
||||
$this->field_option(
|
||||
'basic-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'open',
|
||||
]
|
||||
);
|
||||
|
||||
// Label.
|
||||
$this->field_option( 'label', $field );
|
||||
|
||||
// Choices.
|
||||
$this->field_option( 'choices', $field );
|
||||
|
||||
// AI Feature.
|
||||
$this->field_option(
|
||||
'ai_modal_button',
|
||||
$field,
|
||||
[
|
||||
'value' => esc_html__( 'Generate Choices', 'wpforms-lite' ),
|
||||
'type' => 'choices',
|
||||
]
|
||||
);
|
||||
|
||||
// Add Other Choice.
|
||||
$this->field_option( 'choices_other', $field );
|
||||
|
||||
// Other Field Size for "Other" input.
|
||||
$this->field_option( 'other_size', $field );
|
||||
|
||||
// Other Placeholder for "Other" input.
|
||||
$this->field_option( 'other_placeholder', $field );
|
||||
|
||||
// Choices Images.
|
||||
$this->field_option( 'choices_images', $field );
|
||||
|
||||
// Hide Choices Images.
|
||||
$this->field_option( 'choices_images_hide', $field );
|
||||
|
||||
// Choices Images Style (theme).
|
||||
$this->field_option( 'choices_images_style', $field );
|
||||
|
||||
// Choices Icons.
|
||||
$this->field_option( 'choices_icons', $field );
|
||||
|
||||
// Choices Icons Color.
|
||||
$this->field_option( 'choices_icons_color', $field );
|
||||
|
||||
// Choices Icons Size.
|
||||
$this->field_option( 'choices_icons_size', $field );
|
||||
|
||||
// Choices Icons Style.
|
||||
$this->field_option( 'choices_icons_style', $field );
|
||||
|
||||
// Description.
|
||||
$this->field_option( 'description', $field );
|
||||
|
||||
// Required toggle.
|
||||
$this->field_option( 'required', $field );
|
||||
|
||||
// Options close markup.
|
||||
$this->field_option(
|
||||
'basic-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'close',
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
* Advanced field options.
|
||||
*/
|
||||
|
||||
// Options open markup.
|
||||
$this->field_option(
|
||||
'advanced-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'open',
|
||||
]
|
||||
);
|
||||
|
||||
// Randomize order of choices.
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'random',
|
||||
'content' => $this->field_element(
|
||||
'toggle',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'random',
|
||||
'value' => isset( $field['random'] ) ? '1' : '0',
|
||||
'desc' => esc_html__( 'Randomize Choices', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Check this option to randomize the order of the choices.', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
// Show Values toggle option. This option will only show if already used
|
||||
// or if manually enabled by a filter.
|
||||
if ( ! empty( $field['show_values'] ) || wpforms_show_fields_options_setting() ) {
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'show_values',
|
||||
'content' => $this->field_element(
|
||||
'toggle',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'show_values',
|
||||
'value' => isset( $field['show_values'] ) ? $field['show_values'] : '0',
|
||||
'desc' => esc_html__( 'Show Values', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Check this option to manually set form field values.', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Display format.
|
||||
$this->field_option( 'input_columns', $field );
|
||||
|
||||
// Dynamic choice auto-populating toggle.
|
||||
$this->field_option( 'dynamic_choices', $field );
|
||||
|
||||
// Dynamic choice source.
|
||||
$this->field_option( 'dynamic_choices_source', $field );
|
||||
|
||||
// Custom CSS classes.
|
||||
$this->field_option( 'css', $field );
|
||||
|
||||
// Hide label.
|
||||
$this->field_option( 'label_hide', $field );
|
||||
|
||||
// Options close markup.
|
||||
$this->field_option(
|
||||
'advanced-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'close',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Field preview inside the builder.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
*/
|
||||
public function field_preview( $field ) {
|
||||
|
||||
// Label.
|
||||
$this->field_preview_option( 'label', $field );
|
||||
|
||||
// Choices.
|
||||
$this->field_preview_option( 'choices', $field );
|
||||
|
||||
// Description.
|
||||
$this->field_preview_option( 'description', $field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Field display on the form front-end and admin entry edit page.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
* @param array $deprecated Deprecated array.
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function field_display( $field, $deprecated, $form_data ) {
|
||||
|
||||
$using_image_choices = empty( $field['dynamic_choices'] ) && empty( $field['choices_icons'] ) && ! empty( $field['choices_images'] );
|
||||
$using_icon_choices = empty( $field['dynamic_choices'] ) && empty( $field['choices_images'] ) && ! empty( $field['choices_icons'] );
|
||||
|
||||
// Define data.
|
||||
$container = $field['properties']['input_container'];
|
||||
$choices = $field['properties']['inputs'];
|
||||
|
||||
// Do not display the field with empty choices on the frontend.
|
||||
if ( ! $choices && ! is_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Display a warning message on Entry Edit page.
|
||||
if ( ! $choices && is_admin() ) {
|
||||
$this->display_empty_dynamic_choices_message( $field );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$amp_state_id = '';
|
||||
|
||||
if ( wpforms_is_amp() && ( $using_image_choices || $using_icon_choices ) ) {
|
||||
$amp_state_id = str_replace( '-', '_', sanitize_key( $container['id'] ) ) . '_state';
|
||||
$state = [
|
||||
'selected' => null,
|
||||
];
|
||||
|
||||
foreach ( $choices as $key => $choice ) {
|
||||
if ( $choice['default'] ) {
|
||||
$state['selected'] = $choice['attr']['value'];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf(
|
||||
'<amp-state id="%s"><script type="application/json">%s</script></amp-state>',
|
||||
esc_attr( $amp_state_id ),
|
||||
wp_json_encode( $state )
|
||||
);
|
||||
}
|
||||
|
||||
printf(
|
||||
'<ul %s>',
|
||||
wpforms_html_attributes( $container['id'], $container['class'], $container['data'], $container['attr'] )
|
||||
);
|
||||
|
||||
foreach ( $choices as $key => $choice ) {
|
||||
$label = $this->get_choices_label( $choice['label']['text'] ?? '', $key, $field );
|
||||
|
||||
if ( wpforms_is_amp() && ( $using_image_choices || $using_icon_choices ) ) {
|
||||
$choice['container']['attr']['[class]'] = sprintf(
|
||||
'%s + ( %s == %s ? " wpforms-selected" : "")',
|
||||
wp_json_encode( implode( ' ', $choice['container']['class'] ) ),
|
||||
$amp_state_id,
|
||||
wp_json_encode( $choice['attr']['value'] )
|
||||
);
|
||||
}
|
||||
|
||||
printf(
|
||||
'<li %s>',
|
||||
wpforms_html_attributes( $choice['container']['id'], $choice['container']['class'], $choice['container']['data'], $choice['container']['attr'] )
|
||||
);
|
||||
|
||||
if ( $using_image_choices ) {
|
||||
|
||||
// Make sure the image choices are keyboard-accessible.
|
||||
$choice['label']['attr']['tabindex'] = 0;
|
||||
|
||||
if ( wpforms_is_amp() ) {
|
||||
$choice['label']['attr']['on'] = sprintf(
|
||||
'tap:AMP.setState(%s)',
|
||||
wp_json_encode( [ $amp_state_id => $choice['attr']['value'] ] )
|
||||
);
|
||||
$choice['label']['attr']['role'] = 'button';
|
||||
}
|
||||
|
||||
if ( is_array( $choice['label']['class'] ) && wpforms_is_empty_string( $label ) ) {
|
||||
$choice['label']['class'][] = 'wpforms-field-label-inline-empty';
|
||||
}
|
||||
|
||||
// Image choices.
|
||||
printf(
|
||||
'<label %s>',
|
||||
wpforms_html_attributes( $choice['label']['id'], $choice['label']['class'], $choice['label']['data'], $choice['label']['attr'] )
|
||||
);
|
||||
|
||||
echo '<span class="wpforms-image-choices-image">';
|
||||
|
||||
if ( ! empty( $choice['image'] ) ) {
|
||||
printf(
|
||||
'<img src="%s" alt="%s"%s>',
|
||||
esc_url( $choice['image'] ),
|
||||
esc_attr( $label ),
|
||||
! empty( $label ) ? ' title="' . esc_attr( $label ) . '"' : ''
|
||||
);
|
||||
}
|
||||
|
||||
echo '</span>';
|
||||
|
||||
if ( $field['choices_images_style'] === 'none' ) {
|
||||
echo '<br>';
|
||||
}
|
||||
|
||||
$choice['attr']['tabindex'] = '-1';
|
||||
|
||||
if ( wpforms_is_amp() ) {
|
||||
$choice['attr']['[checked]'] = sprintf(
|
||||
'%s == %s',
|
||||
$amp_state_id,
|
||||
wp_json_encode( $choice['attr']['value'] )
|
||||
);
|
||||
}
|
||||
|
||||
printf(
|
||||
'<input type="radio" %s %s %s>',
|
||||
wpforms_html_attributes( $choice['id'], $choice['class'], $choice['data'], $choice['attr'] ),
|
||||
esc_attr( $choice['required'] ),
|
||||
checked( '1', $choice['default'], false )
|
||||
);
|
||||
|
||||
echo '<span class="wpforms-image-choices-label">' . wp_kses_post( $choice['label']['text'] ) . '</span>';
|
||||
|
||||
echo '</label>';
|
||||
|
||||
} elseif ( $using_icon_choices ) {
|
||||
|
||||
if ( wpforms_is_amp() ) {
|
||||
$choice['label']['attr']['on'] = sprintf(
|
||||
'tap:AMP.setState(%s)',
|
||||
wp_json_encode( [ $amp_state_id => $choice['attr']['value'] ] )
|
||||
);
|
||||
$choice['label']['attr']['role'] = 'button';
|
||||
}
|
||||
|
||||
// Icon Choices.
|
||||
wpforms()->obj( 'icon_choices' )->field_display( $field, $choice, 'radio' );
|
||||
|
||||
} else {
|
||||
// Normal display.
|
||||
printf(
|
||||
'<input type="radio" %s %s %s>',
|
||||
wpforms_html_attributes( $choice['id'], $choice['class'], $choice['data'], $choice['attr'] ),
|
||||
esc_attr( $choice['required'] ),
|
||||
checked( '1', $choice['default'], false )
|
||||
);
|
||||
|
||||
printf(
|
||||
'<label %s>%s</label>',
|
||||
wpforms_html_attributes( $choice['label']['id'], $choice['label']['class'], $choice['label']['data'], $choice['label']['attr'] ),
|
||||
wp_kses_post( $label )
|
||||
);
|
||||
}
|
||||
|
||||
// Capture the text field for "Other" choice to render separately below the list.
|
||||
if ( ! empty( $choice['data']['other-choice'] ) ) {
|
||||
$default_value = '';
|
||||
$size = ! empty( $field['other_size'] ) ? sanitize_html_class( $field['other_size'] ) : 'medium';
|
||||
$size_class = 'wpforms-field-' . $size;
|
||||
|
||||
// Do not hide the Other input if this choice is set as default.
|
||||
$hidden_class = ! empty( $choice['default'] ) ? '' : ' wpforms-hidden';
|
||||
|
||||
if ( isset( $field['choices'][ $key ]['value'] ) && $field['choices'][ $key ]['value'] !== '' ) {
|
||||
$default_value = $field['choices'][ $key ]['value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the default value of the Other choice field option.
|
||||
*
|
||||
* This filter allows modifying what value should be prefilled for the Other choice inputs.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @param string $default_value Default value for the "Other" choice input.
|
||||
* @param array $field Field data and settings.
|
||||
* @param string $label Field label.
|
||||
*/
|
||||
$default_value = apply_filters( 'wpforms_field_radio_other_choice_default_value', $default_value, $field, $label );
|
||||
|
||||
$other_atts = [
|
||||
'name' => "wpforms[fields][{$field['id']}][other]",
|
||||
'value' => $default_value,
|
||||
];
|
||||
|
||||
if ( empty( $choice['default'] ) ) {
|
||||
$other_atts['disabled'] = 'disabled';
|
||||
}
|
||||
|
||||
if ( ! empty( $field['other_placeholder'] ) ) {
|
||||
$other_atts['placeholder'] = $field['other_placeholder'];
|
||||
}
|
||||
|
||||
$other_input_html = sprintf(
|
||||
'<input type="text" %s required>',
|
||||
wpforms_html_attributes(
|
||||
"wpforms-{$form_data['id']}-field_{$field['id']}_other",
|
||||
[ 'wpforms-other-input', 'wpforms-field-required', $size_class, $hidden_class ],
|
||||
[],
|
||||
$other_atts
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
echo '</li>';
|
||||
}
|
||||
|
||||
echo '</ul>';
|
||||
// Render the captured "Other" input separately, under the list of options.
|
||||
if ( ! empty( $other_input_html ) ) {
|
||||
echo $other_input_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate field.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @param int $field_id Field ID.
|
||||
* @param string|array $field_submit Submitted field value (raw data).
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function validate( $field_id, $field_submit, $form_data ) {
|
||||
|
||||
$field = $form_data['fields'][ $field_id ];
|
||||
|
||||
// Skip validation if field is dynamic and choices are empty.
|
||||
if ( $this->is_dynamic_choices_empty( $field, $form_data ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent::validate( $field_id, $field_submit, $form_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and sanitize field.
|
||||
*
|
||||
* @since 1.0.2
|
||||
* @since 1.9.8.3 Changed the expected $field_submit from string to mixed as in case with the Other option we can expect the array to arrive here.
|
||||
*
|
||||
* @param int $field_id Field ID.
|
||||
* @param mixed $field_submit Submitted form data.
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function format( $field_id, $field_submit, $form_data ) {
|
||||
|
||||
$field = $form_data['fields'][ $field_id ];
|
||||
$dynamic = ! empty( $field['dynamic_choices'] ) ? $field['dynamic_choices'] : false;
|
||||
$name = sanitize_text_field( $field['label'] );
|
||||
$value_raw = sanitize_text_field( $field_submit );
|
||||
|
||||
$data = [
|
||||
'name' => $name,
|
||||
'value' => '',
|
||||
'value_raw' => $value_raw,
|
||||
'id' => wpforms_validate_field_id( $field_id ),
|
||||
'type' => $this->type,
|
||||
];
|
||||
|
||||
if ( 'post_type' === $dynamic && ! empty( $field['dynamic_post_type'] ) ) {
|
||||
|
||||
// Dynamic population is enabled using post type.
|
||||
$data['dynamic'] = 'post_type';
|
||||
$data['dynamic_items'] = absint( $value_raw );
|
||||
$data['dynamic_post_type'] = $field['dynamic_post_type'];
|
||||
$post = get_post( $value_raw );
|
||||
|
||||
if ( ! empty( $post ) && ! is_wp_error( $post ) && $data['dynamic_post_type'] === $post->post_type ) {
|
||||
$data['value'] = esc_html( wpforms_get_post_title( $post ) );
|
||||
}
|
||||
} elseif ( 'taxonomy' === $dynamic && ! empty( $field['dynamic_taxonomy'] ) ) {
|
||||
|
||||
// Dynamic population is enabled using taxonomy.
|
||||
$data['dynamic'] = 'taxonomy';
|
||||
$data['dynamic_items'] = absint( $value_raw );
|
||||
$data['dynamic_taxonomy'] = $field['dynamic_taxonomy'];
|
||||
$term = get_term( $value_raw, $data['dynamic_taxonomy'] );
|
||||
|
||||
if ( ! empty( $term ) && ! is_wp_error( $term ) ) {
|
||||
$data['value'] = esc_html( wpforms_get_term_name( $term ) );
|
||||
}
|
||||
} else {
|
||||
|
||||
// Normal processing, dynamic population is off.
|
||||
$choice_key = '';
|
||||
|
||||
// If show_values is true, that means value posted is the raw value
|
||||
// and not the label. So we need to set label value. Also store
|
||||
// the choice key.
|
||||
if ( ! empty( $field['show_values'] ) ) {
|
||||
foreach ( $field['choices'] as $key => $choice ) {
|
||||
if ( ! empty( $field_submit ) && $choice['value'] === $field_submit ) {
|
||||
$data['value'] = sanitize_text_field( $choice['label'] );
|
||||
$choice_key = $key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
$data['value'] = $value_raw;
|
||||
|
||||
// Determine choice key, this is needed for image choices.
|
||||
foreach ( $field['choices'] as $key => $choice ) {
|
||||
/* translators: %s - choice number. */
|
||||
if ( $field_submit === $choice['label'] || $value_raw === sprintf( esc_html__( 'Choice %s', 'wpforms-lite' ), $key ) ) {
|
||||
$choice_key = $key;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Images choices are enabled, lookup and store image URL.
|
||||
if ( ! empty( $choice_key ) && ! empty( $field['choices_images'] ) ) {
|
||||
|
||||
$data['image'] = ! empty( $field['choices'][ $choice_key ]['image'] ) ? esc_url_raw( $field['choices'][ $choice_key ]['image'] ) : '';
|
||||
}
|
||||
}
|
||||
|
||||
// For the Other option the value_raw is the option and the value is text from the input.
|
||||
if ( is_array( $field_submit ) && ! empty( $field_submit['other'] ) ) {
|
||||
$data['value'] = sanitize_text_field( $field_submit['other'] );
|
||||
// Save the flag that the saved value is from the other option field.
|
||||
$data['is_other'] = true;
|
||||
|
||||
foreach ( $field['choices'] as $choice ) {
|
||||
|
||||
if ( isset( $choice['other'] ) ) {
|
||||
$data['value_raw'] = $choice['label'];
|
||||
$data['image'] = ! empty( $field['choices_images'] ) && ! empty( $choice['image'] ) ? esc_url_raw( $choice['image'] ) : '';
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Push field details to be saved.
|
||||
wpforms()->obj( 'process' )->fields[ $field_id ] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export entry field data.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @param array|mixed $field Field data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function export_entry_field_data( $field ): array {
|
||||
|
||||
$field = (array) $field;
|
||||
|
||||
if ( empty( $field['is_other'] ) ) {
|
||||
return $field;
|
||||
}
|
||||
|
||||
$value = (string) ( $field['value'] ?? '' );
|
||||
$value_raw = (string) ( $field['value_raw'] ?? '' );
|
||||
|
||||
$field['value'] = $value_raw . ': ' . $value;
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include a radio field in allowed field types for keyword search.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @param array|mixed $fields Array of field types.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function add_field_to_anti_spam_keyword_filter( $fields ): array {
|
||||
|
||||
$fields[] = $this->type;
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the HTML value for a field.
|
||||
*
|
||||
* It overrides the parent method because of fields with Other choices.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @param mixed $value The value of the field.
|
||||
* @param array $field Field data and settings.
|
||||
* @param array $form_data Optional. Additional form data.
|
||||
* @param string $context Optional. The context in which the value is being rendered.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function field_html_value( $value, $field, $form_data = [], $context = '' ) {
|
||||
|
||||
if ( empty( $field['type'] ) || $field['type'] !== $this->type ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$other_value = $this->get_other_choice_value( $field );
|
||||
$field_value = $other_value ?? $value ?? '';
|
||||
|
||||
return parent::field_html_value(
|
||||
$field_value,
|
||||
$field,
|
||||
$form_data,
|
||||
$context
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return choice value, including Other label if applicable.
|
||||
|
||||
* It overrides the parent method because of fields with Other choices.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
* @param array $form_data Form data.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_choices_value( array $field, array $form_data ): string {
|
||||
|
||||
$other_value = $this->get_other_choice_value( $field );
|
||||
|
||||
return $other_value ?? parent::get_choices_value( $field, $form_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the value for the "Other" choice option in a field.
|
||||
*
|
||||
* This method handles the retrieval of the "Other" choice value, considering
|
||||
* both raw and processed values. It supports cases where the "Other" choice
|
||||
* is enabled and attempts to construct a meaningful representation of the value
|
||||
* based on the available inputs.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @param array $field Field data.
|
||||
*
|
||||
* @return string|null Returns the constructed "Other" choice value if available,
|
||||
* or null if the "Other" choice is not enabled.
|
||||
*/
|
||||
private function get_other_choice_value( array $field ): ?string {
|
||||
|
||||
// Bail out early if it's not an Other choice.
|
||||
if ( empty( $field['is_other'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$value = $field['value'] ?? '';
|
||||
$value_raw = $field['value_raw'] ?? '';
|
||||
|
||||
if ( wpforms_is_empty_string( $value_raw ) ) {
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
// Return a value with a value_raw as a prefix only if both are not empty.
|
||||
if ( ! wpforms_is_empty_string( $value ) ) {
|
||||
return sprintf( '%1$s: %2$s', $value_raw, $value );
|
||||
}
|
||||
|
||||
return (string) $value_raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the entry field before saving.
|
||||
*
|
||||
* It's necessary for fields with other choices.
|
||||
*
|
||||
* @since 1.9.8.3
|
||||
*
|
||||
* @param array $field Field data.
|
||||
* @param array $form_data Form data.
|
||||
* @param int $entry_id Entry ID.
|
||||
*
|
||||
* @return array
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function save_field( $field, $form_data, $entry_id ) {
|
||||
|
||||
if ( empty( $field['type'] ) || $field['type'] !== $this->type ) {
|
||||
return $field;
|
||||
}
|
||||
|
||||
// Save `value_raw: value` for fields with Other choices.
|
||||
// It's necessary for search functionality on the Form Entries Overview table.
|
||||
// Admins should be able to search for entries that have used an other value.
|
||||
$other_value = $this->get_other_choice_value( $field );
|
||||
|
||||
if ( $other_value !== null ) {
|
||||
$field['value'] = $other_value;
|
||||
}
|
||||
|
||||
return $field;
|
||||
}
|
||||
}
|
||||
|
||||
new WPForms_Field_Radio();
|
||||
+795
@@ -0,0 +1,795 @@
|
||||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// phpcs:disable Generic.Commenting.DocComment.MissingShort
|
||||
/** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
/** @noinspection AutoloadingIssuesInspection */
|
||||
// phpcs:enable Generic.Commenting.DocComment.MissingShort
|
||||
|
||||
/**
|
||||
* Dropdown field.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class WPForms_Field_Select extends WPForms_Field {
|
||||
|
||||
/**
|
||||
* The 'Choices JS' version.
|
||||
*
|
||||
* @since 1.6.3
|
||||
*/
|
||||
public const CHOICES_VERSION = '10.2.0';
|
||||
|
||||
/**
|
||||
* Classic (old) style.
|
||||
*
|
||||
* @since 1.6.1
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const STYLE_CLASSIC = 'classic';
|
||||
|
||||
/**
|
||||
* Modern style.
|
||||
*
|
||||
* @since 1.6.1
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const STYLE_MODERN = 'modern';
|
||||
|
||||
/**
|
||||
* Primary class constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function init() { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
|
||||
|
||||
// Define field type information.
|
||||
$this->name = esc_html__( 'Dropdown', 'wpforms-lite' );
|
||||
$this->keywords = esc_html__( 'choice', 'wpforms-lite' );
|
||||
$this->type = 'select';
|
||||
$this->icon = 'fa-caret-square-o-down';
|
||||
$this->order = 70;
|
||||
$this->defaults = [
|
||||
1 => [
|
||||
'label' => esc_html__( 'First Choice', 'wpforms-lite' ),
|
||||
'value' => '',
|
||||
'default' => '',
|
||||
],
|
||||
2 => [
|
||||
'label' => esc_html__( 'Second Choice', 'wpforms-lite' ),
|
||||
'value' => '',
|
||||
'default' => '',
|
||||
],
|
||||
3 => [
|
||||
'label' => esc_html__( 'Third Choice', 'wpforms-lite' ),
|
||||
'value' => '',
|
||||
'default' => '',
|
||||
],
|
||||
];
|
||||
|
||||
$this->default_settings = [
|
||||
'choices' => $this->defaults,
|
||||
];
|
||||
|
||||
// Define additional field properties.
|
||||
add_filter( 'wpforms_field_properties_' . $this->type, [ $this, 'field_properties' ], 5, 3 );
|
||||
|
||||
// Form frontend CSS enqueues.
|
||||
add_action( 'wpforms_frontend_css', [ $this, 'enqueue_frontend_css' ] );
|
||||
|
||||
// Form frontend JS enqueues.
|
||||
add_action( 'wpforms_frontend_js', [ $this, 'enqueue_frontend_js' ] );
|
||||
|
||||
add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_block_editor_assets' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Define additional field properties.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @param array $properties Field properties.
|
||||
* @param array $field Field settings.
|
||||
* @param array $form_data Form data and settings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function field_properties( $properties, $field, $form_data ) {
|
||||
|
||||
// Remove primary input.
|
||||
unset( $properties['inputs']['primary'] );
|
||||
|
||||
// Define data.
|
||||
$form_id = absint( $form_data['id'] );
|
||||
$field_id = wpforms_validate_field_id( $field['id'] );
|
||||
$choices = $field['choices'];
|
||||
$dynamic = wpforms_get_field_dynamic_choices( $field, $form_id, $form_data );
|
||||
|
||||
if ( $dynamic !== false ) {
|
||||
$choices = $dynamic;
|
||||
$field['show_values'] = true;
|
||||
}
|
||||
|
||||
// Set options container (<select>) properties.
|
||||
$properties['input_container'] = [
|
||||
'class' => [],
|
||||
'data' => [],
|
||||
'id' => "wpforms-{$form_id}-field_{$field_id}",
|
||||
'attr' => [
|
||||
'name' => "wpforms[fields][{$field_id}]",
|
||||
],
|
||||
];
|
||||
|
||||
// Set properties.
|
||||
foreach ( $choices as $key => $choice ) {
|
||||
|
||||
// Used for dynamic choices.
|
||||
$depth = isset( $choice['depth'] ) ? absint( $choice['depth'] ) : 1;
|
||||
|
||||
$properties['inputs'][ $key ] = [
|
||||
'container' => [
|
||||
'attr' => [],
|
||||
'class' => [ "choice-{$key}", "depth-{$depth}" ],
|
||||
'data' => [],
|
||||
'id' => '',
|
||||
],
|
||||
'label' => [
|
||||
'attr' => [
|
||||
'for' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
|
||||
],
|
||||
'class' => [ 'wpforms-field-label-inline' ],
|
||||
'data' => [],
|
||||
'id' => '',
|
||||
'text' => $choice['label'],
|
||||
],
|
||||
'attr' => [
|
||||
'name' => "wpforms[fields][{$field_id}]",
|
||||
'value' => isset( $field['show_values'] ) ? $choice['value'] : $choice['label'],
|
||||
],
|
||||
'class' => [],
|
||||
'data' => [],
|
||||
'id' => "wpforms-{$form_id}-field_{$field_id}_{$key}",
|
||||
'required' => ! empty( $field['required'] ) ? 'required' : '',
|
||||
'default' => isset( $choice['default'] ),
|
||||
];
|
||||
}
|
||||
|
||||
// Add a class that changes the field size.
|
||||
if ( ! empty( $field['size'] ) ) {
|
||||
$properties['input_container']['class'][] = 'wpforms-field-' . esc_attr( $field['size'] );
|
||||
}
|
||||
|
||||
// Required class for pagebreak validation.
|
||||
if ( ! empty( $field['required'] ) ) {
|
||||
$properties['input_container']['class'][] = 'wpforms-field-required';
|
||||
}
|
||||
|
||||
// Add additional class for container.
|
||||
if (
|
||||
! empty( $field['style'] ) &&
|
||||
in_array( $field['style'], [ self::STYLE_CLASSIC, self::STYLE_MODERN ], true )
|
||||
) {
|
||||
$properties['container']['class'][] = "wpforms-field-select-style-{$field['style']}";
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Field options panel inside the builder.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
*
|
||||
* @noinspection HtmlUnknownTarget*/
|
||||
public function field_options( $field ) {
|
||||
/*
|
||||
* Basic field options.
|
||||
*/
|
||||
|
||||
// Options open markup.
|
||||
$this->field_option(
|
||||
'basic-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'open',
|
||||
]
|
||||
);
|
||||
|
||||
// Label.
|
||||
$this->field_option( 'label', $field );
|
||||
|
||||
// Choices.
|
||||
$this->field_option( 'choices', $field );
|
||||
|
||||
// AI Feature.
|
||||
$this->field_option(
|
||||
'ai_modal_button',
|
||||
$field,
|
||||
[
|
||||
'value' => esc_html__( 'Generate Choices', 'wpforms-lite' ),
|
||||
'type' => 'choices',
|
||||
]
|
||||
);
|
||||
|
||||
// Description.
|
||||
$this->field_option( 'description', $field );
|
||||
|
||||
// Required toggle.
|
||||
$this->field_option( 'required', $field );
|
||||
|
||||
// Options close markup.
|
||||
$this->field_option(
|
||||
'basic-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'close',
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
* Advanced field options.
|
||||
*/
|
||||
|
||||
// Options open markup.
|
||||
$this->field_option(
|
||||
'advanced-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'open',
|
||||
]
|
||||
);
|
||||
|
||||
// Show Values toggle option. This option will only show if already used
|
||||
// or if manually enabled by a filter.
|
||||
if ( ! empty( $field['show_values'] ) || wpforms_show_fields_options_setting() ) {
|
||||
$show_values = $this->field_element(
|
||||
'toggle',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'show_values',
|
||||
'value' => $field['show_values'] ?? '0',
|
||||
'desc' => esc_html__( 'Show Values', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Check this option to manually set form field values.', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'show_values',
|
||||
'content' => $show_values,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Multiple options selection.
|
||||
$fld = $this->field_element(
|
||||
'toggle',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'multiple',
|
||||
'value' => ! empty( $field['multiple'] ),
|
||||
'desc' => esc_html__( 'Multiple Options Selection', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Allow users to select multiple choices in this field.', 'wpforms-lite' ) . '<br>' .
|
||||
sprintf(
|
||||
wp_kses( /* translators: %s - URL to WPForms.com doc article. */
|
||||
esc_html__( 'For details, including how this looks and works for your site\'s visitors, please check out <a href="%s" target="_blank" rel="noopener noreferrer">our doc</a>.', 'wpforms-lite' ),
|
||||
[
|
||||
'a' => [
|
||||
'href' => [],
|
||||
'target' => [],
|
||||
'rel' => [],
|
||||
],
|
||||
]
|
||||
),
|
||||
esc_url( wpforms_utm_link( 'https://wpforms.com/docs/how-to-allow-multiple-selections-to-a-dropdown-field-in-wpforms/', 'Field Options', 'Multiple Options Selection Documentation' ) )
|
||||
),
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'multiple',
|
||||
'content' => $fld,
|
||||
]
|
||||
);
|
||||
|
||||
// Style.
|
||||
$lbl = $this->field_element(
|
||||
'label',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'style',
|
||||
'value' => esc_html__( 'Style', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Classic style is the default one generated by your browser. Modern has a fresh look and displays all selected options in a single row.', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$fld = $this->field_element(
|
||||
'select',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'style',
|
||||
'value' => ! empty( $field['style'] ) ? $field['style'] : self::STYLE_CLASSIC,
|
||||
'options' => [
|
||||
self::STYLE_CLASSIC => esc_html__( 'Classic', 'wpforms-lite' ),
|
||||
self::STYLE_MODERN => esc_html__( 'Modern', 'wpforms-lite' ),
|
||||
],
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'style',
|
||||
'content' => $lbl . $fld,
|
||||
]
|
||||
);
|
||||
|
||||
// Size.
|
||||
$this->field_option( 'size', $field );
|
||||
|
||||
// Placeholder.
|
||||
$this->field_option( 'placeholder', $field );
|
||||
|
||||
// Dynamic choice auto-populating toggle.
|
||||
$this->field_option( 'dynamic_choices', $field );
|
||||
|
||||
// Dynamic choice source.
|
||||
$this->field_option( 'dynamic_choices_source', $field );
|
||||
|
||||
// Custom CSS classes.
|
||||
$this->field_option( 'css', $field );
|
||||
|
||||
// Hide label.
|
||||
$this->field_option( 'label_hide', $field );
|
||||
|
||||
// Options close markup.
|
||||
$this->field_option(
|
||||
'advanced-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'close',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Field preview inside the builder.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @since 1.6.1 Added a `Modern` style select support.
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
*/
|
||||
public function field_preview( $field ) {
|
||||
|
||||
$args = [];
|
||||
|
||||
// Label.
|
||||
$this->field_preview_option( 'label', $field );
|
||||
|
||||
// Prepare arguments.
|
||||
$args['modern'] = false;
|
||||
|
||||
if (
|
||||
! empty( $field['style'] ) &&
|
||||
$field['style'] === self::STYLE_MODERN
|
||||
) {
|
||||
$args['modern'] = true;
|
||||
$args['class'] = 'choicesjs-select';
|
||||
}
|
||||
|
||||
// Choices.
|
||||
$this->field_preview_option( 'choices', $field, $args );
|
||||
|
||||
// Description.
|
||||
$this->field_preview_option( 'description', $field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Field display on the form front-end and admin entry edit page.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @since 1.5.0 Converted to a new format, where all the data are taken not from $deprecated, but field properties.
|
||||
* @since 1.6.1 Added multiple select support.
|
||||
*
|
||||
* @param array $field Field data and settings.
|
||||
* @param array $deprecated Deprecated array of field attributes.
|
||||
* @param array $form_data Form data and settings.
|
||||
*
|
||||
* @noinspection HtmlUnknownAttribute
|
||||
*/
|
||||
public function field_display( $field, $deprecated, $form_data ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
|
||||
|
||||
$container = $field['properties']['input_container'];
|
||||
$field_placeholder = ! empty( $field['placeholder'] ) ? $field['placeholder'] : '';
|
||||
$is_multiple = ! empty( $field['multiple'] );
|
||||
$is_modern = ! empty( $field['style'] ) && $field['style'] === self::STYLE_MODERN;
|
||||
$choices = $field['properties']['inputs'];
|
||||
|
||||
// Do not display the field with empty choices on the frontend.
|
||||
if ( ! $choices && ! is_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Display a warning message on the Entry Edit page.
|
||||
if ( ! $choices && is_admin() ) {
|
||||
$this->display_empty_dynamic_choices_message( $field );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! empty( $field['properties']['input_container']['class'] ) && in_array( 'wpforms-field-required', $field['properties']['input_container']['class'], true ) ) {
|
||||
$container['attr']['required'] = 'required';
|
||||
}
|
||||
|
||||
// If it's multiple select.
|
||||
if ( $is_multiple ) {
|
||||
$container['attr']['multiple'] = 'multiple';
|
||||
|
||||
// Change a name attribute.
|
||||
if ( ! empty( $container['attr']['name'] ) ) {
|
||||
$container['attr']['name'] .= '[]';
|
||||
}
|
||||
}
|
||||
|
||||
// Add a class for Choices.js initialization.
|
||||
if ( $is_modern ) {
|
||||
$container['class'][] = 'choicesjs-select';
|
||||
|
||||
// Add a size-class to the data attribute - it is used when Choices.js is initialized.
|
||||
if ( ! empty( $field['size'] ) ) {
|
||||
$container['data']['size-class'] = 'wpforms-field-row wpforms-field-' . sanitize_html_class( $field['size'] );
|
||||
}
|
||||
|
||||
$container['data']['search-enabled'] = $this->is_choicesjs_search_enabled( count( $choices ) );
|
||||
}
|
||||
|
||||
$has_default = false;
|
||||
|
||||
// Check to see if any of the options were selected by default.
|
||||
foreach ( $choices as $choice ) {
|
||||
if ( ! empty( $choice['default'] ) ) {
|
||||
$has_default = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Preselect default if no other choices were marked as default.
|
||||
printf(
|
||||
'<select %s>',
|
||||
wpforms_html_attributes( $container['id'], $container['class'], $container['data'], $container['attr'] )
|
||||
);
|
||||
|
||||
// Optional placeholder.
|
||||
if ( ! empty( $field_placeholder ) || $is_modern ) {
|
||||
printf(
|
||||
'<option value="" class="placeholder" disabled %s>%s</option>',
|
||||
selected( false, $has_default || $is_multiple, false ),
|
||||
esc_html( $field_placeholder )
|
||||
);
|
||||
}
|
||||
|
||||
// Build the select options.
|
||||
foreach ( $choices as $key => $choice ) {
|
||||
$label = $this->get_choices_label( $choice['label']['text'] ?? '', $key, $field );
|
||||
$value = isset( $choice['attr']['value'] ) && ! wpforms_is_empty_string( $choice['attr']['value'] ) ? $choice['attr']['value'] : $label;
|
||||
$data = $choice['container']['data'] ?? [];
|
||||
$data_html = '';
|
||||
|
||||
if ( ! empty( $data ) ) {
|
||||
$data_html = wpforms_html_attributes( '', '', $data );
|
||||
}
|
||||
|
||||
$selected = $choice['attr']['selected'] ?? false;
|
||||
$selected_html = $selected ? ' selected="selected"' : '';
|
||||
|
||||
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
printf(
|
||||
'<option value="%1$s" %2$s class="%3$s" %4$s %5$s>%6$s</option>',
|
||||
esc_attr( $value ),
|
||||
selected( true, ! empty( $choice['default'] ), false ),
|
||||
esc_attr( implode( ' ', $choice['container']['class'] ) ),
|
||||
$data_html,
|
||||
$selected_html,
|
||||
wp_kses(
|
||||
$label,
|
||||
[
|
||||
'span' => [
|
||||
'class' => [],
|
||||
],
|
||||
]
|
||||
)
|
||||
);
|
||||
// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
echo '</select>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate field.
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @param int $field_id Field ID.
|
||||
* @param string|array $field_submit Submitted field value (raw data).
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function validate( $field_id, $field_submit, $form_data ) {
|
||||
|
||||
$field = $form_data['fields'][ $field_id ];
|
||||
|
||||
// Skip validation if the field is dynamic and choices are empty.
|
||||
if ( $this->is_dynamic_choices_empty( $field, $form_data ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent::validate( $field_id, $field_submit, $form_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and sanitize field.
|
||||
*
|
||||
* @since 1.0.2
|
||||
* @since 1.6.1 Added support for multiple values.
|
||||
*
|
||||
* @param int $field_id Field ID.
|
||||
* @param string|array $field_submit Submitted field value (selected option).
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function format( $field_id, $field_submit, $form_data ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Generic.Metrics.NestingLevel.MaxExceeded
|
||||
|
||||
$field = $form_data['fields'][ $field_id ];
|
||||
$dynamic = ! empty( $field['dynamic_choices'] ) ? $field['dynamic_choices'] : false;
|
||||
$multiple = ! empty( $field['multiple'] );
|
||||
$name = sanitize_text_field( $field['label'] );
|
||||
$value = [];
|
||||
|
||||
// Convert the submitted field value to array.
|
||||
if ( ! is_array( $field_submit ) ) {
|
||||
$field_submit = [ $field_submit ];
|
||||
}
|
||||
|
||||
$value_raw = wpforms_sanitize_array_combine( $field_submit );
|
||||
|
||||
$data = [
|
||||
'name' => $name,
|
||||
'value' => '',
|
||||
'value_raw' => $value_raw,
|
||||
'id' => wpforms_validate_field_id( $field_id ),
|
||||
'type' => $this->type,
|
||||
];
|
||||
|
||||
if ( $dynamic === 'post_type' && ! empty( $field['dynamic_post_type'] ) ) {
|
||||
|
||||
// Dynamic population is enabled using post type (like for a `Checkboxes` field).
|
||||
$value_raw = implode( ',', array_map( 'absint', $field_submit ) );
|
||||
$data['value_raw'] = $value_raw;
|
||||
$data['dynamic'] = 'post_type';
|
||||
$data['dynamic_items'] = $value_raw;
|
||||
$data['dynamic_post_type'] = $field['dynamic_post_type'];
|
||||
$posts = [];
|
||||
|
||||
foreach ( $field_submit as $id ) {
|
||||
$post = get_post( $id );
|
||||
|
||||
if ( ! empty( $post ) && ! is_wp_error( $post ) && $data['dynamic_post_type'] === $post->post_type ) {
|
||||
$posts[] = esc_html( wpforms_get_post_title( $post ) );
|
||||
}
|
||||
}
|
||||
|
||||
$data['value'] = ! empty( $posts ) ? wpforms_sanitize_array_combine( $posts ) : '';
|
||||
|
||||
} elseif ( $dynamic === 'taxonomy' && ! empty( $field['dynamic_taxonomy'] ) ) {
|
||||
|
||||
// Dynamic population is enabled using taxonomy (like for a `Checkboxes` field).
|
||||
$value_raw = implode( ',', array_map( 'absint', $field_submit ) );
|
||||
$data['value_raw'] = $value_raw;
|
||||
$data['dynamic'] = 'taxonomy';
|
||||
$data['dynamic_items'] = $value_raw;
|
||||
$data['dynamic_taxonomy'] = $field['dynamic_taxonomy'];
|
||||
$terms = [];
|
||||
|
||||
foreach ( $field_submit as $id ) {
|
||||
$term = get_term( $id, $field['dynamic_taxonomy'] );
|
||||
|
||||
if ( ! empty( $term ) && ! is_wp_error( $term ) ) {
|
||||
$terms[] = esc_html( wpforms_get_term_name( $term ) );
|
||||
}
|
||||
}
|
||||
|
||||
$data['value'] = ! empty( $terms ) ? wpforms_sanitize_array_combine( $terms ) : '';
|
||||
|
||||
} else {
|
||||
|
||||
// Normal processing, dynamic population is off.
|
||||
|
||||
// If show_values is true, that means values posted are the raw values
|
||||
// and not the labels. So we need to get the label values.
|
||||
if ( ! empty( $field['show_values'] ) && (int) $field['show_values'] === 1 ) {
|
||||
|
||||
foreach ( $field_submit as $item ) {
|
||||
foreach ( $field['choices'] as $choice ) {
|
||||
if ( $item === $choice['value'] ) {
|
||||
$value[] = $choice['label'];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data['value'] = ! empty( $value ) ? wpforms_sanitize_array_combine( $value ) : '';
|
||||
|
||||
} else {
|
||||
$data['value'] = $value_raw;
|
||||
}
|
||||
}
|
||||
|
||||
// Backward compatibility: for single dropdown save a string, for multiple - array.
|
||||
if ( ! $multiple && is_array( $data ) && ( 1 === count( $data ) ) ) {
|
||||
$data = reset( $data );
|
||||
}
|
||||
|
||||
// Push field details to be saved.
|
||||
wpforms()->obj( 'process' )->fields[ $field_id ] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form frontend CSS enqueues.
|
||||
*
|
||||
* @since 1.6.1
|
||||
*
|
||||
* @param array $forms Forms on the current page.
|
||||
*/
|
||||
public function enqueue_frontend_css( $forms ) {
|
||||
|
||||
$has_modern_select = false;
|
||||
|
||||
foreach ( $forms as $form ) {
|
||||
if ( $this->is_field_style( $form, self::STYLE_MODERN ) ) {
|
||||
$has_modern_select = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $has_modern_select || wpforms()->obj( 'frontend' )->assets_global() ) {
|
||||
$min = wpforms_get_min_suffix();
|
||||
|
||||
wp_enqueue_style(
|
||||
'wpforms-choicesjs',
|
||||
WPFORMS_PLUGIN_URL . "assets/css/choices{$min}.css",
|
||||
[],
|
||||
self::CHOICES_VERSION
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form frontend JS enqueues.
|
||||
*
|
||||
* @since 1.6.1
|
||||
*
|
||||
* @param array $forms Forms on the current page.
|
||||
*/
|
||||
public function enqueue_frontend_js( $forms ) {
|
||||
|
||||
$has_modern_select = false;
|
||||
|
||||
foreach ( $forms as $form ) {
|
||||
if ( $this->is_field_style( $form, self::STYLE_MODERN ) ) {
|
||||
$has_modern_select = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $has_modern_select || wpforms()->obj( 'frontend' )->assets_global() ) {
|
||||
$this->enqueue_choicesjs_once( $forms );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load WPForms Gutenberg block scripts.
|
||||
*
|
||||
* @since 1.8.1
|
||||
*/
|
||||
public function enqueue_block_editor_assets() {
|
||||
|
||||
$min = wpforms_get_min_suffix();
|
||||
|
||||
wp_enqueue_style(
|
||||
'wpforms-choicesjs',
|
||||
WPFORMS_PLUGIN_URL . "assets/css/choices{$min}.css",
|
||||
[],
|
||||
self::CHOICES_VERSION
|
||||
);
|
||||
|
||||
$this->enqueue_choicesjs_once( [] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the provided form has a dropdown field with a specified style.
|
||||
*
|
||||
* @since 1.6.1
|
||||
*
|
||||
* @param array $form Form data.
|
||||
* @param string $style Desired field style.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_field_style( $form, $style ) {
|
||||
|
||||
$is_field_style = false;
|
||||
|
||||
if ( empty( $form['fields'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( (array) $form['fields'] as $field ) {
|
||||
if (
|
||||
! empty( $field['type'] ) &&
|
||||
$field['type'] === $this->type &&
|
||||
! empty( $field['style'] ) &&
|
||||
sanitize_key( $style ) === $field['style']
|
||||
) {
|
||||
$is_field_style = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $is_field_style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a field name for an ajax error message.
|
||||
*
|
||||
* @since 1.6.3
|
||||
*
|
||||
* @param string|mixed $name Field name for error triggered.
|
||||
* @param array $field Field settings.
|
||||
* @param array $props List of properties.
|
||||
* @param string|string[] $error Error message.
|
||||
*
|
||||
* @return string
|
||||
* @noinspection PhpMissingReturnTypeInspection
|
||||
* @noinspection ReturnTypeCanBeDeclaredInspection
|
||||
*/
|
||||
public function ajax_error_field_name( $name, $field, $props, $error ) {
|
||||
|
||||
$name = (string) $name;
|
||||
|
||||
if ( ! isset( $field['type'] ) || $field['type'] !== 'select' ) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
if ( ! empty( $field['multiple'] ) ) {
|
||||
$input = isset( $props['inputs'] ) ? end( $props['inputs'] ) : [];
|
||||
|
||||
return isset( $input['attr']['name'] ) ? $input['attr']['name'] . '[]' : '';
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
new WPForms_Field_Select();
|
||||
+558
@@ -0,0 +1,558 @@
|
||||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Single line text field.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class WPForms_Field_Text extends WPForms_Field {
|
||||
|
||||
/**
|
||||
* Primary class constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function init() {
|
||||
|
||||
// Define field type information.
|
||||
$this->name = esc_html__( 'Single Line Text', 'wpforms-lite' );
|
||||
$this->type = 'text';
|
||||
$this->icon = 'fa-text-width';
|
||||
$this->order = 30;
|
||||
|
||||
// Define additional field properties.
|
||||
add_filter( 'wpforms_field_properties_text', [ $this, 'field_properties' ], 5, 3 );
|
||||
add_action( 'wpforms_frontend_js', [ $this, 'frontend_js' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert mask formatted for jquery.inputmask into the format used by amp-inputmask.
|
||||
*
|
||||
* Note that amp-inputmask does not yet support all of the options that jquery.inputmask provides.
|
||||
* In particular, amp-inputmask doesn't provides:
|
||||
* - Upper-alphabetical mask.
|
||||
* - Upper-alphanumeric mask.
|
||||
* - Advanced Input Masks with arbitrary repeating groups.
|
||||
*
|
||||
* @link https://amp.dev/documentation/components/amp-inputmask
|
||||
* @link https://wpforms.com/docs/how-to-use-custom-input-masks/
|
||||
*
|
||||
* @param string $mask Mask formatted for jquery.inputmask.
|
||||
* @return array {
|
||||
* Mask and placeholder.
|
||||
*
|
||||
* @type string $mask Mask for amp-inputmask.
|
||||
* @type string $placeholder Placeholder derived from mask if one is not supplied.
|
||||
* }
|
||||
*/
|
||||
protected function convert_mask_to_amp_inputmask( $mask ) {
|
||||
$placeholder = '';
|
||||
|
||||
// Convert jquery.inputmask format into amp-inputmask format.
|
||||
$amp_mask = '';
|
||||
$req_mask_mapping = [
|
||||
'9' => '0', // Numeric.
|
||||
'a' => 'L', // Alphabetical (a-z or A-Z).
|
||||
'A' => 'L', // Upper-alphabetical (A-Z). Note: AMP does not have an uppercase-alphabetical mask type, so same as previous.
|
||||
'*' => 'A', // Alphanumeric (0-9, a-z, A-Z).
|
||||
'&' => 'A', // Upper-alphanumeric (A-Z, 0-9). Note: AMP does not have an uppercase-alphanumeric mask type, so same as previous.
|
||||
' ' => '_', // Automatically insert spaces.
|
||||
];
|
||||
$opt_mask_mapping = [
|
||||
'9' => '9', // The user may optionally add a numeric character.
|
||||
'a' => 'l', // The user may optionally add an alphabetical character.
|
||||
'A' => 'l', // The user may optionally add an alphabetical character.
|
||||
'*' => 'a', // The user may optionally add an alphanumeric character.
|
||||
'&' => 'a', // The user may optionally add an alphanumeric character.
|
||||
];
|
||||
$placeholder_mapping = [
|
||||
'9' => '0',
|
||||
'a' => 'a',
|
||||
'A' => 'a',
|
||||
'*' => '_',
|
||||
'&' => '_',
|
||||
];
|
||||
$is_inside_optional = false;
|
||||
$last_mask_token = null;
|
||||
for ( $i = 0, $len = strlen( $mask ); $i < $len; $i++ ) {
|
||||
if ( '[' === $mask[ $i ] ) {
|
||||
$is_inside_optional = true;
|
||||
$placeholder .= $mask[ $i ];
|
||||
continue;
|
||||
} elseif ( ']' === $mask[ $i ] ) {
|
||||
$is_inside_optional = false;
|
||||
$placeholder .= $mask[ $i ];
|
||||
continue;
|
||||
} elseif ( isset( $last_mask_token ) && preg_match( '/^\{(?P<n>\d+)(?:,(?P<m>\d+))?\}/', substr( $mask, $i ), $matches ) ) {
|
||||
$amp_mask .= str_repeat( $req_mask_mapping[ $last_mask_token ], $matches['n'] );
|
||||
$placeholder .= str_repeat( $placeholder_mapping[ $last_mask_token ], $matches['n'] );
|
||||
if ( isset( $matches['m'] ) ) {
|
||||
$amp_mask .= str_repeat( $opt_mask_mapping[ $last_mask_token ], $matches['m'] );
|
||||
$placeholder .= str_repeat( $placeholder_mapping[ $last_mask_token ], $matches['m'] );
|
||||
}
|
||||
$i += strlen( $matches[0] ) - 1;
|
||||
|
||||
$last_mask_token = null; // Reset.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( '\\' === $mask[ $i ] ) {
|
||||
$amp_mask .= '\\';
|
||||
$i++;
|
||||
if ( ! isset( $mask[ $i ] ) ) {
|
||||
continue;
|
||||
}
|
||||
$amp_mask .= $mask[ $i ];
|
||||
} else {
|
||||
// Remember this token in case it is a mask.
|
||||
if ( isset( $opt_mask_mapping[ $mask[ $i ] ] ) ) {
|
||||
$last_mask_token = $mask[ $i ];
|
||||
}
|
||||
|
||||
if ( $is_inside_optional && isset( $opt_mask_mapping[ $mask[ $i ] ] ) ) {
|
||||
$amp_mask .= $opt_mask_mapping[ $mask[ $i ] ];
|
||||
} elseif ( isset( $req_mask_mapping[ $mask[ $i ] ] ) ) {
|
||||
$amp_mask .= $req_mask_mapping[ $mask[ $i ] ];
|
||||
} else {
|
||||
$amp_mask .= '\\' . $mask[ $i ];
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $placeholder_mapping[ $mask[ $i ] ] ) ) {
|
||||
$placeholder .= $placeholder_mapping[ $mask[ $i ] ];
|
||||
} else {
|
||||
$placeholder .= $mask[ $i ];
|
||||
}
|
||||
}
|
||||
|
||||
return [ $amp_mask, $placeholder ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Define additional field properties.
|
||||
*
|
||||
* @since 1.4.5
|
||||
*
|
||||
* @param array $properties Field properties.
|
||||
* @param array $field Field settings.
|
||||
* @param array $form_data Form data and settings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function field_properties( $properties, $field, $form_data ) {
|
||||
|
||||
// Input primary: Detect custom input mask.
|
||||
if ( empty( $field['input_mask'] ) ) {
|
||||
return $properties;
|
||||
}
|
||||
|
||||
// Add class that will trigger custom mask.
|
||||
$properties['inputs']['primary']['class'][] = 'wpforms-masked-input';
|
||||
|
||||
if ( wpforms_is_amp() ) {
|
||||
return $this->get_amp_input_mask_properties( $properties, $field );
|
||||
}
|
||||
|
||||
$properties['inputs']['primary']['data']['rule-inputmask-incomplete'] = true;
|
||||
|
||||
if ( strpos( $field['input_mask'], 'alias:' ) !== false ) {
|
||||
$mask = str_replace( 'alias:', '', $field['input_mask'] );
|
||||
$properties['inputs']['primary']['data']['inputmask-alias'] = $mask;
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
if ( strpos( $field['input_mask'], 'regex:' ) !== false ) {
|
||||
$mask = str_replace( 'regex:', '', $field['input_mask'] );
|
||||
$properties['inputs']['primary']['data']['inputmask-regex'] = $mask;
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
if ( strpos( $field['input_mask'], 'date:' ) !== false ) {
|
||||
$mask = str_replace( 'date:', '', $field['input_mask'] );
|
||||
$properties['inputs']['primary']['data']['inputmask-alias'] = 'datetime';
|
||||
$properties['inputs']['primary']['data']['inputmask-inputformat'] = $mask;
|
||||
|
||||
/**
|
||||
* Some datetime formats include letters, so we need to switch inputmode to text.
|
||||
* For instance:
|
||||
* – tt is am/pm
|
||||
* – TT is AM/PM
|
||||
*/
|
||||
$properties['inputs']['primary']['data']['inputmask-inputmode'] = preg_match( '/[tT]/', $mask ) ? 'text' : 'numeric';
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
$properties['inputs']['primary']['data']['inputmask-mask'] = $field['input_mask'];
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define additional field properties for the inputmask on AMP pages.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param array $properties Field properties.
|
||||
* @param array $field Field settings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_amp_input_mask_properties( $properties, $field ) {
|
||||
|
||||
list( $amp_mask, $placeholder ) = $this->convert_mask_to_amp_inputmask( $field['input_mask'] );
|
||||
|
||||
$properties['inputs']['primary']['attr']['mask'] = $amp_mask;
|
||||
|
||||
if ( empty( $properties['inputs']['primary']['attr']['placeholder'] ) ) {
|
||||
$properties['inputs']['primary']['attr']['placeholder'] = $placeholder;
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Field options panel inside the builder.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
*/
|
||||
public function field_options( $field ) {
|
||||
/*
|
||||
* Basic field options.
|
||||
*/
|
||||
|
||||
// Options open markup.
|
||||
$this->field_option(
|
||||
'basic-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'open',
|
||||
]
|
||||
);
|
||||
|
||||
// Label.
|
||||
$this->field_option( 'label', $field );
|
||||
|
||||
// Description.
|
||||
$this->field_option( 'description', $field );
|
||||
|
||||
// Required toggle.
|
||||
$this->field_option( 'required', $field );
|
||||
|
||||
// Options close markup.
|
||||
$this->field_option(
|
||||
'basic-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'close',
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
* Advanced field options.
|
||||
*/
|
||||
|
||||
// Options open markup.
|
||||
$this->field_option(
|
||||
'advanced-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'open',
|
||||
]
|
||||
);
|
||||
|
||||
// Size.
|
||||
$this->field_option( 'size', $field );
|
||||
|
||||
// Placeholder.
|
||||
$this->field_option( 'placeholder', $field );
|
||||
|
||||
// Limit length.
|
||||
$args = [
|
||||
'slug' => 'limit_enabled',
|
||||
'content' => $this->field_element(
|
||||
'toggle',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'limit_enabled',
|
||||
'value' => isset( $field['limit_enabled'] ),
|
||||
'desc' => esc_html__( 'Limit Length', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Check this option to limit text length by characters or words count.', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
),
|
||||
];
|
||||
|
||||
$this->field_element( 'row', $field, $args );
|
||||
|
||||
$count = $this->field_element(
|
||||
'text',
|
||||
$field,
|
||||
[
|
||||
'type' => 'number',
|
||||
'slug' => 'limit_count',
|
||||
'attrs' => [
|
||||
'min' => 1,
|
||||
'step' => 1,
|
||||
'pattern' => '[0-9]',
|
||||
],
|
||||
'value' => ! empty( $field['limit_count'] ) ? absint( $field['limit_count'] ) : 1,
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$mode = $this->field_element(
|
||||
'select',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'limit_mode',
|
||||
'value' => ! empty( $field['limit_mode'] ) ? esc_attr( $field['limit_mode'] ) : 'characters',
|
||||
'options' => [
|
||||
'characters' => esc_html__( 'Characters', 'wpforms-lite' ),
|
||||
'words' => esc_html__( 'Words', 'wpforms-lite' ),
|
||||
],
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$args = [
|
||||
'slug' => 'limit_controls',
|
||||
'class' => ! isset( $field['limit_enabled'] ) ? 'wpforms-hide' : '',
|
||||
'content' => $count . $mode,
|
||||
];
|
||||
|
||||
$this->field_element( 'row', $field, $args );
|
||||
|
||||
// Default value.
|
||||
$this->field_option( 'default_value', $field );
|
||||
|
||||
// Input Mask.
|
||||
$lbl = $this->field_element(
|
||||
'label',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'input_mask',
|
||||
'value' => esc_html__( 'Input Mask', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Enter your custom input mask.', 'wpforms-lite' ),
|
||||
'after_tooltip' => '<a href="' . esc_url( wpforms_utm_link( 'https://wpforms.com/docs/how-to-use-custom-input-masks/', 'Field Options', 'Input Mask Documentation' ) ) . '" class="after-label-description" target="_blank" rel="noopener noreferrer">' . esc_html__( 'See Examples & Docs', 'wpforms-lite' ) . '</a>',
|
||||
],
|
||||
false
|
||||
);
|
||||
$fld = $this->field_element(
|
||||
'text',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'input_mask',
|
||||
'value' => ! empty( $field['input_mask'] ) ? esc_attr( $field['input_mask'] ) : '',
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$this->field_element(
|
||||
'row',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'input_mask',
|
||||
'content' => $lbl . $fld,
|
||||
]
|
||||
);
|
||||
|
||||
// Custom CSS classes.
|
||||
$this->field_option( 'css', $field );
|
||||
|
||||
// Hide label.
|
||||
$this->field_option( 'label_hide', $field );
|
||||
|
||||
// Options close markup.
|
||||
$this->field_option(
|
||||
'advanced-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'close',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Field preview inside the builder.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
*/
|
||||
public function field_preview( $field ) {
|
||||
|
||||
// Define data.
|
||||
$placeholder = ! empty( $field['placeholder'] ) ? $field['placeholder'] : '';
|
||||
$default_value = ! empty( $field['default_value'] ) ? $field['default_value'] : '';
|
||||
|
||||
// Label.
|
||||
$this->field_preview_option( 'label', $field );
|
||||
|
||||
// Primary input.
|
||||
echo '<input type="text" placeholder="' . esc_attr( $placeholder ) . '" value="' . esc_attr( $default_value ) . '" class="primary-input" readonly>';
|
||||
|
||||
// Description.
|
||||
$this->field_preview_option( 'description', $field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Field display on the form front-end.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field settings.
|
||||
* @param array $deprecated Deprecated.
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function field_display( $field, $deprecated, $form_data ) {
|
||||
|
||||
// Define data.
|
||||
$primary = $field['properties']['inputs']['primary'];
|
||||
|
||||
if ( isset( $field['limit_enabled'] ) ) {
|
||||
$limit_count = isset( $field['limit_count'] ) ? absint( $field['limit_count'] ) : 0;
|
||||
$limit_mode = isset( $field['limit_mode'] ) ? sanitize_key( $field['limit_mode'] ) : 'characters';
|
||||
|
||||
$primary['data']['form-id'] = $form_data['id'];
|
||||
$primary['data']['field-id'] = $field['id'];
|
||||
|
||||
if ( 'characters' === $limit_mode ) {
|
||||
$primary['class'][] = 'wpforms-limit-characters-enabled';
|
||||
$primary['attr']['maxlength'] = $limit_count;
|
||||
$primary['data']['text-limit'] = $limit_count;
|
||||
} else {
|
||||
$primary['class'][] = 'wpforms-limit-words-enabled';
|
||||
$primary['data']['text-limit'] = $limit_count;
|
||||
}
|
||||
}
|
||||
|
||||
// Primary field.
|
||||
printf(
|
||||
'<input type="text" %s %s>',
|
||||
wpforms_html_attributes( $primary['id'], $primary['class'], $primary['data'], $primary['attr'] ),
|
||||
$primary['required'] // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue frontend limit option js.
|
||||
*
|
||||
* @since 1.5.6
|
||||
*
|
||||
* @param array $forms Forms on the current page.
|
||||
*/
|
||||
public function frontend_js( $forms ) {
|
||||
|
||||
// Get fields.
|
||||
$fields = array_map(
|
||||
function( $form ) {
|
||||
return empty( $form['fields'] ) ? [] : $form['fields'];
|
||||
},
|
||||
(array) $forms
|
||||
);
|
||||
|
||||
// Make fields flat.
|
||||
$fields = array_reduce(
|
||||
$fields,
|
||||
function( $accumulator, $current ) {
|
||||
return array_merge( $accumulator, $current );
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
// Leave only fields with limit.
|
||||
$fields = array_filter(
|
||||
$fields,
|
||||
function( $field ) {
|
||||
return $field['type'] === $this->type && isset( $field['limit_enabled'] ) && ! empty( $field['limit_count'] );
|
||||
}
|
||||
);
|
||||
|
||||
if ( count( $fields ) ) {
|
||||
$min = wpforms_get_min_suffix();
|
||||
|
||||
wp_enqueue_script(
|
||||
'wpforms-text-limit',
|
||||
WPFORMS_PLUGIN_URL . "assets/js/frontend/fields/text-limit.es5{$min}.js",
|
||||
[],
|
||||
WPFORMS_VERSION,
|
||||
$this->load_script_in_footer()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and sanitize field.
|
||||
*
|
||||
* @since 1.5.6
|
||||
*
|
||||
* @param int $field_id Field ID.
|
||||
* @param mixed $field_submit Field value that was submitted.
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function format( $field_id, $field_submit, $form_data ) {
|
||||
|
||||
$field = $form_data['fields'][ $field_id ];
|
||||
$name = ! empty( $field['label'] ) ? sanitize_text_field( $field['label'] ) : '';
|
||||
|
||||
// Sanitize.
|
||||
$value = sanitize_text_field( $field_submit );
|
||||
|
||||
wpforms()->obj( 'process' )->fields[ $field_id ] = [
|
||||
'name' => $name,
|
||||
'value' => $value,
|
||||
'id' => wpforms_validate_field_id( $field_id ),
|
||||
'type' => $this->type,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate field on form submit.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param int $field_id Field ID.
|
||||
* @param mixed $field_submit Submitted field value (raw data).
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function validate( $field_id, $field_submit, $form_data ) {
|
||||
|
||||
parent::validate( $field_id, $field_submit, $form_data );
|
||||
|
||||
if ( empty( $form_data['fields'][ $field_id ] ) || empty( $form_data['fields'][ $field_id ]['limit_enabled'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$field = $form_data['fields'][ $field_id ];
|
||||
$limit = absint( $field['limit_count'] );
|
||||
$mode = ! empty( $field['limit_mode'] ) ? sanitize_key( $field['limit_mode'] ) : 'characters';
|
||||
$value = sanitize_text_field( $field_submit );
|
||||
|
||||
if ( 'characters' === $mode ) {
|
||||
if ( mb_strlen( str_replace( "\r\n", "\n", $value ) ) > $limit ) {
|
||||
/* translators: %s - limit characters number. */
|
||||
wpforms()->obj( 'process' )->errors[ $form_data['id'] ][ $field_id ] = sprintf( _n( 'Text can\'t exceed %d character.', 'Text can\'t exceed %d characters.', $limit, 'wpforms-lite' ), $limit );
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if ( wpforms_count_words( $value ) > $limit ) {
|
||||
/* translators: %s - limit words number. */
|
||||
wpforms()->obj( 'process' )->errors[ $form_data['id'] ][ $field_id ] = sprintf( _n( 'Text can\'t exceed %d word.', 'Text can\'t exceed %d words.', $limit, 'wpforms-lite' ), $limit );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new WPForms_Field_Text();
|
||||
+380
@@ -0,0 +1,380 @@
|
||||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Paragraph text field.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class WPForms_Field_Textarea extends WPForms_Field {
|
||||
|
||||
/**
|
||||
* Primary class constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function init() {
|
||||
|
||||
// Define field type information.
|
||||
$this->name = esc_html__( 'Paragraph Text', 'wpforms-lite' );
|
||||
$this->keywords = esc_html__( 'textarea', 'wpforms-lite' );
|
||||
$this->type = 'textarea';
|
||||
$this->icon = 'fa-paragraph';
|
||||
$this->order = 50;
|
||||
|
||||
add_action( 'wpforms_frontend_js', [ $this, 'frontend_js' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value, that is used to prefill via dynamic or fallback population.
|
||||
* Based on field data and current properties.
|
||||
*
|
||||
* @since 1.6.4
|
||||
*
|
||||
* @param string $raw_value Value from a GET param, always a string.
|
||||
* @param string $input Represent a subfield inside the field. May be empty.
|
||||
* @param array $properties Field properties.
|
||||
* @param array $field Current field specific data.
|
||||
*
|
||||
* @return array Modified field properties.
|
||||
*/
|
||||
protected function get_field_populated_single_property_value( $raw_value, $input, $properties, $field ) {
|
||||
|
||||
if ( ! is_string( $raw_value ) ) {
|
||||
return $properties;
|
||||
}
|
||||
|
||||
if (
|
||||
! empty( $input ) &&
|
||||
isset( $properties['inputs'][ $input ] )
|
||||
) {
|
||||
$properties['inputs'][ $input ]['attr']['value'] = wpforms_sanitize_textarea_field( wp_unslash( $raw_value ) );
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Field options panel inside the builder.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field data and settings.
|
||||
*/
|
||||
public function field_options( $field ) {
|
||||
/*
|
||||
* Basic field options.
|
||||
*/
|
||||
|
||||
// Options open markup.
|
||||
$this->field_option(
|
||||
'basic-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'open',
|
||||
]
|
||||
);
|
||||
|
||||
// Label.
|
||||
$this->field_option( 'label', $field );
|
||||
|
||||
// Description.
|
||||
$this->field_option( 'description', $field );
|
||||
|
||||
// Required toggle.
|
||||
$this->field_option( 'required', $field );
|
||||
|
||||
// Options close markup.
|
||||
$this->field_option(
|
||||
'basic-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'close',
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
* Advanced field options.
|
||||
*/
|
||||
|
||||
// Options open markup.
|
||||
$args = [
|
||||
'markup' => 'open',
|
||||
];
|
||||
|
||||
$this->field_option( 'advanced-options', $field, $args );
|
||||
|
||||
// Size.
|
||||
$this->field_option( 'size', $field );
|
||||
|
||||
// Placeholder.
|
||||
$this->field_option( 'placeholder', $field );
|
||||
|
||||
// Limit length.
|
||||
$args = [
|
||||
'slug' => 'limit_enabled',
|
||||
'content' => $this->field_element(
|
||||
'toggle',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'limit_enabled',
|
||||
'value' => isset( $field['limit_enabled'] ) ? '1' : '0',
|
||||
'desc' => esc_html__( 'Limit Length', 'wpforms-lite' ),
|
||||
'tooltip' => esc_html__( 'Check this option to limit text length by characters or words count.', 'wpforms-lite' ),
|
||||
],
|
||||
false
|
||||
),
|
||||
];
|
||||
|
||||
$this->field_element( 'row', $field, $args );
|
||||
|
||||
$count = $this->field_element(
|
||||
'text',
|
||||
$field,
|
||||
[
|
||||
'type' => 'number',
|
||||
'slug' => 'limit_count',
|
||||
'attrs' => [
|
||||
'min' => 1,
|
||||
'step' => 1,
|
||||
'pattern' => '[0-9]',
|
||||
],
|
||||
'value' => ! empty( $field['limit_count'] ) ? $field['limit_count'] : 1,
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$mode = $this->field_element(
|
||||
'select',
|
||||
$field,
|
||||
[
|
||||
'slug' => 'limit_mode',
|
||||
'value' => ! empty( $field['limit_mode'] ) ? esc_attr( $field['limit_mode'] ) : 'characters',
|
||||
'options' => [
|
||||
'characters' => esc_html__( 'Characters', 'wpforms-lite' ),
|
||||
'words' => esc_html__( 'Words', 'wpforms-lite' ),
|
||||
],
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$args = [
|
||||
'slug' => 'limit_controls',
|
||||
'class' => ! isset( $field['limit_enabled'] ) ? 'wpforms-hide' : '',
|
||||
'content' => $count . $mode,
|
||||
];
|
||||
|
||||
$this->field_element( 'row', $field, $args );
|
||||
|
||||
// Default value.
|
||||
$this->field_option( 'default_value', $field );
|
||||
|
||||
// Custom CSS classes.
|
||||
$this->field_option( 'css', $field );
|
||||
|
||||
// Hide label.
|
||||
$this->field_option( 'label_hide', $field );
|
||||
|
||||
// Options close markup.
|
||||
$this->field_option(
|
||||
'advanced-options',
|
||||
$field,
|
||||
[
|
||||
'markup' => 'close',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Field preview inside the builder.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field data and settings.
|
||||
*/
|
||||
public function field_preview( $field ) {
|
||||
|
||||
// Label.
|
||||
$this->field_preview_option( 'label', $field );
|
||||
|
||||
// Primary input.
|
||||
$placeholder = ! empty( $field['placeholder'] ) ? $field['placeholder'] : '';
|
||||
$default_value = ! empty( $field['default_value'] ) ? $field['default_value'] : '';
|
||||
|
||||
echo '<textarea placeholder="' . esc_attr( $placeholder ) . '" class="primary-input" readonly>' . esc_textarea( $default_value ) . '</textarea>';
|
||||
|
||||
// Description.
|
||||
$this->field_preview_option( 'description', $field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Field display on the form front-end.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param array $field Field data and settings.
|
||||
* @param array $deprecated Deprecated.
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function field_display( $field, $deprecated, $form_data ) {
|
||||
|
||||
// Define data.
|
||||
$primary = $field['properties']['inputs']['primary'];
|
||||
$value = '';
|
||||
|
||||
if ( isset( $primary['attr']['value'] ) ) {
|
||||
$value = esc_textarea( html_entity_decode( $primary['attr']['value'] ) );
|
||||
|
||||
unset( $primary['attr']['value'] );
|
||||
}
|
||||
|
||||
if ( isset( $field['limit_enabled'] ) ) {
|
||||
$limit_count = isset( $field['limit_count'] ) ? absint( $field['limit_count'] ) : 0;
|
||||
$limit_mode = isset( $field['limit_mode'] ) ? sanitize_key( $field['limit_mode'] ) : 'characters';
|
||||
|
||||
$primary['data']['form-id'] = $form_data['id'];
|
||||
$primary['data']['field-id'] = $field['id'];
|
||||
|
||||
if ( 'characters' === $limit_mode ) {
|
||||
$primary['class'][] = 'wpforms-limit-characters-enabled';
|
||||
$primary['attr']['maxlength'] = $limit_count;
|
||||
$primary['data']['text-limit'] = $limit_count;
|
||||
} else {
|
||||
$primary['class'][] = 'wpforms-limit-words-enabled';
|
||||
$primary['data']['text-limit'] = $limit_count;
|
||||
}
|
||||
}
|
||||
|
||||
// Primary field.
|
||||
printf(
|
||||
'<textarea %s %s>%s</textarea>',
|
||||
wpforms_html_attributes( $primary['id'], $primary['class'], $primary['data'], $primary['attr'] ),
|
||||
$primary['required'], // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
$value // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue frontend limit option js.
|
||||
*
|
||||
* @since 1.5.6
|
||||
*
|
||||
* @param array $forms Forms on the current page.
|
||||
*/
|
||||
public function frontend_js( $forms ) {
|
||||
|
||||
// Get fields.
|
||||
$fields = array_map(
|
||||
function( $form ) {
|
||||
return empty( $form['fields'] ) ? [] : $form['fields'];
|
||||
},
|
||||
(array) $forms
|
||||
);
|
||||
|
||||
// Make fields flat.
|
||||
$fields = array_reduce(
|
||||
$fields,
|
||||
function( $accumulator, $current ) {
|
||||
return array_merge( $accumulator, $current );
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
// Leave only fields with limit.
|
||||
$fields = array_filter(
|
||||
$fields,
|
||||
function( $field ) {
|
||||
return $field['type'] === $this->type && isset( $field['limit_enabled'] );
|
||||
}
|
||||
);
|
||||
|
||||
if ( count( $fields ) ) {
|
||||
$min = wpforms_get_min_suffix();
|
||||
|
||||
wp_enqueue_script(
|
||||
'wpforms-text-limit',
|
||||
WPFORMS_PLUGIN_URL . "assets/js/frontend/fields/text-limit.es5{$min}.js",
|
||||
[],
|
||||
WPFORMS_VERSION,
|
||||
$this->load_script_in_footer()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and sanitize field.
|
||||
*
|
||||
* @since 1.5.6
|
||||
*
|
||||
* @param int $field_id Field ID.
|
||||
* @param mixed $field_submit Field value that was submitted.
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function format( $field_id, $field_submit, $form_data ) {
|
||||
|
||||
$field = $form_data['fields'][ $field_id ];
|
||||
if ( is_array( $field_submit ) ) {
|
||||
$field_submit = implode( "\r\n", array_filter( $field_submit ) );
|
||||
}
|
||||
|
||||
$name = ! empty( $field['label'] ) ? sanitize_text_field( $field['label'] ) : '';
|
||||
|
||||
// Sanitize but keep line breaks.
|
||||
$value = wpforms_sanitize_textarea_field( $field_submit );
|
||||
|
||||
wpforms()->obj( 'process' )->fields[ $field_id ] = [
|
||||
'name' => $name,
|
||||
'value' => $value,
|
||||
'id' => wpforms_validate_field_id( $field_id ),
|
||||
'type' => $this->type,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate field on form submit.
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param int $field_id Field ID.
|
||||
* @param mixed $field_submit Submitted field value (raw data).
|
||||
* @param array $form_data Form data and settings.
|
||||
*/
|
||||
public function validate( $field_id, $field_submit, $form_data ) {
|
||||
|
||||
parent::validate( $field_id, $field_submit, $form_data );
|
||||
|
||||
if ( empty( $form_data['fields'][ $field_id ] ) || empty( $form_data['fields'][ $field_id ]['limit_enabled'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_array( $field_submit ) ) {
|
||||
$field_submit = implode( "\r\n", array_filter( $field_submit ) );
|
||||
}
|
||||
|
||||
$field = $form_data['fields'][ $field_id ];
|
||||
$limit = absint( $field['limit_count'] );
|
||||
$mode = ! empty( $field['limit_mode'] ) ? sanitize_key( $field['limit_mode'] ) : 'characters';
|
||||
$value = wpforms_sanitize_textarea_field( $field_submit );
|
||||
|
||||
if ( 'characters' === $mode ) {
|
||||
if ( mb_strlen( str_replace( "\r\n", "\n", $value ) ) > $limit ) {
|
||||
/* translators: %s - limit characters number. */
|
||||
wpforms()->obj( 'process' )->errors[ $form_data['id'] ][ $field_id ] = sprintf( _n( 'Text can\'t exceed %d character.', 'Text can\'t exceed %d characters.', $limit, 'wpforms-lite' ), $limit );
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if ( wpforms_count_words( $value ) > $limit ) {
|
||||
/* translators: %s - limit words number. */
|
||||
wpforms()->obj( 'process' )->errors[ $form_data['id'] ][ $field_id ] = sprintf( _n( 'Text can\'t exceed %d word.', 'Text can\'t exceed %d words.', $limit, 'wpforms-lite' ), $limit );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new WPForms_Field_Textarea();
|
||||
Reference in New Issue
Block a user