From bfb1a9c47016550035b3bc31b9a5a78b878860af Mon Sep 17 00:00:00 2001 From: Jan Henckens Date: Sat, 4 Jan 2025 19:42:03 +0100 Subject: [PATCH 1/3] Added google geocoding service Signed-off-by: Jan Henckens --- src/EasyAddressField.php | 46 ++++++++++++++++- src/models/EasyAddressFieldSettingsModel.php | 16 ++++++ src/services/FieldService.php | 2 +- src/services/GeoLocationService.php | 53 +++++++++++++++++++- src/templates/_settings.twig | 25 +++++++++ 5 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 src/models/EasyAddressFieldSettingsModel.php create mode 100644 src/templates/_settings.twig diff --git a/src/EasyAddressField.php b/src/EasyAddressField.php index caa120d..87abbd6 100644 --- a/src/EasyAddressField.php +++ b/src/EasyAddressField.php @@ -6,14 +6,17 @@ namespace studioespresso\easyaddressfield; use Craft; +use craft\base\Model; use craft\base\Plugin; use craft\events\RegisterComponentTypesEvent; use craft\feedme\events\RegisterFeedMeFieldsEvent; +use craft\helpers\UrlHelper; use craft\services\Fields; use craft\web\twig\variables\CraftVariable; use markhuot\CraftQL\Events\GetFieldSchema; use studioespresso\easyaddressfield\fields\EasyAddressFieldFeedMe; use studioespresso\easyaddressfield\fields\EasyAddressFieldField; +use studioespresso\easyaddressfield\models\EasyAddressFieldSettingsModel; use studioespresso\easyaddressfield\services\CountriesService; use studioespresso\easyaddressfield\services\FieldService; use studioespresso\easyaddressfield\services\GeoLocationService; @@ -40,7 +43,7 @@ class EasyAddressField extends Plugin /** * @var bool */ - public bool $hasCpSettings = false; + public bool $hasCpSettings = true; // Public Methods // ========================================================================= @@ -95,6 +98,47 @@ public function init() // Components // ========================================================================= + /** + * Creates and returns the model used to store the plugin’s settings. + * + * @return \craft\base\Model|null + */ + protected function createSettingsModel(): Model + { + return new EasyAddressFieldSettingsModel(); + } + + /** + * @return string + * @throws \yii\base\Exception + * @throws \Twig_Error_Loader + * @throws \RuntimeException + */ + protected function settingsHtml(): string + { + return Craft::$app->getView()->renderTemplate( + 'easy-address-field/_settings', + [ + 'services' => [ + 'nominatim' => 'Nominatim', + 'google' => 'Google Maps', + ], + 'settings' => $this->getSettings(), + ] + ); + } + + /** + * Redirect to settings after install + */ + protected function afterInstall(): void + { + if (!Craft::$app->getRequest()->isConsoleRequest) { + parent::afterInstall(); + Craft::$app->getResponse()->redirect(UrlHelper::cpUrl('settings/plugins/easy-address-field'))->send(); + } + } + public function getField(): FieldService { return $this->field; diff --git a/src/models/EasyAddressFieldSettingsModel.php b/src/models/EasyAddressFieldSettingsModel.php new file mode 100644 index 0000000..6727546 --- /dev/null +++ b/src/models/EasyAddressFieldSettingsModel.php @@ -0,0 +1,16 @@ +field = $field->id; } - if(!ElementHelper::isDraftOrRevision($element) && $field->geoCode) { + if (!ElementHelper::isDraftOrRevision($element) && $field->geoCode) { $value = EasyAddressField::$plugin->geoLocation()->locate($value); } diff --git a/src/services/GeoLocationService.php b/src/services/GeoLocationService.php index 27f2550..9191d2e 100644 --- a/src/services/GeoLocationService.php +++ b/src/services/GeoLocationService.php @@ -2,12 +2,25 @@ namespace studioespresso\easyaddressfield\services; +use Craft; use craft\base\Component; +use craft\helpers\Json; +use GuzzleHttp\Client; use maxh\Nominatim\Nominatim; +use studioespresso\easyaddressfield\EasyAddressField; use studioespresso\easyaddressfield\models\EasyAddressFieldModel; +use yii\base\InvalidConfigException; class GeoLocationService extends Component { + public $settings; + + public function init(): void + { + $this->settings = EasyAddressField::getInstance()->getSettings(); + parent::init(); // TODO: Change the autogenerated stub + } + /** * @param EasyAddressFieldModel $model * @@ -17,13 +30,49 @@ public function locate(EasyAddressFieldModel $model) { try { if (!$model->latitude && !$model->longitude and strlen($model->toString()) >= 2) { - $model = $this->geocodeOSM($model); + if ($this->settings->geoCodingService === 'google') { + $model = $this->geocodeGoogle($model); + } else { + $model = $this->geocodeOSM($model); + } } return $model; } catch (\Throwable $exception) { - \Craft::error($exception->getMessage()); + \Craft::error($exception->getMessage(), 'easy-address-field'); + return $model; + } + } + + private function geocodeGoogle(EasyAddressFieldModel $model) + { + if (!$this->settings->googleApiKey) { return $model; } + + if (!$model->latitude && !$model->longitude and strlen($model->toString()) >= 2) { + $client = new Client(['base_uri' => 'https://maps.googleapis.com']); + $request = $client->request('GET', + 'maps/api/geocode/json?address=' . urlencode($model->toString()) . '&key=' . Craft::parseEnv($this->settings->googleApiKey) . '', + ['allow_redirects' => false] + ); + $json = Json::decodeIfJson($request->getBody()->getContents()); + + if ($json['status'] !== 'OK' && $json['error_message']) { + if (Craft::$app->getConfig()->general->devMode) { + throw new InvalidConfigException('Google API error: ' . $json['error_message']); + } + Craft::error($json['error_message'], 'easy-address-field'); + } + + if ($json['status'] === 'OK') { + if ($json['results'][0]['geometry']['location']) { + $model->latitude = $json['results'][0]['geometry']['location']['lat']; + $model->longitude = $json['results'][0]['geometry']['location']['lng']; + } + } + } + + return $model; } private function geocodeOSM(EasyAddressFieldModel $model) diff --git a/src/templates/_settings.twig b/src/templates/_settings.twig new file mode 100644 index 0000000..53c11fd --- /dev/null +++ b/src/templates/_settings.twig @@ -0,0 +1,25 @@ +{% import "_includes/forms" as forms %} + +{{ forms.selectField({ + label: 'Geocoding service'|t, + instructions: 'The geocoding service used, defaults to Nomanatim', + id: 'geoCodingService', + name: 'geoCodingService', + value: settings['geoCodingService'], + options: services, + errors: settings.getErrors('geoCodingService'), +}) }} + + + +{{ forms.autosuggestField({ + label: 'Google API Key for geocoding requests'|t, + instructions: 'Only needed if you are using the geocoding functions in your templates'|t, + id: 'googleApiKey', + name: 'googleApiKey', + value: settings['googleApiKey'], + errors: settings.getErrors('googleApiKey'), + suggestEnvVars: true, + suggestAliases: true, +}) }} + From 97415b85491fd63fb4c778459b742a30f2753726 Mon Sep 17 00:00:00 2001 From: Jan Henckens Date: Sat, 4 Jan 2025 19:44:22 +0100 Subject: [PATCH 2/3] Added readme for google geocoding Signed-off-by: Jan Henckens --- README.MD | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.MD b/README.MD index 1b7c810..ec2e066 100644 --- a/README.MD +++ b/README.MD @@ -23,6 +23,26 @@ To install the plugin, follow these instructions. 3. In the Control Panel, go to Settings → Plugins and click the “Install” button for "Easy Address Field". +## Settings + +You can choose which geocoding API the plugin will use when you have geocoding enabled for your fields. +Options are: +- OpenStreetMap's Nominatim (default, no configuration, but very strict rate limits) +- Google Maps (requires an API key, see below) + +### Google Maps API +To enable geolocating and custom marker placement, you need to provide a Google Maps API key. +1) Sign in to [Google's developers console](http://console.developers.google.com/) +2) Create a new project +3) Enable the `Google Maps Geocoding API` and the `Google Static Maps API` + +**🚨 Important 🚨:** + +Google's Geocoding API does not support HTTP referrer restrictions. Make sure the API key you are using does not have these set. + +#### API Errors +When `devMode` is enabled, any errors returned by Google's API will show an exception so you can clearly see what's going wrong. With `devMode` disabled, any errors will be logged to Craft's `web.log`. + ## Template variables ### Printing address values From 3e1964f3215eb4ff63d4d714fdcd7836b7a9c39c Mon Sep 17 00:00:00 2001 From: Jan Henckens Date: Sat, 4 Jan 2025 19:47:10 +0100 Subject: [PATCH 3/3] Run CI for pull requests Signed-off-by: Jan Henckens --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3f34f25..d6c3c63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,7 @@ name: Code Quality on: workflow_dispatch: + pull_request: push: branches: - develop