Commit realizado el 12:13:52 08-04-2024

This commit is contained in:
Pagina Web Monito
2024-04-08 12:13:55 -04:00
commit 0c33094de9
7815 changed files with 1365694 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,780 @@
<?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->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 );
// 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 ) {
// Define data.
$form_id = absint( $form_data['id'] );
$field_id = absint( $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( $properties['inputs']['primary'] );
// Set input container (ul) properties.
$properties['input_container'] = [
'class' => [ ! empty( $field['random'] ) ? 'wpforms-randomize' : '' ],
'data' => [],
'attr' => [],
'id' => "wpforms-{$form_id}-field_{$field_id}",
];
$field['choice_limit'] = empty( $field['choice_limit'] ) ? 0 : (int) $field['choice_limit'];
if ( $field['choice_limit'] > 0 ) {
$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 ( $field['choice_limit'] > 0 ) {
$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()->get( '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 );
// Choices Images.
$this->field_option( 'choices_images', $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.
$field['choice_limit'] = empty( $field['choice_limit'] ) ? 0 : (int) $field['choice_limit'];
$this->field_element(
'row',
$field,
[
'slug' => 'choice_limit',
'content' =>
$this->field_element(
'label',
$field,
[
'slug' => 'choice_limit',
'value' => esc_html__( 'Choice Limit', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Limit the number of checkboxes a user can select. Leave empty for unlimited.', 'wpforms-lite' ),
],
false
) . $this->field_element(
'text',
$field,
[
'slug' => 'choice_limit',
'value' => $field['choice_limit'] > 0 ? $field['choice_limit'] : '',
'type' => 'number',
],
false
),
]
);
// 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 ) {
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';
}
// 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( $choice['label']['text'] ),
! empty( $choice['label']['text'] ) ? ' title="' . esc_attr( $choice['label']['text'] ) . '"' : ''
);
}
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( $choice['label']['text'] ) . '</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()->get( '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( $choice['label']['text'] ),
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 (selected option).
* @param array $form_data Form data.
*/
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;
}
$field_submit = (array) $field_submit;
$choice_limit = empty( $form_data['fields'][ $field_id ]['choice_limit'] ) ? 0 : (int) $form_data['fields'][ $field_id ]['choice_limit'];
$count_choices = count( $field_submit );
if ( $choice_limit > 0 && $count_choices > $choice_limit ) {
// Generating the error.
$error = wpforms_setting( 'validation-check-limit', esc_html__( 'You have exceeded the number of allowed selections: {#}.', 'wpforms-lite' ) );
$error = str_replace( '{#}', $choice_limit, $error );
}
// 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()->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' => absint( $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 ) {
if ( $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()->process->fields[ $field_id ] = $data;
}
}
new WPForms_Field_Checkbox();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,315 @@
<?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->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' => '',
],
];
// Set field to default to 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( $properties['inputs']['primary'] );
// 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;
}
/**
* @inheritdoc
*/
public function is_dynamic_population_allowed( $properties, $field ) {
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.
*/
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 $key => $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'] ),
$required
); // WPCS: XSS ok.
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()->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' => absint( $field_id ),
'type' => $this->type,
];
}
}
new WPForms_Field_GDPR_Checkbox();

View File

