HEX
Server: Apache
System: Linux od-b43f49 4.9.0-0.bpo.12-amd64 #1 SMP Debian 4.9.210-1+deb9u1~deb8u1 (2020-06-09) x86_64
User: uid181852 (181852)
PHP: 8.2.30
Disabled: passthru,exec,system,popen,shell_exec,proc_open,pcntl_exec
Upload Files
File: /home/clients/94735d3feef25fe7d1511e6bdd8b0ef6/web/wp-content/plugins/pods/classes/fields/pick.php
<?php

use Pods\Static_Cache;
use Pods\Whatsit\Pod;
use Pods\Whatsit\Field;
use Pods\Whatsit\Object_Field;
use Pods\API\Whatsit\Value_Field;
use Pods\Whatsit\Store;

/**
 * @package Pods\Fields
 */
class PodsField_Pick extends PodsField {

	/**
	 * {@inheritdoc}
	 */
	public static $group = 'Relationships / Media';

	/**
	 * {@inheritdoc}
	 */
	public static $type = 'pick';

	/**
	 * {@inheritdoc}
	 */
	public static $label = 'Relationship';

	/**
	 * {@inheritdoc}
	 */
	protected static $api = false;

	/**
	 * Available Related Objects.
	 *
	 * @var array
	 * @since 2.3.0
	 */
	public static $related_objects = array();

	/**
	 * Custom Related Objects
	 *
	 * @var array
	 * @since 2.3.0
	 */
	public static $custom_related_objects = array();

	/**
	 * Data used during validate / save to avoid extra queries.
	 *
	 * @var array
	 * @since 2.3.0
	 */
	public static $related_data = array();

	/**
	 * Data used during input method (mainly for autocomplete).
	 *
	 * @var array
	 * @since 2.3.0
	 */
	public static $field_data = array();

	/**
	 * Saved array of simple relationship names.
	 *
	 * @var array
	 * @since 2.5.0
	 */
	private static $names_simple = null;

	/**
	 * Saved array of relationship names
	 *
	 * @var array
	 * @since 2.5.0
	 */
	private static $names_related = null;

	/**
	 * Saved array of bidirectional relationship names
	 *
	 * @var array
	 * @since 2.5.0
	 */
	private static $names_bidirectional = null;

	/**
	 * {@inheritdoc}
	 */
	public function setup() {

		static::$group = __( 'Relationships / Media', 'pods' );
		static::$label = __( 'Relationship', 'pods' );
	}

	/**
	 * {@inheritdoc}
	 */
	public function admin_init() {

		// AJAX for Relationship lookups.
		add_action( 'wp_ajax_pods_relationship', array( $this, 'admin_ajax_relationship' ) );
		add_action( 'wp_ajax_nopriv_pods_relationship', array( $this, 'admin_ajax_relationship' ) );

		// Handle modal input.
		add_action( 'pods_meta_box_pre', array( $this, 'admin_modal_input' ) );
		add_action( 'edit_form_top', array( $this, 'admin_modal_input' ) );
		add_action( 'show_user_profile', array( $this, 'admin_modal_input' ) );
		add_action( 'edit_user_profile', array( $this, 'admin_modal_input' ) );

		// Hook into every taxonomy form.
		$taxonomies = get_taxonomies();

		foreach ( $taxonomies as $taxonomy ) {
			if ( $taxonomy instanceof WP_Term ) {
				$taxonomy = $taxonomy->name;
			}

			add_action( $taxonomy . '_add_form', array( $this, 'admin_modal_input' ) );
			add_action( $taxonomy . '_edit_form', array( $this, 'admin_modal_input' ) );
		}

		// Handle modal saving.
		add_filter( 'redirect_post_location', array( $this, 'admin_modal_bail_post_redirect' ), 10, 2 );
		add_action( 'load-edit-tags.php', array( $this, 'admin_modal_bail_term_action' ) );
		add_action( 'load-categories.php', array( $this, 'admin_modal_bail_term_action' ) );
		add_action( 'load-edit-link-categories.php', array( $this, 'admin_modal_bail_term_action' ) );
		add_action( 'personal_options_update', array( $this, 'admin_modal_bail_user_action' ) );
		add_action( 'user_register', array( $this, 'admin_modal_bail_user_action' ) );
		add_action( 'pods_api_processed_form', array( $this, 'admin_modal_bail_pod' ), 10, 3 );

	}

	/**
	 * {@inheritdoc}
	 */
	public function options() {
		// translators: %s: is the Documentation linked text.
		$fallback_help = __( 'More details on our %s.', 'pods' );

		$fallback_help_link = sprintf(
			'<a href="%1$s" target="_blank" rel="noopener noreferrer">%2$s</a>',
			esc_url( 'https://docs.pods.io/fields/relationship/' ),
			__( 'Field Type Documentation', 'pods' )
		);

		$fallback_help = sprintf( $fallback_help, $fallback_help_link );

		$simple_objects = $this->simple_objects();

		$options = [
			static::$type . '_format_type'              => [
				'label'                 => __( 'Selection Type', 'pods' ),
				'help'                  => $fallback_help,
				'default'               => 'single',
				'required'              => true,
				'type'                  => 'pick',
				'data'                  => [
					'single' => __( 'Single Select', 'pods' ),
					'multi'  => __( 'Multiple Select', 'pods' ),
				],
				'pick_show_select_text' => 0,
				'dependency'            => true,
			],
			static::$type . '_format_single'            => [
				'label'                 => __( 'Input Type', 'pods' ),
				'help'                  => $fallback_help,
				'depends-on'            => [
					static::$type . '_format_type' => 'single',
				],
				'default'               => 'dropdown',
				'required'              => true,
				'type'                  => 'pick',
				'data'                  => apply_filters( 'pods_form_ui_field_pick_format_single_options', [
					'dropdown'     => __( 'Drop Down', 'pods' ),
					'radio'        => __( 'Radio Buttons', 'pods' ),
					'autocomplete' => __( 'Autocomplete', 'pods' ),
					'list'         => __( 'List View (single value)', 'pods' ),
				] ),
				'pick_show_select_text' => 0,
				'dependency'            => true,
			],
			static::$type . '_format_multi'             => [
				'label'                 => __( 'Input Type', 'pods' ),
				'help'                  => $fallback_help,
				'depends-on'            => [
					static::$type . '_format_type' => 'multi',
				],
				'default'               => 'list',
				'required'              => true,
				'type'                  => 'pick',
				'data'                  => apply_filters( 'pods_form_ui_field_pick_format_multi_options', [
					'checkbox'     => __( 'Checkboxes', 'pods' ),
					'multiselect'  => __( 'Multi Select (basic selection)', 'pods' ),
					'autocomplete' => __( 'Autocomplete', 'pods' ),
					'list'         => __( 'List View (with reordering)', 'pods' ),
				] ),
				'pick_show_select_text' => 0,
				'dependency'            => true,
			],
			static::$type . '_display_format_multi'     => [
				'label'                 => __( 'Display Format', 'pods' ),
				'help'                  => __( 'Used as format for front-end display', 'pods' ) . ' ' . $fallback_help,
				'depends-on'            => [
					static::$type . '_format_type' => 'multi',
				],
				'default'               => 'default',
				'required'              => true,
				'type'                  => 'pick',
				'data'                  => [
					'default'    => __( 'Item 1, Item 2, and Item 3', 'pods' ),
					'non_serial' => __( 'Item 1, Item 2 and Item 3', 'pods' ),
					'custom'     => __( 'Custom separator (without "and")', 'pods' ),
				],
				'pick_show_select_text' => 0,
				'dependency'            => true,
			],
			static::$type . '_display_format_separator' => [
				'label'      => __( 'Display Format Separator', 'pods' ),
				'help'       => __( 'Used as separator for front-end display. This also turns off the "and" portion of the formatting.', 'pods' ) . ' ' . $fallback_help,
				'depends-on' => [
					static::$type . '_display_format_multi' => 'custom',
					static::$type . '_format_type'          => 'multi',
				],
				'default'    => ', ',
				'type'       => 'text',
			],
			static::$type . '_allow_add_new'            => [
				'label'       => __( 'Allow Add New', 'pods' ),
				'help'        => __( 'Allow new related records to be created in a modal window', 'pods' ) . ' ' . $fallback_help,
				'wildcard-on' => [
					static::$type . '_object' => [
						'^post_type-(?!(custom_css|customize_changeset)).*$',
						//'^taxonomy-.*$', @todo We need to finish adding support for add new on term form.
						'^user$',
						'^pod-.*$',
					],
				],
				'type'        => 'boolean',
				'default'     => 1,
			],
			static::$type . '_add_new_label'            => array(
					'label'       => __( 'Add New Label', 'pods' ),
					'placeholder' => __( 'Add New', 'pods' ),
					'default'     => '',
					'type'        => 'text',
					'depends-on'  => [ static::$type . '_allow_add_new' => true ]
			),
			static::$type . '_taggable'                 => [
				'label'          => __( 'Taggable', 'pods' ),
				'help'           => __( 'Allow new values to be inserted when using an Autocomplete field', 'pods' ) . ' ' . $fallback_help,
				'depends-on-any' => [
					static::$type . '_format_single' => 'autocomplete',
					static::$type . '_format_multi'  => 'autocomplete',
				],
				'excludes-on'    => [
					static::$type . '_object'        => array_merge( [
						'site',
						'network',
					], $simple_objects ),
					static::$type . '_allow_add_new' => false,
				],
				'type'           => 'boolean',
				'default'        => 0,
			],
			static::$type . '_show_icon'                => [
				'label'          => __( 'Show Icons', 'pods' ),
				'help'           => $fallback_help,
				'depends-on-any' => [
					static::$type . '_format_single' => 'list',
					static::$type . '_format_multi'  => 'list',
				],
				'excludes-on'    => [
					static::$type . '_object' => array_merge( [ 'site', 'network' ], $simple_objects ),
				],
				'type'           => 'boolean',
				'default'        => 1,
			],
			static::$type . '_show_edit_link'           => [
				'label'          => __( 'Show Edit Links', 'pods' ),
				'help'           => $fallback_help,
				'depends-on-any' => [
					static::$type . '_format_single' => 'list',
					static::$type . '_format_multi'  => 'list',
				],
				'excludes-on'    => [
					static::$type . '_object' => array_merge( [ 'site', 'network' ], $simple_objects ),
				],
				'type'           => 'boolean',
				'default'        => 1,
			],
			static::$type . '_show_view_link'           => [
				'label'          => __( 'Show View Links', 'pods' ),
				'help'           => $fallback_help,
				'depends-on-any' => [
					static::$type . '_format_single' => 'list',
					static::$type . '_format_multi'  => 'list',
				],
				'excludes-on'    => [
					static::$type . '_object' => array_merge( [ 'site', 'network' ], $simple_objects ),
				],
				'type'           => 'boolean',
				'default'        => 1,
			],
			static::$type . '_select_text'              => [
				'label'            => __( 'Default Select Text', 'pods' ),
				'help'             => __( 'This is the text used for the default "no selection" dropdown item. If left empty, it will default to "-- Select One --"', 'pods' ) . ' ' . $fallback_help,
				'depends-on'       => [
					static::$type . '_format_type'   => 'single',
					static::$type . '_format_single' => 'dropdown',
				],
				'default'          => '',
				'text_placeholder' => __( '-- Select One --', 'pods' ),
				'type'             => 'text',
			],
			static::$type . '_limit'                    => [
				'label'      => __( 'Selection Limit', 'pods' ),
				'help'       => __( 'Default is "0" for no limit, but you can enter 1 or more to limit the number of items that can be selected.', 'pods' ) . ' ' . $fallback_help,
				'depends-on' => [
					static::$type . '_format_type' => 'multi',
				],
				'default'    => 0,
				'type'       => 'number',
			],
			static::$type . '_table_id'                 => [
				'label'      => __( 'Table ID Column', 'pods' ),
				'help'       => __( 'You must provide the ID column name for the table, this will be used to keep track of the relationship', 'pods' ) . ' ' . $fallback_help,
				'depends-on' => [
					static::$type . '_object' => 'table',
				],
				'required'   => 1,
				'default'    => '',
				'type'       => 'text',
			],
			static::$type . '_table_index'              => [
				'label'      => __( 'Table Index Column', 'pods' ),
				'help'       => __( 'You must provide the index column name for the table, this may optionally also be the ID column name', 'pods' ) . ' ' . $fallback_help,
				'depends-on' => [
					static::$type . '_object' => 'table',
				],
				'required'   => 1,
				'default'    => '',
				'type'       => 'text',
			],
			static::$type . '_display'                  => [
				'label'       => __( 'Display Field in Selection List', 'pods' ),
				'help'        => __( 'Provide the name of a field on the related object to reference, example: {@post_title}', 'pods' ) . ' ' . $fallback_help,
				'excludes-on' => [
					static::$type . '_object' => array_merge( [ 'site', 'network' ], $simple_objects ),
				],
				'default'     => '',
				'type'        => 'text',
			],
			static::$type . '_user_role'                => [
				'label'            => __( 'Limit list by Role(s)', 'pods' ),
				'help'             => __( 'You can choose to limit Users available for selection by specific role(s).', 'pods' ) . ' ' . $fallback_help,
				'default'          => '',
				'type'             => 'pick',
				'pick_object'      => 'role',
				'pick_format_type' => 'multi',
				'depends-on'       => [
					static::$type . '_object' => 'user',
				],
			],
			static::$type . '_where'                    => [
				'label'       => __( 'Customized <em>WHERE</em>', 'pods' ),
				'help'        => $fallback_help,
				'excludes-on' => [
					static::$type . '_object' => array_merge( [ 'site', 'network' ], $simple_objects ),
				],
				'default'     => '',
				'type'        => 'text',
			],
			static::$type . '_orderby'                  => [
				'label'       => __( 'Customized <em>ORDER BY</em>', 'pods' ),
				'help'        => $fallback_help,
				'excludes-on' => [
					static::$type . '_object' => array_merge( [ 'site', 'network' ], $simple_objects ),
				],
				'default'     => '',
				'type'        => 'text',
			],
			static::$type . '_groupby'                  => [
				'label'       => __( 'Customized <em>GROUP BY</em>', 'pods' ),
				'help'        => $fallback_help,
				'excludes-on' => [
					static::$type . '_object' => array_merge( [ 'site', 'network' ], $simple_objects ),
				],
				'default'     => '',
				'type'        => 'text',
			],
		];

		$post_type_pick_objects = array();

		foreach ( get_post_types( '', 'names' ) as $post_type ) {
			$post_type_pick_objects[] = 'post_type-' . $post_type;
		}

		$options[ static::$type . '_post_status' ] = [
			'label'            => __( 'Limit list by Post Status', 'pods' ),
			'help'             => __( 'You can choose to limit Posts available for selection by one or more specific post status.', 'pods' ),
			'type'             => 'pick',
			'pick_object'      => 'post-status',
			'pick_format_type' => 'multi',
			'default'          => 'publish',
			'depends-on'       => [
				static::$type . '_object' => $post_type_pick_objects,
			],
		];

		$options[ static::$type . '_post_author' ] = [
			'label'      => __( 'Limit list to the same Post Author', 'pods' ),
			'help'       => __( 'You can choose to limit Posts available for selection to those created by the same Post Author. This only works if this pod is a Post Type and this field is related to a Post Type.', 'pods' ),
			'type'       => 'boolean',
			'default'    => 0,
			'depends-on' => [
				// @todo Support being able to depend on the current pod type like _pod_type => post_type or something.
				static::$type . '_object' => $post_type_pick_objects,
			],
		];

		return $options;

	}

	/**
	 * {@inheritdoc}
	 */
	public function prepare( $options = null ) {
		$format = static::$prepare;

		// Maybe use number format for storage if not a simple relationship and limit is one.
		if ( $options instanceof Field && ! $options->is_simple_relationship() && 1 === $options->get_limit() ) {
			$format = '%d';
		}

		return $format;
	}

	/**
	 * Register a related object.
	 *
	 * @param string $name    Object name.
	 * @param string $label   Object label.
	 * @param array  $options Object options.
	 *
	 * @return array|boolean Object array or false if unsuccessful
	 * @since 2.3.0
	 */
	public function register_related_object( $name, $label, $options = null ) {

		if ( empty( $name ) || empty( $label ) ) {
			return false;
		}

		$related_object = array(
			'label'         => $label,
			'group'         => 'Custom Relationships',
			'simple'        => true,
			'bidirectional' => false,
			'data'          => array(),
			'data_callback' => null,
		);

		$related_object = array_merge( $related_object, $options );

		if ( $related_object['data_callback'] instanceof Closure ) {
			return pods_error( 'Pods does not support closures for data callbacks' );
		}

		self::$custom_related_objects[ $name ] = $related_object;

		return true;

	}

