includes/forms/class-llms-form-field.php
<?php
/**
* Setup and render form fields.
*
* @package LifterLMS/Classes
*
* @since 5.0.0
* @version 6.2.0
*/
defined( 'ABSPATH' ) || exit;
/**
* LLMS_Form_Field class
*
* @since 5.0.0
*/
class LLMS_Form_Field {
/**
* Form Field Settings
*
* @var array {
* Array of field settings.
*
* @type array $attributes Associative array of HTML attributes to add to the field element.
* @type bool $checked Determines if radio and checkbox fields are checked.
* @type int $columns Number of columns the field wrapper should occupy when rendered. Accepts integers >= 1 and <= 12.
* @type string[]|string $classes Additional CSS classes to add to the field element. Accepts a string or an array of strings.
* @type string $data_store Determines where to store field values. Accepts "users" or "usermeta" to store on the respective WP core tables.
* @type string|false $data_store_key Determines the key name to use when storing the field value. Pass `false` to disable automatic storage. Defaults to the value of the `$name` property.
* @type string $description A string to use as the field's description or helper text.
* @type string $default The default value to use for the field.
* @type bool $disabled Whether or not the field is enabled.
* @type string $id The field's HTML "id" attribute. Must be unique. If not supplied, an ID is automatically generated.
* @type string $label Text to use in the label element associated with the field.
* @type bool $label_show_empty When true and no `$label` is supplied, will show an empty label element.
* @type bool $last_column When true, outputs a clearfix element following the element's wrapper. Allows ending a "row" of fields.
* @type bool $match Match this field to another field for validation purposes. Must be the `$id` of another field in the form.
* @type string $name The field's HTML "name" attribute. Default's to the value of `$id` when not supplied.
* @type array $options An associative array of options used for select, checkbox groups, and radio fields.
* @type string $options_preset A string representing a pre-defined set of `$options`. Accepts "countries" or "states". Custom presets can be defined using the filter "llms_form_field_options_preset_{$preset_id}".
* @type string $placeholder The field's HTML placeholder attribute.
* @type bool $required Determines if the field is marked as required.
* @type string $selected Alias of `$default`.
* @type string $type Field type. Accepts any HTML5 input type (text, email, tel, etc...), radio, checkbox, select, textarea, button, reset, submit, and html.
* @type string $value Value of the field.
* @type string[]|string $wrapper_classes Additional CSS classes to add to the field's wrapper element. Accepts a string or an array of strings.
* }
*/
protected $settings = array();
/**
* Cached field HTML.
*
* @var string
*/
protected $html = '';
/**
* Data source where to get field value from.
*
* @var null|WP_Post|WP_User
*/
private $data_source;
/**
* Data source type where to get field value from.
*
* @var null|string
*/
private $data_source_type;
/**
* Constructor
*
* @since 5.0.0
*
* @param array $settings Field settings.
* @param int|object $data_source Optional. Data source where to get field value from. Default is `null`.
* Can be a WP_User or a WP_Post, or their id.
* The actual object will be retrieved basing on the data_store.
* @return void
*/
public function __construct( $settings = array(), $data_source = null ) {
/**
* Filters the settings of a LifterLMS Form Field
*
* @since 5.0.0
*
* @param array $settings Field settings.
* @param LLMS_Form_Field $field Form field class instance.
*/
$this->settings = apply_filters( 'llms_field_settings', wp_parse_args( $settings, $this->get_defaults() ), $this );
$this->define_data_source( $data_source );
$this->prepare();
}
/**
* Define the source of the data
*
* @since 5.0.0
*
* @param int|object $data_source Data source where to get field value from.
* @return void
*/
private function define_data_source( $data_source ) {
if ( empty( $this->settings['data_store'] ) || ! in_array( $this->settings['data_store'], array( 'users', 'usermeta' ), true ) ) {
return;
}
if ( ! is_null( $data_source ) ) {
$data_source = $data_source instanceof WP_User ? $data_source : get_user_by( 'ID', $data_source );
} elseif ( is_user_logged_in() ) {
$data_source = wp_get_current_user();
}
if ( $data_source instanceof WP_User ) {
$this->data_source = $data_source;
$this->data_source_type = 'wp_user';
}
}
/**
* Merge an array of classes into a string or array of classes
*
* @since 5.0.0
*
* @param string[]|string $classes Classes.
* @param string[] $defaults Default classes.
* @return string[]
*/
protected function classes_ensure_array( $classes, $defaults = array() ) {
if ( is_string( $classes ) ) {
$classes = array_map( 'esc_attr', array_map( 'trim', explode( ' ', $classes ) ) );
}
$classes = array_merge( $defaults, $classes );
return array_filter( $classes );
}
/**
* Returns an array of form field objects from this checkbox or radio field's options array.
*
* @since 6.2.0 Moved from `LLMS_Form_Field::get_field_html()` and added the hidden logic.
*
* @param string $is_hidden If true, returns only the checked fields and sets their type to 'hidden',
* else returns all options as `$this->settings['type']` form fields.
* @return LLMS_Form_Field[]
*/
public function explode_options_to_fields( $is_hidden = false ) {
$fields = array();
$value = ! empty( $this->settings['value'] ) || is_array( $this->settings['value'] )
? $this->settings['value']
: $this->settings['default'];
foreach ( $this->settings['options'] as $key => $val ) {
$name = $this->settings['name'];
$checked = $value === $key;
if ( 'checkbox' === $this->settings['type'] ) {
$name .= '[]';
$value = is_array( $value ) ? $value : array( $value );
$checked = in_array( $key, $value, true );
}
if ( $is_hidden && ! $checked ) {
continue;
}
$fields[] = new self(
array(
'data_store' => false,
'id' => sprintf( '%1$s--%2$s', $this->settings['id'], $key ),
'name' => $name,
'value' => $key,
'label' => $val,
'checked' => $checked,
'type' => $is_hidden ? 'hidden' : $this->settings['type'],
)
);
}
return $fields;
}
/**
* Get default field settings.
*
* @since 5.0.0
*
* @return array
*/
protected function get_defaults() {
return array(
'attributes' => array(),
'checked' => false,
'columns' => 12,
'classes' => array(), // Or string of space-separated classes.
'data_store' => 'usermeta', // Users or usermeta.
'data_store_key' => '', // Defaults to value passed for "name".
'description' => '',
'default' => '',
'disabled' => false,
'id' => '',
'label' => '',
'label_show_empty' => false,
'last_column' => true,
'match' => '', // Test.
'name' => '', // Defaults to value passed for "id".
'options' => array(),
'options_preset' => '',
'placeholder' => '',
'required' => false,
'selected' => '', // Alias of "default".
'type' => 'text',
'value' => '',
'wrapper_classes' => array(), // Or string of space-separated classes.
);
}
/**
* Ensure deprecated settings still function.
*
* The legacy "min_length", "max_length", and "style" settings should now
* be passed via the "attributes" setting.
*
* @since 5.0.0
*
* @return array
*/
protected function get_deprecated_html_attributes() {
$attrs = array();
foreach ( array( 'min_length', 'max_length', 'style' ) as $attr ) {
if ( isset( $this->settings[ $attr ] ) ) {
$attrs[ str_replace( '_', '', $attr ) ] = esc_attr( $this->settings[ $attr ] );
}
}
return $attrs;
}
/**
* Retrieve HTML for the fields description
*
* @since 5.0.0
*
* @return string
*/
protected function get_description_html() {
return $this->settings['description'] ? sprintf( '<span class="llms-description">%s</span>', $this->settings['description'] ) : '';
}
/**
* Retrieve the full HTML for the field.
*
* @since 5.0.0
* @since 6.2.0 Moved exploding of checkbox and radio options to `explode_options_to_fields()`.
*
* @return string
*/
protected function get_field_html() {
/**
* Allow 3rd parties to create custom field types or their own field HTML methods.
*
* Returning a non-empty string will override default HTML generation and use the returned HTML instead.
*
* @since 5.0.0
*
* @param string $html Override html.
* @param array $settings Array of field settings initially passed to the class constructor.
* @param LLMS_Form_Field $field Form field object.
*/
$override = apply_filters( 'llms_form_field_get_' . $this->settings['type'] . '_html', '', $this->settings, $this );
if ( ! empty( $override ) ) {
return $override;
}
$extra_attrs = array();
$inner_html = '';
$self_closing = false;
switch ( $this->settings['type'] ) {
case 'button':
case 'reset':
case 'submit':
$tag = 'button';
$classes = array( 'llms-field-button' );
$inner_html = $this->settings['value'];
$extra_attrs['type'] = $this->settings['type'];
break;
case 'checkbox':
case 'radio':
$is_group = ! empty( $this->settings['options'] );
$tag = $is_group ? 'div' : 'input';
$self_closing = ! $is_group;
$classes = array( sprintf( 'llms-field-%s', $this->settings['type'] ) );
if ( ! $is_group ) {
$extra_attrs['type'] = $this->settings['type'];
if ( true === $this->settings['checked'] ) {
$extra_attrs['checked'] = 'checked';
}
} else {
$classes[] = 'llms-input-group';
$fields = $this->explode_options_to_fields( false );
foreach ( $fields as $field ) {
$inner_html .= $field->get_html();
}
}
break;
case 'html':
$tag = 'div';
$classes = array( 'llms-field-html' );
$inner_html = $this->settings['value'];
break;
case 'select':
$tag = 'select';
$classes = array( 'llms-field-select' );
$inner_html = $this->get_options_html();
break;
case 'textarea':
$tag = 'textarea';
$classes = array( 'llms-field-textarea' );
$inner_html = $this->settings['value'];
break;
default:
$tag = 'input';
$self_closing = true;
$classes = array( 'llms-field-input' );
$extra_attrs['type'] = $this->settings['type'];
}
$extra_attrs['class'] = implode( ' ', $this->classes_ensure_array( $this->settings['classes'], $classes ) );
$attributes = array_merge( $this->get_html_attributes( $this->settings ), $extra_attrs );
ksort( $attributes );
$attrs = '';
foreach ( $attributes as $attr => $val ) {
$attrs .= sprintf( ' %1$s="%2$s"', $attr, $val );
}
$open = $self_closing ? sprintf( '<%1$s%2$s', $tag, $attrs ) : sprintf( '<%1$s%2$s>', $tag, $attrs );
$close = $self_closing ? ' />' : sprintf( '</%s>', $tag );
return sprintf( '%1$s%2$s%3$s', $open, $inner_html, $close );
}
/**
* Retrieve an array of HTML attributes which should be added to the main field element.
*
* @since 5.0.0
*
* @return array
*/
protected function get_html_attributes() {
$check = array(
'id',
'disabled',
'name',
'placeholder',
'required',
'value',
);
// Input groups and html only have an id.
if ( $this->is_input_group() || 'html' === $this->settings['type'] ) {
$check = array( 'id' );
}
$attrs = array();
// Settings attributes.
foreach ( $check as $attr ) {
if ( ! empty( $this->settings[ $attr ] ) ) {
$attrs[ $attr ] = esc_attr( wp_strip_all_tags( $this->settings[ $attr ] ) );
}
}
// Any custom attributes.
foreach ( $this->settings['attributes'] as $attr => $val ) {
$attrs[ $attr ] = esc_attr( wp_strip_all_tags( $val ) );
}
if ( $this->settings['match'] ) {
$attrs['data-match'] = $this->settings['match'];
}
return array_merge( $attrs, $this->get_deprecated_html_attributes() );
}
/**
* Retrieve the field's HTML.
*
* @since 5.0.0
*
* @return string
*/
public function get_html() {
/**
* Short-circuit field HTML generation.
*
* Allows a 3rd party to replace the HTML generation method with entirely custom HTML
* by returning a non-null value.
*
* @since 5.0.0
*
* @param string $pre The pre-rendered HTML content. Default `null`.
* @param array $settings The prepared field settings array.
* @param LLMS_Form_Field $field_obj Form field instance.
*/
$pre = apply_filters( 'llms_form_field_pre_render', null, $this->settings, $this );
if ( ! is_null( $pre ) ) {
return $pre;
}
$before = '';
$after = '';
if ( 'hidden' !== $this->settings['type'] ) {
$before .= sprintf( '<div class="%s">', implode( ' ', $this->settings['wrapper_classes'] ) );
$label_pos = $this->get_label_position();
$$label_pos .= $this->get_label_html();
$desc = $this->get_description_html();
if ( $this->is_input_group() ) {
$before .= $desc;
} else {
$after .= $this->get_description_html();
}
$after .= '</div>';
if ( $this->settings['last_column'] ) {
$after .= '<div class="clear"></div>';
}
}
$this->html = $before . $this->get_field_html() . $after;
return apply_filters( 'llms_form_field', $this->html, $this->settings );
}
/**
* Retrieve the HTML for the fields label.
*
* @since 5.0.0
*
* @return string
*/
protected function get_label_html() {
if ( empty( $this->settings['label'] ) && ! $this->settings['label_show_empty'] ) {
return '';
}
$required = '';
if ( $this->settings['required'] ) {
/**
* Customize the character used to denote a required field
*
* @since Unknown.
*
* @param string $character The character used to denote a required field. Defaults to "*" (an asterisk).
* @param array $settings Associative array of field settings.
*/
$char = apply_filters( 'lifterlms_form_field_required_character', '*', $this->settings );
$required = sprintf( '<span class="llms-required">%s</span>', $char );
}
return sprintf( '<label for="%1$s">%2$s%3$s</label>', esc_attr( $this->settings['id'] ), $this->settings['label'], $required );
}
/**
* Determines if the label element should be rendered before the field or after it.
*
* @since 5.0.0
*
* @return string
*/
protected function get_label_position() {
$pos = 'before';
if ( in_array( $this->settings['type'], array( 'checkbox', 'radio' ), true ) && empty( $this->settings['options'] ) ) {
$pos = 'after';
}
return $pos;
}
/**
* Retrieve the HTML for an options list in a select field.
*
* This function works recursively to build optgroups.
*
* @since 5.0.0
*
* @param array $options Prepared options array.
* @param mixed $selected_val The value of the option that should be marked as "selected".
* @return string
*/
protected function get_option_list_html( $options, $selected_val ) {
$html = '';
foreach ( $options as $key => $val ) {
if ( is_array( $val ) ) {
$label = isset( $val['label'] ) ? $val['label'] : $key;
$group_options = isset( $val['options'] ) ? $val['options'] : $val;
$html .= sprintf( '<optgroup label="%1$s" data-key="%2$s">%3$s</optgroup>', esc_attr( $label ), esc_attr( $key ), $this->get_option_list_html( $group_options, $selected_val ) );
} else {
$selected = ( (string) $key === (string) $selected_val ) ? ' selected="selected"' : '';
$disabled = ( $this->settings['placeholder'] && '' === $key ) ? ' disabled="disabled"' : '';
$html .= sprintf( '<option value="%1$s"%3$s%4$s>%2$s</option>', esc_attr( $key ), esc_attr( $val ), $selected, $disabled );
}
}
return $html;
}
/**
* Retrieve the html for all options in a select field.
*
* @since 5.0.0
*
* @return string
*/
protected function get_options_html() {
$html = '';
if ( ! $this->settings['options'] ) {
return $html;
}
$selected_val = ! empty( $this->settings['value'] ) ? $this->settings['value'] : $this->settings['default'];
$html .= $this->get_option_list_html( $this->settings['options'], $selected_val );
return $html;
}
/**
* Retrieve the field settings array.
*
* @since 5.0.0
*
* @return array
*/
public function get_settings() {
return $this->settings;
}
/**
* Determines if the field is a group of checkboxes or radios.
*
* @since 5.0.0
*
* @return bool
*/
protected function is_input_group() {
return in_array( $this->settings['type'], array( 'checkbox', 'radio' ), true ) && ! empty( $this->settings['options'] );
}
/**
* Prepares the field for rendering by configuring all of it's settings.
*
* @since 5.0.0
*
* @return void
*/
protected function prepare() {
if ( empty( $this->settings['id'] ) ) {
$this->settings['id'] = uniqid( 'llms-field-' );
}
$this->prepare_wrapper_classes();
$this->settings['classes'] = $this->classes_ensure_array( $this->settings['classes'] );
// Allow setting `disabled` to `true` to disable the field.
if ( true === $this->settings['disabled'] ) {
$this->settings['disabled'] = 'disabled';
}
// Allow setting `required` to `true` to make the field required the field.
if ( true === $this->settings['required'] ) {
$this->settings['required'] = 'required';
}
// When name is `false` we don't want to output a name on the field.
if ( false !== $this->settings['name'] ) {
// Use the field id as the name if name isn't specified.
$this->settings['name'] = empty( $this->settings['name'] ) ? $this->settings['id'] : $this->settings['name'];
}
// When `data_store_key` is false we won't automatically store or populate the field.
if ( false !== $this->settings['data_store_key'] && empty( $this->settings['data_store_key'] ) ) {
$this->prepare_storage();
}
// Add preset options.
if ( $this->settings['options_preset'] ) {
$this->prepare_options_from_preset();
} elseif ( ! empty( $this->settings['options'] ) ) {
$this->settings['options'] = $this->prepare_options( $this->settings['options'] );
}
$this->prepare_value();
if ( 'llms-password-strength-meter' === $this->settings['id'] ) {
$this->prepare_password_strength_meter();
} elseif ( 'llms_voucher' === $this->settings['id'] ) {
$this->prepare_voucher();
}
}
/**
* Prepare the fields options.
*
* Allows options to be setup as an associative array of key/value pairs or
* an array of associative arrays each with a "label" and "key" property.
* The "key" property may be omitted, in which case the "label" will be
* duplicated as the option's "value".
*
* @since 5.0.0
*
* @param array $raw Raw field data.
* @return array
*/
protected function prepare_options( $raw ) {
$prepared = array();
foreach ( $raw as $key => $val ) {
if ( is_array( $val ) ) {
// Option group.
if ( isset( $val['options'] ) ) {
$prepared[ $key ] = array(
'label' => isset( $val['label'] ) ? $val['label'] : $key,
'options' => $this->prepare_options( $val['options'] ),
);
// From block editor options array.
} elseif ( isset( $val['text'] ) ) {
$item_key = isset( $val['key'] ) ? $val['key'] : $val['text'];
$prepared[ $item_key ] = $val['text'];
if ( isset( $val['default'] ) && llms_parse_bool( $val['default'] ) ) {
if ( 'checkbox' === $this->settings['type'] ) { // Account for multiple defaults.
$this->settings['default'] = is_array( $this->settings['default'] ) ? $this->settings['default'] : array();
$this->settings['default'][] = $item_key;
} else {
$this->settings['default'] = $item_key;
}
}
}
// Flat array of $key=>$val.
} else {
$prepared[ $key ] = $val;
}
}
// Add a placeholder.
if ( $this->settings['placeholder'] ) {
$this->settings['default'] = '';
$prepared = array_merge( array( '' => $this->settings['placeholder'] ), $prepared );
}
return $prepared;
}
/**
* Retrieve options list data based on the options_preset settings.
*
* @since 5.0.0
*
* @return void
*/
protected function prepare_options_from_preset() {
$preset_id = $this->settings['options_preset'];
switch ( $preset_id ) {
case 'countries':
$options = get_lifterlms_countries();
$default = get_lifterlms_country();
$this->settings['wrapper_classes'][] = 'llms-l10n-country-select';
break;
case 'states':
$options = llms_get_states();
$this->settings['wrapper_classes'][] = 'llms-l10n-state-select';
break;
default:
/**
* Define custom / 3rd party presets
*
* @since 5.0.0
*
* @param array $options Array of options.
* @param array $settings Prepared field settings.
* @param LLMS_Form_Field $fomr_field Form field object instance.
*/
$options = apply_filters( "llms_form_field_options_preset_{$preset_id}", array(), $this->settings, $this );
}
if ( isset( $options ) ) {
$this->settings['options'] = $options;
}
if ( isset( $default ) && ! $this->settings['default'] ) {
$this->settings['default'] = $default;
}
}
/**
* Additional preparation for the password strength meter.
*
* @since 5.0.0
* @since 5.10.0 Make sure to enqueue the strength meter js, whether or not `wp_enqueue_scripts` hook has been fired yet.
*
* @return void
*/
protected function prepare_password_strength_meter() {
$meter_settings = array(
'blocklist' => array(),
'min_strength' => ! empty( $this->settings['min_strength'] ) ? $this->settings['min_strength'] : 'strong',
'min_length' => ! empty( $this->settings['min_length'] ) ? max( 6, $this->settings['min_length'] ) : 6,
);
// Backwards compat functionality ends up outputting a minlength attribute on the <div> and we don't want that.
unset( $this->settings['min_length'] );
/**
* Modify password strength meter settings.
*
* @since 5.0.0
*
* @param array $meter_settings {
* Hash of meter configuration options.
*
* @type string[] $blocklist A list of strings that are penalized when used in the password. See "user_inputs" at https://github.com/dropbox/zxcvbn#usage.
* @type string $min_strength The minimum acceptable password strength. Accepts "strong", "medium", or "weak". Default: "strong".
* @type int $min_length The minimum acceptable password length. Must be >= 6. Default: 6.
* }
*/
$meter_settings = apply_filters( 'llms_password_strength_meter_settings', $meter_settings, $this->settings, $this );
// If scripts have been enqueued, add password strength meter script.
if ( did_action( 'wp_enqueue_scripts' ) ) {
return $this->enqueue_strength_meter( $meter_settings );
}
// Otherwise add it whe `wp_enqueue_scripts` is fired.
add_action(
'wp_enqueue_scripts',
function() use ( $meter_settings ) {
$this->enqueue_strength_meter( $meter_settings );
}
);
}
/**
* Enqueue password strength meter script.
*
* @since 5.10.0
*
* @param array $meter_settings {
* Hash of meter configuration options.
*
* @type string[] $blocklist A list of strings that are penalized when used in the password. See "user_inputs" at https://github.com/dropbox/zxcvbn#usage.
* @type string $min_strength The minimum acceptable password strength. Accepts "strong", "medium", or "weak". Default: "strong".
* @type int $min_length The minimum acceptable password length. Must be >= 6. Default: 6.
* }
* @return void
*/
private function enqueue_strength_meter( $meter_settings ) {
wp_enqueue_script( 'password-strength-meter' );
// Localize the script with meter data.
llms()->assets->enqueue_inline(
'llms-pw-strength-settings',
'window.LLMS.PasswordStrength = window.LLMS.PasswordStrength || {};window.LLMS.PasswordStrength.get_settings = function() { return JSON.parse( \'' . wp_json_encode( $meter_settings ) . '\' ); };',
'footer',
15
);
}
/**
* Setup default storage information.
*
* Ensures fields stored on the wp_users table have the proper default `data_store`.
*
* @since 5.0.0
*
* @return void
*/
protected function prepare_storage() {
$name = $this->settings['name'];
// Field Name => Storage Key.
$users_fields = array(
// We prefer these aliases for legacy reasons.
'email_address' => 'user_email',
'password' => 'user_pass',
// Default wp_users column names.
'user_login' => 'user_login',
'user_pass' => 'user_pass',
'user_nicename' => 'user_nicename',
'user_email' => 'user_email',
'user_url' => 'user_url',
'display_name' => 'display_name',
);
// Set data storage for items on the wp_users table.
if ( in_array( $name, array_keys( $users_fields ), true ) ) {
$this->settings['data_store'] = 'users';
$name = $users_fields[ $name ];
// Don't save default core confirmation fields.
} elseif ( in_array( $name, array( 'email_address_confirm', 'password_confirm' ), true ) ) {
$this->settings['data_store'] = false;
}
$this->settings['data_store_key'] = $name;
}
/**
* Prepare the field's value.
*
* @since 5.0.0
* @since 5.9.0 Stop using deprecated `FILTER_SANITIZE_STRING`.
*
* @return void
*/
protected function prepare_value() {
// Never autoload passwords and or fields with an explicit value (except radio and checkbox).
if ( 'password' === $this->settings['type'] || ! empty( $this->settings['value'] && ! in_array( $this->settings['type'], array( 'checkbox', 'radio' ), true ) ) ) {
return;
}
$user_val = null;
// Attempt to populate field data from the most recent $_POST action.
if ( 'POST' === strtoupper( getenv( 'REQUEST_METHOD' ) ) ) {
$posted = wp_unslash( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce is verified prior to reaching this method.
if ( isset( $posted[ $this->settings['name'] ] ) ) {
$filter_options = is_array( $posted[ $this->settings['name'] ] ) ? array( FILTER_REQUIRE_ARRAY ) : array();
$user_val = llms_filter_input_sanitize_string( INPUT_POST, $this->settings['name'], $filter_options );
}
}
// Auto-populate field from the datastore if we have a user and datastore information.
if ( is_null( $user_val ) && ( isset( $this->data_source ) && 'wp_user' === $this->data_source_type ) && $this->settings['data_store_key'] ) {
$user_val = $this->data_source->get( $this->settings['data_store_key'] );
}
// Set the value to the user's submitted or stored value.
if ( ! is_null( $user_val ) ) {
if ( in_array( $this->settings['type'], array( 'checkbox', 'radio' ), true ) && ! $this->is_input_group() ) {
$this->settings['checked'] = ( $this->settings['value'] === $user_val );
} else {
$this->settings['value'] = $user_val;
}
}
// Handle "default" alias "selected".
if ( isset( $this->settings['selected'] ) && '' !== $this->settings['selected'] ) {
$this->settings['default'] = $this->settings['selected'];
}
// Add default value if there's no explicit value and a default value is set.
if ( ! $this->settings['value'] && ! is_array( $this->settings['value'] ) && '' !== $this->settings['default'] ) {
$this->settings['value'] = $this->settings['default'];
}
}
/**
* Additional preparation for the special voucher field.
*
* @since 5.0.0
*
* @return void
*/
protected function prepare_voucher() {
if ( ! $this->settings['required'] && $this->settings['toggleable'] ) {
$this->settings['label'] = sprintf( '<a class="llms-voucher-toggle" id="llms-voucher-toggle" href="#">%s</a>', $this->settings['label'] );
$this->settings['attributes']['style'] = 'display:none;';
$this->settings['data_store_key'] = false;
}
}
/**
* Prepare CSS wrapper classes for the field.
*
* @since 5.0.0
*
* @return void
*/
protected function prepare_wrapper_classes() {
$defaults = array();
// Base field class.
$defaults[] = 'llms-form-field';
// Add class for the field type.
$defaults[] = sprintf( 'type-%s', $this->settings['type'] );
if ( $this->is_input_group() ) {
$defaults[] = 'is-group';
}
// Add columns classes.
$defaults[] = sprintf( 'llms-cols-%d', $this->settings['columns'] );
if ( $this->settings['last_column'] ) {
$defaults[] = 'llms-cols-last';
}
// If required, add a class.
if ( $this->settings['required'] ) {
$defaults[] = 'llms-is-required';
}
$this->settings['wrapper_classes'] = $this->classes_ensure_array(
$this->settings['wrapper_classes'],
$defaults
);
}
/**
* Render/output the field's html.
*
* @since 5.0.0
*
* @return void
*/
public function render() {
if ( ! $this->html ) {
$this->get_html();
}
echo $this->html;
}
}