<?php

if ( ! defined( 'ABSPATH' ) ) {
	die( 'You are not allowed to call this page directly.' );
}

class FrmProFieldsController {

	/**
	 * Store and re-use the pro field selection data for the add_pro_field_class function.
	 *
	 * @since 6.10
	 *
	 * @var array|null
	 */
	private static $pro_field_selection_for_add_pro_field_class;

	/**
	 * Store and re-use the license state when calling add_pro_field_class in order to reduce calls to
	 * FrmProAddonsController::is_expired_outside_grace_period.
	 *
	 * @since 6.10
	 *
	 * @var bool|null
	 */
	private static $is_expired_outside_of_grace_period;

	/**
	 * Store and re-use the choices limit reached statuses for fields.
	 *
	 * @since 6.28
	 *
	 * @var array
	 */
	private static $choices_limit_reached_statuses = array();

	/**
	 * @param string $type
	 * @param array $field
	 *
	 * @return string
	 */
	public static function change_type( $type, $field ) {
		remove_filter( 'frm_field_type', 'FrmFieldsController::change_type' );

		// Don't change user ID fields or repeating sections to hidden
		if ( ! ( $type === 'divider' && FrmField::is_option_true( $field, 'repeat' ) ) && $type !== 'user_id' && ! FrmProGlobalVarsHelper::get_instance()->field_is_visible( $field ) ) {
			$type = 'hidden';
		}

		if ( $type === '10radio' ) {
			$type = 'scale';
		}

		if ( ! FrmAppHelper::is_admin() && $type !== 'hidden' && $type !== 'divider' && ! FrmProFieldsHelper::is_field_visible_to_user( $field ) ) {
			return 'hidden';
		}

		return $type;
	}

	/**
	 * @param array $field
	 */
	public static function use_field_key_value( $opt, $opt_key, $field ) {
		// if(in_array($field['post_field'], array( 'post_category', 'post_status')) or ($field['type'] === 'user_id' and is_admin() and current_user_can('administrator')))
		if ( FrmField::is_option_true( $field, 'use_key' ) ||
			( isset( $field['type'] ) && $field['type'] === 'data' ) ||
			( isset( $field['post_field'] ) && $field['post_field'] === 'post_status' )
		) {
			return $opt_key;
		}

		return $opt;
	}

	/**
	 * @param array    $field
	 * @param stdClass $form
	 * @param int      $parent_form_id
	 *
	 * @return void
	 */
	public static function show_field( $field, $form, $parent_form_id ) {
		if ( 'virtual' === $field['type'] ) {
			return;
		}

		global $frm_vars;

		$is_currency          = ! empty( $field['is_currency'] ) || FrmProCurrencyHelper::is_currency_format( FrmField::get_option( $field, 'format' ) );
		$is_custom_range      = 'range' === $field['type'] && $is_currency;
		$has_formatted_number = empty( $field['calc'] ) && $is_currency;

		if ( empty( $field['calc'] ) && ! $is_custom_range && ! $has_formatted_number ) {
			return;
		}

		/**
		 * We should always pass this check for 'range' field to avoid an issue for ajax
		 * form submission in multi-page forms when slider is formatted as currency.
		 */
		if ( $field['type'] !== 'range' && FrmProForm::is_ajax_on( $form ) && FrmAppHelper::doing_ajax() && empty( $frm_vars['inplace_edit'] ) ) {
			return;
		}

		if ( ! isset( $frm_vars['calc_fields'] ) ) {
			$frm_vars['calc_fields'] = array();
		}

		$attributes = array(
			'field'          => $field,
			'form_id'        => $form->id,
			'parent_form_id' => $parent_form_id,
		);

		if ( $is_custom_range ) {
			$attributes['calc'] = '[' . absint( $field['id'] ) . ']';

			if ( empty( $field['custom_currency'] ) ) {
				// Make sure default currency is loaded if field does not use a custom currency.
				FrmProCurrencyHelper::add_currency_to_global( $field['parent_form_id'] );
			}
		}

		$frm_vars['calc_fields'][ $field['field_key'] ] = FrmProFormsHelper::get_calc_rule_for_field( $attributes );
	}

	/**
	 * @since 6.28
	 *
	 * @param array $args
	 *
	 * @return void
	 */
	public static function admin_single_opt( $args ) {
		$field             = $args['field'];
		$opt_key           = $args['opt_key'];
		$choice_limit      = $field['options'][ $opt_key ]['limit'] ?? '';
		$set_choices_limit = (bool) FrmField::get_option( $field, 'set_choices_limit' );

		include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/choice-limit.php';
	}

	/**
	 * @since 6.28
	 *
	 * @param bool  $default
	 * @param bool  $choice_key
	 * @param bool  $is_selected_choice
	 * @param array $field
	 *
	 * @return bool
	 */
	public static function should_disable_choice( $default, $choice_key, $is_selected_choice, $field ) {
		$choice_limit_reached = self::choices_limit_reached_statuses( $field )[ $choice_key ] ?? false;

		if ( ! $choice_limit_reached ) {
			return false;
		}

		if ( ! $is_selected_choice ) {
			return true;
		}

		global $frm_vars;

		return empty( $frm_vars['editing_entry'] );
	}

	/**
	 * @since 6.28
	 *
	 * @param bool   $default
	 * @param string $choice_key
	 * @param array  $field
	 *
	 * @return bool
	 */
	public static function should_hide_field_choice( $default, $choice_key, $field ) {
		$choice_limit_reached = self::choices_limit_reached_statuses( $field )[ $choice_key ] ?? false;

		if ( ! $choice_limit_reached ) {
			return false;
		}

		return FrmProFieldsHelper::should_hide_maxed_out_field_choices( $field['form_id'], $field, $choice_key );
	}

	/**
	 * @since 6.28
	 *
	 * @param bool  $default
	 * @param array $field
	 *
	 * @return bool
	 */
	public static function should_skip_rendering_choices_for_field( $default, $field ) {
		if ( ! FrmProFieldsHelper::should_show_choices_limit_message( self::choices_limit_reached_statuses( $field ), $field ) ) {
			return false;
		}

		echo esc_html( FrmFieldsHelper::get_error_msg( $field, 'choice_limit_msg' ) );
		return true;
	}

	/**
	 * @since 6.28
	 *
	 * @param array $field
	 *
	 * @return array
	 */
	public static function choices_limit_reached_statuses( $field ) {
		if ( ! empty( self::$choices_limit_reached_statuses[ $field['id'] ] ) ) {
			return self::$choices_limit_reached_statuses[ $field['id'] ];
		}

		$choices_limit_reached_statuses = array();

		foreach ( $field['options'] as $opt_key => $opt ) {
			$choices_limit_reached_statuses[ $opt_key ] = FrmProFieldsHelper::choice_limit_reached( $field, $opt_key );
		}

		self::$choices_limit_reached_statuses[ $field['id'] ] = $choices_limit_reached_statuses;

		return $choices_limit_reached_statuses;
	}

	/**
	 * @since 6.28
	 *
	 * @param array  $field
	 * @param string $opt_key
	 *
	 * @return void
	 */
	public static function after_choice_input( $field, $opt_key ) {
		if ( ! FrmProFieldsHelper::should_show_remaining_choices( $field ) || FrmField::get_option( $field, 'image_options' ) ) {
			return;
		}

		$choice_entry_data = FrmProFieldsHelper::get_choice_entry_data( $field['id'], $opt_key );

		if ( ! $choice_entry_data ) {
			return;
		}

		$choices_left = $choice_entry_data['limit'] - $choice_entry_data['count'];
		FrmAppHelper::kses_echo( FrmProFieldsHelper::get_remaining_qty_message( $choices_left, $field ), 'all' );
	}

	/**
	 * @since 6.28
	 *
	 * @param array $default_field_validation_messages
	 *
	 * @return array
	 */
	public static function default_field_validation_messages( $default_field_validation_messages ) {
		$choice_limit_msg = __( 'All choices have reached their entry limit', 'formidable-pro' );

		$default_field_validation_messages['choice_limit_msg'] = array(
			'full' => $choice_limit_msg,
			'part' => $choice_limit_msg,
		);
		return $default_field_validation_messages;
	}

	/**
	 * Runs after the last validation message is added.
	 *
	 * @since 6.28
	 *
	 * @param array $display
	 * @param array $field
	 *
	 * @return void
	 */
	public static function field_validation_messages( $display, $field ) {
		if ( empty( $display['choice_limit'] ) ) {
			return;
		}
		include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/choices-maxed-out-message.php';
	}

	/**
	 * @param array $field
	 */
	public static function build_field_class( $classes, $field ) {
		if ( 'inline' === $field['conf_field'] ) {
			$classes .= ' frm_conf_inline';
		} elseif ( 'below' === $field['conf_field'] ) {
			$classes .= ' frm_conf_below';
		}

		$columns = '';

		if ( FrmField::is_field_type( $field, 'checkbox' ) || FrmField::is_field_type( $field, 'radio' ) ) {
			$columns   = $field['align'] ?? '';
			$field_obj = FrmFieldFactory::get_field_type( $field['type'], $field );

			$field_obj->prepare_align_class( $columns );
		}

		$classes = str_replace( ' frmstart ', ' frmstart ' . $columns . ' ', $classes );

		self::add_pro_field_class( $field, $classes );

		return $classes;
	}

	/**
	 * @since 6.28
	 *
	 * @return void
	 */
	public static function include_remaining_qty_modal() {
		if ( ! FrmAppHelper::is_form_builder_page( false ) ) {
			return;
		}

		include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/choices-remaining-qty-modal.php';
	}

	public static function update_choice_limit_settings() {
		FrmAppHelper::permission_check( 'frm_edit_forms' );
		check_ajax_referer( 'frm_ajax', 'nonce' );

		$field_id = FrmAppHelper::get_post_param( 'field_id', 0, 'absint' );

		if ( ! $field_id ) {
			wp_send_json_error( __( 'Field ID is required', 'formidable-pro' ) );
		}

		$remaining_qty_label = FrmAppHelper::get_post_param( 'remaining_qty_label', '', 'sanitize_text_field' );
		$exhausted_message   = FrmAppHelper::get_post_param( 'exhausted_message', '', 'sanitize_text_field' );
		$field_options       = FrmDb::get_var( 'frm_fields', array( 'id' => $field_id ), 'field_options' );

		if ( ! $field_options ) {
			wp_send_json_error( __( 'Field options are required', 'formidable-pro' ) );
		}

		FrmAppHelper::unserialize_or_decode( $field_options );

		$field_options['remaining_qty_label'] = $remaining_qty_label;
		$field_options['exhausted_message']   = $exhausted_message;

		FrmField::update(
			$field_id,
			array(
				'field_options' => $field_options,
			)
		);

		wp_send_json_success();
	}

