get_settings(); if ($settings['enabled'] !== 'yes' || empty($settings['google_api_key'])) { return; } add_filter('woocommerce_checkout_fields', array($this, 'inject_search_field'), 5); add_action('wp_enqueue_scripts', array($this, 'enqueue_assets')); } public function get_settings($raw = false) { $stored = get_option(self::OPTION_KEY, array()); if (!is_array($stored)) { $stored = array(); } $legacy = get_option('om_checkout_settings', array()); if (!is_array($legacy)) { $legacy = array(); } return $raw ? (array_merge(array( 'enabled' => isset($legacy['autocomplete_enabled']) ? (string) $legacy['autocomplete_enabled'] : 'no', 'google_api_key' => isset($legacy['google_api_key']) ? (string) $legacy['google_api_key'] : '', 'language' => isset($legacy['autocomplete_language']) ? (string) $legacy['autocomplete_language'] : 'el', 'country' => isset($legacy['autocomplete_country']) ? (string) $legacy['autocomplete_country'] : 'GR', 'field_label' => isset($legacy['autocomplete_field_label']) ? (string) $legacy['autocomplete_field_label'] : '', 'field_placeholder' => isset($legacy['autocomplete_field_placeholder']) ? (string) $legacy['autocomplete_field_placeholder'] : '', 'hide_for_repeat_visitors' => isset($legacy['hide_for_repeat_visitors']) ? (string) $legacy['hide_for_repeat_visitors'] : 'yes', 'geolocation_enabled' => 'no', ), $stored)) : self::fill_i18n_defaults(array_merge(array( 'enabled' => isset($legacy['autocomplete_enabled']) ? (string) $legacy['autocomplete_enabled'] : 'no', 'google_api_key' => isset($legacy['google_api_key']) ? (string) $legacy['google_api_key'] : '', 'language' => isset($legacy['autocomplete_language']) ? (string) $legacy['autocomplete_language'] : 'el', 'country' => isset($legacy['autocomplete_country']) ? (string) $legacy['autocomplete_country'] : 'GR', 'field_label' => isset($legacy['autocomplete_field_label']) ? (string) $legacy['autocomplete_field_label'] : '', 'field_placeholder' => isset($legacy['autocomplete_field_placeholder']) ? (string) $legacy['autocomplete_field_placeholder'] : '', 'hide_for_repeat_visitors' => isset($legacy['hide_for_repeat_visitors']) ? (string) $legacy['hide_for_repeat_visitors'] : 'yes', 'geolocation_enabled' => 'no', ), $stored)); } private static function i18n_labels() { return array( 'field_label' => __('Address search', 'ai-eshop-optimizer'), 'field_placeholder' => __('Type your address...', 'ai-eshop-optimizer'), ); } private static function fill_i18n_defaults(array $s) { if (!did_action('init')) { return $s; } foreach (self::i18n_labels() as $k => $v) { if (!isset($s[$k]) || '' === trim((string) $s[$k])) { $s[$k] = $v; } } return $s; } public function ajax_save() { AIEO_DMM::verify_ajax_request(); $google_api_key = isset($_POST['google_api_key']) ? sanitize_text_field(wp_unslash($_POST['google_api_key'])) : ''; if ('' === trim(str_replace("\u{2022}", '', $google_api_key))) { $google_api_key = (string) ($this->get_settings(true)['google_api_key'] ?? ''); } $patch = array( 'enabled' => !empty($_POST['enabled']) ? 'yes' : 'no', 'google_api_key' => $google_api_key, 'language' => isset($_POST['language']) ? sanitize_text_field(wp_unslash($_POST['language'])) : 'el', 'country' => isset($_POST['country']) ? strtoupper(sanitize_text_field(wp_unslash($_POST['country']))) : 'GR', 'field_label' => isset($_POST['field_label']) ? sanitize_text_field(wp_unslash($_POST['field_label'])) : '', 'field_placeholder' => isset($_POST['field_placeholder']) ? sanitize_text_field(wp_unslash($_POST['field_placeholder'])) : '', 'hide_for_repeat_visitors' => !empty($_POST['hide_for_repeat_visitors']) ? 'yes' : 'no', 'geolocation_enabled' => !empty($_POST['geolocation_enabled']) ? 'yes' : 'no', ); update_option(self::OPTION_KEY, $patch, false); wp_send_json_success(array('message' => __('Address autocomplete settings saved.', 'ai-eshop-optimizer'))); } public function inject_search_field($fields) { if (!function_exists('is_checkout') || !is_checkout()) { return $fields; } $settings = $this->get_settings(); if ($settings['hide_for_repeat_visitors'] === 'yes' && $this->user_has_full_address()) { return $fields; } $field_def = array( 'type' => 'text', 'label' => (string) $settings['field_label'], 'placeholder' => (string) $settings['field_placeholder'], 'class' => array('form-row-wide', 'om-ai-autocomplete-field'), 'priority' => 49, 'required' => false, 'autocomplete' => 'off', ); foreach (array('billing', 'shipping') as $section) { if (empty($fields[$section]) || !isset($fields[$section][$section . '_address_1'])) { continue; } $rebuilt = array(); foreach ($fields[$section] as $key => $f) { if ($key === $section . '_address_1') { $rebuilt[$section . '_address_search'] = $field_def; } $rebuilt[$key] = $f; } $fields[$section] = $rebuilt; } return $fields; } public function enqueue_assets() { if (!function_exists('is_checkout') || !is_checkout()) { return; } $settings = $this->get_settings(); if ($settings['hide_for_repeat_visitors'] === 'yes' && $this->user_has_full_address()) { return; } $api_key = (string) $settings['google_api_key']; $version = defined('AIEO_VERSION') ? AIEO_VERSION : '1.0'; wp_enqueue_script( 'google-maps-places', 'https://maps.googleapis.com/maps/api/js?key=' . urlencode($api_key) . '&libraries=places&language=' . urlencode((string) $settings['language']) . '&loading=async', array(), AIEO_VERSION , true ); wp_enqueue_style( 'aieo-address-autocomplete', plugins_url('assets/css/aieo-address-autocomplete.css', AIEO_FILE), array(), $version ); wp_enqueue_script( 'aieo-address-autocomplete', plugins_url('assets/js/aieo-address-autocomplete.js', AIEO_FILE), array('jquery', 'google-maps-places'), $version, true ); wp_localize_script('aieo-address-autocomplete', 'omAI', array( 'language' => (string) $settings['language'], 'country' => strtolower((string) $settings['country']), )); } private function user_has_full_address() { if (!is_user_logged_in()) { return false; } $uid = get_current_user_id(); $required = array('billing_address_1', 'billing_city', 'billing_postcode', 'billing_country'); foreach ($required as $key) { if (trim((string) get_user_meta($uid, $key, true)) === '') { return false; } } return true; } public function render_subtab() { $settings = $this->get_settings(); $form_id = 'aieo-card-address-autocomplete-address-field-settings-form'; list($open, $close) = AIEO_DMM_UI::card(array( 'anchor' => 'address-autocomplete-address-field-settings', 'title' => __('Address field settings', 'ai-eshop-optimizer'), 'description' => __('Google Places address autocomplete on the WooCommerce checkout. Customers type the start of their address, Google suggests completions, and the standard WC fields (street, city, postcode, country) are filled in automatically — including the postcode, so table-rate shipping recalculates rates immediately without a manual "calculate shipping" click.', 'ai-eshop-optimizer'), 'footer' => class_exists('\Rooster\Admin\States') ? \Rooster\Admin\States::save_button($form_id) : '', )); echo $open; $switch = static function ($name, $on, $label, $sentence, $help = '') { $id = 'aieo-aac-' . str_replace('_', '-', $name); return \Rooster\Admin\Ui::field_row(array( 'id' => $id, 'label' => $label, 'control' => \Rooster\Admin\Ui::toggle($name, (bool) $on, array('id' => $id, 'hidden' => false, 'label' => $sentence)), 'help' => $help, )); }; $rows = $switch('enabled', $settings['enabled'] === 'yes', __('Enable autocomplete', 'ai-eshop-optimizer'), __('Show the address-search field on checkout', 'ai-eshop-optimizer')); $rows .= \Rooster\Admin\Ui::field_row(array( 'id' => 'aieo-aac-key', 'label' => __('Google API key', 'ai-eshop-optimizer'), 'control' => \Rooster\Admin\Fields::secret('google_api_key', '' !== (string) $settings['google_api_key'], array('id' => 'aieo-aac-key', 'label' => __('Google API key', 'ai-eshop-optimizer'))), 'help' => __('Required. Create at console.cloud.google.com → APIs & Services → Credentials. Enable "Places API" and "Maps JavaScript API" on the same key.', 'ai-eshop-optimizer'), )); $rows .= \Rooster\Admin\Ui::field_row(array( 'id' => 'aieo-aac-language', 'label' => __('Language', 'ai-eshop-optimizer'), 'control' => \Rooster\Admin\Ui::input('language', (string) $settings['language'], array('id' => 'aieo-aac-language', 'short' => true, 'maxlength' => 5)), 'help' => __('ISO code (el, en, …).', 'ai-eshop-optimizer'), )); $rows .= \Rooster\Admin\Ui::field_row(array( 'id' => 'aieo-aac-country', 'label' => __('Country', 'ai-eshop-optimizer'), 'control' => \Rooster\Admin\Ui::input('country', (string) $settings['country'], array('id' => 'aieo-aac-country', 'short' => true, 'maxlength' => 2)), 'help' => __('Restrict suggestions to this 2-letter country code (GR, CY, …).', 'ai-eshop-optimizer'), )); $rows .= \Rooster\Admin\Ui::field_row(array( 'id' => 'aieo-aac-label', 'label' => __('Field label', 'ai-eshop-optimizer'), 'control' => \Rooster\Admin\Ui::input('field_label', (string) $settings['field_label'], array('id' => 'aieo-aac-label', 'medium' => true)), )); $rows .= \Rooster\Admin\Ui::field_row(array( 'id' => 'aieo-aac-placeholder', 'label' => __('Field placeholder', 'ai-eshop-optimizer'), 'control' => \Rooster\Admin\Ui::input('field_placeholder', (string) $settings['field_placeholder'], array('id' => 'aieo-aac-placeholder', 'medium' => true)), )); $rows .= $switch('hide_for_repeat_visitors', $settings['hide_for_repeat_visitors'] === 'yes', __('Hide for repeat visitors', 'ai-eshop-optimizer'), __('Skip the field for logged-in users who already have a complete saved address', 'ai-eshop-optimizer')); $rows .= $switch('geolocation_enabled', $settings['geolocation_enabled'] === 'yes', __('“Use my location” button', 'ai-eshop-optimizer'), __('Show a one-tap geolocation button next to the address field on the block checkout', 'ai-eshop-optimizer'), __('Uses the browser’s Geolocation API to read the customer’s coordinates, then asks Google to reverse-geocode them into a street/city/postcode/state. Requires the same Google API key as autocomplete (Geocoding API must be enabled on it). Works independently of the autocomplete toggle.', 'ai-eshop-optimizer')); echo AIEO_DMM_UI::card_form('aieo_dmm_address_autocomplete_save', $this->get_id(), $form_id, '
' . $rows . '
'); echo $close; } }