Skip to content

Commit

Permalink
implement #366, add here maps support (#367)
Browse files Browse the repository at this point in the history
* #366 initial work for supporting HERE maps

* #366 additional work for handling HERE map in the admin

* #366 additional work for handling HERE map in the admin when editing/creating an event

* #366 refactor, add map configuration section in basic settings and system page, remove possibility to override the map configuration in the other conf page
  • Loading branch information
syjer authored and cbellone committed Dec 5, 2017
1 parent 8dd55ab commit bdc347a
Show file tree
Hide file tree
Showing 10 changed files with 326 additions and 72 deletions.
2 changes: 1 addition & 1 deletion src/main/java/alfio/config/MvcConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ public void postHandle(HttpServletRequest request, HttpServletResponse response,
+ " frame-src 'self' https://js.stripe.com https://www.google.com;"
+ " font-src 'self';"//
+ " media-src blob: 'self';"//for loading camera api
+ " connect-src 'self' https://api.stripe.com https://maps.googleapis.com/;" //<- currently stripe.js use jsonp but if they switch to xmlhttprequest+cors we will be ready
+ " connect-src 'self' https://api.stripe.com https://maps.googleapis.com/ https://geocoder.cit.api.here.com;" //<- currently stripe.js use jsonp but if they switch to xmlhttprequest+cors we will be ready
+ (environment.acceptsProfiles(Initializer.PROFILE_DEBUG_CSP) ? " report-uri /report-csp-violation" : ""));
}
};
Expand Down
11 changes: 9 additions & 2 deletions src/main/java/alfio/controller/EventController.java
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,15 @@ public String showEvent(@PathVariable("eventName") String eventName,
.collect(Collectors.toList());
//

LocationDescriptor ld = LocationDescriptor.fromGeoData(event.getLatLong(), TimeZone.getTimeZone(event.getTimeZone()),
configurationManager.getStringConfigValue(Configuration.from(event.getOrganizationId(), event.getId(), ConfigurationKeys.MAPS_CLIENT_API_KEY)));
final int orgId = event.getOrganizationId();
final int eventId = event.getId();
Map<ConfigurationKeys, Optional<String>> geoInfoConfiguration = configurationManager.getStringConfigValueFrom(
Configuration.from(orgId, eventId, ConfigurationKeys.MAPS_PROVIDER),
Configuration.from(orgId, eventId, ConfigurationKeys.MAPS_CLIENT_API_KEY),
Configuration.from(orgId, eventId, ConfigurationKeys.MAPS_HERE_APP_ID),
Configuration.from(orgId, eventId, ConfigurationKeys.MAPS_HERE_APP_CODE));

LocationDescriptor ld = LocationDescriptor.fromGeoData(event.getLatLong(), TimeZone.getTimeZone(event.getTimeZone()), geoInfoConfiguration);

final boolean hasAccessPromotions = ticketCategoryRepository.countAccessRestrictedRepositoryByEventId(event.getId()) > 0 ||
promoCodeRepository.countByEventAndOrganizationId(event.getId(), event.getOrganizationId()) > 0;
Expand Down
54 changes: 44 additions & 10 deletions src/main/java/alfio/controller/api/admin/LocationApiController.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,19 @@
package alfio.controller.api.admin;

import alfio.manager.system.ConfigurationManager;
import alfio.model.modification.support.LocationDescriptor;
import alfio.model.system.Configuration;
import alfio.model.system.ConfigurationKeys;
import com.moodysalem.TimezoneMapper;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.function.Function;

@RestController
@RequestMapping("/admin/api")
Expand All @@ -46,21 +48,53 @@ public String unhandledException(Exception e) {
return e.getMessage();
}

@RequestMapping(value = "/location/maps-client-api-key")
public String mapsClientApiKey() {
return configurationManager.getStringConfigValue(Configuration.getSystemConfiguration(ConfigurationKeys.MAPS_CLIENT_API_KEY)).orElse(null);
}

@RequestMapping(value = "/location/timezones")
@RequestMapping("/location/timezones")
public List<String> getTimezones() {
List<String> s = new ArrayList<>(ZoneId.getAvailableZoneIds());
s.sort(String::compareTo);
return s;
}

@RequestMapping(value = "/location/timezone")
@RequestMapping("/location/timezone")
public String getTimezone(@RequestParam("lat") double lat, @RequestParam("lng") double lng) {
String tzId = TimezoneMapper.tzNameAt(lat, lng);
return getTimezones().contains(tzId) ? tzId : null;
}



@RequestMapping("/location/static-map-image")
public String getMapImage(
@RequestParam("lat") String lat,
@RequestParam("lng") String lng) {
Map<ConfigurationKeys, Optional<String>> geoInfoConfiguration = getGeoConf();
return LocationDescriptor.getMapUrl(lat, lng, geoInfoConfiguration);
}

private Map<ConfigurationKeys, Optional<String>> getGeoConf() {
Function<ConfigurationKeys, Configuration.ConfigurationPathKey> pathKeyBuilder = (key) -> Configuration.getSystemConfiguration(key);
return configurationManager.getStringConfigValueFrom(
pathKeyBuilder.apply(ConfigurationKeys.MAPS_PROVIDER),
pathKeyBuilder.apply(ConfigurationKeys.MAPS_CLIENT_API_KEY),
pathKeyBuilder.apply(ConfigurationKeys.MAPS_HERE_APP_ID),
pathKeyBuilder.apply(ConfigurationKeys.MAPS_HERE_APP_CODE));
}

@RequestMapping("/location/map-provider-client-api-key")
public ProviderAndKeys getGeoInfoProviderAndKeys() {
Map<ConfigurationKeys, Optional<String>> geoInfoConfiguration = getGeoConf();
ConfigurationKeys.GeoInfoProvider provider = LocationDescriptor.getProvider(geoInfoConfiguration);
Map<ConfigurationKeys, String> apiKeys = new HashMap<>();
geoInfoConfiguration.forEach((k,v) -> {
v.ifPresent(value -> apiKeys.put(k, value));
});
return new ProviderAndKeys(provider, apiKeys);
}

@AllArgsConstructor
@Getter
public static class ProviderAndKeys {
private final ConfigurationKeys.GeoInfoProvider provider;
private Map<ConfigurationKeys, String> keys;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package alfio.model.modification.support;

import alfio.model.system.ConfigurationKeys;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.EqualsAndHashCode;
Expand All @@ -32,8 +33,6 @@
@EqualsAndHashCode
public class LocationDescriptor {

private static final String MAP_URL = "https://maps.googleapis.com/maps/api/staticmap?center=${latitude},${longitude}&key=${key}&zoom=16&size=400x400&markers=color:blue%7Clabel:E%7C${latitude},${longitude}";
private static final String OPENSTREETMAP = "https://tyler-demo.herokuapp.com/?center=${latitude},${longitude}&zoom=16&size=400x400&markers=color:blue%7Clabel:E%7C${latitude},${longitude}";

private final String timeZone;
private final String latitude;
Expand All @@ -51,12 +50,56 @@ public LocationDescriptor(@JsonProperty("timeZone") String timeZone,
this.mapUrl = mapUrl;
}

public static LocationDescriptor fromGeoData(Pair<String, String> coordinates, TimeZone timeZone, Optional<String> apiKey) {
public static LocationDescriptor fromGeoData(Pair<String, String> coordinates, TimeZone timeZone, Map<ConfigurationKeys, Optional<String>> geoConf) {
Map<String, String> params = new HashMap<>();
params.put("latitude", coordinates.getLeft());
params.put("longitude", coordinates.getRight());
apiKey.ifPresent((key) -> params.put("key", key));
return new LocationDescriptor(timeZone.getID(), coordinates.getLeft(), coordinates.getRight(), new StrSubstitutor(params).replace(apiKey.isPresent() ? MAP_URL : OPENSTREETMAP));
String lat = coordinates.getLeft();
String lng = coordinates.getRight();
params.put("latitude", lat);
params.put("longitude", lng);

return new LocationDescriptor(timeZone.getID(), coordinates.getLeft(), coordinates.getRight(), getMapUrl(lat, lng, geoConf));
}

public static String getMapUrl(String lat, String lng, Map<ConfigurationKeys, Optional<String>> geoConf) {
Map<String, String> params = new HashMap<>();
params.put("latitude", lat);
params.put("longitude", lng);

ConfigurationKeys.GeoInfoProvider provider = getProvider(geoConf);
String mapUrl = mapUrl(provider);

fillParams(provider, geoConf, params);

return new StrSubstitutor(params).replace(mapUrl);
}

// for backward compatibility reason, the logic is not straightforward
public static ConfigurationKeys.GeoInfoProvider getProvider(Map<ConfigurationKeys, Optional<String>> geoConf) {
if((!geoConf.containsKey(ConfigurationKeys.MAPS_PROVIDER) || !geoConf.get(ConfigurationKeys.MAPS_PROVIDER).isPresent()) &&
(geoConf.containsKey(ConfigurationKeys.MAPS_CLIENT_API_KEY) && geoConf.get(ConfigurationKeys.MAPS_CLIENT_API_KEY).isPresent())) {
return ConfigurationKeys.GeoInfoProvider.GOOGLE;
} else if (geoConf.containsKey(ConfigurationKeys.MAPS_PROVIDER) && geoConf.get(ConfigurationKeys.MAPS_PROVIDER).isPresent()) {
return geoConf.get(ConfigurationKeys.MAPS_PROVIDER).map(ConfigurationKeys.GeoInfoProvider::valueOf).orElseThrow(IllegalStateException::new);
} else {
return ConfigurationKeys.GeoInfoProvider.NONE;
}
}

private static String mapUrl(ConfigurationKeys.GeoInfoProvider provider) {
switch (provider) {
case GOOGLE: return "https://maps.googleapis.com/maps/api/staticmap?center=${latitude},${longitude}&key=${key}&zoom=16&size=400x400&markers=color:blue%7Clabel:E%7C${latitude},${longitude}";
case HERE: return "https://image.maps.api.here.com/mia/1.6/mapview?c=${latitude},${longitude}&z=16&w=400&h=400&poi=${latitude},${longitude}&app_id=${appId}&app_code=${appCode}";
default: return "https://tyler-demo.herokuapp.com/?center=${latitude},${longitude}&zoom=16&size=400x400&markers=color:blue%7Clabel:E%7C${latitude},${longitude}";
}
}

private static void fillParams(ConfigurationKeys.GeoInfoProvider provider, Map<ConfigurationKeys, Optional<String>> geoConf, Map<String, String> params) {
if(ConfigurationKeys.GeoInfoProvider.GOOGLE == provider) {
geoConf.get(ConfigurationKeys.MAPS_CLIENT_API_KEY).ifPresent((key) -> params.put("key", key));
} else if (ConfigurationKeys.GeoInfoProvider.HERE == provider) {
geoConf.get(ConfigurationKeys.MAPS_HERE_APP_ID).ifPresent((appId) -> params.put("appId", appId));
geoConf.get(ConfigurationKeys.MAPS_HERE_APP_CODE).ifPresent((appCode) -> params.put("appCode", appCode));
}
}

}
12 changes: 10 additions & 2 deletions src/main/java/alfio/model/system/ConfigurationKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ public enum ConfigurationKeys {

BASE_URL("Base application url", false, SettingCategory.GENERAL, ComponentType.TEXT, true, EnumSet.of(SYSTEM), true),

MAPS_CLIENT_API_KEY("Google maps' client api key", false, SettingCategory.GENERAL, ComponentType.TEXT, true, EnumSet.of(SYSTEM), true),
MAPS_PROVIDER("Select the maps provider (Google, Here)", false, SettingCategory.MAP, ComponentType.TEXT, false, EnumSet.of(SYSTEM), true),
MAPS_CLIENT_API_KEY("Google maps' client api key", false, SettingCategory.MAP, ComponentType.TEXT, false, EnumSet.of(SYSTEM), true),
MAPS_HERE_APP_ID("HERE map App ID", false, SettingCategory.MAP, ComponentType.TEXT, false, EnumSet.of(SYSTEM), true),
MAPS_HERE_APP_CODE("HERE map App Code", false, SettingCategory.MAP, ComponentType.TEXT, false, EnumSet.of(SYSTEM), true),


RECAPTCHA_API_KEY("Recaptcha api key", false, SettingCategory.GENERAL, ComponentType.TEXT, false, EnumSet.of(SYSTEM), true),
Expand Down Expand Up @@ -162,7 +165,8 @@ public enum SettingCategory {
INVOICE("Invoice settings"),
INVOICE_EU("Invoice settings for EU"),
MAIL("E-Mail settings"),
ALFIO_PI("Offline check-in and badge printing");
ALFIO_PI("Offline check-in and badge printing"),
MAP("Maps settings");

private final String description;
SettingCategory(String description) {
Expand All @@ -171,6 +175,10 @@ public enum SettingCategory {

}

public enum GeoInfoProvider {
GOOGLE, HERE, NONE
}

private static final Predicate<ConfigurationKeys> INTERNAL = ConfigurationKeys::isInternal;
private final String description;
private final boolean internal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,35 @@ <h2>E-Mail</h2>
</div>
</div>


<div class="page-header">
<h2>Map</h2>
<span>Map settings</span>
</div>
<div>
<p class="radio-inline">
<label>
<input type="radio" name="map.mapsProvider" data-ng-model="ctrl.map['MAPS_PROVIDER'].value" data-ng-value="'GOOGLE'">
Google maps
</label>
</p>
<p class="radio-inline">
<label>
<input type="radio" name="map.mapsProvider" data-ng-model="ctrl.map['MAPS_PROVIDER'].value" data-ng-value="'HERE'">
Here maps
</label>
</p>
<div>
<setting data-obj="ctrl.map['MAPS_CLIENT_API_KEY']" data-display-delete-if-needed="true" data-delete-handler="systemConf.delete(config)" ></setting>
</div>
<div>
<setting data-obj="ctrl.map['MAPS_HERE_APP_ID']" data-display-delete-if-needed="true" data-delete-handler="systemConf.delete(config)" ></setting>
</div>
<div>
<setting data-obj="ctrl.map['MAPS_HERE_APP_CODE']" data-display-delete-if-needed="true" data-delete-handler="systemConf.delete(config)" ></setting>
</div>
</div>

<div class="page-header" data-ng-if="ctrl.payment && ctrl.payment.settings.length > 0">
<h2>Payment</h2>
<span>Payment provider settings</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,34 @@ <h2>E-Mail</h2>
</div>
</div>

<div class="page-header">
<h2>Map</h2>
<span>Map settings</span>
</div>
<div>
<p class="radio-inline">
<label>
<input type="radio" name="map.mapsProvider" data-ng-model="systemConf.map['MAPS_PROVIDER'].value" data-ng-value="'GOOGLE'">
Google maps
</label>
</p>
<p class="radio-inline">
<label>
<input type="radio" name="map.mapsProvider" data-ng-model="systemConf.map['MAPS_PROVIDER'].value" data-ng-value="'HERE'">
Here maps
</label>
</p>
<div>
<setting data-obj="systemConf.map['MAPS_CLIENT_API_KEY']" data-display-delete-if-needed="true" data-delete-handler="systemConf.delete(config)" ></setting>
</div>
<div>
<setting data-obj="systemConf.map['MAPS_HERE_APP_ID']" data-display-delete-if-needed="true" data-delete-handler="systemConf.delete(config)" ></setting>
</div>
<div>
<setting data-obj="systemConf.map['MAPS_HERE_APP_CODE']" data-display-delete-if-needed="true" data-delete-handler="systemConf.delete(config)" ></setting>
</div>
</div>


<div class="page-header">
<h2>Invoice settings</h2>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,17 @@
mailReplyTo: _.find(original['MAIL'], function(e) {return e.configurationKey === 'MAIL_REPLY_TO';}),
mailAttemptsCount: _.find(original['MAIL'], function(e) {return e.configurationKey === 'MAIL_ATTEMPTS_COUNT';})
};
};

if(angular.isDefined(original['MAP']) && original['MAP'].length > 0) {
transformed.map = {
MAPS_PROVIDER: _.find(original['MAP'], function(e) {return e.key === 'MAPS_PROVIDER';}),
MAPS_CLIENT_API_KEY: _.find(original['MAP'], function(e) {return e.key === 'MAPS_CLIENT_API_KEY';}),
MAPS_HERE_APP_ID: _.find(original['MAP'], function(e) {return e.key === 'MAPS_HERE_APP_ID';}),
MAPS_HERE_APP_CODE: _.find(original['MAP'], function(e) {return e.key === 'MAPS_HERE_APP_CODE';})
}
}

_.forEach(['PAYMENT', 'PAYMENT_STRIPE', 'PAYMENT_PAYPAL', /*'PAYMENT_MOLLIE',*/ 'PAYMENT_OFFLINE', 'INVOICE_EU', 'ALFIO_PI'], function(group) {
if(angular.isDefined(original[group]) && original[group].length > 0) {
transformed[_.camelCase(group)] = {
Expand Down Expand Up @@ -443,6 +453,12 @@
ctrl.payment = {
settings: onlyBasic(settings['PAYMENT'])
};
ctrl.map = {
MAPS_PROVIDER: _.find(settings['MAP'], function(e) {return e.key === 'MAPS_PROVIDER';}),
MAPS_CLIENT_API_KEY: _.find(settings['MAP'], function(e) {return e.key === 'MAPS_CLIENT_API_KEY';}),
MAPS_HERE_APP_ID: _.find(settings['MAP'], function(e) {return e.key === 'MAPS_HERE_APP_ID';}),
MAPS_HERE_APP_CODE: _.find(settings['MAP'], function(e) {return e.key === 'MAPS_HERE_APP_CODE';})
};
});
ctrl.saveSettings = function(frm, settings, pluginSettings) {
if(!frm.$valid) {
Expand Down
Loading

0 comments on commit bdc347a

Please sign in to comment.