	/**
	 * @since 6.5.1
	 *
	 * @param array  $field
	 * @param string $classes
	 *
	 * @return void
	 */
	private static function add_pro_field_class( $field, &$classes ) {
		if ( ! self::is_expired_outside_grace_period() ) {
			return;
		}

		$pro_fields = self::get_pro_field_selection();

		if ( isset( $pro_fields[ $field['type'] ] ) ) {
			$classes .= ' frm_noallow frm_show_upgrade frm_show_expired_modal';
		}
	}

	/**
	 * @since 6.18
	 *
	 * @param string $options_view_path The path to the options view file.
	 *
	 * @return string
	 */
	public static function get_format_options_path( $options_view_path ) {
		return FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/format-dropdown-options.php';
	}

	/**
	 * @since 6.10
	 *
	 * @return array
	 */
	private static function get_pro_field_selection() {
		if ( ! isset( self::$pro_field_selection_for_add_pro_field_class ) ) {
			self::$pro_field_selection_for_add_pro_field_class = FrmField::pro_field_selection();
		}
		return self::$pro_field_selection_for_add_pro_field_class;
	}

	/**
	 * @since 6.10
	 *
	 * @return bool
	 */
	private static function is_expired_outside_grace_period() {
		if ( ! isset( self::$is_expired_outside_of_grace_period ) ) {
			self::$is_expired_outside_of_grace_period = FrmProAddonsController::is_expired_outside_grace_period();
		}
		return self::$is_expired_outside_of_grace_period;
	}

	/**
	 * @param array $field
	 * @param bool  $echo
	 *
	 * @return string
	 */
	public static function input_html( $field, $echo = true ) {
		$add_html = '';

		self::add_readonly_input_attributes( $field, $add_html );

		self::maybe_add_data_attribute_for_section( $field, $add_html );

		self::add_multiple_select_attribute( $field, $add_html );
		self::add_select_placeholder( $field, $add_html );

		if ( FrmAppHelper::is_admin_page( 'formidable' ) ) {
			if ( $echo ) {
				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
				echo $add_html;
			}

			// Don't continue if we are on the form builder page
			return $add_html;
		}

		FrmProLookupFieldsController::maybe_add_lookup_input_html( $field, $add_html );

		self::add_input_attributes( $field, $add_html );
		self::add_checkbox_limit( $field, $add_html );

		$add_html .= self::setup_input_masks( $field );

		self::add_currency_field_attributes( $field, $add_html );

		self::add_html_autocomplete( $field, $add_html );

		if ( $echo ) {
			// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			echo $add_html;
		}

		return $add_html;
	}

	/**
	 * Add additional classes to input elements.
	 *
	 * @since 6.18
	 *
	 * @param string $class Existing input classes.
	 * @param array  $field The field data.
	 *
	 * @return string
	 */
	public static function add_input_classes( $class, $field ) {
		if ( isset( $field['format'] ) && FrmProCurrencyHelper::is_currency_format( $field['format'] ) && empty( $field['calc'] ) && 'range' !== $field['type'] ) {
			$class .= ' frm-has-number-format';
		}

		return $class;
	}

	/**
	 * Add autocomplete attribute to the field html.
	 *
	 * @since 5.4.1
	 *
	 * @param array  $field The field properties.
	 * @param string $add_html The field html.
	 *
	 * @return void
	 */
	private static function add_html_autocomplete( $field, &$add_html ) {
		if ( ! empty( $field['autocomplete'] ) ) {
			$add_html .= ' autocomplete="' . esc_attr( $field['autocomplete'] ) . '" ';
		}
	}

	/**
	 * @param array $field
	 *
	 * @return string
	 */
	public static function setup_input_masks( $field ) {
		$text_lookup         = $field['type'] === 'lookup' && $field['data_type'] === 'text';
		$is_format_field     = in_array( $field['type'], array( 'phone', 'text' ), true ) || $text_lookup;
		$international_phone = 'phone' === $field['type'] && 'international' === $field['format'];

		if ( FrmProField::is_format_option_true_with_no_regex( $field ) && $is_format_field && ! $international_phone ) {
			return self::setup_input_mask( $field['format'] );
		}

		return '';
	}

	/**
	 * Setup the input mask for the field.
	 *
	 * @param string $format
	 *
	 * @return string
	 */
	public static function setup_input_mask( $format ) {
		if ( FrmProCurrencyHelper::is_currency_format( $format ) ) {
			return '';
		}

		self::setup_input_mask_global_for_currency_field();

		return ' data-frmmask="' . esc_attr( self::convert_format_for_imask( $format ) ) . '"';
	}

	/**
	 * Setup the input mask global variable for a currency field.
	 * This just makes sure that the global is not empty.
	 *
	 * @since 6.32
	 *
	 * @return void
	 */
	private static function setup_input_mask_global_for_currency_field() {
		global $frm_input_masks;

		if ( $frm_input_masks ) {
			// No need to set up the global variable if it's already set.
			return;
		}

		if ( ! is_array( $frm_input_masks ) ) {
			$frm_input_masks = array();
		}

		$frm_input_masks[] = true;
	}

	/**
	 * Convert the format to a format that can be used by imask.
	 *
	 * @since 6.23
	 *
	 * @param string $format
	 *
	 * @return string
	 */
	private static function convert_format_for_imask( $format ) {
		$format = preg_replace( '/\d/', '0', $format );

		// Convert something following a question mark to something wrapped in square braces.
		// This is used to indicate that the character is optional.
		// Example: ?1abc to [1abc].
		$format = preg_replace( '/\?(.+)/s', '[$1]', $format );

		return is_string( $format ) ? $format : '';
	}

	/**
	 * Add product attributes to fields in multi-paged forms.
	 *
	 * @since 4.04
	 *
	 * @param array      $field
	 * @param string     $add_html
	 * @param mixed|null $parent
	 *
	 * @return void
	 */
	public static function add_currency_field_attributes( $field, &$add_html, $parent = null ) {
		$type             = $field['original_type'] ?? $field['type'];
		$is_product_field = in_array( $type, array( 'total', 'quantity', 'product' ), true );

		if ( ! $is_product_field ) {
			return;
		}

		if ( $type === 'total' ) {
			$add_html .= ' data-frmtotal ';
		} elseif ( $type === 'quantity' ) {
			$product_field = FrmField::get_option( $field, 'product_field' );
			$add_html     .= ' data-frmproduct="' . esc_attr( json_encode( $product_field ) ) . '" ';
		} elseif ( $type === 'product' && 'hidden' === $field['type'] ) {
			// We want to do this only for fields that are hidden because it's
			// not their page, hence the check : 'hidden' === $field['type'].
			$price = empty( $field['value'] ) ? 0 : self::get_product_price( $field );

			/**
			 * This helps to know if this field should be included in the total calc of the current page by JS.
			 * Fields on higher pages aren't included, else you get error of invalid total on submission.
			 *
			 * For fields in a repeater or embedded form, their parents are used instead, else the page check may be incorrect.
			 */
			$use_this  = $parent ?? $field;
			$higher_pg = FrmProFieldsHelper::field_on_page( $use_this, 'higher' ) ? 'data-frmhigherpg ' : '';

			$add_html .= ' data-frmprice="' . esc_attr( $price ) . '" ' . $higher_pg;
		}

		if ( FrmProFieldsHelper::is_on_skipped_page( $field, $parent ) ) {
			$add_html .= ' data-frmhidden="1" ';
		}
	}

	/**
	 * @param array $field
	 */
	private static function get_product_price( $field ) {
		if ( is_array( $field['value'] ) ) {
			// '' is unlikely though, let's just do it to prevent warnings
			$value = isset( $field['opt_key'] ) && isset( $field['value'][ $field['opt_key'] ] ) ?
						$field['value'][ $field['opt_key'] ] : '';
		} else {
			$value = $field['value'];
		}

		$field_obj = FrmFieldFactory::get_field_object( $field['id'] );
		return $field_obj->get_posted_price( $value );
	}

	/**
	 * Add readonly/disabled input attributes
	 *
	 * @since 2.02.06
	 *
	 * @param array $field
	 * @param string $add_html
	 *
	 * @return void
	 */
	private static function add_readonly_input_attributes( $field, &$add_html ) {
		if ( ! FrmField::is_option_true( $field, 'read_only' ) || $field['type'] === 'hidden' || $field['type'] === 'lookup' ) {
			return;
		}

		global $frm_vars;

		if ( ( isset( $frm_vars['readonly'] ) && $frm_vars['readonly'] === 'disabled' ) || ( current_user_can( 'frm_edit_entries' ) && FrmAppHelper::is_admin() ) ) {
			// Not read only
		} elseif ( in_array( $field['type'], array( 'select', 'radio', 'checkbox', 'time' ), true ) ) {
			$add_html .= ' disabled="disabled" ';
		} else {
			$add_html .= ' readonly="readonly" ';
		}
	}

	/**
	 * Add multiple select attribute
	 *
	 * @since 2.02.06
	 *
	 * @param array $field
	 * @param string $add_html
	 */
	private static function add_multiple_select_attribute( $field, &$add_html ) {
		if ( FrmField::is_multiple_select( $field ) ) {
			$add_html .= ' multiple="multiple" ';
		}
	}

	/**
	 * @since 4.0
	 *
	 * @param array|object $field
	 * @param string       $add_html
	 *
	 * @return void
	 */
	private static function add_select_placeholder( $field, &$add_html ) {
		if ( ! FrmField::is_field_type( $field, 'select' ) ) {
			return;
		}

		$placeholder  = FrmField::get_option( $field, 'placeholder' );
		$autocomplete = FrmField::get_option( $field, 'autocom' );

		if ( $placeholder === '' && ! $autocomplete ) {
			// The field doesn't need a placeholder.
			return;
		}

		if ( $placeholder ) {
			$use_placeholder = $placeholder;
		} else {
			$default         = FrmField::is_multiple_select( $field ) ? __( 'Select options', 'formidable-pro' ) : __( 'Select an option', 'formidable' );
			$use_placeholder = $default;
		}

		$add_html .= ' data-placeholder="' . esc_attr( $use_placeholder ) . '" ';
	}

	/**
	 * Add a few input attributes.
	 *
	 * @since 2.02.06
	 *
	 * @param array  $field
	 * @param string $add_html
	 *
	 * @return void
	 */
	private static function add_input_attributes( $field, &$add_html ) {
		global $frm_vars;

		if ( $field['type'] !== 'hidden' && FrmField::is_field_type( $field, 'select' ) && FrmProFieldsHelper::dropdown_html_requires_empty_data_placeholder( $field, $add_html ) ) {
			// Add a blank data-placeholder so Chosen shows an empty placeholder instead of its default text.
			$add_html .= ' data-placeholder=" "';
		}

		if ( in_array( $field['type'], array( 'url', 'email' ), true ) && empty( $frm_vars['novalidate'] ) && ( $field['type'] !== 'email' || ( isset( $field['value'] ) && $field['default_value'] == $field['value'] ) ) ) {
			// Add novalidate for drafts
			$frm_vars['novalidate'] = true;
		}
	}

