` (UI only, no name) enhanced on the front * end by intl-tel-input, paired with a hidden `.sd-input-common` input that carries * the submitted value (kept in sync by the frontend script via iti.getNumber()). * The hidden input is what the shared scalar validator reads, mirroring the * dropdown's hidden-input approach. See assets/build/blocks/phone. * * @since 1.1.1 */ class Phone_Markup extends Base { /** * Whether the visitor's country should be auto-detected. * * @var bool * @since 1.1.1 */ protected $auto_country = true; /** * The default (initial) country code, lowercase 2-letter ISO. * * @var string * @since 1.1.1 */ protected $default_country = ''; /** * Whether the country filter is enabled. * * @var bool * @since 1.1.1 */ protected $enable_country_filter = false; /** * Country filter type ('include' or 'exclude'). * * @var string * @since 1.1.1 */ protected $country_filter_type = 'include'; /** * Country codes to include. * * @var array * @since 1.1.1 */ protected $include_countries = []; /** * Country codes to exclude. * * @var array * @since 1.1.1 */ protected $exclude_countries = []; /** * Initialize the properties based on block attributes. * * @param array $attributes Block attributes. * @since 1.1.1 */ public function __construct( $attributes ) { $this->slug = 'phone'; $this->auto_country = ! isset( $attributes['autoCountry'] ) || ! empty( $attributes['autoCountry'] ); $this->default_country = isset( $attributes['defaultCountry'] ) ? strtolower( sanitize_text_field( Helper::get_string_value( $attributes['defaultCountry'] ) ) ) : ''; $this->enable_country_filter = ! empty( $attributes['enableCountryFilter'] ); $this->country_filter_type = isset( $attributes['countryFilterType'] ) && 'exclude' === $attributes['countryFilterType'] ? 'exclude' : 'include'; $this->include_countries = isset( $attributes['includeCountries'] ) && is_array( $attributes['includeCountries'] ) ? array_map( 'strtolower', array_map( 'sanitize_text_field', $attributes['includeCountries'] ) ) : []; $this->exclude_countries = isset( $attributes['excludeCountries'] ) && is_array( $attributes['excludeCountries'] ) ? array_map( 'strtolower', array_map( 'sanitize_text_field', $attributes['excludeCountries'] ) ) : []; // When auto country is enabled and no explicit default is set, detect the // visitor's country via server-side IP geolocation (ipapi.co). See // get_geo_country() for the why and the caching/rate-limit details. if ( $this->auto_country && '' === $this->default_country ) { $this->default_country = $this->get_geo_country(); } $this->set_properties( $attributes ); $this->set_unique_slug(); $this->set_markup_properties(); } /** * Render phone number markup. * * @since 1.1.1 * @return string */ public function markup() { $classes = $this->get_field_classes( [ 'sd-phone-wrap-block' ] ); $aria_desc = $this->get_aria_describedby(); $data_slug = $this->block_slug ? $this->block_slug : $this->unique_slug; ob_start(); ?>
label_markup ); ?> help_markup ); ?>
aria-describedby="" data-required="data_require_attr ); ?>" aria-required="data_require_attr ); ?>" data-default-country="default_country ); ?>" data-auto-country="auto_country ? 'true' : 'false'; ?>" data-enable-country-filter="enable_country_filter ? 'true' : 'false'; ?>" data-country-filter-type="country_filter_type ); ?>" enable_country_filter && 'include' === $this->country_filter_type && ! empty( $this->include_countries ) ) { ?> data-include-countries='include_countries ) ) ) ); ?>' enable_country_filter && 'exclude' === $this->country_filter_type && ! empty( $this->exclude_countries ) ) { ?> data-exclude-countries='exclude_countries ) ) ) ); ?>' placeholder_attr ); ?> autocomplete="tel" inputmode="tel" />
error_msg_markup ); ?>
= $quota_cap ) { set_transient( $cache_key, 'us', HOUR_IN_SECONDS ); return 'us'; } set_transient( $quota_key, $count + 1, HOUR_IN_SECONDS ); // ipapi.co geolocates the *caller's* IP. Since this request originates from // the WordPress server (not the visitor's browser), pass the visitor's IP // explicitly via /{ip}/json/ — otherwise it returns the datacenter's country. $url = 'https://ipapi.co/' . rawurlencode( $ip ) . '/json/'; $response = wp_remote_get( $url, [ 'timeout' => 3, 'user-agent' => 'SureDonation/' . SUREDONATION_VER . ' (+https://suredonations.com)', ] ); if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { set_transient( $cache_key, 'us', HOUR_IN_SECONDS ); return 'us'; } $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( ! is_array( $body ) || empty( $body['country_code'] ) || ! is_string( $body['country_code'] ) ) { set_transient( $cache_key, 'us', HOUR_IN_SECONDS ); return 'us'; } $country = strtolower( $body['country_code'] ); // Validate the external API response is a valid 2-letter country code. if ( ! preg_match( '/^[a-z]{2}$/', $country ) ) { set_transient( $cache_key, 'us', HOUR_IN_SECONDS ); return 'us'; } set_transient( $cache_key, $country, DAY_IN_SECONDS ); return $country; } }