@@ -0,0 +1,857 @@
<?php
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 post meta table.
*
* @since 1.7.6
*
* @var string
*/
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' ], 10, 1 );
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 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 ) {
return false;
}
/**
* Whether 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 ) {
return false;
}
/**
* Define field options to display in 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',
]
);
}
/**
* 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 ) {
echo '<div class="internal-information-wrap wpforms-clear">';
// 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 );
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 button is displayable.
*
* @since 1.7.6
*
* @param array $field Field data.
*
* @return bool
*/
private function is_button_displayable( $field ) {
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 $field_atts Field attributes.
* @param array $form_data Form data.
*/
public function field_display( $field, $field_atts, $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 CSS class to hide field settings when 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 $echo Print or return the value. Print by default.
*
* @return string
*/
public function field_element( $option, $field, $args = [], $echo = true ) {
if ( ! isset( $args['class'] ) ) {
$args['class'] = '';
}
if ( ! $this->is_editable() ) {
$args['class'] .= ' wpforms-hidden ';
}
return parent::field_element( $option, $field, $args, $echo );
}
/**
* Render custom option preview on the right side of 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
*/
private function render_custom_preview( $option, $field, $args = [] ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded, 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 = isset( $field['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 = isset( $field['cta-label'] ) && ! empty( $field['cta-label'] ) && empty( $field['expanded-description'] ) ? esc_attr( $field['cta-label'] ) : esc_attr__( 'Learn More', 'wpforms-lite' );
if ( isset( $field['expanded-description'] ) && ! 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 ( isset( $field['cta-link'] ) && ! 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 ) );
}
return '';
}
/**
* Display field button in 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 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;
}
/**
* 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
*/
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
*/
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 column from the entries list table.
*
* @since 1.7.6
*
* @param array $disallowed Table columns.
*
* @return array
*/
public function hide_column_in_entries_table( $disallowed ) {
$disallowed[] = $this->type;
return $disallowed;
}
/**
* Add 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 ( $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 checkbox state to the post meta table.
*
* @since 1.7.6
*/
public function save_internal_information_checkbox() {
// Run several checks: required items, security, permissions.
if (
! isset( $_POST['formId'], $_POST['name'], $_POST['checked'] ) ||
! check_ajax_referer( 'wpforms-builder', 'nonce', false ) ||
! wpforms_current_user_can( 'edit_forms' )
) {
wp_send_json_error();
}
$form_id = (int) $_POST['formId'];
$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 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'
);
wp_enqueue_script(
'wpforms-internal-information-field',
WPFORMS_PLUGIN_URL . "assets/js/components/admin/fields/internal-information-field{$min}.js",
[ 'wpforms-builder', 'wpforms-md5-hash', 'wpforms-builder-drag-fields' ],
WPFORMS_VERSION
);
}
/**
* Checks if user is allowed to edit the field's content.
*
* @since 1.7.6
*
* @return bool
*/
private function is_editable() {
/**
* 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 ) {
return isset( $field['type'] ) && $field['type'] === $this->type;
}
/**
* Render result of field_preview_option into custom div.
*
* If 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.
*/
private function render_preview( $label, $field, $args = [] ) {
$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 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
*/
private function replace_checkboxes( $description, array $field ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
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 = isset( $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() {
$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( $content ) {
if ( empty( $content ) || ! class_exists( 'DOMDocument' ) ) {
return $content;
}
$dom = new DOMDocument();
$form_data = wpforms()->get( 'form' )->get( $this->form_id, [ 'content_only' => true ] );
$template_data = ! empty( $form_data['meta'] ) ? wpforms()->get( 'builder_templates' )->get_template( $form_data['meta']['template'] ) : [];
$template_name = ! empty( $template_data ) ? $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 );
$inner_html = '';
foreach ( $body->childNodes as $node ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$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( $field ) {
if ( strpos( $field['cta-link'], 'https://wpforms.com' ) === 0 ) {
return wpforms_utm_link( $field['cta-link'], 'Template Documentation' );
}
return $field['cta-link'];
}
}
new WPForms_Field_Internal_Information();

View File

@@ -0,0 +1,622 @@
<?php
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() {
// Define additional field properties.
add_filter( 'wpforms_field_properties_name', [ $this, 'field_properties' ], 5, 3 );
// Set field to default to 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 $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 ) {
$format = ! empty( $field['format'] ) ? esc_attr( $field['format'] ) : 'first-last';
// Simple format.
if ( 'simple' === $format ) {
$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 ) : '';
return $properties;
}
// Expanded formats.
// Remove primary for expanded formats since we have first, middle, last.
unset( $properties['inputs']['primary'] );
$form_id = absint( $form_data['id'] );
$field_id = absint( $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 ) : '',
'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 ) : '',
'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 ) : '',
'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'][] = 'first-last' === $format ? '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'][] = 'first-last' === $format ? 'wpforms-one-half' : 'wpforms-two-fifths';
return $properties;
}
/**
* Name fields should default to being required.
*
* @since 1.0.8
*
* @param bool $required
* @param array $field
*
* @return bool
*/
public function default_required( $required, $field ) {
if ( 'name' === $field['type'] ) {
return true;
}
return $required;
}
/**
* Field options panel inside the builder.
*
* @since 1.0.0
*
* @param array $field
*/
public function field_options( $field ) {
// 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-' . $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">', $field['id'], $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" id="wpforms-field-option-%d-simple_default" name="fields[%d][simple_default]" 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">', $field['id'], $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" id="wpforms-field-option-%1$d-first_default" name="fields[%1$d][first_default]" 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">', $field['id'], $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" id="wpforms-field-option-%1$d-middle_default" name="fields[%1$d][middle_default]" 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">', $field['id'], $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" id="wpforms-field-option-%1$d-last_default" name="fields[%1$d][last_default]" 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.
*/
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 ( 'simple' === $format ) {
// Primary field (Simple).
printf(
'<input type="text" %s %s>',
wpforms_html_attributes( $primary['id'], $primary['class'], $primary['data'], $primary['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'] ),
$first['required']
);
$this->field_display_sublabel( 'first', 'after', $field );
$this->field_display_error( 'first', $field );
echo '</div>';
// Middle name.
if ( 'first-middle-last' === $format ) {
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'] ),
$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'] ),
$last['required']
);
$this->field_display_sublabel( 'last', 'after', $field );
$this->field_display_error( 'last', $field );
echo '</div>';
echo '</div>';
}
}
/**
* Validate field on form submit.
*
* @since 1.0.0
*
* @param int $field_id Field id.
* @param array|string $field_submit Field submit.
* @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()->get( '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()->process->fields[ $field_id ] = [
'name' => sanitize_text_field( $name ),
'value' => sanitize_text_field( $value ),
'id' => absint( $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 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();

View File

@@ -0,0 +1,510 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Number Slider field.
*
* @since 1.5.7
*/
class WPForms_Field_Number_Slider extends WPForms_Field {
/**
* 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.
*/
// 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',
]
);
// Value: min/max.
$lbl = $this->field_element(
'label',
$field,
[
'slug' => 'value',
'value' => esc_html__( 'Value', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Define the minimum and the maximum values for the slider.', 'wpforms-lite' ),
],
false
);
$min = $this->field_element(
'text',
$field,
[
'type' => 'number',
'slug' => 'min',
'class' => 'wpforms-number-slider-min',
'value' => ! empty( $field['min'] ) ? (float) $field['min'] : self::SLIDER_MIN,
],
false
);
$max = $this->field_element(
'text',
$field,
[
'type' => 'number',
'slug' => 'max',
'class' => 'wpforms-number-slider-max',
'value' => ! empty( $field['max'] ) ? (float) $field['max'] : self::SLIDER_MAX,
],
false
);
$this->field_element(
'row',
$field,
[
'slug' => 'min_max',
'content' => $lbl . wpforms_render(
'fields/number-slider/builder-option-min-max',
[
'label' => $lbl,
'input_min' => $min,
'input_max' => $max,
'field_id' => $field['id'],
],
true
),
]
);
// Default value.
$lbl = $this->field_element(
'label',
$field,
[
'slug' => 'default_value',
'value' => esc_html__( 'Default Value', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Enter a default value for this field.', 'wpforms-lite' ),
],
false
);
$fld = $this->field_element(
'text',
$field,
[
'type' => 'number',
'slug' => 'default_value',
'class' => 'wpforms-number-slider-default-value',
'value' => ! empty( $field['default_value'] ) ? (float) $field['default_value'] : 0,
'attrs' => [
'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,
],
],
false
);
$this->field_element(
'row',
$field,
[
'slug' => 'default_value',
'content' => $lbl . $fld,
]
);
// Steps.
$lbl = $this->field_element(
'label',
$field,
[
'slug' => 'step',
'value' => esc_html__( 'Increment', 'wpforms-lite' ),
'tooltip' => esc_html__( 'Determines the increment between selectable values on the slider.', 'wpforms-lite' ),
],
false
);
$fld = $this->field_element(
'text',
$field,
[
'type' => 'number',
'slug' => 'step',
'class' => 'wpforms-number-slider-step',
'value' => ! empty( $field['step'] ) ? abs( $field['step'] ) : self::SLIDER_STEP,
'attrs' => [
'min' => 0,
'max' => isset( $field['max'] ) && is_numeric( $field['max'] ) ? abs( (float) $field['max'] ) : self::SLIDER_MAX,
],
],
false
);
$this->field_element(
'row',
$field,
[
'slug' => 'step',
'content' => $lbl . $fld,
]
);
// 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' );
$default_value = ! empty( $field['default_value'] ) ? (float) $field['default_value'] : 0;
$hint_value = ! empty( $primary['attr']['value'] ) ? (float) $primary['attr']['value'] : $default_value;
$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'],
'default_value' => $default_value,
'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.
* @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()->process->errors[ $form_id ][ $field_id ] = wpforms_get_required_label();
}
// Check if value is numeric.
if ( ! empty( $field_submit ) && ! is_numeric( $field_submit ) ) {
wpforms()->process->errors[ $form_id ][ $field_id ] = apply_filters( 'wpforms_valid_number_label', esc_html__( 'Please provide a valid value.', 'wpforms-lite' ) );
}
}
/**
* 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()->process->fields[ $field_id ] = [
'name' => sanitize_text_field( $name ),
'value' => $value,
'value_raw' => $value_raw,
'id' => absint( $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 );
$abs_value = abs( $signed_value );
$value = strpos( $signed_value, '-' ) === 0 ? '-' . $abs_value : $abs_value;
return $value;
}
}
new WPForms_Field_Number_Slider();

View File

@@ -0,0 +1,220 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Number text field.
*
* @since 1.0.0
*/
class WPForms_Field_Number extends WPForms_Field {
/**
* 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;
}
/**
* 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 );
// 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" pattern="\d*" %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 value.
* @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()->process->errors[ $form_id ][ $field_id ] = wpforms_get_required_label();
}
// Check if value is numeric.
if ( ! empty( $value ) && ! is_numeric( $value ) ) {
wpforms()->process->errors[ $form_id ][ $field_id ] = apply_filters( 'wpforms_valid_number_label', esc_html__( 'Please enter a valid number.', 'wpforms-lite' ) );
}
}
/**
* 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()->process->fields[ $field_id ] = [
'name' => sanitize_text_field( $name ),
'value' => $this->sanitize_value( $field_submit ),
'id' => absint( $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();

View File

@@ -0,0 +1,617 @@
<?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->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 );
// 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 );
}
/**
* 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( $properties['inputs']['primary'] );
// Define data.
$form_id = absint( $form_data['id'] );
$field_id = absint( $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 = isset( $field['show_values'] ) ? $choice['value'] : $choice['label'];
/* translators: %s - choice number. */
$value = ( $value === '' ) ? sprintf( esc_html__( 'Choice %s', 'wpforms-lite' ), $key ) : $value;
$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' => $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'] ),
];
}
// 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()->get( '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 );
// Choices Images.
$this->field_option( 'choices_images', $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 ) {
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';
}
// 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( $choice['label']['text'] ),
! empty( $choice['label']['text'] ) ? ' title="' . esc_attr( $choice['label']['text'] ) . '"' : ''
);
}
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()->get( '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( $choice['label']['text'] )
);
}
echo '</li>';
}
echo '</ul>';
}
/**
* Validate field.
*
* @since 1.8.2
*
* @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 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
*
* @param int $field_id Field ID.
* @param string $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' => absint( $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 ( $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'] ) : '';
}
}
// Push field details to be saved.
wpforms()->process->fields[ $field_id ] = $data;
}
}
new WPForms_Field_Radio();

View File

@@ -0,0 +1,751 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Dropdown field.
*
* @since 1.0.0
*/
class WPForms_Field_Select extends WPForms_Field {
/**
* Choices JS version.
*
* @since 1.6.3
*/
const CHOICES_VERSION = '9.0.1';
/**
* Classic (old) style.
*
* @since 1.6.1
*
* @var string
*/
const STYLE_CLASSIC = 'classic';
/**
* Modern style.
*
* @since 1.6.1
*
* @var string
*/
const STYLE_MODERN = 'modern';
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function init() {
// 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' => '',
],
];
// 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 = absint( $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 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.
*/
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 );
// 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' => 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
);
$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'] ) &&
self::STYLE_MODERN === $field['style']
) {
$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 a 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.
*/
public function field_display( $field, $deprecated, $form_data ) {
$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 Entry Edit page.
if ( ! $choices && is_admin() ) {
$this->display_empty_dynamic_choices_message( $field );
return;
}
if ( ! empty( $field['required'] ) ) {
$container['attr']['required'] = 'required';
}
// If it's a 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 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;
}
}
// Fake placeholder for Modern style.
if ( $is_modern && empty( $field_placeholder ) ) {
$first_choices = reset( $choices );
$field_placeholder = $first_choices['label']['text'];
}
// 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 ) ) {
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 ) {
printf(
'<option value="%s" %s>%s</option>',
esc_attr( $choice['attr']['value'] ),
selected( true, ! empty( $choice['default'] ), false ),
esc_html( $choice['label']['text'] )
);
}
echo '</select>';
}
/**
* Validate field.
*
* @since 1.8.2
*
* @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 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.6.1 Added a 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 ) {
$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 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' => absint( $field_id ),
'type' => $this->type,
];
if ( 'post_type' === $dynamic && ! 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 ( ! 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 (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 ( ! 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.
// 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()->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()->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()->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 $is_field_style;
}
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 field name for ajax error message.
*
* @since 1.6.3
*
* @param string $name Field name for error triggered.
* @param array $field Field settings.
* @param array $props List of properties.
* @param string $error Error message.
*
* @return string
*/
public function ajax_error_field_name( $name, $field, $props, $error ) {
if ( ! isset( $field['type'] ) || 'select' !== $field['type'] ) {
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();

View File

@@ -0,0 +1,548 @@
<?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;
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/text-limit.es5{$min}.js",
[],
WPFORMS_VERSION,
true
);
}
}
/**
* 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()->process->fields[ $field_id ] = [
'name' => $name,
'value' => $value,
'id' => absint( $field_id ),
'type' => $this->type,
];
}
/**
* Validate field on form submit.
*
* @since 1.6.2
*
* @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 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()->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()->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();

View File

@@ -0,0 +1,372 @@
<?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( $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/text-limit.es5{$min}.js", [], WPFORMS_VERSION, true );
}
}
/**
* 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()->process->fields[ $field_id ] = [
'name' => $name,
'value' => $value,
'id' => absint( $field_id ),
'type' => $this->type,
];
}
/**
* Validate field on form submit.
*
* @since 1.6.2
*
* @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 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()->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()->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();