	/**
	 * If the field has a limit set, add it to the HTML.
	 *
	 * @since 4.02
	 *
	 * @param array|object $field
	 * @param string       $add_html
	 *
	 * @return void
	 */
	private static function add_checkbox_limit( $field, &$add_html ) {
		$selections_limit = FrmField::get_option( $field, 'limit_selections' );

		if ( $selections_limit ) {
			$add_html .= ' data-frmlimit="' . esc_attr( $selections_limit ) . '" ';
		}

		$min_selections = FrmField::get_option( $field, 'min_selections' );

		if ( $min_selections ) {
			$add_html .= ' data-frmmin="' . esc_attr( $min_selections ) . '" ';
		}
	}

	/**
	 * Add data-sectionid attribute for fields in section
	 *
	 * @since 2.01.0
	 *
	 * @param array $field
	 * @param string $add_html
	 */
	private static function maybe_add_data_attribute_for_section( $field, &$add_html ) {
		if ( FrmField::is_option_true_in_array( $field, 'in_section' ) ) {
			$add_html .= ' data-sectionid="' . $field['in_section'] . '" ';
		}

		// TODO: Add data attribute for embedded form fields as well
	}

	/**
	 * Update field classes.
	 *
	 * @since 6.18
	 *
	 * @param string $class
	 * @param array  $field
	 *
	 * @return string
	 */
	public static function update_field_classes( $class, $field ) {
		$class = self::add_field_class( $class, $field );

		if ( self::is_password_field_with_show_password( $field ) ) {
			return str_replace( 'auto_width', '', $class );
		}

		return $class;
	}

	/**
	 * Check if the field is a password field with show password option enabled.
	 *
	 * @since 6.18
	 *
	 * @param array|object $field
	 *
	 * @return bool
	 */
	private static function is_password_field_with_show_password( $field ) {
		return FrmField::get_field_type( $field ) === 'password' && FrmField::get_option( $field, 'show_password' );
	}

	/**
	 * Updates field extra html attributes.
	 *
	 * This is used to remove the width style from password fields' input element when show password option enabled
	 * to avoid double width constraint from the input since the container has the same width applied to it.
	 *
	 * @since 6.18
	 *
	 * @param array $html
	 * @param array $field
	 *
	 * @return array
	 */
	public static function field_extra_html( $html, $field ) {
		if ( empty( $html['style'] ) || ! self::is_password_field_with_show_password( $field ) ) {
			return $html;
		}
		$html['style'] = preg_replace( '/\bwidth\s*:\s*[^;]+;?/', '', $html['style'] );
		return $html;
	}

	/**
	 * @param string $class
	 * @param array  $field
	 *
	 * @return string
	 */
	public static function add_field_class( $class, $field ) {
		if ( ( FrmAppHelper::is_admin() && ! FrmAppHelper::is_admin_page( 'formidable-entries' ) ) || FrmField::is_read_only( $field ) ) {
			return $class;
		}

		$is_hidden = FrmField::get_field_type( $field ) === 'hidden';

		if ( ! $is_hidden && FrmField::is_option_true( $field, 'autocom' ) && FrmField::is_field_type( $field, 'select' ) && ! empty( $field['options'] ) ) {
			self::add_autocomplete_classes( $field, $class );
		}

		if ( 'phone' === $field['type'] && 'international' === $field['format'] ) {
			$class .= ' frm-intl-tel-input';
		}

		return trim( $class );
	}

	/**
	 * Add the autocomplete classes to a $class string.
	 *
	 * @since 2.01.0
	 *
	 * @param array  $field
	 * @param string $class
	 *
	 * @return void
	 */
	public static function add_autocomplete_classes( $field, &$class ) {
		global $frm_vars;

		$frm_vars['autocomplete_loaded'] = true;
		$use_slim_select                 = ! FrmProAppHelper::use_chosen_js();

		if ( $use_slim_select ) {
			$class .= ' frm_slimselect';
		} else {
			// For legacy support, continue to set this global for chosen.
			$frm_vars['chosen_loaded'] = true;
			$class                    .= ' frm_chzn';
		}

		$style = FrmStylesController::get_form_style( $field['form_id'] );

		if ( ! $style || 'rtl' !== $style->post_content['direction'] ) {
			return;
		}

		if ( $use_slim_select ) {
			$class .= ' frm_slimselect_rtl';
		} else {
			$class .= ' chosen-rtl';
		}
	}

	/**
	 * Add Other Option after click.
	 *
	 * @since 4.0
	 */
	public static function add_other_option() {
		FrmAppHelper::permission_check( 'frm_edit_forms' );
		check_ajax_referer( 'frm_ajax', 'nonce' );

		$id      = FrmAppHelper::get_post_param( 'field_id', 0, 'absint' );
		$opt_key = FrmAppHelper::get_post_param( 'opt_key', 0, 'absint' );

		$field      = FrmField::getOne( $id );
		$field_data = $field;
		$field      = (array) $field;

		$field['separate_value'] = $field_data->field_options['separate_value'] ?? 0;
		unset( $field_data );

		$field['html_name'] = 'item_meta[' . $field['id'] . ']';
		$field['options']   = array( 'other_' . $opt_key => __( 'Other', 'formidable' ) );
		FrmFieldsHelper::show_single_option( $field );

		wp_die();
	}

	public static function options_form_before( $field ) {
		include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/options-before.php';
	}

	/**
	 * Add currency format to the specified field.
	 *
	 * @since 6.18
	 *
	 * @param array $field Field data.
	 *
	 * @return void
	 */
	public static function add_currency_format( $field ) {
		if ( FrmProFieldsHelper::supports_currency_format( $field['type'] ) ) {
			require FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/currency-format.php';
		}
	}

	/**
	 * @param array $field
	 * @param array $display
	 */
	public static function options_form_top( $field, $display, $values ) {
		$frmpro_settings         = FrmProAppHelper::get_settings();
		$show_jquery_placeholder = FrmProAppHelper::use_jquery_datepicker() && 'date' === $field['type'];

		/* translators: %1$s: Field type. %2$s: Field type. %3$s: Field type */
		$range_option_tooltip = sprintf( __( '%1$s Range: Enables the %2$s range option for %3$s fields', 'formidable-pro' ), ucfirst( $field['type'] ), $field['type'], $field['type'] );

		if ( ! empty( $display['range_field'] ) ) {
			include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/range-option.php';
		} elseif ( 'date' === $field['type'] && ! function_exists( 'frm_dates_autoloader' ) ) {
			// In-product education for the Date Range option when the Dates add-on is not active.
			include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/range-option-education.php';
		}
	}

	/**
	 * @since 4.0
	 *
	 * @param array $args {
	 *
	 *     @type array $display
	 *     @type array $field
	 * }
	 *
	 * @return void
	 */
	public static function advanced_field_options( $args ) {
		$display     = $args['display'];
		$field       = $args['field'];
		$is_checkbox = FrmField::is_field_type( $field, 'checkbox' );
		$is_radio    = FrmField::is_field_type( $field, 'radio' );

		if ( $display['type'] === 'radio' || $display['type'] === 'checkbox' || $is_radio || $is_checkbox ) {
			self::alignment_setting( $args );
		}

		if ( ! empty( $display['prefix'] ) ) {
			include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/prepend-options.php';
		}

		if ( $field['type'] === 'divider' && ! empty( $field['repeat'] ) ) {
			include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/repeat-options.php';
		}

		if ( 'textarea' === $field['type'] ) {
			include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/auto-grow.php';
			include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/content-limit.php';
		}

		if ( ! empty( $display['autocomplete'] ) ) {
			self::show_autocomplete_option( $args['field'] );
		}

		if ( ! empty( $display['visibility'] ) ) {
			self::show_visibility_option( $args['field'] );
		}
	}

	/**
	 * @since 4.0
	 *
	 * @param array $args - includes 'field'
	 *
	 * @return void
	 */
	public static function alignment_setting( $args ) {
		$field                      = $args['field'];
		$active_style_align_setting = '';

		// The alignment helpers live in Lite. Guard against an older Lite version that predates them.
		if ( is_callable( 'FrmStylesController::get_active_style' ) && is_callable( 'FrmStylesController::get_align_key_for_style_settings' ) ) {
			$active_style               = FrmStylesController::get_active_style( $field );
			$field_type                 = FrmField::is_checkbox( $field ) ? 'checkbox' : 'radio';
			$key                        = FrmStylesController::get_align_key_for_style_settings( $field_type );
			$active_style_align_setting = $active_style->post_content[ $key ] ?? '';
		}

		$align_options = self::get_align_setting_options();

		if ( $active_style_align_setting && ! empty( $align_options[ $active_style_align_setting ] ) ) {
			$columns = array(
				/* translators: %s: Alignment option label */
				'' => sprintf( __( 'Use Style (%s)', 'formidable-pro' ), $align_options[ $active_style_align_setting ] ),
			);
		} else {
			$columns = array();
		}

		$columns += $align_options;

		include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/alignment.php';
	}

	/**
	 * @since 6.32
	 *
	 * @return array
	 */
	private static function get_align_setting_options() {
		return array(
			'block'         => __( 'One Column', 'formidable' ),
			'frm_two_col'   => __( 'Two Columns', 'formidable-pro' ),
			'frm_three_col' => __( 'Three Columns', 'formidable-pro' ),
			'frm_four_col'  => __( 'Four Columns', 'formidable-pro' ),
			'inline'        => __( 'Inline Options', 'formidable' ),
		);
	}

	/**
	 * @since 4.0
	 *
	 * @param array $args Includes 'field', 'display', and 'values'.
	 *
	 * @return void
	 */
	public static function options_form_after( $args ) {
		if ( ! empty( $args['display']['logic'] ) ) {
			self::show_conditional_logic_option( $args['field'] );
		}
	}

	/**
	 * Add calc details.
	 *
	 * @since 4.0
	 *
	 * @param array $types
	 * @param array $atts Includes 'display'
	 */
	public static function default_value_types( $types, $atts ) {
		$types['calc']['class'] = ''; // Remove upgrade links.
		$types['calc']['data']  = array(
			'show'    => '.frm-calc-for-{id}',
			'disable' => '#default-value-for-{id}',
		);

		// Backwards compatibility "@since 6.24".
		if ( ! class_exists( 'FrmTextToggleStyleComponent' ) ) {
			$types['calc']['data'] = array(
				'frmshow' => '#calc-for-',
			);
		}

		if ( empty( $atts['display']['calc'] ) ) {
			unset( $types['calc'] );
		}

		$types['get_values_field']['class'] = ''; // Remove upgrade links.
		$types['get_values_field']['data']  = array(
			'show'    => '.frm-lookup-box-{id}',
			'disable' => '#default-value-for-{id}',
		);

		// Backwards compatibility "@since 6.24".
		if ( ! class_exists( 'FrmTextToggleStyleComponent' ) ) {
			$types['get_values_field']['data'] = array(
				'open'    => 'frm-lookup-box-',
				'frmshow' => '.frm-lookup-box-',
				'frmhide' => '.frm-inline-modal,.default-value-section-',
			);
		}

		if ( empty( $atts['display']['autopopulate'] ) ) {
			unset( $types['get_values_field'] );
		}

		// In-product education for Date calculation.
		if ( ! function_exists( 'frm_dates_autoloader' ) && isset( $atts['display']['type'] ) && 'date' === $atts['display']['type'] ) {
			$types['date_calc'] = array(
				'class' => 'frm_noallow',
				'title' => __( 'Default Value', 'formidable' ),
				'icon'  => 'frmfont frm_calculator_icon',
				'data'  => FrmProFieldDate::get_dates_add_on_upgrade_link_data(),
			);
		}

		return $types;
	}