	/**
	 * Setup related objects.
	 *
	 * @param boolean $force Whether to force refresh of related objects.
	 *
	 * @return bool True when data has been loaded
	 * @since 2.3.0
	 */
	public function setup_related_objects( $force = false ) {

		$new_data_loaded = false;

		if ( ! $force && empty( self::$related_objects ) ) {
			// Only load transient if we aren't forcing a refresh.
			self::$related_objects = pods_transient_get( 'pods_related_objects' );

			if ( false !== self::$related_objects ) {
				$new_data_loaded = true;
			}
		} elseif ( $force ) {
			// If we are rebuilding, make sure we start with a clean slate.
			self::$related_objects = array();
		}

		if ( empty( self::$related_objects ) ) {
			// Do a complete build of related_objects.
			$new_data_loaded = true;

			// Custom simple relationship lists.
			self::$related_objects['custom-simple'] = array(
				'label'  => __( 'Simple (custom defined list)', 'pods' ),
				'group'  => __( 'Custom', 'pods' ),
				'simple' => true,
			);

			// Pods options.
			$pod_options = array();

			// Include PodsMeta if not already included.
			pods_meta();

			// Advanced Content Types for relationships.
			$_pods = PodsMeta::$advanced_content_types;

			foreach ( $_pods as $pod ) {
				$pod_options[ $pod['name'] ] = $pod['label'] . ' (' . $pod['name'] . ')';
			}

			/**
			 * Allow filtering the list of Pods to show in the list of relationship objects.
			 *
			 * @since 2.8.0
			 *
			 * @param array $pod_options List of Pods to show in the list of relationship objects.
			 */
			$pod_options = apply_filters( 'pods_field_pick_setup_related_objects_pods', $pod_options );

			asort( $pod_options );

			foreach ( $pod_options as $pod => $label ) {
				self::$related_objects[ 'pod-' . $pod ] = array(
					'label'         => $label,
					'group'         => __( 'Advanced Content Types', 'pods' ),
					'bidirectional' => true,
				);
			}

			/**
			 * Prevent ability to extend core Pods content types.
			 *
			 * @param bool $ignore_internal Default is true, when set to false Pods internal content types can not be extended.
			 *
			 * @since 2.3.19
			 */
			$ignore_internal = apply_filters( 'pods_pick_ignore_internal', true );

			$pods_meta = pods_meta();

			// Public Post Types for relationships.
			$post_types = get_post_types( [ 'public' => true ] );
			asort( $post_types );

			foreach ( $post_types as $post_type => $label ) {
				if ( empty( $post_type ) || 'attachment' === $post_type || ! $pods_meta->is_type_covered( 'post_type', $post_type ) ) {
					unset( $post_types[ $post_type ] );

					continue;
				} elseif ( $ignore_internal && 0 === strpos( $post_type, '_pods_' ) ) {
					unset( $post_types[ $post_type ] );

					continue;
				}

				$post_type = get_post_type_object( $post_type );

				self::$related_objects[ 'post_type-' . $post_type->name ] = array(
					'label'         => $post_type->label . ' (' . $post_type->name . ')',
					'group'         => __( 'Post Types', 'pods' ),
					'bidirectional' => true,
				);
			}

			// Post Types for relationships.
			$post_types = get_post_types( [ 'public' => false ] );
			asort( $post_types );

			foreach ( $post_types as $post_type => $label ) {
				if ( empty( $post_type ) || 'attachment' === $post_type || ! $pods_meta->is_type_covered( 'post_type', $post_type ) ) {
					unset( $post_types[ $post_type ] );

					continue;
				} elseif ( $ignore_internal && 0 === strpos( $post_type, '_pods_' ) ) {
					unset( $post_types[ $post_type ] );

					continue;
				}

				$post_type = get_post_type_object( $post_type );

				self::$related_objects[ 'post_type-' . $post_type->name ] = array(
					'label'         => $post_type->label . ' (' . $post_type->name . ')',
					'group'         => __( 'Post Types (Private)', 'pods' ),
					'bidirectional' => true,
				);
			}

			// Taxonomies for relationships.
			$taxonomies = get_taxonomies();
			asort( $taxonomies );

			foreach ( $taxonomies as $taxonomy => $label ) {
				if ( empty( $taxonomy ) || ! $pods_meta->is_type_covered( 'taxonomy', $taxonomy ) ) {
					unset( $taxonomies[ $taxonomy ] );

					continue;
				} elseif ( $ignore_internal && 0 === strpos( $taxonomy, '_pods_' ) ) {
					unset( $taxonomies[ $taxonomy ] );

					continue;
				}

				$taxonomy = get_taxonomy( $taxonomy );

				self::$related_objects[ 'taxonomy-' . $taxonomy->name ] = array(
					'label'         => $taxonomy->label . ' (' . $taxonomy->name . ')',
					'group'         => __( 'Taxonomies', 'pods' ),
					'bidirectional' => true,
				);
			}//end foreach

			// Other WP Objects for relationships.
			self::$related_objects['user'] = array(
				'label'         => __( 'Users', 'pods' ),
				'group'         => __( 'Other WP Objects', 'pods' ),
				'bidirectional' => true,
			);

			self::$related_objects['role'] = array(
				'label'         => __( 'User Roles', 'pods' ),
				'group'         => __( 'Other WP Objects', 'pods' ),
				'simple'        => true,
				'data_callback' => array( $this, 'data_roles' ),
			);

			self::$related_objects['capability'] = array(
				'label'         => __( 'User Capabilities', 'pods' ),
				'group'         => __( 'Other WP Objects', 'pods' ),
				'simple'        => true,
				'data_callback' => array( $this, 'data_capabilities' ),
			);

			self::$related_objects['media'] = array(
				'label'         => __( 'Media', 'pods' ),
				'group'         => __( 'Other WP Objects', 'pods' ),
				'bidirectional' => true,
			);

			self::$related_objects['comment'] = array(
				'label'         => __( 'Comments', 'pods' ),
				'group'         => __( 'Other WP Objects', 'pods' ),
				'bidirectional' => true,
			);

			self::$related_objects['image-size'] = array(
				'label'         => __( 'Image Sizes', 'pods' ),
				'group'         => __( 'Other WP Objects', 'pods' ),
				'simple'        => true,
				'data_callback' => array( $this, 'data_image_sizes' ),
			);

			self::$related_objects['nav_menu'] = array(
				'label' => __( 'Navigation Menus', 'pods' ),
				'group' => __( 'Other WP Objects', 'pods' ),
			);

			self::$related_objects['post_format'] = array(
				'label' => __( 'Post Formats', 'pods' ),
				'group' => __( 'Other WP Objects', 'pods' ),
			);

			self::$related_objects['post-status'] = array(
				'label'         => __( 'Post Status', 'pods' ),
				'group'         => __( 'Other WP Objects', 'pods' ),
				'simple'        => true,
				'data_callback' => array( $this, 'data_post_stati' ),
			);

			self::$related_objects['post-types'] = [
				'label'         => __( 'Post Type Objects', 'pods' ),
				'group'         => __( 'Other WP Objects', 'pods' ),
				'simple'        => true,
				'data_callback' => [ $this, 'data_post_types' ],
			];

			self::$related_objects['taxonomies'] = [
				'label'         => __( 'Taxonomy Objects', 'pods' ),
				'group'         => __( 'Other WP Objects', 'pods' ),
				'simple'        => true,
				'data_callback' => [ $this, 'data_taxonomies' ],
			];

			do_action( 'pods_form_ui_field_pick_related_objects_other' );

			self::$related_objects['country'] = array(
				'label'         => __( 'Countries', 'pods' ),
				'group'         => __( 'Predefined Lists', 'pods' ),
				'simple'        => true,
				'data_callback' => array( $this, 'data_countries' ),
			);

			self::$related_objects['us_state'] = array(
				'label'         => __( 'US States', 'pods' ),
				'group'         => __( 'Predefined Lists', 'pods' ),
				'simple'        => true,
				'data_callback' => array( $this, 'data_us_states' ),
			);

			self::$related_objects['ca_province'] = array(
				'label'         => __( 'CA Provinces', 'pods' ),
				'group'         => __( 'Predefined Lists', 'pods' ),
				'simple'        => true,
				'data_callback' => array( $this, 'data_ca_provinces' ),
			);

			self::$related_objects['days_of_week'] = array(
				'label'         => __( 'Calendar - Days of Week', 'pods' ),
				'group'         => __( 'Predefined Lists', 'pods' ),
				'simple'        => true,
				'data_callback' => array( $this, 'data_days_of_week' ),
			);

			self::$related_objects['months_of_year'] = array(
				'label'         => __( 'Calendar - Months of Year', 'pods' ),
				'group'         => __( 'Predefined Lists', 'pods' ),
				'simple'        => true,
				'data_callback' => array( $this, 'data_months_of_year' ),
			);

			do_action( 'pods_form_ui_field_pick_related_objects_predefined' );

			if ( did_action( 'init' ) ) {
				pods_transient_set( 'pods_related_objects', self::$related_objects, WEEK_IN_SECONDS );
			}
		}//end if

		/**
		 * Allow custom related objects to be defined
		 */
		do_action( 'pods_form_ui_field_pick_related_objects_custom' );

		foreach ( self::$custom_related_objects as $object => $related_object ) {
			if ( ! isset( self::$related_objects[ $object ] ) ) {
				$new_data_loaded = true;

				self::$related_objects[ $object ] = $related_object;
			}
		}

		if ( $new_data_loaded ) {
			/**
			 * Allow hooking in when new data has been loaded.
			 *
			 * @since 2.8.0
			 */
			do_action( 'pods_form_ui_field_pick_related_objects_new_data_loaded' );
		}

		return true;

	}

	/**
	 * Return available related objects
	 *
	 * @param boolean $force Whether to force refresh of related objects.
	 *
	 * @return array Field selection array
	 * @since 2.3.0
	 */
	public function related_objects( $force = false ) {
		if ( null !== self::$names_related ) {
			return self::$names_related;
		}

		$this->setup_related_objects( $force );

		$related_objects = array();

		foreach ( self::$related_objects as $related_object_name => $related_object ) {
			if ( ! isset( $related_objects[ $related_object['group'] ] ) ) {
				$related_objects[ $related_object['group'] ] = array();
			}

			$related_objects[ $related_object['group'] ][ $related_object_name ] = $related_object['label'];
		}

		self::$names_related = (array) apply_filters( 'pods_form_ui_field_pick_related_objects', $related_objects );

		return self::$names_related;
	}

	/**
	 * Return available simple object names
	 *
	 * @return array Simple object names
	 * @since 2.3.0
	 */
	public function simple_objects() {
		if ( null !== self::$names_simple ) {
			return self::$names_simple;
		}

		$this->setup_related_objects();

		$simple_objects = array();

		foreach ( self::$related_objects as $object => $related_object ) {
			if ( ! isset( $related_object['simple'] ) || ! $related_object['simple'] ) {
				continue;
			}

			$simple_objects[] = $object;
		}

		self::$names_simple = (array) apply_filters( 'pods_form_ui_field_pick_simple_objects', $simple_objects );

		return self::$names_simple;
	}

	/**
	 * Return available bidirectional object names
	 *
	 * @return array Bidirectional object names
	 * @since 2.3.4
	 */
	public function bidirectional_objects() {
		if ( null !== self::$names_bidirectional ) {
			return self::$names_bidirectional;
		}

		$this->setup_related_objects();

		$bidirectional_objects = array();

		foreach ( self::$related_objects as $object => $related_object ) {
			if ( ! isset( $related_object['bidirectional'] ) || ! $related_object['bidirectional'] ) {
				continue;
			}

			$bidirectional_objects[] = $object;
		}

		self::$names_bidirectional = (array) apply_filters( 'pods_form_ui_field_pick_bidirectional_objects', $bidirectional_objects );

		return self::$names_bidirectional;
	}

	/**
	 * {@inheritdoc}
	 */
	public function schema( $options = null ) {

		$schema = false;

		$simple_tableless_objects = $this->simple_objects();

		if ( in_array( pods_v( static::$type . '_object', $options ), $simple_tableless_objects, true ) ) {
			$schema = 'LONGTEXT';
		}

		return $schema;

	}

	/**
	 * {@inheritdoc}
	 */
	public function display( $value = null, $name = null, $options = null, $pod = null, $id = null ) {

		$fields = null;

		if ( is_object( $pod ) && isset( $pod->fields ) ) {
			/**
			 * @var $pod Pods Pods object.
			 */
			$fields = pods_config_get_all_fields( $pod );
		} elseif ( is_array( $pod ) && isset( $pod['fields'] ) ) {
			$fields = pods_config_get_all_fields( $pod );
		}

		$args = array(
			'field'  => $name,
			'fields' => $fields,
		);

		$display_format = pods_v( static::$type . '_display_format_multi', $options, 'default' );

		if ( 'non_serial' === $display_format ) {
			$args['serial'] = false;
		}

		if ( 'custom' === $display_format ) {
			$args['serial'] = false;

			$separator = pods_v( static::$type . '_display_format_separator', $options, ', ' );
			if ( ! empty( $separator ) ) {
				$args['separator'] = $separator;

				// Replicate separator behavior.
				$args['and'] = $args['separator'];
			}
		}

		return pods_serial_comma( $value, $args );
	}

	/**
	 * {@inheritdoc}
	 */
	public function input( $name, $value = null, $options = null, $pod = null, $id = null ) {
		$options = ( is_array( $options ) || is_object( $options ) ) ? $options : (array) $options;

		// Do anything we need to do here with options setup / enforcement.

		// Default labels.
		if ( empty( $options[ static::$type . '_add_new_label' ] ) ) {
			$options[ static::$type . '_add_new_label' ] = __( 'Add New', 'pods' );
		}

		parent::input( $name, $value, $options, $pod, $id );
	}

	/**
	 * {@inheritdoc}
	 */
	public function build_dfv_field_options( $options, $args ) {
		// Use field object if it was provided.
		if ( isset( $options['_field_object'] ) ) {
			$field = $options['_field_object'];

			unset( $options['_field_object'] );

			$options = pods_config_merge_data( $field, $options );
		}

		$field_options = $options instanceof \Pods\Whatsit ? $options->export() : $options;

		if ( ! isset( $field_options['id'] ) ) {
			$field_options['id'] = 0;
		}

		// Enforce defaults.
		$all_options = static::options();

		foreach ( $all_options as $option_name => $option ) {
			$default = pods_v( 'default', $option, '' );

			$field_options[ $option_name ] = pods_v( $option_name, $field_options, $default );

			if ( '' === $field_options[ $option_name ] ) {
				$field_options[ $option_name ] = $default;
			}
		}

		$field_options['grouped'] = 1;

		if ( empty( $field_options[ $args->type . '_object' ] ) ) {
			$field_options[ $args->type . '_object' ] = '';
		}

		if ( empty( $field_options[ $args->type . '_val' ] ) ) {
			$field_options[ $args->type . '_val' ] = '';
		}

		// Unset the table info for now.
		$field_options['table_info'] = [];

		$custom = pods_v( $args->type . '_custom', $field_options, false );

		$custom = apply_filters( 'pods_form_ui_field_pick_custom_values', $custom, $args->name, $args->value, $field_options, $args->pod, $args->id );

		$ajax = false;

		if ( $this->can_ajax( $args->type, $field_options ) ) {
			$ajax = true;

			$field_data = pods_static_cache_get( $field_options['name'] . '/' . $field_options['id'], __CLASS__ . '/field_data' ) ?: [];

			if ( isset( $field_data['autocomplete'] ) ) {
				$ajax = (boolean) $field_data['autocomplete'];
			}
		}

		$ajax = apply_filters( 'pods_form_ui_field_pick_ajax', $ajax, $args->name, $args->value, $field_options, $args->pod, $args->id );

		if ( 0 === (int) pods_v( $args->type . '_ajax', $field_options, 1 ) ) {
			$ajax = false;
		}

		$field_options[ $args->type . '_ajax' ] = (int) $ajax;

		$format_type = pods_v( $args->type . '_format_type', $field_options, 'single', true );

		$limit = 1;

		if ( 'single' === $format_type ) {
			$format_single = pods_v( $args->type . '_format_single', $field_options, 'dropdown', true );

			if ( 'dropdown' === $format_single ) {
				$field_options['view_name'] = 'select';
			} elseif ( 'radio' === $format_single ) {
				$field_options['view_name'] = 'radio';
			} elseif ( 'autocomplete' === $format_single ) {
				$field_options['view_name'] = 'select2';
			} elseif ( 'list' === $format_single ) {
				$field_options['view_name'] = 'list';
			} else {
				$field_options['view_name'] = $format_single;
			}
		} elseif ( 'multi' === $format_type ) {
			$format_multi = pods_v( $args->type . '_format_multi', $field_options, 'checkbox', true );

			if ( ! empty( $args->value ) && ! is_array( $args->value ) ) {
				$args->value = explode( ',', $args->value );
			}

			if ( 'checkbox' === $format_multi ) {
				$field_options['view_name'] = 'checkbox';
			} elseif ( 'multiselect' === $format_multi ) {
				$field_options['view_name'] = 'select';
			} elseif ( 'autocomplete' === $format_multi ) {
				$field_options['view_name'] = 'select2';
			} elseif ( 'list' === $format_multi ) {
				$field_options['view_name'] = 'list';
			} else {
				$field_options['view_name'] = $format_multi;
			}

			$limit = 0;

			if ( ! empty( $field_options[ $args->type . '_limit' ] ) ) {
				$limit = absint( $field_options[ $args->type . '_limit' ] );
			}
		} else {
			$field_options['view_name'] = $format_type;
		}

		$field_options[ $args->type . '_limit' ] = $limit;

		$field_options['ajax_data'] = $this->build_dfv_autocomplete_ajax_data( $field_options, $args, $ajax );
		$field_options['select2_overrides'] = null;

		if ( 'select2' === $field_options['view_name'] ) {
			// @todo Revisit this, they probably aren't used anymore now since this is DFV.
			wp_enqueue_style( 'pods-select2' );
			wp_enqueue_script( 'pods-select2' );

			/**
			 * Allow overriding some Select2/SelectWoo options used in the JS init.
			 *
			 * @since 2.7.0
			 *
			 * @param array|null $select2_overrides Override options for Select2/SelectWoo.
			 */
			$field_options['select2_overrides'] = apply_filters( 'pods_pick_select2_overrides', $field_options['select2_overrides'] );
		}

		return $field_options;
	}

