001/* 002 * Copyright 2015 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.maps; 017 018import java.io.IOException; 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Map.Entry; 025 026import org.apache.avalon.framework.component.Component; 027import org.apache.avalon.framework.logger.AbstractLogEnabled; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.avalon.framework.service.Serviceable; 031import org.apache.commons.lang.StringUtils; 032 033import org.ametys.core.observation.Event; 034import org.ametys.core.observation.ObservationManager; 035import org.ametys.core.ui.Callable; 036import org.ametys.core.user.CurrentUserProvider; 037import org.ametys.plugins.repository.AmetysObjectResolver; 038import org.ametys.plugins.repository.UnknownAmetysObjectException; 039import org.ametys.plugins.repository.metadata.CompositeMetadata; 040import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata; 041import org.ametys.runtime.parameter.Errors; 042import org.ametys.runtime.parameter.ParameterHelper; 043import org.ametys.runtime.parameter.Validator; 044import org.ametys.web.ObservationConstants; 045import org.ametys.web.repository.page.ModifiablePage; 046import org.ametys.web.repository.page.ModifiableZone; 047import org.ametys.web.repository.page.ModifiableZoneItem; 048import org.ametys.web.repository.page.Page; 049import org.ametys.web.repository.page.Page.PageType; 050import org.ametys.web.repository.page.ZoneItem; 051import org.ametys.web.repository.page.ZoneItem.ZoneType; 052import org.ametys.web.service.Service; 053import org.ametys.web.service.ServiceExtensionPoint; 054import org.ametys.web.service.ServiceParameter; 055import org.ametys.web.service.ServiceParameterOrRepeater; 056 057/** 058 * Helper gathering callable methods for the maps plugin. 059 */ 060public class MapServiceHelper extends AbstractLogEnabled implements Serviceable, Component 061{ 062 private AmetysObjectResolver _resolver; 063 private ServiceExtensionPoint _serviceExtensionPoint; 064 private CurrentUserProvider _currentUserProvider; 065 private ObservationManager _observationManager; 066 067 @Override 068 public void service(ServiceManager manager) throws ServiceException 069 { 070 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 071 _serviceExtensionPoint = (ServiceExtensionPoint) manager.lookup(ServiceExtensionPoint.ROLE); 072 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 073 _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE); 074 } 075 076 /** 077 * Get values of service parameters 078 * @param zoneItemId The zone item id 079 * @return The service parameters 080 */ 081 @Callable 082 public Map<String, Object> getServiceParameters (String zoneItemId) 083 { 084 ModifiableZoneItem zoneItem = _resolver.resolveById(zoneItemId); 085 086 Map<String, Object> values = new HashMap<>(); 087 088 ModifiableCompositeMetadata serviceParameters = zoneItem.getServiceParameters(); 089 values.put("centerLat", serviceParameters.getString("centerLat", "0.0")); 090 values.put("centerLng", serviceParameters.getString("centerLng", "0.0")); 091 values.put("zoomLevel", serviceParameters.getString("zoomLevel", "7")); 092 values.put("mapTypeId", serviceParameters.getString("mapTypeId")); 093 094 List<Map<String, Object>> pois = new ArrayList<>(); 095 096 CompositeMetadata poisMeta = serviceParameters.getCompositeMetadata("pois"); 097 098 for (String poiName : poisMeta.getMetadataNames()) 099 { 100 CompositeMetadata poiMeta = poisMeta.getCompositeMetadata(poiName); 101 String type = poiMeta.getString("type"); 102 103 Map<String, Object> poi = new HashMap<>(); 104 105 poi.put("title", poiMeta.getString("title", "")); 106 poi.put("description", poiMeta.getString("description", "")); 107 poi.put("type", type); 108 109 if ("marker".equals(type)) 110 { 111 poi.put("icon", poiMeta.getString("icon", "")); 112 poi.put("lat", poiMeta.getString("lat", "0.0")); 113 poi.put("lng", poiMeta.getString("lng", "0.0")); 114 } 115 else if ("polygon".equals(type)) 116 { 117 poi.put("color", poiMeta.getString("color", "")); 118 119 List<Map<String, Object>> points = new ArrayList<>(); 120 121 CompositeMetadata pointsMeta = poiMeta.getCompositeMetadata("points"); 122 123 String[] metadataNames = pointsMeta.getMetadataNames(); 124 Arrays.sort(metadataNames); 125 126 for (String pointName : metadataNames) 127 { 128 CompositeMetadata pointMeta = pointsMeta.getCompositeMetadata(pointName); 129 130 Map<String, Object> point = new HashMap<>(); 131 132 point.put("lat", pointMeta.getDouble("lat", 0.0)); 133 point.put("lng", pointMeta.getDouble("lng", 0.0)); 134 135 points.add(point); 136 } 137 138 poi.put("points", points); 139 } 140 else 141 { 142 throw new IllegalArgumentException("Unknown type value for POI " + type); 143 } 144 145 pois.add(poi); 146 } 147 148 values.put("pois", pois); 149 150 return values; 151 152 } 153 154 /** 155 * Set service 156 * @param pageId The page id 157 * @param serviceId The service id 158 * @param zoneName The name of zone of add service into 159 * @param parameters the service parameters. Can be empty 160 * @return The result with the ids of updated page, zone and zone item 161 * @throws IOException if an error occurred while saving parameters 162 */ 163 @Callable 164 public Map<String, Object> setService (String pageId, String serviceId, String zoneName, Map<String, Object> parameters) throws IOException 165 { 166 if (StringUtils.isEmpty(serviceId) || StringUtils.isEmpty(pageId) || StringUtils.isEmpty(zoneName)) 167 { 168 throw new IllegalArgumentException("ServiceId, PageId or ZoneName is missing"); 169 } 170 171 // Check the service 172 try 173 { 174 _serviceExtensionPoint.getExtension(serviceId); 175 } 176 catch (IllegalArgumentException e) 177 { 178 throw new IllegalArgumentException("Service with id '" + serviceId + "' does not exist", e); 179 } 180 181 try 182 { 183 Page page = _resolver.resolveById(pageId); 184 if (!(page instanceof ModifiablePage)) 185 { 186 throw new IllegalArgumentException("Can not affect service on a non-modifiable page " + pageId); 187 } 188 189 ModifiablePage mPage = (ModifiablePage) page; 190 191 if (page.getType() != PageType.CONTAINER) 192 { 193 throw new IllegalArgumentException("Can not affect service on a non-container page " + pageId); 194 } 195 196 ModifiableZone zone; 197 if (page.hasZone(zoneName)) 198 { 199 zone = mPage.getZone(zoneName); 200 } 201 else 202 { 203 zone = mPage.createZone(zoneName); 204 } 205 206 ModifiableZoneItem zoneItem = zone.addZoneItem(); 207 zoneItem.setType(ZoneType.SERVICE); 208 zoneItem.setServiceId(serviceId); 209 210 ModifiableCompositeMetadata serviceMetadata = zoneItem.getServiceParameters(); 211 Service service = _serviceExtensionPoint.getExtension(serviceId); 212 213 Map<String, Errors> allErrors = new HashMap<>(); 214 _setParameterValues(serviceMetadata, service, parameters, allErrors); 215 216 Map<String, Object> results = new HashMap<>(); 217 if (!allErrors.isEmpty()) 218 { 219 Map<String, Object> errors = new HashMap<>(); 220 221 for (Entry<String, Errors> errorEntry : allErrors.entrySet()) 222 { 223 String paramId = errorEntry.getKey(); 224 errors.put(paramId, errorEntry.getValue().getErrors()); 225 } 226 227 results.put("errors", errors); 228 } 229 else 230 { 231 mPage.saveChanges(); 232 233 Map<String, Object> eventParams = new HashMap<>(); 234 eventParams.put(ObservationConstants.ARGS_PAGE, page); 235 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_ID, zoneItem.getId()); 236 eventParams.put(ObservationConstants.ARGS_ZONE_TYPE, ZoneType.SERVICE); 237 _observationManager.notify(new Event(ObservationConstants.EVENT_ZONEITEM_ADDED, _currentUserProvider.getUser(), eventParams)); 238 239 results.put("id", page.getId()); 240 results.put("zoneitem-id", zoneItem.getId()); 241 results.put("zone-name", zone.getName()); 242 } 243 244 return results; 245 } 246 catch (UnknownAmetysObjectException e) 247 { 248 throw new IllegalArgumentException("An error occured adding the service '" + serviceId + "' on the page '" + pageId + "'", e); 249 } 250 } 251 252 /** 253 * Configure service 254 * @param serviceId The service id 255 * @param zoneItemId The id of aone item holding this service 256 * @param parameters the service parameters to update 257 * @return The result with the ids of updated page, zone and zone item 258 * @throws IOException if an error occurred while saving parameters 259 */ 260 @Callable 261 public Map<String, Object> configureService (String serviceId, String zoneItemId, Map<String, Object> parameters) throws IOException 262 { 263 Service service = null; 264 265 try 266 { 267 service = _serviceExtensionPoint.getExtension(serviceId); 268 } 269 catch (IllegalArgumentException e) 270 { 271 throw new IllegalArgumentException("Service with id '" + serviceId + "' does not exist", e); 272 } 273 274 ZoneItem zoneItem = _resolver.resolveById(zoneItemId); 275 if (!(zoneItem instanceof ModifiableZoneItem)) 276 { 277 throw new IllegalArgumentException("Can not configure service on a non-modifiable zone item " + zoneItemId); 278 } 279 280 ModifiableZoneItem mZoneItem = (ModifiableZoneItem) zoneItem; 281 282 ModifiableCompositeMetadata serviceMetadata = mZoneItem.getServiceParameters(); 283 284 Map<String, Errors> allErrors = new HashMap<>(); 285 _setParameterValues(serviceMetadata, service, parameters, allErrors); 286 287 Map<String, Object> results = new HashMap<>(); 288 289 if (!allErrors.isEmpty()) 290 { 291 Map<String, Object> errors = new HashMap<>(); 292 293 for (Entry<String, Errors> errorEntry : allErrors.entrySet()) 294 { 295 String paramId = errorEntry.getKey(); 296 errors.put(paramId, errorEntry.getValue().getErrors()); 297 } 298 299 results.put("errors", errors); 300 } 301 else 302 { 303 mZoneItem.saveChanges(); 304 305 Map<String, Object> eventParams = new HashMap<>(); 306 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM, zoneItem); 307 _observationManager.notify(new Event(ObservationConstants.EVENT_SERVICE_MODIFIED, _currentUserProvider.getUser(), eventParams)); 308 309 results.put("id", zoneItem.getZone().getPage().getId()); 310 results.put("zoneitem-id", zoneItem.getId()); 311 results.put("zone-name", zoneItem.getZone().getName()); 312 } 313 314 return results; 315 } 316 317 private void _setParameterValues(ModifiableCompositeMetadata serviceMetadata, Service service, Map<String, Object> values, Map<String, Errors> allErrors) 318 { 319 // Save default parameters 320 for (ServiceParameterOrRepeater param : service.getParameters().values()) 321 { 322 if (param instanceof ServiceParameter) 323 { 324 _getAndSaveParameter("service.param.", (ServiceParameter) param, values, serviceMetadata, allErrors); 325 } 326 } 327 328 // Get additional parameters for Map configuration and Point Of Interests 329 String centerLatStr = (String) values.get("map-service-center-lat"); 330 String centerLngStr = (String) values.get("map-service-center-lng"); 331 int zoomLevel = (Integer) values.get("map-service-zoom-level"); 332 String mapTypeId = (String) values.get("map-service-map-type-id"); 333 334 serviceMetadata.setMetadata("centerLat", Double.parseDouble(centerLatStr)); 335 serviceMetadata.setMetadata("centerLng", Double.parseDouble(centerLngStr)); 336 serviceMetadata.setMetadata("zoomLevel", zoomLevel); 337 serviceMetadata.setMetadata("mapTypeId", mapTypeId); 338 339 @SuppressWarnings("unchecked") 340 List<Map<String, Object>> pois = (List<Map<String, Object>>) values.get("pois"); 341 342 if (serviceMetadata.hasMetadata("pois")) 343 { 344 // Remove old POIs 345 serviceMetadata.removeMetadata("pois"); 346 } 347 348 ModifiableCompositeMetadata poisMetadata = serviceMetadata.getCompositeMetadata("pois", true); 349 int count = 1; 350 351 if (pois != null) 352 { 353 for (Map<String, Object> poi : pois) 354 { 355 ModifiableCompositeMetadata poiMetadata = poisMetadata.getCompositeMetadata("poi-" + count++, true); 356 String type = (String) poi.get("gtype"); 357 poiMetadata.setMetadata("type", type); 358 359 String poiTitle = (String) poi.get("title"); 360 String poiDesc = (String) poi.get("description"); 361 362 poiMetadata.setMetadata("title", poiTitle); 363 poiMetadata.setMetadata("description", poiDesc); 364 365 if ("marker".equals(type)) 366 { 367 String poiIcon = (String) poi.get("icon"); 368 double poiLat = ((Number) poi.get("lat")).doubleValue(); 369 double poiLng = ((Number) poi.get("lng")).doubleValue(); 370 371 poiMetadata.setMetadata("icon", poiIcon); 372 poiMetadata.setMetadata("lat", poiLat); 373 poiMetadata.setMetadata("lng", poiLng); 374 } 375 else if ("polygon".equals(type)) 376 { 377 String poiColor = (String) poi.get("color"); 378 poiMetadata.setMetadata("color", poiColor); 379 380 @SuppressWarnings("unchecked") 381 List<Map<String, Number>> points = (List<Map<String, Number>>) poi.get("points"); 382 383 ModifiableCompositeMetadata pointsMetadata = poiMetadata.getCompositeMetadata("points", true); 384 int count2 = 1; 385 386 for (Map<String, Number> point : points) 387 { 388 // 4-digit number 389 String strCount = StringUtils.leftPad(String.valueOf(count2++), 4, '0'); 390 ModifiableCompositeMetadata pointMetadata = pointsMetadata.getCompositeMetadata("point-" + strCount, true); 391 392 double pointLat = point.get("lat").doubleValue(); 393 double pointLng = point.get("lng").doubleValue(); 394 395 pointMetadata.setMetadata("lat", pointLat); 396 pointMetadata.setMetadata("lng", pointLng); 397 } 398 } 399 else 400 { 401 throw new IllegalArgumentException("Unknown POI type " + type); 402 } 403 } 404 } 405 } 406 407 private void _getAndSaveParameter (String prefix, ServiceParameter param, Map<String, Object> formValues, ModifiableCompositeMetadata metadataHolder, Map<String, Errors> allErrors) 408 { 409 Object objectValue = formValues.get(prefix + param.getId()); 410 411 Validator validator = param.getValidator(); 412 413 if (validator != null) 414 { 415 Errors errors = new Errors(); 416 417 if (objectValue != null && objectValue instanceof List && !param.isMultiple()) 418 { 419 Object singleValue = null; 420 if (((List) objectValue).size() > 0) 421 { 422 singleValue = ((List) objectValue).get(0); 423 } 424 425 validator.validate(singleValue, errors); 426 } 427 else 428 { 429 validator.validate(objectValue, errors); 430 } 431 432 if (errors.hasErrors()) 433 { 434 allErrors.put(param.getId(), errors); 435 return; 436 } 437 } 438 439 if (objectValue != null) 440 { 441 if (param.isMultiple()) 442 { 443 List<String> stringValues = new ArrayList<>(); 444 if (objectValue instanceof List) 445 { 446 int length = ((List) objectValue).size(); 447 for (int i = 0; i < length; i++) 448 { 449 stringValues.add(ParameterHelper.valueToString(((List) objectValue).get(i))); 450 } 451 } 452 else 453 { 454 stringValues.add(ParameterHelper.valueToString(objectValue)); 455 } 456 metadataHolder.setMetadata(param.getId(), stringValues.toArray(new String[stringValues.size()])); 457 } 458 else 459 { 460 Object singleValue = objectValue; 461 if (objectValue instanceof List) 462 { 463 if (((List) objectValue).size() > 0) 464 { 465 singleValue = ((List) objectValue).get(0); 466 } 467 } 468 metadataHolder.setMetadata(param.getId(), ParameterHelper.valueToString(singleValue)); 469 } 470 } 471 } 472}