	/**
	 * @since 4.0
	 *
	 * @param array $args - includes 'field', 'display', 'default_value_types'.
	 */
	public static function more_default_values( $args ) {
		$field               = $args['field'];
		$default_value_types = $args['default_value_types'];

		self::maybe_include_default_values( $field );

		if ( ! empty( $args['display']['calc'] ) ) {
			include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/calculations.php';
		}

		if ( ! empty( $args['display']['autopopulate'] ) ) {
			include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/autopopulate.php';
		}
	}

	/**
	 * Use a list of values in the field instead of smart values for
	 * post categories and dynamic fields.
	 *
	 * @since 4.01.02
	 *
	 * @param array $field
	 */
	private static function maybe_include_default_values( $field ) {
		$is_taxonomy = isset( $field['post_field'] ) && $field['post_field'] === 'post_category';
		$is_dynamic  = $field['type'] === 'data';

		if ( ! $is_taxonomy && ! $is_dynamic ) {
			return;
		}

		FrmFieldsHelper::inline_modal(
			array(
				'title'        => class_exists( 'FrmTextToggleStyleComponent' ) ? '' : __( 'Default Value', 'formidable' ), // Backwards compatibility "@since 6.24".
				'callback'     => array( 'FrmProFieldsController', 'default_dynamic_options' ),
				'args'         => $field,
				'id'           => 'frm-tax-box-' . $field['id'],
				'class'        => 'frm-tax-modal frm-tax-box-' . $field['id'],
				'dismiss-icon' => false,
			)
		);
	}

	/**
	 * Show a list of options in a dynamic field or category field
	 * in order to set the default value.
	 *
	 * @since 4.01.02
	 *
	 * @param array $field
	 */
	public static function default_dynamic_options( $field ) {
		$tags = $field['options'];
		include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/default-terms.php';
	}

	/**
	 * @since 4.0
	 *
	 * @param array $args - includes 'field'
	 */
	public static function calculation_settings( $args ) {
		$field = $args['field'];

		if ( class_exists( 'FrmTextToggleStyleComponent' ) ) {
			include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/calculation-settings.php';
		} else {
			// Backwards compatibility "@since 6.24".
			include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/backwards-compatibility/calculation-settings.php';
		}
	}

	/**
	 * Display the visibility option
	 *
	 * @since 2.02.06
	 *
	 * @param array $field
	 */
	public static function show_visibility_option( $field ) {
		include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/visibility.php';
	}

	/**
	 * Display the autocomplete option
	 *
	 * @since 5.4.1
	 *
	 * @param array $field
	 *
	 * @return void
	 */
	public static function show_autocomplete_option( $field ) {
		include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/autocomplete.php';
	}

	/**
	 * Display the conditional logic option
	 *
	 * @since 2.02.06
	 *
	 * @param array $field
	 */
	public static function show_conditional_logic_option( $field ) {
		$form_fields = false;

		// Migrate submit button conditional logic to submit field.
		if ( 'submit' === $field['type'] ) {
			$form = FrmForm::getOne( $field['form_id'] );

			if ( $form && ! empty( $form->options['submit_conditions'] ) ) {
				$keys_to_copy_over = array(
					'show_hide',
					'any_all',
					'hide_field',
					'hide_field_cond',
					'hide_opt',
				);

				foreach ( $keys_to_copy_over as $key ) {
					if ( isset( $form->options['submit_conditions'][ $key ] ) ) {
						$field[ $key ] = $form->options['submit_conditions'][ $key ];
					}
				}
			}
		}

		if ( ! empty( $field['hide_field'] ) && is_array( $field['hide_field'] ) ) {
			$form_id     = $field['parent_form_id'] ?? $field['form_id'];
			$form_fields = FrmProConditionalLogicOptionData::get_all_fields_for_form( $form_id );
		}

		include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/logic.php';
	}

	public static function get_field_selection() {
		FrmAppHelper::permission_check( 'frm_view_forms' );
		check_ajax_referer( 'frm_ajax', 'nonce' );

		$current_field_id = FrmAppHelper::get_post_param( 'field_id', '', 'absint' );
		$form_id          = FrmAppHelper::get_post_param( 'form_id', '', 'sanitize_text_field' );

		if ( is_numeric( $form_id ) ) {
			$selected_field = '';
			$fields         = self::get_field_selection_fields( $form_id );

			if ( $fields ) {
				require FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/field-selection.php';
			}
		} else {
			$selected_field = $form_id;

			if ( $selected_field === 'taxonomy' ) {
				echo '<div class="frm-inline-message">' . esc_html__( 'Select a taxonomy on the Form Actions tab of the Form Settings page', 'formidable-pro' ) . '</div>';
				echo '<input type="hidden" name="field_options[form_select_' . esc_attr( $current_field_id ) . ']" value="taxonomy" />';
			}
		}

		wp_die();
	}

	/**
	 * Gets fields for field selection.
	 *
	 * @since 5.0.04
	 *
	 * @param int $form_id Form ID.
	 *
	 * @return object[]
	 */
	public static function get_field_selection_fields( $form_id ) {
		$fields = FrmField::get_all_for_form( $form_id, '', 'include' );

		if ( $fields ) {
			/**
			 * Allows modifying fields in Field selection of Dynamic field.
			 *
			 * @since 5.0.04
			 *
			 * @param array $fields The fields.
			 * @param array $args   Includes `form_id`.
			 */
			return apply_filters( 'frm_pro_fields_in_dynamic_selection', $fields, compact( 'form_id' ) );
		}

		return $fields;
	}

	/**
	 * Get the field value selector for field or action logic
	 */
	public static function get_field_values() {
		FrmAppHelper::permission_check( 'frm_view_forms' );
		check_ajax_referer( 'frm_ajax', 'nonce' );

		$selector_args = array(
			'value' => '',
		);

		$selector_args['html_name'] = FrmAppHelper::get_post_param( 'name', '', 'sanitize_text_field' );

		if ( empty( $selector_args['html_name'] ) || $selector_args['html_name'] === 'undefined' ) {
			$selector_args['html_name'] = 'field_options[hide_opt_' . FrmAppHelper::get_post_param( 'current_field', 0, 'absint' ) . '][]';
		}

		if ( FrmAppHelper::get_param( 'form_action', '', 'get', 'sanitize_text_field' ) === 'update_settings' ) {
			$selector_args['source'] = 'form_actions';
		} else {
			$field_type              = FrmAppHelper::get_post_param( 't', '', 'sanitize_text_field' );
			$selector_args['source'] = $field_type ? $field_type : 'unknown';
		}

		FrmFieldsHelper::display_field_value_selector( FrmAppHelper::get_post_param( 'field_id', 0, 'absint' ), $selector_args );

		wp_die();
	}

	public static function get_dynamic_widget_opts() {
		check_ajax_referer( 'frm_ajax', 'nonce' );

		$display_id = FrmAppHelper::get_post_param( 'display_id', 0, 'absint' );

		if ( ! $display_id ) {
			wp_die();
		}

		$form_id = get_post_meta( $display_id, 'frm_form_id', true );

		if ( ! $form_id ) {
			wp_die();
		}

		$fields = FrmField::getAll(
			array(
				'fi.type not' => FrmField::no_save_fields(),
				'fi.form_id'  => $form_id,
			),
			'field_order'
		);

		$options = array(
			'titleValues' => array(),
			'catValues'   => array(),
		);

		foreach ( $fields as $field ) {
			$options['titleValues'][ $field->id ] = $field->name;

			if ( $field->type === 'select' || $field->type === 'radio' ) {
				$options['catValues'][ $field->id ] = $field->name;
			}
			unset( $field );
		}

		echo json_encode( $options );

		wp_die();
	}

	/**
	 * @param string $field_id
	 * @param array  $options
	 *
	 * @return void
	 */
	public static function date_field_js( $field_id, $options ) {
		if ( empty( $options['unique'] ) ) {
			return;
		}

		$defaults = array(
			'entry_id'   => 0,
			'start_year' => '-10',
			'end_year'   => '+10',
			'locale'     => '',
			'unique'     => 0,
			'field_id'   => 0,
		);

		$options = wp_parse_args( $options, $defaults );

		global $wpdb;

		$field = FrmField::getOne( $options['field_id'] );

		if ( isset( $field->field_options['post_field'] ) && $field->field_options['post_field'] != '' ) {
			$query = array( 'post_status' => array( 'publish', 'draft', 'pending', 'future', 'private' ) );

			if ( $field->field_options['post_field'] === 'post_custom' ) {
				$get_field             = 'meta_value';
				$get_table             = $wpdb->postmeta . ' pm LEFT JOIN ' . $wpdb->posts . ' p ON (p.ID=pm.post_id)';
				$query['meta_value !'] = '';
				$query['meta_key']     = $field->field_options['custom_field'];
			} else {
				$get_field = sanitize_title( $field->field_options['post_field'] );
				$get_table = $wpdb->posts;
			}

			$post_dates = FrmDb::get_col( $get_table, $query, $get_field );
		}

		if ( ! empty( $options['field_id'] ) ) {
			$disabled = wp_cache_get( $options['field_id'], 'frm_used_dates' );
		}

		if ( empty( $disabled ) ) {
			$disabled = FrmDb::get_col(
				'frm_item_metas',
				array(
					'field_id'  => $options['field_id'],
					'item_id !' => $options['entry_id'],
				),
				'meta_value'
			);
		}

		if ( ! empty( $post_dates ) ) {
			$disabled = array_unique( array_merge( (array) $post_dates, (array) $disabled ) );
		}

		/**
		 * Allows additional logic to be added to selectable dates
		 * To prevent weekends from being selectable, 'true' would be changed to '(day != 0 && day != 6)'
		 *
		 * @since 2.0
		 */
		$selectable_response = apply_filters( 'frm_selectable_dates', 'true', compact( 'field', 'options' ) );

		$disabled = apply_filters( 'frm_used_dates', $disabled, $field, $options );

		if ( ! empty( $options['field_id'] ) && $disabled ) {
			wp_cache_set( $options['field_id'], $disabled, 'frm_used_dates' );
		}

		// This function is added in v1.04 of the Dates add on. Before that version, these filters are not supported.
		if ( is_callable( 'FrmDatesAppHelper::plugin_version' ) ) {
			add_filter(
				'frm_dates_selectable_response',
				function ( $is_enabled, $filter_field ) use ( $selectable_response, $field ) {
					if ( ! $field->field_options['unique'] ) {
						return $is_enabled;
					}

					return $filter_field->field_key === $field->field_key ? $selectable_response : $is_enabled;
				},
				10,
				2
			);

			add_filter(
				'frm_dates_disabled',
				function ( $blackout_dates, $filter_field ) use ( $disabled, $field ) {
					if ( $filter_field->field_key !== $field->field_key ) {
						return $blackout_dates;
					}

					if ( ! is_array( $blackout_dates ) ) {
						return $disabled;
					}

					return array_merge( $blackout_dates, $disabled );
				},
				10,
				2
			);

			return;
		}

		// TO DO: remove this function when jQuery Datepicker is not supported anymore.
		self::legacy_datepicker_compatibility_handler( $disabled, $selectable_response );
	}