	/**
	 * Build DFV autocomplete AJAX data.
	 *
	 * @param array|Field  $options DFV options.
	 * @param object $args    {
	 *  Field information arguments.
	 *
	 *     @type string     $name    Field name.
	 *     @type string     $type    Field type.
	 *     @type array      $options Field options.
	 *     @type mixed      $value   Current value.
	 *     @type array      $pod     Pod information.
	 *     @type int|string $id      Current item ID.
	 * }
	 * @param bool   $ajax    True if ajax mode should be used.
	 *
	 * @return array
	 */
	public function build_dfv_autocomplete_ajax_data( $options, $args, $ajax = false ) {

		if ( is_array( $args->pod ) ) {
			$pod_name = $args->pod['name'];
		} elseif ( $args->pod instanceof Pods ) {
			$pod_name = $args->pod->pod;
		} elseif ( $args->pod instanceof Pod ) {
			$pod_name = $args->pod->get_name();
		} else {
			$pod_name = '';
		}

		if ( is_array( $options ) ) {
			$field_name = pods_v( 'name', $options );
		} elseif ( $options instanceof Field ) {
			$field_name = $options->get_name();
		} else {
			$field_name = '';
		}

		$id = (int) $args->id;

		if ( is_user_logged_in() ) {
			$uid = 'user_' . get_current_user_id();
		} else {
			$uid = pods_session_id();
		}

		$uri_hash = wp_create_nonce( 'pods_uri_' . $_SERVER['REQUEST_URI'] );

		$nonce_name  = 'pods_relationship:' . json_encode( compact( 'pod_name', 'field_name', 'uid', 'uri_hash', 'id' ) );
		$field_nonce = wp_create_nonce( $nonce_name );

		// Values can be overridden via the `pods_field_dfv_data` filter in $data['fieldConfig']['ajax_data'].
		return [
			'ajax'                 => $ajax,
			'delay'                => 300,
			'minimum_input_length' => 1,
			'pod_name'             => $pod_name,
			'field_name'           => $field_name,
			'id'                   => $id,
			'uri_hash'             => $uri_hash,
			'_wpnonce'             => $field_nonce,
		];
	}

	/**
	 * {@inheritdoc}
	 */
	public function build_dfv_field_config( $args ) {
		$config = parent::build_dfv_field_config( $args );

		// Ensure data is passed in for relationship fields.
		if ( ! isset( $config['data'] ) && ! empty( $args->options['data'] ) ) {
			$config['data'] = $this->get_raw_data( $args->options );
		}

		// Default optgroup handling to off.
		if ( ! isset( $config['optgroup'] ) ) {
			$config['optgroup'] = false;
		}

		/**
		 * Filter on whether to allow modals to be used on the front of the site (in an non-admin area).
		 *
		 * @param boolean $show_on_front
		 * @param array $config
		 * @param array $args
		 *
		 * @since 2.7.0
		 */
		$show_on_front = apply_filters( 'pods_ui_dfv_pick_modals_show_on_front', false, $config, $args );

		/**
		 * Filter on whether to allow nested modals to be used (modals within modals).
		 *
		 * @param boolean $allow_nested_modals
		 * @param array $config
		 * @param array $args
		 *
		 * @since 2.7.0
		 */
		$allow_nested_modals = apply_filters( 'pods_ui_dfv_pick_modals_allow_nested', false, $config, $args );

		// Disallow add/edit outside the admin and when we're already in a modal
		if ( ( ! $show_on_front && ! is_admin() ) || ( ! $allow_nested_modals && pods_is_modal_window() ) ) {
			$config[ $args->type . '_allow_add_new' ]  = false;
			$config[ $args->type . '_show_edit_link' ] = false;
		}

		$config[ $args->type . '_taggable' ]  = filter_var( pods_v( $args->type . '_taggable', $config ), FILTER_VALIDATE_BOOLEAN );
		$config[ $args->type . '_allow_add_new' ]  = filter_var( pods_v( $args->type . '_allow_add_new', $config ), FILTER_VALIDATE_BOOLEAN );
		$config[ $args->type . '_show_edit_link' ] = filter_var( pods_v( $args->type . '_show_edit_link', $config ), FILTER_VALIDATE_BOOLEAN );

		$iframe = array(
			'src'        => '',
			'url'        => '',
			'query_args' => array(),
		);

		// Set the file name and args based on the content type of the relationship
		switch ( $args->options['pick_object'] ) {
			case 'post_type':
				if ( ! empty( $args->options['pick_val'] ) ) {
					$post_type_obj = get_post_type_object( $args->options['pick_val'] );

					if ( $post_type_obj && current_user_can( $post_type_obj->cap->create_posts ) ) {
						$iframe['url']        = admin_url( 'post-new.php' );
						$iframe['query_args'] = array(
							'post_type' => $args->options['pick_val'],
						);
					}
				}

				// Determine the default icon to use for this post type,
				// default to the dashicon for posts
				$config[ 'default_icon' ] = 'dashicons-admin-post';

				// Any custom specified menu icon gets priority
				$post_type = get_post_type_object( $args->options[ 'pick_val' ] );
				if ( ! empty( $post_type->menu_icon ) ) {
					$config[ 'default_icon' ] = $post_type->menu_icon;

				// Page and attachment have their own dashicons
				} elseif ( isset( $post_type->name ) ) {
					switch ( $post_type->name ) {
						case 'page':
							// Default for pages.
							$config[ 'default_icon' ] = 'dashicons-admin-page';
							break;
						case 'attachment':
							// Default for attachments.
							$config[ 'default_icon' ] = 'dashicons-admin-media';
							break;
					}
				}

				break;

			case 'taxonomy':
				/*
				 * @todo Fix add new modal issues
				if ( ! empty( $args->options['pick_val'] ) ) {
					$taxonomy_obj = get_taxonomy( $args->options['pick_val'] );

					if ( $taxonomy_obj && current_user_can( $taxonomy_obj->cap->edit_terms ) ) {
						$iframe['url']  = admin_url( 'edit-tags.php' );
						$iframe['query_args'] = array(
							'taxonomy' => $args->options['pick_val'],
						);
					}
				}
				*/

				break;

			case 'user':
				if ( current_user_can( 'create_users' ) ) {
					$iframe['url'] = admin_url( 'user-new.php' );
				}

				break;

			case 'pod':
				if ( ! empty( $args->options['pick_val'] ) ) {
					if ( pods_is_admin( array( 'pods', 'pods_content', 'pods_edit_' . $args->options['pick_val'] ) ) ) {
						$iframe['url']        = admin_url( 'admin.php' );
						$iframe['query_args'] = array(
							'page'   => 'pods-manage-' . $args->options['pick_val'],
							'action' => 'add',
						);

					}
				}

				break;
		}//end switch

		// Potential valid modal target if we've set the file name
		if ( ! empty( $iframe['url'] ) ) {
			// @todo: Replace string literal with defined constant
			$iframe['query_args']['pods_modal'] = 1;

			// Add args we always need
			$iframe['src'] = add_query_arg( $iframe['query_args'], $iframe['url'] );
		}

		$iframe['title_add']  = sprintf( __( '%s: Add New', 'pods' ), $args->options['label'] );
		$iframe['title_edit'] = sprintf( __( '%s: Edit', 'pods' ), $args->options['label'] );

		/**
		 * Allow filtering iframe configuration
		 *
		 * @param array $iframe
		 * @param array $config
		 * @param array $args
		 *
		 * @since 2.7.0
		 */
		$iframe = apply_filters( 'pods_ui_dfv_pick_modals_iframe', $iframe, $config, $args );

		if ( ! empty( $iframe['src'] ) ) {
			// We extend wp.media.view.Modal for modal add/edit, we must ensure we load the template for it
			wp_enqueue_media();
		}

		$config['iframe_src']        = $iframe['src'];
		$config['iframe_title_add']  = $iframe['title_add'];
		$config['iframe_title_edit'] = $iframe['title_edit'];

		return $config;

	}

	/**
	 * {@inheritdoc}
	 */
	public function build_dfv_field_item_data( $args ) {
		$args->options['supports_thumbnails'] = null;

		$item_data = [];
		$data      = [];

		if ( ! empty( $args->options['data'] ) ) {
			$data = $this->get_raw_data( $args->options );
		} elseif ( ! empty( $args->data ) ) {
			$data = $args->data;
		}

		if ( [] !== $data ) {
			$item_data = $this->build_dfv_field_item_data_recurse( $data, $args );
		}

		return $item_data;

	}

	/**
	 * Loop through relationship data and expand item data with additional information for DFV.
	 *
	 * @param array  $data    Item data to expand.
	 * @param object $args    {
	 *      Field information arguments.
	 *
	 *     @type string     $name    Field name.
	 *     @type string     $type    Field type.
	 *     @type array      $options Field options.
	 *     @type mixed      $value   Current value.
	 *     @type array      $pod     Pod information.
	 *     @type int|string $id      Current item ID.
	 * }
	 *
	 * @return array
	 */
	public function build_dfv_field_item_data_recurse( $data, $args ) {

		$item_data = array();

		foreach ( $data as $item_id => $item_title ) {
			if ( is_array( $item_title ) ) {
				$args->options['optgroup'] = true;

				$item_data[] = array(
					'label'      => $item_id,
					'collection' => $this->build_dfv_field_item_data_recurse( $item_title, $args ),
				);
			} else {
				// Key by item_id temporarily to be able to sort based on $args->value
				$item_data[ $item_id ] = $this->build_dfv_field_item_data_recurse_item( $item_id, $item_title, $args );
			}
		}

		// Maintain any saved sort order from $args->value
		if ( is_array( $args->value ) && 1 < count( $args->value ) && $this->is_autocomplete( $args->options ) ) {
			$new_item_data = [];

			foreach ( $args->value as $value_key => $value_item ) {
				if ( ! is_int( $value_key ) ) {
					if ( isset( $item_data[ $value_key ] ) ) {
						$value_item = $item_data[ $value_key ];
					}

					$new_item_data[ $value_key ] = $value_item;
				}
			}

			$item_data = array_merge( $new_item_data, $item_data );
		}

		// Convert from associative to numeric array
		return array_values( $item_data );

	}

	/**
	 * Loop through relationship data and expand item data with additional information for DFV.
	 *
	 * @param int|string $item_id    Item ID.
	 * @param string     $item_title Item title.
	 * @param object     $args       {
	 *      Field information arguments.
	 *
	 *     @type string      $name    Field name.
	 *     @type string      $type    Field type.
	 *     @type array       $options Field options.
	 *     @type mixed       $value   Current value.
	 *     @type array       $pod     Pod information.
	 *     @type int|string  $id      Current item ID.
	 * }
	 *
	 * @return array
	 */
	public function build_dfv_field_item_data_recurse_item( $item_id, $item_title, $args ) {

		$icon         = '';
		$img_icon     = '';
		$edit_link    = '';
		$link         = '';

		if ( ! isset( $args->options['supports_thumbnails'] ) ) {
			$args->options['supports_thumbnails'] = null;
		}

		switch ( $args->options['pick_object'] ) {
			case 'post_type':
				if ( null === $args->options['supports_thumbnails'] && ! empty( $args->options['pick_val'] ) ) {
					$args->options['supports_thumbnails'] = post_type_supports( $args->options['pick_val'], 'thumbnail' );
				}

				if ( true === $args->options['supports_thumbnails'] ) {
					$post_thumbnail_id = get_post_thumbnail_id( $item_id );

					if ( $post_thumbnail_id ) {
						$thumb = wp_get_attachment_image_src( $post_thumbnail_id, 'thumbnail', false );
					}

					if ( ! empty( $thumb[0] ) ) {
						$img_icon = $thumb[0];
					}
				}

				if ( empty( $img_icon ) ) {

					// Default icon for posts.
					$icon = 'dashicons-admin-post';

					// Post type icons.
					$post_type = (array) get_post_type_object( get_post_type( $item_id ) );

					if ( ! empty( $post_type['menu_icon'] ) ) {
						// Post specific icon.
						$icon = $post_type['menu_icon'];
					} elseif ( isset( $post_type['name'] ) && 'page' ) {
						switch ( $post_type['name'] ) {
							case 'page':
								// Default for pages.
								$icon = 'dashicons-admin-page';
								break;
							case 'attachment':
								// Default for attachments.
								$icon = 'dashicons-admin-media';
								break;
						}
					}
				}//end if

				$edit_link = get_edit_post_link( $item_id, 'raw' );

				$link = get_permalink( $item_id );

				break;

			case 'taxonomy':
				if ( ! empty( $args->options['pick_val'] ) ) {

					// Default icon for taxonomy.
					$icon = 'dashicons-category';

					// Change icon for non-hierarchical taxonomies.
					$taxonomy = get_term( $item_id );
					if ( isset( $taxonomy->taxonomy ) ) {
						$taxonomy = (array) get_taxonomy( $taxonomy->taxonomy );
						if ( isset( $taxonomy['hierarchical'] ) && ! $taxonomy['hierarchical'] ) {
							$icon = 'dashicons-tag';
						}
					}

					$edit_link = get_edit_term_link( $item_id, $args->options['pick_val'] );

					$link = get_term_link( $item_id, $args->options['pick_val'] );
				}

				break;

			case 'user':
				$args->options['supports_thumbnails'] = true;

				$icon     = 'dashicons-admin-users';
				$img_icon = get_avatar_url( $item_id, array( 'size' => 150 ) );

				$edit_link = get_edit_user_link( $item_id );

				$link = get_author_posts_url( $item_id );

				break;

			case 'comment':
				$args->options['supports_thumbnails'] = true;

				$icon     = 'dashicons-admin-comments';
				$img_icon = get_avatar_url( get_comment( $item_id ), array( 'size' => 150 ) );

				$edit_link = get_edit_comment_link( $item_id );

				$link = get_comment_link( $item_id );

				break;

			case 'pod':
				if ( ! empty( $args->options['pick_val'] ) ) {

					$icon = pods_svg_icon( 'pods' );

					if ( pods_is_admin( array( 'pods', 'pods_content', 'pods_edit_' . $args->options['pick_val'] ) ) ) {
						$file_name  = 'admin.php';
						$query_args = array(
							'page'   => 'pods-manage-' . $args->options['pick_val'],
							'action' => 'edit',
							'id'     => $item_id,
						);

						$edit_link = add_query_arg( $query_args, admin_url( $file_name ) );
					}

					// @todo Add $link support
					$link = '';
				}

				break;
		}//end switch

		// Image icons always overwrite default icons
		if ( ! empty( $img_icon ) ) {
			$icon = $img_icon;
		}

		// Parse icon type
		if ( 'none' === $icon || 'div' === $icon ) {
			$icon = '';
		} elseif ( 0 === strpos( $icon, 'dashicons-' ) ) {
			$icon = sanitize_html_class( $icon );
		}

		// #5740 Check for WP_Error object.
		if ( ! is_string( $link ) ) {
			$link = '';
		}

		// Support modal editing
		if ( ! empty( $edit_link ) ) {
			// @todo: Replace string literal with defined constant
			$edit_link = add_query_arg( array( 'pods_modal' => '1' ), $edit_link );
		}

		// Determine if this is a selected item.
		// Issue history for setting selected: #4753, #4892, #5014.
		$selected = false;

		$values = array();

		// If we have values, let's cast them.
		if ( isset( $args->value ) ) {
			// The value may be a single non-array value.
			$values = (array) $args->value;
		}

		// Cast values in array as string.
		$values = array_map( static function( $value ) {
			if ( ! is_scalar( $value ) ) {
				return $value;
			}

			return (string) $value;
		}, $values );

		// If the value array has keys as IDs, let's check for matches from the keys first.
		if ( ! isset( $values[0] ) ) {
			// Get values from keys.
			$key_values = array_keys( $values );

			// Cast key values in array as string.
			$key_values = array_map( static function( $value ) {
				if ( ! is_scalar( $value ) ) {
					return $value;
				}

				return (string) $value;
			}, $key_values );

			// Let's check to see if the current $item_id matches any key values.
			if ( in_array( (string) $item_id, $key_values, true ) ) {
				$selected = true;
			}
		}

		// If we do not have a key match, the normal values may still match.
		if ( ! $selected ) {
			// Let's check to see if the current $item_id matches any values.
			if ( in_array( (string) $item_id, $values, true ) ) {
				$selected = true;
			}
		}

		$item = array(
			'id'        => html_entity_decode( esc_html( $item_id ) ),
			'icon'      => esc_attr( $icon ),
			'name'      => wp_strip_all_tags( html_entity_decode( $item_title ) ),
			'edit_link' => html_entity_decode( esc_url( $edit_link ) ),
			'link'      => html_entity_decode( esc_url( $link ) ),
			'selected'  => $selected,
		);

		return $item;

	}

