?
Current File : /home/c/i/d/cideo/www/wp-includesVIp/js/crop/images/fields.tar
class-wp-rest-post-meta-fields.php000066600000002334151234451570013151 0ustar00<?php
/**
 * REST API: WP_REST_Post_Meta_Fields class
 *
 * @package WordPress
 * @subpackage REST_API
 * @since 4.7.0
 */

/**
 * Core class used to manage meta values for posts via the REST API.
 *
 * @since 4.7.0
 *
 * @see WP_REST_Meta_Fields
 */
class WP_REST_Post_Meta_Fields extends WP_REST_Meta_Fields {

	/**
	 * Post type to register fields for.
	 *
	 * @since 4.7.0
	 * @var string
	 */
	protected $post_type;

	/**
	 * Constructor.
	 *
	 * @since 4.7.0
	 *
	 * @param string $post_type Post type to register fields for.
	 */
	public function __construct( $post_type ) {
		$this->post_type = $post_type;
	}

	/**
	 * Retrieves the post meta type.
	 *
	 * @since 4.7.0
	 *
	 * @return string The meta type.
	 */
	protected function get_meta_type() {
		return 'post';
	}

	/**
	 * Retrieves the post meta subtype.
	 *
	 * @since 4.9.8
	 *
	 * @return string Subtype for the meta type, or empty string if no specific subtype.
	 */
	protected function get_meta_subtype() {
		return $this->post_type;
	}

	/**
	 * Retrieves the type for register_rest_field().
	 *
	 * @since 4.7.0
	 *
	 * @see register_rest_field()
	 *
	 * @return string The REST field type.
	 */
	public function get_rest_field_type() {
		return $this->post_type;
	}
}
class-wp-rest-meta-fields.php000066600000031605151234451570012171 0ustar00<?php
/**
 * REST API: WP_REST_Meta_Fields class
 *
 * @package WordPress
 * @subpackage REST_API
 * @since 4.7.0
 */

/**
 * Core class to manage meta values for an object via the REST API.
 *
 * @since 4.7.0
 */
abstract class WP_REST_Meta_Fields {

	/**
	 * Retrieves the object meta type.
	 *
	 * @since 4.7.0
	 *
	 * @return string One of 'post', 'comment', 'term', 'user', or anything
	 *                else supported by `_get_meta_table()`.
	 */
	abstract protected function get_meta_type();

	/**
	 * Retrieves the object type for register_rest_field().
	 *
	 * @since 4.7.0
	 *
	 * @return string The REST field type, such as post type name, taxonomy name, 'comment', or `user`.
	 */
	abstract protected function get_rest_field_type();

	/**
	 * Registers the meta field.
	 *
	 * @since 4.7.0
	 *
	 * @see register_rest_field()
	 */
	public function register_field() {
		register_rest_field( $this->get_rest_field_type(), 'meta', array(
			'get_callback'    => array( $this, 'get_value' ),
			'update_callback' => array( $this, 'update_value' ),
			'schema'          => $this->get_field_schema(),
		));
	}

	/**
	 * Retrieves the meta field value.
	 *
	 * @since 4.7.0
	 *
	 * @param int             $object_id Object ID to fetch meta for.
	 * @param WP_REST_Request $request   Full details about the request.
	 * @return WP_Error|object Object containing the meta values by name, otherwise WP_Error object.
	 */
	public function get_value( $object_id, $request ) {
		$fields   = $this->get_registered_fields();
		$response = array();

		foreach ( $fields as $meta_key => $args ) {
			$name = $args['name'];
			$all_values = get_metadata( $this->get_meta_type(), $object_id, $meta_key, false );
			if ( $args['single'] ) {
				if ( empty( $all_values ) ) {
					$value = $args['schema']['default'];
				} else {
					$value = $all_values[0];
				}
				$value = $this->prepare_value_for_response( $value, $request, $args );
			} else {
				$value = array();
				foreach ( $all_values as $row ) {
					$value[] = $this->prepare_value_for_response( $row, $request, $args );
				}
			}

			$response[ $name ] = $value;
		}

		return $response;
	}

	/**
	 * Prepares a meta value for a response.
	 *
	 * This is required because some native types cannot be stored correctly
	 * in the database, such as booleans. We need to cast back to the relevant
	 * type before passing back to JSON.
	 *
	 * @since 4.7.0
	 *
	 * @param mixed           $value   Meta value to prepare.
	 * @param WP_REST_Request $request Current request object.
	 * @param array           $args    Options for the field.
	 * @return mixed Prepared value.
	 */
	protected function prepare_value_for_response( $value, $request, $args ) {
		if ( ! empty( $args['prepare_callback'] ) ) {
			$value = call_user_func( $args['prepare_callback'], $value, $request, $args );
		}

		return $value;
	}

	/**
	 * Updates meta values.
	 *
	 * @since 4.7.0
	 *
	 * @param array           $meta      Array of meta parsed from the request.
	 * @param int             $object_id Object ID to fetch meta for.
	 * @return WP_Error|null WP_Error if one occurs, null on success.
	 */
	public function update_value( $meta, $object_id ) {
		$fields = $this->get_registered_fields();
		foreach ( $fields as $meta_key => $args ) {
			$name = $args['name'];
			if ( ! array_key_exists( $name, $meta ) ) {
				continue;
			}

			/*
			 * A null value means reset the field, which is essentially deleting it
			 * from the database and then relying on the default value.
			 */
			if ( is_null( $meta[ $name ] ) ) {
				$result = $this->delete_meta_value( $object_id, $meta_key, $name );
				if ( is_wp_error( $result ) ) {
					return $result;
				}
				continue;
			}

			$is_valid = rest_validate_value_from_schema( $meta[ $name ], $args['schema'], 'meta.' . $name );
			if ( is_wp_error( $is_valid ) ) {
				$is_valid->add_data( array( 'status' => 400 ) );
				return $is_valid;
			}

			$value = rest_sanitize_value_from_schema( $meta[ $name ], $args['schema'] );

			if ( $args['single'] ) {
				$result = $this->update_meta_value( $object_id, $meta_key, $name, $value );
			} else {
				$result = $this->update_multi_meta_value( $object_id, $meta_key, $name, $value );
			}

			if ( is_wp_error( $result ) ) {
				return $result;
			}
		}

		return null;
	}

	/**
	 * Deletes a meta value for an object.
	 *
	 * @since 4.7.0
	 *
	 * @param int    $object_id Object ID the field belongs to.
	 * @param string $meta_key  Key for the field.
	 * @param string $name      Name for the field that is exposed in the REST API.
	 * @return bool|WP_Error True if meta field is deleted, WP_Error otherwise.
	 */
	protected function delete_meta_value( $object_id, $meta_key, $name ) {
		$meta_type = $this->get_meta_type();
		if ( ! current_user_can( "delete_{$meta_type}_meta", $object_id, $meta_key ) ) {
			return new WP_Error(
				'rest_cannot_delete',
				/* translators: %s: custom field key */
				sprintf( __( 'Sorry, you are not allowed to edit the %s custom field.' ), $name ),
				array( 'key' => $name, 'status' => rest_authorization_required_code() )
			);
		}

		if ( ! delete_metadata( $meta_type, $object_id, wp_slash( $meta_key ) ) ) {
			return new WP_Error(
				'rest_meta_database_error',
				__( 'Could not delete meta value from database.' ),
				array( 'key' => $name, 'status' => WP_Http::INTERNAL_SERVER_ERROR )
			);
		}

		return true;
	}

	/**
	 * Updates multiple meta values for an object.
	 *
	 * Alters the list of values in the database to match the list of provided values.
	 *
	 * @since 4.7.0
	 *
	 * @param int    $object_id Object ID to update.
	 * @param string $meta_key  Key for the custom field.
	 * @param string $name      Name for the field that is exposed in the REST API.
	 * @param array  $values    List of values to update to.
	 * @return bool|WP_Error True if meta fields are updated, WP_Error otherwise.
	 */
	protected function update_multi_meta_value( $object_id, $meta_key, $name, $values ) {
		$meta_type = $this->get_meta_type();
		if ( ! current_user_can( "edit_{$meta_type}_meta", $object_id, $meta_key ) ) {
			return new WP_Error(
				'rest_cannot_update',
				/* translators: %s: custom field key */
				sprintf( __( 'Sorry, you are not allowed to edit the %s custom field.' ), $name ),
				array( 'key' => $name, 'status' => rest_authorization_required_code() )
			);
		}

		$current = get_metadata( $meta_type, $object_id, $meta_key, false );

		$to_remove = $current;
		$to_add    = $values;

		foreach ( $to_add as $add_key => $value ) {
			$remove_keys = array_keys( $to_remove, $value, true );

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

			if ( count( $remove_keys ) > 1 ) {
				// To remove, we need to remove first, then add, so don't touch.
				continue;
			}

			$remove_key = $remove_keys[0];

			unset( $to_remove[ $remove_key ] );
			unset( $to_add[ $add_key ] );
		}

		// `delete_metadata` removes _all_ instances of the value, so only call once.
		$to_remove = array_unique( $to_remove );

		foreach ( $to_remove as $value ) {
			if ( ! delete_metadata( $meta_type, $object_id, wp_slash( $meta_key ), wp_slash( $value ) ) ) {
				return new WP_Error(
					'rest_meta_database_error',
					__( 'Could not update meta value in database.' ),
					array( 'key' => $name, 'status' => WP_Http::INTERNAL_SERVER_ERROR )
				);
			}
		}

		foreach ( $to_add as $value ) {
			if ( ! add_metadata( $meta_type, $object_id, wp_slash( $meta_key ), wp_slash( $value ) ) ) {
				return new WP_Error(
					'rest_meta_database_error',
					__( 'Could not update meta value in database.' ),
					array( 'key' => $name, 'status' => WP_Http::INTERNAL_SERVER_ERROR )
				);
			}
		}

		return true;
	}

	/**
	 * Updates a meta value for an object.
	 *
	 * @since 4.7.0
	 *
	 * @param int    $object_id Object ID to update.
	 * @param string $meta_key  Key for the custom field.
	 * @param string $name      Name for the field that is exposed in the REST API.
	 * @param mixed  $value     Updated value.
	 * @return bool|WP_Error True if the meta field was updated, WP_Error otherwise.
	 */
	protected function update_meta_value( $object_id, $meta_key, $name, $value ) {
		$meta_type = $this->get_meta_type();
		if ( ! current_user_can(  "edit_{$meta_type}_meta", $object_id, $meta_key ) ) {
			return new WP_Error(
				'rest_cannot_update',
				/* translators: %s: custom field key */
				sprintf( __( 'Sorry, you are not allowed to edit the %s custom field.' ), $name ),
				array( 'key' => $name, 'status' => rest_authorization_required_code() )
			);
		}

		$meta_key   = wp_slash( $meta_key );
		$meta_value = wp_slash( $value );

		// Do the exact same check for a duplicate value as in update_metadata() to avoid update_metadata() returning false.
		$old_value = get_metadata( $meta_type, $object_id, $meta_key );

		if ( 1 === count( $old_value ) ) {
			if ( $old_value[0] === $meta_value ) {
				return true;
			}
		}

		if ( ! update_metadata( $meta_type, $object_id, $meta_key, $meta_value ) ) {
			return new WP_Error(
				'rest_meta_database_error',
				__( 'Could not update meta value in database.' ),
				array( 'key' => $name, 'status' => WP_Http::INTERNAL_SERVER_ERROR )
			);
		}

		return true;
	}

	/**
	 * Retrieves all the registered meta fields.
	 *
	 * @since 4.7.0
	 *
	 * @return array Registered fields.
	 */
	protected function get_registered_fields() {
		$registered = array();

		foreach ( get_registered_meta_keys( $this->get_meta_type() ) as $name => $args ) {
			if ( empty( $args['show_in_rest'] ) ) {
				continue;
			}

			$rest_args = array();

			if ( is_array( $args['show_in_rest'] ) ) {
				$rest_args = $args['show_in_rest'];
			}

			$default_args = array(
				'name'             => $name,
				'single'           => $args['single'],
				'type'             => ! empty( $args['type'] ) ? $args['type'] : null,
				'schema'           => array(),
				'prepare_callback' => array( $this, 'prepare_value' ),
			);

			$default_schema = array(
				'type'        => $default_args['type'],
				'description' => empty( $args['description'] ) ? '' : $args['description'],
				'default'     => isset( $args['default'] ) ? $args['default'] : null,
			);

			$rest_args = array_merge( $default_args, $rest_args );
			$rest_args['schema'] = array_merge( $default_schema, $rest_args['schema'] );

			$type = ! empty( $rest_args['type'] ) ? $rest_args['type'] : null;
			$type = ! empty( $rest_args['schema']['type'] ) ? $rest_args['schema']['type'] : $type;

			if ( ! in_array( $type, array( 'string', 'boolean', 'integer', 'number' ) ) ) {
				continue;
			}

			if ( empty( $rest_args['single'] ) ) {
				$rest_args['schema']['items'] = array(
					'type' => $rest_args['type'],
				);
				$rest_args['schema']['type'] = 'array';
			}

			$registered[ $name ] = $rest_args;
		}

		return $registered;
	}

	/**
	 * Retrieves the object's meta schema, conforming to JSON Schema.
	 *
	 * @since 4.7.0
	 *
	 * @return array Field schema data.
	 */
	public function get_field_schema() {
		$fields = $this->get_registered_fields();

		$schema = array(
			'description' => __( 'Meta fields.' ),
			'type'        => 'object',
			'context'     => array( 'view', 'edit' ),
			'properties'  => array(),
			'arg_options' => array(
				'sanitize_callback' => null,
				'validate_callback' => array( $this, 'check_meta_is_array' ),
			),
		);

		foreach ( $fields as $args ) {
			$schema['properties'][ $args['name'] ] = $args['schema'];
		}

		return $schema;
	}

	/**
	 * Prepares a meta value for output.
	 *
	 * Default preparation for meta fields. Override by passing the
	 * `prepare_callback` in your `show_in_rest` options.
	 *
	 * @since 4.7.0
	 *
	 * @param mixed           $value   Meta value from the database.
	 * @param WP_REST_Request $request Request object.
	 * @param array           $args    REST-specific options for the meta key.
	 * @return mixed Value prepared for output. If a non-JsonSerializable object, null.
	 */
	public static function prepare_value( $value, $request, $args ) {
		$type = $args['schema']['type'];

		// For multi-value fields, check the item type instead.
		if ( 'array' === $type && ! empty( $args['schema']['items']['type'] ) ) {
			$type = $args['schema']['items']['type'];
		}

		switch ( $type ) {
			case 'string':
				$value = (string) $value;
				break;
			case 'integer':
				$value = (int) $value;
				break;
			case 'number':
				$value = (float) $value;
				break;
			case 'boolean':
				$value = (bool) $value;
				break;
		}

		// Don't allow objects to be output.
		if ( is_object( $value ) && ! ( $value instanceof JsonSerializable ) ) {
			return null;
		}

		return $value;
	}

	/**
	 * Check the 'meta' value of a request is an associative array.
	 *
	 * @since 4.7.0
	 *
	 * @param  mixed           $value   The meta value submitted in the request.
	 * @param  WP_REST_Request $request Full details about the request.
	 * @param  string          $param   The parameter name.
	 * @return WP_Error|string The meta array, if valid, otherwise an error.
	 */
	public function check_meta_is_array( $value, $request, $param ) {
		if ( ! is_array( $value ) ) {
			return false;
		}

		return $value;
	}
}
class-wp-rest-term-meta-fields.php000066600000001766151234451570013143 0ustar00<?php
/**
 * REST API: WP_REST_Term_Meta_Fields class
 *
 * @package WordPress
 * @subpackage REST_API
 * @since 4.7.0
 */

/**
 * Core class used to manage meta values for terms via the REST API.
 *
 * @since 4.7.0
 *
 * @see WP_REST_Meta_Fields
 */
class WP_REST_Term_Meta_Fields extends WP_REST_Meta_Fields {

	/**
	 * Taxonomy to register fields for.
	 *
	 * @since 4.7.0
	 * @var string
	 */
	protected $taxonomy;

	/**
	 * Constructor.
	 *
	 * @since 4.7.0
	 *
	 * @param string $taxonomy Taxonomy to register fields for.
	 */
	public function __construct( $taxonomy ) {
		$this->taxonomy = $taxonomy;
	}

	/**
	 * Retrieves the object meta type.
	 *
	 * @since 4.7.0
	 *
	 * @return string The meta type.
	 */
	protected function get_meta_type() {
		return 'term';
	}

	/**
	 * Retrieves the type for register_rest_field().
	 *
	 * @since 4.7.0
	 *
	 * @return string The REST field type.
	 */
	public function get_rest_field_type() {
		return 'post_tag' === $this->taxonomy ? 'tag' : $this->taxonomy;
	}
}
class-wp-rest-user-meta-fields.php000066600000001243151234451570013140 0ustar00<?php
/**
 * REST API: WP_REST_User_Meta_Fields class
 *
 * @package WordPress
 * @subpackage REST_API
 * @since 4.7.0
 */

/**
 * Core class used to manage meta values for users via the REST API.
 *
 * @since 4.7.0
 *
 * @see WP_REST_Meta_Fields
 */
class WP_REST_User_Meta_Fields extends WP_REST_Meta_Fields {

	/**
	 * Retrieves the object meta type.
	 *
	 * @since 4.7.0
	 *
	 * @return string The user meta type.
	 */
	protected function get_meta_type() {
		return 'user';
	}

	/**
	 * Retrieves the type for register_rest_field().
	 *
	 * @since 4.7.0
	 *
	 * @return string The user REST field type.
	 */
	public function get_rest_field_type() {
		return 'user';
	}
}
class-wp-rest-comment-meta-fields.php000066600000001276151234451570013632 0ustar00<?php
/**
 * REST API: WP_REST_Comment_Meta_Fields class
 *
 * @package WordPress
 * @subpackage REST_API
 * @since 4.7.0
 */

/**
 * Core class to manage comment meta via the REST API.
 *
 * @since 4.7.0
 *
 * @see WP_REST_Meta_Fields
 */
class WP_REST_Comment_Meta_Fields extends WP_REST_Meta_Fields {

	/**
	 * Retrieves the object type for comment meta.
	 *
	 * @since 4.7.0
	 *
	 * @return string The meta type.
	 */
	protected function get_meta_type() {
		return 'comment';
	}

	/**
	 * Retrieves the type for register_rest_field() in the context of comments.
	 *
	 * @since 4.7.0
	 *
	 * @return string The REST field type.
	 */
	public function get_rest_field_type() {
		return 'comment';
	}
}
class-gf-field-captcha.php000066600000050651151264036140011453 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_CAPTCHA extends GF_Field {
	/**
	 * @var string
	 */
	public $type = 'captcha';

	/**
	 * The reCAPTCHA API response.
	 *
	 * @var \stdClass
	 */
	private $response;

	/**
	 * The reCAPTCHA site key.
	 *
	 * @var string
	 */
	private $site_key;

	/**
	 * The reCAPTCHA secret key.
	 *
	 * @var string
	 */
	private $secret_key;

	public function get_form_editor_field_title() {
		return esc_attr__( 'CAPTCHA', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'captcha_type_setting',
			'captcha_badge_setting',
			'captcha_size_setting',
			'captcha_fg_setting',
			'captcha_bg_setting',
			'captcha_language_setting',
			'captcha_theme_setting',
			'conditional_logic_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	/**
	 * Validate the reCAPTCHA field.
	 *
	 * This method always gets called on the last page of a form, as well as on the page where the field is assigned.
	 *
	 * @since Unknown
	 *
	 * @param array|string $value The field value.
	 * @param array        $form  The form data.
	 */
	public function validate( $value, $form ) {
		switch ( $this->captchaType ) {
			case 'simple_captcha' :
				if ( class_exists( 'ReallySimpleCaptcha' ) ) {
					$prefix      = $_POST[ "input_captcha_prefix_{$this->id}" ];
					$captcha_obj = $this->get_simple_captcha();

					if ( ! $captcha_obj->check( $prefix, str_replace( ' ', '', $value ) ) ) {
						$this->set_failed_validation( esc_html__( "The CAPTCHA wasn't entered correctly. Go back and try it again.", 'gravityforms' ) );
					}

					//removes old files in captcha folder (older than 1 hour);
					$captcha_obj->cleanup();
				}
				break;

			case 'math' :
				$prefixes    = explode( ',', $_POST[ "input_captcha_prefix_{$this->id}" ] );
				$captcha_obj = $this->get_simple_captcha();

				//finding first number
				for ( $first = 0; $first < 10; $first ++ ) {
					if ( $captcha_obj->check( $prefixes[0], $first ) ) {
						break;
					}
				}

				//finding second number
				for ( $second = 0; $second < 10; $second ++ ) {
					if ( $captcha_obj->check( $prefixes[2], $second ) ) {
						break;
					}
				}

				//if it is a +, perform the sum
				if ( $captcha_obj->check( $prefixes[1], '+' ) ) {
					$result = $first + $second;
				} else {
					$result = $first - $second;
				}



				if ( intval( $result ) != intval( $value ) ) {
					$this->set_failed_validation( esc_html__( "The CAPTCHA wasn't entered correctly. Go back and try it again.", 'gravityforms' ) );
				}

				//removes old files in captcha folder (older than 1 hour);
				$captcha_obj->cleanup();

				break;

			default:
				$this->validate_recaptcha( $form );
		}

	}

	/**
	 * Validates the reCAPTCHA response.
	 *
	 * In our application flow, we create a decoded string out of the reCAPTCHA service response if the reCAPTCHA field
	 * is added to the form on a page other than the last page. We therefore first attempt to validate the decoded response,
	 * falling back to validating the reCAPTCHA with a request to Google.
	 *
	 * @see GF_Field_CAPTCHA::verify_decoded_response()
	 *
	 * @since unknown
	 *
	 * @param array $form The form data.
	 *
	 * @return bool
	 */
	public function validate_recaptcha( $form ) {
		$response = $this->get_posted_recaptcha_response();

		if ( ! ( $this->verify_decoded_response( $form, $response ) || $this->verify_recaptcha_response( $response ) ) ) {
			$this->set_failed_validation( __( 'The reCAPTCHA was invalid. Go back and try it again.', 'gravityforms' ) );
			return false;
		}

		return true;
	}

	/**
	 * Verifies that the decoded response meets the requirements for submitting the form.
	 *
	 * Returns false if the decoded response doesn't exist or the reCAPTCHA field is on the last page, as we'll want
	 * regular validation at that point instead.
	 *
	 * @since 2.4.24
	 *
	 * @param array  $form     The form data.
	 * @param string $response The encoded response to verify.
	 *
	 * @return bool
	 */
	private function verify_decoded_response( $form, $response ) {
		$decoded_response = $this->get_decoded_recaptcha_response( $response );

		// No decoded object.
		if ( ! is_object( $decoded_response ) ) {
			return false;
		}

		// Not a time that we need to verify the decoded object.
		if ( ! GFFormDisplay::is_last_page( $form ) || $this->is_on_last_page( $form ) ) {
			return false;
		}

		return (
			$decoded_response->success === true
			&& ! empty( $decoded_response->token )
			&& gmdate( time() ) <= strtotime( '+1 day', strtotime( $decoded_response->challenge_ts ) )
		);
	}

	/**
	 * Set validation failed on reCAPTCHA field.
	 *
	 * @since 2.4.24
	 *
	 * @param string $message The message to set if one does not already exist.
	 */
	private function set_failed_validation( $message ) {
		$this->failed_validation  = true;
		$this->validation_message = empty( $this->errorMessage ) ? $message : $this->errorMessage;
	}

	/**
	 * Get the saved site key.
	 *
	 * @since 2.4.24
	 *
	 * @return string
	 */
	public function get_site_key() {
		if ( ! $this->site_key ) {
			$this->site_key   = get_option( 'rg_gforms_captcha_public_key', '' );
		}

		return $this->site_key;
	}

	/**
	 * Get the saved secret key.
	 *
	 * @since 2.4.25
	 *
	 * @return string
	 */
	public function get_secret_key() {
		if ( ! $this->secret_key ) {
			$this->secret_key = get_option( 'rg_gforms_captcha_private_key', '' );
		}

		return $this->secret_key;
	}

	/**
	 * Get the value of the reCAPTCHA response input.
	 *
	 * When user clicks on the "I'm not a robot" box, the response token is populated into a hidden field by Google.
	 * If the current form is a multi-page form and the reCAPTCHA field is on a page other than the last page, this
	 * value will return an openssl encoded string with the Google reCAPTCHA validation data and some supplemental
	 * validation data instead.
	 *
	 * @see GF_Field_CAPTCHA::get_encoded_recaptcha_response()
	 *
	 * @since 2.4.24
	 *
	 * @return string
	 */
	private function get_posted_recaptcha_response() {
		return sanitize_text_field( rgpost( 'g-recaptcha-response' ) );
	}

	/**
	 * Validate the reCAPTCHA token provided by Google.
	 *
	 * @since unknown
	 *
	 * @param string $response   The token to verify.
	 * @param null   $secret_key The secret key for reCAPTCHA verification.
	 *
	 * @return bool
	 */
	public function verify_recaptcha_response( $response, $secret_key = null ) {

		$verify_url = 'https://www.google.com/recaptcha/api/siteverify';

		if ( $secret_key == null ) {
			$secret_key = $this->get_secret_key();
		}

		// pass secret key and token for verification of whether the response was valid
		$response = wp_remote_post( $verify_url, array(
			'method' => 'POST',
			'body'   => array(
				'secret'   => $secret_key,
				'response' => $response
			),
		) );

		if ( ! is_wp_error( $response ) ) {
			$this->response = json_decode( wp_remote_retrieve_body( $response ) );

			return $this->response->success == true;
		} else {
			GFCommon::log_debug( __METHOD__ . '(): Validating the reCAPTCHA response has failed due to the following: ' . $response->get_error_message() );
		}

		return false;
	}

	public function get_field_input( $form, $value = '', $entry = null ) {
		$form_id         = $form['id'];
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$id       = (int) $this->id;
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		switch ( $this->captchaType ) {
			case 'simple_captcha' :
				$size    = empty($this->simpleCaptchaSize) ? 'medium' : esc_attr( $this->simpleCaptchaSize );
				$captcha = $this->get_captcha();

				$tabindex = $this->get_tabindex();

				$dimensions = $is_entry_detail || $is_form_editor ? '' : "width='" . esc_attr( rgar( $captcha, 'width' ) ) . "' height='" . esc_attr( rgar( $captcha, 'height' ) ) . "'";

				return "<div class='gfield_captcha_container'><img class='gfield_captcha' src='" . esc_url( rgar( $captcha, 'url' ) ) . "' alt='' {$dimensions} /><div class='gfield_captcha_input_container simple_captcha_{$size}'><input type='text' autocomplete='off' name='input_{$id}' id='{$field_id}' {$tabindex}/><input type='hidden' name='input_captcha_prefix_{$id}' value='" . esc_attr( rgar( $captcha, 'prefix' ) ) . "' /></div></div>";
				break;

			case 'math' :
				$size      = empty( $this->simpleCaptchaSize ) ? 'medium' : esc_attr( $this->simpleCaptchaSize );
				$captcha_1 = $this->get_math_captcha( 1 );
				$captcha_2 = $this->get_math_captcha( 2 );
				$captcha_3 = $this->get_math_captcha( 3 );

				$tabindex = $this->get_tabindex();

				$dimensions   = $is_entry_detail || $is_form_editor ? '' : "width='" . esc_attr( rgar( $captcha_1, 'width' ) ) . "' height='" . esc_attr( rgar( $captcha_1, 'height' ) ) . "'";
				$prefix_value = rgar( $captcha_1, 'prefix' ) . ',' . rgar( $captcha_2, 'prefix' ) . ',' . rgar( $captcha_3, 'prefix' );

				return "<div class='gfield_captcha_container'><img class='gfield_captcha' src='" . esc_url( rgar( $captcha_1, 'url' ) ) . "' alt='' {$dimensions} /><img class='gfield_captcha' src='" . esc_url( rgar( $captcha_2, 'url' ) ) . "' alt='' {$dimensions} /><img class='gfield_captcha' src='" . esc_url( rgar( $captcha_3, 'url' ) ) . "' alt='' {$dimensions} /><div class='gfield_captcha_input_container math_{$size}'><input type='text' autocomplete='off' name='input_{$id}' id='{$field_id}' {$tabindex}/><input type='hidden' name='input_captcha_prefix_{$id}' value='" . esc_attr( $prefix_value ) . "' /></div></div>";
				break;

			default:

				$this->site_key   = $this->get_site_key();
				$this->secret_key = $this->get_secret_key();
				$theme      = in_array( $this->captchaTheme, array( 'blackglass', 'dark' ) ) ? 'dark' : 'light';
				$type 		= get_option( 'rg_gforms_captcha_type' );
				if ( $is_entry_detail || $is_form_editor ){

					//for admin, show a thumbnail depending on chosen theme
					if ( empty( $this->site_key ) || empty( $this->secret_key ) ) {

						return "<div class='captcha_message'>" . __( 'To use the reCAPTCHA field you must do the following:', 'gravityforms' ) . "</div><div class='captcha_message'>1 - <a href='https://www.google.com/recaptcha/admin' target='_blank'>" . sprintf( __( 'Sign up%s for an API key pair for your site.', 'gravityforms' ), '</a>' ) . "</div><div class='captcha_message'>2 - " . sprintf( __( 'Enter your reCAPTCHA site and secret keys in the reCAPTCHA Settings section of the %sSettings page%s', 'gravityforms' ), "<a href='?page=gf_settings' target='_blank'>", '</a>' ) . '</div>';
					}

					$type_suffix = $type == 'invisible' ? 'invisible_' : '';
					$alt         = esc_attr__( 'An example of reCAPTCHA', 'gravityforms' );

					return "<div class='ginput_container'><img class='gfield_captcha' src='" . GFCommon::get_base_url() . "/images/captcha_{$type_suffix}{$theme}.jpg' alt='{$alt}' /></div>";
				}

				$language     = empty( $this->captchaLanguage ) ? 'en' : $this->captchaLanguage;

				// script is queued for the footer with the language property specified
				wp_enqueue_script( 'gform_recaptcha', 'https://www.google.com/recaptcha/api.js?hl=' . $language . '&render=explicit', array(), false, true );

				add_action( 'wp_footer', array( $this, 'ensure_recaptcha_js' ), 21 );
				add_action( 'gform_preview_footer', array( $this, 'ensure_recaptcha_js' ) );

				$stoken = '';

				if ( $this->use_stoken() ) {
					// The secure token is a deprecated feature of the reCAPTCHA API.
					// https://developers.google.com/recaptcha/docs/secure_token
					$secure_token = self::create_recaptcha_secure_token( $this->secret_key );
					$stoken = sprintf( 'data-stoken=\'%s\'', esc_attr( $secure_token ) );
				}

				$size  = '';
				$badge = '';

				if ( $type == 'invisible' ) {
					$size     = "data-size='invisible'";
					$badge    = $this->captchaBadge ? $this->captchaBadge : 'bottomright';
					$tabindex = -1;
				} else {
					$tabindex = GFCommon::$tab_index > 0 ? GFCommon::$tab_index++ : 0;
				}

				$output = "<div id='" . esc_attr( $field_id ) ."' class='ginput_container ginput_recaptcha' data-sitekey='" . esc_attr( $this->site_key ) . "' {$stoken} data-theme='" . esc_attr( $theme ) . "' data-tabindex='{$tabindex}' {$size} data-badge='{$badge}'></div>";

				$recaptcha_response = $this->get_posted_recaptcha_response();

				if ( ! $this->requires_encoding( $form, $recaptcha_response ) ) {
					return $output;
				}

				ob_start();
				?>
				<input
					type="hidden"
					name="g-recaptcha-response"
					value="<?php esc_attr_e( $this->get_encoded_recaptcha_response( $form, $recaptcha_response) ); ?>"
				/>
				<?php
				return $output .= ob_get_clean();
		}
	}

	/**
	 * Encode the reCAPTCHA response with details from Google.
	 *
	 * @since 2.4.24
	 *
	 * @param array  $form     The form data.
	 * @param string $response The posted response data.
	 *
	 * @return string
	 */
	private function get_encoded_recaptcha_response( $form, $response ) {
		if ( ! $this->response ) {
			return $response;
		}

		$this->response->token = $response;

		return GFCommon::openssl_encrypt( base64_encode( json_encode( $this->response ) ), $this->secret_key );
	}

	/**
	 * Decode and return the value of g-recaptcha-response field.
	 *
	 * The first time this method is called, the $response parameter will be the result of the reCAPTCHA callback,
	 * and decryption will fail. On subsequent requests, it should contain an encoded string of the reCAPTCHA response
	 * and the original token used to make the request.
	 *
	 * @since 2.4.24
	 *
	 * @param string $response An openssl encoded string, or the reCAPTCHA token on the very first call.
	 *
	 * @return string
	 */
	private function get_decoded_recaptcha_response( $response ) {
		$decoded_response = GFCommon::openssl_decrypt( $response, $this->get_secret_key() );

		if ( ! $decoded_response ) {
			return;
		}

		return json_decode( base64_decode( $decoded_response ) );
	}

	/**
	 * Check whether the reCAPTCHA response should be saved and encoded for validation on the final form page.
	 *
	 * @since 2.4.24
	 *
	 * @param array  $form               The form data.
	 * @param string $recaptcha_response The reCAPTCHA response.
	 *
	 * @return bool
	 */
	private function requires_encoding( $form, $recaptcha_response ) {
		return $recaptcha_response && ! $this->failed_validation && GFFormDisplay::get_current_page( rgar( $form, 'id' ) ) != $this->pageNumber && ! $this->is_on_last_page( $form );
	}

	/**
	 * Returns true if this CAPTCHA field is on the last page of the given form.
	 *
	 * @since 2.4.24
	 *
	 * @param array $form The form data.
	 *
	 * @return bool
	 */
	private function is_on_last_page( $form ) {
	    $pages = GFAPI::get_fields_by_type( $form, array( 'page' ) );

	    return count( $pages ) + 1 === (int) $this->pageNumber;
    }

	public function ensure_recaptcha_js(){
		?>
		<script type="text/javascript">
			( function( $ ) {
				$( document ).bind( 'gform_post_render', function() {
					var gfRecaptchaPoller = setInterval( function() {
						if( ! window.grecaptcha || ! window.grecaptcha.render ) {
							return;
						}
						renderRecaptcha();
						clearInterval( gfRecaptchaPoller );
					}, 100 );
				} );
			} )( jQuery );
		</script>

		<?php
	}

	public function get_captcha() {
		if ( ! class_exists( 'ReallySimpleCaptcha' ) ) {
			return array();
		}

		$captcha = $this->get_simple_captcha();

		//If captcha folder does not exist and can't be created, return an empty captcha
		if ( ! wp_mkdir_p( $captcha->tmp_dir ) ) {
			return array();
		}

		$captcha->char_length = 5;
		switch ( $this->simpleCaptchaSize ) {
			case 'small' :
				$captcha->img_size        = array( 100, 28 );
				$captcha->font_size       = 18;
				$captcha->base            = array( 8, 20 );
				$captcha->font_char_width = 17;

				break;

			case 'large' :
				$captcha->img_size        = array( 200, 56 );
				$captcha->font_size       = 32;
				$captcha->base            = array( 18, 42 );
				$captcha->font_char_width = 35;
				break;

			default :
				$captcha->img_size        = array( 150, 42 );
				$captcha->font_size       = 26;
				$captcha->base            = array( 15, 32 );
				$captcha->font_char_width = 25;
				break;
		}

		if ( ! empty( $this->simpleCaptchaFontColor ) ) {
			$captcha->fg = $this->hex2rgb( $this->simpleCaptchaFontColor );
		}
		if ( ! empty( $this->simpleCaptchaBackgroundColor ) ) {
			$captcha->bg = $this->hex2rgb( $this->simpleCaptchaBackgroundColor );
		}

		$word     = $captcha->generate_random_word();
		$prefix   = mt_rand();
		$filename = $captcha->generate_image( $prefix, $word );
		$url      = RGFormsModel::get_upload_url( 'captcha' ) . '/' . $filename;
		$path     = $captcha->tmp_dir . $filename;

		if ( GFCommon::is_ssl() && strpos( $url, 'http:' ) !== false ) {
			$url = str_replace( 'http:', 'https:', $url );
		}

		return array( 'path' => $path, 'url' => $url, 'height' => $captcha->img_size[1], 'width' => $captcha->img_size[0], 'prefix' => $prefix );
	}

	public function get_simple_captcha() {
		$captcha          = new ReallySimpleCaptcha();
		$captcha->tmp_dir = RGFormsModel::get_upload_path( 'captcha' ) . '/';

		return $captcha;
	}

	public function get_math_captcha( $pos ) {
		if ( ! class_exists( 'ReallySimpleCaptcha' ) ) {
			return array();
		}

		$captcha = $this->get_simple_captcha();

		//If captcha folder does not exist and can't be created, return an empty captcha
		if ( ! wp_mkdir_p( $captcha->tmp_dir ) ) {
			return array();
		}

		$captcha->char_length = 1;
		if ( $pos == 1 || $pos == 3 ) {
			$captcha->chars = '0123456789';
		} else {
			$captcha->chars = '+';
		}

		switch ( $this->simpleCaptchaSize ) {
			case 'small' :
				$captcha->img_size        = array( 23, 28 );
				$captcha->font_size       = 18;
				$captcha->base            = array( 6, 20 );
				$captcha->font_char_width = 17;

				break;

			case 'large' :
				$captcha->img_size        = array( 36, 56 );
				$captcha->font_size       = 32;
				$captcha->base            = array( 10, 42 );
				$captcha->font_char_width = 35;
				break;

			default :
				$captcha->img_size        = array( 30, 42 );
				$captcha->font_size       = 26;
				$captcha->base            = array( 9, 32 );
				$captcha->font_char_width = 25;
				break;
		}

		if ( ! empty( $this->simpleCaptchaFontColor ) ) {
			$captcha->fg = $this->hex2rgb( $this->simpleCaptchaFontColor );
		}
		if ( ! empty( $this->simpleCaptchaBackgroundColor ) ) {
			$captcha->bg = $this->hex2rgb( $this->simpleCaptchaBackgroundColor );
		}

		$word     = $captcha->generate_random_word();
		$prefix   = mt_rand();
		$filename = $captcha->generate_image( $prefix, $word );
		$url      = RGFormsModel::get_upload_url( 'captcha' ) . '/' . $filename;
		$path     = $captcha->tmp_dir . $filename;

		if ( GFCommon::is_ssl() && strpos( $url, 'http:' ) !== false ) {
			$url = str_replace( 'http:', 'https:', $url );
		}

		return array( 'path' => $path, 'url' => $url, 'height' => $captcha->img_size[1], 'width' => $captcha->img_size[0], 'prefix' => $prefix );
	}

	private function hex2rgb( $color ) {
		if ( $color[0] == '#' ) {
			$color = substr( $color, 1 );
		}

		if ( strlen( $color ) == 6 ) {
			list( $r, $g, $b ) = array(
				$color[0] . $color[1],
				$color[2] . $color[3],
				$color[4] . $color[5],
			);
		} elseif ( strlen( $color ) == 3 ) {
			list( $r, $g, $b ) = array( $color[0] . $color[0], $color[1] . $color[1], $color[2] . $color[2] );
		} else {
			return false;
		}

		$r = hexdec( $r );
		$g = hexdec( $g );
		$b = hexdec( $b );

		return array( $r, $g, $b );
	}

	public function create_recaptcha_secure_token( $secret_key ) {

		$secret_key = substr( hash( 'sha1', $secret_key, true ), 0, 16 );
		$session_id = uniqid( 'recaptcha' );
		$ts_ms      = round( ( microtime( true ) - 1 ) * 1000 );

		//create json string
		$params    = array( 'session_id' => $session_id, 'ts_ms' => $ts_ms );
		$plaintext = json_encode( $params );
		GFCommon::log_debug( 'recaptcha token parameters: ' . $plaintext );

		//pad json string
		$pad    = 16 - ( strlen( $plaintext ) % 16 );
		$padded = $plaintext . str_repeat( chr( $pad ), $pad );

		//encrypt as 128
		$cypher = defined( 'MCRYPT_RIJNDAEL_128' ) ? MCRYPT_RIJNDAEL_128 : false;
		$encrypted = GFCommon::encrypt( $padded, $secret_key, $cypher );

		$token = str_replace( array( '+', '/', '=' ), array( '-', '_', '' ), $encrypted );
		GFCommon::log_debug( ' token being used is: ' . $token );

		return $token;
	}

	public function use_stoken() {
		// 'gform_recaptcha_keys_status' will be set to true if new keys have been entered
		return ! get_option( 'gform_recaptcha_keys_status', false );
	}

}

GF_Fields::register( new GF_Field_CAPTCHA() );
class-gf-field-multiselect.php000066600000032207151264036140012377 0ustar00<?php

// If the GF_Field class isn't available, bail.
if ( ! class_exists( 'GFForms' ) ) {
	die();
}

/**
 * Class GF_Field_MultiSelect
 *
 * Allows the creation of multiselect fields.
 *
 * @since Unknown
 *
 * @uses GF_Field
 */
class GF_Field_MultiSelect extends GF_Field {

	public $type = 'multiselect';

	/**
	 * Returns the field title.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @return string The field title. Escaped.
	 */
	public function get_form_editor_field_title() {
		return esc_attr__( 'Multi Select', 'gravityforms' );
	}

	/**
	 * Returns the class names of the settings which should be available on the field in the form editor.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @return array Settings available within the field editor.
	 */
	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'enable_enhanced_ui_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'size_setting',
			'choices_setting',
			'rules_setting',
			'visibility_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	/**
	 * Indicates this field type can be used when configuring conditional logic rules.
	 *
	 * @return bool
	 */
	public function is_conditional_logic_supported() {
		return true;
	}

	/**
	 * Whether this field expects an array during submission.
	 *
	 * @since 2.4
	 *
	 * @return bool
	 */
	public function is_value_submission_array() {
		return true;
	}

	/**
	 * Returns the field inner markup.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GF_Field_MultiSelect::is_entry_detail()
	 * @uses GF_Field_MultiSelect::is_form_editor()
	 * @uses GF_Field_MultiSelect::get_conditional_logic_event()
	 * @uses GF_Field_MultiSelect::get_tabindex()
	 *
	 * @param array        $form  The Form Object currently being processed.
	 * @param string|array $value The field value. From default/dynamic population, $_POST, or a resumed incomplete submission.
	 * @param null|array   $entry Null or the Entry Object currently being edited.
	 *
	 * @return string The field input HTML markup.
	 */
	public function get_field_input( $form, $value = '', $entry = null ) {
		$form_id         = absint( $form['id'] );
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$id       = $this->id;
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		$size          = $this->size;
		$class_suffix  = $is_entry_detail ? '_admin' : '';
		$class         = $size . $class_suffix;
		$class         = esc_attr( $class );
		$css_class     = trim( $class . ' gfield_select' );
		$tabindex      = $this->get_tabindex();
		$disabled_text = $is_form_editor ? 'disabled="disabled"' : '';


		/**
		 * Allow the placeholder used by the enhanced ui to be overridden
		 *
		 * @since 1.9.14 Third parameter containing the field ID was added.
		 * @since Unknown
		 *
		 * @param string  $placeholder The placeholder text.
		 * @param integer $form_id     The ID of the current form.
		 */
		$placeholder = gf_apply_filters( array(
			'gform_multiselect_placeholder',
			$form_id,
			$this->id
		), __( 'Click to select...', 'gravityforms' ), $form_id, $this );
		$placeholder = $this->enableEnhancedUI ? "data-placeholder='" . esc_attr( $placeholder ) . "'" : '';

		$size = $this->multiSelectSize;
		if ( empty( $size ) ) {
			$size = 7;
		}
		$size = esc_attr( $size );

		return sprintf( "<div class='ginput_container ginput_container_multiselect'><select multiple='multiple' {$placeholder} size='{$size}' name='input_%d[]' id='%s' class='%s' $tabindex %s>%s</select></div>", $id, esc_attr( $field_id ), $css_class, $disabled_text, $this->get_choices( $value ) );
	}

	/**
	 * Helper for retrieving the markup for the choices.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GFCommon::get_select_choices()
	 *
	 * @param string|array $value The field value. From default/dynamic population, $_POST, or a resumed incomplete submission.
	 *
	 * @return string Returns the choices available within the multi-select field.
	 */
	public function get_choices( $value ) {

		$value = $this->to_array( $value );

		return GFCommon::get_select_choices( $this, $value, false );

	}

	/**
	 * Format the entry value for display on the entries list page.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param string|array $value    The field value.
	 * @param array        $entry    The Entry Object currently being processed.
	 * @param string       $field_id The field or input ID currently being processed.
	 * @param array        $columns  The properties for the columns being displayed on the entry list page.
	 * @param array        $form     The Form Object currently being processed.
	 *
	 * @return string $value The value of the field. Escaped.
	 */
	public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) {
		// Add space after comma-delimited values.
		$value = implode( ', ', $this->to_array( $value ) );
		return esc_html( $value );
	}

	/**
	 * Format the entry value for display on the entry detail page and for the {all_fields} merge tag.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GFCommon::selection_display()
	 *
	 * @param string|array $value    The field value.
	 * @param string       $currency The entry currency code.
	 * @param bool|false   $use_text When processing choice based fields should the choice text be returned instead of the value.
	 * @param string       $format   The format requested for the location the merge is being used. Possible values: html, text or url.
	 * @param string       $media    The location where the value will be displayed. Possible values: screen or email.
	 *
	 * @return string The list items, stored within an unordered list.
	 */
	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {

		if ( empty( $value ) || ( $format == 'text' && $this->storageType !== 'json' ) ) {
			return $value;
		}

		$items = $this->to_array( $value );

		foreach ( $items as &$item ) {
			$item = esc_html( GFCommon::selection_display( $item, $this, $currency, $use_text ) );
		}

		if ( $format === 'text' ) {
			return GFCommon::implode_non_blank( ', ', $items );
		}

		return "<ul class='bulleted'><li>" . GFCommon::implode_non_blank( '</li><li>', $items ) . '</li></ul>';
	}

	/**
	 * Format the value before it is saved to the Entry Object.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GF_Field_MultiSelect::sanitize_entry_value()
	 *
	 * @param array|string $value      The value to be saved.
	 * @param array        $form       The Form Object currently being processed.
	 * @param string       $input_name The input name used when accessing the $_POST.
	 * @param int          $lead_id    The ID of the Entry currently being processed.
	 * @param array        $lead       The Entry Object currently being processed.
	 *
	 * @return string $value The field value. Comma separated if an array.
	 */
	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {

		if ( is_array( $value ) ) {
			foreach ( $value as &$v ) {
				$v = $this->sanitize_entry_value( $v, $form['id'] );
			}
		} else {
			$value = $this->sanitize_entry_value( $value, $form['id'] );
		}

		return empty( $value ) ? '' : $this->to_string( $value );
	}

	/**
	 * Format the entry value for when the field/input merge tag is processed.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GFCommon::format_post_category()
	 * @uses GFCommon::format_variable_value()
	 * @uses GFCommon::selection_display()
	 * @uses GFCommon::implode_non_blank()
	 *
	 * @param string|array $value      The field value. Depending on the location the merge tag is being used the following functions may have already been applied to the value: esc_html, nl2br, and urlencode.
	 * @param string       $input_id   The field or input ID from the merge tag currently being processed.
	 * @param array        $entry      The Entry Object currently being processed.
	 * @param array        $form       The Form Object currently being processed.
	 * @param string       $modifier   The merge tag modifier. e.g. value
	 * @param string|array $raw_value  The raw field value from before any formatting was applied to $value.
	 * @param bool         $url_encode Indicates if the urlencode function may have been applied to the $value.
	 * @param bool         $esc_html   Indicates if the esc_html function may have been applied to the $value.
	 * @param string       $format     The format requested for the location the merge is being used. Possible values: html, text or url.
	 * @param bool         $nl2br      Indicates if the nl2br function may have been applied to the $value.
	 *
	 * @return string $return The merge tag value.
	 */
	public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) {
		$items = $this->to_array( $raw_value );

		$modifiers = $this->get_modifiers();

		if ( $this->type == 'post_category' ) {
			if ( is_array( $items ) ) {
				$use_id = in_array( 'id', $modifiers );

				foreach ( $items as &$item ) {
					$cat  = GFCommon::format_post_category( $item, $use_id );
					$item = GFCommon::format_variable_value( $cat, $url_encode, $esc_html, $format );
				}
			}
		} elseif ( ! in_array( 'value', $modifiers ) ) {

			foreach ( $items as &$item ) {
				$item = GFCommon::selection_display( $item, $this, rgar( $entry, 'currency' ), true );
				$item = GFCommon::format_variable_value( $item, $url_encode, $esc_html, $format );
			}
		}

		$return = GFCommon::implode_non_blank( ', ', $items );

		if ( $format == 'html' || $esc_html ) {
			$return = esc_html( $return );
		}

		return $return;
	}

	/**
	 * Format the entry value before it is used in entry exports and by framework add-ons using GFAddOn::get_field_value().
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GFCommon::selection_display()
	 * @uses GFCommon::implode_non_blank()
	 *
	 * @param array      $entry    The entry currently being processed.
	 * @param string     $input_id The field or input ID.
	 * @param bool|false $use_text When processing choice based fields should the choice text be returned instead of the value.
	 * @param bool|false $is_csv   Is the value going to be used in the .csv entries export?
	 *
	 * @return string $value The value of a field from an export file.
	 */
	public function get_value_export( $entry, $input_id = '', $use_text = false, $is_csv = false ) {
		if ( empty( $input_id ) ) {
			$input_id = $this->id;
		}

		$value = rgar( $entry, $input_id );

		if ( ! empty( $value ) && ! $is_csv ) {
			$items = $this->to_array( $value );

			foreach ( $items as &$item ) {
				$item = GFCommon::selection_display( $item, $this, rgar( $entry, 'currency' ), $use_text );
			}
			$value = GFCommon::implode_non_blank( ', ', $items );

		} elseif ( $this->storageType === 'json' ) {

			$items = json_decode( $value );
			$value = GFCommon::implode_non_blank( ', ', $items );
		}

		return $value;
	}

	/**
	 * Converts an array to a string.
	 *
	 * @since 2.2.3.7 Changed access to public.
	 * @since 2.2
	 * @access public
	 *
	 * @uses \GF_Field_MultiSelect::$storageType
	 *
	 * @param array $value The array to convert to a string.
	 *
	 * @return string The converted string.
	 */
	public function to_string( $value ) {
		if ( $this->storageType === 'json' ) {
			return json_encode( $value );
		} else {
			return is_array( $value ) ? implode( ',', $value ) : $value;
		}
	}

	/**
	 * Converts a string to an array.
	 *
	 * @since 2.2.3.7 Changed access to public.
	 * @since 2.2
	 * @access public
	 *
	 * @uses \GF_Field_MultiSelect::$storageType
	 *
	 * @param string $value A comma-separated or JSON string to convert.
	 *
	 * @return array The converted array.
	 */
	public function to_array( $value ) {
		if ( empty( $value ) ) {
			return array();
		} elseif ( is_array( $value ) ) {
			return $value;
		} elseif ( $this->storageType !== 'json' || $value[0] !== '[' ) {
			return array_map( 'trim', explode( ',', $value ) );
		} else {
			$json = json_decode( $value, true );

			return $json == null ? array() : $json;
		}
	}

	/**
	 * Forces settings into expected values while saving the form object.
	 *
	 * No escaping should be done at this stage to prevent double escaping on output.
	 *
	 * Currently called only for forms created after version 1.9.6.10.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @return void
	 *
	 */
	public function sanitize_settings() {
		parent::sanitize_settings();
		$this->enableEnhancedUI = (bool) $this->enableEnhancedUI;

		$this->storageType = empty( $this->storageType ) || $this->storageType === 'json' ? $this->storageType : 'json';

		if ( $this->type === 'post_category' ) {
			$this->displayAllCategories = (bool) $this->displayAllCategories;
		}
	}

	// # FIELD FILTER UI HELPERS ---------------------------------------------------------------------------------------

	/**
	 * Returns the filter operators for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_operators() {
		return array( 'contains' );
	}

}

// Register the new field type.
GF_Fields::register( new GF_Field_MultiSelect() );
class-gf-field-creditcard.php000066600000052355151264036140012157 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_CreditCard extends GF_Field {

	public $type = 'creditcard';

	public function get_form_editor_field_title() {
		return esc_attr__( 'Credit Card', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'force_ssl_field_setting',
			'credit_card_style_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'sub_labels_setting',
			'sub_label_placement_setting',
			'label_placement_setting',
			'admin_label_setting',
			'rules_setting',
			'description_setting',
			'css_class_setting',
			'credit_card_setting',
			'input_placeholders_setting',
		);
	}

	public function get_form_editor_button() {
		return array(); // this button is conditionally added in the form detail page
	}

	public function validate( $value, $form ) {
		$card_number     = rgpost( 'input_' . $this->id . '_1' );
		$expiration_date = rgpost( 'input_' . $this->id . '_2' );
		$security_code   = rgpost( 'input_' . $this->id . '_3' );

		if ( $this->isRequired && ( empty( $card_number ) || empty( $security_code ) || empty( $expiration_date[0] ) || empty( $expiration_date[1] ) ) ) {
			$this->failed_validation  = true;
			$this->validation_message = empty( $this->errorMessage ) ? esc_html__( 'Please enter your credit card information.', 'gravityforms' ) : $this->errorMessage;
		} elseif ( ! empty( $card_number ) ) {
			$card_type     = GFCommon::get_card_type( $card_number );

			if ( empty( $security_code ) ) {
				$this->failed_validation  = true;
				$this->validation_message = esc_html__( "Please enter your card's security code.", 'gravityforms' );
			} elseif ( ! $card_type ) {
				$this->failed_validation  = true;
				$this->validation_message = esc_html__( 'Invalid credit card number.', 'gravityforms' );
			} elseif ( ! $this->is_card_supported( $card_type['slug'] ) ) {
				$this->failed_validation  = true;
				$this->validation_message = $card_type['name'] . ' ' . esc_html__( 'is not supported. Please enter one of the supported credit cards.', 'gravityforms' );
			}
		}
	}

	public function is_card_supported( $card_slug ) {
		$supported_cards = $this->creditCards;
		$default_cards   = array( 'amex', 'discover', 'mastercard', 'visa' );

		if ( ! empty( $supported_cards ) && in_array( $card_slug, $supported_cards ) ) {
			return true;
		} elseif ( empty( $supported_cards ) && in_array( $card_slug, $default_cards ) ) {
			return true;
		}

		return false;

	}

	public function get_value_submission( $field_values, $get_from_post_global_var = true ) {

		if ( $get_from_post_global_var ) {
			$value[ $this->id . '.1' ] = $this->get_input_value_submission( 'input_' . $this->id . '_1', rgar( $this->inputs[0], 'name' ), $field_values, true );
			$value[ $this->id . '.2' ] = $this->get_input_value_submission( 'input_' . $this->id . '_2', rgar( $this->inputs[1], 'name' ), $field_values, true );
			$value[ $this->id . '.3' ] = $this->get_input_value_submission( 'input_' . $this->id . '_3', rgar( $this->inputs[3], 'name' ), $field_values, true );
			$value[ $this->id . '.4' ] = $this->get_input_value_submission( 'input_' . $this->id . '_4', rgar( $this->inputs[4], 'name' ), $field_values, true );
			$value[ $this->id . '.5' ] = $this->get_input_value_submission( 'input_' . $this->id . '_5', rgar( $this->inputs[5], 'name' ), $field_values, true );
		} else {
			$value = $this->get_input_value_submission( 'input_' . $this->id, $this->inputName, $field_values, $get_from_post_global_var );
		}

		return $value;
	}

	public function get_field_input( $form, $value = '', $entry = null ) {
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$form_id  = $form['id'];
		$id       = intval( $this->id );
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";
		$form_id  = ( $is_entry_detail || $is_form_editor ) && empty( $form_id ) ? rgget( 'id' ) : $form_id;

		$disabled_text = $is_form_editor ? "disabled='disabled'" : '';
		$class_suffix  = $is_entry_detail ? '_admin' : '';


		$form_sub_label_placement  = rgar( $form, 'subLabelPlacement' );
		$field_sub_label_placement = $this->subLabelPlacement;
		$is_sub_label_above        = $field_sub_label_placement == 'above' || ( empty( $field_sub_label_placement ) && $form_sub_label_placement == 'above' );
		$sub_label_class_attribute = $field_sub_label_placement == 'hidden_label' ? "class='hidden_sub_label screen-reader-text'" : '';

		$card_number      = '';
		$card_name        = '';
		$expiration_month = '';
		$expiration_year  = '';
		$security_code    = '';
		$autocomplete     = RGFormsModel::is_html5_enabled() ? "autocomplete='off'" : '';

		if ( is_array( $value ) ) {
			$card_number     = esc_attr( rgget( $this->id . '.1', $value ) );
			$card_name       = esc_attr( rgget( $this->id . '.5', $value ) );
			$expiration_date = rgget( $this->id . '.2', $value );
			if ( ! is_array( $expiration_date ) && ! empty( $expiration_date ) ) {
				$expiration_date = explode( '/', $expiration_date );
			}

			if ( is_array( $expiration_date ) && count( $expiration_date ) == 2 ) {
				$expiration_month = $expiration_date[0];
				$expiration_year  = $expiration_date[1];
			}

			$security_code = esc_attr( rgget( $this->id . '.3', $value ) );
		}

		$action = ! ( $is_entry_detail || $is_form_editor ) ? "gformMatchCard(\"{$field_id}_1\");" : '';

		$onchange = "onchange='{$action}'";
		$onkeyup  = "onkeyup='{$action}'";

		$card_icons = '';
		$cards      = GFCommon::get_card_types();
		$card_style = $this->creditCardStyle ? $this->creditCardStyle : 'style1';

		foreach ( $cards as $card ) {

			$style = '';
			if ( $this->is_card_supported( $card['slug'] ) ) {
				$print_card = true;
			} elseif ( $is_form_editor || $is_entry_detail ) {
				$print_card = true;
				$style      = "style='display:none;'";
			} else {
				$print_card = false;
			}

			if ( $print_card ) {
				$card_icons .= "<div class='gform_card_icon gform_card_icon_{$card['slug']}' {$style}>{$card['name']}</div>";
			}
		}

		$payment_methods = apply_filters( 'gform_payment_methods', array(), $this, $form_id );
		$payment_options = '';
		if ( is_array( $payment_methods ) ) {
			foreach ( $payment_methods as $payment_method ) {
				$checked = rgpost( 'gform_payment_method' ) == $payment_method['key'] ? "checked='checked'" : '';
				$payment_options .= "<div class='gform_payment_option gform_payment_{$payment_method['key']}'><input type='radio' name='gform_payment_method' value='{$payment_method['key']}' id='gform_payment_method_{$payment_method['key']}' onclick='gformToggleCreditCard();' onkeypress='gformToggleCreditCard();' {$checked}/> {$payment_method['label']}</div>";
			}
		}
		$checked           = rgpost( 'gform_payment_method' ) == 'creditcard' || rgempty( 'gform_payment_method' ) ? "checked='checked'" : '';
		$card_radio_button = empty( $payment_options ) ? '' : "<input type='radio' name='gform_payment_method' id='gform_payment_method_creditcard' value='creditcard' onclick='gformToggleCreditCard();' onkeypress='gformToggleCreditCard();' {$checked}/>";
		$card_icons        = "{$payment_options}<div class='gform_card_icon_container gform_card_icon_{$card_style}'>{$card_radio_button}{$card_icons}</div>";

		//card number fields
		$tabindex                = $this->get_tabindex();
		$card_number_field_input = GFFormsModel::get_input( $this, $this->id . '.1' );
		$html5_output            = ! is_admin() && GFFormsModel::is_html5_enabled() ? "pattern='[0-9]*' title='" . esc_attr__( 'Only digits are allowed', 'gravityforms' ) . "'" : '';
		$card_number_label       = rgar( $card_number_field_input, 'customLabel' ) != '' ? $card_number_field_input['customLabel'] : esc_html__( 'Card Number', 'gravityforms' );
		$card_number_label       = gf_apply_filters( array( 'gform_card_number', $form_id ), $card_number_label, $form_id );

		$card_number_placeholder = $this->get_input_placeholder_attribute( $card_number_field_input );
		if ( $is_sub_label_above ) {
			$card_field = "<span class='ginput_full{$class_suffix}' id='{$field_id}_1_container' >
                                    {$card_icons}
                                    <label for='{$field_id}_1' id='{$field_id}_1_label' {$sub_label_class_attribute}>{$card_number_label}</label>
                                    <input type='text' name='input_{$id}.1' id='{$field_id}_1' value='{$card_number}' {$tabindex} {$disabled_text} {$onchange} {$onkeyup} {$autocomplete} {$html5_output} {$card_number_placeholder}/>
                                 </span>";
		} else {
			$card_field = "<span class='ginput_full{$class_suffix}' id='{$field_id}_1_container' >
                                    {$card_icons}
                                    <input type='text' name='input_{$id}.1' id='{$field_id}_1' value='{$card_number}' {$tabindex} {$disabled_text} {$onchange} {$onkeyup} {$autocomplete} {$html5_output} {$card_number_placeholder}/>
                                    <label for='{$field_id}_1' id='{$field_id}_1_label' {$sub_label_class_attribute}>{$card_number_label}</label>
                                 </span>";
		}

		//expiration date field
		$expiration_month_tab_index   = $this->get_tabindex();
		$expiration_year_tab_index    = $this->get_tabindex();
		$expiration_month_input       = GFFormsModel::get_input( $this, $this->id . '.2_month' );
		$expiration_month_placeholder = $this->get_input_placeholder_value( $expiration_month_input );
		$expiration_year_input        = GFFormsModel::get_input( $this, $this->id . '.2_year' );
		$expiration_year_placeholder  = $this->get_input_placeholder_value( $expiration_year_input );
		$expiration_months            = $this->get_expiration_months( $expiration_month, $expiration_month_placeholder );
		$expiration_years             = $this->get_expiration_years( $expiration_year, $expiration_year_placeholder );
		$expiration_label             = rgar( $expiration_month_input, 'customLabel' ) != '' ? $expiration_month_input['customLabel'] : esc_html__( 'Expiration Date', 'gravityforms' );
		$expiration_label             = gf_apply_filters( array( 'gform_card_expiration', $form_id ), $expiration_label, $form_id );
		if ( $is_sub_label_above ) {
			$expiration_field = "<span class='ginput_full{$class_suffix} ginput_cardextras' id='{$field_id}_2_container'>
                                            <span class='ginput_cardinfo_left{$class_suffix}' id='{$field_id}_2_cardinfo_left'>
                                                <span class='ginput_card_expiration_container ginput_card_field'>
                                                    <label for='{$field_id}_2_month' {$sub_label_class_attribute}>{$expiration_label}</label>
                                                    <select name='input_{$id}.2[]' id='{$field_id}_2_month' {$expiration_month_tab_index} {$disabled_text} class='ginput_card_expiration ginput_card_expiration_month'>
                                                        {$expiration_months}
                                                    </select>
                                                    <select name='input_{$id}.2[]' id='{$field_id}_2_year' {$expiration_year_tab_index} {$disabled_text} class='ginput_card_expiration ginput_card_expiration_year'>
                                                        {$expiration_years}
                                                    </select>
                                                </span>
                                            </span>";

		} else {
			$expiration_field = "<span class='ginput_full{$class_suffix} ginput_cardextras' id='{$field_id}_2_container'>
                                            <span class='ginput_cardinfo_left{$class_suffix}' id='{$field_id}_2_cardinfo_left'>
                                                <span class='ginput_card_expiration_container ginput_card_field'>
                                                    <select name='input_{$id}.2[]' id='{$field_id}_2_month' {$expiration_month_tab_index} {$disabled_text} class='ginput_card_expiration ginput_card_expiration_month'>
                                                        {$expiration_months}
                                                    </select>
                                                    <select name='input_{$id}.2[]' id='{$field_id}_2_year' {$expiration_year_tab_index} {$disabled_text} class='ginput_card_expiration ginput_card_expiration_year'>
                                                    {$expiration_years}
                                                    </select>
                                                    <label for='{$field_id}_2_month' {$sub_label_class_attribute}>{$expiration_label}</label>
                                                </span>
                                            </span>";
		}
		//security code field
		$tabindex                  = $this->get_tabindex();
		$security_code_field_input = GFFormsModel::get_input( $this, $this->id . '.3' );
		$security_code_label       = rgar( $security_code_field_input, 'customLabel' ) != '' ? $security_code_field_input['customLabel'] : esc_html__( 'Security Code', 'gravityforms' );
		$security_code_label       = gf_apply_filters( array( 'gform_card_security_code', $form_id ), $security_code_label, $form_id );
		$html5_output              = GFFormsModel::is_html5_enabled() ? "pattern='[0-9]*' title='" . esc_attr__( 'Only digits are allowed', 'gravityforms' ) . "'" : '';
		$security_code_placeholder = $this->get_input_placeholder_attribute( $security_code_field_input );
		if ( $is_sub_label_above ) {
			$security_field = "<span class='ginput_cardinfo_right{$class_suffix}' id='{$field_id}_2_cardinfo_right'>
                                                <label for='{$field_id}_3' {$sub_label_class_attribute}>$security_code_label</label>
                                                <input type='text' name='input_{$id}.3' id='{$field_id}_3' {$tabindex} {$disabled_text} class='ginput_card_security_code' value='{$security_code}' {$autocomplete} {$html5_output} {$security_code_placeholder} />
                                                <span class='ginput_card_security_code_icon'>&nbsp;</span>
                                             </span>
                                        </span>";
		} else {
			$security_field = "<span class='ginput_cardinfo_right{$class_suffix}' id='{$field_id}_2_cardinfo_right'>
                                                <input type='text' name='input_{$id}.3' id='{$field_id}_3' {$tabindex} {$disabled_text} class='ginput_card_security_code' value='{$security_code}' {$autocomplete} {$html5_output} {$security_code_placeholder}/>
                                                <span class='ginput_card_security_code_icon'>&nbsp;</span>
                                                <label for='{$field_id}_3' {$sub_label_class_attribute}>$security_code_label</label>
                                             </span>
                                        </span>";
		}

		$tabindex              = $this->get_tabindex();
		$card_name_field_input = GFFormsModel::get_input( $this, $this->id . '.5' );
		$card_name_label       = rgar( $card_name_field_input, 'customLabel' ) != '' ? $card_name_field_input['customLabel'] : esc_html__( 'Cardholder Name', 'gravityforms' );
		$card_name_label       = gf_apply_filters( array( 'gform_card_name', $form_id ), $card_name_label, $form_id );

		$card_name_placeholder = $this->get_input_placeholder_attribute( $card_name_field_input );
		if ( $is_sub_label_above ) {
			$card_name_field = "<span class='ginput_full{$class_suffix}' id='{$field_id}_5_container'>
                                            <label for='{$field_id}_5' id='{$field_id}_5_label' {$sub_label_class_attribute}>{$card_name_label}</label>
                                            <input type='text' name='input_{$id}.5' id='{$field_id}_5' value='{$card_name}' {$tabindex} {$disabled_text} {$card_name_placeholder}/>
                                        </span>";
		} else {
			$card_name_field = "<span class='ginput_full{$class_suffix}' id='{$field_id}_5_container'>
                                            <input type='text' name='input_{$id}.5' id='{$field_id}_5' value='{$card_name}' {$tabindex} {$disabled_text} {$card_name_placeholder}/>
                                            <label for='{$field_id}_5' id='{$field_id}_5_label' {$sub_label_class_attribute}>{$card_name_label}</label>
                                        </span>";
		}

		return "<div class='ginput_complex{$class_suffix} ginput_container ginput_container_creditcard' id='{$field_id}'>" . $card_field . $expiration_field . $security_field . $card_name_field . ' </div>';

	}

	public function get_field_label_class(){
		return 'gfield_label gfield_label_before_complex';
	}

	private function get_expiration_months( $selected_month, $placeholder ) {
		if ( empty( $placeholder ) ) {
			$placeholder = esc_html__( 'Month', 'gravityforms' );
		}
		$str = "<option value=''>{$placeholder}</option>";
		for ( $i = 1; $i < 13; $i ++ ) {
			$selected = intval( $selected_month ) == $i ? "selected='selected'" : '';
			$month    = str_pad( $i, 2, '0', STR_PAD_LEFT );
			$str .= "<option value='{$i}' {$selected}>{$month}</option>";
		}

		return $str;
	}

	private function get_expiration_years( $selected_year, $placeholder ) {
		if ( empty( $placeholder ) ) {
			$placeholder = esc_html__( 'Year', 'gravityforms' );
		}
		$str  = "<option value=''>{$placeholder}</option>";
		$year = intval( date( 'Y' ) );
		for ( $i = $year; $i < ( $year + 20 ); $i ++ ) {
			$selected = intval( $selected_year ) == $i ? "selected='selected'" : '';
			$str .= "<option value='{$i}' {$selected}>{$i}</option>";
		}

		return $str;
	}

	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {

		if ( is_array( $value ) ) {
			$card_number = trim( rgget( $this->id . '.1', $value ) );
			$card_type   = trim( rgget( $this->id . '.4', $value ) );
			$separator   = $format == 'html' ? '<br/>' : "\n";

			return empty( $card_number ) ? '' : $card_type . $separator . $card_number;
		} else {
			return '';
		}
	}

	public function get_form_inline_script_on_page_render( $form ) {

		$field_id = "input_{$form['id']}_{$this->id}";

		if ( $this->forceSSL && ! GFCommon::is_ssl() && ! GFCommon::is_preview() ) {
			$script = "document.location.href='" . esc_js( RGFormsModel::get_current_page_url( true ) ) . "';";
		} else {
			$script = "jQuery(document).ready(function(){ { gformMatchCard(\"{$field_id}_1\"); } } );";
		}

		$card_rules = $this->get_credit_card_rules();
		$script     = "if(!window['gf_cc_rules']){window['gf_cc_rules'] = new Array(); } window['gf_cc_rules'] = " . GFCommon::json_encode( $card_rules ) . "; $script";

		return $script;
	}

	public function get_credit_card_rules() {

		$cards = GFCommon::get_card_types();
		//$supported_cards = //TODO: Only include enabled cards
		$rules = array();

		foreach ( $cards as $card ) {
			$prefixes = explode( ',', $card['prefixes'] );
			foreach ( $prefixes as $prefix ) {
				$rules[ $card['slug'] ][] = $prefix;
			}
		}

		return $rules;
	}

	public function get_entry_inputs() {
		$inputs = array();
		// only store month and card number input values
		foreach ( $this->inputs as $input ) {
			if ( in_array( $input['id'], array( $this->id . '.1', $this->id . '.4' ) ) ) {
				$inputs[] = $input;
			}
		}

		return $inputs;
	}

	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {

		//saving last 4 digits of credit card
		list( $input_token, $field_id_token, $input_id ) = rgexplode( '_', $input_name, 3 );
		if ( $input_id == '1' ) {
			$value              = str_replace( ' ', '', $value );
			$card_number_length = strlen( $value );
			$value              = substr( $value, - 4, 4 );
			$value              = str_pad( $value, $card_number_length, 'X', STR_PAD_LEFT );
		} elseif ( $input_id == '4' ) {

			$value = rgpost( "input_{$field_id_token}_4" );

			if ( ! $value ) {
				$card_number = rgpost( "input_{$field_id_token}_1" );
				$card_type   = GFCommon::get_card_type( $card_number );
				$value       = $card_type ? $card_type['name'] : '';
			}
		} else {
			$value = '';
		}

		return $this->sanitize_entry_value( $value, $form['id'] );
	}

	/**
	 * Upgrades inputs, if needed.
	 *
	 * @since  2.1.2.7
	 * @access public
	 * @see    GF_Field::post_convert_field()
	 *
	 * @uses GF_Field::post_convert_field()
	 * @uses GF_Field_CreditCard::maybe_upgrade_inputs()
	 *
	 * @return void
	 */
	public function post_convert_field() {
		parent::post_convert_field();
		$this->maybe_upgrade_inputs();
	}

	/**
	 * GF1.8 and earlier used 5 inputs (1 input for the expiration date); GF1.9 changed to 6 inputs (the expiration month and year now separate); upgrade those fields still using the older configuration.
	 */
	public function maybe_upgrade_inputs() {
		$inputs    = $this->inputs;
		$exp_input = $inputs[1];
		$exp_id    = $this->id . '.2';

		if ( count( $inputs ) == 5 && $exp_input['id'] == $exp_id ) {
			$new_inputs = array(
				array(
					'id'           => $exp_id . '_month',
					'label'        => esc_html__( 'Expiration Month', 'gravityforms' ),
					'defaultLabel' => $exp_input['label']
				),
				array(
					'id'    => $exp_id . '_year',
					'label' => esc_html__( 'Expiration Year', 'gravityforms' ),
				)
			);

			array_splice( $inputs, 1, 1, $new_inputs );
			$this->inputs = $inputs;
		}
	}
}

GF_Fields::register( new GF_Field_CreditCard() );
class-gf-field-radio.php000066600000031131151264036140011136 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_Radio extends GF_Field {

	public $type = 'radio';

	public function get_form_editor_field_title() {
		return esc_attr__( 'Radio Buttons', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'choices_setting',
			'rules_setting',
			'visibility_setting',
			'duplicate_setting',
			'description_setting',
			'css_class_setting',
			'other_choice_setting',
		);
	}

	public function is_conditional_logic_supported() {
		return true;
	}

	public function validate( $value, $form ) {
		if ( $this->isRequired && $this->enableOtherChoice && rgpost( "input_{$this->id}" ) == 'gf_other_choice' ) {
			if ( empty( $value ) || strtolower( $value ) == strtolower( GFCommon::get_other_choice_value( $this ) ) ) {
				$this->failed_validation  = true;
				$this->validation_message = empty( $this->errorMessage ) ? esc_html__( 'This field is required.', 'gravityforms' ) : $this->errorMessage;
			}
		}
	}

	public function get_first_input_id( $form ) {
		return '';
	}

	public function get_field_input( $form, $value = '', $entry = null ) {

		$form_id         = $form['id'];
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$id            = $this->id;
		$field_id      = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";
		$disabled_text = $is_form_editor ? 'disabled="disabled"' : '';

		return sprintf( "<div class='ginput_container ginput_container_radio'><ul class='gfield_radio' id='%s'>%s</ul></div>", $field_id, $this->get_radio_choices( $value, $disabled_text, $form_id ) );

	}

	public function get_radio_choices( $value = '', $disabled_text = '', $form_id = 0 ) {
		$choices = '';

		if ( is_array( $this->choices ) ) {
			$is_entry_detail    = $this->is_entry_detail();
			$is_form_editor     = $this->is_form_editor();
			$is_admin           = $is_entry_detail || $is_form_editor;

			$field_choices      = $this->choices;
			$needs_other_choice = $this->enableOtherChoice;
			$editor_limited     = false;

			$choice_id = 0;
			$count     = 1;

			foreach ( $field_choices as $choice ) {
				if ( rgar( $choice, 'isOtherChoice' ) ) {
					if ( ! $needs_other_choice ) {
						continue;
					}
					$needs_other_choice = false;
				}

				$choices .= $this->get_choice_html( $choice, $choice_id, $value, $disabled_text, $is_admin );

				if ( $is_form_editor && $count >= 5 ) {
					$editor_limited = true;
					break;
				}

				$count ++;
			}

			if ( $needs_other_choice ) {
				$other_choice    = array(
					'text'          => GFCommon::get_other_choice_value( $this ),
					'value'         => 'gf_other_choice',
					'isSelected'    => false,
					'isOtherChoice' => true,
				);
				$field_choices[] = $other_choice;

				if ( ! $is_form_editor || ! $editor_limited ) {
					$choices .= $this->get_choice_html( $other_choice, $choice_id, $value, $disabled_text, $is_admin );
					$count ++;
				}
			}

			$total = sizeof( $field_choices );
			if ( $is_form_editor && ( $count < $total ) ) {
				$choices .= "<li class='gchoice_total'>" . sprintf( esc_html__( '%d of %d items shown. Edit field to view all', 'gravityforms' ), $count, $total ) . '</li>';
			}
		}

		/**
		 * Allows the HTML for multiple choices to be overridden.
		 *
		 * @since unknown
		 *
		 * @param string         $choices The choices HTML.
		 * @param GF_Field_Radio $field   The current field object.
		 */
		return gf_apply_filters( array( 'gform_field_choices', $this->formId ), $choices, $this );
	}

	/**
	 * Returns the choice HTML.
	 *
	 * @since 2.4.17
	 *
	 * @param array  $choice        The choice properties.
	 * @param int    &$choice_id    The choice number.
	 * @param string $value         The current field value.
	 * @param string $disabled_text The disabled attribute or an empty string.
	 * @param bool   $is_admin      Indicates if this is the form editor or entry detail page.
	 *
	 * @return string
	 */
	public function get_choice_html( $choice, &$choice_id, $value, $disabled_text, $is_admin ) {
		$form_id = absint( $this->formId );

		if ( $is_admin || $form_id == 0 ) {
			$id = $this->id . '_' . $choice_id ++;
		} else {
			$id = $form_id . '_' . $this->id . '_' . $choice_id ++;
		}

		$field_value = ! empty( $choice['value'] ) || $this->enableChoiceValue ? $choice['value'] : $choice['text'];

		if ( $this->enablePrice ) {
			$price       = rgempty( 'price', $choice ) ? 0 : GFCommon::to_number( rgar( $choice, 'price' ) );
			$field_value .= '|' . $price;
		}

		if ( rgblank( $value ) && rgget( 'view' ) != 'entry' ) {
			$checked = rgar( $choice, 'isSelected' ) ? "checked='checked'" : '';
		} else {
			$checked = GFFormsModel::choice_value_match( $this, $choice, $value ) ? "checked='checked'" : '';
		}

		$tabindex    = $this->get_tabindex();
		$label       = sprintf( "<label for='choice_%s' id='label_%s'>%s</label>", $id, $id, $choice['text'] );
		$input_focus = '';

		// Handle 'other' choice.
		if ( $this->enableOtherChoice && rgar( $choice, 'isOtherChoice' ) ) {
			$other_default_value = empty( $choice['text'] ) ? GFCommon::get_other_choice_value( $this ) : $choice['text'];

			$onfocus = ! $is_admin ? 'jQuery(this).prev("input")[0].click(); if(jQuery(this).val() == "' . $other_default_value . '") { jQuery(this).val(""); }' : '';
			$onblur  = ! $is_admin ? 'if(jQuery(this).val().replace(" ", "") == "") { jQuery(this).val("' . $other_default_value . '"); }' : '';

			$input_focus  = ! $is_admin ? "onfocus=\"jQuery(this).next('input').focus();\"" : '';
			$value_exists = GFFormsModel::choices_value_match( $this, $this->choices, $value );

			if ( $value == 'gf_other_choice' && rgpost( "input_{$this->id}_other" ) ) {
				$other_value = rgpost( "input_{$this->id}_other" );
			} elseif ( ! $value_exists && ! empty( $value ) ) {
				$other_value = $value;
				$value       = 'gf_other_choice';
				$checked     = "checked='checked'";
			} else {
				$other_value = $other_default_value;
			}

			$label = "<input id='input_{$this->formId}_{$this->id}_other' name='input_{$this->id}_other' type='text' value='" . esc_attr( $other_value ) . "' aria-label='" . esc_attr__( 'Other', 'gravityforms' ) . "' onfocus='$onfocus' onblur='$onblur' $tabindex $disabled_text />";
		}

		$choice_markup = sprintf( "<li class='gchoice_$id'><input name='input_%d' type='radio' value='%s' %s id='choice_%s' $tabindex %s %s />%s</li>", $this->id, esc_attr( $field_value ), $checked, $id, $disabled_text, $input_focus, $label );

		/**
		 * Allows the HTML for a specific choice to be overridden.
		 *
		 * @since 1.9.6
		 * @since 1.9.12 Added the field specific version.
		 * @since 2.4.17 Moved from GF_Field_Radio::get_radio_choices().
		 *
		 * @param string         $choice_markup The choice HTML.
		 * @param array          $choice        The choice properties.
		 * @param GF_Field_Radio $field         The current field object.
		 * @param string         $value         The current field value.
		 */
		return gf_apply_filters( array( 'gform_field_choice_markup_pre_render', $this->formId, $this->id ), $choice_markup, $choice, $this, $value );
	}

	public function get_value_default() {
		return $this->is_form_editor() ? $this->defaultValue : GFCommon::replace_variables_prepopulate( $this->defaultValue );
	}

	public function get_value_submission( $field_values, $get_from_post_global_var = true ) {

		$value = $this->get_input_value_submission( 'input_' . $this->id, $this->inputName, $field_values, $get_from_post_global_var );
		if ( $value == 'gf_other_choice' ) {
			//get value from text box
			$value = $this->get_input_value_submission( 'input_' . $this->id . '_other', $this->inputName, $field_values, $get_from_post_global_var );
		}

		return $value;
	}

	public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) {
		return wp_kses_post( GFCommon::selection_display( $value, $this, $entry['currency'] ) );
	}

	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {

		return wp_kses_post( GFCommon::selection_display( $value, $this, $currency, $use_text ) );
	}

	/**
	 * Gets merge tag values.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GFCommon::to_money()
	 * @uses GFCommon::format_post_category()
	 * @uses GFFormsModel::is_field_hidden()
	 * @uses GFFormsModel::get_choice_text()
	 * @uses GFCommon::format_variable_value()
	 * @uses GFCommon::implode_non_blank()
	 *
	 * @param array|string $value      The value of the input.
	 * @param string       $input_id   The input ID to use.
	 * @param array        $entry      The Entry Object.
	 * @param array        $form       The Form Object
	 * @param string       $modifier   The modifier passed.
	 * @param array|string $raw_value  The raw value of the input.
	 * @param bool         $url_encode If the result should be URL encoded.
	 * @param bool         $esc_html   If the HTML should be escaped.
	 * @param string       $format     The format that the value should be.
	 * @param bool         $nl2br      If the nl2br function should be used.
	 *
	 * @return string The processed merge tag.
	 */
	public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) {
		$modifiers       = $this->get_modifiers();
		$use_value       = in_array( 'value', $modifiers );
		$format_currency = ! $use_value && in_array( 'currency', $modifiers );
		$use_price       = $format_currency || ( ! $use_value && in_array( 'price', $modifiers ) );

		if ( is_array( $raw_value ) && (string) intval( $input_id ) != $input_id ) {
			$items = array( $input_id => $value ); // Float input Ids. (i.e. 4.1 ). Used when targeting specific checkbox items.
		} elseif ( is_array( $raw_value ) ) {
			$items = $raw_value;
		} else {
			$items = array( $input_id => $raw_value );
		}

		$ary = array();

		foreach ( $items as $input_id => $item ) {
			if ( $use_value ) {
				list( $val, $price ) = rgexplode( '|', $item, 2 );
			} elseif ( $use_price ) {
				list( $name, $val ) = rgexplode( '|', $item, 2 );
				if ( $format_currency ) {
					$val = GFCommon::to_money( $val, rgar( $entry, 'currency' ) );
				}
			} elseif ( $this->type == 'post_category' ) {
				$use_id     = strtolower( $modifier ) == 'id';
				$item_value = GFCommon::format_post_category( $item, $use_id );

				$val = RGFormsModel::is_field_hidden( $form, $this, array(), $entry ) ? '' : $item_value;
			} else {
				$val = RGFormsModel::is_field_hidden( $form, $this, array(), $entry ) ? '' : RGFormsModel::get_choice_text( $this, $raw_value, $input_id );
			}

			$ary[] = GFCommon::format_variable_value( $val, $url_encode, $esc_html, $format );
		}

		return GFCommon::implode_non_blank( ', ', $ary );
	}

	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {

		if ( $this->enableOtherChoice && $value == 'gf_other_choice' ) {
			$value = rgpost( "input_{$this->id}_other" );
		}

		$value = $this->sanitize_entry_value( $value, $form['id'] );

		return $value;
	}

	public function allow_html() {
		return true;
	}

	public function get_value_export( $entry, $input_id = '', $use_text = false, $is_csv = false ) {
		if ( empty( $input_id ) ) {
			$input_id = $this->id;
		}

		$value = rgar( $entry, $input_id );

		return $is_csv ? $value : GFCommon::selection_display( $value, $this, rgar( $entry, 'currency' ), $use_text );
	}

	/**
	 * Strip scripts and some HTML tags.
	 *
	 * @param string $value The field value to be processed.
	 * @param int $form_id The ID of the form currently being processed.
	 *
	 * @return string
	 */
	public function sanitize_entry_value( $value, $form_id ) {

		if ( is_array( $value ) ) {
			return '';
		}

		$allowable_tags = $this->get_allowable_tags( $form_id );

		if ( $allowable_tags !== true ) {
			$value = strip_tags( $value, $allowable_tags );
		}

		$allowed_protocols = wp_allowed_protocols();
		$value             = wp_kses_no_null( $value, array( 'slash_zero' => 'keep' ) );
		$value             = wp_kses_hook( $value, 'post', $allowed_protocols );
		$value             = wp_kses_split( $value, 'post', $allowed_protocols );

		return $value;
	}

	// # FIELD FILTER UI HELPERS ---------------------------------------------------------------------------------------

	/**
	 * Returns the filter operators for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_operators() {
		$operators = $this->type == 'product' ? array( 'is' ) : array( 'is', 'isnot', '>', '<' );

		return $operators;
	}

}

GF_Fields::register( new GF_Field_Radio() );
class-gf-field-post-content.php000066600000002530151264036140012476 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}

require_once( plugin_dir_path( __FILE__ ) . 'class-gf-field-textarea.php' );

class GF_Field_Post_Content extends GF_Field_Textarea {

	public $type = 'post_content';

	public function get_form_editor_field_title() {
		return esc_attr__( 'Body', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'post_content_template_setting',
			'post_status_setting',
			'post_category_setting',
			'post_author_setting',
			'post_format_setting',
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'size_setting',
			'maxlen_setting',
			'rules_setting',
			'visibility_setting',
			'default_value_textarea_setting',
			'placeholder_textarea_setting',
			'description_setting',
			'css_class_setting',
			'rich_text_editor_setting',
		);
	}

	public function allow_html() {
		return true;
	}

	/**
	 * Filter the rich_editing option for the current user.
	 *
	 * @since 2.2.5.14
	 *
	 * @param string $value The value of the rich_editing option for the current user.
	 *
	 * @return string
	 */
	public function filter_user_option_rich_editing( $value ) {
		return is_user_logged_in() ? $value : 'true';
	}

}

GF_Fields::register( new GF_Field_Post_Content() );
class-gf-field-calculation.php000066600000012353151264036140012343 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_Calculation extends GF_Field {

	public $type = 'calculation';

	function get_form_editor_field_settings() {
		return array(
			'disable_quantity_setting',
			'rules_setting',
			'duplicate_setting',
			'calculation_setting',
			'conditional_logic_field_setting',
		);
	}

	public function get_form_editor_button() {
		return array();
	}

	public function validate( $value, $form ) {
		$quantity_id = $this->id . '.3';
		$quantity    = rgget( $quantity_id, $value );

		if ( $this->isRequired && rgblank( $quantity ) && ! $this->disableQuantity ) {
			$this->failed_validation  = true;
			$this->validation_message = empty($this->errorMessage) ? esc_html__( 'This field is required.', 'gravityforms' ) : $this->errorMessage;
		} elseif ( ! empty( $quantity ) && ( ! is_numeric( $quantity ) || intval( $quantity ) != floatval( $quantity ) || intval( $quantity ) < 0 ) ) {
			$this->failed_validation  = true;
			$this->validation_message = esc_html__( 'Please enter a valid quantity', 'gravityforms' );
		}
	}

	public function get_field_input( $form, $value = '', $entry = null ) {
		$form_id         = $form['id'];
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$id          = (int) $this->id;
		$field_id    = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		$product_name = ! is_array( $value ) || empty( $value[ $this->id . '.1' ] ) ? esc_attr( $this->label ) : esc_attr( $value[ $this->id . '.1' ] );
		$price        = ! is_array( $value ) || empty( $value[ $this->id . '.2' ] ) ? $this->basePrice : esc_attr( $value[ $this->id . '.2' ] );
		$quantity     = is_array( $value ) ? esc_attr( $value[ $this->id . '.3' ] ) : '';

		if ( empty( $price ) ) {
			$price = 0;
		}

		$has_quantity = sizeof( GFCommon::get_product_fields_by_type( $form, array( 'quantity' ), $this->id ) ) > 0;
		if ( $has_quantity ) {
			$this->disableQuantity = true;
		}

		$currency = $is_entry_detail && ! empty( $entry ) ? $entry['currency'] : '';

		$quantity_field = '';
		$disabled_text  = $is_form_editor ? 'disabled="disabled"' : '';

		$qty_input_type = GFFormsModel::is_html5_enabled() ? 'number' : 'text';

		$product_quantity_sub_label = gf_apply_filters( array( 'gform_product_quantity', $form_id, $this->id ), esc_html__( 'Quantity:', 'gravityforms' ), $form_id );

		if ( $is_entry_detail || $is_form_editor  ) {
			$style          = $this->disableQuantity ? "style='display:none;'" : '';
			$quantity_field = " <span class='ginput_quantity_label' {$style}>{$product_quantity_sub_label}</span> <input type='{$qty_input_type}' name='input_{$id}.3' value='{$quantity}' id='ginput_quantity_{$form_id}_{$this->id}' class='ginput_quantity' size='10' {$disabled_text}/>";
		} elseif ( ! $this->disableQuantity ) {
			$tabindex  = $this->get_tabindex();
			$quantity_field .= " <span class='ginput_quantity_label'>" . $product_quantity_sub_label . "</span> <input type='{$qty_input_type}' name='input_{$id}.3' value='{$quantity}' id='ginput_quantity_{$form_id}_{$this->id}' class='ginput_quantity' size='10' {$tabindex} {$disabled_text}/>";
		} else {
			if ( ! is_numeric( $quantity ) ) {
				$quantity = 1;
			}

			if ( ! $has_quantity ) {
				$quantity_field .= "<input type='hidden' name='input_{$id}.3' value='{$quantity}' class='ginput_quantity_{$form_id}_{$this->id} gform_hidden' />";
			}
		}

		return "<div class='ginput_container ginput_container_product_calculation'>
					<input type='hidden' name='input_{$id}.1' value='{$product_name}' class='gform_hidden' />
					<span class='ginput_product_price_label'>" . gf_apply_filters( array( 'gform_product_price', $form_id, $this->id ), esc_html__( 'Price', 'gravityforms' ), $form_id ) . ":</span> <span class='ginput_product_price' id='{$field_id}'>" . esc_html( GFCommon::to_money( $price, $currency ) ) . "</span>
					<input type='hidden' name='input_{$id}.2' id='ginput_base_price_{$form_id}_{$this->id}' class='gform_hidden' value='" . esc_attr( $price ) . "'/>
					{$quantity_field}
				</div>";
	}

	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
		if ( is_array( $value ) && ! empty( $value ) ) {
			$product_name = trim( $value[ $this->id . '.1' ] );
			$price        = trim( $value[ $this->id . '.2' ] );
			$quantity     = trim( $value[ $this->id . '.3' ] );

			$product = $product_name . ', ' . esc_html__( 'Qty: ', 'gravityforms' ) . $quantity . ', ' . esc_html__( 'Price: ', 'gravityforms' ) . $price;

			return $product;
		} else {
			return '';
		}
	}

	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {
		// ignore submitted value and recalculate price in backend
		list( $prefix, $field_id, $input_id ) = rgexplode( '_', $input_name, 3 );
		if ( $input_id == 2 ) {
			$currency = new RGCurrency( GFCommon::get_currency() );
			$lead     = empty( $lead ) ? RGFormsModel::get_lead( $lead_id ) : $lead;
			$value    = $currency->to_money( GFCommon::calculate( $this, $form, $lead ) );
		}
		return $value;
	}

	public function sanitize_settings() {
		parent::sanitize_settings();
		$this->enableCalculation = (bool) $this->enableCalculation;

	}


}

GF_Fields::register( new GF_Field_Calculation() );class-gf-field-hidden.php000066600000004764151264036140011307 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_Hidden extends GF_Field {

	public $type = 'hidden';

	public function get_form_editor_field_title() {
		return esc_attr__( 'Hidden', 'gravityforms' );
	}

	public function is_conditional_logic_supported(){
		return true;
	}

	function get_form_editor_field_settings() {
		return array(
			'prepopulate_field_setting',
			'label_setting',
			'default_value_setting',
		);
	}

	public function get_field_input( $form, $value = '', $entry = null ) {
		$form_id         = $form['id'];
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$id       = (int) $this->id;
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		$disabled_text = $is_form_editor ? 'disabled="disabled"' : '';

		$field_type         = $is_entry_detail || $is_form_editor ? 'text' : 'hidden';
		$class_attribute    = $is_entry_detail || $is_form_editor ? '' : "class='gform_hidden'";
		$required_attribute = $this->isRequired ? 'aria-required="true"' : '';
		$invalid_attribute  = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';

		return sprintf( "<input name='input_%d' id='%s' type='$field_type' {$class_attribute} {$required_attribute} {$invalid_attribute} value='%s' %s/>", $id, $field_id, esc_attr( $value ), $disabled_text );
	}

	public function get_field_content( $value, $force_frontend_label, $form ) {
		$form_id         = $form['id'];
		$admin_buttons   = $this->get_admin_buttons();
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();
		$is_admin        = $is_entry_detail || $is_form_editor;
		$field_label     = $this->get_field_label( $force_frontend_label, $value );
		$field_id        = $is_admin || $form_id == 0 ? "input_{$this->id}" : 'input_' . $form_id . "_{$this->id}";
		$field_content   = ! $is_admin ? '{FIELD}' : $field_content = sprintf( "%s<label class='gfield_label' for='%s'>%s</label>{FIELD}", $admin_buttons, $field_id, esc_html( $field_label ) );

		return $field_content;
	}

	// # FIELD FILTER UI HELPERS ---------------------------------------------------------------------------------------

	/**
	 * Returns the filter operators for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_operators() {
		$operators   = parent::get_filter_operators();
		$operators[] = 'contains';

		return $operators;
	}

}

GF_Fields::register( new GF_Field_Hidden() );
class-gf-field-html.php000066600000004601151264036140011006 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}

class GF_Field_HTML extends GF_Field {

	public $type = 'html';

	public function get_form_editor_field_title() {
		return esc_attr__( 'HTML', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'content_setting',
			'disable_margins_setting',
			'conditional_logic_field_setting',
			'label_setting',
			'css_class_setting',
		);
	}

	public function get_field_input( $form, $value = '', $entry = null ) {

		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$content = $is_entry_detail || $is_form_editor ? "<div class='gf-html-container'><span class='gf_blockheader'>
															<i class='fa fa-code fa-lg'></i> " . esc_html__( 'HTML Content', 'gravityforms' ) .
															'</span><span>' . esc_html__( 'This is a content placeholder. HTML content is not displayed in the form admin. Preview this form to view the content.', 'gravityforms' ) . '</span></div>'
														: $this->content;
		$content = GFCommon::replace_variables_prepopulate( $content ); // adding support for merge tags

		// adding support for shortcodes
		$content = $this->do_shortcode( $content );

		return $content;
	}

	public function get_field_content( $value, $force_frontend_label, $form ) {
		$form_id         = $form['id'];
		$admin_buttons   = $this->get_admin_buttons();
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();
		$is_admin        = $is_entry_detail || $is_form_editor;
		$field_label     = $this->get_field_label( $force_frontend_label, $value );
		$field_id        = $is_admin || $form_id == 0 ? "input_{$this->id}" : 'input_' . $form_id . "_{$this->id}";
		$field_content   = ! $is_admin ? '{FIELD}' : $field_content = sprintf( "%s<label class='gfield_label' for='%s'>%s</label>{FIELD}", $admin_buttons, $field_id, esc_html( $field_label ) );

		return $field_content;
	}

	public function sanitize_settings() {
		parent::sanitize_settings();
		$this->content = GFCommon::maybe_wp_kses( $this->content );
	}

	public function do_shortcode( $content ){

		if( isset($GLOBALS['wp_embed']) ) {
			// adds support for the [embed] shortcode
			$content = $GLOBALS['wp_embed']->run_shortcode( $content );
		}
		// executes all other shortcodes
		$content = do_shortcode( $content );

		return $content;
	}
}

GF_Fields::register( new GF_Field_HTML() );
class-gf-field-consent.php000066600000041572151264036140011523 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}

/**
 * The Consent Field keeps track of exactly what the user consented to. The consent value ("1"), checkbox label and the Form revision ID
 * are all stored in the entry meta table in separate input values when consent is given.
 *
 * @since 2.4
 *
 * Class GF_Field_Consent
 */
class GF_Field_Consent extends GF_Field {

	/**
	 * Declare the field type.
	 *
	 * @since 2.4
	 *
	 * @var string
	 */
	public $type = 'consent';

	/**
	 * Checked indicator URL.
	 *
	 * @since 2.4
	 *
	 * @var string
	 */
	public $checked_indicator_url = '';

	/**
	 * Checked indicator image markup.
	 *
	 * @since 2.4
	 *
	 * @var string
	 */
	public $checked_indicator_markup = '';

	/**
	 * GF_Field_Consent constructor.
	 *
	 * @since 2.4
	 *
	 * @param array $data Data needed when initiate the class.
	 */
	public function __construct( $data = array() ) {
		parent::__construct( $data );

		/**
		 * Filters the consent checked indicator (image) URL.
		 *
		 * @since 2.4
		 *
		 * @param string $url Image URL.
		 */
		$this->checked_indicator_url = apply_filters( 'gform_consent_checked_indicator', GFCommon::get_base_url() . '/images/tick.png' );

		/**
		 * Filters the consent checked indicator (image) element.
		 *
		 * @since 2.4
		 *
		 * @param string $tag Image tag.
		 */
		$this->checked_indicator_markup = apply_filters( 'gform_consent_checked_indicator_markup', '<img src="' . esc_url( $this->checked_indicator_url ) . '" />' );
	}

	/**
	 * Returns the field title.
	 *
	 * @since 2.4
	 *
	 * @return string
	 */
	public function get_form_editor_field_title() {
		return esc_attr__( 'Consent', 'gravityforms' );
	}

	/**
	 * Returns the field button properties for the form editor. The array contains two elements:
	 * 'group' => 'standard_fields' // or  'advanced_fields', 'post_fields', 'pricing_fields'
	 * 'text'  => 'Button text'
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_form_editor_button() {
		return array(
			'group' => 'advanced_fields',
			'text'  => $this->get_form_editor_field_title(),
		);
	}

	/**
	 * Returns the class names of the settings which should be available on the field in the form editor.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'checkbox_label_setting',
			'rules_setting',
			'visibility_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	/**
	 * Indicate if this field type can be used when configuring conditional logic rules.
	 *
	 * @since 2.4
	 *
	 * @return bool
	 */
	public function is_conditional_logic_supported() {
		return true;
	}

	/**
	 * Returns the field inner markup.
	 *
	 * @since 2.4
	 *
	 * @param array      $form  The Form Object currently being processed.
	 * @param array      $value The field value. From default/dynamic population, $_POST, or a resumed incomplete submission.
	 * @param null|array $entry Null or the Entry Object currently being edited.
	 *
	 * @return string
	 */
	public function get_field_input( $form, $value = array(), $entry = null ) {
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();
		$is_admin        = $is_form_editor || $is_entry_detail;

		$html_input_type = 'checkbox';

		$id                 = (int) $this->id;
		$tabindex           = $this->get_tabindex();
		$disabled_text      = $is_form_editor ? 'disabled="disabled"' : '';
		$required_attribute = $this->isRequired ? 'aria-required="true"' : '';
		$invalid_attribute  = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';

		$target_input_id       = parent::get_first_input_id( $form );
		$for_attribute         = empty( $target_input_id ) ? '' : "for='{$target_input_id}'";
		$label_class_attribute = 'class="gfield_consent_label"';
		$required_div          = ( $this->labelPlacement === 'hidden_label' && ( $is_admin || $this->isRequired ) ) ? sprintf( "<span class='gfield_required'>%s</span>", $this->isRequired ? '*' : '' ) : '';

		if ( $is_admin && ! GFCommon::is_entry_detail_edit() ) {
			$checkbox_label = ! is_array( $value ) || empty( $value[ $id . '.2' ] ) ? $this->checkboxLabel : $value[ $id . '.2' ];
			$revision_id    = ! is_array( $value ) || empty( $value[ $id . '.3' ] ) ? GFFormsModel::get_latest_form_revisions_id( $form['id'] ) : $value[ $id . '.3' ];
			$value          = ! is_array( $value ) || empty( $value[ $id . '.1' ] ) ? '0' : esc_attr( $value[ $id . '.1' ] );
		} else {
			$checkbox_label = trim( $this->checkboxLabel );
			$revision_id    = GFFormsModel::get_latest_form_revisions_id( $form['id'] );
			// We compare if the description text from different revisions has been changed.
			$current_description   = $this->get_field_description_from_revision( $revision_id );
			$submitted_description = $this->get_field_description_from_revision( $value[ $id . '.3' ] );

			$value = ! is_array( $value ) || empty( $value[ $id . '.1' ] ) || ( $checkbox_label !== $value[ $id . '.2' ] ) || ( $current_description !== $submitted_description ) ? '0' : esc_attr( $value[ $id . '.1' ] );
		}
		$checked = $is_form_editor ? '' : checked( '1', $value, false );

		$aria_describedby  = '';
		$description       = $is_entry_detail ? $this->get_field_description_from_revision( $revision_id ) : $this->description;
		if ( ! empty( $description ) ) {
			$aria_describedby = "aria-describedby='gfield_consent_description_{$form['id']}_{$this->id}'";
		}

		$input  = "<input name='input_{$id}.1' id='{$target_input_id}' type='{$html_input_type}' value='1' {$tabindex} {$aria_describedby} {$required_attribute} {$invalid_attribute} {$disabled_text} {$checked} /> <label {$label_class_attribute} {$for_attribute} >{$checkbox_label}</label>{$required_div}";
		$input .= "<input type='hidden' name='input_{$id}.2' value='" . esc_attr( $checkbox_label ) . "' class='gform_hidden' />";
		$input .= "<input type='hidden' name='input_{$id}.3' value='" . esc_attr( $revision_id ) . "' class='gform_hidden' />";

		if ( $is_entry_detail ) {
			$input .= $this->get_description( $this->get_field_description_from_revision( $revision_id ), '' );
		}

		return sprintf( "<div class='ginput_container ginput_container_consent'>%s</div>", $input );
	}

	/**
	 * Returns the input ID to be assigned to the field label for attribute.
	 *
	 * @since  2.4
	 *
	 * @param array $form The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_first_input_id( $form ) {

		return '';

	}

	/**
	 * Returns the markup for the field description.
	 *
	 * @since 2.4
	 *
	 * @param string $description The field description.
	 * @param string $css_class   The css class to be assigned to the description container.
	 *
	 * @return string
	 */
	public function get_description( $description, $css_class ) {
		if ( ! empty( $description ) ) {
			$id = "gfield_consent_description_{$this->formId}_{$this->id}";

			$css_class .= ' gfield_consent_description';

			return "<div class='$css_class' id='$id'>" . nl2br( $description ) . '</div>';
		}

		return parent::get_description( $description, $css_class );
	}

	/**
	 * Return the result (bool) by setting $this->failed_validation.
	 * Return the validation message (string) by setting $this->validation_message.
	 *
	 * @since 2.4
	 *
	 * @param string|array $value The field value from get_value_submission().
	 * @param array        $form  The Form Object currently being processed.
	 */
	public function validate( $value, $form ) {
		$consent = rgget( $this->id . '.1', $value );

		if ( $this->isRequired && rgblank( $consent ) ) {
			$this->failed_validation  = true;
			$this->validation_message = empty( $this->errorMessage ) ? esc_html__( 'This field is required.', 'gravityforms' ) : $this->errorMessage;
		}
	}

	/**
	 * Sanitize and format the value before it is saved to the Entry Object.
	 * We also add the value of inputs .2 and .3 here since they are not displayed in the form.
	 *
	 * @since 2.4
	 *
	 * @param string $value      The value to be saved.
	 * @param array  $form       The Form Object currently being processed.
	 * @param string $input_name The input name used when accessing the $_POST.
	 * @param int    $lead_id    The ID of the Entry currently being processed.
	 * @param array  $lead       The Entry Object currently being processed.
	 *
	 * @return array|string The safe value.
	 */
	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {
		list( $input, $field_id, $input_id ) = rgexplode( '_', $input_name, 3 );

		switch ( $input_id ) {
			case '1':
				$value = ( ! empty( $value ) ) ? '1' : '';
				break;
			case '2':
				$value = ( $lead[ $field_id . '.1' ] === '1' ) ? $value : '';
				break;
			case '3':
				$value = ( $lead[ $field_id . '.1' ] === '1' ) ? $value : '';
				break;
		}

		return $value;
	}

	/**
	 * Set the values of consent field inputs in merge tags.
	 *
	 * @since  2.4
	 *
	 * @param string|array $value      The field value. Depending on the location the merge tag is being used the following functions may have already been applied to the value: esc_html, nl2br, and urlencode.
	 * @param string       $input_id   The field or input ID from the merge tag currently being processed.
	 * @param array        $entry      The Entry Object currently being processed.
	 * @param array        $form       The Form Object currently being processed.
	 * @param string       $modifier   The merge tag modifier. e.g. value.
	 * @param string|array $raw_value  The raw field value from before any formatting was applied to $value.
	 * @param bool         $url_encode Indicates if the urlencode function may have been applied to the $value.
	 * @param bool         $esc_html   Indicates if the esc_html function may have been applied to the $value.
	 * @param string       $format     The format requested for the location the merge is being used. Possible values: html, text or url.
	 * @param bool         $nl2br      Indicates if the nl2br function may have been applied to the $value.
	 *
	 * @return string
	 */
	public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) {
		list( $field_id, $input_id ) = explode( '.', $input_id );

		switch ( $input_id ) {
			case '1':
				$value = ! rgblank( $value ) ? $this->checked_indicator_markup : '';
				break;
			case '3':
				$value = ! rgblank( $value ) ? $this->get_field_description_from_revision( $value ) : '';
				if ( $value !== '' && $nl2br ) {
					$value = nl2br( $value );
				}
				break;
		}

		return $value;
	}

	/**
	 * Format the entry value for display on the entries list page.
	 *
	 * @since 2.4
	 *
	 * @param string|array $value    The field value.
	 * @param array        $entry    The Entry Object currently being processed.
	 * @param string       $field_id The field or input ID currently being processed.
	 * @param array        $columns  The properties for the columns being displayed on the entry list page.
	 * @param array        $form     The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) {
		list( $field_id, $input_id ) = explode( '.', $field_id );

		switch ( $input_id ) {
			case '1':
				$value  = ! rgblank( $value ) ? $this->checked_indicator_markup : '';
				$value .= ! rgblank( $value ) ? ' ' . trim( $entry[ $this->id . '.2' ] ) : '';
				break;
		}

		return $value;
	}

	/**
	 * Format the entry value for display on the entry detail page and for the {all_fields} merge tag.
	 *
	 * @since 2.4
	 *
	 * @param string|array $value    The field value.
	 * @param string       $currency The entry currency code.
	 * @param bool|false   $use_text When processing choice based fields should the choice text be returned instead of the value.
	 * @param string       $format   The format requested for the location the merge is being used. Possible values: html, text or url.
	 * @param string       $media    The location where the value will be displayed. Possible values: screen or email.
	 *
	 * @return string
	 */
	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
		$return = '';

		if ( is_array( $value ) && ! empty( $value ) ) {
			$consent     = trim( $value[ $this->id . '.1' ] );
			$text        = trim( $value[ $this->id . '.2' ] );
			$revision_id = absint( trim( $value[ $this->id . '.3' ] ) );

			if ( ! rgblank( $consent ) ) {
				$return  = $this->checked_indicator_markup;
				$return .= ' ' . wp_kses_post( $text );

				// checking revisions.
				$description = $this->get_field_description_from_revision( $revision_id );

				if ( ! empty( $description ) ) {
					$return .= '<br /><div class="gfield_consent_description">' . nl2br( $description ) . '</div>';
				}
			}
		}

		return $return;
	}

	/**
	 * Format the entry value before it is used in entry exports and by framework add-ons using GFAddOn::get_field_value().
	 *
	 * For CSV export return a string or array.
	 *
	 * @since 2.4
	 *
	 * @param array      $entry    The entry currently being processed.
	 * @param string     $input_id The field or input ID.
	 * @param bool|false $use_text When processing choice based fields should the choice text be returned instead of the value.
	 * @param bool|false $is_csv   Is the value going to be used in the .csv entries export.
	 *
	 * @return string|array
	 */
	public function get_value_export( $entry, $input_id = '', $use_text = false, $is_csv = false ) {
		if ( empty( $input_id ) ) {
			return '';
		}

		$value = parent::get_value_export( $entry, $input_id, $use_text, $is_csv );

		list( $field_id, $input_id ) = rgexplode( '.', $input_id, 2 );

		switch ( $input_id ) {
			case '1':
				$value = ! rgblank( $value ) ? esc_html__( 'Checked', 'gravityforms' ) : esc_html__( 'Not Checked', 'gravityforms' );
				break;
			case '3':
				$value = ! rgblank( $value ) ? $this->get_field_description_from_revision( $value ) : '';
				break;
		}

		return $value;
	}

	/**
	 * Forces settings into expected values while saving the form object.
	 *
	 * No escaping should be done at this stage to prevent double escaping on output.
	 *
	 * @since 2.4
	 */
	public function sanitize_settings() {
		parent::sanitize_settings();
		$this->checkboxLabel = $this->maybe_wp_kses( $this->checkboxLabel );
	}

	/**
	 * Returns the filter settings for the current field.
	 *
	 * If overriding to add custom settings call the parent method first to get the default settings.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_settings() {
		$filter_settings = array(
			'key'             => $this->id . '.1',
			'text'            => GFFormsModel::get_label( $this ),
			'preventMultiple' => false,
			'operators'       => $this->get_filter_operators(),
		);

		$values = $this->get_filter_values();
		if ( ! empty( $values ) ) {
			$filter_settings['values'] = $values;
		}

		return $filter_settings;
	}

	/**
	 * Returns the filter operators for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_operators() {
		$operators = array( 'is', 'isnot' );

		return $operators;
	}

	/**
	 * Returns the filters values setting for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_values() {
		$choices = array(
			array(
				'value' => '1',
				'text'  => esc_html__( 'Checked', 'gravityforms' ),
			),
		);

		return $choices;
	}

	/**
	 * Get consent description from the form revision.
	 *
	 * @since 2.4
	 *
	 * @param int $revision_id Revision ID.
	 *
	 * @return string
	 */
	public function get_field_description_from_revision( $revision_id ) {
		global $wpdb;
		$revisions_table_name = GFFormsModel::get_form_revisions_table_name();
		$display_meta         = $wpdb->get_var( $wpdb->prepare( "SELECT display_meta FROM $revisions_table_name WHERE form_id=%d AND id=%d", $this->formId, $revision_id ) );
		$value                = '';
		$is_entry_detail = $this->is_entry_detail();

		if ( ! empty( $display_meta ) ) {
			$display_meta_array = json_decode( $display_meta, true );
			foreach ( $display_meta_array['fields'] as $field ) {
				if ( $field['id'] === $this->id ) {
					$value = $field['description'];

					break;
				}
			}
		} else {
			$value = ( ! empty( $this->description ) ) ? $this->description : '';
		}

		if ( $is_entry_detail ) {
			$value = $this->maybe_wp_kses( $value );
		}

		return $value;
	}

}

GF_Fields::register( new GF_Field_Consent() );
class-gf-field.php000066600000136507151264036140010057 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}

/**
 * Class GF_Field
 *
 * This class provides the base functionality for developers when creating new fields for Gravity Forms. It facilitates the following:
 *  Adding a button for the field to the form editor
 *  Defining the field title to be used in the form editor
 *  Defining which settings should be present when editing the field
 *  Registering the field as compatible with conditional logic
 *  Outputting field scripts to the form editor and front-end
 *  Defining the field appearance on the front-end, in the form editor and on the entry detail page
 *  Validating the field during submission
 *  Saving the entry value
 *  Defining how the entry value is displayed when merge tags are processed, on the entries list and entry detail pages
 *  Defining how the entry value should be formatted when used in csv exports and by framework based add-ons
 */
class GF_Field extends stdClass implements ArrayAccess {

	const SUPPRESS_DEPRECATION_NOTICE = true;

	private static $deprecation_notice_fired = false;

	private $_is_entry_detail = null;

	/**
	 * An array of properties used to help define and determine the context for the field.
	 * As this is private, it won't be available in any json_encode() output and consequently not saved in the Form array.
	 *
	 * @since 2.3
	 *
	 * @private
	 *
	 * @var array
	 */
	private $_context_properties = array();

	/**
	 * @var array $_merge_tag_modifiers An array of modifiers specified on the field or all_fields merge tag being processed.
	 */
	private $_merge_tag_modifiers = array();

	public function __construct( $data = array() ) {
		if ( empty( $data ) ) {
			return;
		}
		foreach ( $data as $key => $value ) {
			$this->{$key} = $value;
		}
	}

	/**
	 * Fires the deprecation notice only once per page. Not fired during AJAX requests.
	 *
	 * @param string $offset The array key being accessed.
	 */
	private function maybe_fire_array_access_deprecation_notice( $offset ) {

		if ( self::SUPPRESS_DEPRECATION_NOTICE ) {
			return;
		};

		if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
			return;
		}

		if ( ! self::$deprecation_notice_fired ) {
			_deprecated_function( "Array access to the field object is now deprecated. Further notices will be suppressed. \$field['" . $offset . "']", '2.0', 'the object operator e.g. $field->' . $offset );
			self::$deprecation_notice_fired = true;
		}
	}

	/**
	 * Handles array notation
	 *
	 * @param mixed $offset
	 *
	 * @return bool
	 */
	public function offsetExists( $offset ) {
		$this->maybe_fire_array_access_deprecation_notice( $offset );

		return isset( $this->$offset );
	}

	public function offsetGet( $offset ) {
		$this->maybe_fire_array_access_deprecation_notice( $offset );
		if ( ! isset( $this->$offset ) ) {
			$this->$offset = '';
		}

		return $this->$offset;
	}

	public function offsetSet( $offset, $data ) {
		$this->maybe_fire_array_access_deprecation_notice( $offset );
		if ( $offset === null ) {
			$this[] = $data;
		} else {
			$this->$offset = $data;
		}
	}

	public function offsetUnset( $offset ) {
		$this->maybe_fire_array_access_deprecation_notice( $offset );
		unset( $this->$offset );
	}

	public function __isset( $key ) {
		return isset( $this->$key );
	}

	public function __set( $key, $value ) {
		switch( $key ) {
			case '_context_properties' :
				_doing_it_wrong( '$field->_context_properties', 'Use $field->get_context_property() instead.', '2.3' );
				break;
			case 'adminOnly':
				// intercept 3rd parties trying to set the adminOnly property and convert to visibility property
				$this->visibility = $value ? 'administrative' : 'visible';
				break;
			default:
				$this->$key = $value;
		}
	}

	/**
	 * The getter method of the field property.
	 *
	 * @since unknown
	 * @since 2.4.19  Add whitelist for the size property.
	 *
	 * @param string $key The field property.
	 *
	 * @return bool|mixed
	 */
	public function &__get( $key ) {

		switch ( $key ) {
			case '_context_properties' :
				_doing_it_wrong( '$field->_context_properties', 'Use $field->get_context_property() instead.', '2.3' );
				$value = false;

				return $value;
			case 'adminOnly' :
				// intercept 3rd parties trying to get the adminOnly property and fetch visibility property instead
				$value = $this->visibility == 'administrative'; // set and return variable to avoid notice

				return $value;
			case 'size':
				$value = '';

				if ( isset( $this->size ) ) {
					$value = GFCommon::whitelist( $this->size, array( 'small', 'medium', 'large' ) );
				}

				return $value;
			default:
				if ( ! isset( $this->$key ) ) {
					$this->$key = '';
				}
		}

		return $this->$key;
	}

	public function __unset( $key ) {
		unset( $this->$key );
	}

	public function set_context_property( $property_key, $value ) {
		$this->_context_properties[ $property_key ] = $value;
	}

	public function get_context_property( $property_key ) {
		return isset( $this->_context_properties[ $property_key ] ) ? $this->_context_properties[ $property_key ] : null;
	}


	// # FORM EDITOR & FIELD MARKUP -------------------------------------------------------------------------------------

	/**
	 * Returns the field title.
	 *
	 * @return string
	 */
	public function get_form_editor_field_title() {
		return $this->type;
	}

	/**
	 * Returns the field button properties for the form editor. The array contains two elements:
	 * 'group' => 'standard_fields' // or  'advanced_fields', 'post_fields', 'pricing_fields'
	 * 'text'  => 'Button text'
	 *
	 * Built-in fields don't need to implement this because the buttons are added in sequence in GFFormDetail
	 *
	 * @return array
	 */
	public function get_form_editor_button() {
		return array(
			'group' => 'standard_fields',
			'text'  => $this->get_form_editor_field_title()
		);
	}

	/**
	 * Returns the class names of the settings which should be available on the field in the form editor.
	 *
	 * @return array
	 */
	public function get_form_editor_field_settings() {
		return array();
	}

	/**
	 * Override to indicate if this field type can be used when configuring conditional logic rules.
	 *
	 * @return bool
	 */
	public function is_conditional_logic_supported() {
		return false;
	}

	/**
	 * Returns the scripts to be included for this field type in the form editor.
	 *
	 * @return string
	 */
	public function get_form_editor_inline_script_on_page_render() {
		return '';
	}

	/**
	 * Returns the scripts to be included with the form init scripts on the front-end.
	 *
	 * @param array $form The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_form_inline_script_on_page_render( $form ) {
		return '';
	}

	/**
	 * Returns the field inner markup.
	 *
	 * @param array        $form  The Form Object currently being processed.
	 * @param string|array $value The field value. From default/dynamic population, $_POST, or a resumed incomplete submission.
	 * @param null|array   $entry Null or the Entry Object currently being edited.
	 *
	 * @return string
	 */
	public function get_field_input( $form, $value = '', $entry = null ) {
		return '';
	}

	/**
	 * Returns the field markup; including field label, description, validation, and the form editor admin buttons.
	 *
	 * The {FIELD} placeholder will be replaced in GFFormDisplay::get_field_content with the markup returned by GF_Field::get_field_input().
	 *
	 * @param string|array $value                The field value. From default/dynamic population, $_POST, or a resumed incomplete submission.
	 * @param bool         $force_frontend_label Should the frontend label be displayed in the admin even if an admin label is configured.
	 * @param array        $form                 The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_field_content( $value, $force_frontend_label, $form ) {
		$form_id = (int) rgar( $form, 'id' );

		$field_label = $this->get_field_label( $force_frontend_label, $value );

		$validation_message_id = 'validation_message_' . $form_id . '_' . $this->id;
		$validation_message = ( $this->failed_validation && ! empty( $this->validation_message ) ) ? sprintf( "<div id='%s' class='gfield_description validation_message' aria-live='polite'>%s</div>", $validation_message_id, $this->validation_message ) : '';

		$is_form_editor  = $this->is_form_editor();
		$is_entry_detail = $this->is_entry_detail();
		$is_admin        = $is_form_editor || $is_entry_detail;

		$required_div = $is_admin || $this->isRequired ? sprintf( "<span class='gfield_required'>%s</span>", $this->isRequired ? '*' : '' ) : '';

		$admin_buttons = $this->get_admin_buttons();

		$target_input_id = $this->get_first_input_id( $form );

		$for_attribute = empty( $target_input_id ) ? '' : "for='{$target_input_id}'";

		$description = $this->get_description( $this->description, 'gfield_description' );
		if ( $this->is_description_above( $form ) ) {
			$clear         = $is_admin ? "<div class='gf_clear'></div>" : '';
			$field_content = sprintf( "%s<label class='%s' $for_attribute >%s%s</label>%s{FIELD}%s$clear", $admin_buttons, esc_attr( $this->get_field_label_class() ), esc_html( $field_label ), $required_div, $description, $validation_message );
		} else {
			$field_content = sprintf( "%s<label class='%s' $for_attribute >%s%s</label>{FIELD}%s%s", $admin_buttons, esc_attr( $this->get_field_label_class() ), esc_html( $field_label ), $required_div, $description, $validation_message );
		}

		return $field_content;
	}

	public function get_field_label_class() {
		return 'gfield_label';
	}


	// # SUBMISSION -----------------------------------------------------------------------------------------------------

	/**
	 * Whether this field expects an array during submission.
	 *
	 * @since 2.4
	 *
	 * @return bool
	 */
	public function is_value_submission_array() {
		return false;
	}

	/**
	 * Used to determine the required validation result.
	 *
	 * @param int $form_id The ID of the form currently being processed.
	 *
	 * @return bool
	 */
	public function is_value_submission_empty( $form_id ) {

		$copy_values_option_activated = $this->enableCopyValuesOption && rgpost( 'input_' . $this->id . '_copy_values_activated' );

		if ( is_array( $this->inputs ) ) {
			foreach ( $this->inputs as $input ) {

				if ( $copy_values_option_activated ) {
					$input_id          = $input['id'];
					$input_name        = 'input_' . str_replace( '.', '_', $input_id );
					$source_field_id   = $this->copyValuesOptionField;
					$source_input_name = str_replace( 'input_' . $this->id, 'input_' . $source_field_id, $input_name );
					$value             = rgpost( $source_input_name );
				} else {
					$value = rgpost( 'input_' . str_replace( '.', '_', $input['id'] ) );
				}

				if ( is_array( $value ) && ! empty( $value ) ) {
					return false;
				}

				if ( ! is_array( $value ) && strlen( trim( $value ) ) > 0 ) {
					return false;
				}
			}

			return true;
		} else {
			if ( $copy_values_option_activated ) {
				$value = rgpost( 'input_' . $this->copyValuesOptionField );
			} else {
				$value = rgpost( 'input_' . $this->id );
			}

			if ( is_array( $value ) ) {
				//empty if any of the inputs are empty (for inputs with the same name)
				foreach ( $value as $input ) {
					$input = GFCommon::trim_deep( $input );
					if ( GFCommon::safe_strlen( $input ) <= 0 ) {
						return true;
					}
				}

				return false;
			} elseif ( $this->enablePrice ) {
				list( $label, $price ) = rgexplode( '|', $value, 2 );
				$is_empty = ( strlen( trim( $price ) ) <= 0 );

				return $is_empty;
			} else {
				$is_empty = ( strlen( trim( $value ) ) <= 0 ) || ( $this->type == 'post_category' && $value < 0 );

				return $is_empty;
			}
		}
	}

	/**
	 * Is the given value considered empty for this field.
	 *
	 * @since 2.4
	 *
	 * @param $value
	 *
	 * @return bool
	 */
	public function is_value_empty( $value ) {
		if ( is_array( $this->inputs ) ) {
			if ( $this->is_value_submission_array() ) {
				foreach ( $this->inputs as $i => $input ) {
					$v = isset( $value[ $i ] ) ?  $value[ $i ] : '';
					if ( is_array( $v ) && ! empty( $v ) ) {
						return false;
					}

					if ( ! is_array( $v ) && strlen( trim( $v ) ) > 0 ) {
						return false;
					}
				}
			} else {
				foreach ( $this->inputs as $input ) {
					$input_id = (string) $input['id'];
					$v = isset( $value[ $input_id ] ) ?  $value[ $input_id ] : '';
					if ( is_array( $v ) && ! empty( $v ) ) {
						return false;
					}

					if ( ! is_array( $v ) && strlen( trim( $v ) ) > 0 ) {
						return false;
					}
				}
			}

		} elseif ( is_array( $value ) ) {
			// empty if any of the inputs are empty (for inputs with the same name)
			foreach ( $value as $input ) {
				$input = GFCommon::trim_deep( $input );
				if ( GFCommon::safe_strlen( $input ) <= 0 ) {
					return true;
				}
			}

			return false;
		} elseif ( empty( $value ) ) {
			return true;
		} else {
			return false;
		}

		return true;
	}

	/**
	 * Override this method to perform custom validation logic.
	 *
	 * Return the result (bool) by setting $this->failed_validation.
	 * Return the validation message (string) by setting $this->validation_message.
	 *
	 * @param string|array $value The field value from get_value_submission().
	 * @param array        $form  The Form Object currently being processed.
	 */
	public function validate( $value, $form ) {
		//
	}

	/**
	 * Retrieve the field value on submission.
	 *
	 * @param array     $field_values             The dynamic population parameter names with their corresponding values to be populated.
	 * @param bool|true $get_from_post_global_var Whether to get the value from the $_POST array as opposed to $field_values.
	 *
	 * @return array|string
	 */
	public function get_value_submission( $field_values, $get_from_post_global_var = true ) {

		$inputs = $this->get_entry_inputs();

		if ( is_array( $inputs ) ) {
			$value = array();
			foreach ( $inputs as $input ) {
				$value[ strval( $input['id'] ) ] = $this->get_input_value_submission( 'input_' . str_replace( '.', '_', strval( $input['id'] ) ), RGForms::get( 'name', $input ), $field_values, $get_from_post_global_var );
			}
		} else {
			$value = $this->get_input_value_submission( 'input_' . $this->id, $this->inputName, $field_values, $get_from_post_global_var );
		}

		return $value;
	}

	/**
	 * Retrieve the input value on submission.
	 *
	 * @param string    $standard_name            The input name used when accessing the $_POST.
	 * @param string    $custom_name              The dynamic population parameter name.
	 * @param array     $field_values             The dynamic population parameter names with their corresponding values to be populated.
	 * @param bool|true $get_from_post_global_var Whether to get the value from the $_POST array as opposed to $field_values.
	 *
	 * @return array|string
	 */
	public function get_input_value_submission( $standard_name, $custom_name = '', $field_values = array(), $get_from_post_global_var = true ) {

		$form_id = $this->formId;
		if ( ! empty( $_POST[ 'is_submit_' . $form_id ] ) && $get_from_post_global_var ) {
			$value = rgpost( $standard_name );
			$value = GFFormsModel::maybe_trim_input( $value, $form_id, $this );

			return $value;
		} elseif ( $this->allowsPrepopulate ) {
			return GFFormsModel::get_parameter_value( $custom_name, $field_values, $this );
		}

	}


	// # ENTRY RELATED --------------------------------------------------------------------------------------------------

	/**
	 * Override and return null if a multi-input field value is to be stored under the field ID instead of the individual input IDs.
	 *
	 * @return array|null
	 */
	public function get_entry_inputs() {
		return $this->inputs;
	}

	/**
	 * Sanitize and format the value before it is saved to the Entry Object.
	 *
	 * @param string $value      The value to be saved.
	 * @param array  $form       The Form Object currently being processed.
	 * @param string $input_name The input name used when accessing the $_POST.
	 * @param int    $lead_id    The ID of the Entry currently being processed.
	 * @param array  $lead       The Entry Object currently being processed.
	 *
	 * @return array|string The safe value.
	 */
	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {
		if ( rgblank( $value ) ) {

			return '';

		} elseif ( is_array( $value ) ) {

			foreach ( $value as &$v ) {

				if ( is_array( $v ) ) {
					$v = '';
				}

				$v = $this->sanitize_entry_value( $v, $form['id'] );

			}

			return implode( ',', $value );

		} else {

			return $this->sanitize_entry_value( $value, $form['id'] );

		}
	}

	/**
	 * Format the entry value for when the field/input merge tag is processed. Not called for the {all_fields} merge tag.
	 *
	 * Return a value that is safe for the context specified by $format.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param string|array $value      The field value. Depending on the location the merge tag is being used the following functions may have already been applied to the value: esc_html, nl2br, and urlencode.
	 * @param string       $input_id   The field or input ID from the merge tag currently being processed.
	 * @param array        $entry      The Entry Object currently being processed.
	 * @param array        $form       The Form Object currently being processed.
	 * @param string       $modifier   The merge tag modifier. e.g. value
	 * @param string|array $raw_value  The raw field value from before any formatting was applied to $value.
	 * @param bool         $url_encode Indicates if the urlencode function may have been applied to the $value.
	 * @param bool         $esc_html   Indicates if the esc_html function may have been applied to the $value.
	 * @param string       $format     The format requested for the location the merge is being used. Possible values: html, text or url.
	 * @param bool         $nl2br      Indicates if the nl2br function may have been applied to the $value.
	 *
	 * @return string
	 */
	public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) {

		if ( $format === 'html' ) {
			$form_id = isset( $form['id'] ) ? absint( $form['id'] ) : null;
			$allowable_tags = $this->get_allowable_tags( $form_id );

			if ( $allowable_tags === false ) {
				// The value is unsafe so encode the value.
				if ( is_array( $value ) ) {
					foreach ( $value as &$v ) {
						$v = esc_html( $v );
					}
					$return = $value;
				} else {
					$return = esc_html( $value );
				}
			} else {
				// The value contains HTML but the value was sanitized before saving.
				if ( is_array( $raw_value ) ) {
					$return = rgar( $raw_value, $input_id );
				} else {
					$return = $raw_value;
				}
			}

			if ( $nl2br ) {
				if ( is_array( $return ) ) {
					foreach ( $return as &$r ) {
						$r = nl2br( $r );
					}
				} else {
					$return = nl2br( $return );
				}
			}
		} else {
			$return = $value;
		}

		return $return;
	}

	/**
	 * Format the entry value for display on the entries list page.
	 *
	 * Return a value that's safe to display on the page.
	 *
	 * @param string|array $value    The field value.
	 * @param array        $entry    The Entry Object currently being processed.
	 * @param string       $field_id The field or input ID currently being processed.
	 * @param array        $columns  The properties for the columns being displayed on the entry list page.
	 * @param array        $form     The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) {
		$allowable_tags = $this->get_allowable_tags( $form['id'] );

		if ( $allowable_tags === false ) {
			// The value is unsafe so encode the value.
			$return = esc_html( $value );
		} else {
			// The value contains HTML but the value was sanitized before saving.
			$return = $value;
		}

		return $return;
	}

	/**
	 * Format the entry value for display on the entry detail page and for the {all_fields} merge tag.
	 *
	 * Return a value that's safe to display for the context of the given $format.
	 *
	 * @param string|array $value    The field value.
	 * @param string       $currency The entry currency code.
	 * @param bool|false   $use_text When processing choice based fields should the choice text be returned instead of the value.
	 * @param string       $format   The format requested for the location the merge is being used. Possible values: html, text or url.
	 * @param string       $media    The location where the value will be displayed. Possible values: screen or email.
	 *
	 * @return string
	 */
	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {

		if ( is_array( $value ) ) {
			_doing_it_wrong( __METHOD__, 'Override this method to handle array values', '2.0' );
			return $value;
		}

		if ( $format === 'html' ) {
			$value = nl2br( $value );

			$allowable_tags = $this->get_allowable_tags();

			if ( $allowable_tags === false ) {
				// The value is unsafe so encode the value.
				$return = esc_html( $value );
			} else {
				// The value contains HTML but the value was sanitized before saving.
				$return = $value;
			}
		} else {
			$return = $value;
		}

		return $return;
	}

	/**
	 * Format the entry value before it is used in entry exports and by framework add-ons using GFAddOn::get_field_value().
	 *
	 * For CSV export return a string or array.
	 *
	 * @param array      $entry    The entry currently being processed.
	 * @param string     $input_id The field or input ID.
	 * @param bool|false $use_text When processing choice based fields should the choice text be returned instead of the value.
	 * @param bool|false $is_csv   Is the value going to be used in the .csv entries export?
	 *
	 * @return string|array
	 */
	public function get_value_export( $entry, $input_id = '', $use_text = false, $is_csv = false ) {
		if ( empty( $input_id ) ) {
			$input_id = $this->id;
		}

		return rgar( $entry, $input_id );
	}

	/**
	 * Apply the gform_get_input_value filter to an entry's value.
	 *
	 * @since 2.4.24
	 *
	 * @param mixed  $value    The field or input value to be filtered.
	 * @param array  $entry    The entry currently being processed.
	 * @param string $input_id The ID of the input being processed from a multi-input field type or an empty string.
	 *
	 * @return mixed
	 */
	public function filter_input_value( $value, $entry, $input_id = '' ) {
		/**
		 * Allows the field or input value to be overridden when populating the entry (usually on retrieval from the database).
		 *
		 * @since 1.5.3
		 * @since 1.9.14 Added the form and field specific versions.
		 *
		 * @param mixed    $value    The field or input value to be filtered.
		 * @param array    $entry    The entry currently being processed.
		 * @param GF_Field $this     The field currently being processed.
		 * @param string   $input_id The ID of the input being processed from a multi-input field type or an empty string.
		 */
		return gf_apply_filters(
			array(
				'gform_get_input_value',
				$this->formId,
				$this->id,
			),
			$value,
			$entry,
			$this,
			$input_id
		);
	}


	// # INPUT ATTRIBUTE HELPERS ----------------------------------------------------------------------------------------

	/**
	 * Maybe return the input attribute which will trigger evaluation of conditional logic rules which depend on this field.
	 *
	 * @since 2.4
	 *
	 * @param string $event The event attribute which should be returned. Possible values: keyup, click, or change.
	 *
	 * @deprecated 2.4 Conditional Logic is now triggered based on .gfield class name. No need to hardcode calls to gf_apply_rules() to every field.
	 *
	 * @return string
	 */
	public function get_conditional_logic_event( $event ) {

		_deprecated_function( __CLASS__ . ':' . __METHOD__, '2.4' );

		if ( empty( $this->conditionalLogicFields ) || $this->is_entry_detail() || $this->is_form_editor() ) {
			return '';
		}

		switch ( $event ) {
			case 'keyup' :
				return "onchange='gf_apply_rules(" . $this->formId . ',' . GFCommon::json_encode( $this->conditionalLogicFields ) . ");' onkeyup='clearTimeout(__gf_timeout_handle); __gf_timeout_handle = setTimeout(\"gf_apply_rules(" . $this->formId . ',' . GFCommon::json_encode( $this->conditionalLogicFields ) . ")\", 300);'";
				break;

			case 'click' :
				return "onclick='gf_apply_rules(" . $this->formId . ',' . GFCommon::json_encode( $this->conditionalLogicFields ) . ");' onkeypress='gf_apply_rules(" . $this->formId . ',' . GFCommon::json_encode( $this->conditionalLogicFields ) . ");'";
				break;

			case 'change' :
				return "onchange='gf_apply_rules(" . $this->formId . ',' . GFCommon::json_encode( $this->conditionalLogicFields ) . ");'";
				break;
		}
	}

	/**
	 * Maybe return the tabindex attribute.
	 *
	 * @return string
	 */
	public function get_tabindex() {
		return GFCommon::$tab_index > 0 ? "tabindex='" . GFCommon::$tab_index ++ . "'" : '';
	}

	/**
	 * If the field placeholder property has a value return the input placeholder attribute.
	 *
	 * @return string
	 */
	public function get_field_placeholder_attribute() {

		$placeholder_value = GFCommon::replace_variables_prepopulate( $this->placeholder );

		return ! rgblank( $placeholder_value ) ? sprintf( "placeholder='%s'", esc_attr( $placeholder_value ) ) : '';
	}

	/**
	 * If the input placeholder property has a value return the input placeholder attribute.
	 *
	 * @param array $input The input currently being processed.
	 *
	 * @return string
	 */
	public function get_input_placeholder_attribute( $input ) {

		$placeholder_value = $this->get_input_placeholder_value( $input );

		return ! rgblank( $placeholder_value ) ? sprintf( "placeholder='%s'", esc_attr( $placeholder_value ) ) : '';
	}

	/**
	 * If configured retrieve the input placeholder value.
	 *
	 * @param array $input The input currently being processed.
	 *
	 * @return string
	 */
	public function get_input_placeholder_value( $input ) {

		$placeholder = rgar( $input, 'placeholder' );

		return rgblank( $placeholder ) ? '' : GFCommon::replace_variables_prepopulate( $placeholder );
	}


	// # BOOLEAN HELPERS ------------------------------------------------------------------------------------------------

	/**
	 * Determine if the current location is the form editor.
	 *
	 * @return bool
	 */
	public function is_form_editor() {
		return GFCommon::is_form_editor();
	}

	/**
	 * Determine if the current location is the entry detail page.
	 *
	 * @return bool
	 */
	public function is_entry_detail() {
		return isset( $this->_is_entry_detail ) ? (bool) $this->_is_entry_detail : GFCommon::is_entry_detail();
	}

	/**
	 * Determine if the current location is the edit entry page.
	 *
	 * @return bool
	 */
	public function is_entry_detail_edit() {
		return GFCommon::is_entry_detail_edit();
	}

	/**
	 * Is this a calculated product field or a number field with a calculation enabled and formula configured.
	 *
	 * @return bool
	 */
	public function has_calculation() {

		$type = $this->get_input_type();

		if ( $type == 'number' ) {
			return $this->enableCalculation && $this->calculationFormula;
		}

		return $type == 'calculation';
	}

	/**
	 * Determines if the field description should be positioned above or below the input.
	 *
	 * @param array $form The Form Object currently being processed.
	 *
	 * @return bool
	 */
	public function is_description_above( $form ) {
		$form_label_placement        = rgar( $form, 'labelPlacement' );
		$field_label_placement       = $this->labelPlacement;
		$form_description_placement  = rgar( $form, 'descriptionPlacement' );
		$field_description_placement = $this->descriptionPlacement;
		if ( empty( $field_description_placement ) ) {
			$field_description_placement = $form_description_placement;
		}
		$is_description_above = $field_description_placement == 'above' && ( $field_label_placement == 'top_label' || $field_label_placement == 'hidden_label' || ( empty( $field_label_placement ) && $form_label_placement == 'top_label' ) );

		return $is_description_above;
	}


	public function is_administrative() {
		return $this->visibility == 'administrative';
	}


	// # OTHER HELPERS --------------------------------------------------------------------------------------------------

	/**
	 * Store the modifiers so they can be accessed in get_value_entry_detail() when preparing the content for the {all_fields} output.
	 *
	 * @param array $modifiers An array of modifiers to be stored.
	 */
	public function set_modifiers( $modifiers ) {

		$this->_merge_tag_modifiers = $modifiers;
	}

	/**
	 * Retrieve the merge tag modifiers.
	 *
	 * @return array
	 */
	public function get_modifiers() {

		return $this->_merge_tag_modifiers;
	}

	/**
	 * Retrieves the field input type.
	 *
	 * @return string
	 */
	public function get_input_type() {

		return empty( $this->inputType ) ? $this->type : $this->inputType;
	}

	/**
	 * Adds the field button to the specified group.
	 *
	 * @param array $field_groups
	 *
	 * @return array
	 */
	public function add_button( $field_groups ) {

		// Check a button for the type hasn't already been added
		foreach ( $field_groups as $group ) {
			foreach ( $group['fields'] as $button ) {
				if ( isset( $button['data-type'] ) && $button['data-type'] == $this->type ) {
					return $field_groups;
				}
			}
		}


		$new_button = $this->get_form_editor_button();
		if ( ! empty( $new_button ) ) {
			foreach ( $field_groups as &$group ) {
				if ( $group['name'] == $new_button['group'] ) {
					$group['fields'][] = array(
						'class'      => 'button',
						'value'      => $new_button['text'],
						'data-type'  => $this->type,
						'onclick'    => "StartAddField('{$this->type}');",
						'onkeypress' => "StartAddField('{$this->type}');",
					);
					break;
				}
			}
		}

		return $field_groups;
	}

	/**
	 * Returns the field admin buttons for display in the form editor.
	 *
	 * @return string
	 */
	public function get_admin_buttons() {
		$duplicate_disabled   = array(
			'captcha',
			'post_title',
			'post_content',
			'post_excerpt',
			'total',
			'shipping',
			'creditcard'
		);
		$duplicate_field_link = ! in_array( $this->type, $duplicate_disabled ) ? "<a class='field_duplicate_icon' id='gfield_duplicate_{$this->id}' title='" . esc_attr__( 'click to duplicate this field', 'gravityforms' ) . "' href='#' onclick='StartDuplicateField(this); return false;' onkeypress='StartDuplicateField(this); return false;'><i class='fa fa-files-o fa-lg'></i></a>" : '';

		/**
		 * This filter allows for modification of the form field duplicate link. This will change the link for all fields
		 *
		 * @param string $duplicate_field_link The Duplicate Field Link (in HTML)
		 */
		$duplicate_field_link = apply_filters( 'gform_duplicate_field_link', $duplicate_field_link );

		$delete_field_link = "<a class='field_delete_icon' id='gfield_delete_{$this->id}' title='" . esc_attr__( 'click to delete this field', 'gravityforms' ) . "' href='#' onclick='DeleteField(this); return false;' onkeypress='DeleteField(this); return false;'><i class='fa fa-times fa-lg'></i></a>";

		/**
		 * This filter allows for modification of a form field delete link. This will change the link for all fields
		 *
		 * @param string $delete_field_link The Delete Field Link (in HTML)
		 */
		$delete_field_link = apply_filters( 'gform_delete_field_link', $delete_field_link );
		$field_type_title  = esc_html( GFCommon::get_field_type_title( $this->type ) );

		$is_form_editor  = $this->is_form_editor();
		$is_entry_detail = $this->is_entry_detail();
		$is_admin        = $is_form_editor || $is_entry_detail;

		$admin_buttons = $is_admin ? "<div class='gfield_admin_icons'><div class='gfield_admin_header_title'>{$field_type_title} : " . esc_html__( 'Field ID', 'gravityforms' ) . " {$this->id}</div>" . $delete_field_link . $duplicate_field_link . "<a href='javascript:void(0);' class='field_edit_icon edit_icon_collapsed' aria-expanded='false' title='" . esc_attr__( 'click to expand and edit the options for this field', 'gravityforms' ) . "'><i class='fa fa-caret-down fa-lg'></i></a></div>" : '';

		return $admin_buttons;
	}

	/**
	 * Retrieve the field label.
	 *
	 * @param bool $force_frontend_label Should the frontend label be displayed in the admin even if an admin label is configured.
	 * @param string $value The field value. From default/dynamic population, $_POST, or a resumed incomplete submission.
	 *
	 * @return string
	 */
	public function get_field_label( $force_frontend_label, $value ) {
		$field_label = $force_frontend_label ? $this->label : GFCommon::get_label( $this );
		if ( ( $this->inputType == 'singleproduct' || $this->inputType == 'calculation' ) && ! rgempty( $this->id . '.1', $value ) ) {
			$field_label = rgar( $value, $this->id . '.1' );
		}

		return $field_label;
	}

	/**
	 * Returns the input ID to be assigned to the field label for attribute.
	 *
	 * @param array $form The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_first_input_id( $form ) {
		$form_id = (int) rgar( $form, 'id' );

		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();
		$field_id        = $is_entry_detail || $is_form_editor || $form_id == 0 ? 'input_' : "input_{$form_id}_";

		if ( is_array( $this->inputs ) ) {
			foreach ( $this->inputs as $input ) {
				// Validate if input id is in x.x format.
				if ( ! is_numeric( $input['id'] ) ) {
					break;
				}

				if ( ! isset( $input['isHidden'] ) || ! $input['isHidden'] ) {
					$field_id .= str_replace( '.', '_', $input['id'] );
					break;
				}
			}
		} else {
			$field_id .= $this->id;
		}

		// The value is used as an HTML attribute, escape it.
		return esc_attr( $field_id );
	}

	/**
	 * Returns the markup for the field description.
	 *
	 * @param string $description The field description.
	 * @param string $css_class   The css class to be assigned to the description container.
	 *
	 * @return string
	 */
	public function get_description( $description, $css_class ) {
		$is_form_editor  = $this->is_form_editor();
		$is_entry_detail = $this->is_entry_detail();
		$is_admin        = $is_form_editor || $is_entry_detail;
		$id              = "gfield_description_{$this->formId}_{$this->id}";

		return $is_admin || ! empty( $description ) ? "<div class='$css_class' id='$id'>" . $description . '</div>' : '';
	}

	/**
	 * If a field has a description, the aria-describedby attribute for the input field is returned.
	 *
	 * @return string
	 */
	public function get_aria_describedby() {

		if ( empty( $this->description ) ) {
			return '';
		}
		$id = "gfield_description_{$this->formId}_{$this->id}";

		return 'aria-describedby="' . $id . '"';

	}

	/**
	 * Returns the field default value if the field does not already have a value.
	 *
	 * @param array|string $value The field value.
	 *
	 * @return array|string
	 */
	public function get_value_default_if_empty( $value ) {

		if ( is_array( $this->inputs ) && is_array( $value ) ) {
			$defaults = $this->get_value_default();
			foreach( $value as $index => &$input_value ) {
				if ( rgblank( $input_value ) ) {
					$input_value = rgar( $defaults, $index );
				}
			}
		}

		if ( ! GFCommon::is_empty_array( $value ) ) {
			return $value;
		}

		return $this->get_value_default();
	}

	/**
	 * Retrieve the field default value.
	 *
	 * @return array|string
	 */
	public function get_value_default() {

		if ( is_array( $this->inputs ) ) {
			$value = array();
			foreach ( $this->inputs as $input ) {
				$value[ strval( $input['id'] ) ] = $this->is_form_editor() ? rgar( $input, 'defaultValue' ) : GFCommon::replace_variables_prepopulate( rgar( $input, 'defaultValue' ) );
			}
		} else {
			$value = $this->is_form_editor() ? $this->defaultValue : GFCommon::replace_variables_prepopulate( $this->defaultValue );
		}

		return $value;
	}

	/**
	 * Registers the script returned by get_form_inline_script_on_page_render() for display on the front-end.
	 *
	 * @param array $form The Form Object currently being processed.
	 */
	public function register_form_init_scripts( $form ) {
		GFFormDisplay::add_init_script( $form['id'], $this->type . '_' . $this->id, GFFormDisplay::ON_PAGE_RENDER, $this->get_form_inline_script_on_page_render( $form ) );
	}


	// # SANITIZATION ---------------------------------------------------------------------------------------------------

	/**
	 * Strip unsafe tags from the field value.
	 *
	 * @param string $string The field value to be processed.
	 *
	 * @return string
	 */
	public function strip_script_tag( $string ) {
		$allowable_tags = '<a><abbr><acronym><address><area><area /><b><base><base /><bdo><big><blockquote><body><br><br /><button><caption><cite><code><col><col /><colgroup><command><command /><dd><del><dfn><div><dl><DOCTYPE><dt><em><fieldset><form><h1><h2><h3><h4><h5><h6><head><html><hr><hr /><i><img><img /><input><input /><ins><kbd><label><legend><li><link><map><meta><meta /><noscript><ol><optgroup><option><p><param><param /><pre><q><samp><select><small><span><strong><style><sub><sup><table><tbody><td><textarea><tfoot><th><thead><title><tr><tt><ul><var><wbr><wbr />';

		$string = strip_tags( $string, $allowable_tags );

		return $string;
	}

	/**
	 * Override this if the field should allow html tags to be saved with the entry value. Default is false.
	 *
	 * @return bool
	 */
	public function allow_html() {

		return false;
	}

	/**
	 * Fields should override this method to implement the appropriate sanitization specific to the field type before the value is saved.
	 *
	 * This base method will only strip HTML tags if the field or the gform_allowable_tags filter allows HTML.
	 *
	 * @param string $value   The field value to be processed.
	 * @param int    $form_id The ID of the form currently being processed.
	 *
	 * @return string
	 */
	public function sanitize_entry_value( $value, $form_id ) {

		if ( is_array( $value ) ) {
			return '';
		}

		$allowable_tags = $this->get_allowable_tags( $form_id );

		if ( $allowable_tags === true ) {

			// HTML is expected. Output will not be encoded so the value will stripped of scripts and some tags and encoded.
			$return = wp_kses_post( $value );

		} elseif ( $allowable_tags === false ) {

			// HTML is not expected. Output will be encoded.
			$return = $value;

		} else {

			// Some HTML is expected. Output will not be encoded so the value will stripped of scripts and some tags and encoded.
			$value = wp_kses_post( $value );

			// Strip all tags except those allowed by the gform_allowable_tags filter.
			$return = strip_tags( $value, $allowable_tags );
		}

		return $return;
	}

	/**
	 * Forces settings into expected values while saving the form object.
	 *
	 * No escaping should be done at this stage to prevent double escaping on output.
	 *
	 * Currently called only for forms created after version 1.9.6.10.
	 *
	 */
	public function sanitize_settings() {
		$this->id     = absint( $this->id );
		$this->type   = wp_strip_all_tags( $this->type );
		$this->formId = absint( $this->formId );

		$this->label       = $this->maybe_wp_kses( $this->label );
		$this->adminLabel  = $this->maybe_wp_kses( $this->adminLabel );
		$this->description = $this->maybe_wp_kses( $this->description );

		$this->isRequired = (bool) $this->isRequired;

		$this->allowsPrepopulate = (bool) $this->allowsPrepopulate;

		$this->inputMask      = (bool) $this->inputMask;
		$this->inputMaskValue = wp_strip_all_tags( $this->inputMaskValue );

		if ( $this->inputMaskIsCustom !== '' ) {
			$this->inputMaskIsCustom = (bool) $this->inputMaskIsCustom;
		}

		if ( $this->maxLength ) {
			$this->maxLength = absint( $this->maxLength );
		}

		if ( $this->inputType ) {
			$this->inputType = wp_strip_all_tags( $this->inputType );
		}

		if ( $this->size ) {
			$this->size = GFCommon::whitelist( $this->size, $this->get_size_choices( true ) );
		}

		if ( $this->errorMessage ) {
			$this->errorMessage = sanitize_text_field( $this->errorMessage );
		}

		if ( $this->labelPlacement ) {
			$this->labelPlacement = wp_strip_all_tags( $this->labelPlacement );
		}

		if ( $this->descriptionPlacement ) {
			$this->descriptionPlacement = wp_strip_all_tags( $this->descriptionPlacement );
		}

		if ( $this->subLabelPlacement ) {
			$this->subLabelPlacement = wp_strip_all_tags( $this->subLabelPlacement );
		}

		if ( $this->placeholder ) {
			$this->placeholder = sanitize_text_field( $this->placeholder );
		}

		if ( $this->cssClass ) {
			$this->cssClass = wp_strip_all_tags( $this->cssClass );
		}

		if ( $this->inputName ) {
			$this->inputName = wp_strip_all_tags( $this->inputName );
		}

		$this->visibility = wp_strip_all_tags( $this->visibility );
		$this->noDuplicates = (bool) $this->noDuplicates;

		if ( $this->defaultValue ) {
			$this->defaultValue = $this->maybe_wp_kses( $this->defaultValue );
		}

		if ( is_array( $this->inputs ) ) {
			foreach ( $this->inputs as &$input ) {
				if ( isset( $input['id'] ) ) {
					$input['id'] = wp_strip_all_tags( $input['id'] );
				}
				if ( isset( $input['customLabel'] ) ) {
					$input['customLabel'] = $this->maybe_wp_kses( $input['customLabel'] );
				}
				if ( isset( $input['label'] ) ) {
					$input['label'] = $this->maybe_wp_kses( $input['label'] );
				}
				if ( isset( $input['name'] ) ) {
					$input['name'] = wp_strip_all_tags( $input['name'] );
				}

				if ( isset( $input['placeholder'] ) ) {
					$input['placeholder'] = sanitize_text_field( $input['placeholder'] );
				}

				if ( isset( $input['defaultValue'] ) ) {
					$input['defaultValue'] = wp_strip_all_tags( $input['defaultValue'] );
				}
			}
		}

		$this->sanitize_settings_choices();
		$this->sanitize_settings_conditional_logic();

	}

	/**
	 * Sanitize the field choices property.
	 *
	 * @param array|null $choices The field choices property.
	 *
	 * @return array|null
	 */
	public function sanitize_settings_choices( $choices = null ) {

		if ( is_null( $choices ) ) {
			$choices = &$this->choices;
		}

		if ( ! is_array( $choices ) ) {
			return $choices;
		}

		foreach ( $choices as &$choice ) {
			if ( isset( $choice['isSelected'] ) ) {
				$choice['isSelected'] = (bool) $choice['isSelected'];
			}

			if ( isset( $choice['price'] ) && ! empty( $choice['price'] ) ) {
				$price_number    = GFCommon::to_number( $choice['price'] );
				$choice['price'] = GFCommon::to_money( $price_number );
			}

			if ( isset( $choice['text'] ) ) {
				$choice['text'] = $this->maybe_wp_kses( $choice['text'] );
			}

			if ( isset( $choice['value'] ) ) {
				// Strip scripts but don't encode
				$allowed_protocols = wp_allowed_protocols();
				$choice['value']   = wp_kses_no_null( $choice['value'], array( 'slash_zero' => 'keep' ) );
				$choice['value']   = wp_kses_hook( $choice['value'], 'post', $allowed_protocols );
				$choice['value']   = wp_kses_split( $choice['value'], 'post', $allowed_protocols );
			}
		}

		return $choices;
	}

	/**
	 * Sanitize the field conditional logic object.
	 *
	 * @param array|null $logic The field conditional logic object.
	 *
	 * @return array|null
	 */
	public function sanitize_settings_conditional_logic( $logic = null ) {

		if ( is_null( $logic ) ) {
			$logic = &$this->conditionalLogic;
		}
		$logic = GFFormsModel::sanitize_conditional_logic( $logic );

		return $logic;
	}

	/**
	 * Applies wp_kses() if the current user doesn't have the unfiltered_html capability
	 *
	 * @param $html
	 * @param string $allowed_html
	 * @param array  $allowed_protocols
	 *
	 * @return string
	 */
	public function maybe_wp_kses( $html, $allowed_html = 'post', $allowed_protocols = array() ) {
		return GFCommon::maybe_wp_kses( $html, $allowed_html, $allowed_protocols );
	}

	/**
	 * Returns the allowed HTML tags for the field value.
	 *
	 * FALSE disallows HTML tags.
	 * TRUE allows all HTML tags allowed by wp_kses_post().
	 * A string of HTML tags allowed. e.g. '<p><a><strong><em>'
	 *
	 * @param null|int $form_id If not specified the form_id field property is used.
	 *
	 * @return bool|string TRUE, FALSE or a string of tags.
	 */
	public function get_allowable_tags( $form_id = null ) {
		if ( empty( $form_id ) ) {
			$form_id = $this->form_id;
		}
		$form_id    = absint( $form_id );
		$allow_html = $this->allow_html();

		/**
		 * Allows the list of tags allowed in the field value to be modified.
		 *
		 * Return FALSE to disallow HTML tags.
		 * Return TRUE to allow all HTML tags allowed by wp_kses_post().
		 * Return a string of HTML tags allowed. e.g. '<p><a><strong><em>'
		 *
		 * @since Unknown
		 *
		 * @param bool     $allow_html
		 * @param GF_Field $this
		 * @param int      $form_id
		 */
		$allowable_tags = apply_filters( 'gform_allowable_tags', $allow_html, $this, $form_id );
		$allowable_tags = apply_filters( "gform_allowable_tags_{$form_id}", $allowable_tags, $this, $form_id );

		return $allowable_tags;
	}

	/**
	 * Actions to be performed after the field has been converted to an object.
	 *
	 * @since  2.1.2.7
	 * @access public
	 *
	 * @uses    GF_Field::failed_validation()
	 * @uses    GF_Field::validation_message()
	 * @used-by GFFormsModel::convert_field_objects()
	 *
	 * @return void
	 */
	public function post_convert_field() {
		// Fix an issue where fields can show up as invalid in the form editor if the form was updated using the form object returned after a validation failure.
		unset( $this->failed_validation );
		unset( $this->validation_message );
	}

	/**
	 * Returns the choices for the Field Size setting.
	 *
	 * @since 2.4.19
	 *
	 * @param bool $values_only Indicates if only the choice values should be returned.
	 *
	 * @return array
	 */
	public function get_size_choices( $values_only = false ) {
		$choices = array(
			array( 'value' => 'small', 'text' => __( 'Small', 'gravityforms' ) ),
			array( 'value' => 'medium', 'text' => __( 'Medium', 'gravityforms' ) ),
			array( 'value' => 'large', 'text' => __( 'Large', 'gravityforms' ) ),
		);

		/**
		 * Allows the choices for Field Size setting to be customized.
		 *
		 * @since 2.4.19
		 *
		 * @param array $choices An array of choices (value and text) to be included in the Field Size setting.
		 */
		$choices = apply_filters( 'gform_field_size_choices', $choices );

		return $values_only ? wp_list_pluck( $choices, 'value' ) : $choices;
	}

	// # FIELD FILTER UI HELPERS ---------------------------------------------------------------------------------------

	/**
	 * Returns the filter settings for the current field.
	 *
	 * If overriding to add custom settings call the parent method first to get the default settings.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_settings() {
		$filter_settings = array(
			'key'  => $this->id,
			'text' => GFFormsModel::get_label( $this ),
		);

		$sub_filters = $this->get_filter_sub_filters();
		if ( ! empty( $sub_filters ) ) {
			$filter_settings['group']   = true;
			$filter_settings['filters'] = $sub_filters;
		} else {
			$filter_settings['preventMultiple'] = false;
			$filter_settings['operators']       = $this->get_filter_operators();

			$values = $this->get_filter_values();
			if ( ! empty( $values ) ) {
				$filter_settings['values'] = $values;
			}
		}

		return $filter_settings;
	}

	/**
	 * Returns the filter operators for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_operators() {
		return array( 'is', 'isnot', '>', '<' );
	}

	/**
	 * Returns the filters values setting for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_values() {
		if ( ! is_array( $this->choices ) ) {
			return array();
		}

		$choices = $this->choices;
		if ( $this->type == 'post_category' ) {
			foreach ( $choices as &$choice ) {
				$choice['value'] = $choice['text'] . ':' . $choice['value'];
			}
		}

		if ( $this->enablePrice ) {
			foreach ( $choices as &$choice ) {
				$price = rgempty( 'price', $choice ) ? 0 : GFCommon::to_number( rgar( $choice, 'price' ) );

				$choice['value'] .= '|' . $price;
			}
		}

		return $choices;
	}

	/**
	 * Returns the sub-filters for the current field.
	 *
	 * @since  2.4
	 *
	 * @return array
	 */
	public function get_filter_sub_filters() {
		return array();
	}

}
class-gf-field-email.php000066600000032211151264036140011127 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_Email extends GF_Field {

	public $type = 'email';

	public function get_form_editor_field_title() {
		return esc_attr__( 'Email', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'email_confirm_setting',
			'admin_label_setting',
			'size_setting',
			'rules_setting',
			'visibility_setting',
			'duplicate_setting',
			'default_value_setting',
			'placeholder_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	public function is_conditional_logic_supported() {
		return true;
	}

	public function get_entry_inputs() {
		return null;
	}

	/**
	 * Whether this field expects an array during submission.
	 *
	 * @since 2.4
	 *
	 * @return bool
	 */
	public function is_value_submission_array() {
		return (bool) $this->emailConfirmEnabled ;
	}

	public function validate( $value, $form ) {
		$email = is_array( $value ) ? rgar( $value, 0 ) : $value; // Form objects created in 1.8 will supply a string as the value.
		$is_blank = rgblank( $value ) || ( is_array( $value ) && rgempty( array_filter( $value ) ) );

		if ( ! $is_blank && ! GFCommon::is_valid_email( $email ) ) {
			$this->failed_validation  = true;
			$this->validation_message = empty( $this->errorMessage ) ? esc_html__( 'Please enter a valid email address.', 'gravityforms' ) : $this->errorMessage;
		} elseif ( $this->emailConfirmEnabled && ! empty( $email ) ) {
			$confirm = is_array( $value ) ? rgar( $value, 1 ) : $this->get_input_value_submission( 'input_' . $this->id . '_2' );
			if ( $confirm != $email ) {
				$this->failed_validation  = true;
				$this->validation_message = esc_html__( 'Your emails do not match.', 'gravityforms' );
			}
		}
	}

	public function get_field_input( $form, $value = '', $entry = null ) {
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		if ( is_array( $value ) ) {
			$value = array_values( $value );
		}

		$form_id  = absint( $form['id'] );
		$id       = absint( $this->id );
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";
		$form_id  = ( $is_entry_detail || $is_form_editor ) && empty( $form_id ) ? rgget( 'id' ) : $form_id;

		$size          = $this->size;
		$disabled_text = $is_form_editor ? "disabled='disabled'" : '';
		$class_suffix  = $is_entry_detail ? '_admin' : '';

		$class         = $this->emailConfirmEnabled ? '' : $size . $class_suffix; //Size only applies when confirmation is disabled
		$class         = esc_attr( $class );

		$form_sub_label_placement  = rgar( $form, 'subLabelPlacement' );
		$field_sub_label_placement = $this->subLabelPlacement;
		$is_sub_label_above        = $field_sub_label_placement == 'above' || ( empty( $field_sub_label_placement ) && $form_sub_label_placement == 'above' );
		$sub_label_class_attribute = $field_sub_label_placement == 'hidden_label' ? "class='hidden_sub_label screen-reader-text'" : '';

		$html_input_type = RGFormsModel::is_html5_enabled() ? 'email' : 'text';

		$required_attribute    = $this->isRequired ? 'aria-required="true"' : '';
		$invalid_attribute     = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';
		$aria_describedby      = $this->get_aria_describedby();

		$enter_email_field_input = GFFormsModel::get_input( $this, $this->id . '' );
		$confirm_field_input     = GFFormsModel::get_input( $this, $this->id . '.2' );

		$enter_email_label   = rgar( $enter_email_field_input, 'customLabel' ) != '' ? $enter_email_field_input['customLabel'] : esc_html__( 'Enter Email', 'gravityforms' );
		$enter_email_label   = gf_apply_filters( array( 'gform_email', $form_id ), $enter_email_label, $form_id );
		$confirm_email_label = rgar( $confirm_field_input, 'customLabel' ) != '' ? $confirm_field_input['customLabel'] : esc_html__( 'Confirm Email', 'gravityforms' );
		$confirm_email_label = gf_apply_filters( array( 'gform_email_confirm', $form_id ), $confirm_email_label, $form_id );

		$single_placeholder_attribute        = $this->get_field_placeholder_attribute();
		$enter_email_placeholder_attribute   = $this->get_input_placeholder_attribute( $enter_email_field_input );
		$confirm_email_placeholder_attribute = $this->get_input_placeholder_attribute( $confirm_field_input );

		if ( $is_form_editor ) {
			$single_style  = $this->emailConfirmEnabled ? "style='display:none;'" : '';
			$confirm_style = $this->emailConfirmEnabled ? '' : "style='display:none;'";

			if ( $is_sub_label_above ) {
				return "<div class='ginput_container ginput_container_email ginput_single_email' {$single_style}>
                            <input name='input_{$id}' type='{$html_input_type}' class='" . esc_attr( $class ) . "' disabled='disabled' {$single_placeholder_attribute} {$required_attribute} {$invalid_attribute} />
                            <div class='gf_clear gf_clear_complex'></div>
                        </div>
                        <div class='ginput_complex ginput_container ginput_container_email ginput_confirm_email' {$confirm_style} id='{$field_id}_container'>
                            <span id='{$field_id}_1_container' class='ginput_left'>
                                <label for='{$field_id}' {$sub_label_class_attribute}>{$enter_email_label}</label>
                                <input class='{$class}' type='text' name='input_{$id}' id='{$field_id}' disabled='disabled' {$enter_email_placeholder_attribute} {$required_attribute} {$invalid_attribute} />
                            </span>
                            <span id='{$field_id}_2_container' class='ginput_right'>
                                <label for='{$field_id}_2' {$sub_label_class_attribute}>{$confirm_email_label}</label>
                                <input class='{$class}' type='text' name='input_{$id}_2' id='{$field_id}_2' disabled='disabled' {$confirm_email_placeholder_attribute} {$required_attribute} {$invalid_attribute} />
                            </span>
                            <div class='gf_clear gf_clear_complex'></div>
                        </div>";
			} else {
				return "<div class='ginput_container ginput_container_email ginput_single_email' {$single_style}>
                            <input name='input_{$id}' type='{$html_input_type}' class='" . esc_attr( $class ) . "' disabled='disabled' {$single_placeholder_attribute} {$required_attribute} {$invalid_attribute} />
                            <div class='gf_clear gf_clear_complex'></div>
                        </div>
                        <div class='ginput_complex ginput_container ginput_container_email ginput_confirm_email' {$confirm_style} id='{$field_id}_container'>
                            <span id='{$field_id}_1_container' class='ginput_left'>
                                <input class='{$class}' type='text' name='input_{$id}' id='{$field_id}' disabled='disabled' {$enter_email_placeholder_attribute} {$required_attribute} {$invalid_attribute} />
                                <label for='{$field_id}' {$sub_label_class_attribute}>{$enter_email_label}</label>
                            </span>
                            <span id='{$field_id}_2_container' class='ginput_right'>
                                <input class='{$class}' type='text' name='input_{$id}_2' id='{$field_id}_2' disabled='disabled' {$confirm_email_placeholder_attribute} {$required_attribute} {$invalid_attribute} />
                                <label for='{$field_id}_2' {$sub_label_class_attribute}>{$confirm_email_label}</label>
                            </span>
                            <div class='gf_clear gf_clear_complex'></div>
                        </div>";
			}
		} else {

			if ( $this->emailConfirmEnabled && ! $is_entry_detail ) {
				$first_tabindex        = $this->get_tabindex();
				$last_tabindex         = $this->get_tabindex();
				$email_value           = is_array( $value ) ? rgar( $value, 0 ) : $value;
				$email_value = esc_attr( $email_value );
				$confirmation_value    = is_array( $value ) ? rgar( $value, 1 ) : rgpost( 'input_' . $this->id . '_2' );
				$confirmation_value = esc_attr( $confirmation_value );
				$confirmation_disabled = $is_entry_detail ? "disabled='disabled'" : $disabled_text;
				if ( $is_sub_label_above ) {
					return "<div class='ginput_complex ginput_container ginput_container_email' id='{$field_id}_container'>
                                <span id='{$field_id}_1_container' class='ginput_left'>
                                    <label for='{$field_id}'>" . $enter_email_label . "</label>
                                    <input class='{$class}' type='{$html_input_type}' name='input_{$id}' id='{$field_id}' value='{$email_value}' {$first_tabindex} {$disabled_text} {$enter_email_placeholder_attribute} {$required_attribute} {$invalid_attribute}/>
                                </span>
                                <span id='{$field_id}_2_container' class='ginput_right'>
                                    <label for='{$field_id}_2' {$sub_label_class_attribute}>{$confirm_email_label}</label>
                                    <input class='{$class}' type='{$html_input_type}' name='input_{$id}_2' id='{$field_id}_2' value='{$confirmation_value}' {$last_tabindex} {$confirmation_disabled} {$confirm_email_placeholder_attribute} {$required_attribute} {$invalid_attribute}/>
                                </span>
                                <div class='gf_clear gf_clear_complex'></div>
                            </div>";
				} else {
					return "<div class='ginput_complex ginput_container ginput_container_email' id='{$field_id}_container'>
                                <span id='{$field_id}_1_container' class='ginput_left'>
                                    <input class='{$class}' type='{$html_input_type}' name='input_{$id}' id='{$field_id}' value='{$email_value}' {$first_tabindex} {$disabled_text} {$enter_email_placeholder_attribute} {$required_attribute} {$invalid_attribute}/>
                                    <label for='{$field_id}' {$sub_label_class_attribute}>{$enter_email_label}</label>
                                </span>
                                <span id='{$field_id}_2_container' class='ginput_right'>
                                    <input class='{$class}' type='{$html_input_type}' name='input_{$id}_2' id='{$field_id}_2' value='{$confirmation_value}' {$last_tabindex} {$confirmation_disabled} {$confirm_email_placeholder_attribute} {$required_attribute} {$invalid_attribute}/>
                                    <label for='{$field_id}_2' {$sub_label_class_attribute}>{$confirm_email_label}</label>
                                </span>
                                <div class='gf_clear gf_clear_complex'></div>
                            </div>";
				}
			} else {
				$tabindex = $this->get_tabindex();
				$value    = esc_attr( $value );
				$class    = esc_attr( $class );

				return "<div class='ginput_container ginput_container_email'>
                            <input name='input_{$id}' id='{$field_id}' type='{$html_input_type}' value='$value' class='{$class}' {$tabindex} {$disabled_text} {$single_placeholder_attribute} {$required_attribute} {$invalid_attribute} {$aria_describedby}/>
                        </div>";
			}
		}
	}

	public function get_field_label_class(){
		return $this->emailConfirmEnabled ? 'gfield_label gfield_label_before_complex' : 'gfield_label';
	}

	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
		if ( GFCommon::is_valid_email( $value ) && $format == 'html'  ) {
			return sprintf( "<a href='mailto:%s'>%s</a>", esc_attr( $value ), esc_html( $value ) );
		}

		return esc_html( $value );
	}

	public function get_value_submission( $field_values, $get_from_post_global_var = true ) {

		if ( $this->emailConfirmEnabled && ! $this->is_entry_detail() && is_array( $this->inputs ) ) {
			$value[0] = $this->get_input_value_submission( 'input_' . $this->id, $this->inputName, $field_values, $get_from_post_global_var );
			$value[1] = $this->get_input_value_submission( 'input_' . $this->id . '_2', $this->inputName, $field_values, $get_from_post_global_var );
		} else {
			$value = $this->get_input_value_submission( 'input_' . $this->id, $this->inputName, $field_values, $get_from_post_global_var );
		}

		return $value;
	}

	/**
	 * Removes the "for" attribute in the field label when the confirmation input is enabled.
	 * Inputs are only allowed one label (a11y) and the inputs already have labels.
	 *
	 * @since  2.4
	 * @access public
	 *
	 * @param array $form The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_first_input_id( $form ) {
		return $this->emailConfirmEnabled ? '' : parent::get_first_input_id( $form );
	}

	// # FIELD FILTER UI HELPERS ---------------------------------------------------------------------------------------

	/**
	 * Returns the filter operators for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_operators() {
		$operators   = parent::get_filter_operators();
		$operators[] = 'contains';

		return $operators;
	}
}

GF_Fields::register( new GF_Field_Email() );
class-gf-field-text.php000066600000016253151264036140011034 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_Text extends GF_Field {

	public $type = 'text';

	public function get_form_editor_field_title() {
		return esc_attr__( 'Single Line Text', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'size_setting',
			'input_mask_setting',
			'maxlen_setting',
			'password_field_setting',
			'rules_setting',
			'visibility_setting',
			'duplicate_setting',
			'default_value_setting',
			'placeholder_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	public function is_conditional_logic_supported() {
		return true;
	}

	/**
	 * Return the result (bool) by setting $this->failed_validation.
	 * Return the validation message (string) by setting $this->validation_message.
	 *
	 * @since 2.4.11
	 *
	 * @param string|array $value The field value from get_value_submission().
	 * @param array        $form  The Form Object currently being processed.
	 */
	public function validate( $value, $form ) {

		if ( ! $this->maxLength || ! is_numeric( $this->maxLength ) ) {
			return;
		}

		if ( GFCommon::safe_strlen( $value ) > $this->maxLength ) {
			$this->failed_validation  = true;
			$this->validation_message = empty( $this->errorMessage ) ? esc_html__( 'The text entered exceeds the maximum number of characters.', 'gravityforms' ) : $this->errorMessage;
		}

	}

	public function get_field_input( $form, $value = '', $entry = null ) {
		$form_id         = absint( $form['id'] );
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$html_input_type = 'text';

		if ( $this->enablePasswordInput && ! $is_entry_detail ) {
			$html_input_type = 'password';
		}

		$id          = (int) $this->id;
		$field_id    = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		$value        = esc_attr( $value );
		$size         = $this->size;
		$class_suffix = $is_entry_detail ? '_admin' : '';
		$class        = $size . $class_suffix;
		$class        = esc_attr( $class );

		$max_length = is_numeric( $this->maxLength ) ? "maxlength='{$this->maxLength}'" : '';

		$tabindex              = $this->get_tabindex();
		$disabled_text         = $is_form_editor ? 'disabled="disabled"' : '';
		$placeholder_attribute = $this->get_field_placeholder_attribute();
		$required_attribute    = $this->isRequired ? 'aria-required="true"' : '';
		$invalid_attribute     = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';
		$aria_describedby      = $this->get_aria_describedby();


		$input = "<input name='input_{$id}' id='{$field_id}' type='{$html_input_type}' value='{$value}' class='{$class}' {$max_length} {$aria_describedby} {$tabindex} {$placeholder_attribute} {$required_attribute} {$invalid_attribute} {$disabled_text}/>";

		return sprintf( "<div class='ginput_container ginput_container_text'>%s</div>", $input );
	}

	public function allow_html() {
		return in_array( $this->type, array( 'post_custom_field', 'post_tags' ) ) ? true : false;
	}

	/**
	 * Gets merge tag values.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GF_Field::get_allowable_tags()
	 *
	 * @param array|string $value      The value of the input.
	 * @param string       $input_id   The input ID to use.
	 * @param array        $entry      The Entry Object.
	 * @param array        $form       The Form Object
	 * @param string       $modifier   The modifier passed.
	 * @param array|string $raw_value  The raw value of the input.
	 * @param bool         $url_encode If the result should be URL encoded.
	 * @param bool         $esc_html   If the HTML should be escaped.
	 * @param string       $format     The format that the value should be.
	 * @param bool         $nl2br      If the nl2br function should be used.
	 *
	 * @return string The processed merge tag.
	 */
	public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) {

		if ( $format === 'html' ) {
			$value = $raw_value;
			if ( $nl2br ) {
				$value = nl2br( $value );
			}

			$form_id        = absint( $form['id'] );
			$allowable_tags = $this->get_allowable_tags( $form_id );

			if ( $allowable_tags === false ) {
				// The value is unsafe so encode the value.
				$return = esc_html( $value );
			} else {
				// The value contains HTML but the value was sanitized before saving.
				$return = $value;
			}
		} else {
			$return = $value;
		}

		return $return;
	}

	/**
	 * Format the entry value safe for displaying on the entry list page.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GF_Field::get_allowable_tags()
	 *
	 * @param string $value    The field value.
	 * @param array  $entry    The Entry Object currently being processed.
	 * @param string $field_id The field or input ID currently being processed.
	 * @param array  $columns  The properties for the columns being displayed on the entry list page.
	 * @param array  $form     The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) {

		if ( is_array( $value ) ) {
			return '';
		}

		$form_id = absint( $form['id'] );
		$allowable_tags = $this->get_allowable_tags( $form_id );

		if ( $allowable_tags === false ) {
			// The value is unsafe so encode the value.
			$return = esc_html( $value );
		} else {
			// The value contains HTML but the value was sanitized before saving.
			$return = $value;
		}

		return $return;
	}

	/**
	 * Format the entry value safe for displaying on the entry detail page and for the {all_fields} merge tag.
	 *
	 * @param string|array $value The field value.
	 * @param string $currency The entry currency code.
	 * @param bool|false $use_text When processing choice based fields should the choice text be returned instead of the value.
	 * @param string $format The format requested for the location the merge is being used. Possible values: html, text or url.
	 * @param string $media The location where the value will be displayed. Possible values: screen or email.
	 *
	 * @return string
	 */
	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {

		if ( is_array( $value ) ) {
			return '';
		}

		if ( $format === 'html' ) {
			$value = nl2br( $value );

			$allowable_tags = $this->get_allowable_tags();

			if ( $allowable_tags === false ) {
				// The value is unsafe so encode the value.
				$return = esc_html( $value );
			} else {
				// The value contains HTML but the value was sanitized before saving.
				$return = $value;
			}
		} else {
			$return = $value;
		}

		return $return;
	}

	// # FIELD FILTER UI HELPERS ---------------------------------------------------------------------------------------

	/**
	 * Returns the filter operators for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_operators() {
		$operators   = parent::get_filter_operators();
		$operators[] = 'contains';

		return $operators;
	}

}

GF_Fields::register( new GF_Field_Text() );
class-gf-field-option.php000066600000001217151264036140011352 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_Option extends GF_Field {

	public $type = 'option';

	function get_form_editor_field_settings() {
		return array(
			'product_field_setting',
			'option_field_type_setting',
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'label_setting',
			'admin_label_setting',
			'label_placement_setting',
			'default_value_setting',
			'placeholder_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	public function get_form_editor_field_title() {
		return esc_attr__( 'Option', 'gravityforms' );
	}

}

GF_Fields::register( new GF_Field_Option() );class-gf-field-phone.php000066600000024012151264036140011151 0ustar00<?php

// If Gravity Forms isn't loaded, bail.
if ( ! class_exists( 'GFForms' ) ) {
	die();
}

/**
 * Class GF_Field_Phone
 *
 * Handles the behavior of Phone fields.
 *
 * @since Unknown
 */
class GF_Field_Phone extends GF_Field {

	/**
	 * Defines the field type.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @var string The field type.
	 */
	public $type = 'phone';

	/**
	 * Defines the field title to be used in the form editor.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFCommon::get_field_type_title()
	 *
	 * @return string The field title. Translatable and escaped.
	 */
	public function get_form_editor_field_title() {
		return esc_attr__( 'Phone', 'gravityforms' );
	}

	/**
	 * Defines the field settings available within the field editor.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @return array The field settings available for the field.
	 */
	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'size_setting',
			'rules_setting',
			'visibility_setting',
			'duplicate_setting',
			'default_value_setting',
			'placeholder_setting',
			'description_setting',
			'phone_format_setting',
			'css_class_setting',
		);
	}

	/**
	 * Defines if conditional logic is supported in this field type.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFFormDetail::inline_scripts()
	 * @used-by GFFormSettings::output_field_scripts()
	 *
	 * @return bool true
	 */
	public function is_conditional_logic_supported() {
		return true;
	}

	/**
	 * Validates inputs for the Phone field.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFFormDisplay::validate()
	 * @uses    GF_Field_Phone::get_phone_format()
	 * @uses    GF_Field_Phone::$validation_message
	 * @uses    GF_Field_Phone::$errorMessage
	 *
	 * @param array|string $value The field value to be validated.
	 * @param array        $form  The Form Object.
	 *
	 * @return void
	 */
	public function validate( $value, $form ) {
		$phone_format = $this->get_phone_format();

		if ( rgar( $phone_format, 'regex' ) && $value !== '' && $value !== 0 && ! preg_match( $phone_format['regex'], $value ) ) {
			$this->failed_validation = true;
			if ( ! empty( $this->errorMessage ) ) {
				$this->validation_message = $this->errorMessage;
			}
		}
	}

	/**
	 * Returns the field input.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFCommon::get_field_input()
	 * @uses    GF_Field::is_entry_detail()
	 * @uses    GF_Field::is_form_editor()
	 * @uses    GF_Field_Phone::$failed_validation
	 * @uses    GF_Field_Phone::get_phone_format()
	 * @uses    GFFormsModel::is_html5_enabled()
	 * @uses    GF_Field::get_field_placeholder_attribute()
	 * @uses    GF_Field_Phone::$isRequired
	 * @uses    GF_Field::get_tabindex()
	 *
	 * @param array      $form  The Form Object.
	 * @param string     $value The value of the input. Defaults to empty string.
	 * @param null|array $entry The Entry Object. Defaults to null.
	 *
	 * @return string The HTML markup for the field.
	 */
	public function get_field_input( $form, $value = '', $entry = null ) {

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

		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$form_id  = $form['id'];
		$id       = intval( $this->id );
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		$size          = $this->size;
		$disabled_text = $is_form_editor ? "disabled='disabled'" : '';
		$class_suffix  = $is_entry_detail ? '_admin' : '';
		$class         = $size . $class_suffix;
		$class         = esc_attr( $class );

		$instruction_div = '';
		if ( $this->failed_validation ) {
			$phone_format = $this->get_phone_format();
			if ( rgar( $phone_format, 'instruction' ) ) {
				$instruction_div = sprintf( "<div class='instruction validation_message'>%s %s</div>", esc_html__( 'Phone format:', 'gravityforms' ), $phone_format['instruction'] );
			}
		}

		$html_input_type       = RGFormsModel::is_html5_enabled() ? 'tel' : 'text';
		$placeholder_attribute = $this->get_field_placeholder_attribute();
		$required_attribute    = $this->isRequired ? 'aria-required="true"' : '';
		$invalid_attribute     = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';
		$aria_describedby      = $this->get_aria_describedby();

		$tabindex = $this->get_tabindex();

		return sprintf( "<div class='ginput_container ginput_container_phone'><input name='input_%d' id='%s' type='{$html_input_type}' value='%s' class='%s' {$tabindex} {$placeholder_attribute} {$required_attribute} {$invalid_attribute} {$aria_describedby} %s/>{$instruction_div}</div>", $id, $field_id, esc_attr( $value ), esc_attr( $class ), $disabled_text );

	}

	/**
	 * Gets the value of the submitted field.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFFormsModel::get_field_value()
	 * @uses    GF_Field::get_value_submission()
	 * @uses    GF_Field_Phone::sanitize_entry_value()
	 *
	 * @param array $field_values             The dynamic population parameter names with their corresponding values to be populated.
	 * @param bool  $get_from_post_global_var Whether to get the value from the $_POST array as opposed to $field_values. Defaults to true.
	 *
	 * @return array|string
	 */
	public function get_value_submission( $field_values, $get_from_post_global_var = true ) {

		$value = parent::get_value_submission( $field_values, $get_from_post_global_var );
		$value = $this->sanitize_entry_value( $value, $this->formId );

		return $value;
	}

	/**
	 * Sanitizes the entry value.
	 *
	 * @since Unknown
	 * @access public
	 *
	 * @used-by GF_Field_Phone::get_value_save_entry()
	 * @used-by GF_Field_Phone::get_value_submission()
	 *
	 * @param string $value   The value to be sanitized.
	 * @param int    $form_id The form ID of the submitted item.
	 *
	 * @return string The sanitized value.
	 */
	public function sanitize_entry_value( $value, $form_id ) {
		$value = is_array( $value ) ? array_map( 'sanitize_text_field', $value ) : sanitize_text_field( $value );
		return $value;
	}

	/**
	 * Gets the field value when an entry is being saved.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFFormsModel::prepare_value()
	 * @uses    GF_Field_Phone::sanitize_entry_value()
	 * @uses    GF_Field_Phone::$phoneFormat
	 *
	 * @param string $value      The input value.
	 * @param array  $form       The Form Object.
	 * @param string $input_name The input name.
	 * @param int    $lead_id    The Entry ID.
	 * @param array  $lead       The Entry Object.
	 *
	 * @return string The field value.
	 */
	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {
		$value = $this->sanitize_entry_value( $value, $form['id'] );

		if ( $this->phoneFormat == 'standard' && preg_match( '/^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$/', $value, $matches ) ) {
			$value = sprintf( '(%s) %s-%s', $matches[1], $matches[2], $matches[3] );
		}

		return $value;
	}

	/**
	 * Outputs any inline scripts to be used when the page is rendered.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GF_Field::register_form_init_scripts()
	 * @uses    GF_Field_Phone::get_phone_format()
	 *
	 * @param array $form The Form Object.
	 *
	 * @return string The inline scripts.
	 */
	public function get_form_inline_script_on_page_render( $form ) {
		$script       = '';
		$phone_format = $this->get_phone_format();

		if ( rgar( $phone_format, 'mask' ) ) {
			$script = "jQuery('#input_{$form['id']}_{$this->id}').mask('{$phone_format['mask']}').bind('keypress', function(e){if(e.which == 13){jQuery(this).blur();} } );";
		}
		return $script;
	}

	/**
	 * Sanitizes the field settings.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFFormDetail::add_field()
	 * @used-by GFFormsModel::sanitize_settings()
	 * @uses    GF_Field::sanitize_settings()
	 * @uses    GF_Field_Phone::get_phone_format()
	 * @uses    GF_Field_Phone::$phoneFormat
	 *
	 * @return void
	 */
	public function sanitize_settings() {
		parent::sanitize_settings();

		if ( ! $this->get_phone_format() ) {
			$this->phoneFormat = 'standard';
		}
	}

	/**
	 * Get an array of phone formats.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GF_Field_Phone::get_phone_format()
	 *
	 * @param null|int $form_id The ID of the current form or null to use the value from the current fields form_id property. Defaults to null.
	 *
	 * @return array The phone formats available.
	 */
	public function get_phone_formats( $form_id = null ) {

		if ( empty( $form_id ) ) {
			$form_id = $this->formId;
		}
		$form_id = absint( $form_id );

		$phone_formats = array(
			'standard'      => array(
				'label'       => '(###) ###-####',
				'mask'        => '(999) 999-9999',
				'regex'       => '/^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$/',
				'instruction' => '(###) ###-####',
			),
			'international' => array(
				'label'       => __( 'International', 'gravityforms' ),
				'mask'        => false,
				'regex'       => false,
				'instruction' => false,
			),
		);

		/**
		 * Allow custom phone formats to be defined.
		 *
		 * @since 2.0.0
		 *
		 * @param array $phone_formats The phone formats.
		 * @param int   $form_id       The ID of the current form.
		 */
		return gf_apply_filters( array( 'gform_phone_formats', $form_id ), $phone_formats, $form_id );
	}

	/**
	 * Get the properties for the fields selected phone format.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GF_Field_Phone::get_field_input()
	 * @used-by GF_Field_Phone::get_form_inline_script_on_page_render()
	 * @used-by GF_Field_Phone::sanitize_settings()
	 * @used-by GF_Field_Phone::validate()
	 * @uses    GF_Field_Phone::get_phone_formats()
	 * @uses    GF_Field_Phone::$phoneFormat
	 *
	 * @return array The phone format.
	 */
	public function get_phone_format() {
		$phone_formats = $this->get_phone_formats();

		return rgar( $phone_formats, $this->phoneFormat );
	}
}

// Register the phone field with the field framework.
GF_Fields::register( new GF_Field_Phone() );
class-gf-field-website.php000066600000007367151264036140011520 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_Website extends GF_Field {

	public $type = 'website';

	public function get_form_editor_field_title() {
		return esc_attr__( 'Website', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'size_setting',
			'rules_setting',
			'visibility_setting',
			'duplicate_setting',
			'default_value_setting',
			'placeholder_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	public function is_conditional_logic_supported() {
		return true;
	}

	public function validate( $value, $form ) {
		if ( empty( $value ) || in_array( $value, array( 'http://', 'https://' ) ) ) {
			$value = '';
			if ( $this->isRequired ) {
				$this->failed_validation  = true;
				$this->validation_message = empty( $this->errorMessage ) ? esc_html__( 'This field is required.', 'gravityforms' ) : $this->errorMessage;
			}
		}

		if ( ! empty( $value ) && ! GFCommon::is_valid_url( $value ) ) {
			$this->failed_validation  = true;
			$this->validation_message = empty( $this->errorMessage ) ? esc_html__( 'Please enter a valid Website URL (e.g. http://www.gravityforms.com).', 'gravityforms' ) : $this->errorMessage;
		}
	}

	public function get_field_input( $form, $value = '', $entry = null ) {
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$form_id  = $form['id'];
		$id       = intval( $this->id );
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		$size            = $this->size;
		$disabled_text   = $is_form_editor ? "disabled='disabled'" : '';
		$class_suffix    = $is_entry_detail ? '_admin' : '';
		$class           = $size . $class_suffix;
		$class           = esc_attr( $class );
		$is_html5        = RGFormsModel::is_html5_enabled();
		$html_input_type = $is_html5 ? 'url' : 'text';

		$placeholder_attribute = $this->get_field_placeholder_attribute();
		$required_attribute    = $this->isRequired ? 'aria-required="true"' : '';
		$invalid_attribute     = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';
		$aria_describedby      = $this->get_aria_describedby();

		$tabindex = $this->get_tabindex();
		$value    = esc_attr( $value );
		$class    = esc_attr( $class );

		return "<div class='ginput_container ginput_container_website'>
                    <input name='input_{$id}' id='{$field_id}' type='$html_input_type' value='{$value}' class='{$class}' {$tabindex} {$aria_describedby} {$disabled_text} {$placeholder_attribute} {$required_attribute} {$invalid_attribute}/>
                </div>";
	}

	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
		$safe_value = esc_url( $value );
		return GFCommon::is_valid_url( $value ) && $format == 'html' ? "<a href='$safe_value' target='_blank'>$safe_value</a>" : $safe_value;
	}

	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {

		if ( empty( $value ) || in_array( $value, array( 'http://', 'https://' ) ) ) {
			return '';
		}

		$value = filter_var( $value, FILTER_VALIDATE_URL );

		return $value ? $value : '';
	}

	// # FIELD FILTER UI HELPERS ---------------------------------------------------------------------------------------

	/**
	 * Returns the filter operators for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_operators() {
		$operators   = parent::get_filter_operators();
		$operators[] = 'contains';

		return $operators;
	}

}

GF_Fields::register( new GF_Field_Website() );
class-gf-field-total.php000066600000006132151264036140011166 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_Total extends GF_Field {

	public $type = 'total';

	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'label_setting',
			'admin_label_setting',
			'label_placement_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	public function get_form_editor_field_title() {
		return esc_attr__( 'Total', 'gravityforms' );
	}

	public function get_field_input( $form, $value = '', $entry = null ) {
		$form_id         = absint( $form['id'] );
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$id          = (int) $this->id;
		$field_id    = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		if ( $is_entry_detail ) {
			return "<div class='ginput_container ginput_container_total'>
						<input type='text' name='input_{$id}' value='{$value}' />
					</div>";
		} else {
			return "<div class='ginput_container ginput_container_total'>
						<span class='ginput_total ginput_total_{$form_id}' aria-live='polite'>" . GFCommon::to_money( '0' ) . "</span>
						<input type='hidden' name='input_{$id}' id='{$field_id}' class='gform_hidden'/>
					</div>";
		}

	}

	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
		return GFCommon::to_money( $value, $currency );
	}

	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {
		$lead  = empty( $lead ) ? RGFormsModel::get_lead( $lead_id ) : $lead;
		$value = GFCommon::get_order_total( $form, $lead );

		return $value;
	}

	public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) {
		return GFCommon::to_money( $value, $entry['currency'] );
	}

	/**
	 * Gets merge tag values.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GFCommon::to_number()
	 * @uses GFCommon::to_money()
	 * @uses GFCommon::format_variable_value()
	 *
	 * @param array|string $value      The value of the input.
	 * @param string       $input_id   The input ID to use.
	 * @param array        $entry      The Entry Object.
	 * @param array        $form       The Form Object
	 * @param string       $modifier   The modifier passed.
	 * @param array|string $raw_value  The raw value of the input.
	 * @param bool         $url_encode If the result should be URL encoded.
	 * @param bool         $esc_html   If the HTML should be escaped.
	 * @param string       $format     The format that the value should be.
	 * @param bool         $nl2br      If the nl2br function should be used.
	 *
	 * @return string The processed merge tag.
	 */
	public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) {
		$format_numeric = in_array( 'price', $this->get_modifiers() );

		$value = $format_numeric ? GFCommon::to_number( $value ) : GFCommon::to_money( $value );

		return GFCommon::format_variable_value( $value, $url_encode, $esc_html, $format );
	}


}

GF_Fields::register( new GF_Field_Total() );class-gf-field-repeater.php000066600000072706151264036140011664 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}

/**
 * The Repeater field.
 *
 * 2.4
 *
 * Class GF_Field_Repeater
 */
class GF_Field_Repeater extends GF_Field {

	public $type = 'repeater';

	/**
	 * Returns the field title for the form editor.
	 *
	 * @since 2.4
	 *
	 * @return string
	 */
	public function get_form_editor_field_title() {
		return esc_attr__( 'Repeater', 'gravityforms' );
	}

	/**
	 * Returns the field settings for the form editor.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'rules_setting',
			'visibility_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	public function get_form_editor_button() {
		return array();
	}

	/**
	 * Used to determine the required validation result.
	 *
	 * @since 2.4
	 *
	 * @param int $form_id The ID of the form currently being processed.
	 *
	 * @return bool
	 */
	public function is_value_submission_empty( $form_id ) {
		return false;
	}

	/**
	 * Validates each sub-field.
	 *
	 * @since 2.4
	 *
	 * @param string|array $items The field values from get_value_submission().
	 * @param array        $form  The Form Object currently being processed.
	 */
	public function validate( $items, $form ) {

		if ( empty( $items ) ) {
			return;
		}

		/* @var GF_Field[] $fields */
		$fields = $this->fields;

		foreach ( $items as $i => $item ) {
			foreach ( $fields as $field ) {

				$field->set_context_property( 'itemIndex', $i );

				$inputs = $field->get_entry_inputs();
				if ( is_array( $inputs ) ) {
					$field_value = array();
					$field_keys  = array_keys( $item );
					foreach ( $field_keys as $input_id ) {
						if ( is_numeric( $input_id ) && absint( $input_id ) == absint( $field->id ) ) {
							$field_value[ $input_id ] = $item[ $input_id ];
						}
					}
				} else {
					$field_value = isset( $item[ $field->id ] ) ? $item[ $field->id ] : '';
				}

				if ( $field->isRequired && $field->is_value_empty( $field_value ) ) {
					$field->failed_validation = true;
				} else {
					$field->validate( $field_value, $form );
				}

				$custom_validation_result = gf_apply_filters( array( 'gform_field_validation', $form['id'], $field->id ), array(
					'is_valid' => $field->failed_validation ? false : true,
					'message'  => $field->validation_message
				), $field_value, $form, $field );
				$this->failed_validation  = rgar( $custom_validation_result, 'is_valid' ) ? false : true;

				// Reset the field validation and item index.
				$field->failed_validation = false;
				$field->set_context_property( 'itemIndex', null );

				if ( $this->failed_validation ) {
					// One field has failed validation so the entire repeater fails.
					return;
				}

			}
		}
	}

	/**
	 * Retrieve the field value on submission.
	 *
	 * @since 2.4
	 *
	 * @param array     $field_values             The dynamic population parameter names with their corresponding values to be populated.
	 * @param bool|true $get_from_post_global_var Whether to get the value from the $_POST array as opposed to $field_values.
	 *
	 * @return array|string
	 */
	public function get_value_submission( $field_values, $get_from_post_global_var = true ) {

		$submission_values = $this->get_value_submission_recursive( $field_values, $get_from_post_global_var );

		$items = $this->hydrate( $submission_values );
		return $items[ $this->id ];
	}

	/**
	 * Returns the submission values for the repeater.
	 *
	 * @since 2.4
	 *
	 * @param array $field_values
	 * @param bool $get_from_post_global_var
	 *
	 * @return array
	 */
	public function get_value_submission_recursive( $field_values, $get_from_post_global_var ) {

		$items = array();

		if ( isset( $this->fields ) && is_array( $this->fields ) ) {

			foreach ( $this->fields as $sub_field ) {
				/* @var GF_Field $sub_field */
				if ( isset( $sub_field->fields ) && is_array( $sub_field->fields ) ) {
					/* @var GF_Field_Repeater_Table $sub_field */
					$field_items = $sub_field->get_value_submission_recursive( $field_values, $get_from_post_global_var );
				} else {
					$values = $sub_field->get_value_submission( $field_values, $get_from_post_global_var );

					if ( is_array( $sub_field->get_entry_inputs() ) ) {
						$prefix = '';
					} else {
						$prefix = $sub_field->id . '_';
					}

					$field_items = $this->flatten( $values, $prefix, $sub_field->is_value_submission_array() );
				}
				$items = array_merge( $items, $field_items );
			}
		} else {
			$values      = $this->get_value_submission( $field_values, $get_from_post_global_var );
			$field_items = $this->flatten( $values, $this->id . '_', $this->is_value_submission_array() );
			$items       = array_merge( $items, $field_items );
		}

		return $items;

	}

	/**
	 * Utility to flatten array values recursively so they can be saved with the appropriate index.
	 *
	 * @since 2.4
	 *
	 * @param        $array
	 * @param string $prefix
	 * @param bool   $field_value_is_array
	 *
	 * @return array
	 */
	private function flatten( $array, $prefix = '', $field_value_is_array = false ) {
		$result = array();
		if ( ! is_array( $array ) ) {
			return $result;
		}
		foreach ( $array as $key => $value ) {
			if ( is_array( $value ) ) {
				if ( $field_value_is_array && ! is_array( $value[0] ) ) {
					$result[ $prefix . $key ] = $value;
				} else {
					$result = $result + $this->flatten( $value, $prefix . $key . '_' );
				}
			} else {
				$result[ $prefix . $key ] = $value;
			}
		}

		return $result;
	}

	/**
	 * Returns the field inner markup.
	 *
	 * @since 2.4
	 *
	 * @param array        $form  The Form Object currently being processed.
	 * @param string|array $values The field values. From default/dynamic population, $_POST, or a resumed incomplete submission.
	 * @param null|array   $entry Null or the Entry Object currently being edited.
	 *
	 * @return string
	 */
	public function get_field_input( $form, $values = '', $entry = null ) {

		if ( $this->is_form_editor() ) {
			return sprintf( "<p>%s</p>", $this->label );
		}

		if ( empty( $values ) ) {
			$values = array( '' );
		}

		$input_top = $this->get_input_top( $values );

		$items = $this->get_input_items( $values, $entry );

		$html = $input_top . $items . $this->get_input_bottom();

		$max_items = intval( $this->maxItems );

		return sprintf( "<div class='gfield_repeater_wrapper' data-max_items='{$max_items}'>%s</div>", $html );
	}

	/**
	 * Returns the markup for the top of the repeater container.
	 *
	 * This method must return the opening tag for the container and this tag must have the class 'gfield_repeater_container'
	 *
	 * @since 2.4
	 *
	 * @param $values
	 *
	 * @return string
	 */
	public function get_input_top( $values ) {
		$html = "<fieldset class='gfield_repeater gfield_repeater_container'>\n";
		$label = esc_html( $this->label );
		$html .= "<legend class='gfield_label'>{$label}</legend>";
		return $html;
	}

	/**
	 * Returns the markup for the items.
	 *
	 * This method must return a single HTML element with the class 'gfield_repeater_items'. This elemment must contain
	 * all the items as direct children and each item must have the class 'gfield_repeater_item'.
	 *
	 * @since 2.4
	 *
	 * @param $values
	 * @param $entry
	 *
	 * @return string
	 */
	public function get_input_items( $values, $entry ) {

		/* @var GF_Field[] $fields */
		$fields = $this->fields;

		$form = GFAPI::get_form( $this->formId );

		$rows = '<div class="gfield_repeater_items">';

		$i = 0;
		foreach ( $values as $value ) {
			$row = "<div class='gfield_repeater_item'>";
			foreach ( $fields as $field ) {

				$field_value = $this->get_field_value( $field, $value );

				$field->set_context_property( 'itemIndex', $i );

				$field_input = $this->get_sub_field_input( $field, $form, $field_value, $entry, $i );

				$row .= "<div class='gfield_repeater_cell'>" . $field_input . '</div>';

				$field->set_context_property( 'itemIndex', null );
			}
			$buttons = $this->get_buttons( $values );
			$row .= "<div class='gfield_repeater_buttons'>{$buttons}</div>";
			$row .= '</div>';
			$rows .= $row;
			$i++;
		}
		$rows .= '</div>';
		return $rows;
	}

	/**
	 * Return the markup for the bottom of the repeater. Close the tags opened in the top.
	 *
	 * @since 2.4
	 *
	 * @return string
	 */
	public function get_input_bottom() {
		return '</fieldset>';
	}

	public function get_field_content( $value, $force_frontend_label, $form ) {

		$is_form_editor  = $this->is_form_editor();
		$is_entry_detail = $this->is_entry_detail();
		$is_admin        = $is_form_editor || $is_entry_detail;

		$admin_buttons = $this->get_admin_buttons();

		$description = $this->get_description( $this->description, 'gfield_description' );
		if ( $this->is_description_above( $form ) ) {
			$clear         = $is_admin ? "<div class='gf_clear'></div>" : '';
			$field_content = sprintf( "%s%s{FIELD}$clear", $admin_buttons, $description );
		} else {
			$field_content = sprintf( "%s{FIELD}%s", $admin_buttons, $description );
		}

		return $field_content;
	}

	/**
	 * Returns the repeater buttons.
	 *
	 * @since 2.4
	 *
	 * @return string
	 */
	public function get_buttons( $values ) {
		$is_form_editor = $this->is_form_editor();

		$delete_display = count( $values ) == 1 ? 'visibility:hidden;' : '';

		$add_events    = $is_form_editor ? '' : "onclick='gformAddRepeaterItem(this)' onkeypress='gformAddRepeaterItem(this)'";
		$delete_events = $is_form_editor ? '' : sprintf( "onclick='if(confirm(\"%s\")){gformDeleteRepeaterItem(this)};' onkeypress='gformDeleteRepeaterItem(this)'", esc_js( __( 'Are you sure you want to remove this item?', 'gravityforms' ) ) );

		$disabled_icon_class = ! empty( $this->maxItems ) && count( $values ) >= intval( $this->maxItems ) ? 'gfield_icon_disabled' : '';

		$add_button_text    = $this->addButtonText ? $this->addButtonText : '&#43;';
		$remove_button_text = $this->removeButtonText ? $this->removeButtonText : '&#45;' ;

		$add_button_class = $this->addButtonText ? 'add_repeater_item_text' : 'add_repeater_item_plus';
		$remove_button_class = $this->removeButtonText ? 'remove_repeater_item_text' : 'remove_repeater_item_minus';
		$html = "<button type='button' class='add_repeater_item {$disabled_icon_class} {$add_button_class}' {$add_events}>" . $add_button_text . "</button>" .
		        "<button type='button' class='remove_repeater_item {$remove_button_class}' {$delete_events} style='{$delete_display}'>" . $remove_button_text . "</button>";

		return $html;
	}

	/**
	 * Gets merge tag values.
	 *
	 * @since 2.4
	 *
	 * @param array|string $value      The value of the input.
	 * @param string       $input_id   The input ID to use.
	 * @param array        $entry      The Entry Object.
	 * @param array        $form       The Form Object
	 * @param string       $modifier   The modifier passed.
	 * @param array|string $raw_value  The raw value of the input.
	 * @param bool         $url_encode If the result should be URL encoded.
	 * @param bool         $esc_html   If the HTML should be escaped.
	 * @param string       $format     The format that the value should be.
	 * @param bool         $nl2br      If the nl2br function should be used.
	 *
	 * @return string The processed merge tag.
	 */
	public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) {

		$use_value = in_array( 'value', $this->get_modifiers() );
		$use_text  = ! $use_value;

		if ( $format == 'html' ) {
			$media = $esc_html ? 'screen' :'email';
			$merge_tag = $this->get_value_entry_detail( $raw_value, $entry['currency'], $use_text, $format, $media );
		} else {
			$merge_tag = $this->get_value_export_recursive( $entry, $input_id, $use_text, false, 0, '&nbsp;&nbsp;&nbsp;&nbsp;' );
		}

		return $merge_tag;
	}

	/**
	 * Format the entry value safe for displaying on the entry list page.
	 *
	 * @since 2.4
	 *
	 * @param string $value    The field value.
	 * @param array  $entry    The Entry Object currently being processed.
	 * @param string $field_id The field or input ID currently being processed.
	 * @param array  $columns  The properties for the columns being displayed on the entry list page.
	 * @param array  $form     The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) {

		/* translators: %d: the number of items in value of the repeater field. */
		$display_value = is_array( $value ) ? sprintf( esc_html__( 'Number of items: %d' ), count( $value ) ) : '';

		return $display_value;
	}

	/**
	 * Format the entry value safe for displaying on the entry detail page and for the {all_fields} merge tag.
	 *
	 * @since 2.4
	 *
	 * @param string|array $item_values The field value.
	 * @param string $currency The entry currency code.
	 * @param bool|false $use_text When processing choice based fields should the choice text be returned instead of the value.
	 * @param string $format The format requested for the location the merge is being used. Possible values: html, text or url.
	 * @param string $media The location where the value will be displayed. Possible values: screen or email.
	 *
	 * @return string
	 */
	public function get_value_entry_detail( $item_values, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {

		if ( $format == 'text' ) {
			return $this->get_value_export_recursive( array( $this->id => $item_values ), $this->id, $use_text, false, 0, '&nbsp;&nbsp;&nbsp;&nbsp;' );
		}

		$repeater_style        = $media == 'email' ? "style='padding: 5px 0 0 15px;font-size: 14px'" : '';
		$label_style           = $media == 'email' ? "style='color: rgba(35, 40, 45, 1.000);font-weight:600; padding-top:10px;font-size: 14px'" : '';
		$sub_field_label_style = $media == 'email' ? "style='color:rgb(155, 154, 154);padding-top:8px;font-size: 14px;'" : '';

		/* @var GF_Field[] $fields */
		$fields = $this->fields;
		$html   = "<div class='gfield_repeater' {$repeater_style}>";
		$repeater_label = $this->nestingLevel === 0 ? '' : $this->label;
		$html   .= "<div class='gfield_label' {$label_style}>{$repeater_label}</div>";
		$html   .= '<div class="gfield_repeater_items">';
		foreach ( $item_values as $item_value ) {
			$html .= '<div class="gfield_repeater_item">';
			foreach ( $fields as $sub_field ) {
				if ( $sub_field->fields ) {
					$sub_field_value = $item_value[ $sub_field->id ];
				} else {
					$sub_field_value = $this->get_field_value( $sub_field, $item_value );
				}
				$label = $sub_field->get_field_label( true, $item_values );
				$label = empty( $sub_field->fields ) ? "<div class='gfield_repeater_label' {$sub_field_label_style}>{$label}</div>" : '';
				$value = $sub_field->get_value_entry_detail( $sub_field_value, $currency, $use_text, 'html', $media );
				$value = "<div class='gfield_repeater_value' style='color:rgba(117, 117, 117, 1);font-size: 14px'>{$value}</div>";
				$html .= '<div class="gfield_repeater_cell">' . $label . $value . '</div>';
			}
			$html .= '</div>';
		}
		$html .= '</div>';
		$html .= '</div>';

		return $html;
	}

	/**
	 * Returns the value for a field inside a repeater.
	 *
	 * @since 2.4
	 *
	 * @param GF_Field $field
	 * @param array|string $value
	 *
	 * @return array|string
	 */
	public function get_field_value( $field, $value ) {
		if ( $field->fields ) {
			$field_value = isset( $value[ $field->id ] ) ? $value[ $field->id ] : '';
		} else {
			$inputs = $field->get_entry_inputs();
			if ( is_array( $value ) ) {
				if ( is_array( $inputs ) ) {
					$field_value = array();
					$field_keys = array_keys( $value );
					natsort( $field_keys );
					foreach ( $field_keys as $input_id ) {
						if ( is_numeric( $input_id ) && absint( $input_id ) == absint( $field->id ) ) {
							$val = $value[ $input_id ];
							$field_value[ $input_id ] = $val;
						}
					}
				} else {
					$field_value = isset( $value[ $field->id ] ) ? $value[ $field->id ] : '';
				}
			} else {
				$field_value = '';
			}
		}

		return $field_value;
	}

	/**
	 * Returns the input markup for a field inside a repeater.
	 *
	 * Appends the item index to the name and id attributes and validates the value.
	 *
	 * @since 2.4
	 *
	 * @param GF_Field $field
	 * @param array $form
	 * @param array $field_value
	 * @param array $entry
	 * @param int $index
	 *
	 * @return mixed
	 */
	public function get_sub_field_input( $field, $form, $field_value, $entry, $index ) {
		$field_content = $this->get_sub_field_content( $field, $field_value, $form, $entry );

		// Adjust all the name attributes in the markup
		preg_match_all( "/(name='input_[^\[|']*)((\[[0-9]*\])*)'/", $field_content, $matches, PREG_SET_ORDER );

		$replaced = array();
		foreach ( $matches as $match ) {
			if ( ! in_array( $match[0], $replaced ) ) {
				$input_name    = str_replace( $match[1], $match[1] . "[{$index}]", $match[0] );
				$field_content = str_replace( $match[0], $input_name, $field_content );
				$replaced[]    = $match[0];
			}
		}

		// Adjust all the id attributes in the markup
		preg_match_all( "/(id='((input|choice)_[0-9|_]*))[0-9|-]*'/", $field_content, $matches, PREG_SET_ORDER );

		$replaced = array();
		foreach ( $matches as $match ) {
			if ( ! in_array( $match[0], $replaced ) ) {
				$input_id      = str_replace( $match[1], $match[1] . "-{$index}", $match[0] ) ;
				$field_content = str_replace( $match[0], $input_id, $field_content );
				$replaced[]    = $match[0];
			}
		}

		// Adjust all the for attributes in the markup
		preg_match_all( "/(for='(input|choice)_[^\[']*)'/", $field_content, $matches, PREG_SET_ORDER );

		$replaced = array();
		foreach ( $matches as $match ) {
			if ( ! in_array( $match[1], $replaced ) ) {
				$input_id      = $match[1] . "-{$index}";
				$field_content = str_replace( $match[1], $input_id, $field_content );
				$replaced[]    = $match[1];
			}
		}

		$target_page = rgpost( 'gform_target_page_number_' . $this->formId );
		$source_page = rgpost( 'gform_source_page_number_' . $this->formId );
		$validate = $source_page == $field->pageNumber && rgpost( 'is_submit_' . $this->formId ) && ( $target_page == 0 || $target_page > $source_page );

		if ( $validate ) {
			$field->failed_validation = false;
			if ( $field->isRequired && $field->is_value_empty( $field_value ) ) {
				$field->failed_validation  = true;
				$field->validation_message = empty( $field->errorMessage ) ? __( 'This field is required.', 'gravityforms' ) : $field->errorMessage;
			}

			if ( ! $field->failed_validation ) {
				$field->validate( $field_value, $form );
			}

			$custom_validation_result = gf_apply_filters( array( 'gform_field_validation', $form['id'], $field->id ), array(
				'is_valid' => $field->failed_validation ? false : true,
				'message'  => $field->validation_message
			), $field_value, $form, $field );
			$field->failed_validation  = rgar( $custom_validation_result, 'is_valid' ) ? false : true;
		}

		$validation_message = ( $field->failed_validation && ! empty( $field->validation_message ) ) ? sprintf( "<div class='gfield_description validation_message'>%s</div>", $field->validation_message ) : '';

		return $field_content . $validation_message;
	}

	/**
	 * Returns the markup for the sub field.
	 *
	 * @since 2.4
	 *
	 * @param GF_Field     $field
	 * @param string|array $value
	 * @param array        $form
	 * @param array        $entry
	 *
	 * @return string
	 */
	public function get_sub_field_content( $field, $value, $form, $entry ) {

		$validation_status = $field->failed_validation;

		if ( empty( $field->fields ) ) {
			// Validation will be handled later inside GF_Field_Repeater::get_sub_field_input so temporarily set failed_validation to false.
			$field->failed_validation = false;
		}

		if ( ! class_exists( 'GFFormDisplay' ) ) {
			require_once( GFCommon::get_base_path() .'/form_display.php' );
		}

		$field_content = GFFormDisplay::get_field_content( $field, $value, true, $form['id'], $form );

		$field->failed_validation = $validation_status;

		return $field_content;
	}

	/**
	 * Builds the repeater's array of items.
	 *
	 * @since 2.4
	 *
	 * @param $entry
	 *
	 * @return mixed
	 */
	public function hydrate( $entry, $apply_filters = false ) {
		$entry[ $this->id ] = $this->get_repeater_items( $entry, '', '', $apply_filters );
		return $entry;
	}

	/**
	 * Recursively converts the repeater values from flattened values in the entry array into a multidimensional array
	 * of items.
	 *
	 * @since 2.4
	 *
	 * @param array             $entry
	 * @param GF_Field_Repeater $repeater_field
	 * @param string            $index
	 *
	 * @return array
	 */
	public function get_repeater_items( &$entry, $repeater_field = null, $index = '', $apply_filters = false ) {
		if ( ! $repeater_field ) {
			$repeater_field = $this;
		}

		$items = array();

		// Blank items are not stored but we need to display them if a value exists with a higher index.
		$max_indexes = $this->get_max_indexes( $entry, $repeater_field, $index );

		$repeater_fields = array();

		foreach ( $repeater_field->fields as $field ) {
			if ( is_array( $field->fields ) ) {
				$repeater_fields[] = $field;
				continue;
			}

			for ( $i = 0; $i <= $max_indexes[ $field->id ]; $i ++ ) {
				$inputs = $field->get_entry_inputs();

				if ( is_array( $inputs ) ) {
					foreach ( $inputs as $input ) {

						$input_id = $input['id'];

						$key = $input_id . $index . '_' . $i;

						$value = isset( $entry[ $key ] ) ? $entry[ $key ] : '';

						// Don't add new item if max indexes is 0 and value is empty.
						if ( $field->isRequired || $max_indexes[ $field->id ] > 0 || ( $max_indexes[ $field->id ] === 0 && $value !== '' ) ) {
							if ( $apply_filters ) {
								$items[ $i ][ $input_id ] = $field->filter_input_value( $value, $entry );
							} else {
								$items[ $i ][ $input_id ] = $value;
							}
						}

						if ( isset( $entry[ $key ] ) ) {
							unset( $entry[ $key ] );
						}
					}
				} else {

					$key = $field->id . $index . '_' . $i;

					$value = isset( $entry[ $key ] ) ? $entry[ $key ] : '';

					if ( $field->isRequired || $max_indexes[ $field->id ] > 0 || ( $max_indexes[ $field->id ] === 0 && $value !== '' ) ) {
						if ( $apply_filters ) {
							$items[ $i ][ $field->id ] = $field->filter_input_value( $value, $entry );
						} else {
							$items[ $i ][ $field->id ] = $value;
						}
					}

					if ( isset( $entry[ $key ] ) ) {
						unset( $entry[ $key ] );
					}
				}
			}
		}

		if ( ! empty( $repeater_fields ) ) {

			$i = 0;

			do {
				$all_repeaters_have_values = true;
				foreach ( $repeater_fields as $repeater ) {
					$v = $this->get_repeater_items( $entry, $repeater, $index . '_' . $i );

					$is_empty = $this->empty_deep( $v );

					if ( ( $i == 0 || ! $is_empty ) || ( empty( $index ) && isset( $items[ $i ] ) && ! $this->empty_deep( $items[ $i ] ) ) ) {
						$items[ $i ][ $repeater->id ] = $v;
					}

					if ( $is_empty ) {
						$all_repeaters_have_values = false;
					}
				}
				$i ++;
			} while ( $all_repeaters_have_values );
		}

		return $items;
	}

	/**
	 * Parses all the flat entry array keys and returns the maximum index by field ID.
	 *
	 * @since 2.4
	 *
	 * @param array $entry                          The entry array
	 * @param GF_Field_Repeater $repeater_field     The repeater field
	 * @param string $index                         The index prefix
	 *
	 * @return array
	 */
	protected function get_max_indexes( $entry, $repeater_field, $index ) {

		$field_ids = array_keys( $entry );

		$max_indexes = array();

		$matches = array();


		foreach ( $repeater_field->fields as $field ) {
			if ( ! isset( $matches[ $field->id ] ) ) {
				$matches[ $field->id ] = array( 0 );
			}
			foreach ( $field_ids as $f_id ) {
				if ( preg_match( "/{$field->id}[^_]*{$index}_([0-9]+)/", $f_id, $m ) ) {
					$matches[ $field->id ][] = intval( $m[1] );
				}
			}
			$max_indexes[ $field->id ] = max( $matches[ $field->id ] );
		}

		return $max_indexes;
	}

	/**
	 * Recursively checks whether a multi-dimensional array is empty.
	 *
	 * @since 2.4
	 *
	 * @param $val
	 *
	 * @return bool
	 */
	public function empty_deep( $val ) {

		$result = true;

		if ( is_array( $val ) && count( $val ) > 0 ) {
			foreach ( $val as $v ) {
				$result = $result && $this->empty_deep( $v );
			}
		} else {
			$result = empty( $val );
		}

		return $result;

	}

	/**
	 * Returns the sub-filters for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_sub_filters() {
		$filters = array();
		$fields  = $this->fields;
		foreach ( $fields as $field ) {
			/** @var GF_Field $field */
			$filter_settings = array(
				'key'  => $field->id,
				'text' => GFFormsModel::get_label( $field, false, true ),
			);

			if ( is_array( $field->fields ) ) {
				$filter_settings = $field->get_filter_settings();
				$filters[]       = $filter_settings;
				continue;
			}
			$sub_filters = $field->get_filter_sub_filters();

			if ( ! empty( $sub_filters ) ) {
				$filter_settings['group']   = true;
				$filter_settings['filters'] = $sub_filters;
			} else {
				$filter_settings['preventMultiple'] = false;
				$filter_settings['operators']       = $field->get_filter_operators();

				$values = $field->get_filter_values();
				if ( ! empty( $values ) ) {
					$filter_settings['values'] = $values;
				}
			}

			$values = $field->get_filter_values();
			if ( ! empty( $values ) ) {
				$filter_settings['values'] = $values;
			}

			$filters[] = $filter_settings;
		}

		return $filters;
	}

	/**
	 * Returns the filter settings for the current field.
	 *
	 * If overriding to add custom settings call the parent method first to get the default settings.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_settings() {

		$filter_settings = parent::get_filter_settings();

		$filter_settings['isNestable'] = true;

		return $filter_settings;
	}

	/**
	 * Format the entry value before it is used in entry exports and by framework add-ons using GFAddOn::get_field_value().
	 *
	 * @since 2.4
	 *
	 * @param array      $entry    The entry currently being processed.
	 * @param string     $input_id The field or input ID.
	 * @param bool|false $use_text When processing choice based fields should the choice text be returned instead of the value.
	 * @param bool|false $is_csv   Is the value going to be used in the .csv entries export?
	 *
	 * @return string|array
	 */
	public function get_value_export( $entry, $input_id = '', $use_text = false, $is_csv = false ) {
		$export = $this->get_value_export_recursive( $entry, $input_id, $use_text, $is_csv );
		return $export;
	}

	/**
	 * Format the entry value before it is used in entry exports and by framework add-ons using GFAddOn::get_field_value().
	 *
	 * @since 2.4
	 *
	 * @param array      $entry    The entry currently being processed.
	 * @param string     $input_id The field or input ID.
	 * @param bool|false $use_text When processing choice based fields should the choice text be returned instead of the value.
	 * @param bool|false $is_csv   Is the value going to be used in the .csv entries export?
	 *
	 * @return string|array
	 */
	public function get_value_export_recursive( $entry, $input_id = '', $use_text = false, $is_csv = false, $depth = 0, $padding = '    ' ) {
		if ( empty( $input_id ) ) {
			$input_id = $this->id;
		}

		$items = rgar( $entry, $input_id );

		/* @var GF_Field[] $fields */
		$fields = $this->fields;

		$csv = array();

		foreach ( $items as $item ) {

			foreach ( $fields as $field ) {

				$inputs = $field->get_entry_inputs();

				if ( is_array( $inputs ) ) {
					$field_value = array();
					$field_keys  = array_keys( $item );
					foreach ( $field_keys as $input_id ) {
						if ( is_numeric( $input_id ) && absint( $input_id ) == absint( $field->id ) ) {
							$field_value[ $input_id ] = $item[ $input_id ];
						}
					}
				} else {
					$field_value = isset( $item[ $field->id ] ) ? $item[ $field->id ] : '';
					$field_value = array( (string) $field->id => $field_value );
				}

				$label = str_repeat( $padding, $depth ) . GFFormsModel::get_label( $field );

				if ( is_array( $field->fields ) ) {
					$new_depth = $depth + 1;
					$line      = $label . "\n" . $field->get_value_export_recursive( $field_value, $field->id, $use_text, $is_csv, $new_depth, $padding );
					if ( $depth == 0 ) {
						$line .= "\n";
					}
				} else {

					if ( 'list' === $field->get_input_type() && ! empty( $field_value[ $field->id ] ) ) {

						$list_rows = maybe_unserialize( $field_value[ $field->id ] );

						if ( is_array( $list_rows[0] ) ) {
							$lines = array();
							foreach ( $list_rows as $i => $list_row ) {
								$row_label = $label . ' ' . ( $i + 1 );

								// Prepare row value.
								$row_value = implode( '|', $list_row );
								if ( strpos( $row_value, '=' ) === 0 ) {
									// Prevent Excel formulas
									$row_value = "'" . $row_value;
								}

								$lines[] = $row_label . ': ' . $row_value;
							}
							$line = implode( "\n", $lines );
						} else {
							$value = implode( '|', $list_rows );
							if ( strpos( $value, '=' ) === 0 ) {
								// Prevent Excel formulas
								$value = "'" . $value;
							}
							$line = $label . ': ' . $value;
						}

					} else {
						$line = $label . ': ' . $field->get_value_export( $field_value, $field->id, $use_text, $is_csv );
					}

				}

				$csv[] = $line;
			}
		}

		return implode( "\n", $csv );
	}

	/**
	 * Store the modifiers so they can be accessed when preparing the {all_fields} and field merge tag output.
	 *
	 * @since 2.4
	 *
	 * @param array $modifiers An array of modifiers to be stored.
	 */
	public function set_modifiers( $modifiers ) {
		parent::set_modifiers( $modifiers );

		/* @var GF_Field $sub_field */
		foreach ( $this->fields as $sub_field ) {
			$sub_field->set_modifiers( $modifiers );
		}
	}

}

GF_Fields::register( new GF_Field_Repeater() );
class-gf-field-textarea.php000066600000032570151264036140011665 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_Textarea extends GF_Field {

	public $type = 'textarea';

	public function get_form_editor_field_title() {
		return esc_attr__( 'Paragraph Text', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'maxlen_setting',
			'size_setting',
			'rules_setting',
			'visibility_setting',
			'duplicate_setting',
			'default_value_textarea_setting',
			'placeholder_textarea_setting',
			'description_setting',
			'css_class_setting',
			'rich_text_editor_setting',
		);
	}

	public function is_conditional_logic_supported() {
		return true;
	}

	public function allow_html() {
		return empty( $this->useRichTextEditor ) ? false : true;
	}

	public function get_field_input( $form, $value = '', $entry = null ) {

		global $current_screen;

		$form_id         = absint( $form['id'] );
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$is_admin = $is_entry_detail || $is_form_editor;

		$id            = intval( $this->id );
		$field_id      = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";
		$size          = $this->size;
		$class_suffix  = $is_entry_detail ? '_admin' : '';
		$class         = $size . $class_suffix;
		$class         = esc_attr( $class );
		$disabled_text = $is_form_editor ? 'disabled="disabled"' : '';

		$maxlength_attribute   = is_numeric( $this->maxLength ) ? "maxlength='{$this->maxLength}'" : '';
		$placeholder_attribute = $this->get_field_placeholder_attribute();
		$required_attribute    = $this->isRequired ? 'aria-required="true"' : '';
		$invalid_attribute     = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';
		$aria_describedby      = $this->get_aria_describedby();

		$tabindex = $this->get_tabindex();

		if ( $this->get_allowable_tags() === false ) {
			$value = esc_textarea( $value );
		} else {
			$value = wp_kses_post( $value );
		}

		//see if the field is set to use the rich text editor
		if ( ! $is_admin && $this->is_rich_edit_enabled() && ( ! $current_screen || ( $current_screen && ! rgobj( $current_screen, 'is_block_editor' ) ) ) ) {
			//placeholders cannot be used with the rte; message displayed in admin when this occurs
			//field cannot be used in conditional logic by another field; message displayed in admin and field removed from conditional logic drop down
			$tabindex = GFCommon::$tab_index > 0 ? GFCommon::$tab_index ++ : '';

			add_filter( 'mce_buttons', array( $this, 'filter_mce_buttons' ), 10, 2 );
			add_filter( 'mce_buttons_2', array( $this, 'filter_mce_buttons' ), 10, 2 );
			add_filter( 'mce_buttons_3', array( $this, 'filter_mce_buttons' ), 10, 2 );
			add_filter( 'mce_buttons_4', array( $this, 'filter_mce_buttons' ), 10, 2 );

			/**
			 * Filters the field options for the rich text editor.
			 *
			 * @since 2.0.0
			 *
			 * @param array  $editor_settings Array of settings that can be changed.
			 * @param object $this            The field object
			 * @param array  $form            Current form object
			 * @param array  $entry           Current entry object, if available
			 *
			 * Additional filters for specific form and fields IDs.
			 */
			$editor_settings = apply_filters( 'gform_rich_text_editor_options', array(
				'textarea_name' => 'input_' . $id,
				'wpautop' 		=> true,
				'editor_class' 	=> $class,
				'editor_height' => rgar( array( 'small' => 110, 'medium' => 180, 'large' => 280 ), $this->size ? $this->size : 'medium' ),
				'tabindex' 		=> $tabindex,
				'media_buttons' => false,
				'quicktags'     => false,
				'tinymce'		=> array( 'init_instance_callback' =>  "function (editor) {
												editor.on( 'keyup paste mouseover', function (e) {
													var content = editor.getContent( { format: 'text' } ).trim();													
													var textarea = jQuery( '#' + editor.id ); 
													textarea.val( content ).trigger( 'keyup' ).trigger( 'paste' ).trigger( 'mouseover' );													
												
													
												});}" ),
			), $this, $form, $entry );

			$editor_settings = apply_filters( sprintf( 'gform_rich_text_editor_options_%d', $form['id'] ),               $editor_settings, $this, $form, $entry );
			$editor_settings = apply_filters( sprintf( 'gform_rich_text_editor_options_%d_%d', $form['id'], $this->id ), $editor_settings, $this, $form, $entry );

			if ( ! has_action( 'wp_tiny_mce_init', array( __class__, 'start_wp_tiny_mce_init_buffer' ) ) ) {
				add_action( 'wp_tiny_mce_init', array( __class__, 'start_wp_tiny_mce_init_buffer' ) );
			}

			ob_start();
			wp_editor( $value, $field_id, $editor_settings );
			$input = ob_get_clean();

			remove_filter( 'mce_buttons', array( $this, 'filter_mce_buttons' ), 10 );
			remove_filter( 'mce_buttons_2', array( $this, 'filter_mce_buttons' ), 10 );
			remove_filter( 'mce_buttons_3', array( $this, 'filter_mce_buttons' ), 10 );
			remove_filter( 'mce_buttons_4', array( $this, 'filter_mce_buttons' ), 10 );
		} else {

			$input       = '';
			$input_style = '';

			// RTE preview
			if ( $this->is_form_editor() ) {
				$display     = $this->useRichTextEditor ? 'block' : 'none';
				$input_style = $this->useRichTextEditor ? 'style="display:none;"' : '';
				$size        = $this->size ? $this->size : 'medium';
				$input       = sprintf( '<div id="%s_rte_preview" class="gform-rte-preview %s" style="display:%s"></div>', $field_id, $size, $display );
			}

			$input .= "<textarea name='input_{$id}' id='{$field_id}' class='textarea {$class}' {$tabindex} {$aria_describedby} {$maxlength_attribute} {$placeholder_attribute} {$required_attribute} {$invalid_attribute} {$disabled_text} {$input_style} rows='10' cols='50'>{$value}</textarea>";

		}

		return sprintf( "<div class='ginput_container ginput_container_textarea'>%s</div>", $input );
	}

	public function validate( $value, $form ) {
		if ( ! is_numeric( $this->maxLength ) ) {
			return;
		}

		if ( $this->useRichTextEditor ) {
			$value = wp_specialchars_decode( $value );
		}

		// Clean the string of characters not counted by the textareaCounter plugin.
		$value = strip_tags( $value );
		$value = str_replace( "\r", '', $value );
		$value = trim( $value );

		if ( GFCommon::safe_strlen( $value ) > $this->maxLength ) {
			$this->failed_validation  = true;
			$this->validation_message = empty( $this->errorMessage ) ? esc_html__( 'The text entered exceeds the maximum number of characters.', 'gravityforms' ) : $this->errorMessage;
		}
	}

	public static function start_wp_tiny_mce_init_buffer() {
		ob_start();
		add_action( 'after_wp_tiny_mce', array( __class__, 'end_wp_tiny_mce_init_buffer' ), 1 );
	}

	public static function end_wp_tiny_mce_init_buffer() {

		$script = ob_get_clean();
		$pattern = '/(<script.*>)([\s\S]+)(<\/script>)/';

		preg_match_all( $pattern, $script, $matches, PREG_SET_ORDER );

		// Fix editor height issue: https://core.trac.wordpress.org/ticket/45461.
		$wp_version       = get_bloginfo( 'version' );
		$height_issue_fix = version_compare( $wp_version, '5.0', '>=' ) && version_compare( $wp_version, '5.2', '<' ) ? ' gform_post_conditional_logic' : '';

		foreach ( $matches as $match ) {

			list( $search, $open_tag, $guts, $close_tag ) = $match;

			$custom  = "if ( typeof current_page === 'undefined' ) { return; }\nfor( var id in tinymce.editors ) { tinymce.EditorManager.remove( tinymce.editors[id] ); }";
			$replace = sprintf( "%s\njQuery( document ).on( 'gform_post_render%s', function( event, form_id, current_page ) { \n%s\n%s } );\n%s", $open_tag, $height_issue_fix, $custom, $guts, $close_tag );
			$script  = str_replace( $search, $replace, $script );

		}

		echo $script;

	}

	public function filter_mce_buttons( $mce_buttons, $editor_id ) {

		$remove_key = array_search( 'wp_more', $mce_buttons );
		if ( $remove_key !== false ) {
			unset( $mce_buttons[ $remove_key ] );
		}

		// Get current filter to detect which mce_buttons core filter is running.
		$current_filter = current_filter();

		// Depending on the current mce_buttons filter, set variable to support filtering all potential rows.
		switch ( $current_filter ) {

			case 'mce_buttons_2':
				$mce_filter = '_row_two';
				break;

			case 'mce_buttons_3':
				$mce_filter = '_row_three';
				break;

			case 'mce_buttons_4':
				$mce_filter = '_row_four';
				break;

			default:
				$mce_filter = '';
				break;

		}

		/**
		 * Filters the buttons within the TinyMCE editor
		 *
		 * @since 2.0.0
		 *
		 * @param array  $mce_buttons Buttons to be included.
		 * @param string $editor_id   HTML ID of the field.
		 * @param object $this        The field object
		 *
		 * Additional filters for specific form and fields IDs.
		 */
		$mce_buttons = gf_apply_filters( array( 'gform_rich_text_editor_buttons' . $mce_filter, $this->formId, $this->id ), $mce_buttons, $editor_id, $this );

		return $mce_buttons;
	}

	/**
	 * Format the entry value for display on the entry detail page and for the {all_fields} merge tag.
	 * Return a value that's safe to display for the context of the given $format.
	 *
	 * @param string|array $value The field value.
	 * @param string $currency The entry currency code.
	 * @param bool|false $use_text When processing choice based fields should the choice text be returned instead of the value.
	 * @param string $format The format requested for the location the merge is being used. Possible values: html, text or url.
	 * @param string $media The location where the value will be displayed. Possible values: screen or email.
	 *
	 * @return string
	 */
	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {

		if ( $format === 'html' ) {

			$allowable_tags = $this->get_allowable_tags();

			if ( $allowable_tags === false ) {
				// The value is unsafe so encode the value.
				$value = esc_html( $value );
				$return = nl2br( $value );

			} else {
				// The value contains HTML but the value was sanitized before saving.
				$return = wpautop( $value );
			}
		} else {
			$return = $value;
		}

		return $return;
	}

	/**
	 * Format the entry value for when the field/input merge tag is processed. Not called for the {all_fields} merge tag.
	 *
	 * Return a value that is safe for the context specified by $format.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param string|array $value      The field value. Depending on the location the merge tag is being used the following functions may have already been applied to the value: esc_html, nl2br, and urlencode.
	 * @param string       $input_id   The field or input ID from the merge tag currently being processed.
	 * @param array        $entry      The Entry Object currently being processed.
	 * @param array        $form       The Form Object currently being processed.
	 * @param string       $modifier   The merge tag modifier. e.g. value
	 * @param string|array $raw_value  The raw field value from before any formatting was applied to $value.
	 * @param bool         $url_encode Indicates if the urlencode function may have been applied to the $value.
	 * @param bool         $esc_html   Indicates if the esc_html function may have been applied to the $value.
	 * @param string       $format     The format requested for the location the merge is being used. Possible values: html, text or url.
	 * @param bool         $nl2br      Indicates if the nl2br function may have been applied to the $value.
	 *
	 * @return string
	 */
	public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) {

		if ( $format === 'html' ) {
			$form_id        = absint( $form['id'] );
			$allowable_tags = $this->get_allowable_tags( $form_id );

			if ( $allowable_tags === false ) {
				// The raw value is unsafe so escape it.
				$return = esc_html( $raw_value );
				// Run nl2br() to preserve line breaks when auto-formatting is disabled on notifications/confirmations.
				$return = nl2br( $return );
			} else {
				// The value contains HTML but the value was sanitized before saving.
				$return = wpautop( $raw_value );
			}
		} else {
			$return = $value;
		}

		return $return;
	}

	/**
	 * Determines if the RTE can be enabled for the current field and user.
	 *
	 * @since 2.2.5.14
	 *
	 * @return bool
	 */
	public function is_rich_edit_enabled() {
		if ( ! $this->useRichTextEditor ) {
			return false;
		}

		global $wp_rich_edit;
		$wp_rich_edit = null;

		add_filter( 'get_user_option_rich_editing', array( $this, 'filter_user_option_rich_editing' ) );
		$user_can_rich_edit = user_can_richedit();
		remove_filter( 'get_user_option_rich_editing', array( $this, 'filter_user_option_rich_editing' ) );

		return $user_can_rich_edit;
	}

	/**
	 * Filter the rich_editing option for the current user.
	 *
	 * @since 2.2.5.14
	 *
	 * @param string $value The value of the rich_editing option for the current user.
	 *
	 * @return string
	 */
	public function filter_user_option_rich_editing( $value ) {
		return 'true';
	}

	// # FIELD FILTER UI HELPERS ---------------------------------------------------------------------------------------

	/**
	 * Returns the filter operators for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_operators() {
		$operators   = parent::get_filter_operators();
		$operators[] = 'contains';

		return $operators;
	}

}

GF_Fields::register( new GF_Field_Textarea() );
class-gf-field-donation.php000066600000004614151264036140011661 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}

/**
 * @deprecated since 1.6
 *
 * Class GF_Field_Donationç
 */
class GF_Field_Donation extends GF_Field {

	public $type = 'donation';

	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'donation_field_type_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'admin_label_setting',
			'label_placement_setting',
			'rules_setting',
			'default_value_setting',
			'placeholder_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	public function get_form_editor_button() {
		return array();
	}

	public function validate( $value, $form ) {
		$price = GFCommon::to_number( $value );
		if ( ! rgblank( $value ) && ( $price === false || $price < 0 ) ) {
			$this->failed_validation  = true;
			$this->validation_message = empty( $this->errorMessage ) ? esc_html__( 'Please enter a valid amount.', 'gravityforms' ) : $this->errorMessage;
		}
	}


	public function get_field_input( $form, $value = '', $entry = null ) {
		$form_id         = absint( $form['id'] );
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$id       = absint( $this->id );
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		$value = esc_attr( $value );

		$placeholder_attribute = $this->get_field_placeholder_attribute();
		$required_attribute    = $this->isRequired ? 'aria-required="true"' : '';
		$invalid_attribute     = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';

		$size         = $this->size;
		$class_suffix = $is_entry_detail ? '_admin' : '';
		$class        = $size . $class_suffix;
		$class        = esc_attr( $class );

		$disabled_text = $is_form_editor ? 'disabled="disabled"' : '';

		$tabindex = $this->get_tabindex();

		return "<div class='ginput_container ginput_container_donation'>
					<input name='input_{$id}' id='{$field_id}' type='text' value='{$value}' class='{$class} ginput_donation_amount' {$tabindex} {$placeholder_attribute} {$required_attribute} {$invalid_attribute} {$disabled_text}/>
				</div>";

	}

	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
		return GFCommon::to_money( $value, $currency );
	}

}

GF_Fields::register( new GF_Field_Donation() );index.php000066600000000033151264036140006366 0ustar00<?php
//Nothing to see hereclass-gf-field-name.php000066600000074561151264036140010776 0ustar00<?php

// If Gravity Forms isn't loaded, bail.
if ( ! class_exists( 'GFForms' ) ) {
	die();
}

/**
 * Class GF_Field_Name
 *
 * Handles the behavior of the Name field.
 *
 * @since Unknown
 */
class GF_Field_Name extends GF_Field {

	/**
	 * Sets the field type.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @var string The type of field.
	 */
	public $type = 'name';

	/**
	 * Sets the field title of the Name field.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFCommon::get_field_type_title()
	 * @used-by GF_Field::get_form_editor_button()
	 *
	 * @return string
	 */
	public function get_form_editor_field_title() {
		return esc_attr__( 'Name', 'gravityforms' );
	}

	/**
	 * Defines if conditional logic is supported by the Name field.
	 *
	 * @since Unknown
	 * @access public
	 *
	 * @used-by GFFormDetail::inline_scripts()
	 * @used-by GFFormSettings::output_field_scripts()
	 *
	 * @return bool true
	 */
	public function is_conditional_logic_supported() {
		return true;
	}

	/**
	 * Validates Name field inputs.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFFormDisplay::validate()
	 * @uses    GF_Field_Name::$isRequired
	 * @uses    GF_Field_Name::$nameFormat
	 * @uses    GF_Field_Name::get_input_property
	 * @uses    GF_Field_Name::$failed_validation
	 * @uses    GF_Field_Name::$validation_message
	 * @uses    GF_Field_Name::$errorMessage
	 *
	 * @param array|string $value The value of the field to validate. Not used here.
	 * @param array        $form  The Form Object. Not used here.
	 *
	 * @return void
	 */
	function validate( $value, $form ) {

		if ( $this->isRequired && $this->nameFormat != 'simple' ) {
			$first = rgar( $value, $this->id . '.3' );
			$last  = rgar( $value, $this->id . '.6' );
			if (   ( empty( $first ) && ! $this->get_input_property( '3', 'isHidden' ) )
				|| ( empty( $last )  && ! $this->get_input_property( '6', 'isHidden' ) ) ) {
				$this->failed_validation  = true;
				$this->validation_message = empty( $this->errorMessage ) ? esc_html__( 'This field is required. Please enter the first and last name.', 'gravityforms' ) : $this->errorMessage;
			}
		}
	}

	/**
	 * Defines the field settings available for the Name field in the form editor.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFFormDetail::inline_scripts()
	 *
	 * @return array The field settings available.
	 */
	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'admin_label_setting',
			'label_placement_setting',
			'sub_label_placement_setting',
			'default_input_values_setting',
			'input_placeholders_setting',
			'name_setting',
			'rules_setting',
			'visibility_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	/**
	 * Gets the HTML markup for the field input.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFCommon::get_field_input()
	 * @uses    GF_Field::is_entry_detail()
	 * @uses    GF_Field::is_form_editor()
	 * @uses    GF_Field_Name::$size
	 * @uses    GF_Field_Name::$id
	 * @uses    GF_Field_Name::$subLabelPlacement
	 * @uses    GF_Field_Name::$isRequired
	 * @uses    GF_Field_Name::$failed_validation
	 * @uses    GFForms::get()
	 * @uses    GFFormsModel::get_input()
	 * @uses    GFCommon::get_input_placeholder_attribute()
	 * @uses    GFCommon::get_tabindex()
	 * @uses    GFCommon::get_field_placeholder_attribute()
	 * @uses    GF_Field_Name::get_css_class()
	 *
	 * @param array      $form  The Form Object.
	 * @param string     $value The value of the field. Defaults to empty string.
	 * @param array|null $entry The Entry Object. Defaults to null.
	 *
	 * @return string The HTML markup for the field input.
	 */
	public function get_field_input( $form, $value = '', $entry = null ) {

		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();
		$is_admin = $is_entry_detail || $is_form_editor;

		$form_id  = $form['id'];
		$id       = intval( $this->id );
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";
		$form_id  = ( $is_entry_detail || $is_form_editor ) && empty( $form_id ) ? rgget( 'id' ) : $form_id;

		$size         = $this->size;
		$class_suffix = rgget('view') == 'entry' ? '_admin' : '';
		$class        = $size . $class_suffix;
		$class        = esc_attr( $class );

		$disabled_text = $is_form_editor ? "disabled='disabled'" : '';
		$class_suffix  = $is_entry_detail ? '_admin' : '';

		$form_sub_label_placement  = rgar( $form, 'subLabelPlacement' );
		$field_sub_label_placement = $this->subLabelPlacement;
		$is_sub_label_above        = $field_sub_label_placement == 'above' || ( empty( $field_sub_label_placement ) && $form_sub_label_placement == 'above' );
		$sub_label_class_attribute = $field_sub_label_placement == 'hidden_label' ? "class='hidden_sub_label screen-reader-text'" : '';

		$prefix = '';
		$first  = '';
		$middle = '';
		$last   = '';
		$suffix = '';

		if ( is_array( $value ) ) {
			$prefix = esc_attr( GFForms::get( $this->id . '.2', $value ) );
			$first  = esc_attr( GFForms::get( $this->id . '.3', $value ) );
			$middle = esc_attr( GFForms::get( $this->id . '.4', $value ) );
			$last   = esc_attr( GFForms::get( $this->id . '.6', $value ) );
			$suffix = esc_attr( GFForms::get( $this->id . '.8', $value ) );
		}

		$prefix_input = GFFormsModel::get_input( $this, $this->id . '.2' );
		$first_input  = GFFormsModel::get_input( $this, $this->id . '.3' );
		$middle_input = GFFormsModel::get_input( $this, $this->id . '.4' );
		$last_input   = GFFormsModel::get_input( $this, $this->id . '.6' );
		$suffix_input = GFFormsModel::get_input( $this, $this->id . '.8' );

		$first_placeholder_attribute  = GFCommon::get_input_placeholder_attribute( $first_input );
		$middle_placeholder_attribute = GFCommon::get_input_placeholder_attribute( $middle_input );
		$last_placeholder_attribute   = GFCommon::get_input_placeholder_attribute( $last_input );
		$suffix_placeholder_attribute = GFCommon::get_input_placeholder_attribute( $suffix_input );

		// ARIA labels. Prefix is handled in self::get_name_prefix_field().
		$first_name_aria_label  = esc_attr__( 'First name', 'gravityforms' );
		$middle_name_aria_label = esc_attr__( 'Middle name', 'gravityforms' );
		$last_name_aria_label   = esc_attr__( 'Last name', 'gravityforms' );
		$suffix_aria_label      = esc_attr__( 'Name suffix', 'gravityforms' );
		$required_attribute     = $this->isRequired ? 'aria-required="true"' : '';
		$invalid_attribute      = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';

		switch ( $this->nameFormat ) {

			case 'advanced' :
			case 'extended' :
				$prefix_tabindex = GFCommon::get_tabindex();
				$first_tabindex  = GFCommon::get_tabindex();
				$middle_tabindex = GFCommon::get_tabindex();
				$last_tabindex   = GFCommon::get_tabindex();
				$suffix_tabindex = GFCommon::get_tabindex();

				$prefix_sub_label      = rgar( $prefix_input, 'customLabel' ) != '' ? $prefix_input['customLabel'] : gf_apply_filters( array( 'gform_name_prefix', $form_id ), esc_html__( 'Prefix', 'gravityforms' ), $form_id );
				$first_name_sub_label  = rgar( $first_input, 'customLabel' ) != '' ? $first_input['customLabel'] : gf_apply_filters( array( 'gform_name_first', $form_id ), esc_html__( 'First', 'gravityforms' ), $form_id );
				$middle_name_sub_label = rgar( $middle_input, 'customLabel' ) != '' ? $middle_input['customLabel'] : gf_apply_filters( array( 'gform_name_middle', $form_id ), esc_html__( 'Middle', 'gravityforms' ), $form_id );
				$last_name_sub_label   = rgar( $last_input, 'customLabel' ) != '' ? $last_input['customLabel'] : gf_apply_filters( array( 'gform_name_last', $form_id ), esc_html__( 'Last', 'gravityforms' ), $form_id );
				$suffix_sub_label      = rgar( $suffix_input, 'customLabel' ) != '' ? $suffix_input['customLabel'] : gf_apply_filters( array( 'gform_name_suffix', $form_id ), esc_html__( 'Suffix', 'gravityforms' ), $form_id );

				$prefix_markup = '';
				$first_markup  = '';
				$middle_markup = '';
				$last_markup   = '';
				$suffix_markup = '';

				if ( $is_sub_label_above ) {

					$style = ( $is_admin && rgar( $prefix_input, 'isHidden' ) ) ? "style='display:none;'" : '';
					if ( $is_admin || ! rgar( $prefix_input, 'isHidden' ) ) {
						$prefix_select_class = isset( $prefix_input['choices'] ) && is_array( $prefix_input['choices'] ) ? 'name_prefix_select' : '';
						$prefix_markup       = self::get_name_prefix_field( $prefix_input, $id, $field_id, $prefix, $disabled_text, $prefix_tabindex );
						$prefix_markup       = "<span id='{$field_id}_2_container' class='name_prefix {$prefix_select_class}' {$style}>
                                                    <label for='{$field_id}_2' {$sub_label_class_attribute}>{$prefix_sub_label}</label>
                                                    {$prefix_markup}
                                                  </span>";
					}

					$style = ( $is_admin && rgar( $first_input, 'isHidden' ) ) ? "style='display:none;'" : '';
					if ( $is_admin || ! rgar( $first_input, 'isHidden' ) ) {
						$first_markup = "<span id='{$field_id}_3_container' class='name_first' {$style}>
                                                    <label for='{$field_id}_3' {$sub_label_class_attribute}>{$first_name_sub_label}</label>
                                                    <input type='text' name='input_{$id}.3' id='{$field_id}_3' value='{$first}' aria-label='{$first_name_aria_label}' {$first_tabindex} {$disabled_text} {$required_attribute} {$invalid_attribute} {$first_placeholder_attribute}/>
                                                </span>";
					}

					$style = ( $is_admin && ( ! isset( $middle_input['isHidden'] ) || rgar( $middle_input, 'isHidden' ) ) ) ? "style='display:none;'" : '';
					if ( $is_admin || ( isset( $middle_input['isHidden'] ) && $middle_input['isHidden'] == false ) ) {
						$middle_markup = "<span id='{$field_id}_4_container' class='name_middle' {$style}>
                                                    <label for='{$field_id}_4' {$sub_label_class_attribute}>{$middle_name_sub_label}</label>
                                                    <input type='text' name='input_{$id}.4' id='{$field_id}_4' value='{$middle}' aria-label='{$middle_name_aria_label}' {$middle_tabindex} {$disabled_text} {$required_attribute} {$invalid_attribute} {$middle_placeholder_attribute}/>
                                                </span>";
					}

					$style = ( $is_admin && rgar( $last_input, 'isHidden' ) ) ? "style='display:none;'" : '';
					if ( $is_admin || ! rgar( $last_input, 'isHidden' ) ) {
						$last_markup = "<span id='{$field_id}_6_container' class='name_last' {$style}>
                                                            <label for='{$field_id}_6' {$sub_label_class_attribute}>{$last_name_sub_label}</label>
                                                            <input type='text' name='input_{$id}.6' id='{$field_id}_6' value='{$last}' aria-label='{$last_name_aria_label}' {$last_tabindex} {$disabled_text} {$required_attribute} {$invalid_attribute} {$last_placeholder_attribute}/>
                                                        </span>";
					}

					$style = ( $is_admin && rgar( $suffix_input, 'isHidden' ) ) ? "style='display:none;'" : '';
					if ( $is_admin || ! rgar( $suffix_input, 'isHidden' ) ) {
						$suffix_select_class = isset( $suffix_input['choices'] ) && is_array( $suffix_input['choices'] ) ? 'name_suffix_select' : '';
						$suffix_markup       = "<span id='{$field_id}_8_container' class='name_suffix {$suffix_select_class}' {$style}>
                                                        <label for='{$field_id}_8' {$sub_label_class_attribute}>{$suffix_sub_label}</label>
                                                        <input type='text' name='input_{$id}.8' id='{$field_id}_8' value='{$suffix}' aria-label='{$suffix_aria_label}' {$suffix_tabindex} {$disabled_text} {$required_attribute} {$invalid_attribute} {$suffix_placeholder_attribute}/>
                                                    </span>";
					}
				} else {
					$style = ( $is_admin && rgar( $prefix_input, 'isHidden' ) ) ? "style='display:none;'" : '';
					if ( $is_admin || ! rgar( $prefix_input, 'isHidden' ) ) {
						$prefix_select_class = isset( $prefix_input['choices'] ) && is_array( $prefix_input['choices'] ) ? 'name_prefix_select' : '';
						$prefix_markup       = self::get_name_prefix_field( $prefix_input, $id, $field_id, $prefix, $disabled_text, $prefix_tabindex );
						$prefix_markup       = "<span id='{$field_id}_2_container' class='name_prefix {$prefix_select_class}' {$style}>
                                                    {$prefix_markup}
                                                    <label for='{$field_id}_2' {$sub_label_class_attribute}>{$prefix_sub_label}</label>
                                                  </span>";
					}

					$style = ( $is_admin && rgar( $first_input, 'isHidden' ) ) ? "style='display:none;'" : '';
					if ( $is_admin || ! rgar( $first_input, 'isHidden' ) ) {
						$first_markup = "<span id='{$field_id}_3_container' class='name_first' {$style}>
                                                    <input type='text' name='input_{$id}.3' id='{$field_id}_3' value='{$first}' aria-label='{$first_name_aria_label}' {$first_tabindex} {$disabled_text} {$required_attribute} {$invalid_attribute} {$first_placeholder_attribute}/>
                                                    <label for='{$field_id}_3' {$sub_label_class_attribute}>{$first_name_sub_label}</label>
                                                </span>";
					}

					$style = ( $is_admin && ( ! isset( $middle_input['isHidden'] ) || rgar( $middle_input, 'isHidden' ) ) ) ? "style='display:none;'" : '';
					if ( $is_admin || ( isset( $middle_input['isHidden'] ) && $middle_input['isHidden'] == false ) ) {
						$middle_markup = "<span id='{$field_id}_4_container' class='name_middle' {$style}>
                                                    <input type='text' name='input_{$id}.4' id='{$field_id}_4' value='{$middle}' aria-label='{$middle_name_aria_label}' {$middle_tabindex} {$disabled_text} {$required_attribute} {$invalid_attribute} {$middle_placeholder_attribute}/>
                                                    <label for='{$field_id}_4' {$sub_label_class_attribute}>{$middle_name_sub_label}</label>
                                                </span>";
					}

					$style = ( $is_admin && rgar( $last_input, 'isHidden' ) ) ? "style='display:none;'" : '';
					if ( $is_admin || ! rgar( $last_input, 'isHidden' ) ) {
						$last_markup = "<span id='{$field_id}_6_container' class='name_last' {$style}>
                                                    <input type='text' name='input_{$id}.6' id='{$field_id}_6' value='{$last}' aria-label='{$last_name_aria_label}' {$last_tabindex} {$disabled_text} {$required_attribute} {$invalid_attribute} {$last_placeholder_attribute}/>
                                                    <label for='{$field_id}_6' {$sub_label_class_attribute}>{$last_name_sub_label}</label>
                                                </span>";
					}

					$style = ( $is_admin && rgar( $suffix_input, 'isHidden' ) ) ? "style='display:none;'" : '';
					if ( $is_admin || ! rgar( $suffix_input, 'isHidden' ) ) {
						$suffix_select_class = isset( $suffix_input['choices'] ) && is_array( $suffix_input['choices'] ) ? 'name_suffix_select' : '';
						$suffix_markup       = "<span id='{$field_id}_8_container' class='name_suffix {$suffix_select_class}' {$style}>
                                                    <input type='text' name='input_{$id}.8' id='{$field_id}_8' value='{$suffix}' aria-label='{$suffix_aria_label}' {$suffix_tabindex} {$disabled_text} {$required_attribute} {$invalid_attribute} {$suffix_placeholder_attribute}/>
                                                    <label for='{$field_id}_8' {$sub_label_class_attribute}>{$suffix_sub_label}</label>
                                                </span>";
					}
				}
				$css_class = $this->get_css_class();


				return "<div class='ginput_complex{$class_suffix} ginput_container {$css_class}' id='{$field_id}'>
                            {$prefix_markup}
                            {$first_markup}
                            {$middle_markup}
                            {$last_markup}
                            {$suffix_markup}
                        </div>";
			case 'simple' :
				$value                 = esc_attr( $value );
				$class                 = esc_attr( $class );
				$tabindex              = GFCommon::get_tabindex();
				$placeholder_attribute = GFCommon::get_field_placeholder_attribute( $this );

				return "<div class='ginput_container ginput_container_name'>
                                    <input name='input_{$id}' id='{$field_id}' type='text' value='{$value}' class='{$class}' {$tabindex} {$disabled_text} {$required_attribute} {$invalid_attribute} {$placeholder_attribute}/>
                                </div>";
			default :
				$first_tabindex       = GFCommon::get_tabindex();
				$last_tabindex        = GFCommon::get_tabindex();
				$first_name_sub_label = rgar( $first_input, 'customLabel' ) != '' ? $first_input['customLabel'] : gf_apply_filters( array( 'gform_name_first', $form_id ), esc_html__( 'First', 'gravityforms' ), $form_id );
				$last_name_sub_label  = rgar( $last_input, 'customLabel' ) != '' ? $last_input['customLabel'] : gf_apply_filters( array( 'gform_name_last', $form_id ), esc_html__( 'Last', 'gravityforms' ), $form_id );
				if ( $is_sub_label_above ) {
					$first_markup = '';
					$style        = ( $is_admin && rgar( $first_input, 'isHidden' ) ) ? "style='display:none;'" : '';
					if ( $is_admin || ! rgar( $first_input, 'isHidden' ) ) {
						$first_markup = "<span id='{$field_id}_3_container' class='name_first' {$style}>
                                                    <label for='{$field_id}_3' {$sub_label_class_attribute}>{$first_name_sub_label}</label>
                                                    <input type='text' name='input_{$id}.3' id='{$field_id}_3' value='{$first}' aria-label='{$first_name_aria_label}' {$first_tabindex} {$disabled_text} {$required_attribute} {$invalid_attribute} {$first_placeholder_attribute}/>
                                                </span>";
					}

					$last_markup = '';
					$style       = ( $is_admin && rgar( $last_input, 'isHidden' ) ) ? "style='display:none;'" : '';
					if ( $is_admin || ! rgar( $last_input, 'isHidden' ) ) {
						$last_markup = "<span id='{$field_id}_6_container' class='name_last' {$style}>
                                                <label for='{$field_id}_6' {$sub_label_class_attribute}>" . $last_name_sub_label . "</label>
                                                <input type='text' name='input_{$id}.6' id='{$field_id}_6' value='{$last}' aria-label='{$last_name_aria_label}' {$last_tabindex} {$disabled_text} {$required_attribute} {$invalid_attribute} {$last_placeholder_attribute}/>
                                            </span>";
					}
				} else {
					$first_markup = '';
					$style        = ( $is_admin && rgar( $first_input, 'isHidden' ) ) ? "style='display:none;'" : '';
					if ( $is_admin || ! rgar( $first_input, 'isHidden' ) ) {
						$first_markup = "<span id='{$field_id}_3_container' class='name_first' {$style}>
                                                    <input type='text' name='input_{$id}.3' id='{$field_id}_3' value='{$first}' aria-label='{$first_name_aria_label}' {$first_tabindex} {$disabled_text} {$required_attribute} {$invalid_attribute} {$first_placeholder_attribute}/>
                                                    <label for='{$field_id}_3' {$sub_label_class_attribute}>{$first_name_sub_label}</label>
                                               </span>";
					}

					$last_markup = '';
					$style       = ( $is_admin && rgar( $last_input, 'isHidden' ) ) ? "style='display:none;'" : '';
					if ( $is_admin || ! rgar( $last_input, 'isHidden' ) ) {
						$last_markup = "<span id='{$field_id}_6_container' class='name_last' {$style}>
                                                    <input type='text' name='input_{$id}.6' id='{$field_id}_6' value='{$last}' aria-label='{$last_name_aria_label}' {$last_tabindex} {$disabled_text} {$required_attribute} {$invalid_attribute} {$last_placeholder_attribute}/>
                                                    <label for='{$field_id}_6' {$sub_label_class_attribute}>{$last_name_sub_label}</label>
                                                </span>";
					}
				}

				$css_class = $this->get_css_class();

				return "<div class='ginput_complex{$class_suffix} ginput_container {$css_class}' id='{$field_id}'>
                            {$first_markup}
                            {$last_markup}
                            <div class='gf_clear gf_clear_complex'></div>
                        </div>";
		}
	}

	/**
	 * Defines the CSS class to be applied to the field label.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GF_Field::get_field_content()
	 *
	 * @return string The CSS class.
	 */
	public function get_field_label_class() {
		return 'gfield_label gfield_label_before_complex';
	}

	/**
	 * Sets the CSS class to be used by the field input.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GF_Field_Name::get_field_input()
	 * @uses    GFFormsModel::get_input()
	 *
	 * @return string The CSS class to use for the field.
	 */
	public function get_css_class() {

		$prefix_input = GFFormsModel::get_input( $this, $this->id . '.2' );
		$first_input  = GFFormsModel::get_input( $this, $this->id . '.3' );
		$middle_input = GFFormsModel::get_input( $this, $this->id . '.4' );
		$last_input   = GFFormsModel::get_input( $this, $this->id . '.6' );
		$suffix_input = GFFormsModel::get_input( $this, $this->id . '.8' );

		$css_class = '';
		$visible_input_count = 0;

		if ( $prefix_input && ! rgar( $prefix_input, 'isHidden' ) ) {
			$visible_input_count++;
			$css_class .= 'has_prefix ';
		} else {
			$css_class .= 'no_prefix ';
		}

		if ( $first_input && ! rgar( $first_input, 'isHidden' ) ) {
			$visible_input_count++;
			$css_class .= 'has_first_name ';
		} else {
			$css_class .= 'no_first_name ';
		}

		if ( $middle_input && ! rgar( $middle_input, 'isHidden' ) ) {
			$visible_input_count++;
			$css_class .= 'has_middle_name ';
		} else {
			$css_class .= 'no_middle_name ';
		}

		if ( $last_input && ! rgar( $last_input, 'isHidden' ) ) {
			$visible_input_count++;
			$css_class .= 'has_last_name ';
		} else {
			$css_class .= 'no_last_name ';
		}

		if ( $suffix_input && ! rgar( $suffix_input, 'isHidden' ) ) {
			$visible_input_count++;
			$css_class .= 'has_suffix ';
		} else {
			$css_class .= 'no_suffix ';
		}

		$css_class .= "gf_name_has_{$visible_input_count} ginput_container_name ";

		return trim( $css_class );
	}

	/**
	 * Defines the field markup to be used for the name prefix.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GF_Field_Name::get_field_input()
	 * @uses    GFCommon::get_input_placeholder_value()
	 * @uses    GFCommon::get_input_placeholder_attribute()
	 *
	 * @param array  $input         The input item choices.
	 * @param int    $id            The ID of the name field.
	 * @param int    $field_id      The field ID of the name field.
	 * @param string $value         The value to be used in the prefix field item.
	 * @param string $disabled_text The text to be used if the prefix field item is disabled.
	 * @param int    $tabindex      The tab index of the prefix field item.
	 *
	 * @return string The field HTML markup.
	 */
	public static function get_name_prefix_field( $input, $id, $field_id, $value, $disabled_text, $tabindex ) {

		$prefix_aria_label = esc_attr__( 'Name prefix', 'gravityforms' );


		if ( isset( $input['choices'] ) && is_array( $input['choices'] ) ) {
			$placeholder_value = GFCommon::get_input_placeholder_value( $input );
			$options           = "<option value=''>{$placeholder_value}</option>";
			$value_enabled     = rgar( $input, 'enableChoiceValue' );
			foreach ( $input['choices'] as $choice ) {
				$choice_value            = $value_enabled ? $choice['value'] : $choice['text'];
				$is_selected_by_default  = rgar( $choice, 'isSelected' );
				$is_this_choice_selected = empty( $value ) ? $is_selected_by_default : strtolower( $choice_value ) == strtolower( $value );
				$selected                = $is_this_choice_selected ? "selected='selected'" : '';
				$options .= "<option value='{$choice_value}' {$selected}>{$choice['text']}</option>";
			}

			$markup = "<select name='input_{$id}.2' id='{$field_id}_2' aria-label='{$prefix_aria_label}' {$tabindex} {$disabled_text}>
                          {$options}
                      </select>";

		} else {
			$placeholder_attribute = GFCommon::get_input_placeholder_attribute( $input );

			$markup = "<input type='text' name='input_{$id}.2' id='{$field_id}_2' value='{$value}' aria-label='{$prefix_aria_label}' {$tabindex} {$disabled_text} {$placeholder_attribute}/>";
		}

		return $markup;
	}

	/**
	 * Gets the field value to be displayed on the entry detail page.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFCommon::get_lead_field_display()
	 * @uses    GF_Field_Name::$id
	 *
	 * @param array|string $value    The value of the field input.
	 * @param string       $currency Not used.
	 * @param bool         $use_text Not used.
	 * @param string       $format   The format to output the value. Defaults to 'html'.
	 * @param string       $media    Not used.
	 *
	 * @return array|string The value to be displayed on the entry detail page.
	 */
	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {

		if ( is_array( $value ) ) {
			$prefix = trim( rgget( $this->id . '.2', $value ) );
			$first  = trim( rgget( $this->id . '.3', $value ) );
			$middle = trim( rgget( $this->id . '.4', $value ) );
			$last   = trim( rgget( $this->id . '.6', $value ) );
			$suffix = trim( rgget( $this->id . '.8', $value ) );

			$name = $prefix;
			$name .= ! empty( $name ) && ! empty( $first ) ? " $first" : $first;
			$name .= ! empty( $name ) && ! empty( $middle ) ? " $middle" : $middle;
			$name .= ! empty( $name ) && ! empty( $last ) ? " $last" : $last;
			$name .= ! empty( $name ) && ! empty( $suffix ) ? " $suffix" : $suffix;

			$return = $name;
		} else {
			$return = $value;
		}

		if ( $format === 'html' ) {
			$return = esc_html( $return );
		}
		return $return;
	}

	/**
	 * Gets a property value from an input.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GF_Field_Name::validate()
	 * @uses    GFFormsModel::get_input()
	 *
	 * @param int    $input_id      The input ID to obtain the property from.
	 * @param string $property_name The property name to search for.
	 *
	 * @return null|string The property value if found. Otherwise, null.
	 */
	public function get_input_property( $input_id, $property_name ) {
		$input = GFFormsModel::get_input( $this, $this->id . '.' . (string) $input_id );

		return rgar( $input, $property_name );
	}

	/**
	 * Sanitizes the field settings choices.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFFormDetail::add_field()
	 * @used-by GFFormsModel::sanitize_settings()
	 * @uses    GF_Field::sanitize_settings()
	 * @uses    GF_Field::sanitize_settings_choices()
	 *
	 * @return void
	 */
	public function sanitize_settings() {
		parent::sanitize_settings();
		if ( is_array( $this->inputs ) ) {
			foreach ( $this->inputs as &$input ) {
				if ( isset ( $input['choices'] ) && is_array( $input['choices'] ) ) {
					$input['choices'] = $this->sanitize_settings_choices( $input['choices'] );
				}
			}
		}
	}

	/**
	 * Gets the field value to be used when exporting.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFExport::start_export()
	 * @used-by GFAddOn::get_field_value()
	 * @used-by GFAddOn::get_full_name()
	 *
	 * @param array  $entry    The Entry Object.
	 * @param string $input_id The input ID to format. Defaults to empty string. If not set, uses t
	 * @param bool   $use_text Not used.
	 * @param bool   $is_csv   Not used.
	 *
	 * @return string The field value.
	 */
	public function get_value_export( $entry, $input_id = '', $use_text = false, $is_csv = false ) {
		if ( empty( $input_id ) ) {
			$input_id = $this->id;
		}

		if ( absint( $input_id ) == $input_id ) {
			// If field is simple (one input), simply return full content.
			$name = rgar( $entry, $input_id );
			if ( ! empty( $name ) ) {
				return $name;
			}

			// Complex field (multiple inputs). Join all pieces and create name.
			$prefix = trim( rgar( $entry, $input_id . '.2' ) );
			$first  = trim( rgar( $entry, $input_id . '.3' ) );
			$middle = trim( rgar( $entry, $input_id . '.4' ) );
			$last   = trim( rgar( $entry, $input_id . '.6' ) );
			$suffix = trim( rgar( $entry, $input_id . '.8' ) );

			$name = $prefix;
			$name .= ! empty( $name ) && ! empty( $first ) ? ' ' . $first : $first;
			$name .= ! empty( $name ) && ! empty( $middle ) ? ' ' . $middle : $middle;
			$name .= ! empty( $name ) && ! empty( $last ) ? ' ' . $last : $last;
			$name .= ! empty( $name ) && ! empty( $suffix ) ? ' ' . $suffix : $suffix;

			return $name;
		} else {

			return rgar( $entry, $input_id );
		}
	}

	/**
	 * Removes the "for" attribute in the field label. Inputs are only allowed one label (a11y) and the inputs already have labels.
	 *
	 * @since  2.4
	 * @access public
	 *
	 * @param array $form The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_first_input_id( $form ) {
		return '';
	}

	// # FIELD FILTER UI HELPERS ---------------------------------------------------------------------------------------

	/**
	 * Returns the sub-filters for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_sub_filters() {
		$sub_filters = array();

		if ( $this->nameFormat == 'simple' ) {
			return $sub_filters;
		}

		$inputs = $this->inputs;

		foreach ( (array) $inputs as $input ) {
			if ( rgar( $input, 'isHidden' ) ) {
				continue;
			}

			$sub_filters[] = array(
				'key'             => rgar( $input, 'id' ),
				'text'            => rgar( $input, 'customLabel', rgar( $input, 'label' ) ),
				'preventMultiple' => false,
				'operators'       => $this->get_filter_operators(),
			);
		}

		return $sub_filters;
	}

	/**
	 * Returns the filter operators for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_operators() {
		$operators   = parent::get_filter_operators();
		$operators[] = 'contains';

		return $operators;
	}
}

// Registers the Name field with the field framework.
GF_Fields::register( new GF_Field_Name() );
class-gf-field-list.php000066600000055616151264036140011031 0ustar00<?php

// If Gravity Forms isn't loaded, bail.
if ( ! class_exists( 'GFForms' ) ) {
	die();
}

/**
 * Class GF_Field_List
 *
 * Handles the behavior of List fields.
 *
 * @since Unknown
 */
class GF_Field_List extends GF_Field {

	/**
	 * Sets the field type for the List field.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @var string The field type.
	 */
	public $type = 'list';

	/**
	 * Sets the field title to be displayed in the form editor.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @return string The field title. Escaped and translatable.
	 */
	public function get_form_editor_field_title() {
		return esc_attr__( 'List', 'gravityforms' );
	}

	/**
	 * Defines the field settings available in the form editor.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFFormDetail::inline_scripts()
	 *
	 * @return array The settings available.
	 */
	function get_form_editor_field_settings() {
		return array(
			'columns_setting',
			'maxrows_setting',
			'add_icon_url_setting',
			'delete_icon_url_setting',
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'rules_setting',
			'visibility_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	/**
	 * Gets the ID of the first input.
	 *
	 * @since Unknown
	 * @access public
	 *
	 * @uses GF_Field::is_form_editor()
	 * @uses GF_Field_List::$id
	 *
	 * @param array $form The Form Object.
	 *
	 * @return string The ID of the first input. Empty string if not found.
	 */
	public function get_first_input_id( $form ) {
		return '';
	}

	/**
	 * Defines if the inline style block has been printed.
	 *
	 * @since  Unknown
	 * @access private
	 *
	 * @var bool false
	 */
	private static $_style_block_printed = false;

	/**
	 * Builds the field input HTML markup.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param array      $form  The Form Object.
	 * @param string     $value The field value. Defaults to empty string.
	 * @param null|array $entry The Entry Object. Defaults to null.
	 *
	 * @return string The List field HTML markup.
	 */
	public function get_field_input( $form, $value = '', $entry = null ) {

		$form_id        = $form['id'];
		$is_form_editor = $this->is_form_editor();

		if ( ! empty( $value ) ) {
			$value = maybe_unserialize( $value );
		}

		if ( is_array( $value ) ) {
			if ( ! is_array( $value[0] ) ) {
				$value = $this->create_list_array( $value );
			}
		} else {
			$value = array( array() );
		}

		$has_columns = is_array( $this->choices );
		$columns     = $has_columns ? $this->choices : array( array() );

		$list = '';
		if ( ! self::$_style_block_printed ){
			// This style block needs to be inline so that the list field continues to work even if the option to turn off CSS output is activated.
			$list .= '<style type="text/css">

						body .ginput_container_list table.gfield_list tbody tr td.gfield_list_icons {
							vertical-align: middle !important;
						}

						body .ginput_container_list table.gfield_list tbody tr td.gfield_list_icons img {
							background-color: transparent !important;
							background-position: 0 0;
							background-size: 16px 16px !important;
							background-repeat: no-repeat;
							border: none !important;
							width: 16px !important;
							height: 16px !important;
							opacity: 0.5;
							transition: opacity .5s ease-out;
						    -moz-transition: opacity .5s ease-out;
						    -webkit-transition: opacity .5s ease-out;
						    -o-transition: opacity .5s ease-out;
						}

						body .ginput_container_list table.gfield_list tbody tr td.gfield_list_icons a:hover img {
							opacity: 1.0;
						}

						</style>';

			self::$_style_block_printed = true;
		}

		$list .= "<div class='ginput_container ginput_container_list ginput_list'>" .
			"<table class='gfield_list gfield_list_container'>";

		if ( $has_columns ) {

			$list .= '<colgroup>';
			for ( $colnum = 1; $colnum <= count( $columns ) + 1; $colnum++ ) {
				$odd_even = ( $colnum % 2 ) == 0 ? 'even' : 'odd';
				$list .= sprintf( "<col id='gfield_list_%d_col_%d' class='gfield_list_col_%s' />", $this->id, $colnum, $odd_even );
			}
			$list .= '</colgroup>';

			$list .= '<thead><tr>';
			foreach ( $columns as $column ) {
				// a11y: scope="col"
				$list .= '<th scope="col">' . esc_html( $column['text'] ) . '</th>';
			}

			if ( $this->maxRows != 1 ) {
				// Using td instead of th because empty th tags break a11y.
				$list .= '<td>&nbsp;</td>';
			}

			$list .= '</tr></thead>';
		} else {
			$list .=
				'<colgroup>' .
					"<col id='gfield_list_{$this->id}_col1' class='gfield_list_col_odd' />" .
					"<col id='gfield_list_{$this->id}_col2' class='gfield_list_col_even' />" .
				'</colgroup>';
		}

		$delete_display      = count( $value ) == 1 ? 'style="visibility:hidden;"' : '';
		$maxRow              = intval( $this->maxRows );
		$disabled_icon_class = ! empty( $maxRow ) && count( $value ) >= $maxRow ? 'gfield_icon_disabled' : '';

		$add_icon    = ! empty( $this->addIconUrl ) ? $this->addIconUrl : GFCommon::get_base_url() . '/images/list-add.svg';
		$delete_icon = ! empty( $this->deleteIconUrl ) ? $this->deleteIconUrl : GFCommon::get_base_url() . '/images/list-remove.svg';

		$add_events    = $is_form_editor ? '' : "onclick='gformAddListItem(this, {$maxRow})' onkeypress='gformAddListItem(this, {$maxRow})'";
		$delete_events = $is_form_editor ? '' : "onclick='gformDeleteListItem(this, {$maxRow})' onkeypress='gformDeleteListItem(this, {$maxRow})'";

		$list .= '<tbody>';
		$rownum = 1;
		foreach ( $value as $item ) {

			$odd_even = ( $rownum % 2 ) == 0 ? 'even' : 'odd';

			$list .= "<tr class='gfield_list_row_{$odd_even} gfield_list_group'>";
			$colnum = 1;
			foreach ( $columns as $column ) {
				$data_label = '';

				// Getting value. Taking into account columns being added/removed from form meta.
				if ( is_array( $item ) ) {
					if ( $has_columns ) {
						$val = rgar( $item, $column['text'] );
						$data_label = "data-label='" . esc_attr( $column['text'] ) . "'";
					} else {
						$vals = array_values( $item );
						$val  = rgar( $vals, 0 );
					}
				} else {
					$val = $colnum == 1 ? $item : '';
				}

				$list .= "<td class='gfield_list_cell gfield_list_{$this->id}_cell{$colnum}' {$data_label}>" . $this->get_list_input( $has_columns, $column, $val, $form_id ) . '</td>';
				$colnum ++;
			}

			if ( $this->maxRows != 1 ) {

				// Can't replace these icons with the webfont versions since they appear on the front end.

				$list .= "<td class='gfield_list_icons'>";
				$list .= "   <a href='javascript:void(0);' class='add_list_item {$disabled_icon_class}' aria-label='" . esc_attr__( 'Add another row', 'gravityforms' ) . "' {$add_events}><img src='{$add_icon}' alt='' title='" . esc_attr__( 'Add a new row', 'gravityforms' ) . "' /></a>" .
				         "   <a href='javascript:void(0);' class='delete_list_item' aria-label='" . esc_attr__( 'Remove this row', 'gravityforms' ) . "' {$delete_events} {$delete_display}><img src='{$delete_icon}' alt='' title='" . esc_attr__( 'Remove this row', 'gravityforms' ) . "' /></a>";
				$list .= '</td>';

			}

			$list .= '</tr>';

			if ( ! empty( $maxRow ) && $rownum >= $maxRow ) {
				break;
			}

			$rownum ++;
		}

		$list .= '</tbody>';
		$list .= '</table></div>';

		return $list;

	}

	/**
	 * Builds the input that will be inside the List field.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param bool   $has_columns If the input has columns.
	 * @param array  $column      The column details.
	 * @param string $value       The existing value of the input.
	 * @param int    $form_id     The form ID.
	 *
	 * @return string The input HTML markup.
	 */
	public function get_list_input( $has_columns, $column, $value, $form_id ) {

		$tabindex = $this->get_tabindex();
		$disabled = $this->is_form_editor() ? 'disabled' : '';

		$column_index = 1;
		if ( $has_columns && is_array( $this->choices ) ) {
			foreach ( $this->choices as $choice ) {
				if ( $choice['text'] == $column['text'] ) {
					break;
				}

				$column_index ++;
			}
		}
		$input_info = array( 'type' => 'text' );

		$column_text = rgar( $column, 'text' );

		$aria_label = isset( $column['text'] ) ? $column_text : $this->label;

		/**
		 * Filters the column input.
		 *
		 * @since Unknown
		 *
		 * @param array  $input_info     Information about the input. Contains the input type.
		 * @param object GF_Field_List   Field object for this field type.
		 * @param string $column['text'] The column text value.
		 * @param int    $form_id        The form ID.
		 */
		$input_info = gf_apply_filters( array(
			'gform_column_input',
			$form_id,
			$this->id,
			$column_index
		), $input_info, $this, $column_text, $value, $form_id );

		switch ( $input_info['type'] ) {

			case 'select' :
				$input = "<select name='input_{$this->id}[]' {$tabindex} {$disabled} >";
				if ( ! is_array( $input_info['choices'] ) ) {
					$input_info['choices'] = array_map( 'trim', explode( ',', $input_info['choices'] ) );
				}

				foreach ( $input_info['choices'] as $choice ) {
					if ( is_array( $choice ) ) {
						$choice_value    = $choice['value'];
						$choice_text     = $choice['text'];
						$choice_selected = array_key_exists( 'isSelected', $choice ) ? $choice['isSelected'] : false;
					} else {
						$choice_value    = $choice;
						$choice_text     = $choice;
						$choice_selected = false;
					}
					$is_selected = empty( $value ) ? $choice_selected : $choice_value == $value;
					$selected    = $is_selected ? "selected='selected'" : '';
					$input .= "<option value='" . esc_attr( $choice_value ) . "' {$selected}>" . esc_html( $choice_text ) . '</option>';
				}
				$input .= '</select>';

				break;

			default :
				// a11y: inputs without a label must have the aria-label attribute set.
				$input = "<input aria-label='" . esc_attr( $aria_label ) . "' type='text' name='input_{$this->id}[]' value='" . esc_attr( $value ) . "' {$tabindex} {$disabled}/>";
				break;
		}

		/**
		 * Filters the column input HTML markup.
		 *
		 * @since Unknown
		 *
		 * @param string $input          The input markup.
		 * @param array  $input_info     The information that was used to build the input.
		 * @param object GF_Field_List   An instance of the List field object.
		 * @param string $column['text'] The column text value.
		 * @param int    $form_id        The form ID.
		 */
		return gf_apply_filters( array(
			'gform_column_input_content',
			$form_id,
			$this->id,
			$column_index
		), $input, $input_info, $this, rgar( $column, 'text' ), $value, $form_id );

	}

	/**
	 * Gets the CSS class to be used in the field label.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GF_Field::get_field_content()
	 *
	 * @return string String containing the CSS class names.
	 */
	public function get_field_label_class(){

		$has_columns = is_array( $this->choices );

		return $has_columns ? 'gfield_label gfield_label_before_complex' : 'gfield_label';
	}

	/**
	 * Whether this field expects an array during submission.
	 *
	 * @since 2.4
	 *
	 * @return bool
	 */
	public function is_value_submission_array() {
		return true;
	}

	/**
	 * Gets the value of te field from the form submission.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param array $field_values             The properties to search for.
	 * @param bool  $get_from_post_global_var If the global GET variable should be used to obtain the value. Defaults to true.
	 *
	 * @return array The submission value.
	 */
	public function get_value_submission( $field_values, $get_from_post_global_var = true ) {
		$value = $this->get_input_value_submission( 'input_' . $this->id, $this->inputName, $field_values, $get_from_post_global_var );

		return $value;
	}

	/**
	 * Creates an array from the list items. Recurses if the field is inside a Repeater.
	 *
	 * @since 2.4
	 *
	 * @param $value
	 *
	 * @return array
	 */
	public function create_list_array_recursive( $value ) {
		if ( isset( $value[0] ) && is_array( $value[0] ) ) {
			$new_value = array();
			foreach ( $value  as $k => $v ) {
				$new_value[ $k ] = $this->create_list_array_recursive( $v );
			}
		} else {
			$new_value = $this->create_list_array( $value );
		}
		return $new_value;
	}

	/**
	 * Check if the submission value is empty.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param int $form_id The form ID to check.
	 *
	 * @return bool True if empty. False otherwise.
	 */
	public function is_value_submission_empty( $form_id ) {
		$value = rgpost( 'input_' . $this->id );
		if ( is_array( $value ) ) {
			// Empty if all inputs are empty (for inputs with the same name).
			foreach ( $value as $input ) {
				if ( strlen( trim( $input ) ) > 0 ) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * Gets the field value HTML markup to be used on the entry detail page.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param array  $value    The submitted entry value.
	 * @param string $currency Not used.
	 * @param bool   $use_text Not used.
	 * @param string $format   The format to be used when building the items.
	 *                         Accepted values are text, url, or html. Defaults to html.
	 * @param string $media    Defines how the content will be output.
	 *                         Accepted values are screen or email. Defaults to screen.
	 *
	 * @return string The HTML markup to be displayed.
	 */
	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
		if ( empty( $value ) ) {
			return '';
		}

		$value = maybe_unserialize( $value );

		if( ! is_array( $value ) || ! isset( $value[0] ) ) {
			return '';
		}

		$has_columns = is_array( $value[0] );

		if ( ! $has_columns ) {
			$items = '';
			foreach ( $value as $key => $item ) {
				if ( ! empty( $item ) ) {
					$item = wp_kses_post( $item );
					switch ( $format ) {
						case 'text' :
							$items .= $item . ', ';
							break;
						case 'url' :
							$items .= $item . ',';
							break;
						default :
							if ( $media == 'email' ) {
								$items .= "<li>{$item}</li>";
							} else {
								$items .= "<li>{$item}</li>";
							}
							break;
					}
				}
			}

			if ( empty( $items ) ) {
				return '';
			} elseif ( $format == 'text' ) {
				return substr( $items, 0, strlen( $items ) - 2 ); // Removing last comma.
			} elseif ( $format == 'url' ) {
				return substr( $items, 0, strlen( $items ) - 1 ); // Removing last comma.
			} elseif ( $media == 'email' ) {
				return "<ul class='bulleted'>{$items}</ul>";
			} else {
				return "<ul class='bulleted'>{$items}</ul>";
			}
		} elseif ( is_array( $value ) ) {
			$columns = array_keys( $value[0] );

			$list = '';

			switch ( $format ) {
				case 'text' :
					$is_first_row = true;
					foreach ( $value as $item ) {
						if ( ! $is_first_row ) {
							$list .= "\n\n" . $this->label . ': ';
						}

						$item = array_map( 'wp_kses_post', $item );

						$list .= implode( ',', array_values( $item ) );

						$is_first_row = false;
					}
					break;

				case 'url' :
					foreach ( $value as $item ) {
						$item = array_map( 'wp_kses_post', $item );
						$list .= implode( "|", array_values( $item ) ) . ',';
					}
					if ( ! empty( $list ) ) {
						$list = substr( $list, 0, strlen( $list ) - 1 );
					}

					break;

				default :
					if ( $media == 'email' ) {
						$list = "<table class='gfield_list' style='border-top: 1px solid #DFDFDF; border-left: 1px solid #DFDFDF; border-spacing: 0; padding: 0; margin: 2px 0 6px; width: 100%'><thead><tr>\n";

						//reading columns from entry data
						foreach ( $columns as $column ) {
							$list .= "<th style='background-image: none; border-right: 1px solid #DFDFDF; border-bottom: 1px solid #DFDFDF; padding: 6px 10px; font-family: sans-serif; font-size: 12px; font-weight: bold; background-color: #F1F1F1; color:#333; text-align:left'>" . esc_html( $column ) . '</th>' . "\n";
						}
						$list .= '</tr></thead>' . "\n";

						$list .= "<tbody style='background-color: #F9F9F9'>";
						foreach ( $value as $item ) {
							$list .= '<tr>';
							foreach ( $columns as $column ) {
								$val = rgar( $item, $column );
								$val = wp_kses_post( $val );
								$list .= "<td style='padding: 6px 10px; border-right: 1px solid #DFDFDF; border-bottom: 1px solid #DFDFDF; border-top: 1px solid #FFF; font-family: sans-serif; font-size:12px;'>{$val}</td>\n";
							}

							$list .= '</tr>' . "\n";
						}

						$list .= '<tbody></table>' . "\n";
					} else {
						$list = "<table class='gfield_list'><thead><tr>";

						// Reading columns from entry data.
						foreach ( $columns as $column ) {
							$list .= '<th scope="col">' . esc_html( $column ) . '</th>' . "\n";
						}
						$list .= '</tr></thead>' . "\n";

						$list .= '<tbody>';
						foreach ( $value as $item ) {
							$list .= '<tr>';
							foreach ( $columns as $column ) {
								$val = rgar( $item, $column );
								$val = wp_kses_post( $val );
								$list .= "<td>{$val}</td>\n";
							}

							$list .= '</tr>' . "\n";
						}

						$list .= '<tbody></table>' . "\n";
					}
					break;
			}

			return $list;
		}

		return '';
	}

	/**
	 * Gets the value of the field when the entry is saved.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param string $value      The value to use.
	 * @param array  $form       The form that the entry is associated with.
	 * @param string $input_name The name of the input containing the value.
	 * @param int    $lead_id    The entry ID.
	 * @param array  $lead       The Entry Object.
	 *
	 * @return string The entry value. Escaped.
	 */
	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {

		if ( $this->is_administrative() && $this->allowsPrepopulate ) {
			$value = json_decode( $value );
		}

		if ( GFCommon::is_empty_array( $value ) ) {
			$value = '';
		} else {
			$value = $this->create_list_array( $value );
			$value = serialize( $value );
		}

		$value_safe = $this->sanitize_entry_value( $value, $form['id'] );

		return $value_safe;
	}

	/**
	 * Gets merge tag values.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param array|string $value      The value of the input.
	 * @param string       $input_id   The input ID to use.
	 * @param array        $entry      The Entry Object.
	 * @param array        $form       The Form Object
	 * @param string       $modifier   The modifier passed.
	 * @param array|string $raw_value  The raw value of the input.
	 * @param bool         $url_encode If the result should be URL encoded.
	 * @param bool         $esc_html   If the HTML should be escaped.
	 * @param string       $format     The format that the value should be.
	 * @param bool         $nl2br      If the nl2br function should be used.
	 *
	 * @return string The processed merge tag.
	 */
	public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) {

		$modifiers = $this->get_modifiers();

		$allowed_modifiers = array( 'text', 'html', 'url' );

		if( $found_modifiers = array_intersect( $modifiers, $allowed_modifiers ) ) {
			$output_format = $found_modifiers[0];
		} else {
			$output_format = $format;
		}

		return GFCommon::get_lead_field_display( $this, $raw_value, $entry['currency'], true, $output_format );
	}

	/**
	 * Format the entry value for display on the entries list page.
	 *
	 * By default, the List field will not be available for selection on the entry list.
	 * Use the gform_display_field_select_columns_entry_list filter to make the list field available.
	 *
	 *
	 * @since 2.4
	 *
	 * @param string|array $value    The field value.
	 * @param array        $entry    The Entry Object currently being processed.
	 * @param string       $field_id The field or input ID currently being processed.
	 * @param array        $columns  The properties for the columns being displayed on the entry list page.
	 * @param array        $form     The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) {
		return GFCommon::get_lead_field_display( $this, $value, $entry['currency'], true, 'html' );
	}

	/**
	 * Creates an array from the list items.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param array $value The pre-formatted list.
	 *
	 * @return array The list rows.
	 */
	function create_list_array( $value ) {
		if ( ! $this->enableColumns ) {
			return $value;
		} else {
			$value     = empty( $value ) ? array() : $value;
			$col_count = count( $this->choices );
			$rows      = array();

			$row_count = count( $value ) / $col_count;

			$col_index = 0;
			for ( $i = 0; $i < $row_count; $i ++ ) {
				$row = array();
				foreach ( $this->choices as $column ) {
					$row[ $column['text'] ] = rgar( $value, $col_index );
					$col_index ++;
				}
				$rows[] = $row;
			}

			return $rows;
		}
	}

	/**
	 * Sanitizes the field settings.
	 *
	 * @since  Unknown
	 * @access public
	 */
	public function sanitize_settings() {
		parent::sanitize_settings();
		$this->maxRows       = absint( $this->maxRows );
		$this->enableColumns = (bool) $this->enableColumns;
	}

	/**
	 * Gets the field value, formatted for exports. For CSV export return an array.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFExport::start_export()
	 * @used-by GFAddOn::get_field_value()
	 * @uses    GF_Field_List::$id
	 * @uses    GF_Field_List::$enableColumns
	 * @uses    GF_Field_List::$choices
	 * @uses    GFCommon::implode_non_blank()
	 *
	 * @param array  $entry    The Entry Object.
	 * @param string $input_id Input ID to export. If not defined, uses the current input ID. Defaults to empty string.
	 * @param bool   $use_text Not used. Defaults to false.
	 * @param bool   $is_csv   Is the value going to be used in the CSV export? Defaults to false.
	 *
	 * @return string|array
	 */
	public function get_value_export( $entry, $input_id = '', $use_text = false, $is_csv = false ) {
		if ( empty( $input_id ) ) {
			$input_id = $this->id;
		} elseif ( ! ctype_digit( $input_id ) ) {
			$field_id_array = explode( '.', $input_id );
			$input_id       = rgar( $field_id_array, 0 );
			$column_num     = rgar( $field_id_array, 1 );
		}

		$value = rgar( $entry, $input_id );
		$value = maybe_unserialize( $value );

		if ( empty( $value ) || $is_csv ) {
			return $value;
		}

		$list_values = $column_values = $value;

		if ( isset( $column_num ) && is_numeric( $column_num ) && $this->enableColumns ) {
			$column        = rgars( $this->choices, "{$column_num}/text" );
			$column_values = array();
			foreach ( $list_values as $value ) {
				$column_values[] = rgar( $value, $column );
			}
		} elseif ( $this->enableColumns ) {

			return json_encode( $list_values );
		}

		return GFCommon::implode_non_blank( ', ', $column_values );
	}

	// # FIELD FILTER UI HELPERS ---------------------------------------------------------------------------------------

	/**
	 * Returns the filter operators for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_operators() {
		return array( 'contains' );
	}

}

// Register the list field.
GF_Fields::register( new GF_Field_List() );
class-gf-fields.php000066600000005014151264036140010226 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}

require_once( plugin_dir_path( __FILE__ ) . 'class-gf-field.php' );

class GF_Fields {

	public static $deprecation_notice_fired = false;

	/* @var GF_Field[] */
	private static $_fields = array();

	public static function register( $field ) {
		if ( ! is_subclass_of( $field, 'GF_Field' ) ) {
			throw new Exception( 'Must be a subclass of GF_Field' );
		}
		if ( empty( $field->type ) ) {
			throw new Exception( 'The type must be set' );
		}
		if ( isset( self::$_fields[ $field->type ] ) ) {
			throw new Exception( 'Field type already registered: ' . $field->type );
		}
		self::$_fields[ $field->type ] = $field;
	}

	public static function exists( $field_type ) {
		return isset( self::$_fields[ $field_type ] );
	}

	/**
	 * @param $field_type
	 *
	 * @return GF_Field
	 */
	public static function get_instance( $field_type ) {
		return isset( self::$_fields[ $field_type ] ) ? self::$_fields[ $field_type ] : false;
	}

	/**
	 * Alias for get_instance()
	 *
	 * @param $field_type
	 *
	 * @return GF_Field
	 */
	public static function get( $field_type ) {
		return self::get_instance( $field_type );
	}

	/**
	 * Return all the registered field types.
	 *
	 * @return GF_Field[]
	 */
	public static function get_all() {
		return self::$_fields;
	}

	/**
	 * Creates a Field object from an array of field properties.
	 *
	 * @param array|GF_Field $properties
	 *
	 * @return GF_Field | bool
	 */
	public static function create( $properties ) {
		if ( $properties instanceof GF_Field ) {
			$type = $properties->type;
			$type = empty( $properties->inputType ) ? $type : $properties->inputType;
		} else {
			$type = isset( $properties['type'] ) ? $properties['type'] : '';
			$type = empty( $properties['inputType'] ) ? $type : $properties['inputType'];
		}

		if ( empty( $type ) || ! isset( self::$_fields[ $type ] ) ) {
			return new GF_Field( $properties );
		}
		$class      = self::$_fields[ $type ];
		$class_name = get_class( $class );
		$field      = new $class_name( $properties );

		/**
		 * Filter the GF_Field object after it is created.
		 *
		 * @since  1.9.18.2
		 *
		 * @param  GF_Field $field      A GF_Field object.
		 * @param  array    $properties An array of field properties used to generate the GF_Field object.
		 *
		 * @see    https://docs.gravityforms.com/gform_gf_field_create/
		 */
		return apply_filters( 'gform_gf_field_create', $field, $properties );

	}
}

// Load all the field files automatically.
GFCommon::glob_require_once( '/includes/fields/class-gf-field-*.php' );
class-gf-field-password.php000066600000037471151264036140011717 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}

class GF_Field_Password extends GF_Field {

	public $type = 'password';

	private static $passwords = array();

	public function get_form_editor_field_title() {
		return esc_attr__( 'Password', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'size_setting',
			'rules_setting',
			'input_placeholders_setting',
			'sub_label_placement_setting',
			'description_setting',
			'css_class_setting',
			'password_strength_setting',
			'password_visibility_setting',
			'password_setting',
		);
	}

	public function get_form_editor_button() {
		return array(); // this button is conditionally added in the form detail page
	}

	public function get_entry_inputs() {
		return null;
	}

	public function validate( $value, $form ) {
		$password = rgpost( 'input_' . $this->id );
		$confirm  = rgpost( 'input_' . $this->id . '_2' );
		if ( $this->is_confirm_input_enabled() && $password != $confirm ) {
			$this->failed_validation  = true;
			$this->validation_message = esc_html__( 'Your passwords do not match.', 'gravityforms' );
		} elseif ( $this->passwordStrengthEnabled && ! empty( $this->minPasswordStrength ) && ! empty( $password ) ) {
			
			$strength = rgpost('input_' . $this->id . '_strength' );
			
			if ( empty( $strength ) ) {
				$strength = $this->get_password_strength( $password );
			}

			$levels = array( 'short' => 1, 'bad' => 2, 'good' => 3, 'strong' => 4 );
			if ( rgar( $levels, $strength, 0 ) < $levels[ $this->minPasswordStrength ] ) {
				$this->failed_validation  = true;
				$this->validation_message = empty( $this->errorMessage ) ? sprintf( esc_html__( 'Your password does not meet the required strength. %sHint: To make it stronger, use upper and lower case letters, numbers and symbols like ! " ? $ %% ^ & ).', 'gravityforms' ), '<br />' ) : $this->errorMessage;
			}
		}
	}
	
	/**
	 * Calculate the password score using PHP when not passed by JS.
	 *
	 * @since 2.4.11
	 *
	 * @see gravityforms.js gformPasswordStrength() JS code
	 *
	 * @param string $password The password that should be checked.
	 *
	 * @return string blank|short|bad|good|strong
	 */
	protected function get_password_strength( $password = '' ) {

		$symbol_size = 0;
		$strlen      = GFCommon::safe_strlen( $password );

		if ( 0 >= $strlen ) {
			return 'blank';
		}

		if ( $strlen < 4 ) {
			return 'short';
		}

		if ( preg_match( '/[ 0 - 9 ] /', $password ) ) {
			$symbol_size += 10;
		}

		if ( preg_match( '/[ a - z ] /', $password ) ) {
			$symbol_size += 26;
		}

		if ( preg_match( '/[ A - Z ] /', $password ) ) {
			$symbol_size += 26;
		}

		if ( preg_match( '/[^a - zA - Z0 - 9]/', $password ) ) {
			$symbol_size += 31;
		}

		$natLog = log( pow( $symbol_size, $strlen ) );
		$score  = $natLog / log( 2 );

		if ( 40 > $score ) {
			return 'bad';
		}

		if ( 56 > $score ) {
			return 'good';
		}

		return 'strong';
	}

	public function get_field_input( $form, $value = '', $entry = null ) {

		if ( is_array( $value ) ) {
			$value = array_values( $value );
		}

		$form_id         = $form['id'];
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();
		$is_admin        = $is_entry_detail || $is_form_editor;

		$id       = (int) $this->id;
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		$size         = $this->size;
		$class_suffix = $is_entry_detail ? '_admin' : '';
		$class        = $this->is_confirm_input_enabled() ? '' : $size . $class_suffix; // Size only applies when confirmation is disabled.
		$class        = esc_attr( $class );

		$form_sub_label_placement  = rgar( $form, 'subLabelPlacement' );
		$field_sub_label_placement = $this->subLabelPlacement;
		$is_sub_label_above        = $field_sub_label_placement == 'above' || ( empty( $field_sub_label_placement ) && $form_sub_label_placement == 'above' );
		$sub_label_class_attribute = $field_sub_label_placement == 'hidden_label' ? "class='hidden_sub_label screen-reader-text'" : '';

		$disabled_text = $is_form_editor ? 'disabled="disabled"' : '';

		$first_tabindex = $this->get_tabindex();
		$last_tabindex  = $this->get_tabindex();

		$strength_style           = ! $this->passwordStrengthEnabled ? "style='display:none;'" : '';
		$strength_indicator_label = esc_html__( 'Strength indicator', 'gravityforms' );
		$strength                 = $this->passwordStrengthEnabled || $is_admin ? "<div id='{$field_id}_strength_indicator' class='gfield_password_strength' {$strength_style}>
																			{$strength_indicator_label}
																		</div>
																		<input type='hidden' class='gform_hidden' id='{$field_id}_strength' name='input_{$id}_strength' />" : '';

		$action   = ! $is_admin ? "gformShowPasswordStrength(\"$field_id\");" : '';
		$onchange = $this->passwordStrengthEnabled ? "onchange='{$action}'" : '';
		$onkeyup  = $this->passwordStrengthEnabled ? "onkeyup='{$action}'" : '';

		$confirmation_value = rgpost( 'input_' . $id . '_2' );

		$password_value     = is_array( $value ) ? $value[0] : $value;
		$password_value     = esc_attr( $password_value );
		$confirmation_value = esc_attr( $confirmation_value );

		$enter_password_field_input   = GFFormsModel::get_input( $this, $this->id . '' );
		$confirm_password_field_input = GFFormsModel::get_input( $this, $this->id . '.2' );

		$enter_password_label = rgar( $enter_password_field_input, 'customLabel' ) != '' ? $enter_password_field_input['customLabel'] : esc_html__( 'Enter Password', 'gravityforms' );
		$enter_password_label = gf_apply_filters( array( 'gform_password', $form_id ), $enter_password_label, $form_id );

		$confirm_password_label = rgar( $confirm_password_field_input, 'customLabel' ) != '' ? $confirm_password_field_input['customLabel'] : esc_html__( 'Confirm Password', 'gravityforms' );
		$confirm_password_label = gf_apply_filters( array( 'gform_password_confirm', $form_id ), $confirm_password_label, $form_id );
	
		$required_attribute    = $this->isRequired ? 'aria-required="true"' : '';
		$invalid_attribute     = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';

		$enter_password_placeholder_attribute   = GFCommon::get_input_placeholder_attribute( $enter_password_field_input );
		$confirm_password_placeholder_attribute = GFCommon::get_input_placeholder_attribute( $confirm_password_field_input );

		$visibility_toggle_style = ! $this->passwordVisibilityEnabled ? " style='display:none;'" : '';
		$enter_password_toggle   = $this->passwordVisibilityEnabled || $is_admin ? "<button type='button' onclick='javascript:gformToggleShowPassword(\"{$field_id}\");' label='" . esc_attr__( 'Show Password', 'gravityforms' ) . "' data-label-show='" . esc_attr__( 'Show Password', 'gravityforms' ) . "' data-label-hide='" . esc_attr__( 'Hide Password', 'gravityforms' ) . "'{$visibility_toggle_style}><span class='dashicons dashicons-hidden' aria-hidden='true'></span></button>" : "";
		$confirm_password_toggle = $this->passwordVisibilityEnabled || $is_admin ? "<button type='button' onclick='javascript:gformToggleShowPassword(\"{$field_id}_2\");' label='" . esc_attr__( 'Show Password', 'gravityforms' ) . "' data-label-show='" . esc_attr__( 'Show Password', 'gravityforms' ) . "' data-label-hide='" . esc_attr__( 'Hide Password', 'gravityforms' ) . "'{$visibility_toggle_style}><span class='dashicons dashicons-hidden' aria-hidden='true'></span></button>" : "";

		if ( $is_form_editor ) {
			$confirm_style = $this->is_confirm_input_enabled() ? '' : "style='display:none;'";

			if ( $is_sub_label_above ) {
				return "<div class='ginput_complex$class_suffix ginput_container ginput_container_password' id='{$field_id}_container'>
						<span id='{$field_id}_1_container' class='ginput_left'>
							<label for='{$field_id}' {$sub_label_class_attribute} {$confirm_style}>{$enter_password_label}</label>
							<input type='password' name='input_{$id}' id='{$field_id}' {$onkeyup} {$onchange} value='{$password_value}' {$first_tabindex} {$enter_password_placeholder_attribute} {$required_attribute} {$invalid_attribute} {$disabled_text}/>
							{$enter_password_toggle}
						</span>
						<span id='{$field_id}_2_container' class='ginput_right' {$confirm_style}>
							<label for='{$field_id}_2' {$sub_label_class_attribute}>{$confirm_password_label}</label>
							<input type='password' name='input_{$id}_2' id='{$field_id}_2' {$onkeyup} {$onchange} value='{$confirmation_value}' {$last_tabindex} {$confirm_password_placeholder_attribute} {$required_attribute} {$invalid_attribute} {$disabled_text}/>
							{$confirm_password_toggle}
						</span>
						<div class='gf_clear gf_clear_complex'></div>
					</div>{$strength}";
			} else {
				return "<div class='ginput_complex$class_suffix ginput_container ginput_container_password' id='{$field_id}_container'>
						<span id='{$field_id}_1_container' class='ginput_left'>
							<input type='password' name='input_{$id}' id='{$field_id}' {$onkeyup} {$onchange} value='{$password_value}' {$first_tabindex} {$enter_password_placeholder_attribute} {$required_attribute} {$invalid_attribute} {$disabled_text}/>
							{$enter_password_toggle}
							<label for='{$field_id}' {$sub_label_class_attribute} {$confirm_style}>{$enter_password_label}</label>
						</span>
						<span id='{$field_id}_2_container' class='ginput_right' {$confirm_style}>
							<input type='password' name='input_{$id}_2' id='{$field_id}_2' {$onkeyup} {$onchange} value='{$confirmation_value}' {$last_tabindex} {$confirm_password_placeholder_attribute} {$required_attribute} {$invalid_attribute} {$disabled_text}/>
							{$confirm_password_toggle}
							<label for='{$field_id}_2' {$sub_label_class_attribute}>{$confirm_password_label}</label>
						</span>
						<div class='gf_clear gf_clear_complex'></div>
					</div>{$strength}";
			}
		}

		if ( $this->is_confirm_input_enabled() ) {

			if ( $is_sub_label_above ) {
				return "<div class='ginput_complex$class_suffix ginput_container ginput_container_password' id='{$field_id}_container'>
						<span id='{$field_id}_1_container' class='ginput_left'>
							<label for='{$field_id}' {$sub_label_class_attribute}>{$enter_password_label}</label>
							<input type='password' name='input_{$id}' id='{$field_id}' {$onkeyup} {$onchange} value='{$password_value}' {$first_tabindex} {$enter_password_placeholder_attribute} {$required_attribute} {$invalid_attribute} {$disabled_text}/>
							{$enter_password_toggle}
						</span>
						<span id='{$field_id}_2_container' class='ginput_right'>
							<label for='{$field_id}_2' {$sub_label_class_attribute}>{$confirm_password_label}</label>
							<input type='password' name='input_{$id}_2' id='{$field_id}_2' {$onkeyup} {$onchange} value='{$confirmation_value}' {$last_tabindex} {$confirm_password_placeholder_attribute} {$required_attribute} {$invalid_attribute} {$disabled_text}/>
							{$confirm_password_toggle}
						</span>
						<div class='gf_clear gf_clear_complex'></div>
					</div>{$strength}";
			} else {
				return "<div class='ginput_complex$class_suffix ginput_container ginput_container_password' id='{$field_id}_container'>
						<span id='{$field_id}_1_container' class='ginput_left'>
							<input type='password' name='input_{$id}' id='{$field_id}' {$onkeyup} {$onchange} value='{$password_value}' {$first_tabindex} {$enter_password_placeholder_attribute} {$required_attribute} {$invalid_attribute} {$disabled_text}/>
							{$enter_password_toggle}
							<label for='{$field_id}' {$sub_label_class_attribute}>{$enter_password_label}</label>
						</span>
						<span id='{$field_id}_2_container' class='ginput_right'>
							<input type='password' name='input_{$id}_2' id='{$field_id}_2' {$onkeyup} {$onchange} value='{$confirmation_value}' {$last_tabindex} {$confirm_password_placeholder_attribute} {$required_attribute} {$invalid_attribute} {$disabled_text}/>
							{$confirm_password_toggle}
							<label for='{$field_id}_2' {$sub_label_class_attribute}>{$confirm_password_label}</label>
						</span>
						<div class='gf_clear gf_clear_complex'></div>
					</div>{$strength}";
			}

		} else {
			$class    = esc_attr( $class );

			return "<div class='ginput_container ginput_container_password'>
						<span id='{$field_id}_1_container' class='{$size}'>
							<input type='password' name='input_{$id}' id='{$field_id}' {$onkeyup} {$onchange} value='{$password_value}' {$first_tabindex} {$enter_password_placeholder_attribute} {$required_attribute} {$invalid_attribute} {$disabled_text}/>
							{$enter_password_toggle}
						</span>
						<div class='gf_clear gf_clear_complex'></div>
					</div>{$strength}";

		}

	}

	public function get_field_label_class(){
		return 'gfield_label gfield_label_before_complex';
	}

	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {

		/**
		 * A filter to allow the password to be encrypted (default set to false)
		 *
		 * @param bool Whether to encrypt the Password field with true or false
		 * @param array $form The Current Form Object
		 */
		$encrypt_password = apply_filters( 'gform_encrypt_password', false, $this, $form );
		if ( $encrypt_password ) {
			$value = GFCommon::openssl_encrypt( $value );
			GFFormsModel::set_openssl_encrypted_fields( $lead_id, $this->id );
		}

		return $value;
	}

	/**
	 * @deprecated 2.4.16
	 *
	 * @param $entry
	 * @param $form
	 */
	public static function delete_passwords( $entry, $form ) {
		$password_fields = GFAPI::get_fields_by_type( $form, array( 'password' ) );

		$field_ids = array();

		$encrypted_fields = GFFormsModel::get_openssl_encrypted_fields( $entry['id'] );

		foreach ( $password_fields as $password_field ) {
			$field_ids[] = $password_field->id;
			GFAPI::update_entry_field( $entry['id'], $password_field->id, '' );

			$key = array_search( $password_field->id, $encrypted_fields );
			if ( $key !== false ) {
				unset( $encrypted_fields[ $key ] );
			}
		}

		if ( empty( $encrypted_fields ) ) {
			gform_delete_meta( $entry['id'], '_openssl_encrypted_fields' );
		} else {
			gform_update_meta( $entry['id'], '_openssl_encrypted_fields', $encrypted_fields );
		}

	}

	/**
	 * Removes the "for" attribute in the field label.
	 * Inputs are only allowed one label (a11y) and the inputs already have labels.
	 *
	 * @since  2.4
	 * @access public
	 *
	 * @param array $form The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_first_input_id( $form ) {
		return '';
	}

	/**
	 * Determines if the Confirm Password input is enabled.
	 *
	 * @since 2.4.15
	 *
	 * @return bool
	 */
	private function is_confirm_input_enabled() {

		// Get Confirm Password input.
		$confirm_input = GFFormsModel::get_input( $this, $this->id . '.2' );

		return isset( $confirm_input['isHidden'] ) ? ! $confirm_input['isHidden'] : true;

	}

	/**
	 * Passwords are not saved to the database and won't be available in the runtime $entry object unless we stash and
	 * rehydrate them into the $entry object after it has been retrieved from the database.
	 *
	 * @since 2.4.16
	 *
	 * @param $form
	 */
	public static function stash_passwords( $form ) {
		foreach( $form['fields'] as $field ) {
			/* @var GF_Field $field */
			if ( $field->get_input_type() == 'password' ) {
				self::$passwords[ $field->id ] = $field->get_value_submission( rgpost( 'gform_field_values' ) );
			}
		}
	}

	/**
	 * Hydrate the stashed passwords back into the runtime $entry object that has just been saved and retrieved from the
	 * database.
	 *
	 * @since 2.4.16
	 *
	 * @param $entry
	 *
	 * @return array $entry
	 */
	public static function hydrate_passwords( $entry ) {
		foreach( self::$passwords as $field_id => $password ) {
			$entry[ $field_id ] = $password;
		}
		// Reset passwords so they are not available for the next submission in multi-submission requests (only possible via API).
		self::$passwords = array();
		return $entry;
	}

}

GF_Fields::register( new GF_Field_Password() );
class-gf-field-select.php000066600000014043151264036140011322 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_Select extends GF_Field {

	public $type = 'select';

	public function get_form_editor_field_title() {
		return esc_attr__( 'Drop Down', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'enable_enhanced_ui_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'size_setting',
			'choices_setting',
			'rules_setting',
			'placeholder_setting',
			'default_value_setting',
			'visibility_setting',
			'duplicate_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	public function is_conditional_logic_supported() {
		return true;
	}

	public function get_field_input( $form, $value = '', $entry = null ) {
		$form_id         = absint( $form['id'] );
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$id       = $this->id;
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		$size               = $this->size;
		$class_suffix       = $is_entry_detail ? '_admin' : '';
		$class              = $size . $class_suffix;
		$css_class          = trim( esc_attr( $class ) . ' gfield_select' );
		$tabindex           = $this->get_tabindex();
		$disabled_text      = $is_form_editor ? 'disabled="disabled"' : '';
		$required_attribute = $this->isRequired ? 'aria-required="true"' : '';
		$invalid_attribute  = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';

		return sprintf( "<div class='ginput_container ginput_container_select'><select name='input_%d' id='%s' class='%s' $tabindex %s %s %s>%s</select></div>", $id, $field_id, $css_class, $disabled_text, $required_attribute, $invalid_attribute, $this->get_choices( $value ) );
	}

	public function get_choices( $value ) {
		return GFCommon::get_select_choices( $this, $value );
	}

	public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) {
		$return = esc_html( $value );
		return GFCommon::selection_display( $return, $this, $entry['currency'] );
	}


	/**
	 * Gets merge tag values.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GFCommon::to_money()
	 * @uses GFCommon::format_post_category()
	 * @uses GFFormsModel::is_field_hidden()
	 * @uses GFFormsModel::get_choice_text()
	 * @uses GFCommon::format_variable_value()
	 * @uses GFCommon::implode_non_blank()
	 *
	 * @param array|string $value      The value of the input.
	 * @param string       $input_id   The input ID to use.
	 * @param array        $entry      The Entry Object.
	 * @param array        $form       The Form Object
	 * @param string       $modifier   The modifier passed.
	 * @param array|string $raw_value  The raw value of the input.
	 * @param bool         $url_encode If the result should be URL encoded.
	 * @param bool         $esc_html   If the HTML should be escaped.
	 * @param string       $format     The format that the value should be.
	 * @param bool         $nl2br      If the nl2br function should be used.
	 *
	 * @return string The processed merge tag.
	 */
	public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) {
		$modifiers       = $this->get_modifiers();
		$use_value       = in_array( 'value', $modifiers );
		$format_currency = ! $use_value && in_array( 'currency', $modifiers );
		$use_price       = $format_currency || ( ! $use_value && in_array( 'price', $modifiers ) );

		if ( is_array( $raw_value ) && (string) intval( $input_id ) != $input_id ) {
			$items = array( $input_id => $value ); // Float input Ids. (i.e. 4.1 ). Used when targeting specific checkbox items.
		} elseif ( is_array( $raw_value ) ) {
			$items = $raw_value;
		} else {
			$items = array( $input_id => $raw_value );
		}

		$ary = array();

		foreach ( $items as $input_id => $item ) {
			if ( $use_value ) {
				list( $val, $price ) = rgexplode( '|', $item, 2 );
			} elseif ( $use_price ) {
				list( $name, $val ) = rgexplode( '|', $item, 2 );
				if ( $format_currency ) {
					$val = GFCommon::to_money( $val, rgar( $entry, 'currency' ) );
				}
			} elseif ( $this->type == 'post_category' ) {
				$use_id     = strtolower( $modifier ) == 'id';
				$item_value = GFCommon::format_post_category( $item, $use_id );

				$val = RGFormsModel::is_field_hidden( $form, $this, array(), $entry ) ? '' : $item_value;
			} else {
				$val = RGFormsModel::is_field_hidden( $form, $this, array(), $entry ) ? '' : RGFormsModel::get_choice_text( $this, $raw_value, $input_id );
			}

			$ary[] = GFCommon::format_variable_value( $val, $url_encode, $esc_html, $format );
		}

		return GFCommon::implode_non_blank( ', ', $ary );
	}

	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
		$return = esc_html( $value );
		return GFCommon::selection_display( $return, $this, $currency, $use_text );
	}

	public function get_value_export( $entry, $input_id = '', $use_text = false, $is_csv = false ) {
		if ( empty( $input_id ) ) {
			$input_id = $this->id;
		}

		$value = rgar( $entry, $input_id );

		return $is_csv ? $value : GFCommon::selection_display( $value, $this, rgar( $entry, 'currency' ), $use_text );
	}

	/**
	 * Strips all tags from the input value.
	 *
	 * @param string $value The field value to be processed.
	 * @param int $form_id The ID of the form currently being processed.
	 *
	 * @return string
	 */
	public function sanitize_entry_value( $value, $form_id ) {

		$value = wp_strip_all_tags( $value );

		return $value;
	}

	// # FIELD FILTER UI HELPERS ---------------------------------------------------------------------------------------

	/**
	 * Returns the filter operators for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_operators() {
		$operators = $this->type == 'product' ? array( 'is' ) : array( 'is', 'isnot', '>', '<' );

		return $operators;
	}

}

GF_Fields::register( new GF_Field_Select() );
class-gf-field-price.php000066600000004264151264036140011151 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_Price extends GF_Field {

	public $type = 'price';

	function get_form_editor_field_settings() {
		return array(
			'rules_setting',
			'conditional_logic_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'placeholder_setting',
			'size_setting',
			'duplicate_setting',
		);
	}

	public function get_form_editor_button() {
		return array();
	}

	public function validate( $value, $form ) {
		$price = GFCommon::to_number( $value );
		if ( ! rgblank( $value ) && ( $price === false || $price < 0 ) ) {
			$this->failed_validation  = true;
			$this->validation_message = empty( $this->errorMessage ) ? __( 'Please enter a valid amount.', 'gravityforms' ) : $this->errorMessage;
		}
	}

	public function get_field_input( $form, $value = '', $entry = null ) {
		$form_id         = absint( $form['id'] );
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$id       = (int) $this->id;
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		$value = esc_attr( $value );

		$placeholder_attribute = $this->get_field_placeholder_attribute();

		$size         = $this->size;
		$class_suffix = $is_entry_detail ? '_admin' : '';
		$class        = $size . $class_suffix;
		$class        = esc_attr( $class );

		$disabled_text      = $is_form_editor ? 'disabled="disabled"' : '';
		$required_attribute = $this->isRequired ? 'aria-required="true"' : '';
		$invalid_attribute  = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';

		$tabindex = $this->get_tabindex();

		return "<div class='ginput_container ginput_container_product_price'>
					<input name='input_{$id}' id='{$field_id}' type='text' value='{$value}' class='{$class} ginput_amount' {$tabindex} {$placeholder_attribute} {$required_attribute} {$invalid_attribute} {$disabled_text}/>
				</div>";


	}

	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
		return GFCommon::to_money( $value, $currency );
	}


}

GF_Fields::register( new GF_Field_Price() );class-gf-field-section.php000066600000001641151264036140011507 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}

class GF_Field_Section extends GF_Field {

	public $type = 'section';

	public function get_form_editor_field_title() {
		return esc_attr__( 'Section', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'label_setting',
			'description_setting',
			'visibility_setting',
			'css_class_setting',
		);
	}

	public function get_field_content( $value, $force_frontend_label, $form ) {

		$field_label = $this->get_field_label( $force_frontend_label, $value );

		$admin_buttons = $this->get_admin_buttons();

		$description   = $this->get_description( $this->description, 'gsection_description' );
		$field_content = sprintf( "%s<h2 class='gsection_title'>%s</h2>%s", $admin_buttons, esc_html( $field_label ), $description );

		return $field_content;
	}

}

GF_Fields::register( new GF_Field_Section() );class-gf-field-page.php000066600000003326151264036140010761 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}

class GF_Field_Page extends GF_Field {

	public $type = 'page';

	public function get_form_editor_field_title() {
		return esc_attr__( 'Page', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'next_button_setting',
			'previous_button_setting',
			'css_class_setting',
			'conditional_logic_page_setting',
			'conditional_logic_nextbutton_setting',
		);
	}

	public function get_field_content( $value, $force_frontend_label, $form ) {
		$admin_buttons = $this->get_admin_buttons();
		$field_content = "{$admin_buttons} <label class='gfield_label'>&nbsp;</label><div class='gf-pagebreak-inline gf-pagebreak-container'><div class='gf-pagebreak-text-before'>" . esc_html__( 'end of page', 'gravityforms' ) . "</div><div class='gf-pagebreak-text-main'><span>" . esc_html__( 'PAGE BREAK', 'gravityforms' ) . "</span></div><div class='gf-pagebreak-text-after'>" . esc_html__( 'top of new page', 'gravityforms' ) . '</div></div>';
		return $field_content;
	}

	public function sanitize_settings() {
		parent::sanitize_settings();
		if ( $this->nextButton ) {
			$this->nextButton['imageUrl'] = wp_strip_all_tags( $this->nextButton['imageUrl'] );
			$allowed_tags      = wp_kses_allowed_html( 'post' );
			$this->nextButton['text'] = wp_kses( $this->nextButton['text'], $allowed_tags );
			$this->nextButton['type'] = wp_strip_all_tags( $this->nextButton['type'] );
			if ( isset( $this->nextButton['conditionalLogic'] ) && is_array( $this->nextButton['conditionalLogic'] ) ) {
				$this->nextButton['conditionalLogic'] = $this->sanitize_settings_conditional_logic( $this->nextButton['conditionalLogic'] );
			}
		}
	}

}

GF_Fields::register( new GF_Field_Page() );class-gf-field-fileupload.php000066600000072665151264036140012205 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_FileUpload extends GF_Field {

	public $type = 'fileupload';

	public function get_form_editor_field_title() {
		return esc_attr__( 'File Upload', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'rules_setting',
			'file_extensions_setting',
			'file_size_setting',
			'multiple_files_setting',
			'visibility_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	public function validate( $value, $form ) {
		$input_name = 'input_' . $this->id;
		GFCommon::log_debug( __METHOD__ . '(): Validating field ' . $input_name );

		$allowed_extensions = ! empty( $this->allowedExtensions ) ? GFCommon::clean_extensions( explode( ',', strtolower( $this->allowedExtensions ) ) ) : array();
		if ( $this->multipleFiles ) {
			$file_names = isset( GFFormsModel::$uploaded_files[ $form['id'] ][ $input_name ] ) ? GFFormsModel::$uploaded_files[ $form['id'] ][ $input_name ] : array();
		} else {
			$max_upload_size_in_bytes = isset( $this->maxFileSize ) && $this->maxFileSize > 0 ? $this->maxFileSize * 1048576 : wp_max_upload_size();
			$max_upload_size_in_mb    = $max_upload_size_in_bytes / 1048576;
			if ( ! empty( $_FILES[ $input_name ]['name'] ) && $_FILES[ $input_name ]['error'] > 0 ) {
				$uploaded_file_name = isset( GFFormsModel::$uploaded_files[ $form['id'] ][ $input_name ] ) ? GFFormsModel::$uploaded_files[ $form['id'] ][ $input_name ] : '';
				if ( empty( $uploaded_file_name ) ) {
					$this->failed_validation = true;
					switch ( $_FILES[ $input_name ]['error'] ) {
						case UPLOAD_ERR_INI_SIZE :
						case UPLOAD_ERR_FORM_SIZE :
							GFCommon::log_debug( __METHOD__ . '(): File ' . $_FILES[ $input_name ]['name'] . ' exceeds size limit. Maximum file size: ' . $max_upload_size_in_mb . 'MB' );
							$fileupload_validation_message = sprintf( esc_html__( 'File exceeds size limit. Maximum file size: %dMB', 'gravityforms' ), $max_upload_size_in_mb );
							break;
						default :
							GFCommon::log_debug( __METHOD__ . '(): The following error occurred while uploading - ' . $_FILES[ $input_name ]['error'] );
							$fileupload_validation_message = sprintf( esc_html__( 'There was an error while uploading the file. Error code: %d', 'gravityforms' ), $_FILES[ $input_name ]['error'] );
					}
					$this->validation_message = empty( $this->errorMessage ) ? $fileupload_validation_message : $this->errorMessage;
					return;
				}
			} elseif ( $_FILES[ $input_name ]['size'] > 0 && $_FILES[ $input_name ]['size'] > $max_upload_size_in_bytes ) {
				$this->failed_validation = true;
				GFCommon::log_debug( __METHOD__ . '(): File ' . $_FILES[ $input_name ]['name'] . ' exceeds size limit. Maximum file size: ' . $max_upload_size_in_mb . 'MB' );
				$this->validation_message = sprintf( esc_html__( 'File exceeds size limit. Maximum file size: %dMB', 'gravityforms' ), $max_upload_size_in_mb );
				return;
			}

			/**
			 * A filter to allow or disallow whitelisting when uploading a file
			 *
			 * @param bool false To set upload whitelisting to true or false (default is false, which means it is enabled)
			 */
			$whitelisting_disabled = apply_filters( 'gform_file_upload_whitelisting_disabled', false );

			if ( ! empty( $_FILES[ $input_name ]['name'] ) && ! $whitelisting_disabled ) {
				$check_result = GFCommon::check_type_and_ext( $_FILES[ $input_name ] );
				if ( is_wp_error( $check_result ) ) {
					$this->failed_validation = true;
					GFCommon::log_debug( sprintf( '%s(): %s; %s', __METHOD__, $check_result->get_error_code(), $check_result->get_error_message()  ) );
					$this->validation_message = esc_html__( 'The uploaded file type is not allowed.', 'gravityforms' );
					return;
				}
			}
			$single_file_name = $_FILES[ $input_name ]['name'];
			$file_names = array( array( 'uploaded_filename' => $single_file_name ) );
		}

		foreach ( $file_names as $file_name ) {
			GFCommon::log_debug( __METHOD__ . '(): Validating file upload for ' . $file_name['uploaded_filename'] );
			$info = pathinfo( rgar( $file_name, 'uploaded_filename' ) );

			if ( empty( $allowed_extensions ) ) {
				if ( GFCommon::file_name_has_disallowed_extension( rgar( $file_name, 'uploaded_filename' ) ) ) {
					GFCommon::log_debug( __METHOD__ . '(): The file has a disallowed extension, failing validation.' );
					$this->failed_validation  = true;
					$this->validation_message = empty( $this->errorMessage ) ? esc_html__( 'The uploaded file type is not allowed.', 'gravityforms' ) : $this->errorMessage;
				}
			} else {
				if ( ! empty( $info['basename'] ) && ! GFCommon::match_file_extension( rgar( $file_name, 'uploaded_filename' ), $allowed_extensions ) ) {
					GFCommon::log_debug( __METHOD__ . '(): The file is of a type that cannot be uploaded, failing validation.' );
					$this->failed_validation  = true;
					$this->validation_message = empty( $this->errorMessage ) ? sprintf( esc_html__( 'The uploaded file type is not allowed. Must be one of the following: %s', 'gravityforms' ), strtolower( $this->allowedExtensions ) ) : $this->errorMessage;
				}
			}
		}
		GFCommon::log_debug( __METHOD__ . '(): Validation complete.' );
	}

	public function get_first_input_id( $form ) {

		return $this->multipleFiles ? '' : 'input_' . $form['id'] . '_' . $this->id;
	}

	public function get_field_input( $form, $value = '', $entry = null ) {

		$lead_id = absint( rgar( $entry, 'id' ) );

		$form_id         = absint( $form['id'] );
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$id       = absint( $this->id );
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		$size         = $this->size;
		$class_suffix = $is_entry_detail ? '_admin' : '';
		$class        = $size . $class_suffix;
		$class        = esc_attr( $class );

		$disabled_text = $is_form_editor ? 'disabled="disabled"' : '';

		$tabindex        = $this->get_tabindex();
		$multiple_files  = $this->multipleFiles;
		$file_list_id    = 'gform_preview_' . $form_id . '_' . $id;

		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();
		$is_admin = $is_entry_detail || $is_form_editor;

		$max_upload_size = ! $is_admin && $this->maxFileSize > 0 ? $this->maxFileSize * 1048576 : wp_max_upload_size();
		$allowed_extensions = ! empty( $this->allowedExtensions ) ? join( ',', GFCommon::clean_extensions( explode( ',', strtolower( $this->allowedExtensions ) ) ) ) : array();
		if ( ! empty( $allowed_extensions ) ) {
			$extensions_message = esc_attr( sprintf( __( 'Accepted file types: %s.', 'gravityforms' ), str_replace( ',', ', ', $allowed_extensions ) ) );
		} else {
			$extensions_message = '';
		}

		$extensions_message_id = 'extensions_message_' . $form_id . '_' . $id;

		if ( $multiple_files ) {
			$upload_action_url = trailingslashit( site_url() ) . '?gf_page=' . GFCommon::get_upload_page_slug();
			$max_files         = $this->maxFiles > 0 ? $this->maxFiles : 0;
			$browse_button_id  = 'gform_browse_button_' . $form_id . '_' . $id;
			$container_id      = 'gform_multifile_upload_' . $form_id . '_' . $id;
			$drag_drop_id      = 'gform_drag_drop_area_' . $form_id . '_' . $id;

			$messages_id        = "gform_multifile_messages_{$form_id}_{$id}";
			if ( empty( $allowed_extensions ) ) {
				$allowed_extensions = '*';
			}
			$disallowed_extensions = GFCommon::get_disallowed_file_extensions();
			if ( defined( 'DOING_AJAX' ) && DOING_AJAX && 'rg_change_input_type' === rgpost( 'action' ) ) {
				$plupload_init = array();
			} else {
				$plupload_init = array(
					'runtimes'            => 'html5,flash,html4',
					'browse_button'       => $browse_button_id,
					'container'           => $container_id,
					'drop_element'        => $drag_drop_id,
					'filelist'            => $file_list_id,
					'unique_names'        => true,
					'file_data_name'      => 'file',
					/*'chunk_size' => '10mb',*/ // chunking doesn't currently have very good cross-browser support
					'url'                 => $upload_action_url,
					'flash_swf_url'       => includes_url( 'js/plupload/plupload.flash.swf' ),
					'silverlight_xap_url' => includes_url( 'js/plupload/plupload.silverlight.xap' ),
					'filters'             => array(
						'mime_types'    => array( array( 'title' => __( 'Allowed Files', 'gravityforms' ), 'extensions' => $allowed_extensions ) ),
						'max_file_size' => $max_upload_size . 'b',
					),
					'multipart'           => true,
					'urlstream_upload'    => false,
					'multipart_params'    => array(
						'form_id'  => $form_id,
						'field_id' => $id,
					),
					'gf_vars'             => array(
						'max_files'             => $max_files,
						'message_id'            => $messages_id,
						'disallowed_extensions' => $disallowed_extensions,
					)
				);

				if ( rgar( $form, 'requireLogin' ) ) {
					$plupload_init['multipart_params'][ '_gform_file_upload_nonce_' . $form_id ] = wp_create_nonce( 'gform_file_upload_' . $form_id, '_gform_file_upload_nonce_' . $form_id );
				}

				// plupload 2 was introduced in WordPress 3.9. Plupload 1 accepts a slightly different init array.
				if ( version_compare( get_bloginfo( 'version' ), '3.9-RC1', '<' ) ) {
					$plupload_init['max_file_size'] = $max_upload_size . 'b';
					$plupload_init['filters']       = array( array( 'title' => __( 'Allowed Files', 'gravityforms' ), 'extensions' => $allowed_extensions ) );
				}
			}

			$plupload_init = gf_apply_filters( array( 'gform_plupload_settings', $form_id ), $plupload_init, $form_id, $this );

			$drop_files_here_text = esc_html__( 'Drop files here or', 'gravityforms' );
			$select_files_text    = esc_attr__( 'Select files', 'gravityforms' );

			$plupload_init_json = htmlspecialchars( json_encode( $plupload_init ), ENT_QUOTES, 'UTF-8' );
			$upload             = "<div id='{$container_id}' data-settings='{$plupload_init_json}' class='gform_fileupload_multifile'>
										<div id='{$drag_drop_id}' class='gform_drop_area'>
											<span class='gform_drop_instructions'>{$drop_files_here_text} </span>
											<input id='{$browse_button_id}' type='button' value='{$select_files_text}' class='button gform_button_select_files' aria-describedby='{$extensions_message_id}' {$tabindex} />
										</div>
									</div>";
			if ( ! $is_admin ) {
				$upload .= "<span id='{$extensions_message_id}' class='screen-reader-text'>{$extensions_message}</span>";
				$upload .= "<div class='validation_message'>
								<ul id='{$messages_id}'>
								</ul>
							</div>";
			}

			if ( $is_entry_detail ) {
				$upload .= sprintf( '<input type="hidden" name="input_%d" value=\'%s\' />', $id, esc_attr( $value ) );
			}
		} else {
			$upload = '';
			if ( $max_upload_size <= 2047 * 1048576 ) {
				//  MAX_FILE_SIZE > 2048MB fails. The file size is checked anyway once uploaded, so it's not necessary.
				$upload = sprintf( "<input type='hidden' name='MAX_FILE_SIZE' value='%d' />", $max_upload_size );
			}
			$validation_message_id = 'validation_message_' . $form_id . '_' . $id;
			$live_validation_message_id= 'live_validation_message_' . $form_id . '_' . $id;
			$upload .= sprintf( "<input name='input_%d' id='%s' type='file' class='%s' aria-describedby='%s %s %s' onchange='javascript:gformValidateFileSize( this, %s );' {$tabindex} %s/>", $id, $field_id, esc_attr( $class ), $validation_message_id, $live_validation_message_id, $extensions_message_id, esc_attr( $max_upload_size ), $disabled_text );

			if ( ! $is_admin ) {
				$upload .= "<span id='{$extensions_message_id}' class='screen-reader-text'>{$extensions_message}</span>";
				$upload .= "<div class='validation_message' id='{$live_validation_message_id}'></div>";
			}
		}

		if ( $is_entry_detail && ! empty( $value ) ) { // edit entry
			$file_urls      = $multiple_files ? json_decode( $value ) : array( $value );
			$upload_display = $multiple_files ? '' : "style='display:none'";
			$preview        = "<div id='upload_$id' {$upload_display}>$upload</div>";
			$preview .= sprintf( "<div id='%s'></div>", $file_list_id );
			$preview .= sprintf( "<div id='preview_existing_files_%d'>", $id );

			foreach ( $file_urls as $file_index => $file_url ) {

				/**
				 * Allow for override of SSL replacement.
				 *
				 * By default Gravity Forms will attempt to determine if the schema of the URL should be overwritten for SSL.
				 * This is not ideal for all situations, particularly domain mapping. Setting $field_ssl to false will prevent
				 * the override.
				 *
				 * @since 2.1.1.23
				 *
				 * @param bool                $field_ssl True to allow override if needed or false if not.
				 * @param string              $file_url  The file URL in question.
				 * @param GF_Field_FileUpload $field     The field object for further context.
				 */
				$field_ssl = apply_filters( 'gform_secure_file_download_is_https', true, $file_url, $this );

				if ( GFCommon::is_ssl() && strpos( $file_url, 'http:' ) !== false && $field_ssl === true ) {
					$file_url = str_replace( 'http:', 'https:', $file_url );
				}
				$download_file_text  = esc_attr__( 'Download file', 'gravityforms' );
				$delete_file_text    = esc_attr__( 'Delete file', 'gravityforms' );
				$view_file_text      = esc_attr__( 'View file', 'gravityforms' );
				$file_index          = intval( $file_index );
				$file_url            = esc_attr( $file_url );
				$display_file_url    = GFCommon::truncate_url( $file_url );
				$file_url            = $this->get_download_url( $file_url );
				$download_button_url = GFCommon::get_base_url() . '/images/download.png';
				$delete_button_url   = GFCommon::get_base_url() . '/images/delete.png';
				$preview .= "<div id='preview_file_{$file_index}' class='ginput_preview'>
								<a href='{$file_url}' target='_blank' aria-label='{$view_file_text}'>{$display_file_url}</a>
								<a href='{$file_url}' target='_blank' aria-label='{$download_file_text}'>
								<img src='{$download_button_url}' alt='' style='margin-left:10px;'/></a><a href='javascript:void(0);' aria-label='{$delete_file_text}' onclick='DeleteFile({$lead_id},{$id},this);' onkeypress='DeleteFile({$lead_id},{$id},this);' ><img src='{$delete_button_url}' alt='' style='margin-left:10px;'/></a>
							</div>";
			}

			$preview .= '</div>';

			return $preview;
		} else {
			$input_name     = "input_{$id}";
			$uploaded_files = isset( GFFormsModel::$uploaded_files[ $form_id ][ $input_name ] ) ? GFFormsModel::$uploaded_files[ $form_id ][ $input_name ] : array();
			$file_infos     = $multiple_files ? $uploaded_files : RGFormsModel::get_temp_filename( $form_id, $input_name );

			if ( ! empty( $file_infos ) ) {
				$preview    = sprintf( "<div id='%s'>", $file_list_id );
				$file_infos = $multiple_files ? $uploaded_files : array( $file_infos );
				foreach ( $file_infos as $file_info ) {
					$file_upload_markup = apply_filters( 'gform_file_upload_markup', "<img alt='" . esc_attr__( 'Delete file', 'gravityforms' ) . "' class='gform_delete' src='" . GFCommon::get_base_url() . "/images/delete.png' onclick='gformDeleteUploadedFile({$form_id}, {$id}, this);' onkeypress='gformDeleteUploadedFile({$form_id}, {$id}, this);' /> <strong>" . esc_html( $file_info['uploaded_filename'] ) . '</strong>', $file_info, $form_id, $id );
					$preview .= "<div class='ginput_preview'>{$file_upload_markup}</div>";
				}
				$preview .= '</div>';
				if ( ! $multiple_files ) {
					$upload = str_replace( " class='", " class='gform_hidden ", $upload );
				}

				return "<div class='ginput_container ginput_container_fileupload'>" . $upload . " {$preview}</div>";
			} else {

				$preview = $multiple_files ? sprintf( "<div id='%s'></div>", $file_list_id ) : '';

				return "<div class='ginput_container ginput_container_fileupload'>$upload</div>" . $preview;
			}
		}
	}

	public function is_value_submission_empty( $form_id ) {
		$input_name = 'input_' . $this->id;

		if ( $this->multipleFiles ) {
			$uploaded_files = GFFormsModel::$uploaded_files[ $form_id ];
			$file_info      = rgar( $uploaded_files, $input_name );

			return empty( $file_info );
		} else {
			$file_info = GFFormsModel::get_temp_filename( $form_id, $input_name );

			return ! $file_info && empty( $_FILES[ $input_name ]['name'] );
		}
	}

	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {
		if ( ! $this->multipleFiles ) {
			return $this->get_single_file_value( $form['id'], $input_name );
		}

		if ( $this->is_entry_detail() && empty( $lead ) ) {
			// Deleted files remain in the $value from $_POST so use the updated entry value.
			$lead  = GFFormsModel::get_lead( $lead_id );
			$value = rgar( $lead, strval( $this->id ) );
		}

		return $this->get_multifile_value( $form['id'], $input_name, $value );
	}

	public function get_multifile_value( $form_id, $input_name, $value ) {
		global $_gf_uploaded_files;

		GFCommon::log_debug( __METHOD__ . '(): Starting.' );

		if ( isset( $_gf_uploaded_files[ $input_name ] ) ) {
			$value = $_gf_uploaded_files[ $input_name ];
		} else {
			if ( isset( GFFormsModel::$uploaded_files[ $form_id ][ $input_name ] ) ) {
				$uploaded_temp_files = GFFormsModel::$uploaded_files[ $form_id ][ $input_name ];
				$uploaded_files      = array();
				foreach ( $uploaded_temp_files as $i => $file_info ) {
					$temp_filepath = GFFormsModel::get_upload_path( $form_id ) . '/tmp/' . wp_basename( $file_info['temp_filename'] );
					if ( $file_info && file_exists( $temp_filepath ) ) {
						$uploaded_files[ $i ] = $this->move_temp_file( $form_id, $file_info );
					}
				}

				if ( ! empty( $value ) ) { // merge with existing files (admin edit entry)
					$value = json_decode( $value, true );
					$value = array_merge( $value, $uploaded_files );
					$value = json_encode( $value );
				} else {
					$value = json_encode( $uploaded_files );
				}
			} else {
				GFCommon::log_debug( __METHOD__ . '(): No files uploaded. Exiting.' );

				$value = '';
			}
			$_gf_uploaded_files[ $input_name ] = $value;
		}

		$value_safe = $this->sanitize_entry_value( $value, $form_id );

		return $value_safe;
	}

	public function get_single_file_value( $form_id, $input_name ) {
		global $_gf_uploaded_files;

		GFCommon::log_debug( __METHOD__ . '(): Starting.' );

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

		if ( ! isset( $_gf_uploaded_files[ $input_name ] ) ) {

			//check if file has already been uploaded by previous step
			$file_info     = GFFormsModel::get_temp_filename( $form_id, $input_name );
			$temp_filename = rgar( $file_info, 'temp_filename', '' );
			$temp_filepath = GFFormsModel::get_upload_path( $form_id ) . '/tmp/' . $temp_filename;

			if ( $file_info && file_exists( $temp_filepath ) ) {
				GFCommon::log_debug( __METHOD__ . '(): File already uploaded to tmp folder, moving.' );
				$_gf_uploaded_files[ $input_name ] = $this->move_temp_file( $form_id, $file_info );
			} else if ( ! empty( $_FILES[ $input_name ]['name'] ) ) {
				GFCommon::log_debug( __METHOD__ . '(): calling upload_file' );
				$_gf_uploaded_files[ $input_name ] = $this->upload_file( $form_id, $_FILES[ $input_name ] );
			} else {
				GFCommon::log_debug( __METHOD__ . '(): No file uploaded. Exiting.' );
			}
		}

		return rgget( $input_name, $_gf_uploaded_files );
	}

	public function upload_file( $form_id, $file ) {
		GFCommon::log_debug( __METHOD__ . '(): Uploading file: ' . $file['name'] );

		$target = GFFormsModel::get_file_upload_path( $form_id, $file['name'] );
		if ( ! $target ) {
			GFCommon::log_debug( __METHOD__ . '(): FAILED (Upload folder could not be created.)' );

			return 'FAILED (Upload folder could not be created.)';
		}
		GFCommon::log_debug( __METHOD__ . '(): Upload folder is ' . print_r( $target, true ) );

		if ( move_uploaded_file( $file['tmp_name'], $target['path'] ) ) {
			GFCommon::log_debug( __METHOD__ . '(): File ' . $file['tmp_name'] . ' successfully moved to ' . $target['path'] . '.' );
			$this->set_permissions( $target['path'] );

			return $target['url'];
		} else {
			GFCommon::log_debug( __METHOD__ . '(): FAILED (Temporary file ' . $file['tmp_name'] . ' could not be copied to ' . $target['path'] . '.)' );

			return 'FAILED (Temporary file could not be copied.)';
		}
	}

	public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) {
		if ( $this->multipleFiles ) {
			$uploaded_files_arr = empty( $value ) ? array() : json_decode( $value, true );
			$file_count         = count( $uploaded_files_arr );
			if ( $file_count > 1 ) {
				$value = empty( $uploaded_files_arr ) ? '' : sprintf( esc_html__( '%d files', 'gravityforms' ), count( $uploaded_files_arr ) );
				return $value;
			} elseif ( $file_count == 1 ) {
				$value = current( $uploaded_files_arr );
			} elseif ( $file_count == 0 ) {
				return;
			}
		}

		$file_path = $value;
		if ( ! empty( $file_path ) ) {
			//displaying thumbnail (if file is an image) or an icon based on the extension
			$thumb     = GFEntryList::get_icon_url( $file_path );
			$file_path = $this->get_download_url( $file_path );
			$file_path = esc_attr( $file_path );
			$value     = "<a href='$file_path' target='_blank' aria-label='" . esc_attr__( 'View the image', 'gravityforms' ) . "'><img src='$thumb' alt='' /></a>";
		}
		return $value;
	}

	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
		$output = '';
		if ( ! empty( $value ) ) {
			$output_arr     = array();
			$file_paths     = $this->multipleFiles ? json_decode( $value ) : array( $value );
			$force_download = in_array( 'download', $this->get_modifiers() );

			if ( is_array( $file_paths ) ) {
				foreach ( $file_paths as $file_path ) {
					$basename = wp_basename( $file_path );
					$file_path = $this->get_download_url( $file_path, $force_download );

					/**
					 * Allow for override of SSL replacement
					 *
					 * By default Gravity Forms will attempt to determine if the schema of the URL should be overwritten for SSL.
					 * This is not ideal for all situations, particularly domain mapping. Setting $field_ssl to false will prevent
					 * the override.
					 *
					 * @since 2.1.1.23
					 *
					 * @param bool                $field_ssl True to allow override if needed or false if not.
					 * @param string              $file_path The file path of the download file.
					 * @param GF_Field_FileUpload $field     The field object for further context.
					 */
					$field_ssl = apply_filters( 'gform_secure_file_download_is_https', true, $file_path, $this );

					if ( GFCommon::is_ssl() && strpos( $file_path, 'http:' ) !== false && $field_ssl === true ) {
						$file_path = str_replace( 'http:', 'https:', $file_path );
					}

					/**
					 * Allows for the filtering of the file path before output.
					 *
					 * @since 2.1.1.23
					 *
					 * @param string              $file_path The file path of the download file.
					 * @param GF_Field_FileUpload $field     The field object for further context.
					 */
					$file_path    = str_replace( ' ', '%20', apply_filters( 'gform_fileupload_entry_value_file_path', $file_path, $this ) );
					$output_arr[] = $format == 'text' ? $file_path : sprintf( "<li><a href='%s' target='_blank' aria-label='%s'>%s</a></li>", esc_attr( $file_path ), esc_attr__( 'Click to view', 'gravityforms' ), $basename );

				}
				$output = join( PHP_EOL, $output_arr );
			}
		}
		$output = empty( $output ) || $format == 'text' ? $output : sprintf( '<ul>%s</ul>', $output );

		return $output;
	}

	/**
	 * Gets merge tag values.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GF_Field::get_modifiers()
	 * @uses GF_Field_FileUpload::get_download_url()
	 *
	 * @param array|string $value      The value of the input.
	 * @param string       $input_id   The input ID to use.
	 * @param array        $entry      The Entry Object.
	 * @param array        $form       The Form Object
	 * @param string       $modifier   The modifier passed.
	 * @param array|string $raw_value  The raw value of the input.
	 * @param bool         $url_encode If the result should be URL encoded.
	 * @param bool         $esc_html   If the HTML should be escaped.
	 * @param string       $format     The format that the value should be.
	 * @param bool         $nl2br      If the nl2br function should be used.
	 *
	 * @return string The processed merge tag.
	 */
	public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) {

		$force_download = in_array( 'download', $this->get_modifiers() );

		if ( $this->multipleFiles ) {

			$files = empty( $raw_value ) ? array() : json_decode( $raw_value, true );
			foreach ( $files as &$file ) {
				$file = $this->get_download_url( $file, $force_download );
				$file = str_replace( ' ', '%20', $file );
				if ( $esc_html ) {
					$value = esc_html( $value );
				}
			}
			$value = $format == 'html' ? join( '<br />', $files ) : join( ', ', $files );

		} else {
			$value = $this->get_download_url( $value, $force_download );
			$value = str_replace( ' ', '%20', $value );
		}

		if ( $url_encode ) {
			$value = urlencode( $value );
		}

		return $value;
	}


	public function move_temp_file( $form_id, $tempfile_info ) {

		$target = GFFormsModel::get_file_upload_path( $form_id, $tempfile_info['uploaded_filename'] );
		$source = GFFormsModel::get_upload_path( $form_id ) . '/tmp/' . wp_basename( $tempfile_info['temp_filename'] );

		GFCommon::log_debug( __METHOD__ . '(): Moving temp file from: ' . $source );

		if ( rename( $source, $target['path'] ) ) {
			GFCommon::log_debug( __METHOD__ . '(): File successfully moved.' );
			$this->set_permissions( $target['path'] );

			return $target['url'];
		} else {
			GFCommon::log_debug( __METHOD__ . '(): FAILED (Temporary file could not be moved.)' );

			return 'FAILED (Temporary file could not be moved.)';
		}
	}

	function set_permissions( $path ) {
		GFCommon::log_debug( __METHOD__ . '(): Setting permissions on: ' . $path );

		GFFormsModel::set_permissions( $path );
	}

	public function sanitize_settings() {
		parent::sanitize_settings();
		if ( $this->maxFileSize ) {
			$this->maxFileSize = absint( $this->maxFileSize );
		}

		if ( $this->maxFiles ) {
			$this->maxFiles = preg_replace( '/[^0-9,.]/', '', $this->maxFiles );
		}

		$this->multipleFiles = (bool) $this->multipleFiles;

		$this->allowedExtensions = sanitize_text_field( $this->allowedExtensions );
	}

	public function get_value_export( $entry, $input_id = '', $use_text = false, $is_csv = false ) {
		if ( empty( $input_id ) ) {
			$input_id = $this->id;
		}

		$value = rgar( $entry, $input_id );
		if ( $this->multipleFiles && ! empty( $value ) ) {
			return implode( ' , ', json_decode( $value, true ) );
		}

		return $value;
	}

	/**
	 * Returns the download URL for a file. The URL is not escaped for output.
	 *
	 * @since  2.0
	 * @access public
	 *
	 * @param string $file           The complete file URL.
	 * @param bool   $force_download If the download should be forced. Defaults to false.
	 *
	 * @return string
	 */
	public function get_download_url( $file, $force_download = false ) {
		$download_url = $file;

		$secure_download_location = true;

		/**
		 * By default the real location of the uploaded file will be hidden and the download URL will be generated with
		 * a security token to prevent guessing or enumeration attacks to discover the location of other files.
		 *
		 * Return FALSE to display the real location.
		 *
		 * @param bool                $secure_download_location If the secure location should be used.  Defaults to true.
		 * @param string              $file                     The URL of the file.
		 * @param GF_Field_FileUpload $this                     The Field
		 */
		$secure_download_location = apply_filters( 'gform_secure_file_download_location', $secure_download_location, $file, $this );
		$secure_download_location = apply_filters( 'gform_secure_file_download_location_' . $this->formId, $secure_download_location, $file, $this );

		if ( ! $secure_download_location ) {

			/**
			 * Allow filtering of the download URL.
			 *
			 * Allows for manual filtering of the download URL to handle conditions such as
			 * unusual domain mapping and others.
			 *
			 * @since 2.1.1.1
			 *
			 * @param string              $download_url The URL from which to download the file.
			 * @param GF_Field_FileUpload $field        The field object for further context.
			 */
			return apply_filters( 'gform_secure_file_download_url', $download_url, $this );

		}

		$upload_root = GFFormsModel::get_upload_url( $this->formId );
		$upload_root = trailingslashit( $upload_root );

		// Only hide the real URL if the location of the file is in the upload root for the form.
		// The upload root is calculated using the WP Salts so if the WP Salts have changed then file can't be located during the download request.
		if ( strpos( $file, $upload_root ) !== false ) {
			$file = str_replace( $upload_root, '', $file );
			$download_url = site_url( 'index.php' );
			$args = array(
				'gf-download' => urlencode( $file ),
				'form-id' => $this->formId,
				'field-id' => $this->id,
				'hash' => GFCommon::generate_download_hash( $this->formId, $this->id, $file ),
			);
			if ( $force_download ) {
				$args['dl'] = 1;
			}
			$download_url = add_query_arg( $args, $download_url );
		}

		/**
		 * Allow filtering of the download URL.
		 *
		 * Allows for manual filtering of the download URL to handle conditions such as
		 * unusual domain mapping and others.
		 *
		 * @param string              $download_url The URL from which to download the file.
		 * @param GF_Field_FileUpload $field        The field object for further context.
		 */
		return apply_filters( 'gform_secure_file_download_url', $download_url, $this );
	}
}

GF_Fields::register( new GF_Field_FileUpload() );
class-gf-field-date.php000066600000106553151264036140010770 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_Date extends GF_Field {

	public $type = 'date';

	public function get_form_editor_field_title() {
		return esc_attr__( 'Date', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'sub_label_placement_setting',
			'admin_label_setting',
			'rules_setting',
			'date_input_type_setting',
			'visibility_setting',
			'duplicate_setting',
			'date_format_setting',
			'default_value_setting',
			'placeholder_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	/**
	 * Whether this field expects an array during submission.
	 *
	 * @since 2.4
	 *
	 * @return bool
	 */
	public function is_value_submission_array() {
		return in_array( $this->dateType, array( 'datefield', 'datedropdown' ) );
	}

	public function validate( $value, $form ) {
		if ( is_array( $value ) && rgempty( 0, $value ) && rgempty( 1, $value ) && rgempty( 2, $value ) ) {
			$value = null;
		}

		if ( ! empty( $value ) ) {
			$format = empty( $this->dateFormat ) ? 'mdy' : $this->dateFormat;
			$date   = GFCommon::parse_date( $value, $format );

			if ( empty( $date ) || ! $this->checkdate( $date['month'], $date['day'], $date['year'] ) ) {
				$this->failed_validation = true;
				$format_name             = '';
				switch ( $format ) {
					case 'mdy' :
						$format_name = 'mm/dd/yyyy';
						break;
					case 'dmy' :
						$format_name = 'dd/mm/yyyy';
						break;
					case 'dmy_dash' :
						$format_name = 'dd-mm-yyyy';
						break;
					case 'dmy_dot' :
						$format_name = 'dd.mm.yyyy';
						break;
					case 'ymd_slash' :
						$format_name = 'yyyy/mm/dd';
						break;
					case 'ymd_dash' :
						$format_name = 'yyyy-mm-dd';
						break;
					case 'ymd_dot' :
						$format_name = 'yyyy.mm.dd';
						break;
				}
				$message                  = $this->dateType == 'datepicker' ? sprintf( esc_html__( 'Please enter a valid date in the format (%s).', 'gravityforms' ), $format_name ) : esc_html__( 'Please enter a valid date.', 'gravityforms' );
				$this->validation_message = empty( $this->errorMessage ) ? $message : $this->errorMessage;
			}
		}
	}

	public function is_value_submission_empty( $form_id ) {
		$value = rgpost( 'input_' . $this->id );
		if ( is_array( $value ) ) {
			// Date field and date drop-downs
			foreach ( $value as $input ) {
				if ( strlen( trim( $input ) ) <= 0 ) {
					return true;
				}
			}

			return false;
		} else {

			// Date picker
			return strlen( trim( $value ) ) <= 0;
		}
	}

	public function get_field_input( $form, $value = '', $entry = null ) {

		$picker_value = '';
		if ( is_array( $value ) ) {
			// GFCommon::parse_date() takes a numeric array.
			$value = array_values( $value );
		} else {
			$picker_value = esc_attr( $value );
		}
		$format    = empty( $this->dateFormat ) ? 'mdy' : esc_attr( $this->dateFormat );
		$date_info = GFCommon::parse_date( $value, $format );

		$day_value   = esc_attr( rgget( 'day', $date_info ) );
		$month_value = esc_attr( rgget( 'month', $date_info ) );
		$year_value  = esc_attr( rgget( 'year', $date_info ) );

		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$form_id  = $form['id'];
		$id       = intval( $this->id );
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		$size          = $this->size;
		$disabled_text = $is_form_editor ? "disabled='disabled'" : '';
		$class_suffix  = $is_entry_detail ? '_admin' : '';
		$class         = $size . $class_suffix;
		$class         = esc_attr( $class );

		$form_sub_label_placement  = rgar( $form, 'subLabelPlacement' );
		$field_sub_label_placement = $this->subLabelPlacement;
		$is_sub_label_above        = $field_sub_label_placement == 'above' || ( empty( $field_sub_label_placement ) && $form_sub_label_placement == 'above' );
		$sub_label_class_attribute = $field_sub_label_placement == 'hidden_label' ? "class='hidden_sub_label screen-reader-text'" : '';

		$month_input = GFFormsModel::get_input( $this, $this->id . '.1' );
		$day_input   = GFFormsModel::get_input( $this, $this->id . '.2' );
		$year_input  = GFFormsModel::get_input( $this, $this->id . '.3' );

		$month_sub_label = rgar( $month_input, 'customLabel' ) != '' ? $month_input['customLabel'] : esc_html( _x( 'MM', 'Abbreviation: Month', 'gravityforms' ) );
		$day_sub_label   = rgar( $day_input, 'customLabel' ) != '' ? $day_input['customLabel'] : esc_html__( 'DD', 'gravityforms' );
		$year_sub_label  = rgar( $year_input, 'customLabel' ) != '' ? $year_input['customLabel'] : esc_html__( 'YYYY', 'gravityforms' );

		$month_placeholder_attribute = GFCommon::get_input_placeholder_attribute( $month_input );
		$day_placeholder_attribute   = GFCommon::get_input_placeholder_attribute( $day_input );
		$year_placeholder_attribute  = GFCommon::get_input_placeholder_attribute( $year_input );

		$month_placeholder_value = GFCommon::get_input_placeholder_value( $month_input );
		$day_placeholder_value   = GFCommon::get_input_placeholder_value( $day_input );
		$year_placeholder_value  = GFCommon::get_input_placeholder_value( $year_input );

		$date_picker_placeholder = $this->get_field_placeholder_attribute();

		$is_html5               = RGFormsModel::is_html5_enabled();
		$date_input_type        = $is_html5 ? 'number' : 'text';

		$month_html5_attributes = $is_html5 ? "min='1' max='12' step='1'" : '';
		$day_html5_attributes   = $is_html5 ? "min='1' max='31' step='1'" : '';

		$year_min = apply_filters( 'gform_date_min_year', '1920', $form, $this );
		$year_max = apply_filters( 'gform_date_max_year', date( 'Y' ) + 1, $form, $this );

		$year_min_attribute  = $is_html5 && is_numeric( $year_min ) ? "min='{$year_min}'" : '';
		$year_max_attribute  = $is_html5 && is_numeric( $year_max ) ? "max='{$year_max}'" : '';
		$year_step_attribute = $is_html5 ? "step='1'" : '';

		$field_position = substr( $format, 0, 3 );
		if ( $is_form_editor ) {
			$datepicker_display = in_array( $this->dateType, array( 'datefield', 'datedropdown' ) ) ? 'none' : 'inline';
			$datefield_display  = $this->dateType == 'datefield' ? 'inline' : 'none';
			$dropdown_display   = $this->dateType == 'datedropdown' ? 'inline' : 'none';
			$icon_display       = $this->calendarIconType == 'calendar' ? 'inline' : 'none';

			if ( $is_sub_label_above ) {
				$month_field = "<div class='gfield_date_month ginput_date' id='gfield_input_date_month' style='display:$datefield_display'>
                                    <label for='{$field_id}_1' {$sub_label_class_attribute}>{$month_sub_label}</label>
                                    <input id='{$field_id}_1' name='ginput_month' type='text' {$month_placeholder_attribute} {$disabled_text} value='{$month_value}'/>
                                </div>";
				$day_field   = "<div class='gfield_date_day ginput_date' id='gfield_input_date_day' style='display:$datefield_display'>
                                    <label for='{$field_id}_2' {$sub_label_class_attribute}>{$day_sub_label}</label>
                                    <input id='{$field_id}_2' name='ginput_day' type='text' {$day_placeholder_attribute} {$disabled_text} value='{$day_value}'/>
                               </div>";
				$year_field  = "<div class='gfield_date_year ginput_date' id='gfield_input_date_year' style='display:$datefield_display'>
                                    <label for='{$field_id}_3' {$sub_label_class_attribute}>{$year_sub_label}</label>
                                    <input id='{$field_id}_3' type='text' name='text' {$year_placeholder_attribute} {$disabled_text} value='{$year_value}'/>
                               </div>";
			} else {
				$month_field = "<div class='gfield_date_month ginput_date' id='gfield_input_date_month' style='display:$datefield_display'>
                                    <input id='{$field_id}_1' name='ginput_month' type='text' {$month_placeholder_attribute} {$disabled_text} value='{$month_value}'/>
                                    <label for='{$field_id}_1' {$sub_label_class_attribute}>{$month_sub_label}</label>
                                </div>";
				$day_field   = "<div class='gfield_date_day ginput_date' id='gfield_input_date_day' style='display:$datefield_display'>
                                    <input id='{$field_id}_2' name='ginput_day' type='text' {$day_placeholder_attribute} {$disabled_text} value='{$day_value}'/>
                                    <label for='{$field_id}_2' {$sub_label_class_attribute}>{$day_sub_label}</label>
                              </div>";
				$year_field  = "<div class='gfield_date_year ginput_date' id='gfield_input_date_year' style='display:$datefield_display'>
                                    <input type='text' id='{$field_id}_3' name='ginput_year' {$year_placeholder_attribute} {$disabled_text} value='{$year_value}'/>
                                    <label for='{$field_id}_3' {$sub_label_class_attribute}>{$year_sub_label}</label>
                               </div>";
			}

			$month_dropdown = "<div class='gfield_date_dropdown_month ginput_date_dropdown' id='gfield_dropdown_date_month' style='display:$dropdown_display'>" . $this->get_month_dropdown( '', "{$field_id}_1", rgar( $date_info, 'month' ), '', $disabled_text, $month_placeholder_value ) . '</div>';
			$day_dropdown   = "<div class='gfield_date_dropdown_day ginput_date_dropdown' id='gfield_dropdown_date_day' style='display:$dropdown_display'>" . $this->get_day_dropdown( '', "{$field_id}_2", rgar( $date_info, 'day' ), '', $disabled_text, $day_placeholder_value ) . '</div>';
			$year_dropdown  = "<div class='gfield_date_dropdown_year ginput_date_dropdown' id='gfield_dropdown_date_year' style='display:$dropdown_display'>" . $this->get_year_dropdown( '', "{$field_id}_3", rgar( $date_info, 'year' ), '', $disabled_text, $year_placeholder_value, $form ) . '</div>';

			$field_string = "<div class='ginput_container ginput_container_date' id='gfield_input_datepicker' style='display:$datepicker_display'><input name='ginput_datepicker' type='text' {$date_picker_placeholder} {$disabled_text} value='{$picker_value}'/><img src='" . GFCommon::get_base_url() . "/images/calendar.png' id='gfield_input_datepicker_icon' style='display:$icon_display'/></div>";

			switch ( $field_position ) {
				case 'dmy' :
					$date_inputs = $day_field . $month_field . $year_field . $day_dropdown . $month_dropdown . $year_dropdown;
					break;

				case 'ymd' :
					$date_inputs = $year_field . $month_field . $day_field . $year_dropdown . $month_dropdown . $day_dropdown;
					break;

				default :
					$date_inputs = $month_field . $day_field . $year_field . $month_dropdown . $day_dropdown . $year_dropdown;
					break;
			}

			$field_string .= "<div id='{$field_id}' class='ginput_container ginput_container_date'>{$date_inputs}</div>";

			return $field_string;
		} else {

			$date_type = $this->dateType;
			if ( in_array( $date_type, array( 'datefield', 'datedropdown' ) ) ) {

				switch ( $field_position ) {

					case 'dmy' :

						$tabindex = $this->get_tabindex();

						if ( $date_type == 'datedropdown' ) {

							$field_str = "<div class='clear-multi'><div class='gfield_date_dropdown_day ginput_container ginput_container_date' id='{$field_id}_2_container'>" . $this->get_day_dropdown( "input_{$id}[]", "{$field_id}_2", rgar( $date_info, 'day' ), $tabindex, $disabled_text, $day_placeholder_value ) . '</div>';

							$tabindex = $this->get_tabindex();

							$field_str .= "<div class='gfield_date_dropdown_month ginput_container ginput_container_date' id='{$field_id}_1_container'>" . $this->get_month_dropdown( "input_{$id}[]", "{$field_id}_1", rgar( $date_info, 'month' ), $tabindex, $disabled_text, $month_placeholder_value ) . '</div>';

							$tabindex = $this->get_tabindex();

							$field_str .= "<div class='gfield_date_dropdown_year ginput_container ginput_container_date' id='{$field_id}_3_container'>" . $this->get_year_dropdown( "input_{$id}[]", "{$field_id}_3", rgar( $date_info, 'year' ), $tabindex, $disabled_text, $year_placeholder_value, $form ) . '</div></div>';
						} else {

							$field_str = $is_sub_label_above
								? "<div class='clear-multi'>
                                        <div class='gfield_date_day ginput_container ginput_container_date' id='{$field_id}_2_container'>
                                            <label for='{$field_id}_2' {$sub_label_class_attribute}>{$day_sub_label}</label>
                                            <input type='{$date_input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_2' value='$day_value' {$tabindex} {$disabled_text} {$day_placeholder_attribute} {$day_html5_attributes}/>
                                        </div>"
								: "<div class='clear-multi'>
                                        <div class='gfield_date_day ginput_container ginput_container_date' id='{$field_id}_2_container'>
                                            <input type='{$date_input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_2' value='$day_value' {$tabindex} {$disabled_text} {$day_placeholder_attribute} {$day_html5_attributes}/>
                                            <label for='{$field_id}_2' {$sub_label_class_attribute}>{$day_sub_label}</label>
                                        </div>";

							$tabindex = $this->get_tabindex();

							$field_str .= $is_sub_label_above
								? "<div class='gfield_date_month ginput_container ginput_container_date' id='{$field_id}_1_container'>
                                        <label for='{$field_id}_1' {$sub_label_class_attribute}>{$month_sub_label}</label>
                                        <input type='{$date_input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_1' value='{$month_value}' {$tabindex} {$disabled_text} {$month_placeholder_attribute} {$month_html5_attributes}/>
                                   </div>"
								: "<div class='gfield_date_month ginput_container ginput_container_date' id='{$field_id}_1_container'>
                                        <input type='{$date_input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_1' value='{$month_value}' {$tabindex} {$disabled_text} {$month_placeholder_attribute} {$month_html5_attributes}/>
                                        <label for='{$field_id}_1' {$sub_label_class_attribute}>{$month_sub_label}</label>
                                   </div>";

							$tabindex = $this->get_tabindex();

							$field_str .= $is_sub_label_above
								? "<div class='gfield_date_year ginput_container ginput_container_date' id='{$field_id}_3_container'>
                                            <label for='{$field_id}_3' {$sub_label_class_attribute}>{$year_sub_label}</label>
                                            <input type='{$date_input_type}' maxlength='4' name='input_{$id}[]' id='{$field_id}_3' value='{$year_value}' {$tabindex} {$disabled_text} {$year_placeholder_attribute} {$year_min_attribute} {$year_max_attribute} {$year_step_attribute}/>
                                       </div>
                                    </div>"
								: "<div class='gfield_date_year ginput_container ginput_container_date' id='{$field_id}_3_container'>
                                        <input type='{$date_input_type}' maxlength='4' name='input_{$id}[]' id='{$field_id}_3' value='{$year_value}' {$tabindex} {$disabled_text} {$year_placeholder_attribute} {$year_min_attribute} {$year_max_attribute} {$year_step_attribute}/>
                                        <label for='{$field_id}_3' {$sub_label_class_attribute}>{$year_sub_label}</label>
                                   </div>
                                </div>";

						}

						break;

					case 'ymd' :

						$tabindex = $this->get_tabindex();

						if ( $date_type == 'datedropdown' ) {

							$field_str = "<div class='clear-multi'><div class='gfield_date_dropdown_year ginput_container ginput_container_date' id='{$field_id}_3_container'>" . $this->get_year_dropdown( "input_{$id}[]", "{$field_id}_3", rgar( $date_info, 'year' ), $tabindex, $disabled_text, $year_placeholder_value, $form ) . '</div>';

							$tabindex = $this->get_tabindex();

							$field_str .= "<div class='gfield_date_dropdown_month ginput_container ginput_container_date' id='{$field_id}_1_container'>" . $this->get_month_dropdown( "input_{$id}[]", "{$field_id}_1", rgar( $date_info, 'month' ), $tabindex, $disabled_text, $month_placeholder_value ) . '</div>';

							$tabindex = $this->get_tabindex();

							$field_str .= "<div class='gfield_date_dropdown_day ginput_container ginput_container_date' id='{$field_id}_2_container'>" . $this->get_day_dropdown( "input_{$id}[]", "{$field_id}_2", rgar( $date_info, 'day' ), $tabindex, $disabled_text, $day_placeholder_value ) . '</div></div>';
						} else {

							$field_str = $is_sub_label_above
								? "<div class='clear-multi'>
                                            <div class='gfield_date_year ginput_container ginput_container_date' id='{$field_id}_3_container'>
                                                <label for='{$field_id}_3' {$sub_label_class_attribute}>{$year_sub_label}</label>
                                                <input type='{$date_input_type}' maxlength='4' name='input_{$id}[]' id='{$field_id}_3' value='{$year_value}' {$tabindex} {$disabled_text} {$year_placeholder_attribute} {$year_min_attribute} {$year_max_attribute} {$year_step_attribute}/>
                                            </div>"
								: "<div class='clear-multi'>
                                            <div class='gfield_date_year ginput_container ginput_container_date' id='{$field_id}_3_container'>
                                                <input type='{$date_input_type}' maxlength='4' name='input_{$id}[]' id='{$field_id}_3' value='{$year_value}' {$tabindex} {$disabled_text} {$year_placeholder_attribute} {$year_min_attribute} {$year_max_attribute} {$year_step_attribute}/>
                                                <label for='{$field_id}_3' {$sub_label_class_attribute}>{$year_sub_label}</label>
                                            </div>";

							$tabindex = $this->get_tabindex();

							$field_str .= $is_sub_label_above
								? "<div class='gfield_date_month ginput_container ginput_container_date' id='{$field_id}_1_container'>
                                                <label for='{$field_id}_1' {$sub_label_class_attribute}>{$month_sub_label}</label>
                                                <input type='{$date_input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_1' value='{$month_value}' {$tabindex} {$disabled_text} {$month_placeholder_attribute} {$month_html5_attributes}/>
                                            </div>"
								: "<div class='gfield_date_month ginput_container ginput_container_date' id='{$field_id}_1_container'>
                                                <input type='{$date_input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_1' value='{$month_value}' {$tabindex} {$disabled_text} {$month_placeholder_attribute} {$month_html5_attributes}/>
                                                <label for='{$field_id}_1' {$sub_label_class_attribute}>{$month_sub_label}</label>
                                            </div>";

							$tabindex = $this->get_tabindex();

							$field_str .= $is_sub_label_above
								? "<div class='gfield_date_day ginput_container ginput_container_date' id='{$field_id}_2_container'>
                                                <label for='{$field_id}_2' {$sub_label_class_attribute}>{$day_sub_label}</label>
                                                <input type='{$date_input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_2' value='{$day_value}' {$tabindex} {$disabled_text} {$day_placeholder_attribute} {$day_html5_attributes}/>
                                           </div>
                                        </div>"
								: "<div class='gfield_date_day ginput_container ginput_container_date' id='{$field_id}_2_container'>
                                                <input type='{$date_input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_2' value='{$day_value}' {$tabindex} {$disabled_text} {$day_placeholder_attribute} {$day_html5_attributes}/>
                                                <label for='{$field_id}_2' {$sub_label_class_attribute}>{$day_sub_label}</label>
                                           </div>
                                        </div>";
						}

						break;

					default :
						$tabindex = $this->get_tabindex();

						if ( $date_type == 'datedropdown' ) {

							$field_str = "<div class='clear-multi'><div class='gfield_date_dropdown_month ginput_container ginput_container_date' id='{$field_id}_1_container'>" . $this->get_month_dropdown( "input_{$id}[]", "{$field_id}_1", rgar( $date_info, 'month' ), $tabindex, $disabled_text, $month_placeholder_value ) . '</div>';

							$tabindex = $this->get_tabindex();

							$field_str .= "<div class='gfield_date_dropdown_day ginput_container ginput_container_date' id='{$field_id}_2_container'>" . $this->get_day_dropdown( "input_{$id}[]", "{$field_id}_2", rgar( $date_info, 'day' ), $tabindex, $disabled_text, $day_placeholder_value ) . '</div>';

							$tabindex = $this->get_tabindex();

							$field_str .= "<div class='gfield_date_dropdown_year ginput_container ginput_container_date' id='{$field_id}_3_container'>" . $this->get_year_dropdown( "input_{$id}[]", "{$field_id}_3", rgar( $date_info, 'year' ), $tabindex, $disabled_text, $year_placeholder_value, $form ) . '</div></div>';
						} else {

							$field_str = $is_sub_label_above
								? "<div class='clear-multi'><div class='gfield_date_month ginput_container ginput_container_date' id='{$field_id}_1_container'>
                                            <label for='{$field_id}_1' {$sub_label_class_attribute}>{$month_sub_label}</label>
                                            <input type='{$date_input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_1' value='{$month_value}' {$tabindex} {$disabled_text} {$month_placeholder_attribute} {$month_html5_attributes}/>
                                        </div>"
								: "<div class='clear-multi'><div class='gfield_date_month ginput_container ginput_container_date' id='{$field_id}_1_container'>
                                            <input type='{$date_input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_1' value='{$month_value}' {$tabindex} {$disabled_text} {$month_placeholder_attribute} {$month_html5_attributes}/>
                                            <label for='{$field_id}_1' {$sub_label_class_attribute}>{$month_sub_label}</label>
                                        </div>";

							$tabindex = $this->get_tabindex();

							$field_str .= $is_sub_label_above
								? "<div class='gfield_date_day ginput_container ginput_container_date' id='{$field_id}_2_container'>
                                            <label for='{$field_id}_2' {$sub_label_class_attribute}>{$day_sub_label}</label>
                                            <input type='{$date_input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_2' value='{$day_value}' {$tabindex} {$disabled_text} {$day_placeholder_attribute} {$day_html5_attributes}/>
                                        </div>"
								: "<div class='gfield_date_day ginput_container ginput_container_date' id='{$field_id}_2_container'>
                                            <input type='{$date_input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_2' value='{$day_value}' {$tabindex} {$disabled_text} {$day_placeholder_attribute} {$day_html5_attributes}/>
                                            <label for='{$field_id}_2' {$sub_label_class_attribute}>{$day_sub_label}</label>
                                        </div>";

							$tabindex = $this->get_tabindex();

							$field_str .= $is_sub_label_above
								? "<div class='gfield_date_year ginput_container ginput_container_date' id='{$field_id}_3_container'>
                                            <label for='{$field_id}_3' {$sub_label_class_attribute}>{$year_sub_label}</label>
                                            <input type='{$date_input_type}' maxlength='4' name='input_{$id}[]' id='{$field_id}_3' value='{$year_value}' {$tabindex} {$disabled_text} {$year_placeholder_attribute} {$year_min_attribute} {$year_max_attribute} {$year_step_attribute}/>
                                       </div>
                                   </div>"
								: "<div class='gfield_date_year ginput_container ginput_container_date' id='{$field_id}_3_container'>
                                            <input type='{$date_input_type}' maxlength='4' name='input_{$id}[]' id='{$field_id}_3' value='{$year_value}' {$tabindex} {$disabled_text} {$year_placeholder_attribute} {$year_min_attribute} {$year_max_attribute} {$year_step_attribute}/>
                                            <label for='{$field_id}_3' {$sub_label_class_attribute}>{$year_sub_label}</label>
                                       </div>
                                   </div>";
						}

						break;
				}

				return "<div id='{$field_id}' class='ginput_container ginput_container_date'>$field_str</div>";
			} else {
				$picker_value = esc_attr( GFCommon::date_display( $picker_value, $format ) );
				$icon_class   = $this->calendarIconType == 'none' ? 'datepicker_no_icon' : 'datepicker_with_icon';
				$icon_url     = empty( $this->calendarIconUrl ) ? GFCommon::get_base_url() . '/images/calendar.png' : $this->calendarIconUrl;
				$icon_url     = esc_url( $icon_url );
				$tabindex     = $this->get_tabindex();
				$class        = esc_attr( $class );

				$aria_describedby  = "aria-describedby='{$field_id}_date_format'";
				$date_format_label = esc_attr__( 'Date Format: ', 'gravityforms' );
				switch ( $format ) {
					case 'mdy':
						$date_format_label .= esc_attr__( 'MM slash DD slash YYYY', 'gravityforms' );
						break;
					case 'dmy':
						$date_format_label .= esc_attr__( 'DD slash MM slash YYYY', 'gravityforms' );
						break;
					case 'dmy_dash':
						$date_format_label .= esc_attr__( 'DD dash MM dash YYYY', 'gravityforms' );
						break;
					case 'dmy_dot':
						$date_format_label .= esc_attr__( 'DD dot MM dot YYYY', 'gravityforms' );
						break;
					case 'ymd_slash':
						$date_format_label .= esc_attr__( 'YYYY slash MM slash DD', 'gravityforms' );
						break;
					case 'ymd_dash':
						$date_format_label .= esc_attr__( 'YYYY dash MM dash DD', 'gravityforms' );
						break;
					case 'ymd_dot':
						$date_format_label .= esc_attr__( 'YYYY dot MM dot DD', 'gravityforms' );
						break;
				}

				return "<div class='ginput_container ginput_container_date'>
                            <input name='input_{$id}' id='{$field_id}' type='text' value='{$picker_value}' class='datepicker {$class} {$format} {$icon_class}' {$tabindex} {$disabled_text} {$date_picker_placeholder} {$aria_describedby} />
                            <span id='{$field_id}_date_format' class='screen-reader-text'>{$date_format_label}</span>
                        </div>
                        <input type='hidden' id='gforms_calendar_icon_$field_id' class='gform_hidden' value='$icon_url'/>";
			}
		}
	}

	public function get_field_label_class() {
		return $this->dateType == 'datefield' ? 'gfield_label gfield_label_before_complex' : 'gfield_label';
	}

	public function get_value_default() {

		$value = parent::get_value_default();

		if ( is_array( $this->inputs ) ) {
			$value = $this->get_date_array_by_format( $value );
		}

		return $value;
	}

	/**
	 * The default value for mulit-input date fields will always be an array in mdy order
	 * this code will alter the order of the values to the date format of the field
	 */
	public function get_date_array_by_format( $value ) {
		$format   = empty( $this->dateFormat ) ? 'mdy' : esc_attr( $this->dateFormat );
		$position = substr( $format, 0, 3 );
		$date     = array_combine( array( 'm', 'd', 'y' ), $value );            // takes our numerical array and converts it to an associative array
		$value    = array_merge( array_flip( str_split( $position ) ), $date ); // uses the mdy position as the array keys and creates a new array in the desired order

		return $value;
	}

	public function checkdate( $month, $day, $year ) {
		if ( empty( $month ) || ! is_numeric( $month ) || empty( $day ) || ! is_numeric( $day ) || empty( $year ) || ! is_numeric( $year ) || strlen( $year ) != 4 ) {
			return false;
		}

		return checkdate( $month, $day, $year );
	}

	public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) {
		return GFCommon::date_display( $value, $this->dateFormat );
	}


	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {

		return GFCommon::date_display( $value, $this->dateFormat, $this->get_output_date_format() );
	}

	/**
	 * Gets merge tag values.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GFCommon::date_display()
	 * @uses GF_Field_Date::$dateFormat
	 *
	 * @param array|string $value      The value of the input.
	 * @param string       $input_id   The input ID to use.
	 * @param array        $entry      The Entry Object.
	 * @param array        $form       The Form Object
	 * @param string       $modifier   The modifier passed.
	 * @param array|string $raw_value  The raw value of the input.
	 * @param bool         $url_encode If the result should be URL encoded.
	 * @param bool         $esc_html   If the HTML should be escaped.
	 * @param string       $format     The format that the value should be.
	 * @param bool         $nl2br      If the nl2br function should be used.
	 *
	 * @return string The processed merge tag.
	 */
	public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) {

		return GFCommon::date_display( $value, $this->dateFormat, $this->get_output_date_format() );
	}

	/**
	 * Returns the date format to use when outputting the entry value on the detail page and when merge tags are processed.
	 *
	 * @since 2.4
	 *
	 * @return string
	 */
	public function get_output_date_format() {
		$modifiers = $this->get_modifiers();
		if ( ! empty( $modifiers ) ) {
			$valid_modifiers = array(
				'year',
				'month',
				'day',
				'ymd',
				'ymd_dash',
				'ymd_dot',
				'ymd_slash',
				'mdy',
				'mdy_dash',
				'mdy_dot',
				'mdy_slash',
				'dmy',
				'dmy_dash',
				'dmy_dot',
				'dmy_slash',
			);

			foreach ( $modifiers as $modifier ) {
				if ( in_array( $modifier, $valid_modifiers ) ) {
					return $modifier;
				}
			}
		}

		return $this->dateFormat;
	}


	/**
	 * Returns a JS script to be rendered in the front end of the form.
	 *
	 * @param array $form The Form Object
	 *
	 * @return string Returns a JS script to be processed in the front end.
	 */
	public function get_form_inline_script_on_page_render( $form ) {

		//Only return merge tag script if form supports JS merge tags
		if ( ! GFFormDisplay::has_js_merge_tag( $form ) ) {
			return '';
		}

		return "gform.addFilter( 'gform_value_merge_tag_{$form['id']}_{$this->id}', function( value, input, modifier ) { if( modifier === 'label' ) { return false; } return input.length == 1 ? input.val() : jQuery(input[0]).val() + '/' + jQuery(input[1]).val() + '/' + jQuery(input[2]).val(); } );";

	}

	private function get_month_dropdown( $name = '', $id = '', $selected_value = '', $tabindex = '', $disabled_text = '', $placeholder = '' ) {
		if ( $placeholder == '' ) {
			$placeholder = esc_html__( 'Month', 'gravityforms' );
		}

		return $this->get_number_dropdown( $name, $id, $selected_value, $tabindex, $disabled_text, $placeholder, 1, 12 );
	}

	private function get_day_dropdown( $name = '', $id = '', $selected_value = '', $tabindex = '', $disabled_text = '', $placeholder = '' ) {
		if ( $placeholder == '' ) {
			$placeholder = esc_html__( 'Day', 'gravityforms' );
		}

		return $this->get_number_dropdown( $name, $id, $selected_value, $tabindex, $disabled_text, $placeholder, 1, 31 );
	}

	private function get_year_dropdown( $name, $id, $selected_value, $tabindex, $disabled_text, $placeholder, $form ) {
		$name           = ( is_string( $name ) ) ? $name : '';
		$id             = ( is_string( $id ) ) ? $id : '';
		$selected_value = ( is_string( $selected_value ) ) ? $selected_value : '';
		$tabindex       = ( is_string( $tabindex ) ) ? $tabindex : '';
		$disabled_text  = ( is_string( $disabled_text ) ) ? $disabled_text : '';
		$placeholder    = ( is_string( $placeholder ) ) ? $placeholder : '';

		if ( $placeholder == '' ) {
			$placeholder = esc_html__( 'Year', 'gravityforms' );
		}
		$year_min = apply_filters( 'gform_date_min_year', '1920', $form, $this );
		$year_max = apply_filters( 'gform_date_max_year', date( 'Y' ) + 1, $form, $this );

		return $this->get_number_dropdown( $name, $id, $selected_value, $tabindex, $disabled_text, $placeholder, $year_max, $year_min );
	}

	private function get_number_dropdown( $name, $id, $selected_value, $tabindex, $disabled_text, $placeholder, $start_number, $end_number ) {
		$str = "<select name='{$name}' id='{$id}' {$tabindex} {$disabled_text} aria-label='{$placeholder}'>";
		if ( $placeholder !== false ) {
			$str .= "<option value=''>{$placeholder}</option>";
		}

		$increment = $start_number < $end_number ? 1 : - 1;

		for ( $i = $start_number; $i != ( $end_number + $increment ); $i += $increment ) {
			$selected = intval( $i ) == intval( $selected_value ) ? "selected='selected'" : '';
			$str .= "<option value='{$i}' {$selected}>{$i}</option>";
		}
		$str .= '</select>';

		return $str;
	}

	/**
	 * Returns the value to save in the entry.
	 *
	 * @param string $value
	 * @param array $form
	 * @param string $input_name
	 * @param int $lead_id
	 * @param array $lead
	 *
	 * @return string
	 */
	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {
		// if $value is a default value and also an array, it will be an associative array; to be safe, let's convert all array $value to numeric
		if ( is_array( $value ) ) {
			$value = array_values( $value );
		}

		$value = GFFormsModel::prepare_date( $this->dateFormat, $value );
		$value = $this->sanitize_entry_value( $value, $form['id'] );

		return $value;
	}

	public function get_entry_inputs() {
		return null;
	}

	public function sanitize_settings() {
		parent::sanitize_settings();
		$this->calendarIconType = wp_strip_all_tags( $this->calendarIconType );
		$this->calendarIconUrl  = wp_strip_all_tags( $this->calendarIconUrl );
		if ( $this->dateFormat && ! in_array( $this->dateFormat, array(	'mdy', 'dmy', 'dmy_dash', 'dmy_dot', 'ymd_slash', 'ymd_dash', 'ymd_dot' ) ) ) {
			$this->dateFormat = 'mdy';
		}
	}

	/**
	 * Removes the "for" attribute in the field label. Inputs are only allowed one label (a11y) and the inputs already have labels.
	 *
	 * @since  2.4
	 * @access public
	 *
	 * @param array $form The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_first_input_id( $form ) {
		return in_array( $this->dateType, array( 'datefield', 'datedropdown' ) ) ? '' : parent::get_first_input_id( $form ) ;
	}

	// # FIELD FILTER UI HELPERS ---------------------------------------------------------------------------------------

	/**
	 * Returns the filter settings for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_settings() {
		$filter_settings                = parent::get_filter_settings();
		$filter_settings['placeholder'] = esc_html__( 'yyyy-mm-dd', 'gravityforms' );
		$filter_settings['cssClass']    = 'datepicker ymd_dash';

		return $filter_settings;
	}
}

GF_Fields::register( new GF_Field_Date() );
class-gf-field-time.php000066600000034241151264036140011003 0ustar00<?php

// If Gravity Forms isn't loaded, bail.
if ( ! class_exists( 'GFForms' ) ) {
	die();
}

/**
 * Class GF_Field_Time
 *
 * Handles Time fields.
 *
 * @since Unknown
 * @uses  GF_Field
 */
class GF_Field_Time extends GF_Field {

	/**
	 * Sets the field type to be used in the field framework.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @var string $type The type of field this is.
	 */
	public $type = 'time';

	/**
	 * Sets the title of the field to be used in the form editor.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFCommon::get_field_type_title()
	 * @used-by GFAddOn::get_field_map_choices()
	 * @used-by GFAddOn::prepare_field_select_field()
	 * @used-by GFAddOn::settings_field_map_select()
	 * @used-by GF_Field::get_form_editor_button()
	 *
	 * @return string The field title.
	 */
	public function get_form_editor_field_title() {
		return esc_attr__( 'Time', 'gravityforms' );
	}

	/**
	 * Defines the field editor settings that are available for this field.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFFormDetail::inline_scripts()
	 *
	 * @return array Contains the settings available within the field editor.
	 */
	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'sub_labels_setting',
			'label_placement_setting',
			'sub_label_placement_setting',
			'admin_label_setting',
			'time_format_setting',
			'rules_setting',
			'visibility_setting',
			'duplicate_setting',
			'default_input_values_setting',
			'input_placeholders_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	/**
	 * Validates the field inputs.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFFormDisplay::validate()
	 * @uses    GF_Field_Time::$failed_validation
	 * @uses    GF_Field_Time::$validation_message
	 * @uses    GF_Field_Time::$timeFormat
	 * @uses    GF_Field_Time::$errorMessage
	 *
	 * @param array|string $value The field value or values to validate.
	 * @param array        $form  The Form Object.
	 *
	 * @return void
	 */
	public function validate( $value, $form ) {
		// Create variable values if time came in one field.
		if ( ! is_array( $value ) && ! empty( $value ) ) {
			preg_match( '/^(\d*):(\d*) ?(.*)$/', $value, $matches );
			$value    = array();
			$value[0] = $matches[1];
			$value[1] = $matches[2];
		}

		$hour   = rgar( $value, 0 );
		$minute = rgar( $value, 1 );

		if ( empty( $hour ) && empty( $minute ) ) {
			return;
		}

		$is_valid_format = is_numeric( $hour ) && is_numeric( $minute );

		$min_hour   = $this->timeFormat == '24' ? 0 : 1;
		$max_hour   = $this->timeFormat == '24' ? 24 : 12;
		$max_minute = $hour >= 24 ? 0 : 59;

		if ( ! $is_valid_format || $hour < $min_hour || $hour > $max_hour || $minute < 0 || $minute > $max_minute ) {
			$this->failed_validation  = true;
			$this->validation_message = empty( $this->errorMessage ) ? esc_html__( 'Please enter a valid time.', 'gravityforms' ) : $this->errorMessage;
		}
	}

	/**
	 * Defines how the Time field input is shown.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFCommon::get_field_input()
	 * @uses    GF_Field::is_entry_detail()
	 * @uses    GF_Field::is_form_editor()
	 * @uses    GF_Field_Time::$subLabelPlacement
	 * @uses    GFFormsModel::get_input()
	 * @uses    GF_Field::get_input_placeholder_attribute()
	 * @uses    GF_Field::get_tabindex()
	 * @uses    GFFormsModel::is_html5_enabled()
	 *
	 * @param array      $form  The Form Object.
	 * @param string     $value The field default value. Defaults to empty string.
	 * @param array|null $entry The Entry Object, if available. Defaults to null.
	 *
	 * @return string The field HTML markup.
	 */
	public function get_field_input( $form, $value = '', $entry = null ) {

		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$form_id  = absint( $form['id'] );
		$id       = intval( $this->id );
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		$form_sub_label_placement  = rgar( $form, 'subLabelPlacement' );
		$field_sub_label_placement = $this->subLabelPlacement;
		$is_sub_label_above        = $field_sub_label_placement == 'above' || ( empty( $field_sub_label_placement ) && $form_sub_label_placement == 'above' );
		$sub_label_class_attribute = $field_sub_label_placement == 'hidden_label' ? "class='hidden_sub_label screen-reader-text'" : '';

		$disabled_text = $is_form_editor ? "disabled='disabled'" : '';

		$hour = $minute = $am_selected = $pm_selected = '';

		if ( ! is_array( $value ) && ! empty( $value ) ) {
			preg_match( '/^(\d*):(\d*) ?(.*)$/', $value, $matches );
			$hour        = esc_attr( $matches[1] );
			$minute      = esc_attr( $matches[2] );
			$the_rest    = strtolower( rgar( $matches, 3 ) );
			$am_selected = strpos( $the_rest, 'am' ) > -1 ? "selected='selected'" : '';
			$pm_selected = strpos( $the_rest, 'pm' ) > -1  ? "selected='selected'" : '';
		} elseif ( is_array( $value ) ) {
			$value       = array_values( $value );
			$hour        = esc_attr( $value[0] );
			$minute      = esc_attr( $value[1] );
			$am_selected = strtolower( rgar( $value, 2 ) ) == 'am' ? "selected='selected'" : '';
			$pm_selected = strtolower( rgar( $value, 2 ) ) == 'pm' ? "selected='selected'" : '';
		}

		$hour_input   = GFFormsModel::get_input( $this, $this->id . '.1' );
		$minute_input = GFFormsModel::get_input( $this, $this->id . '.2' );

		$hour_placeholder_attribute   = $this->get_input_placeholder_attribute( $hour_input );
		$minute_placeholder_attribute = $this->get_input_placeholder_attribute( $minute_input );

		$hour_tabindex   = $this->get_tabindex();
		$minute_tabindex = $this->get_tabindex();
		$ampm_tabindex   = $this->get_tabindex();

		$is_html5   = RGFormsModel::is_html5_enabled();
		$input_type = $is_html5 ? 'number' : 'text';

		$max_hour = $this->timeFormat == '24' ? 24 : 12;
		$hour_html5_attributes   = $is_html5 ? "min='0' max='{$max_hour}' step='1'" : '';
		$minute_html5_attributes = $is_html5 ? "min='0' max='59' step='1'" : '';

		$ampm_field_style = $is_form_editor && $this->timeFormat == '24' ? "style='display:none;'" : '';
		if ( $is_form_editor || $this->timeFormat != '24' ) {
			$am_text = esc_html__( 'AM', 'gravityforms' );
			$pm_text = esc_html__( 'PM', 'gravityforms' );
			$aria_label = esc_attr( 'AM/PM', 'gravityforms' );
			$ampm_field = "<div class='gfield_time_ampm ginput_container ginput_container_time' {$ampm_field_style}>
                                " . ( $is_sub_label_above ? "<div class='gfield_time_ampm_shim' aria-hidden='true'>&nbsp;</div>" : "" ). "
                                <select name='input_{$id}[]' id='{$field_id}_3' $ampm_tabindex {$disabled_text} aria-label='{$aria_label}'>
                                    <option value='am' {$am_selected}>{$am_text}</option>
                                    <option value='pm' {$pm_selected}>{$pm_text}</option>
                                </select>
                           </div>";
		} else {
			$ampm_field = '';
		}

		$hour_label = rgar( $hour_input, 'customLabel' ) != '' ? $hour_input['customLabel'] : esc_html__( 'HH', 'gravityforms' );
		$minute_label = rgar( $minute_input, 'customLabel' ) != '' ? $minute_input['customLabel'] : esc_html( _x( 'MM', 'Abbreviation: Minutes', 'gravityforms' ) );

		if ( $is_sub_label_above ) {
			return "<div class='clear-multi'>
                        <div class='gfield_time_hour ginput_container ginput_container_time' id='{$field_id}'>
                            <label for='{$field_id}_1' {$sub_label_class_attribute}>{$hour_label}</label>
                            <input type='{$input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_1' value='{$hour}' {$hour_tabindex} {$hour_html5_attributes} {$disabled_text} {$hour_placeholder_attribute}/> <i>:</i>
                        </div>
                        <div class='gfield_time_minute ginput_container ginput_container_time'>
                            <label for='{$field_id}_2' {$sub_label_class_attribute}>{$minute_label}</label>
                            <input type='{$input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_2' value='{$minute}' {$minute_tabindex} {$minute_html5_attributes} {$disabled_text} {$minute_placeholder_attribute}/>
                        </div>
                        {$ampm_field}
                    </div>";
		} else {
			return "<div class='clear-multi'>
                        <div class='gfield_time_hour ginput_container ginput_container_time' id='{$field_id}'>
                            <input type='{$input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_1' value='{$hour}' {$hour_tabindex} {$hour_html5_attributes} {$disabled_text} {$hour_placeholder_attribute}/> <i>:</i>
                            <label for='{$field_id}_1' {$sub_label_class_attribute}>{$hour_label}</label>
                        </div>
                        <div class='gfield_time_minute ginput_container ginput_container_time'>
                            <input type='{$input_type}' maxlength='2' name='input_{$id}[]' id='{$field_id}_2' value='{$minute}' {$minute_tabindex} {$minute_html5_attributes} {$disabled_text} {$minute_placeholder_attribute}/>
                            <label for='{$field_id}_2' {$sub_label_class_attribute}>{$minute_label}</label>
                        </div>
                        {$ampm_field}
                    </div>";
		}
	}

	/**
	 * Adds additional classes to the field labels.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GF_Field::get_field_content()
	 *
	 * @return string The class string to use for the Time field.
	 */
	public function get_field_label_class(){
		return 'gfield_label gfield_label_before_complex';
	}

	/**
	 * Whether this field expects an array during submission.
	 *
	 * @since 2.4
	 *
	 * @return bool
	 */
	public function is_value_submission_array() {
		return true;
	}

	/**
	 * Determines if any of the submission values are empty.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFFormDisplay::is_empty()
	 *
	 * @param int $form_id The form ID.
	 *
	 * @return bool True if empty. False otherwise.
	 */
	public function is_value_submission_empty( $form_id ) {
		$value = rgpost( 'input_' . $this->id );
		if ( is_array( $value ) ) {
			// Date field and date drop-downs.
			foreach ( $value as $input ) {
				if ( strlen( trim( $input ) ) <= 0 ) {
					return true;
				}
			}

			return false;
		} else {

			// Date picker.
			return strlen( trim( $value ) ) <= 0;
		}
	}

	/**
	 * Determines whether the given value is considered empty for this field.
	 *
	 * @since  2.4
	 *
	 * @param string|array $value The value.
	 *
	 * @return bool True if empty. False otherwise.
	 */
	public function is_value_empty( $value ) {
		if ( is_array( $value ) ) {
			foreach ( $value as $input ) {
				if ( strlen( trim( $input ) ) <= 0 ) {
					return true;
				}
			}

			return false;
		} else {
			return strlen( trim( $value ) ) <= 0;
		}
	}

	/**
	 * Prepares the field value to be saved after an entry is submitted.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFFormsModel::prepare_value()
	 *
	 * @param string $value      The value to prepare.
	 * @param array  $form       The Form Object. Not used.
	 * @param string $input_name The name of the input. Not used.
	 * @param int    $lead_id    The entry ID. Not used.
	 * @param array  $lead       The Entry Object. Not used.
	 *
	 * @return array|string      The field value, prepared and stripped of tags.
	 */
	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {

		if ( empty( $value ) && ! is_array( $value ) ) {
			return '';
		}

		// If $value is a default value and also an array, it will be an associative array; to be safe, let's convert all array $value to numeric.
		if ( is_array( $value ) ) {
			$value = array_values( $value );
		}

		if ( ! is_array( $value ) && ! empty( $value ) ) {
			preg_match( '/^(\d*):(\d*) ?(.*)$/', $value, $matches );
			$value    = array();
			$value[0] = $matches[1];
			$value[1] = $matches[2];
			$value[2] = rgar( $matches, 3 );
		}

		$hour   = wp_strip_all_tags( $value[0] );
		$minute = wp_strip_all_tags( $value[1] );
		$ampm   = wp_strip_all_tags( rgar( $value, 2 ) );
		if ( ! empty( $ampm ) ) {
			$ampm = " $ampm";
		}

		if ( ! ( rgblank( $hour ) && rgblank( $minute ) ) ) {
			$value = sprintf( '%02d:%02d%s', $hour, $minute, $ampm );
		} else {
			$value = '';
		}

		return $value;
	}

	/**
	 * Returns a JS script to be rendered in the front end of the form.
	 *
	 * @param array $form The Form Object
	 *
	 * @return string Returns a JS script to be processed in the front end.
	 */
	public function get_form_inline_script_on_page_render( $form ) {

		//Only return merge tag script if form supports JS merge tags
		if ( ! GFFormDisplay::has_js_merge_tag( $form ) ) {
			return '';
		}

		return "gform.addFilter( 'gform_value_merge_tag_{$form['id']}_{$this->id}', function( value, input, modifier ) { if( modifier === 'label' ) { return false; } var ampm = input.length == 3 ? ' ' + jQuery(input[2]).val() : ''; return jQuery(input[0]).val() + ':' + jQuery(input[1]).val() + ' ' + ampm; } );";

	}

	/**
	 * Overrides GF_Field to prevent the standard input ID from being used.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @return null
	 */
	public function get_entry_inputs() {
		return null;
	}


	/**
	 * Removes the "for" attribute in the field label. Inputs are only allowed one label (a11y) and the inputs already have labels.
	 *
	 * @since  2.4
	 * @access public
	 *
	 * @param array $form The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_first_input_id( $form ) {
		return '';
	}

	/**
	 * Sanitizes settings for the Time field.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @used-by GFFormDetail::add_field()
	 * @used-by GFFormsModel::sanitize_settings()
	 * @uses    GF_Field::sanitize_settings
	 * @uses    GF_Field_Time::$timeFormat
	 *
	 * @return void
	 */
	public function sanitize_settings() {
		parent::sanitize_settings();
		if ( ! $this->timeFormat || ! in_array( $this->timeFormat, array( 12, 24 ) ) ) {
			$this->timeFormat = '12';
		}
	}

}

// Register the Time field with the field framework.
GF_Fields::register( new GF_Field_Time() );
class-gf-field-quantity.php000066600000001231151264036140011714 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_Quantity extends GF_Field {

	public $type = 'quantity';

	function get_form_editor_field_settings() {
		return array(
			'product_field_setting',
			'quantity_field_type_setting',
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'label_setting',
			'admin_label_setting',
			'label_placement_setting',
			'default_value_setting',
			'placeholder_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	public function get_form_editor_field_title() {
		return esc_attr__( 'Quantity', 'gravityforms' );
	}

}

GF_Fields::register( new GF_Field_Quantity() );class-gf-field-post-tags.php000066600000004106151264036140011763 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}

class GF_Field_Post_Tags extends GF_Field {

	public $type = 'post_tags';

	public function get_form_editor_field_title() {
		return esc_attr__( 'Tags', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'post_tag_type_setting',
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'size_setting',
			'rules_setting',
			'default_value_setting',
			'visibility_setting',
			'description_setting',
			'css_class_setting',
			'placeholder_setting',
		);
	}

	public function is_conditional_logic_supported() {
		return true;
	}

	public function get_field_input( $form, $value = '', $entry = null ) {
		$form_id         = absint( $form['id'] );
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$id       = (int) $this->id;
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		$value        = esc_attr( $value );
		$size         = $this->size;
		$class_suffix = $is_entry_detail ? '_admin' : '';
		$class        = $size . $class_suffix;
		$class        = esc_attr( $class );

		$disabled_text = $is_form_editor ? 'disabled="disabled"' : '';

		$tabindex              = $this->get_tabindex();
		$placeholder_attribute = $this->get_field_placeholder_attribute();
		$required_attribute    = $this->isRequired ? 'aria-required="true"' : '';
		$invalid_attribute     = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';
		$aria_describedby      = $this->get_aria_describedby();

		return "<div class='ginput_container ginput_container_post_tags'>
					<input name='input_{$id}' id='{$field_id}' type='text' value='{$value}' class='{$class}' {$tabindex} {$placeholder_attribute} {$required_attribute} {$invalid_attribute} {$aria_describedby} {$disabled_text}/>
				</div>";
	}

	public function allow_html() {
		return true;
	}
}

GF_Fields::register( new GF_Field_Post_Tags() );class-gf-field-address.php000066600000164566151264036140011510 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}

class GF_Field_Address extends GF_Field {

	public $type = 'address';

	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'admin_label_setting',
			'label_placement_setting',
			'sub_label_placement_setting',
			'default_input_values_setting',
			'input_placeholders_setting',
			'address_setting',
			'rules_setting',
			'copy_values_option',
			'description_setting',
			'visibility_setting',
			'css_class_setting',
		);
	}

	public function is_conditional_logic_supported() {
		return true;
	}

	public function get_form_editor_field_title() {
		return esc_attr__( 'Address', 'gravityforms' );
	}

	function validate( $value, $form ) {

		if ( $this->isRequired ) {
			$copy_values_option_activated = $this->enableCopyValuesOption && rgpost( 'input_' . $this->id . '_copy_values_activated' );
			if ( $copy_values_option_activated ) {
				// validation will occur in the source field
				return;
			}

			$street  = rgar( $value, $this->id . '.1' );
			$city    = rgar( $value, $this->id . '.3' );
			$state   = rgar( $value, $this->id . '.4' );
			$zip     = rgar( $value, $this->id . '.5' );
			$country = rgar( $value, $this->id . '.6' );

			if ( empty( $street ) && ! $this->get_input_property( $this->id . '.1', 'isHidden' )
			     || empty( $city ) && ! $this->get_input_property( $this->id . '.3', 'isHidden' )
			     || empty( $zip ) && ! $this->get_input_property( $this->id . '.5', 'isHidden' )
			     || ( empty( $state ) && ! ( $this->hideState || $this->get_input_property( $this->id . '.4', 'isHidden' ) ) )
			     || ( empty( $country ) && ! ( $this->hideCountry || $this->get_input_property( $this->id . '.6', 'isHidden' ) ) )
			) {
				$this->failed_validation  = true;
				$this->validation_message = empty( $this->errorMessage ) ? esc_html__( 'This field is required. Please enter a complete address.', 'gravityforms' ) : $this->errorMessage;
			}
		}
	}

	public function get_value_submission( $field_values, $get_from_post_global_var = true ) {

		$value                                         = parent::get_value_submission( $field_values, $get_from_post_global_var );
		$value[ $this->id . '_copy_values_activated' ] = (bool) rgpost( 'input_' . $this->id . '_copy_values_activated' );

		return $value;
	}

	public function get_field_input( $form, $value = '', $entry = null ) {

		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();
		$is_admin        = $is_entry_detail || $is_form_editor;

		$form_id  = absint( $form['id'] );
		$id       = intval( $this->id );
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";
		$form_id  = ( $is_entry_detail || $is_form_editor ) && empty( $form_id ) ? rgget( 'id' ) : $form_id;

		$disabled_text      = $is_form_editor ? "disabled='disabled'" : '';
		$class_suffix       = $is_entry_detail ? '_admin' : '';
		$required_attribute = $this->isRequired ? 'aria-required="true"' : '';

		$form_sub_label_placement  = rgar( $form, 'subLabelPlacement' );
		$field_sub_label_placement = $this->subLabelPlacement;
		$is_sub_label_above        = $field_sub_label_placement == 'above' || ( empty( $field_sub_label_placement ) && $form_sub_label_placement == 'above' );
		$sub_label_class_attribute = $field_sub_label_placement == 'hidden_label' ? "class='hidden_sub_label screen-reader-text'" : '';

		$street_value  = '';
		$street2_value = '';
		$city_value    = '';
		$state_value   = '';
		$zip_value     = '';
		$country_value = '';

		if ( is_array( $value ) ) {
			$street_value  = esc_attr( rgget( $this->id . '.1', $value ) );
			$street2_value = esc_attr( rgget( $this->id . '.2', $value ) );
			$city_value    = esc_attr( rgget( $this->id . '.3', $value ) );
			$state_value   = esc_attr( rgget( $this->id . '.4', $value ) );
			$zip_value     = esc_attr( rgget( $this->id . '.5', $value ) );
			$country_value = esc_attr( rgget( $this->id . '.6', $value ) );
		}

		// Inputs.
		$address_street_field_input  = GFFormsModel::get_input( $this, $this->id . '.1' );
		$address_street2_field_input = GFFormsModel::get_input( $this, $this->id . '.2' );
		$address_city_field_input    = GFFormsModel::get_input( $this, $this->id . '.3' );
		$address_state_field_input   = GFFormsModel::get_input( $this, $this->id . '.4' );
		$address_zip_field_input     = GFFormsModel::get_input( $this, $this->id . '.5' );
		$address_country_field_input = GFFormsModel::get_input( $this, $this->id . '.6' );

		// Placeholders.
		$street_placeholder_attribute  = GFCommon::get_input_placeholder_attribute( $address_street_field_input );
		$street2_placeholder_attribute = GFCommon::get_input_placeholder_attribute( $address_street2_field_input );
		$city_placeholder_attribute    = GFCommon::get_input_placeholder_attribute( $address_city_field_input );
		$zip_placeholder_attribute     = GFCommon::get_input_placeholder_attribute( $address_zip_field_input );

		$address_types = $this->get_address_types( $form_id );
		$addr_type     = empty( $this->addressType ) ? $this->get_default_address_type( $form_id ) : $this->addressType;
		$address_type  = rgar( $address_types, $addr_type );

		$state_label  = empty( $address_type['state_label'] ) ? esc_html__( 'State', 'gravityforms' ) : $address_type['state_label'];
		$zip_label    = empty( $address_type['zip_label'] ) ? esc_html__( 'Zip Code', 'gravityforms' ) : $address_type['zip_label'];
		$hide_country = ! empty( $address_type['country'] ) || $this->hideCountry || rgar( $address_country_field_input, 'isHidden' );

		if ( empty( $country_value ) ) {
			$country_value = $this->defaultCountry;
		}

		if ( empty( $state_value ) ) {
			$state_value = $this->defaultState;
		}

		$country_placeholder = GFCommon::get_input_placeholder_value( $address_country_field_input );
		$country_list        = $this->get_country_dropdown( $country_value, $country_placeholder );

		// Changing css classes based on field format to ensure proper display.
		$address_display_format = apply_filters( 'gform_address_display_format', 'default', $this );
		$city_location          = $address_display_format == 'zip_before_city' ? 'right' : 'left';
		$zip_location           = $address_display_format != 'zip_before_city' && ( $this->hideState || rgar( $address_state_field_input, 'isHidden' ) ) ? 'right' : 'left'; // support for $this->hideState legacy property
		$state_location         = $address_display_format == 'zip_before_city' ? 'left' : 'right';
		$country_location       = $this->hideState || rgar( $address_state_field_input, 'isHidden' ) ? 'left' : 'right'; // support for $this->hideState legacy property

		// Labels.
		$address_street_sub_label  = rgar( $address_street_field_input, 'customLabel' ) != '' ? $address_street_field_input['customLabel'] : esc_html__( 'Street Address', 'gravityforms' );
		$address_street_sub_label  = gf_apply_filters( array( 'gform_address_street', $form_id, $this->id ), $address_street_sub_label, $form_id );
		$address_street2_sub_label = rgar( $address_street2_field_input, 'customLabel' ) != '' ? $address_street2_field_input['customLabel'] : esc_html__( 'Address Line 2', 'gravityforms' );
		$address_street2_sub_label = gf_apply_filters( array( 'gform_address_street2', $form_id, $this->id ), $address_street2_sub_label, $form_id );
		$address_zip_sub_label     = rgar( $address_zip_field_input, 'customLabel' ) != '' ? $address_zip_field_input['customLabel'] : $zip_label;
		$address_zip_sub_label     = gf_apply_filters( array( 'gform_address_zip', $form_id, $this->id ), $address_zip_sub_label, $form_id );
		$address_city_sub_label    = rgar( $address_city_field_input, 'customLabel' ) != '' ? $address_city_field_input['customLabel'] : esc_html__( 'City', 'gravityforms' );
		$address_city_sub_label    = gf_apply_filters( array( 'gform_address_city', $form_id, $this->id ), $address_city_sub_label, $form_id );
		$address_state_sub_label   = rgar( $address_state_field_input, 'customLabel' ) != '' ? $address_state_field_input['customLabel'] : $state_label;
		$address_state_sub_label   = gf_apply_filters( array( 'gform_address_state', $form_id, $this->id ), $address_state_sub_label, $form_id );
		$address_country_sub_label = rgar( $address_country_field_input, 'customLabel' ) != '' ? $address_country_field_input['customLabel'] : esc_html__( 'Country', 'gravityforms' );
		$address_country_sub_label = gf_apply_filters( array( 'gform_address_country', $form_id, $this->id ), $address_country_sub_label, $form_id );

		// Address field.
		$street_address = '';
		$tabindex       = $this->get_tabindex();
		$style          = ( $is_admin && rgar( $address_street_field_input, 'isHidden' ) ) ? "style='display:none;'" : '';
		if ( $is_admin || ! rgar( $address_street_field_input, 'isHidden' ) ) {
			if ( $is_sub_label_above ) {
				$street_address = " <span class='ginput_full{$class_suffix} address_line_1' id='{$field_id}_1_container' {$style}>
                                        <label for='{$field_id}_1' id='{$field_id}_1_label' {$sub_label_class_attribute}>{$address_street_sub_label}</label>
                                        <input type='text' name='input_{$id}.1' id='{$field_id}_1' value='{$street_value}' {$tabindex} {$disabled_text} {$street_placeholder_attribute} {$required_attribute}/>
                                    </span>";
			} else {
				$street_address = " <span class='ginput_full{$class_suffix} address_line_1' id='{$field_id}_1_container' {$style}>
                                        <input type='text' name='input_{$id}.1' id='{$field_id}_1' value='{$street_value}' {$tabindex} {$disabled_text} {$street_placeholder_attribute} {$required_attribute}/>
                                        <label for='{$field_id}_1' id='{$field_id}_1_label' {$sub_label_class_attribute}>{$address_street_sub_label}</label>
                                    </span>";
			}
		}

		// Address line 2 field.
		$street_address2 = '';
		$style           = ( $is_admin && ( $this->hideAddress2 || rgar( $address_street2_field_input, 'isHidden' ) ) ) ? "style='display:none;'" : ''; // support for $this->hideAddress2 legacy property
		if ( $is_admin || ( ! $this->hideAddress2 && ! rgar( $address_street2_field_input, 'isHidden' ) ) ) {
			$tabindex = $this->get_tabindex();
			if ( $is_sub_label_above ) {
				$street_address2 = "<span class='ginput_full{$class_suffix} address_line_2' id='{$field_id}_2_container' {$style}>
                                        <label for='{$field_id}_2' id='{$field_id}_2_label' {$sub_label_class_attribute}>{$address_street2_sub_label}</label>
                                        <input type='text' name='input_{$id}.2' id='{$field_id}_2' value='{$street2_value}' {$tabindex} {$disabled_text} {$street2_placeholder_attribute}/>
                                    </span>";
			} else {
				$street_address2 = "<span class='ginput_full{$class_suffix} address_line_2' id='{$field_id}_2_container' {$style}>
                                        <input type='text' name='input_{$id}.2' id='{$field_id}_2' value='{$street2_value}' {$tabindex} {$disabled_text} {$street2_placeholder_attribute}/>
                                        <label for='{$field_id}_2' id='{$field_id}_2_label' {$sub_label_class_attribute}>{$address_street2_sub_label}</label>
                                    </span>";
			}
		}

		if ( $address_display_format == 'zip_before_city' ) {
			// Zip field.
			$zip      = '';
			$tabindex = $this->get_tabindex();
			$style    = ( $is_admin && rgar( $address_zip_field_input, 'isHidden' ) ) ? "style='display:none;'" : '';
			if ( $is_admin || ! rgar( $address_zip_field_input, 'isHidden' ) ) {
				if ( $is_sub_label_above ) {
					$zip = "<span class='ginput_{$zip_location}{$class_suffix} address_zip' id='{$field_id}_5_container' {$style}>
                                    <label for='{$field_id}_5' id='{$field_id}_5_label' {$sub_label_class_attribute}>{$address_zip_sub_label}</label>
                                    <input type='text' name='input_{$id}.5' id='{$field_id}_5' value='{$zip_value}' {$tabindex} {$disabled_text} {$zip_placeholder_attribute} {$required_attribute}/>
                                </span>";
				} else {
					$zip = "<span class='ginput_{$zip_location}{$class_suffix} address_zip' id='{$field_id}_5_container' {$style}>
                                    <input type='text' name='input_{$id}.5' id='{$field_id}_5' value='{$zip_value}' {$tabindex} {$disabled_text} {$zip_placeholder_attribute} {$required_attribute}/>
                                    <label for='{$field_id}_5' id='{$field_id}_5_label' {$sub_label_class_attribute}>{$address_zip_sub_label}</label>
                                </span>";
				}
			}

			// City field.
			$city     = '';
			$tabindex = $this->get_tabindex();
			$style    = ( $is_admin && rgar( $address_city_field_input, 'isHidden' ) ) ? "style='display:none;'" : '';
			if ( $is_admin || ! rgar( $address_city_field_input, 'isHidden' ) ) {
				if ( $is_sub_label_above ) {
					$city = "<span class='ginput_{$city_location}{$class_suffix} address_city' id='{$field_id}_3_container' {$style}>
                                    <label for='{$field_id}_3' id='{$field_id}_3_label' {$sub_label_class_attribute}>{$address_city_sub_label}</label>
                                    <input type='text' name='input_{$id}.3' id='{$field_id}_3' value='{$city_value}' {$tabindex} {$disabled_text} {$city_placeholder_attribute} {$required_attribute}/>
                                 </span>";
				} else {
					$city = "<span class='ginput_{$city_location}{$class_suffix} address_city' id='{$field_id}_3_container' {$style}>
                                    <input type='text' name='input_{$id}.3' id='{$field_id}_3' value='{$city_value}' {$tabindex} {$disabled_text} {$city_placeholder_attribute} {$required_attribute}/>
                                    <label for='{$field_id}_3' id='{$field_id}_3_label' {$sub_label_class_attribute}>{$address_city_sub_label}</label>
                                 </span>";
				}
			}

			// State field.
			$style = ( $is_admin && ( $this->hideState || rgar( $address_state_field_input, 'isHidden' ) ) ) ? "style='display:none;'" : ''; // support for $this->hideState legacy property
			if ( $is_admin || ( ! $this->hideState && ! rgar( $address_state_field_input, 'isHidden' ) ) ) {
				$state_field = $this->get_state_field( $id, $field_id, $state_value, $disabled_text, $form_id );
				if ( $is_sub_label_above ) {
					$state = "<span class='ginput_{$state_location}{$class_suffix} address_state' id='{$field_id}_4_container' {$style}>
                                           <label for='{$field_id}_4' id='{$field_id}_4_label' {$sub_label_class_attribute}>{$address_state_sub_label}</label>
                                           $state_field
                                      </span>";
				} else {
					$state = "<span class='ginput_{$state_location}{$class_suffix} address_state' id='{$field_id}_4_container' {$style}>
                                           $state_field
                                           <label for='{$field_id}_4' id='{$field_id}_4_label' {$sub_label_class_attribute}>{$address_state_sub_label}</label>
                                      </span>";
				}
			} else {
				$state = sprintf( "<input type='hidden' class='gform_hidden' name='input_%d.4' id='%s_4' value='%s'/>", $id, $field_id, $state_value );
			}
		} else {

			// City field.
			$city     = '';
			$tabindex = $this->get_tabindex();
			$style    = ( $is_admin && rgar( $address_city_field_input, 'isHidden' ) ) ? "style='display:none;'" : '';
			if ( $is_admin || ! rgar( $address_city_field_input, 'isHidden' ) ) {
				if ( $is_sub_label_above ) {
					$city = "<span class='ginput_{$city_location}{$class_suffix} address_city' id='{$field_id}_3_container' {$style}>
                                    <label for='{$field_id}_3' id='{$field_id}_3_label' {$sub_label_class_attribute}>{$address_city_sub_label}</label>
                                    <input type='text' name='input_{$id}.3' id='{$field_id}_3' value='{$city_value}' {$tabindex} {$disabled_text} {$city_placeholder_attribute} {$required_attribute}/>
                                 </span>";
				} else {
					$city = "<span class='ginput_{$city_location}{$class_suffix} address_city' id='{$field_id}_3_container' {$style}>
                                    <input type='text' name='input_{$id}.3' id='{$field_id}_3' value='{$city_value}' {$tabindex} {$disabled_text} {$city_placeholder_attribute} {$required_attribute}/>
                                    <label for='{$field_id}_3' id='{$field_id}_3_label' {$sub_label_class_attribute}>{$address_city_sub_label}</label>
                                 </span>";
				}
			}

			// State field.
			$style = ( $is_admin && ( $this->hideState || rgar( $address_state_field_input, 'isHidden' ) ) ) ? "style='display:none;'" : ''; // support for $this->hideState legacy property
			if ( $is_admin || ( ! $this->hideState && ! rgar( $address_state_field_input, 'isHidden' ) ) ) {
				$state_field = $this->get_state_field( $id, $field_id, $state_value, $disabled_text, $form_id );
				if ( $is_sub_label_above ) {
					$state = "<span class='ginput_{$state_location}{$class_suffix} address_state' id='{$field_id}_4_container' {$style}>
                                        <label for='{$field_id}_4' id='{$field_id}_4_label' {$sub_label_class_attribute}>$address_state_sub_label</label>
                                        $state_field
                                      </span>";
				} else {
					$state = "<span class='ginput_{$state_location}{$class_suffix} address_state' id='{$field_id}_4_container' {$style}>
                                        $state_field
                                        <label for='{$field_id}_4' id='{$field_id}_4_label' {$sub_label_class_attribute}>$address_state_sub_label</label>
                                      </span>";
				}
			} else {
				$state = sprintf( "<input type='hidden' class='gform_hidden' name='input_%d.4' id='%s_4' value='%s'/>", $id, $field_id, $state_value );
			}

			// Zip field.
			$zip      = '';
			$tabindex = GFCommon::get_tabindex();
			$style    = ( $is_admin && rgar( $address_zip_field_input, 'isHidden' ) ) ? "style='display:none;'" : '';
			if ( $is_admin || ! rgar( $address_zip_field_input, 'isHidden' ) ) {
				if ( $is_sub_label_above ) {
					$zip = "<span class='ginput_{$zip_location}{$class_suffix} address_zip' id='{$field_id}_5_container' {$style}>
                                    <label for='{$field_id}_5' id='{$field_id}_5_label' {$sub_label_class_attribute}>{$address_zip_sub_label}</label>
                                    <input type='text' name='input_{$id}.5' id='{$field_id}_5' value='{$zip_value}' {$tabindex} {$disabled_text} {$zip_placeholder_attribute} {$required_attribute}/>
                                </span>";
				} else {
					$zip = "<span class='ginput_{$zip_location}{$class_suffix} address_zip' id='{$field_id}_5_container' {$style}>
                                    <input type='text' name='input_{$id}.5' id='{$field_id}_5' value='{$zip_value}' {$tabindex} {$disabled_text} {$zip_placeholder_attribute} {$required_attribute}/>
                                    <label for='{$field_id}_5' id='{$field_id}_5_label' {$sub_label_class_attribute}>{$address_zip_sub_label}</label>
                                </span>";
				}
			}
		}

		if ( $is_admin || ! $hide_country ) {
			$style    = $hide_country ? "style='display:none;'" : '';
			$tabindex = $this->get_tabindex();
			if ( $is_sub_label_above ) {
				$country = "<span class='ginput_{$country_location}{$class_suffix} address_country' id='{$field_id}_6_container' {$style}>
                                        <label for='{$field_id}_6' id='{$field_id}_6_label' {$sub_label_class_attribute}>{$address_country_sub_label}</label>
                                        <select name='input_{$id}.6' id='{$field_id}_6' {$tabindex} {$disabled_text} {$required_attribute}>{$country_list}</select>
                                    </span>";
			} else {
				$country = "<span class='ginput_{$country_location}{$class_suffix} address_country' id='{$field_id}_6_container' {$style}>
                                        <select name='input_{$id}.6' id='{$field_id}_6' {$tabindex} {$disabled_text} {$required_attribute}>{$country_list}</select>
                                        <label for='{$field_id}_6' id='{$field_id}_6_label' {$sub_label_class_attribute}>{$address_country_sub_label}</label>
                                    </span>";
			}
		} else {
			$country = sprintf( "<input type='hidden' class='gform_hidden' name='input_%d.6' id='%s_6' value='%s'/>", $id, $field_id, $country_value );
		}

		$inputs = $address_display_format == 'zip_before_city' ? $street_address . $street_address2 . $zip . $city . $state . $country : $street_address . $street_address2 . $city . $state . $zip . $country;

		$copy_values_option = '';
		$input_style        = '';
		if ( ( $this->enableCopyValuesOption || $is_form_editor ) && ! $is_entry_detail ) {
			$copy_values_label      = esc_html( $this->copyValuesOptionLabel );
			$copy_values_style      = $is_form_editor && ! $this->enableCopyValuesOption ? "style='display:none;'" : '';
			$copy_values_is_checked = isset( $value[$this->id . '_copy_values_activated'] ) ? $value[$this->id . '_copy_values_activated'] == true : $this->copyValuesOptionDefault == true;
			$copy_values_checked    = checked( true, $copy_values_is_checked, false );
			$copy_values_option     = "<div id='{$field_id}_copy_values_option_container' class='copy_values_option_container' {$copy_values_style}>
                                        <input type='checkbox' id='{$field_id}_copy_values_activated' class='copy_values_activated' value='1' name='input_{$id}_copy_values_activated' {$disabled_text} {$copy_values_checked}/>
                                        <label for='{$field_id}_copy_values_activated' id='{$field_id}_copy_values_option_label' class='copy_values_option_label inline'>{$copy_values_label}</label>
                                    </div>";
			if ( $copy_values_is_checked ) {
				$input_style = "style='display:none;'";
			}
		}

		$css_class = $this->get_css_class();

		return "    {$copy_values_option}
                    <div class='ginput_complex{$class_suffix} ginput_container {$css_class}' id='$field_id' {$input_style}>
                        {$inputs}
                    <div class='gf_clear gf_clear_complex'></div>
                </div>";
	}

	public function get_field_label_class(){
		return 'gfield_label gfield_label_before_complex';
	}

	public function get_css_class() {

		$address_street_field_input  = GFFormsModel::get_input( $this, $this->id . '.1' );
		$address_street2_field_input = GFFormsModel::get_input( $this, $this->id . '.2' );
		$address_city_field_input    = GFFormsModel::get_input( $this, $this->id . '.3' );
		$address_state_field_input   = GFFormsModel::get_input( $this, $this->id . '.4' );
		$address_zip_field_input     = GFFormsModel::get_input( $this, $this->id . '.5' );
		$address_country_field_input = GFFormsModel::get_input( $this, $this->id . '.6' );

		$css_class = '';
		if ( ! rgar( $address_street_field_input, 'isHidden' ) ) {
			$css_class .= 'has_street ';
		}
		if ( ! rgar( $address_street2_field_input, 'isHidden' ) ) {
			$css_class .= 'has_street2 ';
		}
		if ( ! rgar( $address_city_field_input, 'isHidden' ) ) {
			$css_class .= 'has_city ';
		}
		if ( ! rgar( $address_state_field_input, 'isHidden' ) ) {
			$css_class .= 'has_state ';
		}
		if ( ! rgar( $address_zip_field_input, 'isHidden' ) ) {
			$css_class .= 'has_zip ';
		}
		if ( ! rgar( $address_country_field_input, 'isHidden' ) ) {
			$css_class .= 'has_country ';
		}

		$css_class .= 'ginput_container_address';

		return trim( $css_class );
	}

	public function get_address_types( $form_id ) {

		$addressTypes = array(
			'international' => array( 'label'       => esc_html__( 'International', 'gravityforms' ),
			                          'zip_label'   => gf_apply_filters( array( 'gform_address_zip', $form_id ), esc_html__( 'ZIP / Postal Code', 'gravityforms' ), $form_id ),
			                          'state_label' => gf_apply_filters( array( 'gform_address_state', $form_id ), esc_html__( 'State / Province / Region', 'gravityforms' ), $form_id )
			),
			'us'            => array(
				'label'       => esc_html__( 'United States', 'gravityforms' ),
				'zip_label'   => gf_apply_filters( array( 'gform_address_zip', $form_id ), esc_html__( 'ZIP Code', 'gravityforms' ), $form_id ),
				'state_label' => gf_apply_filters( array( 'gform_address_state', $form_id ), esc_html__( 'State', 'gravityforms' ), $form_id ),
				'country'     => 'United States',
				'states'      => array_merge( array( '' ), $this->get_us_states() )
			),
			'canadian'      => array(
				'label'       => esc_html__( 'Canadian', 'gravityforms' ),
				'zip_label'   => gf_apply_filters( array( 'gform_address_zip', $form_id ), esc_html__( 'Postal Code', 'gravityforms' ), $form_id ),
				'state_label' => gf_apply_filters( array( 'gform_address_state', $form_id ), esc_html__( 'Province', 'gravityforms' ), $form_id ),
				'country'     => 'Canada',
				'states'      => array_merge( array( '' ), $this->get_canadian_provinces() )
			)
		);

		/**
		 * Filters the address types available.
		 *
		 * @since Unknown
		 *
		 * @param array $addressTypes Contains the details for existing address types.
		 * @param int   $form_id      The form ID.
		 */
		return gf_apply_filters( array( 'gform_address_types', $form_id ), $addressTypes, $form_id );
	}

	/**
	 * Retrieve the default address type for this field.
	 *
	 * @param int $form_id The current form ID.
	 *
	 * @return string
	 */
	public function get_default_address_type( $form_id ) {
		$default_address_type = 'international';

		/**
		 * Allow the default address type to be overridden.
		 *
		 * @param string $default_address_type The default address type of international.
		 */
		$default_address_type = apply_filters( 'gform_default_address_type', $default_address_type, $form_id );

		return apply_filters( 'gform_default_address_type_' . $form_id, $default_address_type, $form_id );
	}

	public function get_state_field( $id, $field_id, $state_value, $disabled_text, $form_id ) {

		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();
		$is_admin        = $is_entry_detail || $is_form_editor;

		$required_attribute     = $this->isRequired ? 'aria-required="true"' : '';

		$state_dropdown_class = $state_text_class = $state_style = $text_style = $state_field_id = '';

		if ( empty( $state_value ) ) {
			$state_value = $this->defaultState;

			// For backwards compatibility (Canadian address type used to store the default state into the defaultProvince property).
			if ( $this->addressType == 'canadian' && ! empty( $this->defaultProvince ) ) {
				$state_value = $this->defaultProvince;
			}
		}

		$address_type        = empty( $this->addressType ) ? $this->get_default_address_type( $form_id ) : $this->addressType;
		$address_types       = $this->get_address_types( $form_id );
		$has_state_drop_down = isset( $address_types[ $address_type ]['states'] ) && is_array( $address_types[ $address_type ]['states'] );

		if ( $is_admin && rgget('view') != 'entry' ) {
			$state_dropdown_class = "class='state_dropdown'";
			$state_text_class     = "class='state_text'";
			$state_style          = ! $has_state_drop_down ? "style='display:none;'" : '';
			$text_style           = $has_state_drop_down ? "style='display:none;'" : '';
			$state_field_id       = '';
		} else {
			// ID only displayed on front end.
			$state_field_id = "id='" . $field_id . "_4'";
		}

		$tabindex         = $this->get_tabindex();
		$state_input      = GFFormsModel::get_input( $this, $this->id . '.4' );
		$sate_placeholder = GFCommon::get_input_placeholder_value( $state_input );
		$states           = empty( $address_types[ $address_type ]['states'] ) ? array() : $address_types[ $address_type ]['states'];
		$state_dropdown   = sprintf( "<select name='input_%d.4' %s {$tabindex} %s {$state_dropdown_class} {$state_style} {$required_attribute}>%s</select>", $id, $state_field_id, $disabled_text, $this->get_state_dropdown( $states, $state_value, $sate_placeholder ) );

		$tabindex                    = $this->get_tabindex();
		$state_placeholder_attribute = GFCommon::get_input_placeholder_attribute( $state_input );
		$state_text                  = sprintf( "<input type='text' name='input_%d.4' %s value='%s' {$tabindex} %s {$state_text_class} {$text_style} {$state_placeholder_attribute} {$required_attribute}/>", $id, $state_field_id, $state_value, $disabled_text );

		if ( $is_admin && rgget('view') != 'entry' ) {
			return $state_dropdown . $state_text;
		} elseif ( $has_state_drop_down ) {
			return $state_dropdown;
		} else {
			return $state_text;
		}
	}

	/**
	 * Returns a list of countries.
	 *
	 * @since Unknown
	 * @since 2.4     Updated to use ISO 3166-1 list of countries.
	 * @since 2.4.20  Updated to use GF_Field_Address::get_default_countries() and to sort the countries.
	 *
	 * @return array
	 */
	public function get_countries() {

		$countries = array_values( $this->get_default_countries() );
		sort( $countries );

		/**
		 * A list of countries displayed in the Address field country drop down.
		 *
		 * @since Unknown
		 *
		 * @param array $countries ISO 3166-1 list of countries.
		 */
		return apply_filters( 'gform_countries', $countries );

	}

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

	/**
	 * Returns the ISO 3166-1 alpha-2 code for the supplied country name.
	 *
	 * @since Unknown
	 *
	 * @param string $country_name The country name.
	 *
	 * @return string|null
	 */
	public function get_country_code( $country_name ) {
		$codes = $this->get_country_codes();

		return rgar( $codes, GFCommon::safe_strtoupper( $country_name ) );
	}

	/**
	 * Returns the default countries array updated to use the uppercase country name as the key to the ISO 3166-1 alpha-2 code.
	 *
	 * @since Unknown
	 * @since 2.4     Updated to use ISO 3166-1 list of countries.
	 * @since 2.4.20  Updated to use GF_Field_Address::get_default_countries().
	 *
	 * @return array
	 */
	public function get_country_codes() {
		$countries = array_map( array( 'GFCommon', 'safe_strtoupper' ), $this->get_default_countries() );

		return array_flip( $countries );
	}

	public function get_us_states() {
		return apply_filters(
			'gform_us_states', array(
				__( 'Alabama', 'gravityforms' ),
				__( 'Alaska', 'gravityforms' ),
				__( 'Arizona', 'gravityforms' ),
				__( 'Arkansas', 'gravityforms' ),
				__( 'California', 'gravityforms' ),
				__( 'Colorado', 'gravityforms' ),
				__( 'Connecticut', 'gravityforms' ),
				__( 'Delaware', 'gravityforms' ),
				__( 'District of Columbia', 'gravityforms' ),
				__( 'Florida', 'gravityforms' ),
				_x( 'Georgia', 'US State', 'gravityforms' ),
				__( 'Hawaii', 'gravityforms' ),
				__( 'Idaho', 'gravityforms' ),
				__( 'Illinois', 'gravityforms' ),
				__( 'Indiana', 'gravityforms' ),
				__( 'Iowa', 'gravityforms' ),
				__( 'Kansas', 'gravityforms' ),
				__( 'Kentucky', 'gravityforms' ),
				__( 'Louisiana', 'gravityforms' ),
				__( 'Maine', 'gravityforms' ),
				__( 'Maryland', 'gravityforms' ),
				__( 'Massachusetts', 'gravityforms' ),
				__( 'Michigan', 'gravityforms' ),
				__( 'Minnesota', 'gravityforms' ),
				__( 'Mississippi', 'gravityforms' ),
				__( 'Missouri', 'gravityforms' ),
				__( 'Montana', 'gravityforms' ),
				__( 'Nebraska', 'gravityforms' ),
				__( 'Nevada', 'gravityforms' ),
				__( 'New Hampshire', 'gravityforms' ),
				__( 'New Jersey', 'gravityforms' ),
				__( 'New Mexico', 'gravityforms' ),
				__( 'New York', 'gravityforms' ),
				__( 'North Carolina', 'gravityforms' ),
				__( 'North Dakota', 'gravityforms' ),
				__( 'Ohio', 'gravityforms' ),
				__( 'Oklahoma', 'gravityforms' ),
				__( 'Oregon', 'gravityforms' ),
				__( 'Pennsylvania', 'gravityforms' ),
				__( 'Rhode Island', 'gravityforms' ),
				__( 'South Carolina', 'gravityforms' ),
				__( 'South Dakota', 'gravityforms' ),
				__( 'Tennessee', 'gravityforms' ),
				__( 'Texas', 'gravityforms' ),
				__( 'Utah', 'gravityforms' ),
				__( 'Vermont', 'gravityforms' ),
				__( 'Virginia', 'gravityforms' ),
				__( 'Washington', 'gravityforms' ),
				__( 'West Virginia', 'gravityforms' ),
				__( 'Wisconsin', 'gravityforms' ),
				__( 'Wyoming', 'gravityforms' ),
				__( 'Armed Forces Americas', 'gravityforms' ),
				__( 'Armed Forces Europe', 'gravityforms' ),
				__( 'Armed Forces Pacific', 'gravityforms' ),
			)
		);
	}

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

		$state_name = GFCommon::safe_strtoupper( $state_name );
		$code       = isset( $states[ $state_name ] ) ? $states[ $state_name ] : $state_name;

		return $code;
	}

	public function get_canadian_provinces() {
		return array(
			__( 'Alberta', 'gravityforms' ),
			__( 'British Columbia', 'gravityforms' ),
			__( 'Manitoba', 'gravityforms' ),
			__( 'New Brunswick', 'gravityforms' ),
			__( 'Newfoundland and Labrador', 'gravityforms' ),
			__( 'Northwest Territories', 'gravityforms' ),
			__( 'Nova Scotia', 'gravityforms' ),
			__( 'Nunavut', 'gravityforms' ),
			__( 'Ontario', 'gravityforms' ),
			__( 'Prince Edward Island', 'gravityforms' ),
			__( 'Quebec', 'gravityforms' ),
			__( 'Saskatchewan', 'gravityforms' ),
			__( 'Yukon', 'gravityforms' )
		);
	}

	public function get_state_dropdown( $states, $selected_state = '', $placeholder = '' ) {
		$str = '';
		foreach ( $states as $code => $state ) {
			if ( is_array( $state ) ) {
				$str .= sprintf( '<optgroup label="%1$s">%2$s</optgroup>', esc_attr( $code ), $this->get_state_dropdown( $state, $selected_state, $placeholder ) );
			} else {
				if ( is_numeric( $code ) ) {
					$code = $state;
				}
				if ( empty( $state ) ) {
					$state = $placeholder;
				}

				$str .= $this->get_select_option( $code, $state, $selected_state );
			}
		}

		return $str;
	}

	/**
	 * Returns the option tag for the current choice.
	 *
	 * @param string $value The choice value.
	 * @param string $label The choice label.
	 * @param string $selected_value The value for the selected choice.
	 *
	 * @return string
	 */
	public function get_select_option( $value, $label, $selected_value ) {
		$selected = $value == $selected_value ? "selected='selected'" : '';

		return sprintf( "<option value='%s' %s>%s</option>", esc_attr( $value ), $selected, esc_html( $label ) );
	}

	public function get_us_state_dropdown( $selected_state = '' ) {
		$states = array_merge( array( '' ), $this->get_us_states() );
		$str    = '';
		foreach ( $states as $code => $state ) {
			if ( is_numeric( $code ) ) {
				$code = $state;
			}

			$selected = $code == $selected_state ? "selected='selected'" : '';
			$str .= "<option value='" . esc_attr( $code ) . "' $selected>" . esc_html( $state ) . '</option>';
		}

		return $str;
	}

	public function get_canadian_provinces_dropdown( $selected_province = '' ) {
		$states = array_merge( array( '' ), $this->get_canadian_provinces() );
		$str    = '';
		foreach ( $states as $state ) {
			$selected = $state == $selected_province ? "selected='selected'" : '';
			$str .= "<option value='" . esc_attr( $state ) . "' $selected>" . esc_html( $state ) . '</option>';
		}

		return $str;
	}

	public function get_country_dropdown( $selected_country = '', $placeholder = '' ) {
		$str       = '';
		$selected_country = strtolower( $selected_country );
		$countries = array_merge( array( '' ), $this->get_countries() );
		foreach ( $countries as $code => $country ) {
			if ( is_numeric( $code ) ) {
				$code = $country;
			}
			if ( empty( $country ) ) {
				$country = $placeholder;
			}
			$selected = strtolower( esc_attr( $code ) ) == $selected_country ? "selected='selected'" : '';
			$str .= "<option value='" . esc_attr( $code ) . "' $selected>" . esc_html( $country ) . '</option>';
		}

		return $str;
	}

	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
		if ( is_array( $value ) ) {
			$street_value  = trim( rgget( $this->id . '.1', $value ) );
			$street2_value = trim( rgget( $this->id . '.2', $value ) );
			$city_value    = trim( rgget( $this->id . '.3', $value ) );
			$state_value   = trim( rgget( $this->id . '.4', $value ) );
			$zip_value     = trim( rgget( $this->id . '.5', $value ) );
			$country_value = trim( rgget( $this->id . '.6', $value ) );

			if ( $format === 'html' ) {
				$street_value  = esc_html( $street_value );
				$street2_value = esc_html( $street2_value );
				$city_value    = esc_html( $city_value );
				$state_value   = esc_html( $state_value );
				$zip_value     = esc_html( $zip_value );
				$country_value = esc_html( $country_value );

				$line_break = '<br />';
			} else {
				$line_break = "\n";
			}

			/**
			 * Filters the format that the address is displayed in.
			 *
			 * @since Unknown
			 *
			 * @param string           'default' The format to use. Defaults to 'default'.
			 * @param GF_Field_Address $this     An instance of the GF_Field_Address object.
			 */
			$address_display_format = apply_filters( 'gform_address_display_format', 'default', $this );
			if ( $address_display_format == 'zip_before_city' ) {
				/*
                Sample:
                3333 Some Street
                suite 16
                2344 City, State
                Country
                */

				$addr_ary   = array();
				$addr_ary[] = $street_value;

				if ( ! empty( $street2_value ) ) {
					$addr_ary[] = $street2_value;
				}

				$zip_line = trim( $zip_value . ' ' . $city_value );
				$zip_line .= ! empty( $zip_line ) && ! empty( $state_value ) ? ", {$state_value}" : $state_value;
				$zip_line = trim( $zip_line );
				if ( ! empty( $zip_line ) ) {
					$addr_ary[] = $zip_line;
				}

				if ( ! empty( $country_value ) ) {
					$addr_ary[] = $country_value;
				}

				$address = implode( '<br />', $addr_ary );

			} else {
				$address = $street_value;
				$address .= ! empty( $address ) && ! empty( $street2_value ) ? $line_break . $street2_value : $street2_value;
				$address .= ! empty( $address ) && ( ! empty( $city_value ) || ! empty( $state_value ) ) ? $line_break . $city_value : $city_value;
				$address .= ! empty( $address ) && ! empty( $city_value ) && ! empty( $state_value ) ? ", $state_value" : $state_value;
				$address .= ! empty( $address ) && ! empty( $zip_value ) ? " $zip_value" : $zip_value;
				$address .= ! empty( $address ) && ! empty( $country_value ) ? $line_break . $country_value : $country_value;
			}

			// Adding map link.
			/**
			 * Disables the Google Maps link from displaying in the address field.
			 *
			 * @since 1.9
			 *
			 * @param bool false Determines if the map link should be disabled. Set to true to disable. Defaults to false.
			 */
			$map_link_disabled = apply_filters( 'gform_disable_address_map_link', false );
			if ( ! empty( $address ) && $format == 'html' && ! $map_link_disabled ) {
				$address_qs = str_replace( $line_break, ' ', $address ); //replacing <br/> and \n with spaces
				$address_qs = urlencode( $address_qs );
				$address .= "<br/><a href='http://maps.google.com/maps?q={$address_qs}' target='_blank' class='map-it-link'>Map It</a>";
			}

			return $address;
		} else {
			return '';
		}
	}

	public function get_input_property( $input_id, $property_name ) {
		$input = GFFormsModel::get_input( $this, $input_id );

		return rgar( $input, $property_name );
	}

	public function sanitize_settings() {
		parent::sanitize_settings();
		if ( $this->addressType ) {
			$this->addressType = wp_strip_all_tags( $this->addressType );
		}

		if ( $this->defaultCountry ) {
			$this->defaultCountry = wp_strip_all_tags( $this->defaultCountry );
		}

		if ( $this->defaultProvince ) {
			$this->defaultProvince = wp_strip_all_tags( $this->defaultProvince );
		}

		if ( $this->copyValuesOptionLabel ) {
			$this->copyValuesOptionLabel = wp_strip_all_tags( $this->copyValuesOptionLabel );
		}

	}

	public function get_value_export( $entry, $input_id = '', $use_text = false, $is_csv = false ) {
		if ( empty( $input_id ) ) {
			$input_id = $this->id;
		}

		if ( absint( $input_id ) == $input_id ) {
			$street_value  = str_replace( '  ', ' ', trim( rgar( $entry, $input_id . '.1' ) ) );
			$street2_value = str_replace( '  ', ' ', trim( rgar( $entry, $input_id . '.2' ) ) );
			$city_value    = str_replace( '  ', ' ', trim( rgar( $entry, $input_id . '.3' ) ) );
			$state_value   = str_replace( '  ', ' ', trim( rgar( $entry, $input_id . '.4' ) ) );
			$zip_value     = trim( rgar( $entry, $input_id . '.5' ) );
			$country_value = $this->get_country_code( trim( rgar( $entry, $input_id . '.6' ) ) );

			$address = $street_value;
			$address .= ! empty( $address ) && ! empty( $street2_value ) ? "  $street2_value" : $street2_value;
			$address .= ! empty( $address ) && ( ! empty( $city_value ) || ! empty( $state_value ) ) ? ", $city_value," : $city_value;
			$address .= ! empty( $address ) && ! empty( $city_value ) && ! empty( $state_value ) ? "  $state_value" : $state_value;
			$address .= ! empty( $address ) && ! empty( $zip_value ) ? "  $zip_value," : $zip_value;
			$address .= ! empty( $address ) && ! empty( $country_value ) ? "  $country_value" : $country_value;

			return $address;
		} else {

			return rgar( $entry, $input_id );
		}
	}

	/**
	 * Removes the "for" attribute in the field label. Inputs are only allowed one label (a11y) and the inputs already have labels.
	 *
	 * @since  2.4
	 * @access public
	 *
	 * @param array $form The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_first_input_id( $form ) {
		return '';
	}

	// # FIELD FILTER UI HELPERS ---------------------------------------------------------------------------------------

	/**
	 * Returns the sub-filters for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_sub_filters() {
		$sub_filters = array();
		$inputs      = $this->inputs;

		foreach ( $inputs as $input ) {
			if ( rgar( $input, 'isHidden' ) ) {
				continue;
			}

			$sub_filters[] = array(
				'key'             => rgar( $input, 'id' ),
				'text'            => rgar( $input, 'customLabel', rgar( $input, 'label' ) ),
				'preventMultiple' => false,
				'operators'       => $this->get_filter_operators(),
			);
		}

		return $sub_filters;
	}

	/**
	 * Returns the filter operators for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_operators() {
		$operators   = parent::get_filter_operators();
		$operators[] = 'contains';

		return $operators;
	}
}

GF_Fields::register( new GF_Field_Address() );
class-gf-field-number.php000066600000030172151264036140011334 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_Number extends GF_Field {

	public $type = 'number';

	public function get_form_editor_field_title() {
		return esc_attr__( 'Number', 'gravityforms' );
	}

	function get_form_editor_field_settings() {
		return array(
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'size_setting',
			'number_format_setting',
			'range_setting',
			'rules_setting',
			'visibility_setting',
			'duplicate_setting',
			'default_value_setting',
			'placeholder_setting',
			'description_setting',
			'css_class_setting',
			'calculation_setting',
		);
	}

	public function is_conditional_logic_supported() {
		return true;
	}

	public function get_value_submission( $field_values, $get_from_post_global_var = true ) {

		$value = $this->get_input_value_submission( 'input_' . $this->id, $this->inputName, $field_values, $get_from_post_global_var );

		if ( is_array( $value ) ) {
			$value = array_map( 'trim', $value );
			foreach ( $value  as &$v ) {
				$v = trim( $v );
				$v = $this->clean_value( $v );
			}
		} else {
			$value = trim( $value );
			$value = $this->clean_value( $value );
		}

		return $value;
	}

	/**
	 * Ensures the POST value is in the correct number format.
	 *
	 * @since 2.4
	 *
	 * @param $value
	 *
	 * @return bool|float|string
	 */
	public function clean_value( $value ) {

		if ( $this->numberFormat == 'currency' ) {
			$currency = new RGCurrency( GFCommon::get_currency() );
			$value    = $currency->to_number( $value );
		} elseif ( $this->numberFormat == 'decimal_comma' ) {
			$value = GFCommon::clean_number( $value, 'decimal_comma' );
		} elseif ( $this->numberFormat == 'decimal_dot' ) {
			$value = GFCommon::clean_number( $value, 'decimal_dot' );
		}

		return $value;
	}

	public function validate( $value, $form ) {

		// The POST value has already been converted from currency or decimal_comma to decimal_dot and then cleaned in get_field_value().
		$value = GFCommon::maybe_add_leading_zero( $value );

		// Raw value will be tested against the is_numeric() function to make sure it is in the right format.
		// If the POST value is an array then the field is inside a repeater so use $value.
		$raw_value = isset( $_POST[ 'input_' . $this->id ] ) && ! is_array( $_POST[ 'input_' . $this->id ] ) ? GFCommon::maybe_add_leading_zero( rgpost( 'input_' . $this->id ) ) : $value;

		$requires_valid_number = ! rgblank( $raw_value ) && ! $this->has_calculation();
		$is_valid_number       = $this->validate_range( $value ) && GFCommon::is_numeric( $raw_value, $this->numberFormat );

		if ( $requires_valid_number && ! $is_valid_number ) {
			$this->failed_validation  = true;
			$this->validation_message = empty( $this->errorMessage ) ? $this->get_range_message() : $this->errorMessage;
		} elseif ( $this->type == 'quantity' ) {
			if ( intval( $value ) != $value ) {
				$this->failed_validation  = true;
				$this->validation_message = empty( $field['errorMessage'] ) ? esc_html__( 'Please enter a valid quantity. Quantity cannot contain decimals.', 'gravityforms' ) : $field['errorMessage'];
			} elseif ( ! empty( $value ) && ( ! is_numeric( $value ) || intval( $value ) != floatval( $value ) || intval( $value ) < 0 ) ) {
				$this->failed_validation  = true;
				$this->validation_message = empty( $field['errorMessage'] ) ? esc_html__( 'Please enter a valid quantity', 'gravityforms' ) : $field['errorMessage'];
			}
		}

	}

	/**
	 * Validates the range of the number according to the field settings.
	 *
	 * @param string $value A decimal_dot formatted string
	 *
	 * @return true|false True on valid or false on invalid
	 */
	private function validate_range( $value ) {

		if ( ! GFCommon::is_numeric( $value, 'decimal_dot' ) ) {
			return false;
		}

		$numeric_min = $this->numberFormat == 'decimal_comma' ? GFCommon::clean_number( $this->rangeMin, 'decimal_comma' ) : $this->rangeMin;
		$numeric_max = $this->numberFormat == 'decimal_comma' ? GFCommon::clean_number( $this->rangeMax, 'decimal_comma' ) : $this->rangeMax;

		if ( ( is_numeric( $numeric_min ) && $value < $numeric_min ) ||
		     ( is_numeric( $numeric_max ) && $value > $numeric_max )
		) {
			return false;
		} else {
			return true;
		}
	}

	public function get_range_message() {
		$min     = $this->rangeMin;
		$max     = $this->rangeMax;

		$numeric_min = $min;
		$numeric_max = $max;

		if( $this->numberFormat == 'decimal_comma' ){
			$numeric_min = empty( $min ) ? '' : GFCommon::clean_number( $min, 'decimal_comma', '');
			$numeric_max = empty( $max ) ? '' : GFCommon::clean_number( $max, 'decimal_comma', '');
		}

		$message = '';

		if ( is_numeric( $numeric_min ) && is_numeric( $numeric_max ) ) {
			$message = sprintf( esc_html__( 'Please enter a number from %s to %s.', 'gravityforms' ), "<strong>$min</strong>", "<strong>$max</strong>" );
		} elseif ( is_numeric( $numeric_min ) ) {
			$message = sprintf( esc_html__( 'Please enter a number greater than or equal to %s.', 'gravityforms' ), "<strong>$min</strong>" );
		} elseif ( is_numeric( $numeric_max ) ) {
			$message = sprintf( esc_html__( 'Please enter a number less than or equal to %s.', 'gravityforms' ), "<strong>$max</strong>" );
		} elseif ( $this->failed_validation ) {
			$message = esc_html__( 'Please enter a valid number', 'gravityforms' );
		}

		return $message;
	}

	public function get_field_input( $form, $value = '', $entry = null ) {
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$form_id  = $form['id'];
		$id       = intval( $this->id );
		$field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";

		$size          = $this->size;
		$disabled_text = $is_form_editor ? "disabled='disabled'" : '';
		$class_suffix  = $is_entry_detail ? '_admin' : '';
		$class         = $size . $class_suffix;
		$class         = esc_attr( $class );

		$instruction = '';
		$read_only   = '';

		if ( ! $is_entry_detail && ! $is_form_editor ) {

			if ( $this->has_calculation() ) {

				// calculation-enabled fields should be read only
				$read_only = 'readonly="readonly"';

			} else {

				$message          = $this->get_range_message();
				$validation_class = $this->failed_validation ? 'validation_message' : '';

				if ( ! $this->failed_validation && ! empty( $message ) && empty( $this->errorMessage ) ) {
					$instruction = "<div class='instruction $validation_class'>" . $message . '</div>';
				}
			}
		} elseif ( rgget('view') == 'entry' ) {
			$value = GFCommon::format_number( $value, $this->numberFormat, rgar( $entry, 'currency' ) );
		}

		$is_html5        = RGFormsModel::is_html5_enabled();
		$html_input_type = $is_html5 && ! $this->has_calculation() && ( $this->numberFormat != 'currency' && $this->numberFormat != 'decimal_comma' ) ? 'number' : 'text'; // chrome does not allow number fields to have commas, calculations and currency values display numbers with commas
		$step_attr       = $is_html5 ? "step='any'" : '';

		$min = $this->rangeMin;
		$max = $this->rangeMax;

		$min_attr = $is_html5 && is_numeric( $min ) ? "min='{$min}'" : '';
		$max_attr = $is_html5 && is_numeric( $max ) ? "max='{$max}'" : '';

		$include_thousands_sep = apply_filters( 'gform_include_thousands_sep_pre_format_number', $html_input_type == 'text', $this );
		$value                 = GFCommon::format_number( $value, $this->numberFormat, rgar( $entry, 'currency' ), $include_thousands_sep );

		$placeholder_attribute = $this->get_field_placeholder_attribute();
		$required_attribute    = $this->isRequired ? 'aria-required="true"' : '';
		$invalid_attribute     = $this->failed_validation ? 'aria-invalid="true"' : 'aria-invalid="false"';
		$aria_describedby      = $this->get_aria_describedby();

		$tabindex = $this->get_tabindex();

		$input = sprintf( "<div class='ginput_container ginput_container_number'><input name='input_%d' id='%s' type='{$html_input_type}' {$step_attr} {$min_attr} {$max_attr} value='%s' class='%s' {$tabindex} {$read_only} %s %s %s %s %s/>%s</div>", $id, $field_id, esc_attr( $value ), esc_attr( $class ), $disabled_text, $placeholder_attribute, $required_attribute, $invalid_attribute, $aria_describedby, $instruction );
		return $input;
	}

	public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) {
		$include_thousands_sep = apply_filters( 'gform_include_thousands_sep_pre_format_number', true, $this );

		return GFCommon::format_number( $value, $this->numberFormat, rgar( $entry, 'currency' ), $include_thousands_sep );
	}

	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
		$include_thousands_sep = apply_filters( 'gform_include_thousands_sep_pre_format_number', $use_text, $this );

		return GFCommon::format_number( $value, $this->numberFormat, $currency, $include_thousands_sep );
	}

	/**
	 * Gets merge tag values.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GFCommon::format_number()
	 *
	 * @param array|string $value      The value of the input.
	 * @param string       $input_id   The input ID to use.
	 * @param array        $entry      The Entry Object.
	 * @param array        $form       The Form Object
	 * @param string       $modifier   The modifier passed.
	 * @param array|string $raw_value  The raw value of the input.
	 * @param bool         $url_encode If the result should be URL encoded.
	 * @param bool         $esc_html   If the HTML should be escaped.
	 * @param string       $format     The format that the value should be.
	 * @param bool         $nl2br      If the nl2br function should be used.
	 *
	 * @return string The processed merge tag.
	 */
	public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) {
		$include_thousands_sep = ! in_array( 'value', $this->get_modifiers() );

		/**
		 * Filters if the thousands separator should be used when displaying the a number field result.
		 *
		 * @since 1.9.5
		 *
		 * @param bool            $include_thousands_sep If the modifier passed in the merge tag is not 'value', false. Otherwise, true.
		 * @param GF_Field_Number $this                  An instance of this class.
		 */
		$include_thousands_sep = apply_filters( 'gform_include_thousands_sep_pre_format_number', $include_thousands_sep, $this );

		$formatted_value = GFCommon::format_number( $value, $this->numberFormat, rgar( $entry, 'currency' ), $include_thousands_sep );

		return $url_encode ? urlencode( $formatted_value ) : $formatted_value;
	}

	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {
		if ( $this->has_calculation() ) {
			if ( empty( $lead ) ) {
				$lead = GFFormsModel::get_lead( $lead_id );
			}

			$value = GFCommon::calculate( $this, $form, $lead );

			if ( $this->numberFormat !== 'currency' ) {
				$value = GFCommon::round_number( $value, $this->calculationRounding );
			}

			// Return the value as a string when it is zero and a calc so that the "==" comparison done when checking if the field has changed isn't treated as false.
			if ( $value == 0 ) {
				$value = '0';
			}
		} else {
			$value = $this->clean_number( GFCommon::maybe_add_leading_zero( $value ) );
		}

		return $this->sanitize_entry_value( $value, $form['id'] );
	}

	public function sanitize_settings() {
		parent::sanitize_settings();
		$this->enableCalculation = (bool) $this->enableCalculation;

		if ( ! in_array( $this->numberFormat, array( 'currency', 'decimal_comma', 'decimal_dot' ) ) ) {
			$this->numberFormat = GFCommon::is_currency_decimal_dot() ? 'decimal_dot' : 'decimal_comma';
		}

		$this->rangeMin = $this->clean_number( $this->rangeMin );
		$this->rangeMax = $this->clean_number( $this->rangeMax );

		if ( $this->numberFormat == 'decimal_comma' ) {
			$this->rangeMin = GFCommon::format_number( $this->rangeMin, 'decimal_comma' );
			$this->rangeMax = GFCommon::format_number( $this->rangeMax, 'decimal_comma' );
		}
	}

	public function clean_number( $value ) {

		if ( $this->numberFormat == 'currency' ) {
			return GFCommon::to_number( $value );
		} else {
			return GFCommon::clean_number( $value, $this->numberFormat );
		}
	}
}

GF_Fields::register( new GF_Field_Number() );
class-gf-field-checkbox.php000066600000057517151264036140011646 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}

class GF_Field_Checkbox extends GF_Field {

	/**
	 * @var string $type The field type.
	 */
	public $type = 'checkbox';





	// # FORM EDITOR & FIELD MARKUP -------------------------------------------------------------------------------------

	/**
	 * Returns the field title.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @return string
	 */
	public function get_form_editor_field_title() {

		return esc_attr__( 'Checkboxes', 'gravityforms' );

	}

	/**
	 * The class names of the settings which should be available on the field in the form editor.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @return array
	 */
	public function get_form_editor_field_settings() {

		return array(
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'error_message_setting',
			'label_setting',
			'label_placement_setting',
			'admin_label_setting',
			'choices_setting',
			'rules_setting',
			'visibility_setting',
			'description_setting',
			'css_class_setting',
			'select_all_choices_setting',
		);

	}

	/**
	 * Indicate if this field type can be used when configuring conditional logic rules.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @return bool
	 */
	public function is_conditional_logic_supported() {

		return true;

	}

	/**
	 * Returns the field inner markup.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param array        $form  The Form Object currently being processed.
	 * @param string|array $value The field value. From default/dynamic population, $_POST, or a resumed incomplete submission.
	 * @param null|array   $entry Null or the Entry Object currently being edited.
	 *
	 * @uses GF_Field::is_entry_detail()
	 * @uses GF_Field::is_form_editor()
	 * @uses GF_Field_Checkbox::get_checkbox_choices()
	 *
	 * @return string
	 */
	public function get_field_input( $form, $value = '', $entry = null ) {

		$form_id         = absint( $form['id'] );
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		$id            = $this->id;
		$field_id      = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id";
		$disabled_text = $is_form_editor ? 'disabled="disabled"' : '';

		return sprintf(
			"<div class='ginput_container ginput_container_checkbox'><ul class='gfield_checkbox' id='%s'>%s</ul></div>",
			esc_attr( $field_id ),
			$this->get_checkbox_choices( $value, $disabled_text, $form_id )
		);

	}





	// # SUBMISSION -----------------------------------------------------------------------------------------------------

	/**
	 * Retrieve the field value on submission.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param array     $field_values             The dynamic population parameter names with their corresponding values to be populated.
	 * @param bool|true $get_from_post_global_var Whether to get the value from the $_POST array as opposed to $field_values.
	 *
	 * @uses GFFormsModel::choice_value_match()
	 * @uses GFFormsModel::get_parameter_value()
	 *
	 * @return array|string
	 */
	public function get_value_submission( $field_values, $get_from_post_global_var = true ) {

		// Get parameter values for field.
		$parameter_values = GFFormsModel::get_parameter_value( $this->inputName, $field_values, $this );

		// If parameter values exist but are not an array, convert to array.
		if ( ! empty( $parameter_values ) && ! is_array( $parameter_values ) ) {
			$parameter_values = explode( ',', $parameter_values );
		}

		// If no inputs are defined, return an empty string.
		if ( ! is_array( $this->inputs ) ) {
			return '';
		}

		// Set initial choice index.
		$choice_index = 0;

		// Initialize submission value array.
		$value = array();

		// Loop through field inputs.
		foreach ( $this->inputs as $input ) {

			if ( ! empty( $_POST[ 'is_submit_' . $this->formId ] ) && $get_from_post_global_var ) {

				$input_value = rgpost( 'input_' . str_replace( '.', '_', strval( $input['id'] ) ) );

				$value[ strval( $input['id'] ) ] = $input_value;

			} else {

				if ( is_array( $parameter_values ) ) {

					foreach ( $parameter_values as $item ) {

						$item = trim( $item );

						if ( GFFormsModel::choice_value_match( $this, $this->choices[ $choice_index ], $item ) ) {
							$value[ $input['id'] . '' ] = $item;
							break;
						}

					}

				}

			}

			// Increase choice index.
			$choice_index ++;

		}

		return $value;

	}





	// # ENTRY RELATED --------------------------------------------------------------------------------------------------

	/**
	 * Format the entry value for display on the entries list page.
	 *
	 * Return a value that's safe to display on the page.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param string|array $value    The field value.
	 * @param array        $entry    The Entry Object currently being processed.
	 * @param string       $field_id The field or input ID currently being processed.
	 * @param array        $columns  The properties for the columns being displayed on the entry list page.
	 * @param array        $form     The Form Object currently being processed.
	 *
	 * @uses GFCommon::implode_non_blank()
	 * @uses GFCommon::prepare_post_category_value()
	 * @uses GFCommon::selection_display()
	 * @uses GF_Field_Checkbox::is_checkbox_checked()
	 *
	 * @return string
	 */
	public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) {

		// If this is the main checkbox field (not an input), display a comma separated list of all inputs.
		if ( absint( $field_id ) == $field_id ) {

			$lead_field_keys = array_keys( $entry );
			$items           = array();

			foreach ( $lead_field_keys as $input_id ) {
				if ( is_numeric( $input_id ) && absint( $input_id ) == $field_id ) {
					$items[] = GFCommon::selection_display( rgar( $entry, $input_id ), null, $entry['currency'], false );
				}
			}

			$value = GFCommon::implode_non_blank( ', ', $items );

			// Special case for post category checkbox fields.
			if ( $this->type == 'post_category' ) {
				$value = GFCommon::prepare_post_category_value( $value, $this, 'entry_list' );
			}

		} else {

			$value = '';

			if ( ! rgblank( $this->is_checkbox_checked( $field_id, $columns[ $field_id ]['label'], $entry ) ) ) {
				$value = "<i class='fa fa-check gf_valid'></i>";
			}

		}

		return $value;

	}

	/**
	 * Format the entry value for display on the entry detail page and for the {all_fields} merge tag.
	 *
	 * Return a value that's safe to display for the context of the given $format.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param string|array $value    The field value.
	 * @param string       $currency The entry currency code.
	 * @param bool|false   $use_text When processing choice based fields should the choice text be returned instead of the value.
	 * @param string       $format   The format requested for the location the merge is being used. Possible values: html, text or url.
	 * @param string       $media    The location where the value will be displayed. Possible values: screen or email.
	 *
	 * @uses GFCommon::selection_display()
	 *
	 * @return string
	 */
	public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {

		if ( is_array( $value ) ) {

			$items = '';

			foreach ( $value as $key => $item ) {
				if ( ! rgblank( $item ) ) {
					switch ( $format ) {
						case 'text' :
							$items .= GFCommon::selection_display( $item, $this, $currency, $use_text ) . ', ';
							break;

						default:
							$items .= '<li>' . wp_kses_post( GFCommon::selection_display( $item, $this, $currency, $use_text ) ) . '</li>';
							break;
					}
				}
			}

			if ( empty( $items ) ) {
				return '';
			} elseif ( $format == 'text' ) {
				return substr( $items, 0, strlen( $items ) - 2 ); // Removing last comma.
			} else {
				return "<ul class='bulleted'>$items</ul>";
			}

		} else {

			return $value;

		}

	}

	/**
	 * Gets merge tag values.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GFCommon::to_money()
	 * @uses GFCommon::format_post_category()
	 * @uses GFFormsModel::is_field_hidden()
	 * @uses GFFormsModel::get_choice_text()
	 * @uses GFCommon::format_variable_value()
	 * @uses GFCommon::implode_non_blank()
	 *
	 * @param array|string $value      The value of the input.
	 * @param string       $input_id   The input ID to use.
	 * @param array        $entry      The Entry Object.
	 * @param array        $form       The Form Object
	 * @param string       $modifier   The modifier passed.
	 * @param array|string $raw_value  The raw value of the input.
	 * @param bool         $url_encode If the result should be URL encoded.
	 * @param bool         $esc_html   If the HTML should be escaped.
	 * @param string       $format     The format that the value should be.
	 * @param bool         $nl2br      If the nl2br function should be used.
	 *
	 * @uses GFCommon::format_post_category()
	 * @uses GFCommon::format_variable_value()
	 * @uses GFCommon::implode_non_blank()
	 * @uses GFCommon::to_money()
	 * @uses GFFormsModel::is_field_hidden()
	 *
	 * @return string The processed merge tag.
	 */
	public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) {

		// Check for passed modifiers.
		$use_value       = $modifier == 'value';
		$use_price       = in_array( $modifier, array( 'price', 'currency' ) );
		$format_currency = $modifier == 'currency';

		if ( is_array( $raw_value ) && (string) intval( $input_id ) != $input_id ) {
			$items = array( $input_id => $value ); // Float input IDs. (i.e. 4.1 ). Used when targeting specific checkbox items.
		} elseif ( is_array( $raw_value ) ) {
			$items = $raw_value;
		} else {
			$items = array( $input_id => $raw_value );
		}

		$ary = array();

		// Get the items available within the merge tags.
		foreach ( $items as $input_id => $item ) {

			// If the 'value' modifier was passed.
			if ( $use_value ) {

				list( $val, $price ) = rgexplode( '|', $item, 2 );

			// If the 'price' or 'currency' modifiers were passed.
			} elseif ( $use_price ) {

				list( $name, $val ) = rgexplode( '|', $item, 2 );

				if ( $format_currency ) {
					$val = GFCommon::to_money( $val, rgar( $entry, 'currency' ) );
				}

			// If this is a post category checkbox.
			} else if ( $this->type == 'post_category' ) {

				$use_id     = strtolower( $modifier ) == 'id';
				$item_value = GFCommon::format_post_category( $item, $use_id );

				$val = GFFormsModel::is_field_hidden( $form, $this, array(), $entry ) ? '' : $item_value;

			// If no modifiers were passed.
			} else {

				$val = GFFormsModel::is_field_hidden( $form, $this, array(), $entry ) ? '' : RGFormsModel::get_choice_text( $this, $raw_value, $input_id );

			}

			$ary[] = GFCommon::format_variable_value( $val, $url_encode, $esc_html, $format );

		}

		return GFCommon::implode_non_blank( ', ', $ary );

	}

	/**
	 * Sanitize and format the value before it is saved to the Entry Object.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param string $value      The value to be saved.
	 * @param array  $form       The Form Object currently being processed.
	 * @param string $input_name The input name used when accessing the $_POST.
	 * @param int    $lead_id    The ID of the Entry currently being processed.
	 * @param array  $lead       The Entry Object currently being processed.
	 *
	 * @uses GF_Field_Checkbox::sanitize_entry_value()
	 *
	 * @return array|string The safe value.
	 */
	public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) {

		if ( rgblank( $value ) ) {

			return '';

		} elseif ( is_array( $value ) ) {

			foreach ( $value as &$v ) {

				if ( is_array( $v ) ) {
					$v = '';
				}

				$v = $this->sanitize_entry_value( $v, $form['id'] );

			}

			return implode( ',', $value );

		} else {

			return $this->sanitize_entry_value( $value, $form['id'] );

		}

	}

	/**
	 * Format the entry value before it is used in entry exports and by framework add-ons using GFAddOn::get_field_value().
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param array      $entry    The entry currently being processed.
	 * @param string     $input_id The field or input ID.
	 * @param bool|false $use_text When processing choice based fields should the choice text be returned instead of the value.
	 * @param bool|false $is_csv   Is the value going to be used in the .csv entries export?
	 *
	 * @uses GFCommon::get_label()
	 * @uses GFCommon::selection_display()
	 * @uses GF_Field_Checkbox::is_checkbox_checked()
	 *
	 * @return string
	 */
	public function get_value_export( $entry, $input_id = '', $use_text = false, $is_csv = false ) {

		if ( empty( $input_id ) || absint( $input_id ) == $input_id ) {

			$selected = array();

			foreach ( $this->inputs as $input ) {

				$index = (string) $input['id'];

				if ( ! rgempty( $index, $entry ) ) {
					$selected[] = GFCommon::selection_display( rgar( $entry, $index ), $this, rgar( $entry, 'currency' ), $use_text );
				}

			}

			return implode( ', ', $selected );

		} else if ( $is_csv ) {

			$value = $this->is_checkbox_checked( $input_id, GFCommon::get_label( $this, $input_id ), $entry );

			return empty( $value ) ? '' : $value;

		} else {

			return GFCommon::selection_display( rgar( $entry, $input_id ), $this, rgar( $entry, 'currency' ), $use_text );

		}

	}





	// # INPUT ATTRIBUTE HELPERS ----------------------------------------------------------------------------------------

	/**
	 * Get checkbox choice inputs for field.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param string|array $value         The field value. From default/dynamic population, $_POST, or a resumed incomplete submission.
	 * @param string       $disabled_text The HTML disabled attribute.
	 * @param int          $form_id       The current form ID.
	 *
	 * @uses GFCommon::to_number()
	 * @uses GF_Field::get_conditional_logic_event()
	 * @uses GF_Field::get_tabindex()
	 * @uses GF_Field::is_entry_detail()
	 * @uses GF_Field::is_form_editor()
	 * @uses GFFormsModel::choice_value_match()
	 *
	 * @return string
	 */
	public function get_checkbox_choices( $value, $disabled_text, $form_id = 0 ) {

		$choices = '';
		$is_entry_detail = $this->is_entry_detail();
		$is_form_editor  = $this->is_form_editor();

		if ( is_array( $this->choices ) ) {

			$choice_number = 1;
			$count         = 1;

			// Add Select All choice.
			if ( $this->enableSelectAll ) {

				/**
				 * Modify the "Select All" checkbox label.
				 *
				 * @since 2.3
				 *
				 * @param string $select_label The "Select All" label.
				 * @param object $field        The field currently being processed.
				 */
				$select_label = gf_apply_filters( array( 'gform_checkbox_select_all_label', $this->formId, $this->id ), esc_html__( 'Select All', 'gravityforms' ), $this );
				$select_label = esc_html( $select_label );
				
				/**
				 * Modify the "Deselect All" checkbox label.
				 *
				 * @since 2.3
				 *
				 * @param string $deselect_label The "Deselect All" label.
				 * @param object $field          The field currently being processed.
				 */
				$deselect_label = gf_apply_filters( array( 'gform_checkbox_deselect_all_label', $this->formId, $this->id ), esc_html__( 'Deselect All', 'gravityforms' ), $this );
				$deselect_label = esc_html( $deselect_label );

				// Get tabindex.
				$tabindex = $this->get_tabindex();

				// Prepare choice ID.
				$id = 'choice_' . $this->id . '_select_all';

				// Prepare choice markup.
				$choice_markup = "<li class='gchoice_select_all'>
						<input type='checkbox' id='{$id}' {$tabindex} {$disabled_text} onclick='gformToggleCheckboxes( this )' onkeypress='gformToggleCheckboxes( this )' />
						<label for='{$id}' id='label_" . $this->id . "_select_all' data-label-select='{$select_label}' data-label-deselect='{$deselect_label}'>{$select_label}</label>
					</li>";

				/**
				 * Override the default choice markup used when rendering radio button, checkbox and drop down type fields.
				 *
				 * @since 1.9.6
				 *
				 * @param string $choice_markup The string containing the choice markup to be filtered.
				 * @param array  $choice        An associative array containing the choice properties.
				 * @param object $field         The field currently being processed.
				 * @param string $value         The value to be selected if the field is being populated.
				 */
				$choices .= gf_apply_filters( array( 'gform_field_choice_markup_pre_render', $this->formId, $this->id ), $choice_markup, array(), $this, $value );

			}

			// Loop through field choices.
			foreach ( $this->choices as $choice ) {

				// Hack to skip numbers ending in 0, so that 5.1 doesn't conflict with 5.10.
				if ( $choice_number % 10 == 0 ) {
					$choice_number ++;
				}

				// Prepare input ID.
				$input_id = $this->id . '.' . $choice_number;

				if ( $is_entry_detail || $is_form_editor || $form_id == 0 ) {
					$id = $this->id . '_' . $choice_number ++;
				} else {
					$id = $form_id . '_' . $this->id . '_' . $choice_number ++;
				}

				if ( ( $is_form_editor || ( ! isset( $_GET['gf_token'] ) && empty( $_POST ) ) ) && rgar( $choice, 'isSelected' ) ) {
					$checked = "checked='checked'";
				} elseif ( is_array( $value ) && GFFormsModel::choice_value_match( $this, $choice, rgget( $input_id, $value ) ) ) {
					$checked = "checked='checked'";
				} elseif ( ! is_array( $value ) && GFFormsModel::choice_value_match( $this, $choice, $value ) ) {
					$checked = "checked='checked'";
				} else {
					$checked = '';
				}

				$tabindex     = $this->get_tabindex();
				$choice_value = $choice['value'];

				if ( $this->enablePrice ) {
					$price = rgempty( 'price', $choice ) ? 0 : GFCommon::to_number( rgar( $choice, 'price' ) );
					$choice_value .= '|' . $price;
				}

				$choice_value  = esc_attr( $choice_value );
				$choice_markup = "<li class='gchoice_{$id}'>
								<input name='input_{$input_id}' type='checkbox'  value='{$choice_value}' {$checked} id='choice_{$id}' {$tabindex} {$disabled_text} />
								<label for='choice_{$id}' id='label_{$id}'>{$choice['text']}</label>
							</li>";

				/**
				 * Override the default choice markup used when rendering radio button, checkbox and drop down type fields.
				 *
				 * @since 1.9.6
				 *
				 * @param string $choice_markup The string containing the choice markup to be filtered.
				 * @param array  $choice        An associative array containing the choice properties.
				 * @param object $field         The field currently being processed.
				 * @param string $value         The value to be selected if the field is being populated.
				 */
				$choices .= gf_apply_filters( array( 'gform_field_choice_markup_pre_render', $this->formId, $this->id ), $choice_markup, $choice, $this, $value );

				$is_admin = $is_entry_detail || $is_form_editor;

				if ( $is_admin && rgget('view') != 'entry' && $count >= 5 ) {
					break;
				}

				$count ++;

			}

			$total = sizeof( $this->choices );

			if ( $count < $total ) {
				$choices .= "<li class='gchoice_total'>" . sprintf( esc_html__( '%d of %d items shown. Edit field to view all', 'gravityforms' ), $count, $total ) . '</li>';
			}

		}

		/**
		 * Modify the checkbox items before they are added to the checkbox list.
		 *
		 * @since Unknown
		 *
		 * @param string $choices The string containing the choices to be filtered.
		 * @param object $field   Ahe field currently being processed.
		 */
		return gf_apply_filters( array( 'gform_field_choices', $this->formId, $this->id ), $choices, $this );

	}

	/**
	 * Determine if a specific checkbox is checked.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param int    $field_id    Field ID.
	 * @param string $field_label Field label.
	 * @param array  $entry       Entry object.
	 *
	 * @return bool
	 */
	public function is_checkbox_checked( $field_id, $field_label, $entry ) {

		$allowed_tags = wp_kses_allowed_html( 'post' );

		$entry_field_keys = array_keys( $entry );

		// Looping through lead detail values trying to find an item identical to the column label. Mark with a tick if found.
		foreach ( $entry_field_keys as $input_id ) {

			// Mark as a tick if input label (from form meta) is equal to submitted value (from lead)
			if ( is_numeric( $input_id ) && absint( $input_id ) == absint( $field_id ) ) {

				$sanitized_value = wp_kses( $entry[ $input_id ], $allowed_tags );
				$sanitized_label = wp_kses( $field_label, $allowed_tags );

				if ( $sanitized_value == $sanitized_label ) {

					return $entry[ $input_id ];

				} else {
	
					if ( $this->enableChoiceValue || $this->enablePrice ) {

						foreach ( $this->choices as $choice ) {

							if ( $choice['value'] == $entry[ $field_id ] ) {

								return $choice['value'];

							} else if ( $this->enablePrice ) {

								$ary   = explode( '|', $entry[ $field_id ] );
								$val   = count( $ary ) > 0 ? $ary[0] : '';
								$price = count( $ary ) > 1 ? $ary[1] : '';

								if ( $val == $choice['value'] ) {
									return $choice['value'];
								}

							}

						}

					}

				}

			}

		}

		return false;

	}





	// # OTHER HELPERS --------------------------------------------------------------------------------------------------

	/**
	 * Returns the input ID to be assigned to the field label for attribute.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param array $form The Form Object currently being processed.
	 *
	 * @return string
	 */
	public function get_first_input_id( $form ) {

		return '';

	}

	/**
	 * Retrieve the field default value.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @uses GFCommon::replace_variables_prepopulate()
	 * @uses GF_Field::is_form_editor()
	 *
	 * @return array|string
	 */
	public function get_value_default() {

		return $this->is_form_editor() ? $this->defaultValue : GFCommon::replace_variables_prepopulate( $this->defaultValue );

	}





	// # SANITIZATION ---------------------------------------------------------------------------------------------------

	/**
	 * If the field should allow html tags to be saved with the entry value. Default is false.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @return bool
	 */
	public function allow_html() {

		return true;

	}

	/**
	 * Forces settings into expected values while saving the form object.
	 *
	 * No escaping should be done at this stage to prevent double escaping on output.
	 *
	 * Currently called only for forms created after version 1.9.6.10.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 */
	public function sanitize_settings() {

		parent::sanitize_settings();

		if ( 'option' === $this->type ) {
			$this->productField = absint( $this->productField );
		}

		if ( 'post_category' === $this->type ) {
			$this->displayAllCategories = (bool) $this->displayAllCategories;
		}

	}

	/**
	 * Strip scripts and some HTML tags.
	 *
	 * @since  Unknown
	 * @access public
	 *
	 * @param string $value   The field value to be processed.
	 * @param int    $form_id The ID of the form currently being processed.
	 *
	 * @uses GF_Field::get_allowable_tags()
	 *
	 * @return string
	 */
	public function sanitize_entry_value( $value, $form_id ) {

		// If the value is an array, return an empty string.
		if ( is_array( $value ) ) {
			return '';
		}

		// Get allowable tags for field value.
		$allowable_tags = $this->get_allowable_tags( $form_id );

		// If allowable tags are defined, strip unallowed tags.
		if ( $allowable_tags !== true ) {
			$value = strip_tags( $value, $allowable_tags );
		}

		// Sanitize value.
		$allowed_protocols = wp_allowed_protocols();
		$value             = wp_kses_no_null( $value, array( 'slash_zero' => 'keep' ) );
		$value             = wp_kses_hook( $value, 'post', $allowed_protocols );
		$value             = wp_kses_split( $value, 'post', $allowed_protocols );
		
		return $value;

	}

	// # FIELD FILTER UI HELPERS ---------------------------------------------------------------------------------------

	/**
	 * Returns the filter operators for the current field.
	 *
	 * @since 2.4
	 *
	 * @return array
	 */
	public function get_filter_operators() {
		return array( 'is' );
	}

}

GF_Fields::register( new GF_Field_Checkbox() );
class-gf-field-shipping.php000066600000001107151264036140011661 0ustar00<?php

if ( ! class_exists( 'GFForms' ) ) {
	die();
}


class GF_Field_Shipping extends GF_Field {

	public $type = 'shipping';

	function get_form_editor_field_settings() {
		return array(
			'shipping_field_type_setting',
			'conditional_logic_field_setting',
			'prepopulate_field_setting',
			'label_setting',
			'admin_label_setting',
			'label_placement_setting',
			'description_setting',
			'css_class_setting',
		);
	}

	public function get_form_editor_field_title() {
		return esc_attr__( 'Shipping', 'gravityforms' );
	}

}

GF_Fields::register( new GF_Field_Shipping() );