diff --git a/CHANGELOG.md b/CHANGELOG.md index a01419a..d7ccfb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 3.8.1 - 2020-02-27 +### Added +- Add env support to settings (Closes #241) + +### Improved +- Improve settings page appearance during load + ## 3.8.0 - 2020-02-25 ### Added - Add What3Words support (Closes #236) diff --git a/composer.json b/composer.json index f0d0e74..047bbae 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "ether/simplemap", "description": "A beautifully simple Map field type for Craft CMS 3", - "version": "3.8.0", + "version": "3.8.1", "type": "craft-plugin", "license": "proprietary", "minimum-stability": "dev", diff --git a/src/SimpleMap.php b/src/SimpleMap.php index ff03947..f7d7088 100644 --- a/src/SimpleMap.php +++ b/src/SimpleMap.php @@ -166,7 +166,7 @@ protected function createSettingsModel () } /** - * @return Settings + * @return bool|\craft\base\Model|Settings */ public function getSettings () { @@ -230,6 +230,9 @@ public function onRegisterGqlTypes (RegisterGqlTypesEvent $event) $event->types[] = MapPartsType::class; } + /** + * @throws \Exception + */ public function onApplicationInit () { if ($this->getSettings()->geoLocationAutoRedirect) diff --git a/src/fields/MapField.php b/src/fields/MapField.php index 8189592..31e5a43 100644 --- a/src/fields/MapField.php +++ b/src/fields/MapField.php @@ -508,13 +508,13 @@ private function _renderMap ($value, $isSettings = false) 'mapTiles' => $settings->mapTiles, 'mapToken' => GeoService::getToken( - $settings->mapToken, + $settings->getMapToken(), $settings->mapTiles ), 'geoService' => $settings->geoService, 'geoToken' => GeoService::getToken( - $settings->geoToken, + $settings->getGeoToken(), $settings->geoService ), @@ -549,11 +549,11 @@ private function _renderMap ($value, $isSettings = false) if (strpos($settings->mapTiles, 'google') !== false) { - if ($settings->mapToken !== $settings->geoToken) + if ($settings->getMapToken() !== $settings->getGeoToken()) { $view->registerJsFile( 'https://maps.googleapis.com/maps/api/js?key=' . - $settings->mapToken + $settings->getMapToken() ); } } @@ -571,7 +571,7 @@ private function _renderMap ($value, $isSettings = false) { $view->registerJsFile( 'https://maps.googleapis.com/maps/api/js?libraries=places&key=' . - $settings->geoToken + $settings->getGeoToken() ); } elseif ($settings->geoService === GeoEnum::AppleMapKit) @@ -584,11 +584,11 @@ private function _renderMap ($value, $isSettings = false) // what3words // --------------------------------------------------------------------- - if ($settings->w3wEnabled && !empty($settings->w3wToken)) + if ($settings->w3wEnabled && !empty($settings->getW3WToken())) { $view->registerJsFile( 'https://assets.what3words.com/sdk/v3/what3words.js?key=' . - $settings->w3wToken + $settings->getW3WToken() ); } diff --git a/src/models/Settings.php b/src/models/Settings.php index a36f9cf..3ec7ac0 100644 --- a/src/models/Settings.php +++ b/src/models/Settings.php @@ -119,4 +119,40 @@ public function isW3WEnabled () return $this->w3wEnabled && SimpleMap::v(SimpleMap::EDITION_PRO); } + // Getters + // ========================================================================= + + public function getMapToken () + { + return $this->_parseEnv($this->mapToken); + } + + public function getGeoToken () + { + return $this->_parseEnv($this->geoToken); + } + + public function getW3WToken () + { + return $this->_parseEnv($this->w3wToken); + } + + public function getGeoLocationToken () + { + return $this->_parseEnv($this->geoLocationToken); + } + + // Helpers + // ========================================================================= + + private function _parseEnv ($value) + { + if (is_string($value)) + return Craft::parseEnv($value); + + return array_map(function ($v) { + return Craft::parseEnv($v); + }, $value); + } + } diff --git a/src/services/EmbedService.php b/src/services/EmbedService.php index d932ee6..b55366d 100644 --- a/src/services/EmbedService.php +++ b/src/services/EmbedService.php @@ -145,7 +145,7 @@ private function _embedGoogle (EmbedOptions $options, Settings $settings) ); $params = http_build_query([ - 'key' => $settings->mapToken, + 'key' => $settings->getMapToken(), 'callback' => $callbackName, ]); @@ -159,7 +159,7 @@ private function _embedGoogle (EmbedOptions $options, Settings $settings) function {$callbackName} () { {$options->id} = new google.maps.Map(document.getElementById('{$options->id}'), $formattedOptions); - + {$options->id}._markers = []; {$formattedMarkers}.forEach(function (marker) { marker.map = {$options->id}; @@ -189,7 +189,7 @@ private function _embedApple (EmbedOptions $options, Settings $settings) $view = Craft::$app->getView(); $token = GeoService::getToken( - $settings->mapToken, + $settings->getMapToken(), $settings->mapTiles ); $latLng = implode(', ', array_values($options->getCenter())); @@ -258,7 +258,7 @@ private function _embedApple (EmbedOptions $options, Settings $settings) {$formattedMarkers}.forEach(function (marker) { marker.position.unshift(null); const m = new mapkit.MarkerAnnotation( - new (mapkit.Coordinate.bind.apply(mapkit.Coordinate, marker.position)), + new (mapkit.Coordinate.bind.apply(mapkit.Coordinate, marker.position)), { glyphText: marker.label || '', color: marker.color || '', @@ -335,7 +335,7 @@ private function _embedMapbox (EmbedOptions $options, Settings $settings) ); $initJs = <<mapToken) || !$settings->mapToken['apiKey']) + if (!array_key_exists('apiKey', $settings->getMapToken()) || !$settings->getMapToken()['apiKey']) throw new InvalidConfigException('Missing HERE API Key'); $view = Craft::$app->getView(); @@ -433,7 +433,7 @@ private function _embedHere (EmbedOptions $options, Settings $settings) ); $initJs = <<mapToken['apiKey']}' }); +const HERE_platform = new H.service.Platform({ apikey: '{$settings->getMapToken()['apiKey']}' }); window.HERE_defaultLayers = HERE_platform.createDefaultLayers(); JS; @@ -450,7 +450,7 @@ private function _embedHere (EmbedOptions $options, Settings $settings) {$formattedMarkers}.forEach(function (marker) { const m = new H.map.Marker( - marker.position, + marker.position, { icon: new H.map.Icon('{$markerIcon}'.replace('##FILL##', marker.color).replace('##LABEL##', marker.label)), } @@ -549,7 +549,7 @@ className: '', {$options->id}._markers = []; {$formattedMarkers}.forEach(function (marker) { const m = L.marker( - marker.position, + marker.position, { icon: window.LMapMarkerIcon(marker) } ); {$options->id}._markers.push(m); diff --git a/src/services/GeoLocationService.php b/src/services/GeoLocationService.php index f3d6e15..5095011 100644 --- a/src/services/GeoLocationService.php +++ b/src/services/GeoLocationService.php @@ -75,10 +75,10 @@ public function lookup ($ip = null) switch ($settings->geoLocationService) { case self::IpStack: - $userLocation = $this->_lookup_IpStack($settings->geoLocationToken, $ip); + $userLocation = $this->_lookup_IpStack($settings->getGeoLocationToken(), $ip); break; case self::MaxMind: - $userLocation = $this->_lookup_MaxMind($settings->geoLocationToken, $ip); + $userLocation = $this->_lookup_MaxMind($settings->getGeoLocationToken(), $ip); break; case self::MaxMindLite: $userLocation = $this->_lookup_MaxMindLite($ip); diff --git a/src/services/GeoService.php b/src/services/GeoService.php index 833a2fe..6474314 100644 --- a/src/services/GeoService.php +++ b/src/services/GeoService.php @@ -582,7 +582,7 @@ public static function latLngFromAddress ($address, $country = null) { /** @var Settings $settings */ $settings = SimpleMap::getInstance()->getSettings(); - $token = static::getToken($settings->geoToken, $settings->geoService); + $token = static::getToken($settings->getGeoToken(), $settings->geoService); switch ($settings->geoService) { @@ -626,7 +626,7 @@ public static function addressFromLatLng ($lat, $lng) /** @var Settings $settings */ $settings = SimpleMap::getInstance()->getSettings(); $token = - static::getToken($settings->geoToken, $settings->geoService); + static::getToken($settings->getGeoToken(), $settings->geoService); switch ($settings->geoService) { diff --git a/src/services/StaticService.php b/src/services/StaticService.php index 050afd0..0bda615 100644 --- a/src/services/StaticService.php +++ b/src/services/StaticService.php @@ -93,7 +93,7 @@ private function _generateGoogle ($options, $settings) 'language' => Craft::$app->getLocale()->getLanguageID(), 'region' => $this->_getTld(), 'key' => GeoService::getToken( - $settings->mapToken, + $settings->getMapToken(), $settings->mapTiles ), ]; @@ -152,8 +152,8 @@ private function _generateApple ($options, $settings) 'size' => $options->getSize(), 'scale' => $options->scale, 'lang' => Craft::$app->getLocale()->getLanguageID(), - 'teamId' => $settings->mapToken['teamId'], - 'keyId' => $settings->mapToken['keyId'], + 'teamId' => $settings->getMapToken()['teamId'], + 'keyId' => $settings->getMapToken()['keyId'], ]; if (!empty($options->markers)) @@ -189,7 +189,7 @@ private function _generateApple ($options, $settings) } $path = '/api/v1/snapshot?' . http_build_query($params); - openssl_sign($path, $signature, $settings->mapToken['privateKey'], OPENSSL_ALGO_SHA256); + openssl_sign($path, $signature, $settings->getMapToken()['privateKey'], OPENSSL_ALGO_SHA256); $signature = $this->_encode($signature); return 'https://snapshot.apple-mapkit.com' . $path . '&signature=' . $signature; @@ -251,7 +251,7 @@ private function _generateMapbox ($options, $settings) if ($options->scale > 1) $url .= '@2x'; - return $url . '?access_token=' . $settings->mapToken; + return $url . '?access_token=' . $settings->getMapToken(); } /** @@ -264,8 +264,8 @@ private function _generateMapbox ($options, $settings) private function _generateHere ($options, $settings) { $params = [ - 'app_id' => $settings->mapToken['appId'], - 'app_code' => $settings->mapToken['appCode'], + 'app_id' => $settings->getMapToken()['appId'], + 'app_code' => $settings->getMapToken()['appCode'], 'nodot' => true, 'c' => implode(',', $options->getCenter()), 'z' => $options->zoom, diff --git a/src/services/What3WordsService.php b/src/services/What3WordsService.php index 6871be9..e51bad3 100644 --- a/src/services/What3WordsService.php +++ b/src/services/What3WordsService.php @@ -32,7 +32,7 @@ private static function _geocoder () if ($geocoder) return $geocoder; - return $geocoder = new Geocoder(SimpleMap::getInstance()->getSettings()->w3wToken); + return $geocoder = new Geocoder(SimpleMap::getInstance()->getSettings()->getW3WToken()); } } diff --git a/src/templates/settings.twig b/src/templates/settings.twig index ba1ac1d..3bdfa7a 100644 --- a/src/templates/settings.twig +++ b/src/templates/settings.twig @@ -4,6 +4,8 @@ {% set fullPageForm = true %} {% do craft.app.view.registerAssetBundle('craft\\web\\assets\\vue\\VueAsset') %} +{#{% dd settings %}#} + {% macro selectLoop (options, value) %} {% set hasOptgroups = false %} {% for key, option in options %} @@ -42,6 +44,7 @@ {% from _self import selectLoop, label, tokenValue %} {% block content %} +
{{ redirectInput('maps/settings') }} @@ -60,15 +63,12 @@ {% for key, label in mapTileOptions if key != '0' and key != '1' %} {% set x1 = craft.app.assetManager.getPublishedUrl('@simplemapimages/' ~ (key|replace({'.':'-','/':'-'})) ~ '-1x.jpg', true) %} {% set x2 = craft.app.assetManager.getPublishedUrl('@simplemapimages/' ~ (key|replace({'.':'-','/':'-'})) ~ '.jpg', true) %} - - {{ label is iterable ? label.label : label }} - + {% endfor %} {% namespace 'settings' %} @@ -84,11 +84,10 @@
{% set children %} - + > {% endset %} {{ label('Map Token', 'Add the API key for map tiles service you are using.', children) }}
@@ -99,51 +98,46 @@ rows="6" >{{ tokenValue(settings.mapToken, 'privateKey') }} {% endset %} - {{ label('Private Key', 'Paste the contents of your private key files below.', children) }} + {{ label('Private Key', 'Paste the contents of your private key files below (supports env variables).', children) }} {% set children %} - + > {% endset %} {{ label('Key ID', 'The ID of the key associated with your private key.', children) }} {% set children %} - + > {% endset %} {{ label('Team ID', 'The team ID that created the key ID and private key.', children) }}
{% set children %} - + > {% endset %} {{ label('App ID', 'Your Here app ID.', children) }} {% set children %} - + > {% endset %} {{ label('App Code', 'Your Here app code.', children) }} {% set children %} - + > {% endset %} {{ label('API Key', 'Your Here API Key (only required for front-end embed maps).', children) }}
@@ -166,11 +160,10 @@
{% set children %} - + > {% endset %} {{ label('Geo Token', 'Add the API key for the geocoding service.', children) }}
@@ -191,43 +184,39 @@ rows="6" >{{ tokenValue(settings.geoToken, 'privateKey') }} {% endset %} - {{ label('Private Key', 'Paste the contents of your private key files below.', children) }} + {{ label('Private Key', 'Paste the contents of your private key files below (supports env variables).', children) }} {% set children %} - + > {% endset %} {{ label('Key ID', 'The ID of the key associated with your private key.', children) }} {% set children %} - + > {% endset %} {{ label('Team ID', 'The team ID that created the key ID and private key.', children) }}
{% set children %} - + > {% endset %} {{ label('App ID', 'Your Here app ID.', children) }} {% set children %} - + > {% endset %} {{ label('App Code', 'Your Here app code.', children) }}
@@ -283,30 +272,27 @@ {% set geoLocationTokenIterable = settings.geoLocationToken is iterable %}
{% set children %} - + > {% endset %} {{ label('Geolocation Token', 'Add the API key for the geolocation service.', children) }}
{% set children %} - + > {% endset %} {{ label('Account ID', 'Your MaxMind account ID.', children) }} {% set children %} - + > {% endset %} {{ label('License Key', 'Your MaxMind license key.', children) }}
@@ -351,11 +337,10 @@
{% set children %} - + > {% endset %} {{ label('what3words Token', 'Your what3words API key.', children) }}
@@ -373,20 +358,150 @@ {% endnamespace %} + {% endblock %} {% do craft.app.view.registerAssetBundle('craft\\web\\assets\\vue\\VueAsset') %} +{# Note: these are separate purely as a workaround for issues with PHPStorms syntax highlighting :( #} {% js %} -new Vue({ - el: '#content', +window.__mapsData = { + mapTiles: '{{ settings.mapTiles }}', + geoService: '{{ settings.geoService }}', + geoLocationService: '{{ settings.geoLocationService }}', + w3wEnabled: '{{ settings.w3wEnabled ? 1 }}', +}; +window.__mapsCraftAutosuggestSuggestions = {{ craft.cp.getEnvSuggestions()|json_encode|raw }}; +{% endjs %} +{% js %} +const CraftAutosuggest = { + props: ['name', 'value'], + delimiters: ['%{', '}'], + template: `
+ + + +
`, data () { return { - mapTiles: '{{ settings.mapTiles }}', - geoService: '{{ settings.geoService }}', - geoLocationService: '{{ settings.geoLocationService }}', - w3wEnabled: '{{ settings.w3wEnabled ? 1 }}', + selected: '', + filteredOptions: [], + suggestions: window.__mapsCraftAutosuggestSuggestions || [], + limit: 5, + inputProps: { + initialValue: this.value, + name: this.name, + onInputChange: this.onInputChange, + }, }; }, + methods: { + onInputChange(text) { + if (text === '' || text === undefined) { + this.filteredOptions = this.suggestions; + return; + } + + text = text.toLowerCase(); + + var filtered = []; + var i, j, sectionFilter, item, name; + var that = this; + + for (i = 0; i < this.suggestions.length; i++) { + sectionFilter = []; + for (j = 0; j < this.suggestions[i].data.length; j++) { + item = this.suggestions[i].data[j]; + if ( + (item.name || item).toLowerCase().indexOf(text) !== -1 || + (item.hint && item.hint.toLowerCase().indexOf(text) !== -1) + ) { + sectionFilter.push(item.name ? item : {name: item}); + } + } + if (sectionFilter.length) { + sectionFilter.sort(function(a, b) { + var scoreA = that.scoreItem(a, text); + var scoreB = that.scoreItem(b, text); + if (scoreA === scoreB) { + return 0; + } + return scoreA < scoreB ? 1 : -1; + }); + filtered.push({ + label: this.suggestions[i].label || null, + data: sectionFilter.slice(0, this.limit) + }); + } + } + + this.filteredOptions = filtered; + }, + scoreItem(item, text) { + var score = 0; + if (item.name.toLowerCase().indexOf(text) !== -1) { + score += 100 + text.length / item.name.length; + } + if (item.hint && item.hint.toLowerCase().indexOf(text) !== -1) { + score += text.length / item.hint.length; + } + return score; + }, + onSelected(option) { + this.selected = option.item; + + // Bring focus back to the input if they selected an alias + if (option.item.name[0] == '@') { + var input = this.$el.querySelector('input'); + input.focus(); + input.selectionStart = input.selectionEnd = input.value.length; + } + }, + getSuggestionValue(suggestion) { + return suggestion.item.name || suggestion.item; + }, + }, +}; + +const ImageFadeOnLoad = { + props: ['src', 'srcset', 'alt', 'show'], + template: ` + + `, + data () { + return { loaded: false }; + }, + methods: { + onLoaded () { + this.loaded = true; + }, + }, +}; + +new Vue({ + el: '#content', + components: { + 'craft-autosuggest': CraftAutosuggest, + 'image-fade-on-load': ImageFadeOnLoad, + }, + data () { + return window.__mapsData; + }, computed: { requiresMapToken () { return !( @@ -451,6 +566,10 @@ hr { opacity: 0.5; } +[v-cloak] { + display: none; +} + .fade-enter-active, .fade-leave-active { transition: opacity .5s; } @@ -567,6 +686,7 @@ hr { padding: 30px; padding: var(--y-pad, 30px) var(--x-pad, 30px); min-height: 561px; + background-color: #e4edf6; } @media only screen and (max-width: 767px) { .maps-map { diff --git a/src/web/Variable.php b/src/web/Variable.php index 619e68c..db9ce09 100644 --- a/src/web/Variable.php +++ b/src/web/Variable.php @@ -34,7 +34,7 @@ public function getMapToken () $settings = SimpleMap::getInstance()->getSettings(); return GeoService::getToken( - $settings->mapToken, + $settings->getMapToken(), $settings->mapTiles ); }