	/**
	 * {@inheritdoc}
	 */
	public function validate( $value, $name = null, $options = null, $fields = null, $pod = null, $id = null, $params = null ) {
		$validate = parent::validate( $value, $name, $options, $fields, $pod, $id, $params );

		$errors = array();

		if ( is_array( $validate ) ) {
			$errors = $validate;
		}

		if ( empty( self::$api ) ) {
			self::$api = pods_api();
		}

		$simple_tableless_objects = $this->simple_objects();

		$related_pick_limit  = 0;
		$related_field       = false;
		$related_pod         = false;
		$current_related_ids = false;

		// Bidirectional relationship requirement checks
		$related_object = pods_v( static::$type . '_object', $options, '' );
		// pod, post_type, taxonomy, etc..
		$related_val = pods_v( static::$type . '_val', $options, $related_object, null, true );
		// pod name, post type name, taxonomy name, etc..
		if ( empty( $related_val ) ) {
			$related_val = $related_object;
		}

		$related_sister_id = pods_v( 'sister_id', $options, 0 );

		if ( is_numeric( $related_sister_id ) ) {
			$related_sister_id = (int) $related_sister_id;
		} else {
			$related_sister_id = 0;
		}

		$options['id'] = (int) $options['id'];

		$related_data = pods_static_cache_get( $options['name'] . '/' . $options['id'], __CLASS__ . '/related_data' ) ?: [];

		if ( ! empty( $related_sister_id ) && ! in_array( $related_object, $simple_tableless_objects, true ) ) {
			$related_pod = self::$api->load_pod( [
				'name'       => $related_val,
				'auto_setup' => true,
			] );

			if ( false !== $related_pod && ( 'pod' === $related_object || $related_object === $related_pod['type'] ) ) {
				$related_field = false;

				// Ensure sister_id exists on related Pod.
				foreach ( $related_pod['fields'] as $related_pod_field ) {
					if ( 'pick' === $related_pod_field['type'] && $related_sister_id === $related_pod_field['id'] ) {
						$related_field = $related_pod_field;

						break;
					}
				}

				if ( ! empty( $related_field ) ) {
					$current_ids = self::$api->lookup_related_items( $options['id'], $pod['id'], $id, $options, $pod );

					$related_data[ 'current_ids_' . $id ] = $current_ids;

					$value_ids = $value;

					// Convert values from a comma-separated string into an array.
					if ( ! is_array( $value_ids ) ) {
						$value_ids = explode( ',', $value_ids );
					}

					$value_ids = array_unique( array_filter( $value_ids ) );

					// Get ids to remove.
					$remove_ids = array_diff( $current_ids, $value_ids );

					$related_data[ 'remove_ids_' . $id ] = $remove_ids;

					$related_required   = (boolean) pods_v( 'required', $related_field, 0 );
					$related_pick_limit = (int) pods_v( static::$type . '_limit', $related_field, 0 );

					if ( 'single' === pods_v( static::$type . '_format_type', $related_field ) ) {
						$related_pick_limit = 1;
					}

					// Validate Required fields.
					if ( $related_required && ! empty( $remove_ids ) ) {
						foreach ( $remove_ids as $related_id ) {
							$bidirectional_ids = self::$api->lookup_related_items( $related_field['id'], $related_pod['id'], $related_id, $related_field, $related_pod );

							$related_data[ 'related_ids_' . $related_id ] = $bidirectional_ids;

							if ( empty( $bidirectional_ids ) || ( in_array( (int) $id, $bidirectional_ids, true ) && 1 === count( $bidirectional_ids ) ) ) {
								// Translators: %1$s and %2$s stand for field labels.
								$errors[] = sprintf( esc_html__( 'The %1$s field is required and cannot be removed by the %2$s field', 'pods' ), $related_field['label'], $options['label'] );
								return $errors;
							}
						}
					}
				} else {
					$related_pod = false;
				}//end if
			} else {
				$related_pod = false;
			}//end if
		}//end if

		if ( ! empty( $related_data ) ) {
			$related_data['related_pod']        = $related_pod;
			$related_data['related_field']      = $related_field;
			$related_data['related_pick_limit'] = $related_pick_limit;

			pods_static_cache_set( $options['name'] . '/' . $options['id'], $related_data, __CLASS__ . '/related_data' );

			$pick_limit = (int) pods_v( static::$type . '_limit', $options, 0 );

			if ( 'single' === pods_v( static::$type . '_format_type', $options ) ) {
				$pick_limit = 1;
			}

			$related_field['id'] = (int) $related_field['id'];

			$bidirectional_related_data = pods_static_cache_get( $related_field['name'] . '/' . $related_field['id'], __CLASS__ . '/related_data' ) ?: [];

			if ( empty( $bidirectional_related_data ) ) {
				$bidirectional_related_data = [
					'related_pod'        => $pod,
					'related_field'      => $options,
					'related_pick_limit' => $pick_limit,
				];

				pods_static_cache_set( $related_field['name'] . '/' . $related_field['id'], $bidirectional_related_data, __CLASS__ . '/related_data' );
			}
		}//end if

		if ( ! empty( $errors ) ) {
			return $errors;
		}

		return $validate;

	}

	/**
	 * {@inheritdoc}
	 */
	public function save( $value, $id = null, $name = null, $options = null, $fields = null, $pod = null, $params = null ) {

		if ( empty( self::$api ) ) {
			self::$api = pods_api();
		}

		$options['id'] = (int) $options['id'];

		$related_pod        = null;
		$related_field      = null;
		$related_pick_limit = 0;
		$current_ids        = [];
		$remove_ids         = [];

		if ( null === $value ) {
			$value = [];
		} elseif ( ! is_array( $value ) ) {
			$value = [
				$value,
			];
		}

		$value_ids = array_unique( array_filter( $value ) );

		$related_data = pods_static_cache_get( $options['name'] . '/' . $options['id'], __CLASS__ . '/related_data' ) ?: [];

		if ( ! empty( $related_data ) && isset( $related_data['current_ids_' . $id ], $related_data['remove_ids_' . $id ] ) ) {
			$related_pod        = $related_data['related_pod'];
			$related_field      = $related_data['related_field'];
			$related_pick_limit = $related_data['related_pick_limit'];
			$current_ids        = $related_data[ 'current_ids_' . $id ];
			$remove_ids         = $related_data[ 'remove_ids_' . $id ];
		} elseif ( $options instanceof Field || $options instanceof Value_Field ) {
			$related_field = $options->get_bidirectional_field();

			if ( ! $related_field ) {
				return;
			}

			$related_pod        = $related_field->get_parent_object();
			$related_pick_limit = $related_field->get_arg( 'related_pick_limit', 0 );
			$current_ids        = self::$api->lookup_related_items( $options['id'], $pod['id'], $id, $options, $pod );

			// Get ids to remove.
			$remove_ids = array_diff( $current_ids, $value_ids );
		}

		if ( empty( $related_field ) || empty( $related_pod ) ) {
			return;
		}

		// Handle the bi-directional relationship updates.

		$no_conflict = true;

		// Only check no conflict mode if this isn't the current pod type.
		if ( $related_pod['type'] !== $pod['type'] ) {
			$no_conflict = pods_no_conflict_check( $related_pod['type'] );
		}

		if ( ! $no_conflict ) {
			pods_no_conflict_on( $related_pod['type'] );
		}

		if ( empty( $value_ids ) ) {
			// Remove all bidirectional relationships.
			if ( ! empty( $remove_ids ) ) {
				self::$api->delete_relationships( $remove_ids, $id, $related_pod, $related_field );
				self::$api->delete_relationships( $id, $remove_ids, $pod, $options );
			}

			if ( ! $no_conflict ) {
				pods_no_conflict_off( $related_pod['type'] );
			}

			return;
		}

		foreach ( $value_ids as $related_id ) {
			if ( ! empty( $related_data[ 'related_ids_' . $related_id ] ) ) {
				$bidirectional_ids = $related_data[ 'related_ids_' . $related_id ];
			} else {
				$bidirectional_ids = self::$api->lookup_related_items( $related_field['id'], $related_pod['id'], $related_id, $related_field, $related_pod );
			}

			$bidirectional_ids = array_filter( $bidirectional_ids );

			if ( empty( $bidirectional_ids ) ) {
				$bidirectional_ids = array();
			}

			$bidirectional_remove_ids = array();

			if ( 0 < $related_pick_limit && ! empty( $bidirectional_ids ) && ! in_array( $id, $bidirectional_ids, true ) ) {
				$total_bidirectional_ids = count( $bidirectional_ids );

				while ( $related_pick_limit <= $total_bidirectional_ids ) {
					$bidirectional_remove_ids[] = (int) array_pop( $bidirectional_ids );

					$total_bidirectional_ids = count( $bidirectional_ids );
				}
			}

			// Remove this item from related items no longer related to.
			$bidirectional_remove_ids = array_unique( array_filter( $bidirectional_remove_ids ) );

			if ( ! in_array( $id, $bidirectional_ids, true ) ) {
				// Add to related items.
				$bidirectional_ids[] = $id;
			} elseif ( empty( $remove_ids ) ) {
				// Nothing to change.
				continue;
			}

			self::$api->save_relationships( $related_id, $bidirectional_ids, $related_pod, $related_field );

			if ( ! empty( $bidirectional_remove_ids ) ) {
				self::$api->delete_relationships( $bidirectional_remove_ids, $related_id, $pod, $options );
			}
		}//end foreach

		if ( ! $no_conflict ) {
			pods_no_conflict_off( $related_pod['type'] );
		}
	}

	/**
	 * Delete the value from the DB
	 *
	 * @param int|null    $id      Item ID.
	 * @param string|null $name    Field name.
	 * @param array|null  $options Field options.
	 * @param array|null  $pod     Pod options.
	 *
	 * @since 2.3.0
	 */
	public function delete( $id = null, $name = null, $options = null, $pod = null ) {

		if ( empty( self::$api ) ) {
			self::$api = pods_api();
		}

		$simple_tableless_objects = $this->simple_objects();

		// Bidirectional relationship requirement checks.
		$related_object = pods_v( static::$type . '_object', $options, '' );

		// pod, post_type, taxonomy, etc..
		$related_val = pods_v( static::$type . '_val', $options, $related_object, true );

		// pod name, post type name, taxonomy name, etc..
		$related_sister_id = pods_v( 'sister_id', $options, 0 );

		if ( is_numeric( $related_sister_id ) ) {
			$related_sister_id = (int) $related_sister_id;
		} else {
			$related_sister_id = 0;
		}

		if ( ! empty( $related_sister_id ) && ! in_array( $related_object, $simple_tableless_objects, true ) ) {
			$related_pod = self::$api->load_pod( [
				'name'       => $related_val,
				'auto_setup' => true,
			] );

			if ( false !== $related_pod && ( 'pod' === $related_object || $related_object === $related_pod['type'] ) ) {
				$related_field = false;

				// Ensure sister_id exists on related Pod.
				foreach ( $related_pod['fields'] as $related_pod_field ) {
					if ( 'pick' === $related_pod_field['type'] && (int) $related_sister_id === (int) $related_pod_field['id'] ) {
						$related_field = $related_pod_field;

						break;
					}
				}

				if ( ! empty( $related_field ) ) {
					$values = self::$api->lookup_related_items( $options['id'], $pod['id'], $id, $options, $pod );

					if ( ! empty( $values ) ) {
						$no_conflict = pods_no_conflict_check( $related_pod['type'] );

						if ( ! $no_conflict ) {
							pods_no_conflict_on( $related_pod['type'] );
						}

						self::$api->delete_relationships( $values, $id, $related_pod, $related_field );

						if ( ! $no_conflict ) {
							pods_no_conflict_off( $related_pod['type'] );
						}
					}
				}
			}//end if
		}//end if

	}

	/**
	 * {@inheritdoc}
	 */
	public function ui( $id, $value, $name = null, $options = null, $fields = null, $pod = null ) {

		$value = $this->simple_value( $name, $value, $options, $pod, $id );

		return $this->display( $value, $name, $options, $pod, $id );

	}

	/**
	 * Get the raw data from the field data provided.
	 *
	 * @since 2.9.9
	 *
	 * @param array|Field $field The field data.
	 *
	 * @return array|mixed
	 */
	public function get_raw_data( $field ) {
		$data = pods_v( 'data', $field, null, true );

		if ( null !== $data ) {
			// Support late-initializing the data from a callback function passed in.
			if ( ! is_array( $data ) && ! is_string( $data ) && is_callable( $data ) ) {
				$data = $data();
			}

			$data = (array) $data;
		}

		return $data;
	}

	/**
	 * {@inheritdoc}
	 */
	public function data( $name, $value = null, $options = null, $pod = null, $id = null, $in_form = true ) {
		$data = $this->get_raw_data( $options );

		$object_params = array(
			// The name of the field.
			'name'    => $name,
			// The value of the field.
			'value'   => $value,
			// Field options.
			'options' => $options,
			// Pod data.
			'pod'     => $pod,
			// Item ID.
			'id'      => $id,
			// Data context.
			'context' => 'data',
		);

		if ( null !== $data ) {
			$data = (array) $data;
		} else {
			$data = (array) $this->get_object_data( $object_params );
		}

		/**
		 * Allow filtering whether to always show default select text for the Single select Dropdown field even if
		 * the field is required.
		 *
		 * @since 2.8.9
		 *
		 * @param bool            $always_show_default_select_text Whether to always show default select text.
		 * @param array           $data                            The object data.
		 * @param string|null     $name                            Field name.
		 * @param mixed|null      $value                           Current value.
		 * @param array|null      $options                         Field options.
		 * @param array|null      $pod                             Pod information.
		 * @param int|string|null $id                              Current item ID.
		 */
		$always_show_default_select_text = (bool) apply_filters( 'pods_field_pick_always_show_default_select_text', false, $data, $name, $value, $options, $pod, $id );

		if (
			'single' === pods_v( static::$type . '_format_type', $options, 'single' )
			&& 'dropdown' === pods_v( static::$type . '_format_single', $options, 'dropdown' )
			//&& 0 !== (int) pods_v( static::$type . '_show_select_text', $options, 1 )
			&& (
				$always_show_default_select_text
				|| empty( $value )
				|| 0 === (int) pods_v( 'required', $options, 0 )
			)
		) {
			$default_select = [
				'' => pods_v( static::$type . '_select_text', $options, __( '-- Select One --', 'pods' ), true ),
			];

			// Unset to prevent conflict.
			if ( isset( $data[''] ) ) {
				unset( $data[''] );
			}

			// Prevent resetting the numeric ID keys when adding the empty option via array_merge, use union instead.
			$data = $default_select + $data;
		}

		$data = apply_filters( 'pods_field_pick_data', $data, $name, $value, $options, $pod, $id );

		return $data;

	}