	/**
	 * TO DO: remove this function when jQuery Datepicker is not supported anymore.
	 *
	 * @since 6.19
	 *
	 * @param array  $disabled
	 * @param string $selectable_response
	 *
	 * @return void
	 */
	private static function legacy_datepicker_compatibility_handler( $disabled, $selectable_response ) {
		if ( ! FrmProAppHelper::use_jquery_datepicker() ) {
			return;
		}

		$js_vars = 'var m=(date.getMonth()+1),d=date.getDate(),y=date.getFullYear(),day=date.getDay();';

		if ( ! $disabled ) {
			if ( $selectable_response !== 'true' ) {
				// If the filter has been used, include it
				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
				echo ',beforeShowDay:function(date){' . $js_vars . 'return [' . $selectable_response . '];}';
			}

			return;
		}

		$formatted = array();

		foreach ( $disabled as $dis ) {
			// Format to match javascript dates
			$formatted[] = gmdate( 'Y-n-j', strtotime( $dis ) );
		}

		$disabled = $formatted;
		unset( $formatted );

		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		echo ',beforeShowDay: function(date){' . $js_vars . 'var disabled=' . json_encode( $disabled ) . ';if($.inArray(y+"-"+m+"-"+d,disabled) != -1){return [false];} return [' . $selectable_response . '];}';
	}

	/**
	 * @since 2.0.23
	 *
	 * @param bool  $required
	 * @param array $field
	 *
	 * @return bool
	 */
	public static function maybe_make_field_optional( $required, $field ) {
		if ( ! $required || FrmAppHelper::is_admin_page( 'formidable' ) ) {
			return $required;
		}

		global $frm_vars;
		$is_editing = ! empty( $frm_vars['editing_entry'] ) && is_numeric( $frm_vars['editing_entry'] );

		if ( ! $is_editing ) {
			return $required;
		}

		$optional_on_edit = apply_filters( 'frm_optional_fields_on_edit', array( 'password', 'credit_card' ) );

		if ( in_array( $field['type'], (array) $optional_on_edit ) ) {
			$entry = FrmEntry::getOne( $frm_vars['editing_entry'] );

			if ( $entry && $entry->form_id === $field['form_id'] && ! $entry->is_draft ) {
				$required = false;
			}
		}

		return $required;
	}

