diff --git a/README.md b/README.md index fa9f159..8663ba5 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,15 @@ You can call data from the Google settings on the frontend via `$GoogleConfig`, <% end_with %> ``` +### GTM Scripts + +You can render the GTM scripts in your template with the following: + +* `$HeadScripts.RAW` +* `$BodyStartScripts.RAW` +* `$BodyEndScripts.RAW` + + ## Places Setting up a Place in the CMS, with an example of the Review data @@ -50,5 +59,7 @@ With a selected Place, you can render the badge and feed in your template with ` ## TODO +* Fix the issue of the CMS needing to be reloaded for the map to appear when going back and viewing another map * Add more APIs * Update CSS to properly render as-is on the frontend (like a widget) +* Allow enabling/disabling of specific Admin sections \ No newline at end of file diff --git a/client/css/badge.css b/client/css/badge.css index 8354b9f..3852733 100644 --- a/client/css/badge.css +++ b/client/css/badge.css @@ -24,6 +24,8 @@ box-shadow: 0 0 2px rgba(0,0,0,.12),0 2px 4px rgba(0,0,0,.24); width: 100px !important; height: 100px !important; + object-fit: cover; + object-position: center; } .info-place a { diff --git a/client/css/map-field.css b/client/css/map-field.css new file mode 100644 index 0000000..32522b7 --- /dev/null +++ b/client/css/map-field.css @@ -0,0 +1,33 @@ +#map-field { + height:500px; +} + +.controls { + background-color: #fff; + border-radius: 2px; + border: 1px solid transparent; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); + box-sizing: border-box; + font-family: Roboto !important; + font-size: 15px !important; + font-weight: 300; + height: 29px; + margin-left: 17px; + margin-top: 10px; + outline: none; + padding: 0 11px 0 13px; + text-overflow: ellipsis; + width: 400px; +} + +.controls:focus { + border-color: #4d90fe; +} + +.title { + font-weight: bold; +} + +#map #infowindow-content { + display: inline; +} \ No newline at end of file diff --git a/client/javascript/default-map.js b/client/javascript/default-map.js new file mode 100644 index 0000000..92b5c92 --- /dev/null +++ b/client/javascript/default-map.js @@ -0,0 +1,6 @@ +function initMap() { + const map = new google.maps.Map(document.getElementById("map-field"), { + center: { lat: -33.8688, lng: 151.2195 }, + zoom: 13, + }); +} diff --git a/client/javascript/places-map.js b/client/javascript/places-map.js new file mode 100644 index 0000000..bc02357 --- /dev/null +++ b/client/javascript/places-map.js @@ -0,0 +1,74 @@ +function initMap() { + const position = { lat: -33.8688, lng: 151.2195 }; + const existingPlace = document.getElementById("place-id"); + + const map = new google.maps.Map(document.getElementById("map-field"), { + center: position, + zoom: 13, + mapId: "DEMO_MAP_ID" + }); + + const input = document.getElementById("pac-input"); + const autocomplete = new google.maps.places.Autocomplete(input); + + autocomplete.bindTo("bounds", map); + + map.controls[google.maps.ControlPosition.TOP_LEFT].push(input); + + const infowindow = new google.maps.InfoWindow({content: null, ariaLabel: 'Test'}); + const infowindowContent = document.getElementById("infowindow-content"); + + infowindow.setContent(infowindowContent); + + const marker = new google.maps.marker.AdvancedMarkerElement({ + map: map, + position: null, + }); + + if (existingPlace.value) { + const geocoder = new google.maps.Geocoder(); + geocoder + .geocode({ placeId: existingPlace.value }) + .then(({ results }) => { + const existingCoords = results[0].geometry.location; + marker.position = existingCoords; + map.setCenter(existingCoords); + }); + } + + autocomplete.addListener("place_changed", function() { + marker.position = null; + infowindow.close(); + + const place = autocomplete.getPlace(); + + if (!place.geometry || !place.geometry.location) { + return; + } + + if (place.geometry.viewport) { + map.fitBounds(place.geometry.viewport); + } else { + map.setCenter(place.geometry.location); + map.setZoom(17); + } + + marker.position = place.geometry.location; + + infowindowContent.children.namedItem("place-id").textContent = place.place_id; + // set href + infowindowContent.children.namedItem("place-id").href = `javascript:setPlaceValue('${place.place_id}')`; + infowindowContent.children.namedItem("place-address").textContent = place.formatted_address; + infowindow.open(map, marker); + + let heading = document.createElement('div'); + heading.textContent = place.name; + heading.style.fontWeight = 'bold'; + + infowindow.setHeaderContent(heading); + }); +} + +function setPlaceValue(placeId) { + document.getElementById("Form_ItemEditForm_PlaceID").value = placeId; +} diff --git a/composer.json b/composer.json index c9c3357..7a1dc66 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ }, "extra": { "expose": [ - "client/css" + "client/css", + "client/javscript" ] }, "minimum-stability": "dev" diff --git a/docs/images/place-fields.png b/docs/images/place-fields.png index 9563135..ea8d5e4 100644 Binary files a/docs/images/place-fields.png and b/docs/images/place-fields.png differ diff --git a/src/Fields/GoogleMapField.php b/src/Fields/GoogleMapField.php new file mode 100644 index 0000000..33c065f --- /dev/null +++ b/src/Fields/GoogleMapField.php @@ -0,0 +1,133 @@ +setMapType($mapType); + $this->setUrlParams($urlParams); + + parent::__construct($name); + } + + /** + * @param string $type + * + * @return $this + */ + public function setMapType($mapType) + { + if ($mapType) { + $this->mapType = $mapType; + } else { + $this->mapType = self::TYPE_DEFAULT; + } + + return $this; + } + + /** + * @return string + */ + public function getMapType() + { + return $this->mapType; + } + + /** + * @param string $type + * + * @return $this + */ + public function setUrlParams($urlParams) + { + $urlParams['loading'] = 'async'; + $urlParams['callback'] = 'initMap'; + $urlParams['v'] = 'weekly'; + + if ($this->getMapType() === self::TYPE_PLACES) { + $urlParams['libraries'] = 'places,marker'; + } + + $this->urlParams = $urlParams; + + return $this; + } + + /** + * @return array + */ + public function getUrlParams() + { + return $this->urlParams; + } + + public function Field($properties = []) + { + Requirements::css('iliain/silverstripe-google-config: client/css/map-field.css'); + + switch ($this->getMapType()) { + case self::TYPE_PLACES: + Requirements::javascript('iliain/silverstripe-google-config: client/javascript/places-map.js', ['defer' => true]); + break; + default: + Requirements::javascript('iliain/silverstripe-google-config: client/javascript/default-map.js', ['defer' => true]); + break; + } + + Requirements::javascript($this->getMapURL(), ['async' => true, 'defer' => true]); + + $properties['MapType'] = $this->getMapType(); + + // Get the place ID from object loading this field + $properties['PlaceID'] = $this->getForm()->getRecord()->PlaceID; + + return parent::Field($properties); + } + + /** + * Returns the URL for the Google Maps API with relevant parameters + * @return string + */ + public function getMapURL() + { + if (!Environment::getEnv('GOOGLE_MAPS_API_KEY')) { + throw new \Exception('No Google Maps API key set'); + } + + $url = 'https://maps.googleapis.com/maps/api/js?key=' . Environment::getEnv('GOOGLE_MAPS_API_KEY'); + + if ($this->getUrlParams()) { + $url .= '&' . http_build_query($this->getUrlParams()); + } + + return $url; + } +} diff --git a/src/Model/GooglePlace.php b/src/Model/GooglePlace.php index be54973..20ff8b9 100644 --- a/src/Model/GooglePlace.php +++ b/src/Model/GooglePlace.php @@ -3,6 +3,7 @@ namespace Iliain\GoogleConfig\Models; use Exception; +use Iliain\GoogleConfig\Fields\GoogleMapField; use SilverStripe\ORM\ArrayList; use SilverStripe\ORM\DataObject; use SilverStripe\Forms\TextField; @@ -12,7 +13,6 @@ use SilverStripe\Forms\TextareaField; use SilverStripe\ORM\FieldType\DBText; use SilverStripe\ORM\FieldType\DBHTMLText; -use Iliain\GoogleConfig\Models\GoogleConfig; use SilverStripe\Forms\ToggleCompositeField; class GooglePlace extends DataObject @@ -64,9 +64,9 @@ public function getCMSFields() TextField::create('PlaceTitle', 'Place Title', $this->getPlaceField('name'))->setReadonly(true), TextField::create('PlaceID', 'Place ID'), LiteralField::create('Message', '
'), + // ToggleCompositeField is causing issues with the elements not rendering before the script executes ToggleCompositeField::create('PlaceMap', 'Map', [ - // Use map listed in the guides. If it stops working, we'll have to figure out how best to get the place ID - LiteralField::create('SelectorMap', ''), + GoogleMapField::create('MapFrame', 'places'), ])->setHeadingLevel(4)->setStartClosed($this->PlaceID ? true : false), ]); diff --git a/templates/iliain/GoogleConfig/Fields/GoogleMapField.ss b/templates/iliain/GoogleConfig/Fields/GoogleMapField.ss new file mode 100644 index 0000000..d91e1fb --- /dev/null +++ b/templates/iliain/GoogleConfig/Fields/GoogleMapField.ss @@ -0,0 +1,33 @@ +