	/**
	 * Convert a simple value to the correct value
	 *
	 * @param string            $name    The name of the field.
	 * @param string|array|null $value   The value of the field.
	 * @param array|null        $options Field options.
	 * @param array|null        $pod     Pod data.
	 * @param int|null          $id      Item ID.
	 * @param boolean           $raw     Whether to return the raw list of keys (true) or convert to key=>value (false).
	 *
	 * @return mixed Corrected value
	 */
	public function simple_value( $name, $value = null, $options = null, $pod = null, $id = null, $raw = false ) {

		if ( in_array( pods_v( static::$type . '_object', $options ), $this->simple_objects(), true ) ) {
			if ( ! is_array( $value ) && 0 < strlen( $value ) ) {
				$simple = @json_decode( $value, true );

				if ( is_array( $simple ) ) {
					$value = (array) $simple;
				}
			}

			$data = $this->get_raw_data( $options );

			$object_params = array(
				// The name of the field.
				'name'    => $name,
				// The value of the field.
				'value'   => $value,
				// Field options.
				'options' => $options,
				// Pod data.
				'pod'     => $pod,
				// Item ID.
				'id'      => $id,
				// Data context.
				'context' => 'simple_value',
			);

			if ( null !== $data ) {
				$data = (array) $data;
			} else {
				$data = (array) $this->get_object_data( $object_params );
			}

			$key = 0;

			if ( is_array( $value ) ) {
				if ( ! empty( $data ) ) {
					$val = array();

					foreach ( $value as $k => $v ) {
						if ( is_scalar( $v ) && isset( $data[ $v ] ) ) {
							if ( false === $raw ) {
								$k = $v;
								$v = $data[ $v ];
							}

							$val[ $k ] = $v;
						}
					}

					$value = $val;
				}
			} elseif ( isset( $data[ $value ] ) && false === $raw ) {
				$key   = $value;
				$value = $data[ $value ];
			}//end if

			$single_multi = pods_v( static::$type . '_format_type', $options, 'single' );

			if ( 'multi' === $single_multi ) {
				$limit = (int) pods_v( static::$type . '_limit', $options, 0 );
			} else {
				$limit = 1;
			}

			if ( is_array( $value ) && 0 < $limit ) {
				if ( 1 === $limit ) {
					$value = current( $value );
				} else {
					$value = array_slice( $value, 0, $limit, true );
				}
			} elseif ( ! is_array( $value ) && null !== $value && 0 < strlen( $value ) ) {
				if ( 1 !== $limit || ( true === $raw && 'multi' === $single_multi ) ) {
					$value = array(
						$key => $value,
					);
				}
			}
		}//end if

		return $value;

	}

	/**
	 * Get the label from a pick value.
	 *
	 * @param string            $name    The name of the field.
	 * @param string|array|null $value   The value of the field.
	 * @param array|null        $options Field options.
	 * @param array|null        $pod     Pod data.
	 * @param int|null          $id      Item ID.
	 *
	 * @return string
	 *
	 * @since 2.2.0
	 */
	public function value_to_label( $name, $value = null, $options = null, $pod = null, $id = null ) {
		$data = $this->get_raw_data( $options );

		$object_params = array(
			// The name of the field.
			'name'    => $name,
			// The value of the field.
			'value'   => $value,
			// Field options.
			'options' => $options,
			// Pod data.
			'pod'     => $pod,
			// Item ID.
			'id'      => $id,
			// Data context.
			'context' => 'value_to_label',
		);

		if ( null !== $data ) {
			$data = (array) $data;
		} else {
			$data = (array) $this->get_object_data( $object_params );
		}

		$labels = array();

		foreach ( $data as $v => $l ) {
			if ( ! in_array( (string) $l, $labels, true ) && ( (string) $value === (string) $v || ( is_array( $value ) && in_array( (string) $v, $value, true ) ) ) ) {
				$labels[] = (string) $l;
			}
		}

		$labels = apply_filters( 'pods_field_pick_value_to_label', $labels, $name, $value, $options, $pod, $id );

		$labels = pods_serial_comma( $labels );

		return $labels;

	}

	/**
	 * Get available items from a relationship field.
	 *
	 * @param array|string $field         Field array or field name.
	 * @param array        $deprecated       Field options array overrides.
	 * @param array        $object_params Additional get_object_data options.
	 *
	 * @return array An array of available items from a relationship field
	 */
	public function get_field_data( $field, $deprecated = null, $object_params = array() ) {
		$options = array();

		$is_field_object = $field instanceof Field;

		if ( is_array( $field ) || $is_field_object ) {
			$options = $field;
		}

		// Get field name from array.
		$field = pods_v( 'name', $options, $field, true );

		// Field name or options not set.
		if ( empty( $field ) || empty( $options ) ) {
			return array();
		}

		// Setup object params.
		$object_params = array_merge(
			array(
				// The name of the field.
				'name'    => $field,
				// Field options.
				'options' => $options,
			), $object_params
		);

		// Get data override.
		$data = $this->get_raw_data( $options );

		if ( null !== $data ) {
			// Return data override.
			$data = (array) $data;
		} else {
			// Get object data.
			$data = $this->get_object_data( $object_params );
		}

		return $data;

	}

