name = 'acf_city_selector';
$this->label = 'City Selector';
$this->category = esc_attr__( 'Choice', 'acf-city-selector' );
$this->defaults = array(
'show_labels' => 1,
'store_meta' => 0,
'which_fields' => 'all',
'use_select2' => 0,
);
$this->l10n = acfcs_get_js_translations();
$this->settings = $settings;
parent::__construct();
}
function settings( $settings ) {
return $settings;
}
/**
* render_field_settings()
*
* Create extra settings for your field. These are visible when editing a field
*
* @param $field (array) the $field being edited
*/
function render_field_settings( $field ) {
$select_options = array(
1 => esc_attr__( 'Yes', 'acf-city-selector' ),
0 => esc_attr__( 'No', 'acf-city-selector' )
);
acf_render_field_setting( $field, array(
'choices' => $select_options,
'instructions' => esc_html__( 'Show field labels above the dropdown menus', 'acf-city-selector' ),
'label' => esc_html__( 'Show labels', 'acf-city-selector' ),
'layout' => 'horizontal',
'name' => 'show_labels',
'type' => 'radio',
'value' => $field[ 'show_labels' ],
) );
acf_render_field_setting( $field, array(
'choices' => $select_options,
'instructions' => esc_html__( 'Use select2 for dropdowns', 'acf-city-selector' ),
'label' => esc_html__( 'Select2', 'acf-city-selector' ),
'layout' => 'horizontal',
'name' => 'use_select2',
'type' => 'radio',
'value' => $field[ 'use_select2' ],
) );
acf_render_field_setting( $field, array(
'choices' => $select_options,
'instructions' => esc_html__( 'Store location as single meta values', 'acf-city-selector' ),
'label' => esc_html__( 'Store meta', 'acf-city-selector' ),
'layout' => 'horizontal',
'name' => 'store_meta',
'type' => 'radio',
'value' => $field[ 'store_meta' ],
) );
$countries = acfcs_get_countries( true, false, true );
acf_render_field_setting( $field, array(
'choices' => $countries,
'instructions' => esc_html__( 'Select a default country for a new field', 'acf-city-selector' ),
'label' => esc_html__( 'Default country', 'acf-city-selector' ),
'name' => 'default_country',
'type' => 'select',
) );
$default_country_fields = array(
'all' => esc_attr__( 'All fields [default]', 'acf-city-selector' ),
'country_only' => esc_attr__( 'Country only', 'acf-city-selector' ),
'country_state' => esc_attr__( 'Country + State/province', 'acf-city-selector' ),
'country_city' => esc_attr__( 'Country + City', 'acf-city-selector' ),
'state_city' => esc_attr__( 'State/province + City', 'acf-city-selector' ),
);
acf_render_field_setting( $field, array(
'choices' => $default_country_fields,
'instructions' => esc_html__( 'Select which fields are used', 'acf-city-selector' ),
'label' => esc_html__( 'Fields to use', 'acf-city-selector' ),
'name' => 'which_fields',
'type' => 'radio',
) );
}
/**
* render_field()
*
* Create the HTML interface for your field
*
* @param $field (array) the $field being edited
*/
function render_field( $field ) {
$default_country = ( isset( $field[ 'default_country' ] ) && ! empty( $field[ 'default_country' ] ) ) ? $field[ 'default_country' ] : false;
$prefill_cities = [];
$prefill_states = [];
$selected_country = ( isset( $field[ 'value' ][ 'countryCode' ] ) ) ? $field[ 'value' ][ 'countryCode' ] : false;
$selected_state = ( isset( $field[ 'value' ][ 'stateCode' ] ) ) ? $field[ 'value' ][ 'stateCode' ] : false;
$selected_city = ( isset( $field[ 'value' ][ 'cityName' ] ) ) ? $field[ 'value' ][ 'cityName' ] : false;
$show_first = true;
$store_meta = ( isset( $field[ 'store_meta' ] ) ) ? $field[ 'store_meta' ] : false;
$which_fields = ( isset( $field[ 'which_fields' ] ) ) ? $field[ 'which_fields' ] : 'all';
if ( false !== $default_country && false == $selected_country ) {
// New post with default country
if ( in_array( $which_fields, [ 'all', 'country_state', 'state_city' ] ) ) {
$prefill_states = acfcs_get_states( $default_country, $show_first, $field );
}
if ( in_array( $which_fields, [ 'country_city', 'state_city' ] ) ) {
$prefill_cities = acfcs_get_cities( $default_country, false, $field );
}
} elseif ( false !== $selected_country ) {
if ( in_array( $which_fields, [ 'all', 'country_state', 'state_city' ] ) ) {
$prefill_states = acfcs_get_states( $selected_country, $show_first, $field );
}
if ( in_array( $which_fields, [ 'all', 'country_city', 'state_city' ] ) ) {
$prefill_cities = acfcs_get_cities( $selected_country, $selected_state, $field );
}
if ( 'country_city' != $which_fields ) {
if ( false != $selected_country && false != $selected_state ) {
$selected_state = $selected_country . '-' . $selected_state;
}
}
} elseif ( false == $default_country && 'state_city' == $which_fields ) {
// no default country is set, so show warning
$message = esc_html__( "You haven't set a default country, so NO provinces/states and cities will be loaded.", 'acf-city-selector' );
echo sprintf( '
', esc_html( $message ) );
}
$prefill_values = [
'prefill_states' => $prefill_states,
'prefill_cities' => $prefill_cities,
];
if ( 'state_city' != $which_fields ) {
acfcs_render_dropdown( 'country', $field, $selected_country, $prefill_values );
}
if ( 'all' == $which_fields || strpos( $which_fields, 'state' ) !== false ) {
acfcs_render_dropdown( 'state', $field, $selected_state, $prefill_values );
}
if ( 'all' == $which_fields || strpos( $which_fields, 'city' ) !== false ) {
acfcs_render_dropdown( 'city', $field, $selected_city, $prefill_values );
}
if ( ! isset( $field[ 'parent_layout' ] ) && ! isset( $field[ 'parent_repeater' ] ) && $store_meta ) {
acfcs_render_hidden_field( 'store_meta', '1' );
}
}
/**
* input_admin_enqueue_scripts()
*
* This action is called in the admin_enqueue_scripts action on the edit screen where your field is created.
* Use this action to add CSS + JavaScript to assist your render_field() action.
*/
function input_admin_enqueue_scripts() {
$plugin_url = trailingslashit( sprintf( '%s/plugins/acf-city-selector', WP_CONTENT_URL ) );
$plugin_version = get_option( 'acfcs_version' );
wp_register_script( 'acfcs-init', "{$plugin_url}assets/js/init.js", array( 'jquery', 'acf-input' ), $plugin_version, false );
wp_enqueue_script( 'acfcs-init' );
wp_register_script( 'acfcs-process', "{$plugin_url}assets/js/city-selector.js", array( 'jquery', 'acf-input' ), $plugin_version, false );
wp_enqueue_script( 'acfcs-process' );
$all_info = acfcs_get_field_settings();
$js_vars[ 'ajaxurl' ] = admin_url( 'admin-ajax.php' );
$js_vars[ 'default_country' ] = ( isset( $all_info[ 'default_country' ] ) && false != $all_info[ 'default_country' ] ) ? $all_info[ 'default_country' ] : false;
$js_vars[ 'post_id' ] = 0 < get_the_ID() ? (int) get_the_ID() : false;
$js_vars[ 'acfcs_state_nonce' ] = wp_create_nonce( 'acfcs-state-nonce' );
$js_vars[ 'acfcs_city_nonce' ] = wp_create_nonce( 'acfcs-city-nonce' );
$js_vars[ 'show_labels' ] = ( isset( $all_info[ 'show_labels' ] ) ) ? $all_info[ 'show_labels' ] : apply_filters( 'acfcs_show_labels', true );
$js_vars[ 'store_meta' ] = ( isset( $all_info[ 'store_meta' ] ) ) ? $all_info[ 'store_meta' ] : false;
$js_vars[ 'use_select2' ] = ( isset( $all_info[ 'use_select2' ] ) ) ? $all_info[ 'use_select2' ] : false;
$js_vars[ 'which_fields' ] = ( isset( $all_info[ 'which_fields' ] ) ) ? $all_info[ 'which_fields' ] : 'all';
wp_localize_script( 'acfcs-process', 'city_selector_vars', $js_vars );
}
/*
* load_value()
*
* This filter is applied to the $value after it is loaded from the db
* This returns false if no country/state is selected (but empty values are stored)
*
* @param $value (mixed) the value found in the database
* @param $post_id (mixed) the $post_id from which the value was loaded
* @param $field (array) the field array holding all the field options
*
* @return $value
*
*/
function load_value( $value, $post_id, $field ) {
$state_code = false;
$country_code = ( isset( $value[ 'countryCode' ] ) ) ? $value[ 'countryCode' ] : false;
if ( isset( $value[ 'stateCode' ] ) ) {
if ( 3 < strlen( $value[ 'stateCode' ] ) ) {
// $value[ 'stateCode' ] is longer than 3 characters, which starts with xx-
// where xx is the country code
$state_code = substr( $value[ 'stateCode' ], 3 );
} elseif ( 1 <= strlen( $value[ 'stateCode' ] ) ) {
// this is a fallback and is probably never reached
$state_code = $value[ 'stateCode' ];
}
}
if ( strlen( $country_code ) == 2 && false != $state_code ) {
global $wpdb;
$cache_key = 'country_state_' . md5( $country_code . '_' . $state_code );
$cache_group = 'cities_data';
$row = wp_cache_get( $cache_key, $cache_group );
if ( false === $row ) {
$table = $wpdb->prefix . 'cities';
$method = 'get_row';
$row = $wpdb->$method(
$wpdb->prepare(
"SELECT country, state_name FROM %i WHERE country_code = %s AND state_code = %s",
$table,
$country_code,
$state_code
)
);
// If $row is null (no match found), it caches that too to prevent repeated broken lookups (Negative Caching)
wp_cache_set( $cache_key, $row, $cache_group, 2 * HOUR_IN_SECONDS );
}
$value[ 'stateCode' ] = $state_code;
$value[ 'stateName' ] = ( isset( $row->state_name ) ) ? $row->state_name : false;
$value[ 'countryName' ] = ( isset( $row->country ) ) ? $row->country : false;
}
return $value;
}
/*
* update_value()
*
* This filter is applied to the $value before it is saved in the db
* @param $value (mixed) the value found in the database
* @param $post_id (mixed) the $post_id from which the value was loaded
* @param $field (array) the field array holding all the field options
*
* @TODO: DRY
*
* @return $value
*/
function update_value( $value, $post_id, $field ) {
$required = $field[ 'required' ];
if ( 0 == $required ) {
if ( isset( $field[ 'which_fields' ] ) && 'all' == $field[ 'which_fields' ] || ! isset( $field[ 'which_fields' ] ) ) {
// if nothing is selected, set value to false
if ( empty( $value[ 'countryCode' ] ) && empty( $value[ 'stateCode' ] ) && empty( $value[ 'cityName' ] ) ) {
$value = false;
} elseif ( empty( $value[ 'stateCode' ] ) && empty( $value[ 'cityName' ] ) ) {
$value = false;
}
} elseif ( isset( $field[ 'which_fields' ] ) && 'country_only' == $field[ 'which_fields' ] ) {
if ( empty( $value[ 'countryCode' ] ) ) {
$value = false;
}
} elseif ( isset( $field[ 'which_fields' ] ) && 'country_state' == $field[ 'which_fields' ] ) {
if ( empty( $value[ 'countryCode' ] ) || empty( $value[ 'stateCode' ] ) ) {
$value = false;
}
} elseif ( isset( $field[ 'which_fields' ] ) && 'country_city' == $field[ 'which_fields' ] ) {
if ( empty( $value[ 'countryCode' ] ) || empty( $value[ 'cityName' ] ) ) {
$value = false;
}
} elseif ( isset( $field[ 'which_fields' ] ) && 'state_city' == $field[ 'which_fields' ] ) {
if ( isset( $field[ 'default_country' ] ) ) {
$value[ 'countryCode' ] = $field[ 'default_country' ];
}
if ( empty( $value[ 'stateCode' ] ) || empty( $value[ 'cityName' ] ) ) {
$value = false;
}
}
} else {
// field == required
if ( isset( $field[ 'which_fields' ] ) && 'all' == $field[ 'which_fields' ] || ! isset( $field[ 'which_fields' ] ) ) {
// if nothing is selected, set value to false
if ( empty( $value[ 'countryCode' ] ) && empty( $value[ 'stateCode' ] ) && empty( $value[ 'cityName' ] ) ) {
$value = false;
} elseif ( empty( $value[ 'countryCode' ] ) || empty( $value[ 'stateCode' ] ) || empty( $value[ 'cityName' ] ) ) {
$value = false;
}
} elseif ( isset( $field[ 'which_fields' ] ) && 'country_only' == $field[ 'which_fields' ] ) {
if ( empty( $value[ 'countryCode' ] ) ) {
$value = false;
}
} elseif ( isset( $field[ 'which_fields' ] ) && 'country_state' == $field[ 'which_fields' ] ) {
if ( empty( $value[ 'countryCode' ] ) || empty( $value[ 'stateCode' ] ) ) {
$value = false;
}
} elseif ( isset( $field[ 'which_fields' ] ) && 'country_city' == $field[ 'which_fields' ] ) {
if ( empty( $value[ 'countryCode' ] ) || empty( $value[ 'cityName' ] ) ) {
$value = false;
}
} elseif ( isset( $field[ 'which_fields' ] ) && 'state_city' == $field[ 'which_fields' ] ) {
if ( isset( $field[ 'default_country' ] ) ) {
$value[ 'countryCode'] = $field[ 'default_country' ];
}
if ( empty( $value[ 'stateCode' ] ) || empty( $value[ 'cityName' ] ) ) {
$value = false;
}
}
}
if ( ! isset( $field[ 'parent_layout' ] ) && ! isset( $field[ 'parent_repeater' ] ) ) {
do_action( 'acfcs_store_meta', $value, $post_id );
}
return $value;
}
/*
* validate_value()
*
* This filter is used to perform validation on the value prior to saving.
* All values are validated regardless of the field's required setting. This allows you to validate and return
* messages to the user if the value is not correct
*
* @param $valid (boolean) validation status based on the value and the field's required setting
* @param $value (mixed) the $_POST value
* @param $field (array) the field array holding all the field options
* @param $input (string) the corresponding input name for $_POST value
*
* @return $valid
*/
function validate_value( $valid, $value, $field, $input ) {
if ( 1 == $field[ 'required' ] ) {
$nothing = esc_html__( "You didn't select anything.", 'acf-city-selector' );
$no_city = esc_html__( "You didn't select a city.", 'acf-city-selector' );
$no_country = esc_html__( "You didn't select a country.", 'acf-city-selector' );
$no_state = esc_html__( "You didn't select a state.", 'acf-city-selector' );
$no_state_city = esc_html__( "You didn't select a state and city.", 'acf-city-selector' );
if ( 'all' == $field[ 'which_fields' ] ) {
if ( empty( $value[ 'countryCode' ] ) && empty( $value[ 'stateCode' ] ) && empty( $value[ 'cityName' ] ) ) {
$valid = $nothing;
} elseif ( empty( $value[ 'stateCode' ] ) && empty( $value[ 'cityName' ] ) ) {
$valid = $no_state_city;
} elseif ( empty( $value[ 'cityName' ] ) ) {
$valid = $no_city;
}
} elseif ( 'country_only' == $field[ 'which_fields' ] ) {
if ( empty( $value[ 'countryCode' ] ) ) {
$valid = $no_country;
}
} elseif ( 'country_state' == $field[ 'which_fields' ] ) {
if ( empty( $value[ 'countryCode' ] ) && empty( $value[ 'stateCode' ] ) ) {
$valid = $nothing;
} elseif ( empty( $value[ 'stateCode' ] ) ) {
$valid = $no_state;
}
} elseif ( 'country_city' == $field[ 'which_fields' ] ) {
if ( empty( $value[ 'countryCode' ] ) && empty( $value[ 'cityName' ] ) ) {
$valid = $nothing;
} elseif ( empty( $value[ 'cityName' ] ) ) {
$valid = $no_city;
}
} elseif ( 'state_city' == $field[ 'which_fields' ] ) {
if ( empty( $value[ 'stateCode' ] ) && empty( $value[ 'cityName' ] ) ) {
$valid = $nothing;
} elseif ( empty( $value[ 'stateCode' ] ) ) {
$valid = $no_state;
} elseif ( empty( $value[ 'cityName' ] ) ) {
$valid = $no_city;
}
}
}
return $valid;
}
}
new acf_field_city_selector( $this );
}