	/**
	 * Validate if a dynamic field is accessible to the current user.
	 *
	 * @since 6.33
	 *
	 * @param stdClass $field The field object.
	 *
	 * @return bool True if the field is accessible, false otherwise.
	 */
	private static function validate_dynamic_field( $field ) {
		if ( ! $field || 'data' !== $field->type || empty( $field->field_options['form_select'] ) ) {
			return false;
		}

		$form = FrmForm::getOne( $field->form_id );

		if ( ! $form ) {
			return false;
		}

		if ( $form->parent_form_id ) {
			$form = FrmForm::getOne( $form->parent_form_id );
		}

		if ( $form->logged_in ) {
			if ( ! is_user_logged_in() ) {
				return false;
			}

			if ( ! empty( $form->options['logged_in_role'] ) && ! FrmAppHelper::user_has_permission( $form->options['logged_in_role'] ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * @since 5.2.04
	 */
	public static function ajax_get_data_arr() { // phpcs:ignore SlevomatCodingStandard.Complexity.Cognitive.ComplexityTooHigh
		$post_data = FrmAppHelper::get_param( 'postData' );
		$response  = array();

		foreach ( $post_data as $data ) {
			if ( ! isset( $data['entry_id'] ) || ! isset( $data['current_field'] ) || ! isset( $data['hide_id'] ) ) {
				continue;
			}

			$result_str    = '';
			$entry_id      = is_array( $data['entry_id'] ) ? array_map( 'intval', $data['entry_id'] ) : intval( $data['entry_id'] );
			$current_field = intval( $data['current_field'] );

			if ( ! $current_field ) {
				continue;
			}

			$hidden_field_id = sanitize_text_field( wp_unslash( $data['hide_id'] ) );
			$current         = FrmField::getOne( $current_field );

			if ( ! self::validate_dynamic_field( $current ) ) {
				continue;
			}

			$data_field = FrmField::getOne( $current->field_options['form_select'] );
			$meta_value = self::get_meta_value_for_ajax_handler( $entry_id, $data_field );

			if ( $meta_value === null ) {
				$response[] = $result_str;
				continue;
			}

			$data_display_opts = apply_filters(
				'frm_display_data_opts',
				array(
					'html'    => true,
					'wpautop' => false,
				)
			);

			$value = FrmFieldsHelper::get_display_value( $meta_value, $data_field, $data_display_opts );

			if ( is_array( $value ) ) {
				$value = implode( ', ', $value );
			}

			if ( is_array( $meta_value ) ) {
				$meta_value = implode( ', ', $meta_value );
			}

			$current_field = (array) $current;

			foreach ( $current->field_options as $o => $v ) {
				if ( ! isset( $current_field[ $o ] ) ) {
					$current_field[ $o ] = $v;
				}
				unset( $o, $v );
			}

			// Set up HTML ID and HTML name.
			$html_id    = '';
			$field_name = 'item_meta';
			FrmProFieldsHelper::get_html_id_from_container( $field_name, $html_id, (array) $current, $hidden_field_id );

			$on_current_page = $data['on_current_page'];
			$on_current_page = 'true' === $on_current_page;

			if ( $on_current_page && FrmProFieldsHelper::is_field_visible_to_user( $current ) ) {
				if ( FrmAppHelper::is_not_empty_value( $value ) && $value !== false ) {
					$display_value = FrmProFieldsHelper::maybe_use_option_label( $value, $current_field, $data_field );
					$result_str   .= apply_filters(
						'frm_show_it',
						'<p class="frm_show_it">' . $display_value . "</p>\n",
						$value,
						array(
							'field'    => $data_field,
							'value'    => $meta_value,
							'entry_id' => $entry_id,
						)
					);
				}

				$result_str .= '<input type="hidden" id="' . esc_attr( $html_id ) . '" name="' . esc_attr( $field_name ) . '" value="' . esc_attr( $value ) . '" ';
				$result_str .= FrmAppHelper::clip(
					function () use ( $current_field ) {
						do_action( 'frm_field_input_html', $current_field );
					}
				);
				$result_str .= '/>';
			} else {
				$result_str .= esc_attr( $value );
			}

			$response[] = $result_str;
		}

		wp_send_json( $response );
	}

	/**
	 * Gets meta value for ajax handler.
	 *
	 * @since 5.2.04
	 * @since 5.2.05 The first parameter can be int or array.
	 *
	 * @param array|int $entry_id   Entry ID or array of entry IDs.
	 * @param object    $data_field The data field.
	 *
	 * @return array|string
	 */
	private static function get_meta_value_for_ajax_handler( $entry_id, $data_field ) {
		if ( ! is_array( $entry_id ) ) {
			return FrmProEntryMetaHelper::get_post_or_meta_value( $entry_id, $data_field );
		}

		$meta_value = array();

		foreach ( $entry_id as $eid ) {
			$new_meta = FrmProEntryMetaHelper::get_post_or_meta_value( $eid, $data_field );

			if ( $new_meta ) {
				foreach ( (array) $new_meta as $nm ) {
					array_push( $meta_value, $nm );
					unset( $nm );
				}
			}
			unset( $new_meta, $eid );
		}

		return array_unique( $meta_value );
	}

	/**
	 * @since 2.05.04
	 *
	 * @return string
	 */
	private static function get_posted_entry_ids() {
		$entry_id = FrmAppHelper::get_param( 'entry_id', '', 'get', 'sanitize_text_field' );

		if ( is_array( $entry_id ) ) {
			$entry_id = implode( ',', $entry_id );
		}

		return trim( $entry_id, ',' );
	}

	/**
	 * Get the HTML for a dependent Dynamic field when the parent changes
	 */
	public static function ajax_data_options() {
		// check_ajax_referer( 'frm_ajax', 'nonce' );

		$args = array(
			'trigger_field_id' => FrmAppHelper::get_param( 'trigger_field_id', '', 'post', 'absint' ),
			'entry_id'         => FrmAppHelper::get_param( 'entry_id', '', 'post', 'sanitize_text_field' ),
			'field_id'         => FrmAppHelper::get_param( 'field_id', '', 'post', 'absint' ),
			'container_id'     => FrmAppHelper::get_param( 'container_id', '', 'post', 'sanitize_title' ),
			'default_value'    => FrmAppHelper::get_param( 'default_value', '', 'post', 'sanitize_title' ),
			'prev_val'         => FrmAppHelper::get_param( 'prev_val', '', 'post', 'absint' ),
		);

		if ( $args['entry_id'] == '' ) {
			wp_die();
		}

		if ( ! is_array( $args['entry_id'] ) ) {
			$entry_id = explode( ',', $args['entry_id'] );
		}

		$args['field_data'] = FrmField::getOne( $args['field_id'] );
		$field              = self::initialize_dependent_dynamic_field( $args );

		if ( is_numeric( $args['field_data']->field_options['form_select'] ) ) {
			// If Dynamic field is pulling options from a regular field
			self::get_dependent_dynamic_field_options( $args, $field );
		} elseif ( $args['field_data']->field_options['form_select'] === 'taxonomy' ) {
			// If Dynamic field is pulling options from a taxonomy
			self::get_dependent_category_field_options( $args, $field );
		}

		self::get_dependent_dynamic_field_value( $args['prev_val'], $field );

		// Set up HTML ID and HTML name
		$input_args = array(
			'field_name'    => 'item_meta',
			'field_id'      => $args['field_data']->id,
			'field_plus_id' => '',
			'section_id'    => '',
			'html_id'       => '',
		);

		FrmProFieldsHelper::get_html_id_from_container( $input_args['field_name'], $input_args['html_id'], $field, $args['container_id'] );

		if ( FrmField::is_multiple_select( $args['field_data'] ) ) {
			$input_args['field_name'] .= '[]';
		}

		$field_obj = FrmFieldFactory::get_field_type( 'data', $field );
		echo $field_obj->include_front_field_input( $input_args, array() );

		wp_die();
	}

	/**
	 * Initialize the field array for a dependent dynamic field
	 *
	 * @param array $args
	 *
	 * @return array Field.
	 */
	private static function initialize_dependent_dynamic_field( $args ) {
		return FrmProFieldsHelper::initialize_array_field( $args['field_data'], $args );
	}

	/**
	 * Get the options for a dependent Dynamic field
	 *
	 * @since 2.0.16
	 *
	 * @param array $args
	 * @param array $field
	 */
	private static function get_dependent_dynamic_field_options( $args, &$field ) {
		$linked_field     = FrmField::getOne( $args['field_data']->field_options['form_select'] );
		$field['options'] = array();
		$metas            = array();
		FrmProEntryMetaHelper::meta_through_join( $args['trigger_field_id'], $linked_field, $args['entry_id'], $args['field_data'], $metas );
		$metas = wp_unslash( $metas );

		if ( FrmProDynamicFieldsController::include_blank_option( $metas, $args['field_data'] ) ) {
			$field['options'][''] = '';
		}

		foreach ( $metas as $meta ) {
			$field['options'][ $meta->item_id ] = FrmEntriesHelper::display_value(
				$meta->meta_value,
				$linked_field,
				array(
					'type'          => $linked_field->type,
					'show_icon'     => true,
					'show_filename' => false,
				)
			);
			unset( $meta );
		}

		// Change the form_select value so the filter doesn't override the values
		$args['field_data']->field_options['form_select'] = 'filtered_' . $args['field_data']->field_options['form_select'];

		FrmFieldsHelper::prepare_new_front_field( $field, $args['field_data'] );

		// Sort the options
		$pass_args        = array(
			'metas'         => $metas,
			'field'         => $linked_field,
			'dynamic_field' => $field,
		);
		$field['options'] = apply_filters( 'frm_data_sort', $field['options'], $pass_args );
	}

	/**
	 * Get the options for a dependent Dynamic category field
	 *
	 * @since 2.0.16
	 *
	 * @param array $args
	 * @param array $field
	 */
	private static function get_dependent_category_field_options( $args, &$field ) {
		if ( $args['entry_id'] == 0 ) {
			wp_die();
		}

		if ( is_array( $args['entry_id'] ) ) {
			$zero = array_search( 0, $args['entry_id'] );

			if ( $zero !== false ) {
				unset( $args['entry_id'][ $zero ] );
			}

			if ( empty( $args['entry_id'] ) ) {
				wp_die();
			}
		}

		FrmFieldsHelper::prepare_new_front_field( $field, $args['field_data'] );

		$cat_ids = array_keys( $field['options'] );

		$cat_args = array(
			'include'    => implode( ',', $cat_ids ),
			'hide_empty' => false,
		);

		$post_type            = FrmProFormsHelper::post_type( $args['field_data']->form_id );
		$cat_args['taxonomy'] = FrmProAppHelper::get_custom_taxonomy( $post_type, $args['field_data'] );

		if ( ! $cat_args['taxonomy'] ) {
			wp_die();
		}

		$cats = get_categories( $cat_args );

		foreach ( $cats as $cat ) {
			if ( ! in_array( $cat->parent, (array) $args['entry_id'] ) ) {
				unset( $field['options'][ $cat->term_id ] );
			}
		}

		if ( count( $field['options'] ) === 1 && reset( $field['options'] ) == '' ) {
			wp_die();
		}

		// Sort the options
		$field['options'] = apply_filters( 'frm_data_sort', $field['options'], array( 'dynamic_field' => $field ) );
	}

	/**
	 * Get the field value for a dependent dynamic field
	 *
	 * @since 2.0.16
	 *
	 * @param array $prev_val
	 * @param array $field
	 */
	private static function get_dependent_dynamic_field_value( $prev_val, &$field ) {
		// Set the value to the previous value if it was set. Otherwise, set to default value.
		if ( $prev_val ) {
			$prev_val       = array_unique( $prev_val );
			$field['value'] = $prev_val;
		} else {
			$field['value'] = $field['default_value'];
		}

		// Unset the field value if it isn't an option
		if ( $field['value'] ) {
			$field['value'] = (array) $field['value'];

			foreach ( $field['value'] as $key => $field_val ) {
				if ( ! array_key_exists( $field_val, $field['options'] ) ) {
					unset( $field['value'][ $key ] );
				}
			}
		}

		if ( is_array( $field['value'] ) && empty( $field['value'] ) ) {
			$field['value'] = '';
		}

		// If we have a radio field, set the field value to a string
		if ( $field['data_type'] === 'radio' && is_array( $field['value'] ) ) {
			$field['value'] = reset( $field['value'] );
		}
	}

	/**
	 * Order the values in a Dynamic or Lookup field.
	 *
	 * @since 5.5.2
	 *
	 * @param array $options The field options (choices)
	 * @param array $args
	 *
	 * @return array
	 */
	public static function order_values( $options, $args = array() ) {
		if ( ! $options || empty( $args['dynamic_field'] ) || ! isset( $args['dynamic_field']['option_order'] ) ) {
			return $options;
		}

		$order = $args['dynamic_field']['option_order'];

		if ( $order !== 'ascending' && $order !== 'descending' ) {
			return $options;
		}

		if ( class_exists( 'Collator' ) ) {
			$collator = new Collator( get_locale() );
			$collator->asort( $options );
		} else {
			natcasesort( $options );
		}

		if ( $order === 'descending' ) {
			return array_reverse( $options, true );
		}

		return $options;
	}

	/**
	 * Add an option at the top of the media library page
	 * to show the unattached Formidable files based on user role.
	 *
	 * @since 2.02
	 */
	public static function filter_media_library_link() {
		global $current_screen;

		if ( ! $current_screen || 'upload' !== $current_screen->base || ! current_user_can( 'frm_edit_entries' ) ) {
			return;
		}

		echo '<label for="frm-attachment-filter" class="screen-reader-text">';
		esc_html_e( 'Show form uploads', 'formidable-pro' );
		echo '</label>';

		$filtered = FrmAppHelper::get_param( 'frm-attachment-filter', '', 'get', 'absint' );
		echo '<select name="frm-attachment-filter" id="frm-attachment-filter">';
		echo '<option value="">' . esc_html__( 'Hide form uploads', 'formidable-pro' ) . '</option>';
		echo '<option value="1" ' . selected( $filtered, 1 ) . '>' . esc_html__( 'Show form uploads', 'formidable-pro' ) . '</option>';
		echo '</select>';
	}

	/**
	 * If this file is a Formidable file,
	 * temp redirect to the home page
	 *
	 * @since 2.02
	 */
	public static function redirect_attachment() {
		global $post;

		if ( ! is_object( $post ) || ! is_attachment() || absint( $post->post_parent ) >= 1 || ! FrmProFileField::is_formidable_file( $post->ID ) ) {
			return;
		}

		$user_has_file_access = current_user_can( 'frm_edit_entries' ) && FrmProFileField::user_has_permission( $post->ID );

		if ( ! $user_has_file_access ) {
			wp_redirect( get_bloginfo( 'wpurl' ), 302 );
			die();
		}
	}

	/**
	 * Check for old temp files and delete them
	 *
	 * @since 2.02
	 */
	public static function delete_temp_files() {
		remove_action( 'pre_get_posts', 'FrmProFileField::filter_media_library', 99 );

		$cutoff_period            = apply_filters( 'frm_delete_temp_files_period', '-3 hours' );
		$timestamp_cutoff         = gmdate( 'Y-m-d H:i:s', strtotime( $cutoff_period ) );
		$attachment_ids_to_delete = get_posts(
			array(
				'fields'         => 'ids',
				'post_type'      => 'attachment',
				'posts_per_page' => 50,
				'date_query'     => array(
					'column' => 'post_date_gmt',
					'before' => $timestamp_cutoff,
				),
				'meta_query'     => array(
					array(
						'key'     => '_frm_temporary',
						'compare' => 'EXISTS',
					),
				),
				'post_parent'    => 0,
			)
		);

		foreach ( $attachment_ids_to_delete as $file_id ) {
			// Double check in case other plugins have changed the query
			if ( FrmProFileField::file_is_temporary( $file_id ) ) {
				wp_delete_attachment( $file_id, true );
			}
		}

		add_action( 'pre_get_posts', 'FrmProFileField::filter_media_library', 99 );
	}

	public static function ajax_upload() {
		// Skip nonce for caching.
		$response = FrmProFileField::ajax_upload();

		if ( ! empty( $response['errors'] ) ) {
			status_header( 403 );
			$status = 403;
			echo implode( ' ', $response['errors'] );
		} else {
			$status = 200;
			echo json_encode( $response['media_ids'] );
		}

		wp_die( '', '', array( 'response' => $status ) );
	}

	/**
	 * Allow more field types for switching.
	 *
	 * @since 4.05
	 *
	 * @param array $fields
	 *
	 * @return array
	 */
	public static function single_input_fields( $fields ) {
		$fields[] = 'range';
		$fields[] = 'virtual';
		return $fields;
	}

	public static function _logic_row() {
		check_ajax_referer( 'frm_ajax', 'nonce' );
		FrmAppHelper::permission_check( 'frm_edit_forms', 'show' );

		$meta_name  = FrmAppHelper::get_post_param( 'meta_name', '', 'absint' );
		$field_id   = FrmAppHelper::get_post_param( 'field_id', '', 'absint' );
		$form_id    = FrmAppHelper::get_post_param( 'form_id', '', 'absint' );
		$hide_field = '';

		$field = FrmField::getOne( $field_id );
		$field = FrmFieldsHelper::setup_edit_vars( $field );

		if ( $field['form_id'] != $form_id ) {
			$field['parent_form_id'] = $form_id;
		}

		if ( ! isset( $field['hide_field_cond'][ $meta_name ] ) ) {
			$field['hide_field_cond'][ $meta_name ] = '==';
		}

		$form_fields = self::get_live_fields( $form_id );

		include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/_logic_row.php';
		wp_die();
	}

	/**
	 * Merge fields from DB with live field list.
	 *
	 * @since 4.0
	 *
	 * @param int|string $form_id
	 *
	 * @return array
	 */
	private static function get_live_fields( $form_id ) {
		$form_fields = FrmField::get_all_for_form( $form_id );
		$field_names = FrmAppHelper::get_param( 'fields', '', 'post', 'sanitize_text_field' );

		if ( ! $field_names ) {
			return $form_fields;
		}

		$fields = array();

		foreach ( $field_names as $field ) {
			if ( ! empty( $field->type ) ) {
				$fields[ $field['fieldId'] ] = $field;
			}
		}

		foreach ( $form_fields as $k => $form_field ) {
			if ( ! isset( $fields[ $form_field->id ] ) ) {
				continue;
			}
			$form_fields[ $k ]->type = $fields[ $form_field->id ]['fieldType'];
			$form_fields[ $k ]->name = $fields[ $form_field->id ]['fieldName'];
		}

		return $form_fields;
	}

	/**
	 * @param array      $new_field
	 * @param int|string $form_id
	 *
	 * @return void
	 */
	public static function create_multiple_fields( $new_field, $form_id ) {
		// $args = compact('field_data', 'form_id', 'field');
		if ( ! $new_field || $new_field['type'] !== 'divider' ) {
			return;
		}

		// Add an "End section" when a section field is created
		FrmFieldsController::include_new_field( 'end_divider', $form_id );
	}

	/**
	 * Update a field after dragging and dropping it on the form builder page
	 *
	 * @since 2.0.24
	 */
	public static function update_field_after_move() {
		FrmAppHelper::permission_check( 'frm_edit_forms' );
		check_ajax_referer( 'frm_ajax', 'nonce' );

		$field_id         = FrmAppHelper::get_post_param( 'field', 0, 'absint' );
		$form_id          = FrmAppHelper::get_post_param( 'form_id', 0, 'absint' );
		$section_id       = FrmAppHelper::get_post_param( 'section_id', 0, 'absint' );
		$previous_form_id = FrmAppHelper::get_post_param( 'previous_form_id', 0, 'absint' );

		if ( ! $field_id ) {
			wp_die();
		}

		$update_values = array();
		$field_options = FrmDb::get_var( 'frm_fields', array( 'id' => $field_id ), 'field_options' );
		FrmAppHelper::unserialize_or_decode( $field_options );

		// Update the in_section value
		if ( ! isset( $field_options['in_section'] ) || $field_options['in_section'] != $section_id ) {
			$field_options['in_section']    = $section_id;
			$update_values['field_options'] = $field_options;
			self::maybe_place_field_before_end_divider( $field_id );
		}

		// Update the form_id value
		if ( $form_id ) {
			$update_values['form_id'] = $form_id;
		}

		FrmField::update( $field_id, $update_values );

		if ( $previous_form_id ) {
			// Prevent field cache from showing fields inside of a repeater that were moved outside of the section.
			FrmField::delete_form_transient( $previous_form_id );
		}

		wp_die();
	}

	/**
	 * Make sure that when a new field is dragged into a repeater that it is at least before the end_divider so that it doesn't end up appearing outside of the repeater.
	 *
	 * @param int $field_id
	 */
	private static function maybe_place_field_before_end_divider( $field_id ) {
		$row = FrmDb::get_row( 'frm_fields', array( 'id' => $field_id ), 'field_order, form_id' );

		if ( ! $row ) {
			return;
		}

		$field_order          = $row->field_order;
		$form_id              = $row->form_id;
		$field_order_before   = $field_order - 1;
		$where_end_divider    = array(
			'field_order' => $field_order_before,
			'type'        => 'end_divider',
			'form_id'     => $form_id,
		);
		$end_divider_field_id = FrmDb::get_var( 'frm_fields', $where_end_divider );

		if ( ! $end_divider_field_id ) {
			return;
		}

		FrmField::update( $field_id, array( 'field_order' => $field_order_before ) );
		FrmField::update( $end_divider_field_id, array( 'field_order' => $field_order ) );
	}

	public static function duplicate_section( $section_field, $form_id ) {
		FrmAppHelper::permission_check( 'frm_edit_forms' );
		check_ajax_referer( 'frm_ajax', 'nonce' );

		global $wpdb, $frm_duplicate_ids;

		$post_children = (array) FrmAppHelper::get_post_param( 'children', array() );

		if ( $post_children ) {
			$children = array_filter( $post_children, 'is_numeric' );
			$fields   = FrmField::getAll( array( 'fi.id' => $children ), 'field_order' );
		} else {
			$fields = array();
		}

		array_unshift( $fields, $section_field );

		$order_query       = array(
			'field_order >' => $section_field->field_order,
			'form_id'       => $form_id,
			'type'          => 'end_divider',
		);
		$end_section_order = FrmDb::get_var( 'frm_fields', $order_query, 'field_order', array( 'order_by' => 'field_order ASC' ) );
		$field_order       = max( $section_field->field_order, $end_section_order );
		$ended             = false;

		if ( ! empty( $section_field->field_options['repeat'] ) ) {
			// Create the repeatable form
			$new_form_id = FrmProField::create_repeat_form(
				0,
				array(
					'parent_form_id' => $form_id,
					'field_name'     => $section_field->name,
				)
			);
		} else {
			$new_form_id = $form_id;
		}

		$grid_helper = class_exists( 'FrmFieldGridHelper' ) ? new FrmFieldGridHelper() : false;

		foreach ( $fields as $field ) {
			// Keep the current form id or give it the id of the newly created form
			$this_form_id = $field->form_id == $form_id ? $form_id : $new_form_id;

			$values = array();
			FrmFieldsHelper::fill_field( $values, $field, $this_form_id );

			if ( FrmField::is_repeating_field( $field ) ) {
				$values['field_options']['form_select'] = $new_form_id;
			}

			$values['field_order'] = $field_order;
			++$field_order;

			$values     = apply_filters( 'frm_duplicated_field', $values );
			$field_id   = FrmField::create( $values );
			$copy_field = $field;
			do_action( 'frm_after_duplicate_field', compact( 'field_id', 'values', 'copy_field', 'form_id' ) );

			if ( ! $field_id ) {
				continue;
			}

			$frm_duplicate_ids[ $field->id ]        = $field_id;
			$frm_duplicate_ids[ $field->field_key ] = $field_id;

			if ( 'end_divider' === $field->type ) {
				$ended = true;
			}

			$duplicated_field_object = FrmField::getOne( $field_id );

			if ( $grid_helper instanceof FrmFieldGridHelper ) {
				$grid_helper->set_field( $duplicated_field_object );
				$grid_helper->maybe_begin_field_wrapper();
			}

			$values['id'] = $this_form_id;
			FrmFieldsController::load_single_field( $duplicated_field_object, $values );

			if ( $grid_helper instanceof FrmFieldGridHelper ) {
				$grid_helper->sync_list_size();
			}

			unset( $field, $duplicated_field_object );
		}

		if ( ! $ended ) {
			// Make sure the section is ended
			self::create_multiple_fields( (array) $section_field, $form_id );
		}

		// Prevent the function in the free version from completing
		wp_die();
	}

	/**
	 * Update the repeating form name when a repeating section name is updated
	 *
	 * @since 3.0.03
	 *
	 * @param array $values
	 *
	 * @return array Values.
	 */
	public static function update_repeater_form_name( $values ) {
		if ( ! empty( $values['field_options']['repeat'] ) ) {
			FrmForm::update( $values['field_options']['form_select'], array( 'name' => $values['name'] ) );
		}

		return $values;
	}

	/**
	 * Setup each field's array when an entry is being edited
	 * Similar to FrmAppHelper::fill_field_defaults
	 *
	 * @since 2.01.0
	 *
	 * @param object $entry
	 * @param array $fields
	 * @param array $args (always contains 'parent_form_id')
	 * If field is repeating, $args includes 'repeating', 'parent_field_id' and 'key_pointer'
	 * If field is embedded, $args includes 'in_embed_form'
	 *
	 * @return array
	 */
	public static function setup_field_data_for_editing_entry( $entry, $fields, $args ) {
		$new_fields = array();

		foreach ( $fields as $field ) {
			$default_value = apply_filters( 'frm_get_default_value', $field->default_value, $field, true );
			$field_value   = self::get_posted_or_saved_value( $entry, $field, $args );

			$field_array = array(
				'id'             => $field->id,
				'value'          => $field_value,
				'default_value'  => $default_value,
				'name'           => $field->name,
				'description'    => $field->description,
				'type'           => apply_filters( 'frm_field_type', $field->type, $field, $field_value ),
				'options'        => $field->options,
				'required'       => $field->required,
				'field_key'      => $field->field_key,
				'field_order'    => $field->field_order,
				'form_id'        => $field->form_id,
				'parent_form_id' => $args['parent_form_id'],
				'in_embed_form'  => $args['in_embed_form'] ?? '0',
			);

			FrmFieldsHelper::prepare_edit_front_field( $field_array, $field, $entry->id, $args );

			if ( empty( $field_array['unique'] ) ) {
				$field_array['unique_msg'] = '';
			}

			$field_array = array_merge( $field->field_options, $field_array );

			$values['fields'][ $field->id ] = $field_array;

			$new_fields[ $field->id ] = $field_array;
		}

		return $new_fields;
	}

	/**
	 * If the field has a posted value, get it. Otherwise, get the saved field value
	 *
	 * @since 2.01.0
	 *
	 * @param object $entry
	 * @param object $field
	 * @param array $args (if repeating, this includes 'repeating', 'parent_field_id', and 'key_pointer')
	 *
	 * @return array|string Field value.
	 */
	private static function get_posted_or_saved_value( $entry, $field, $args ) {
		if ( ! empty( $args['save_draft_click'] ) && FrmField::is_repeating_field( $field ) ) {
			// If save draft was just clicked, and this is a repeating section, get the saved value
			$field_value = self::get_saved_value( $entry, $field );
		} elseif ( FrmEntriesHelper::value_is_posted( $field, $args ) ) {
			$field_value = '';
			FrmEntriesHelper::get_posted_value( $field, $field_value, $args );
		} else {
			$field_value = self::get_saved_value( $entry, $field );
		}

		return $field_value;
	}

	/**
	 * Get the saved value for a field
	 *
	 * @since 2.02.05
	 *
	 * @param object $entry
	 * @param object $field
	 *
	 * @return array|bool|mixed|string
	 */
	private static function get_saved_value( $entry, $field ) {
		$pass_args = array(
			'links'    => false,
			'truncate' => false,
		);
		return FrmProEntryMetaHelper::get_post_or_meta_value( $entry, $field, $pass_args );
	}

	/**
	 * Product Bulk Edit
	 *
	 * @since 4.04
	 */
	public static function bulk_edit_products() {
		FrmAppHelper::permission_check( 'frm_edit_forms' );
		check_ajax_referer( 'frm_ajax', 'nonce' );

		$field_id = FrmAppHelper::get_param( 'field_id', '', 'post', 'absint' );
		$field    = FrmField::getOne( $field_id );

		if ( ! $field || 'product' !== $field->type ) {
			wp_die();
		}

		$field = FrmFieldsHelper::setup_edit_vars( $field );

		$separate                = FrmAppHelper::get_param( 'separate', '', 'post', 'sanitize_text_field' );
		$field['separate_value'] = $separate === 'true';

		$field['options'] = self::product_strings_to_array();

		FrmProFieldProduct::single_option( $field );

		wp_die();
	}

	/**
	 * When bulk editing, convert | to array.
	 *
	 * @since 4.04
	 */
	private static function product_strings_to_array() {
		$opts = FrmAppHelper::get_param( 'opts', '', 'post', 'wp_kses_post' );
		$opts = explode( "\n", rtrim( $opts, "\n" ) );
		$opts = array_map( 'trim', $opts );

		foreach ( $opts as $opt_key => $opt ) {
			if ( ! $opt ) {
				unset( $opts[ $opt_key ] );
				continue;
			}

			if ( ! str_contains( $opt, '|' ) ) {
				continue;
			}

			$vals  = explode( '|', $opt );
			$count = count( $vals );
			$label = isset( $vals[0] ) ? trim( $vals[0] ) : '';

			// Only product name is available
			$opts[ $opt_key ] = array(
				'label' => $label,
				'value' => $label,
				'price' => '',
			);

			if ( 2 === $count ) {
				// Product name & price
				$opts[ $opt_key ]['price'] = trim( $vals[1] );
			} elseif ( 3 === $count ) {
				// Product name, separate value & price
				$opts[ $opt_key ]['value'] = trim( $vals[1] );
				$opts[ $opt_key ]['price'] = trim( $vals[2] );
			}
			unset( $vals, $opt_key, $opt );
		}

		// Just to renumber indices from 0.
		return array_values( $opts );
	}

	/*
	 * Autocomplete users admin ajax endpoint
	 *
	 * @since 4.03.06
	 */
	public static function user_search() {
		FrmAppHelper::permission_check( 'frm_edit_entries' );
		check_ajax_referer( 'frm_ajax', 'nonce' );

		global $wpdb;

		$term = FrmAppHelper::get_param( 'term', '', 'get', 'sanitize_text_field' );

		if ( $term ) {
			$items = $wpdb->get_results(
				$wpdb->prepare(
					"SELECT ID, display_name FROM {$wpdb->users}
					WHERE ID = %d OR user_email LIKE %s OR user_login LIKE %s OR display_name LIKE %s LIMIT 25",
					absint( $term ),
					'%' . $wpdb->esc_like( $term ) . '%',
					'%' . $wpdb->esc_like( $term ) . '%',
					'%' . $wpdb->esc_like( $term ) . '%'
				)
			);
		} else {
			$args  = array(
				'limit'    => 20,
				'order_by' => 'display_name',
			);
			$items = FrmDb::get_results( $wpdb->users, array(), 'ID, display_name', $args );
		}

		$results = array();

		foreach ( $items as $item ) {
			$results[] = array(
				'value' => $item->ID,
				'label' => $item->display_name,
			);
		}

		wp_send_json( $results );
	}

	/**
	 * Changes options of Display format setting of Radio field.
	 *
	 * @since 6.3.3
	 *
	 * @param array $options The options.
	 *
	 * @return array
	 */
	public static function change_field_display_format_options( $options ) {
		if ( isset( $options['1']['addon'] ) ) {
			unset( $options['1']['addon'] );
		}

		return $options;
	}

	/**
	 * Changes arguments of Display format setting of Radio field.
	 *
	 * @since 5.0
	 *
	 * @param array $args        The arguments.
	 * @param array $method_args The arguments from the method. Contains `field`, `options`.
	 *
	 * @return array
	 */
	public static function change_radio_display_format_args( $args, array $method_args ) {
		return self::change_field_display_format_args( $args, $method_args );
	}

	/**
	 * Changes arguments of Display format setting of Checkbox field.
	 *
	 * @since 6.4.1
	 *
	 * @param array $args        The arguments.
	 * @param array $method_args The arguments from the method. Contains `field`, `options`.
	 *
	 * @return array
	 */
	public static function change_checkbox_display_format_args( $args, $method_args ) {
		return self::change_field_display_format_args( $args, $method_args );
	}

	/**
	 * Changes arguments of Display format setting of a field.
	 *
	 * @since 6.4.1
	 *
	 * @param array $args        The arguments.
	 * @param array $method_args The arguments from the method. Contains `field`, `options`.
	 *
	 * @return array
	 */
	private static function change_field_display_format_args( $args, $method_args ) {
		$field            = $method_args['field'];
		$args['selected'] = ! empty( $field['image_options'] ) ? '1' : '0';

		return $args;
	}

	/**
	 * Prevent field creation when license is expired.
	 *
	 * @since 5.4.2
	 *
	 * @param string $field_type
	 *
	 * @return void
	 */
	public static function before_create_field( $field_type ) {
		if ( ! FrmProAddonsController::is_expired_outside_grace_period() ) {
			return;
		}

		$pro_fields            = FrmField::pro_field_selection();
		$field_type_is_allowed = ! array_key_exists( $field_type, $pro_fields );

		if ( ! $field_type_is_allowed ) {
			// Die early instead of adding a pro field for an expired license.
			wp_die( -1 );
		}
	}

	/**
	 * Turn off sanitizing in lite for specific pro field option strings.
	 * This way the sanitizing can be handled in Pro for a specific option key in the FrmProField::update function.
	 *
	 * @since 5.5.6
	 *
	 * @param bool   $should_sanitize
	 * @param string $opt
	 *
	 * @return bool
	 */
	public static function should_sanitize_field_opt_string( $should_sanitize, $opt ) {
		if ( 'custom_thousand_separator' === $opt ) {
			// Override sanitizing for the custom thousand separator so it doesn't get trimmed in FrmForm::sanitize_field_opt.
			// This way we can support a ' ' space thousand separator.
			return false;
		}
		return $should_sanitize;
	}

	/**
	 * Adds show password HTML to the backend conf input.
	 *
	 * @since 6.3.1
	 *
	 * @param string $input_html Input HTML.
	 * @param array  $args       Contains `field` array.
	 *
	 * @return string
	 */
	public static function add_show_password_html_to_backend_conf_input( $input_html, $args ) {
		if ( 'password' === FrmField::get_field_type( $args['field'] ) ) {
			return FrmProFieldsHelper::add_show_password_html( $input_html );
		}
		return $input_html;
	}

	/**
	 * Renders the confirmation field preview in the form builder.
	 *
	 * @since 6.32
	 *
	 * @param array $args Contains `field` and `display` arrays.
	 *
	 * @return void
	 */
	public static function add_confirmation_field_preview( $args ) {
		if ( empty( $args['display']['conf_field'] ) ) {
			return;
		}

		$field   = $args['field'];
		$display = $args['display'];

		include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/confirmation-preview.php';
	}

	/**
	 * Make sure that FrmField::is_field_type calls work when we're posting 'dropdown' as our data type.
	 * We need checks for 'select' to return true despite the mismatch.
	 * 'dropdown' is sent instead of 'select' because 'select' gets interpreted as SQL injection by some security tools.
	 *
	 * @since 6.7.1
	 *
	 * @param bool  $is_field_type
	 * @param array $args
	 *
	 * @return bool
	 */
	public static function is_field_type( $is_field_type, $args ) {
		if ( $is_field_type ) {
			return $is_field_type;
		}

		$field      = $args['field'];
		$field_type = FrmField::get_original_field_type( $field );

		if ( ! in_array( $field_type, array( 'data', 'lookup', 'product' ), true ) ) {
			return $is_field_type;
		}

		$data_type = FrmField::get_option( $field, 'data_type' );

		if ( 'dropdown' === $data_type && 'select' === $args['is_type'] ) {
			return true;
		}

		return $is_field_type;
	}

	/**
	 * If no placeholder option is added to a dropdown field and conditional logic is enabled, add a hidden empty option.
	 * This fixes an issue with conditional logic, where conditionally hidden dropdown values cannot be cleared.
	 * Triggered after dropdown field placeholder option is not added.
	 *
	 * @since 6.16.2
	 *
	 * @param array $field
	 *
	 * @return void
	 */
	public static function dropdown_field_after_no_placeholder_option( $field ) {
		if ( empty( $field['hide_field'] ) ) {
			return;
		}

		// Do an additional check if the first option has an empty value before adding a hidden empty option.
		if ( ! empty( $field['options'] ) && is_array( $field['options'] ) ) {
			$first_option = reset( $field['options'] );

			if ( is_array( $first_option ) && isset( $first_option['value'] ) && '' === $first_option['value'] ) {
				return;
			}

			if ( is_string( $first_option ) && '' === $first_option ) {
				return;
			}
		}

		if ( ! isset( $_POST['item_meta'] ) || ! isset( $_POST['item_meta'][ $field['id'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
			// This is only required when the field data is being posted.
			return;
		}

		FrmProHtmlHelper::echo_dropdown_option(
			'',
			! isset( $field['value'] ) || '' === $field['value'],
			array(
				'value' => '',
				'class' => 'frm_hidden frm_hidden_placeholder',
			)
		);
	}

	/**
	 * Includes the confirmation placeholder template.
	 *
	 * @since 6.19
	 *
	 * @param array $args The arguments. Includes 'field', 'display', and 'values'.
	 *
	 * @return void
	 */
	public static function add_confirmation_placeholder( $args ) {
		if ( empty( $args['display']['conf_field'] ) ) {
			return;
		}

		$field = $args['field'];
		include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/confirmation-placeholder.php';
	}

	/**
	 * Adds additional alignment options for fields when Pro is active.
	 *
	 * @since 6.32
	 *
	 * @param array $align_options The alignment options.
	 *
	 * @return array
	 */
	public static function add_additional_pro_align_options( $align_options ) {
		return array_merge( $align_options, self::get_additional_pro_align_options() );
	}

	/**
	 * Returns additional alignment options for fields when Pro is active.
	 *
	 * @since 6.32
	 *
	 * @return array
	 */
	private static function get_additional_pro_align_options() {
		$align_options = self::get_align_setting_options();
		unset( $align_options['inline'], $align_options['block'] );
		return $align_options;
	}

	/**
	 * @deprecated 3.0
	 *
	 * @codeCoverageIgnore
	 *
	 * @param mixed  $field
	 * @param string $name
	 */
	public static function show( $field, $name = '' ) {
		_deprecated_function( __FUNCTION__, '3.0', 'FrmFieldType::show_on_form_builder' );
	}

	/**
	 * @deprecated 3.0
	 *
	 * @codeCoverageIgnore
	 *
	 * @param mixed  $field
	 * @param string $field_name
	 * @param array  $atts
	 *
	 * @return void
	 */
	public static function form_fields( $field, $field_name, $atts ) {
		_deprecated_function( __FUNCTION__, '3.0', 'FrmFieldType Modals' );
	}

	/**
	 * Changes options of Display format setting of Radio field.
	 *
	 * @since 5.0
	 * @deprecated 6.3.3
	 *
	 * @param array $options The options.
	 *
	 * @return array
	 */
	public static function change_radio_display_format_options( $options ) {
		_deprecated_function( __METHOD__, '6.3.3', self::class . '::change_field_display_format_options' );
		return self::change_field_display_format_options( $options );
	}
}