	/**
	 * Get data from relationship objects.
	 *
	 * @param array $object_params Object data parameters.
	 *
	 * @return array|bool Object data
	 */
	public function get_object_data( $object_params = null ) {

		/**
		 * @var $wpdb wpdb
		 */
		global $wpdb;

		$object_params = array_merge(
			array(
				// The name of the field.
				'name'        => '',
				// The value of the field.
				'value'       => '',
				// Field options.
				'options'     => array(),
				// Pod data.
				'pod'         => '',
				// Item ID.
				'id'          => '',
				// Data context.
				'context'     => '',
				// Data parameters.
				'data_params' => array(
					// Query being searched.
					'query' => '',
				),
				// Page number of results to get.
				'page'        => 1,
				// How many data items to limit to (autocomplete defaults to 30, set to -1 or 1+ to override).
				'limit'       => 0,
			), $object_params
		);

		/**
		 * Overwrite parameters used by PodsField_Pick::get_object_data.
		 *
		 * @since 2.7.21
		 *
		 * @param array  $object_params       {
		 *     Get object parameters
		 *
		 *     @type string     $name        Field name.
		 *     @type mixed      $value       Current value.
		 *     @type array      $options     Field options.
		 *     @type array      $pod         Pod data.
		 *     @type int|string $id          Current item ID.
		 *     @type string     $context     Data context.
		 *     @type array      $data_params Data parameters.
		 *     @type int        $page        Page number of results to get.
		 *     @type int        $limit       How many data items to limit to (autocomplete defaults to 30, set to -1 or 1+ to override).
		 * }
		 */
		$object_params = apply_filters( 'pods_field_pick_object_data_params', $object_params );

		$object_params['data_params'] = (array) $object_params['data_params'];

		$name         = $object_params['name'];
		$value        = $object_params['value'];
		$options      = $object_params['options'];
		$pod          = $object_params['pod'];
		$id           = $object_params['id'];
		$context      = $object_params['context'];
		$data_params  = $object_params['data_params'];
		$page         = min( 1, (int) $object_params['page'] );
		$limit        = (int) $object_params['limit'];
		$autocomplete = false;

		// Use field object if it was provided.
		if ( isset( $options['_field_object'] ) ) {
			$options = $options['_field_object'];

			$name = $options->get_name();
		}

		$data  = apply_filters( 'pods_field_pick_object_data', null, $name, $value, $options, $pod, $id, $object_params );
		$items = array();

		if ( ! isset( $options[ static::$type . '_object' ] ) ) {
			$data = $this->get_raw_data( $options );
		}

		$simple = false;

		if ( null === $data ) {
			$data = array();

			$pick_object = pods_v( static::$type . '_object', $options, null, true );

			// No pick object means this has no configuration to work from.
			if ( empty( $pick_object ) ) {
				return $data;
			}

			if ( 'custom-simple' === $pick_object ) {
				$custom = pods_v( static::$type . '_custom', $options, '' );

				$custom = apply_filters( 'pods_form_ui_field_pick_custom_values', $custom, $name, $value, $options, $pod, $id, $object_params );

				if ( ! empty( $custom ) ) {
					if ( ! is_array( $custom ) ) {
						$data = array();

						$custom = explode( "\n", trim( $custom ) );

						foreach ( $custom as $custom_value ) {
							$custom_value = trim( trim( $custom_value, '|' ) );
							$custom_label = explode( '|', $custom_value );

							if ( empty( $custom_label ) ) {
								continue;
							}

							if ( 1 === count( $custom_label ) ) {
								$custom_label = $custom_value;
							} else {
								$custom_value = $custom_label[0];
								$custom_label = $custom_label[1];
							}

							$custom_value = trim( (string) $custom_value );
							$custom_label = trim( (string) $custom_label );

							$data[ $custom_value ] = $custom_label;
						}
					} else {
						$data = $custom;
					}//end if

					$simple = true;
				}//end if
			} elseif (
				$pick_object
				&& $this->setup_related_objects()
				&& isset( self::$related_objects[ $pick_object ] )
				&& ! empty( self::$related_objects[$pick_object ]['data'] )
			) {
				$data = self::$related_objects[ $options[ static::$type . '_object' ] ]['data'];

				$simple = true;
			} elseif (
				$pick_object
				&& $this->setup_related_objects()
				&& isset( self::$related_objects[ $pick_object ] )
				&& isset( self::$related_objects[ $pick_object ]['data_callback'] )
				&& is_callable( self::$related_objects[ $pick_object ]['data_callback'] )
			) {
				$data = call_user_func_array(
					self::$related_objects[ $options[ static::$type . '_object' ] ]['data_callback'], [
						$name,
						$value,
						$options,
						$pod,
						$id,
					]
				);

				if ( 'data' === $context ) {
					pods_static_cache_set( $name . '/' . $options['id'], [
						'autocomplete' => false,
					], __CLASS__ . '/field_data' );
				}

				$simple = true;

				// Cache data from callback.
				if ( ! empty( $data ) ) {
					self::$related_objects[ $options[ static::$type . '_object' ] ]['data'] = $data;
				}
			} elseif ( 'simple_value' !== $context ) {
				$pick_val = pods_v( static::$type . '_val', $options );

				if ( 'table' === $pick_object ) {
					$pick_val = pods_v( static::$type . '_table', $options, $pick_val, true );
				}

				$current_pod = null;

				if ( $pod instanceof Pod ) {
					$current_pod = $pod;
				} elseif ( $pod instanceof Pods ) {
					$current_pod = $pod->pod_data;
				} elseif ( is_array( $pod ) ) {
					$current_pod = $pod;
				}

				$related_pod = null;

				if ( '__current__' === $pick_val ) {
					if ( $pod instanceof Pod ) {
						$pick_val = $pod->get_name();

						$related_pod = $pod;
					} elseif ( $pod instanceof Pods ) {
						$pick_val = $pod->pod_data->get_name();

						$related_pod = $pod->pod_data;
					} elseif ( is_array( $pod ) ) {
						$pick_val = $pod['name'];
					} elseif ( 0 < strlen( $pod ) ) {
						$pick_val = $pod;
					}
				}

				$table_info = pods_v( 'table_info', $options );

				if ( empty( $table_info ) && ! empty( $pick_object ) ) {
					$table_info = pods_api()->get_table_info( $pick_object, $pick_val, null, null, $options );
				}

				if ( null === $related_pod && $table_info && $table_info['pod'] ) {
					$related_pod = $table_info['pod'];
				}

				$search_data = pods_data( $related_pod );
				$search_data->table( $table_info );

				$default_field_index = $search_data->field_index;

				$params = array(
					'select'     => "`t`.`{$search_data->field_id}`, `t`.`{$search_data->field_index}`",
					'table'      => $search_data->table,
					'where'      => pods_v( static::$type . '_where', $options, (array) $table_info['where_default'], true ),
					'orderby'    => pods_v( static::$type . '_orderby', $options, null, true ),
					'having'     => pods_v( static::$type . '_having', $options, null, true ),
					'groupby'    => pods_v( static::$type . '_groupby', $options, null, true ),
					'pagination' => false,
					'search'     => false,
				);

				if ( in_array( $options[ static::$type . '_object' ], array( 'site', 'network' ), true ) ) {
					$params['select'] .= ', `t`.`path`';
				}

				if ( ! empty( $params['where'] ) && (array) $table_info['where_default'] !== $params['where'] ) {
					$params['where'] = pods_evaluate_tags( $params['where'], true );
				}

				if ( ! empty( $params['having'] ) ) {
					$params['having'] = pods_evaluate_tags( $params['having'], true );
				}

				if ( empty( $params['where'] ) || ( ! is_array( $params['where'] ) && '' === trim( $params['where'] ) ) ) {
					$params['where'] = array();
				}

				$params['where'] = (array) $params['where'];

				if ( 'value_to_label' === $context ) {
					$params['where'][] = "`t`.`{$search_data->field_id}` = " . number_format( $value, 0, '', '' );
				}

				// Check if we need to limit by the current post author.
				if (
					1 === (int) pods_v( static::$type . '_post_author', $options, 0 )
					&& $current_pod
					&& 'post_type' === $current_pod['type']
					&& 'post_type' === $options[ static::$type . '_object' ]
				) {
					$post_author_id = 0;

					if ( empty( $id ) ) {
						if ( is_user_logged_in() ) {
							$post_author_id = get_current_user_id();
						}
					} else {
						$post = get_post( $id );

						if ( $post ) {
							$post_author_id = $post->post_author;
						}
					}

					$params['where'][] = '`t`.`post_author` = ' . (int) $post_author_id;
				}

				$display = trim( pods_v( static::$type . '_display', $options ), ' {@}' );

				$display_field       = "`t`.`{$search_data->field_index}`";
				$display_field_name  = $search_data->field_index;
				$display_field_alias = false;

				if ( 0 < strlen( $display ) ) {
					if ( ! empty( $table_info['pod'] ) ) {
						/** @var Pod $related_pod */
						$related_pod = $table_info['pod'];

						$related_storage     = $related_pod->get_storage();
						$related_type        = $related_pod->get_type();
						$found_display_field = $related_pod->get_field( $display );

						if ( $found_display_field ) {
							$display_field_name = $found_display_field->get_name();
						}

						if ( $found_display_field instanceof Object_Field ) {
							$display_field = "`t`.`{$display_field_name}`";
						} elseif (
							'table' === $related_storage
							&& ! in_array(
								$related_type, array(
									'pod',
									'table',
								), true
							)
						) {
							$display_field = "`d`.`{$display_field_name}`";
						} elseif ( 'meta' === $related_storage ) {
							$display_field = "`{$display_field_name}`.`meta_value`";

							$display_field_alias = true;
						} else {
							$display_field = "`t`.`{$display_field_name}`";
						}
					} elseif ( isset( $table_info['object_fields'] ) && isset( $table_info['object_fields'][ $display ] ) ) {
						$display_field_name = $table_info['object_fields'][ $display ];

						$display_field = "`t`.`{$display_field_name}`";
					}//end if
				}//end if

				if ( false === strpos( $params['select'], $display_field ) ) {
					$params['select'] .= ', ' . $display_field . ( $display_field_alias ? " AS `{$display_field_name}`" : '' );
				}

				$autocomplete = $this->is_autocomplete( $options );

				$hierarchy = false;

				if ( 'data' === $context && ! $autocomplete ) {
					if ( 'single' === pods_v( static::$type . '_format_type', $options, 'single' ) && in_array(
						pods_v( static::$type . '_format_single', $options, 'dropdown' ), array(
							'dropdown',
							'radio',
						), true
					)
					) {
						$hierarchy = true;
					} elseif ( 'multi' === pods_v( static::$type . '_format_type', $options, 'single' ) && in_array(
						pods_v( static::$type . '_format_multi', $options, 'checkbox' ), array(
							'multiselect',
							'checkbox',
						), true
					)
					) {
						$hierarchy = true;
					}
				}

				if ( $hierarchy && $table_info['object_hierarchical'] && ! empty( $table_info['field_parent'] ) ) {
					if ( false === strpos( $params['select'], $table_info['field_parent_select'] ) ) {
						$params['select'] .= ', ' . $table_info['field_parent_select'];
					}
				}

				if ( $autocomplete ) {
					if ( 0 === $limit ) {
						$limit = 30;
					}

					$params['limit'] = apply_filters( 'pods_form_ui_field_pick_autocomplete_limit', $limit, $name, $value, $options, $pod, $id, $object_params );

					if ( is_array( $value ) && $params['limit'] < count( $value ) ) {
						$params['limit'] = count( $value );
					}

					$params['page'] = $page;

					if ( 'admin_ajax_relationship' === $context ) {
						$lookup_where = array(
							$search_data->field_index => "`t`.`{$search_data->field_index}` LIKE '%" . pods_sanitize_like( $data_params['query'] ) . "%'",
						);

						if ( $display_field_name !== $search_data->field_index ) {
							$lookup_where[ $display_field_name ] = "{$display_field} LIKE '%" . pods_sanitize_like( $data_params['query'] ) . "%'";
						}

						// @todo Hook into WPML for each table
						if ( $wpdb->users === $search_data->table ) {
							$lookup_where['display_name'] = "`t`.`display_name` LIKE '%" . pods_sanitize_like( $data_params['query'] ) . "%'";
							$lookup_where['user_login']   = "`t`.`user_login` LIKE '%" . pods_sanitize_like( $data_params['query'] ) . "%'";
							$lookup_where['user_email']   = "`t`.`user_email` LIKE '%" . pods_sanitize_like( $data_params['query'] ) . "%'";
						} elseif ( $wpdb->posts === $search_data->table ) {
							$lookup_where['post_title']   = "`t`.`post_title` LIKE '%" . pods_sanitize_like( $data_params['query'] ) . "%'";
							$lookup_where['post_name']    = "`t`.`post_name` LIKE '%" . pods_sanitize_like( $data_params['query'] ) . "%'";
							$lookup_where['post_content'] = "`t`.`post_content` LIKE '%" . pods_sanitize_like( $data_params['query'] ) . "%'";
							$lookup_where['post_excerpt'] = "`t`.`post_excerpt` LIKE '%" . pods_sanitize_like( $data_params['query'] ) . "%'";
						} elseif ( $wpdb->terms === $search_data->table ) {
							$lookup_where['name'] = "`t`.`name` LIKE '%" . pods_sanitize_like( $data_params['query'] ) . "%'";
							$lookup_where['slug'] = "`t`.`slug` LIKE '%" . pods_sanitize_like( $data_params['query'] ) . "%'";
						} elseif ( $wpdb->comments === $search_data->table ) {
							$lookup_where['comment_content']      = "`t`.`comment_content` LIKE '%" . pods_sanitize_like( $data_params['query'] ) . "%'";
							$lookup_where['comment_author']       = "`t`.`comment_author` LIKE '%" . pods_sanitize_like( $data_params['query'] ) . "%'";
							$lookup_where['comment_author_email'] = "`t`.`comment_author_email` LIKE '%" . pods_sanitize_like( $data_params['query'] ) . "%'";
						}

						$lookup_where = apply_filters( 'pods_form_ui_field_pick_autocomplete_lookup', $lookup_where, $data_params['query'], $name, $value, $options, $pod, $id, $object_params, $search_data );

						if ( ! empty( $lookup_where ) ) {
							$params['where'][] = implode( ' OR ', $lookup_where );
						}

						$orderby   = array();
						$orderby[] = "( {$display_field} LIKE '%" . pods_sanitize_like( $data_params['query'] ) . "%' ) DESC";

						$pick_orderby = pods_v( static::$type . '_orderby', $options, null, true );

						if ( 0 < strlen( $pick_orderby ) ) {
							$orderby[] = $pick_orderby;
						}

						if ( ! in_array( $search_data->field_index, $orderby, true ) ) {
							$orderby[] = "`t`.`{$search_data->field_index}`";
						}

						if ( ! in_array( $search_data->field_id, $orderby, true ) ) {
							$orderby[] = "`t`.`{$search_data->field_id}`";
						}

						$params['orderby'] = $orderby;
					}//end if
				} elseif ( 0 < $limit ) {
					$params['limit'] = $limit;
					$params['page']  = $page;
				}//end if

				$extra = '';

				if ( $wpdb->posts === $search_data->table ) {
					$extra = '`t`.`post_type`, `t`.`menu_order`, `t`.`post_date`';
				} elseif ( $wpdb->terms === $search_data->table ) {
					$extra = '`tt`.`taxonomy`';
				} elseif ( $wpdb->comments === $search_data->table ) {
					$extra = '`t`.`comment_type`';
				} elseif ( $wpdb->site === $search_data->table ) {
					$extra = '`t`.`path`';
				} elseif ( $wpdb->blogs === $search_data->table ) {
					$extra = '`t`.`path`';
				}

				if ( '' !== $extra && false === strpos( $params['select'], $extra ) ) {
					$params['select'] .= ', ' . $extra;
				}

				if ( 'user' === pods_v( static::$type . '_object', $options ) ) {
					$roles = pods_v( static::$type . '_user_role', $options );

					if ( ! empty( $roles ) ) {
						$where = array();

						foreach ( (array) $roles as $role ) {
							if ( empty( $role ) || ( pods_clean_name( $role ) !== $role && sanitize_title( $role ) !== $role ) ) {
								continue;
							}

							$prefix = $wpdb->base_prefix;

							if ( is_multisite() && ! is_main_site() ) {
								$prefix .= get_current_blog_id() . '_';
							}

							$where[] = $prefix . 'capabilities.meta_value LIKE "%\"' . pods_sanitize_like( $role ) . '\"%"';
						}

						if ( ! empty( $where ) ) {
							$params['where'][] = implode( ' OR ', $where );
						}
					}//end if
				}//end if

				if ( empty( $params['where'] ) ) {
					$params['where'] = null;
				}

				$results = $search_data->select( $params );

				if ( $autocomplete && 0 < $params['limit'] && $params['limit'] < $search_data->total_found() ) {
					if ( ! empty( $value ) ) {
						$ids = $value;

						if ( is_array( $ids ) ) {
							if ( isset( $ids[0] ) && is_array( $ids[0] ) ) {
								$ids = wp_list_pluck( $ids, $search_data->field_id );
							}

							if ( $params['limit'] < count( $ids ) ) {
								$params['limit'] = count( $ids );
							}

							$ids = array_map( 'absint', $ids );
							$ids = implode( ', ', $ids );
						} else {
							$ids = (int) $ids;
						}

						if ( is_array( $params['where'] ) ) {
							$params['where'] = implode( ' AND ', $params['where'] );
						}
						if ( ! empty( $params['where'] ) ) {
							$params['where'] = '(' . $params['where'] . ') AND ';
						}

						$params['where'] .= "`t`.`{$search_data->field_id}` IN ( {$ids} )";

						$results = $search_data->select( $params );
					}//end if
				} else {
					$autocomplete = false;
				}//end if

				if ( 'data' === $context ) {
					pods_static_cache_set( $name . '/' . $options['id'], [
						'autocomplete' => $autocomplete,
					], __CLASS__ . '/field_data' );
				}

				if ( $hierarchy && ! $autocomplete && ! empty( $results ) && $table_info['object_hierarchical'] && ! empty( $table_info['field_parent'] ) ) {
					$select_args = array(
						'id'     => $table_info['field_id'],
						'index'  => $display_field_name,
						'parent' => $table_info['field_parent'],
					);

					$results = pods_hierarchical_select( $results, $select_args );
				}

				$ids = array();

				if ( ! empty( $results ) ) {
					foreach ( $results as $result ) {
						$result      = get_object_vars( $result );
						$field_id    = $search_data->field_id;
						$field_index = $display_field_name;

						if ( ! isset( $result[ $field_index ] ) ) {
							$field_index = $default_field_index;
						}

						if ( ! isset( $result[ $field_id ], $result[ $field_index ] ) ) {
							continue;
						}

						$result[ $field_index ] = trim( $result[ $field_index ] );

						$object      = '';
						$object_type = '';

						if ( $wpdb->posts === $search_data->table && isset( $result['post_type'] ) ) {
							$object      = $result['post_type'];
							$object_type = 'post_type';
						} elseif ( $wpdb->terms === $search_data->table && isset( $result['taxonomy'] ) ) {
							$object      = $result['taxonomy'];
							$object_type = 'taxonomy';
						}

						$field_index_data_to_use = pods_v( $field_index, $search_data->object_fields );

						$display_filter = pods_v( 'display_filter', $field_index_data_to_use );

						if ( 0 < strlen( $display_filter ) ) {
							$display_filter_args = pods_v( 'display_filter_args', $field_index_data_to_use );

							$filter_args = array(
								$display_filter,
								$result[ $field_index ],
							);

							if ( ! empty( $display_filter_args ) ) {
								foreach ( (array) $display_filter_args as $display_filter_arg ) {
									// Manual solution to a problem that won't map correctly.
									if ( 'post_ID' === $display_filter_arg ) {
										$display_filter_arg = 'ID';
									}

									if ( isset( $result[ $display_filter_arg ] ) ) {
										$filter_args[] = $result[ $display_filter_arg ];
									}
								}
							}

							$result[ $field_index ] = call_user_func_array( 'apply_filters', $filter_args );
						}

						if ( in_array( $options[ static::$type . '_object' ], array( 'site', 'network' ), true ) ) {
							$result[ $field_index ] = $result[ $field_index ] . $result['path'];
						} elseif ( '' === $result[ $field_index ] ) {
							$result[ $field_index ] = '(No Title)';
						}

						if ( 'admin_ajax_relationship' === $context ) {
							$items[] = $this->build_dfv_field_item_data_recurse_item( $result[ $field_id ], $result[ $field_index ], (object) $object_params );
						} else {
							$data[ $result[ $field_id ] ] = $result[ $field_index ];
						}

						$ids[] = $result[ $field_id ];
					}//end foreach
				}//end if
			}//end if

			if ( $simple && 'admin_ajax_relationship' === $context ) {
				$found_data = array();

				foreach ( $data as $k => $v ) {
					if ( false !== stripos( $v, $data_params['query'] ) || false !== stripos( $k, $data_params['query'] ) ) {
						$found_data[ $k ] = $v;
					}
				}

				$data = $found_data;
			}
		}//end if

		if ( 'admin_ajax_relationship' === $context ) {
			if ( empty( $items ) && ! empty( $data ) ) {
				foreach ( $data as $k => $v ) {
					$items[] = array(
						'id'   => $k,
						'text' => $v,
					);
				}
			}

			$data = $items;
		}

		return $data;

	}

	/**
	 * Check if field is autocomplete enabled.
	 *
	 * @param array $options Field options.
	 *
	 * @return bool
	 *
	 * @since 2.7.0
	 */
	private function is_autocomplete( $options ) {

		$autocomplete = false;

		if ( 'single' === pods_v( static::$type . '_format_type', $options, 'single' ) ) {
			if ( in_array( pods_v( static::$type . '_format_single', $options, 'dropdown' ), array( 'autocomplete', 'list' ), true ) ) {
				$autocomplete = true;
			}
		} elseif ( 'multi' === pods_v( static::$type . '_format_type', $options, 'single' ) ) {
			if ( in_array( pods_v( static::$type . '_format_multi', $options, 'checkbox' ), array( 'autocomplete', 'list' ), true ) ) {
				$autocomplete = true;
			}
		}

		return $autocomplete;
	}

	/**
	 * Check if a field type is a tableless text field type.
	 *
	 * @since 2.7.4
	 *
	 * @param string $type    Field type.
	 * @param array  $options Field options.
	 * @return bool True if the field type is a tableless text field type, false otherwise.
	 */
	private function is_simple_tableless( $type, array $options ) {
		$field_object = pods_v( $type . '_object', $options );

		return in_array( $field_object, PodsForm::simple_tableless_objects(), true );
	}

	/**
	 * Check if a field supports AJAX mode
	 *
	 * @param string $type    Field type.
	 * @param array  $options Field options.
	 *
	 * @return bool
	 * @since 2.7.4
	 */
	private function can_ajax( $type, $options ) {
		return $this->is_autocomplete( $options ) && ! $this->is_simple_tableless( $type, $options );
	}


	/**
	 * Handle autocomplete AJAX.
	 *
	 * @since 2.3.0
	 */
	public function admin_ajax_relationship() {

		pods_session_start();

		// Sanitize input.
		// @codingStandardsIgnoreLine
		$params = pods_unslash( (array) $_POST );

		foreach ( $params as $key => $value ) {
			if ( 'action' === $key ) {
				continue;
			}

			unset( $params[ $key ] );

			$params[ str_replace( '_podsfix_', '', $key ) ] = $value;
		}

		$params = (object) $params;

		if ( ! isset( $params->_wpnonce, $params->pod_name, $params->field_name, $params->uri_hash, $params->id ) ) {
			pods_error( __( 'Unauthorized request', 'pods' ), PodsInit::$admin );
		}

		if ( ! isset( $params->query ) || '' === trim( $params->query ) ) {
			pods_error( __( 'Invalid field request', 'pods' ), PodsInit::$admin );
		}

		$_wpnonce   = $params->_wpnonce;
		$pod_name   = $params->pod_name;
		$field_name = $params->field_name;
		$uri_hash   = $params->uri_hash;
		$id         = (int) $params->id;

		$query = $params->query;
		$limit = pods_v( 'limit', $params, 15, true );
		$page  = pods_v( 'page', $params, 1, true );

		$uid = pods_session_id();

		if ( is_user_logged_in() ) {
			$uid = 'user_' . get_current_user_id();
		}

		$nonce_name = 'pods_relationship:' . json_encode( compact( 'pod_name', 'field_name', 'uid', 'uri_hash', 'id' ) );

		if ( false === wp_verify_nonce( $_wpnonce, $nonce_name ) ) {
			pods_error( __( 'Unauthorized request', 'pods' ), PodsInit::$admin );
		}

		if ( empty( self::$api ) ) {
			self::$api = pods_api();
		}

		$pod = self::$api->load_pod( [
			'name' => $pod_name,
		] );

		if ( ! $pod ) {
			pods_error( __( 'Invalid Pod configuration', 'pods' ), PodsInit::$admin );
		}

		$field = $pod->get_field( $field_name );

		if ( ! $field ) {
			pods_error( __( 'Invalid Field configuration', 'pods' ), PodsInit::$admin );
		}

		if ( ! $field->is_autocomplete_relationship() ) {
			pods_error( __( 'Invalid field', 'pods' ), PodsInit::$admin );
		}

		$object_params = array(
			// The name of the field.
			'name'        => $field['name'],
			// The value of the field.
			'value'       => null,
			// Field options.
			'options'     => $field,
			// Pod data.
			'pod'         => $pod,
			// Item ID.
			'id'          => $id,
			// Data context.
			'context'     => 'admin_ajax_relationship',
			'data_params' => $params,
			'page'        => $page,
			'limit'       => $limit,
		);

		$pick_data = apply_filters( 'pods_field_pick_data_ajax', null, $field['name'], null, $field, $pod, $id );

		if ( null !== $pick_data ) {
			$items = $pick_data;
		} else {
			$items = $this->get_object_data( $object_params );
		}

		if ( ! empty( $items ) && isset( $items[0] ) && ! is_array( $items[0] ) ) {
			$new_items = array();

			foreach ( $items as $id => $text ) {
				$new_items[] = array(
					'id'    => $id,
					'text'  => $text,
					'image' => '',
				);
			}

			$items = $new_items;
		}

		$items = apply_filters( 'pods_field_pick_data_ajax_items', $items, $field['name'], null, $field, $pod, $id );

		$items = array(
			'results' => $items,
		);

		wp_send_json( $items );

		die();
		// KBAI!
	}

	/**
	 * Data callback for Post Stati.
	 *
	 * @param string|null       $name    The name of the field.
	 * @param string|array|null $value   The value of the field.
	 * @param array|null        $options Field options.
	 * @param array|null        $pod     Pod data.
	 * @param int|null          $id      Item ID.
	 *
	 * @return array
	 *
	 * @since 2.3.0
	 */
	public function data_post_stati( $name = null, $value = null, $options = null, $pod = null, $id = null ) {

		$data = array();

		$post_stati = get_post_stati( array(), 'objects' );

		foreach ( $post_stati as $post_status ) {
			$data[ $post_status->name ] = $post_status->label;
		}

		return apply_filters( 'pods_form_ui_field_pick_data_post_stati', $data, $name, $value, $options, $pod, $id );

	}

	/**
	 * Data callback for User Roles.
	 *
	 * @param string|null       $name    The name of the field.
	 * @param string|array|null $value   The value of the field.
	 * @param array|null        $options Field options.
	 * @param array|null        $pod     Pod data.
	 * @param int|null          $id      Item ID.
	 *
	 * @return array
	 *
	 * @since 2.3.0
	 */
	public function data_roles( $name = null, $value = null, $options = null, $pod = null, $id = null ) {

		$data = array();

		global $wp_roles;

		foreach ( $wp_roles->role_objects as $key => $role ) {
			$data[ $key ] = $wp_roles->role_names[ $key ];
		}

		return apply_filters( 'pods_form_ui_field_pick_data_roles', $data, $name, $value, $options, $pod, $id );

	}

	/**
	 * Data callback for User Capabilities.
	 *
	 * @param string|null       $name    The name of the field.
	 * @param string|array|null $value   The value of the field.
	 * @param array|null        $options Field options.
	 * @param array|null        $pod     Pod data.
	 * @param int|null          $id      Item ID.
	 *
	 * @return array
	 *
	 * @since 2.3.0
	 */
	public function data_capabilities( $name = null, $value = null, $options = null, $pod = null, $id = null ) {

		$data = array();

		global $wp_roles;

		$default_caps = array(
			'activate_plugins',
			'add_users',
			'create_users',
			'delete_others_pages',
			'delete_others_posts',
			'delete_pages',
			'delete_plugins',
			'delete_posts',
			'delete_private_pages',
			'delete_private_posts',
			'delete_published_pages',
			'delete_published_posts',
			'delete_users',
			'edit_dashboard',
			'edit_files',
			'edit_others_pages',
			'edit_others_posts',
			'edit_pages',
			'edit_plugins',
			'edit_posts',
			'edit_private_pages',
			'edit_private_posts',
			'edit_published_pages',
			'edit_published_posts',
			'edit_theme_options',
			'edit_themes',
			'edit_users',
			'import',
			'install_plugins',
			'install_themes',
			'list_users',
			'manage_categories',
			'manage_links',
			'manage_options',
			'moderate_comments',
			'promote_users',
			'publish_pages',
			'publish_posts',
			'read',
			'read_private_pages',
			'read_private_posts',
			'remove_users',
			'switch_themes',
			'unfiltered_html',
			'unfiltered_upload',
			'update_core',
			'update_plugins',
			'update_themes',
			'upload_files',
		);

		$role_caps = array();

		foreach ( $wp_roles->role_objects as $key => $role ) {
			if ( is_array( $role->capabilities ) ) {
				foreach ( $role->capabilities as $cap => $grant ) {
					$role_caps[ $cap ] = $cap;
				}
			}
		}

		$role_caps = array_unique( $role_caps );

		$capabilities = array_merge( $default_caps, $role_caps );

		// To support Members filters.
		$capabilities = apply_filters( 'members_get_capabilities', $capabilities );

		$capabilities = apply_filters( 'pods_roles_get_capabilities', $capabilities );

		sort( $capabilities );

		$capabilities = array_unique( $capabilities );

		global $wp_roles;

		foreach ( $capabilities as $capability ) {
			$data[ $capability ] = $capability;
		}

		return apply_filters( 'pods_form_ui_field_pick_data_capabilities', $data, $name, $value, $options, $pod, $id );

	}

	/**
	 * Data callback for Image Sizes.
	 *
	 * @param string|null       $name    The name of the field.
	 * @param string|array|null $value   The value of the field.
	 * @param array|null        $options Field options.
	 * @param array|null        $pod     Pod data.
	 * @param int|null          $id      Item ID.
	 *
	 * @return array
	 *
	 * @since 2.3.0
	 */
	public function data_image_sizes( $name = null, $value = null, $options = null, $pod = null, $id = null ) {

		$data = array();

		$image_sizes = get_intermediate_image_sizes();

		foreach ( $image_sizes as $image_size ) {
			$data[ $image_size ] = ucwords( str_replace( '-', ' ', $image_size ) );
		}

		return apply_filters( 'pods_form_ui_field_pick_data_image_sizes', $data, $name, $value, $options, $pod, $id );

	}

	/**
	 * Data callback for Post Types
	 *
	 * @param string       $name    The name of the field
	 * @param string|array $value   The value of the field
	 * @param array        $options Field options
	 * @param array        $pod     Pod data
	 * @param int          $id      Item ID
	 *
	 * @return array
	 *
	 * @since 2.3.0
	 */
	public function data_post_types( $name = null, $value = null, $options = null, $pod = null, $id = null ) {

		$data = array();

		$post_types = get_post_types( array(), 'objects' );

		$ignore = [
			'revision',
			'nav_menu_item',
			'custom_css',
			'customize_changeset',
			'attachment',
			'oembed_cache',
			'user_request',
			'wp_block',
			'wp_template',
			'wp_template_part',
			'wp_global_styles',
			'wp_navigation',
		];

		foreach ( $post_types as $post_type ) {
			if ( in_array( $post_type->name, $ignore, true ) || 0 === strpos( $post_type->name, '_pods_' ) ) {
				continue;
			}

			$data[ $post_type->name ] = $post_type->label;
		}

		return apply_filters( 'pods_form_ui_field_pick_data_post_types', $data, $name, $value, $options, $pod, $id );
	}

	/**
	 * Data callback for Taxonomies
	 *
	 * @param string       $name    The name of the field
	 * @param string|array $value   The value of the field
	 * @param array        $options Field options
	 * @param array        $pod     Pod data
	 * @param int          $id      Item ID
	 *
	 * @return array
	 *
	 * @since 2.3.0
	 */
	public function data_taxonomies( $name = null, $value = null, $options = null, $pod = null, $id = null ) {

		$data = array();

		$taxonomies = get_taxonomies( array(), 'objects' );

		$ignore = array( 'nav_menu', 'post_format' );

		foreach ( $taxonomies as $taxonomy ) {
			if ( in_array( $taxonomy->name, $ignore, true ) ) {
				continue;
			}

			$data[ $taxonomy->name ] = $taxonomy->label;
		}

		return apply_filters( 'pods_form_ui_field_pick_data_taxonomies', $data, $name, $value, $options, $pod, $id );
	}

	/**
	 * Data callback for Countries.
	 *
	 * @param string|null       $name    The name of the field.
	 * @param string|array|null $value   The value of the field.
	 * @param array|null        $options Field options.
	 * @param array|null        $pod     Pod data.
	 * @param int|null          $id      Item ID.
	 *
	 * @return array
	 *
	 * @since 2.3.0
	 */
	public function data_countries( $name = null, $value = null, $options = null, $pod = null, $id = null ) {

		$data = array(
			'AF' => __( 'Afghanistan' ),
			'AL' => __( 'Albania' ),
			'DZ' => __( 'Algeria' ),
			'AS' => __( 'American Samoa' ),
			'AD' => __( 'Andorra' ),
			'AO' => __( 'Angola' ),
			'AI' => __( 'Anguilla' ),
			'AQ' => __( 'Antarctica' ),
			'AG' => __( 'Antigua and Barbuda' ),
			'AR' => __( 'Argentina' ),
			'AM' => __( 'Armenia' ),
			'AW' => __( 'Aruba' ),
			'AU' => __( 'Australia' ),
			'AT' => __( 'Austria' ),
			'AZ' => __( 'Azerbaijan' ),
			'BS' => __( 'Bahamas' ),
			'BH' => __( 'Bahrain' ),
			'BD' => __( 'Bangladesh' ),
			'BB' => __( 'Barbados' ),
			'BY' => __( 'Belarus' ),
			'BE' => __( 'Belgium' ),
			'BZ' => __( 'Belize' ),
			'BJ' => __( 'Benin' ),
			'BM' => __( 'Bermuda' ),
			'BT' => __( 'Bhutan' ),
			'BO' => __( 'Bolivia' ),
			'BA' => __( 'Bosnia and Herzegovina' ),
			'BW' => __( 'Botswana' ),
			'BV' => __( 'Bouvet Island' ),
			'BR' => __( 'Brazil' ),
			'BQ' => __( 'British Antarctic Territory' ),
			'IO' => __( 'British Indian Ocean Territory' ),
			'VG' => __( 'British Virgin Islands' ),
			'BN' => __( 'Brunei' ),
			'BG' => __( 'Bulgaria' ),
			'BF' => __( 'Burkina Faso' ),
			'BI' => __( 'Burundi' ),
			'KH' => __( 'Cambodia' ),
			'CM' => __( 'Cameroon' ),
			'CA' => __( 'Canada' ),
			'CT' => __( 'Canton and Enderbury Islands' ),
			'CV' => __( 'Cape Verde' ),
			'KY' => __( 'Cayman Islands' ),
			'CF' => __( 'Central African Republic' ),
			'TD' => __( 'Chad' ),
			'CL' => __( 'Chile' ),
			'CN' => __( 'China' ),
			'CX' => __( 'Christmas Island' ),
			'CC' => __( 'Cocos [Keeling] Islands' ),
			'CO' => __( 'Colombia' ),
			'KM' => __( 'Comoros' ),
			'CG' => __( 'Congo - Brazzaville' ),
			'CD' => __( 'Congo - Kinshasa' ),
			'CK' => __( 'Cook Islands' ),
			'CR' => __( 'Costa Rica' ),
			'HR' => __( 'Croatia' ),
			'CU' => __( 'Cuba' ),
			'CY' => __( 'Cyprus' ),
			'CZ' => __( 'Czech Republic' ),
			'CI' => __( 'Côte d’Ivoire' ),
			'DK' => __( 'Denmark' ),
			'DJ' => __( 'Djibouti' ),
			'DM' => __( 'Dominica' ),
			'DO' => __( 'Dominican Republic' ),
			'NQ' => __( 'Dronning Maud Land' ),
			'DD' => __( 'East Germany' ),
			'EC' => __( 'Ecuador' ),
			'EG' => __( 'Egypt' ),
			'SV' => __( 'El Salvador' ),
			'GQ' => __( 'Equatorial Guinea' ),
			'ER' => __( 'Eritrea' ),
			'EE' => __( 'Estonia' ),
			'ET' => __( 'Ethiopia' ),
			'FK' => __( 'Falkland Islands' ),
			'FO' => __( 'Faroe Islands' ),
			'FJ' => __( 'Fiji' ),
			'FI' => __( 'Finland' ),
			'FR' => __( 'France' ),
			'GF' => __( 'French Guiana' ),
			'PF' => __( 'French Polynesia' ),
			'TF' => __( 'French Southern Territories' ),
			'FQ' => __( 'French Southern and Antarctic Territories' ),
			'GA' => __( 'Gabon' ),
			'GM' => __( 'Gambia' ),
			'GE' => __( 'Georgia' ),
			'DE' => __( 'Germany' ),
			'GH' => __( 'Ghana' ),
			'GI' => __( 'Gibraltar' ),
			'GR' => __( 'Greece' ),
			'GL' => __( 'Greenland' ),
			'GD' => __( 'Grenada' ),
			'GP' => __( 'Guadeloupe' ),
			'GU' => __( 'Guam' ),
			'GT' => __( 'Guatemala' ),
			'GG' => __( 'Guernsey' ),
			'GN' => __( 'Guinea' ),
			'GW' => __( 'Guinea-Bissau' ),
			'GY' => __( 'Guyana' ),
			'HT' => __( 'Haiti' ),
			'HM' => __( 'Heard Island and McDonald Islands' ),
			'HN' => __( 'Honduras' ),
			'HK' => __( 'Hong Kong SAR China' ),
			'HU' => __( 'Hungary' ),
			'IS' => __( 'Iceland' ),
			'IN' => __( 'India' ),
			'ID' => __( 'Indonesia' ),
			'IR' => __( 'Iran' ),
			'IQ' => __( 'Iraq' ),
			'IE' => __( 'Ireland' ),
			'IM' => __( 'Isle of Man' ),
			'IL' => __( 'Israel' ),
			'IT' => __( 'Italy' ),
			'JM' => __( 'Jamaica' ),
			'JP' => __( 'Japan' ),
			'JE' => __( 'Jersey' ),
			'JT' => __( 'Johnston Island' ),
			'JO' => __( 'Jordan' ),
			'KZ' => __( 'Kazakhstan' ),
			'KE' => __( 'Kenya' ),
			'KI' => __( 'Kiribati' ),
			'KW' => __( 'Kuwait' ),
			'KG' => __( 'Kyrgyzstan' ),
			'LA' => __( 'Laos' ),
			'LV' => __( 'Latvia' ),
			'LB' => __( 'Lebanon' ),
			'LS' => __( 'Lesotho' ),
			'LR' => __( 'Liberia' ),
			'LY' => __( 'Libya' ),
			'LI' => __( 'Liechtenstein' ),
			'LT' => __( 'Lithuania' ),
			'LU' => __( 'Luxembourg' ),
			'MO' => __( 'Macau SAR China' ),
			'MK' => __( 'Macedonia' ),
			'MG' => __( 'Madagascar' ),
			'MW' => __( 'Malawi' ),
			'MY' => __( 'Malaysia' ),
			'MV' => __( 'Maldives' ),
			'ML' => __( 'Mali' ),
			'MT' => __( 'Malta' ),
			'MH' => __( 'Marshall Islands' ),
			'MQ' => __( 'Martinique' ),
			'MR' => __( 'Mauritania' ),
			'MU' => __( 'Mauritius' ),
			'YT' => __( 'Mayotte' ),
			'FX' => __( 'Metropolitan France' ),
			'MX' => __( 'Mexico' ),
			'FM' => __( 'Micronesia' ),
			'MI' => __( 'Midway Islands' ),
			'MD' => __( 'Moldova' ),
			'MC' => __( 'Monaco' ),
			'MN' => __( 'Mongolia' ),
			'ME' => __( 'Montenegro' ),
			'MS' => __( 'Montserrat' ),
			'MA' => __( 'Morocco' ),
			'MZ' => __( 'Mozambique' ),
			'MM' => __( 'Myanmar [Burma]' ),
			'NA' => __( 'Namibia' ),
			'NR' => __( 'Nauru' ),
			'NP' => __( 'Nepal' ),
			'NL' => __( 'Netherlands' ),
			'AN' => __( 'Netherlands Antilles' ),
			'NT' => __( 'Neutral Zone' ),
			'NC' => __( 'New Caledonia' ),
			'NZ' => __( 'New Zealand' ),
			'NI' => __( 'Nicaragua' ),
			'NE' => __( 'Niger' ),
			'NG' => __( 'Nigeria' ),
			'NU' => __( 'Niue' ),
			'NF' => __( 'Norfolk Island' ),
			'KP' => __( 'North Korea' ),
			'VD' => __( 'North Vietnam' ),
			'MP' => __( 'Northern Mariana Islands' ),
			'NO' => __( 'Norway' ),
			'OM' => __( 'Oman' ),
			'PC' => __( 'Pacific Islands Trust Territory' ),
			'PK' => __( 'Pakistan' ),
			'PW' => __( 'Palau' ),
			'PS' => __( 'Palestinian Territories' ),
			'PA' => __( 'Panama' ),
			'PZ' => __( 'Panama Canal Zone' ),
			'PG' => __( 'Papua New Guinea' ),
			'PY' => __( 'Paraguay' ),
			'YD' => __( "People's Democratic Republic of Yemen" ),
			'PE' => __( 'Peru' ),
			'PH' => __( 'Philippines' ),
			'PN' => __( 'Pitcairn Islands' ),
			'PL' => __( 'Poland' ),
			'PT' => __( 'Portugal' ),
			'PR' => __( 'Puerto Rico' ),
			'QA' => __( 'Qatar' ),
			'RO' => __( 'Romania' ),
			'RU' => __( 'Russia' ),
			'RW' => __( 'Rwanda' ),
			'RE' => __( 'Réunion' ),
			'BL' => __( 'Saint Barthélemy' ),
			'SH' => __( 'Saint Helena' ),
			'KN' => __( 'Saint Kitts and Nevis' ),
			'LC' => __( 'Saint Lucia' ),
			'MF' => __( 'Saint Martin' ),
			'PM' => __( 'Saint Pierre and Miquelon' ),
			'VC' => __( 'Saint Vincent and the Grenadines' ),
			'WS' => __( 'Samoa' ),
			'SM' => __( 'San Marino' ),
			'SA' => __( 'Saudi Arabia' ),
			'SN' => __( 'Senegal' ),
			'RS' => __( 'Serbia' ),
			'CS' => __( 'Serbia and Montenegro' ),
			'SC' => __( 'Seychelles' ),
			'SL' => __( 'Sierra Leone' ),
			'SG' => __( 'Singapore' ),
			'SK' => __( 'Slovakia' ),
			'SI' => __( 'Slovenia' ),
			'SB' => __( 'Solomon Islands' ),
			'SO' => __( 'Somalia' ),
			'ZA' => __( 'South Africa' ),
			'GS' => __( 'South Georgia and the South Sandwich Islands' ),
			'KR' => __( 'South Korea' ),
			'ES' => __( 'Spain' ),
			'LK' => __( 'Sri Lanka' ),
			'SD' => __( 'Sudan' ),
			'SR' => __( 'Suriname' ),
			'SJ' => __( 'Svalbard and Jan Mayen' ),
			'SZ' => __( 'Swaziland' ),
			'SE' => __( 'Sweden' ),
			'CH' => __( 'Switzerland' ),
			'SY' => __( 'Syria' ),
			'ST' => __( 'São Tomé and Príncipe' ),
			'TW' => __( 'Taiwan' ),
			'TJ' => __( 'Tajikistan' ),
			'TZ' => __( 'Tanzania' ),
			'TH' => __( 'Thailand' ),
			'TL' => __( 'Timor-Leste' ),
			'TG' => __( 'Togo' ),
			'TK' => __( 'Tokelau' ),
			'TO' => __( 'Tonga' ),
			'TT' => __( 'Trinidad and Tobago' ),
			'TN' => __( 'Tunisia' ),
			'TR' => __( 'Turkey' ),
			'TM' => __( 'Turkmenistan' ),
			'TC' => __( 'Turks and Caicos Islands' ),
			'TV' => __( 'Tuvalu' ),
			'UM' => __( 'U.S. Minor Outlying Islands' ),
			'PU' => __( 'U.S. Miscellaneous Pacific Islands' ),
			'VI' => __( 'U.S. Virgin Islands' ),
			'UG' => __( 'Uganda' ),
			'UA' => __( 'Ukraine' ),
			'SU' => __( 'Union of Soviet Socialist Republics' ),
			'AE' => __( 'United Arab Emirates' ),
			'GB' => __( 'United Kingdom' ),
			'US' => __( 'United States' ),
			'ZZ' => __( 'Unknown or Invalid Region' ),
			'UY' => __( 'Uruguay' ),
			'UZ' => __( 'Uzbekistan' ),
			'VU' => __( 'Vanuatu' ),
			'VA' => __( 'Vatican City' ),
			'VE' => __( 'Venezuela' ),
			'VN' => __( 'Vietnam' ),
			'WK' => __( 'Wake Island' ),
			'WF' => __( 'Wallis and Futuna' ),
			'EH' => __( 'Western Sahara' ),
			'YE' => __( 'Yemen' ),
			'ZM' => __( 'Zambia' ),
			'ZW' => __( 'Zimbabwe' ),
			'AX' => __( 'Åland Islands' ),
		);

		return apply_filters( 'pods_form_ui_field_pick_data_countries', $data, $name, $value, $options, $pod, $id );

	}

	/**
	 * Data callback for US States.
	 *
	 * @param string|null       $name    The name of the field.
	 * @param string|array|null $value   The value of the field.
	 * @param array|null        $options Field options.
	 * @param array|null        $pod     Pod data.
	 * @param int|null          $id      Item ID.
	 *
	 * @return array
	 *
	 * @since 2.3.0
	 */
	public function data_us_states( $name = null, $value = null, $options = null, $pod = null, $id = null ) {

		$data = array(
			'AL' => __( 'Alabama' ),
			'AK' => __( 'Alaska' ),
			'AZ' => __( 'Arizona' ),
			'AR' => __( 'Arkansas' ),
			'CA' => __( 'California' ),
			'CO' => __( 'Colorado' ),
			'CT' => __( 'Connecticut' ),
			'DE' => __( 'Delaware' ),
			'DC' => __( 'District Of Columbia' ),
			'FL' => __( 'Florida' ),
			'GA' => __( 'Georgia' ),
			'HI' => __( 'Hawaii' ),
			'ID' => __( 'Idaho' ),
			'IL' => __( 'Illinois' ),
			'IN' => __( 'Indiana' ),
			'IA' => __( 'Iowa' ),
			'KS' => __( 'Kansas' ),
			'KY' => __( 'Kentucky' ),
			'LA' => __( 'Louisiana' ),
			'ME' => __( 'Maine' ),
			'MD' => __( 'Maryland' ),
			'MA' => __( 'Massachusetts' ),
			'MI' => __( 'Michigan' ),
			'MN' => __( 'Minnesota' ),
			'MS' => __( 'Mississippi' ),
			'MO' => __( 'Missouri' ),
			'MT' => __( 'Montana' ),
			'NE' => __( 'Nebraska' ),
			'NV' => __( 'Nevada' ),
			'NH' => __( 'New Hampshire' ),
			'NJ' => __( 'New Jersey' ),
			'NM' => __( 'New Mexico' ),
			'NY' => __( 'New York' ),
			'NC' => __( 'North Carolina' ),
			'ND' => __( 'North Dakota' ),
			'OH' => __( 'Ohio' ),
			'OK' => __( 'Oklahoma' ),
			'OR' => __( 'Oregon' ),
			'PA' => __( 'Pennsylvania' ),
			'RI' => __( 'Rhode Island' ),
			'SC' => __( 'South Carolina' ),
			'SD' => __( 'South Dakota' ),
			'TN' => __( 'Tennessee' ),
			'TX' => __( 'Texas' ),
			'UT' => __( 'Utah' ),
			'VT' => __( 'Vermont' ),
			'VA' => __( 'Virginia' ),
			'WA' => __( 'Washington' ),
			'WV' => __( 'West Virginia' ),
			'WI' => __( 'Wisconsin' ),
			'WY' => __( 'Wyoming' ),
		);

		return apply_filters( 'pods_form_ui_field_pick_data_us_states', $data, $name, $value, $options, $pod, $id );

	}

	/**
	 * Data callback for CA Provinces.
	 *
	 * @param string|null       $name    The name of the field.
	 * @param string|array|null $value   The value of the field.
	 * @param array|null        $options Field options.
	 * @param array|null        $pod     Pod data.
	 * @param int|null          $id      Item ID.
	 *
	 * @return array
	 *
	 * @since 2.3.0
	 */
	public function data_ca_provinces( $name = null, $value = null, $options = null, $pod = null, $id = null ) {

		$data = array(
			'AB' => __( 'Alberta' ),
			'BC' => __( 'British Columbia' ),
			'MB' => __( 'Manitoba' ),
			'NB' => __( 'New Brunswick' ),
			'NL' => __( 'Newfoundland and Labrador' ),
			'NT' => __( 'Northwest Territories' ),
			'NS' => __( 'Nova Scotia' ),
			'NU' => __( 'Nunavut' ),
			'ON' => __( 'Ontario' ),
			'PE' => __( 'Prince Edward Island' ),
			'QC' => __( 'Quebec' ),
			'SK' => __( 'Saskatchewan' ),
			'YT' => __( 'Yukon' ),
		);

		return apply_filters( 'pods_form_ui_field_pick_data_ca_provinces', $data, $name, $value, $options, $pod, $id );

	}

	/**
	 * Data callback for Days of the Week.
	 *
	 * @param string|null       $name    The name of the field.
	 * @param string|array|null $value   The value of the field.
	 * @param array|null        $options Field options.
	 * @param array|null        $pod     Pod data.
	 * @param int|null          $id      Item ID.
	 *
	 * @return array
	 *
	 * @since 2.3.0
	 */
	public function data_days_of_week( $name = null, $value = null, $options = null, $pod = null, $id = null ) {

		/**
		 * @var WP_Locale
		 */
		global $wp_locale;

		return $wp_locale->weekday;

	}

	/**
	 * Data callback for Months of the Year.
	 *
	 * @param string|null       $name    The name of the field.
	 * @param string|array|null $value   The value of the field.
	 * @param array|null        $options Field options.
	 * @param array|null        $pod     Pod data.
	 * @param int|null          $id      Item ID.
	 *
	 * @return array
	 *
	 * @since 2.3.0
	 */
	public function data_months_of_year( $name = null, $value = null, $options = null, $pod = null, $id = null ) {

		/**
		 * @var WP_Locale
		 */
		global $wp_locale;

		return $wp_locale->month;

	}

	/**
	 * Add our modal input to the form so we can track whether we're in our modal during saving or not.
	 */
	public function admin_modal_input() {

		if ( ! pods_is_modal_window() ) {
			return;
		}

		echo '<input name="pods_modal" type="hidden" value="1" />';

	}

	/**
	 * Bail to send new saved data back to our modal handler.
	 *
	 * @param int    $item_id    Item ID.
	 * @param string $item_title Item title.
	 * @param object $field_args Field arguments.
	 */
	public function admin_modal_bail( $item_id, $item_title, $field_args ) {

		$model_data = $this->build_dfv_field_item_data_recurse_item( $item_id, $item_title, $field_args );
		?>
			<script type="text/javascript">
				window.parent.postMessage( {
					type: 'PODS_MESSAGE',
					data: <?php echo wp_json_encode( $model_data, JSON_HEX_TAG ); ?>,
				}, window.location.origin );
			</script>
		<?php

		die();

	}

	/**
	 * Bail to send new saved data back to our modal handler.
	 *
	 * @param int    $item_id    Item ID.
	 * @param string $item_title Item title.
	 * @param object $field_args Field arguments.
	 */
	public function admin_modal_bail_JSON( $item_id, $item_title, $field_args ) {

		$model_data = $this->build_dfv_field_item_data_recurse_item( $item_id, $item_title, $field_args );
		echo wp_json_encode( $model_data, JSON_HEX_TAG );

		die();
	}

	/**
	 * Bail on Post save redirect for Admin modal.
	 *
	 * @param string $location The destination URL.
	 * @param int    $post_id  The post ID.
	 *
	 * @return string
	 */
	public function admin_modal_bail_post_redirect( $location, $post_id ) {

		if ( ! pods_is_modal_window() ) {
			return $location;
		}

		$post_title = get_the_title( $post_id );

		$field_args = (object) array(
			'options' => array(
				'pick_object' => 'post_type',
				'pick_val'    => get_post_type( $post_id ),
			),
			'value'   => array(
				$post_id => $post_title,
			),
		);

		$this->admin_modal_bail( $post_id, $post_title, $field_args );

		return $location;

	}

	/**
	 * Hook into term updating process to bail on redirect.
	 */
	public function admin_modal_bail_term_action() {

		if ( ! pods_is_modal_window() ) {
			return;
		}

		if ( function_exists( 'get_current_screen' ) ) {
			$screen = get_current_screen();

			if ( 'edit-tags' === $screen->base ) {
				// @todo Need more effort on the solution for add new handling.
				//add_action( 'admin_footer', [ $this, 'admin_modal_bail_term_action_add_new' ], 20 );
			}
		}

		add_action( 'created_term', array( $this, 'admin_modal_bail_term' ), 10, 3 );
		add_action( 'edited_term', array( $this, 'admin_modal_bail_term' ), 10, 3 );

	}

	/**
	 * Hook into term creation process to bail after success.
	 *
	 * @todo Try and catch the added tr node on the table tbody.
	 */
	public function admin_modal_bail_term_action_add_new() {
		?>
			<script type="text/javascript">
				jQuery( function ( $ ) {
					/** @var {jQuery.Event} e */
					$( '.tags' ).on( 'DOMSubtreeModified', function(e) {
						console.log( e );

						if ( !== e.target.is( 'tbody#the-list' ) ) {
							return;
						}

						const $theTermRow = $( e.target.innerHTML() );
						const titleRow = $theTermRow.find( '.column-name .row-title' );
						const actionView = $theTermRow.find( '.row-actions span.view a' );

						const termData = {
							id       : $theTermRow.find( '.check-column input' ).val(),
							icon     : '',
							name     : titleRow.text(),
							edit_link: titleRow.prop( 'href' ),
							link     : actionView[0] ? actionView.prop( 'href' ) : '',
							selected : true,
						};

						console.log( termData );

						window.parent.postMessage( {
							type : 'PODS_MESSAGE',
							data : termData,
						}, window.location.origin );
					} );
				} );
			</script>
		<?php
	}

	/**
	 * Bail on Term save redirect for Admin modal.
	 *
	 * @param int    $term_id  Term ID.
	 * @param int    $tt_id    Term taxonomy ID.
	 * @param string $taxonomy Taxonomy slug.
	 */
	public function admin_modal_bail_term( $term_id, $tt_id, $taxonomy ) {

		if ( ! pods_is_modal_window() ) {
			return;
		}

		$term = get_term( $term_id );

		if ( ! $term || is_wp_error( $term ) ) {
			return;
		}

		$field_args = (object) array(
			'options' => array(
				'pick_object' => 'taxonomy',
				'pick_val'    => $term->taxonomy,
			),
			'value'   => array(
				$term->term_id => $term->name,
			),
		);

		$this->admin_modal_bail( $term->term_id, $term->name, $field_args );

	}

	/**
	 * Hook into user updating process to bail on redirect.
	 */
	public function admin_modal_bail_user_action() {

		if ( ! pods_is_modal_window() ) {
			return;
		}

		add_filter( 'wp_redirect', array( $this, 'admin_modal_bail_user_redirect' ) );

	}

	/**
	 * Bail on User save redirect for Admin modal.
	 *
	 * @param string $location The destination URL.
	 *
	 * @return string
	 */
	public function admin_modal_bail_user_redirect( $location ) {

		if ( ! pods_is_modal_window() ) {
			return $location;
		}

		global $user_id;

		$user = get_userdata( $user_id );

		if ( ! $user || is_wp_error( $user ) ) {
			return $location;
		}

		$field_args = (object) array(
			'options' => array(
				'pick_object' => 'user',
				'pick_val'    => '',
			),
			'value'   => array(
				$user->ID => $user->display_name,
			),
		);

		$this->admin_modal_bail( $user->ID, $user->display_name, $field_args );

		return $location;

	}

	/**
	 * Bail on Pod item save for Admin modal.
	 *
	 * @param int       $id     Item ID.
	 * @param array     $params save_pod_item parameters.
	 * @param null|Pods $obj    Pod object (if set).
	 */
	public function admin_modal_bail_pod( $id, $params, $obj ) {

		if ( ! pods_is_modal_window() ) {
			return;
		}

		if ( ! $obj ) {
			$obj = pods( $params['pod'] );
		}

		if ( ! $obj || ! $obj->fetch( $id ) ) {
			return;
		}

		$item_id    = $obj->id();
		$item_title = $obj->index();

		$field_args = (object) array(
			'options' => array(
				'pick_object' => $obj->pod_data['type'],
				'pick_val'    => $obj->pod,
			),
			'value'   => array(
				$obj->id() => $item_title,
			),
		);

		$this->admin_modal_bail_JSON( $item_id, $item_title, $field_args );

	}

	/**
	 * Build field data for Pods DFV.
	 *
	 * @param object $args            {
	 *     Field information arguments.
	 *
	 *     @type string     $name            Field name.
	 *     @type string     $type            Field type.
	 *     @type array      $options         Field options.
	 *     @type mixed      $value           Current value.
	 *     @type array      $pod             Pod information.
	 *     @type int|string $id              Current item ID.
	 *     @type string     $form_field_type HTML field type.
	 * }
	 *
	 * @return array
	 */
	public function build_dfv_field_data( $args ) {
		$data = parent::build_dfv_field_data( $args );

		// Normalize arrays for multiple select.
		if ( is_array( $data['fieldValue'] ) ) {
			$data['fieldValue'] = array_values( $data['fieldValue'] );
		}

		return $data;
	}

}