001/* 002 * Copyright 2018 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.web.repository.page; 017 018import java.io.IOException; 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023import java.util.Optional; 024 025import org.apache.avalon.framework.component.Component; 026import org.apache.avalon.framework.context.Context; 027import org.apache.avalon.framework.context.ContextException; 028import org.apache.avalon.framework.context.Contextualizable; 029import org.apache.avalon.framework.service.ServiceException; 030import org.apache.avalon.framework.service.ServiceManager; 031import org.apache.avalon.framework.service.Serviceable; 032import org.apache.cocoon.ProcessingException; 033import org.apache.cocoon.components.ContextHelper; 034import org.apache.cocoon.environment.Request; 035import org.apache.commons.lang.StringUtils; 036import org.apache.commons.lang3.tuple.ImmutablePair; 037import org.apache.commons.lang3.tuple.Pair; 038 039import org.ametys.core.observation.Event; 040import org.ametys.core.observation.ObservationManager; 041import org.ametys.core.ui.Callable; 042import org.ametys.core.user.CurrentUserProvider; 043import org.ametys.plugins.repository.AmetysObjectResolver; 044import org.ametys.plugins.repository.UnknownAmetysObjectException; 045import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder; 046import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder; 047import org.ametys.runtime.i18n.I18nizableText; 048import org.ametys.runtime.model.DefinitionContext; 049import org.ametys.runtime.model.ModelItem; 050import org.ametys.runtime.model.View; 051import org.ametys.runtime.model.ViewItem; 052import org.ametys.runtime.model.ViewItemContainer; 053import org.ametys.runtime.plugin.component.AbstractLogEnabled; 054import org.ametys.web.ObservationConstants; 055import org.ametys.web.WebConstants; 056import org.ametys.web.parameters.ParametersManager; 057import org.ametys.web.parameters.view.ViewParametersDAO; 058import org.ametys.web.parameters.view.ViewParametersManager; 059import org.ametys.web.parameters.view.ViewParametersModel; 060import org.ametys.web.repository.page.ZoneItem.ZoneType; 061import org.ametys.web.service.Service; 062import org.ametys.web.service.ServiceExtensionPoint; 063 064/** 065 * Class containing callables to retrieve and configure service parameters 066 */ 067public class ZoneItemManager extends AbstractLogEnabled implements Component, Serviceable, Contextualizable 068{ 069 /** Avalon Role */ 070 public static final String ROLE = ZoneItemManager.class.getName(); 071 072 /** Constant for untouched binary metadata. */ 073 protected static final String __SERVICE_PARAM_UNTOUCHED_BINARY = "untouched"; 074 075 private ServiceExtensionPoint _serviceExtensionPoint; 076 private AmetysObjectResolver _resolver; 077 private ObservationManager _observationManager; 078 private CurrentUserProvider _currentUserProvider; 079 private ParametersManager _parametersManager; 080 private ViewParametersManager _viewParametersManager; 081 private Context _context; 082 083 public void service(ServiceManager manager) throws ServiceException 084 { 085 _serviceExtensionPoint = (ServiceExtensionPoint) manager.lookup(ServiceExtensionPoint.ROLE); 086 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 087 _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE); 088 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 089 _parametersManager = (ParametersManager) manager.lookup(ParametersManager.ROLE); 090 _viewParametersManager = (ViewParametersManager) manager.lookup(ViewParametersManager.ROLE); 091 } 092 093 public void contextualize(Context context) throws ContextException 094 { 095 _context = context; 096 } 097 098 /** 099 * Retrieves the parameter definitions of the given service 100 * @param serviceId Identifier of the service 101 * @param pageId the page id 102 * @param zoneItemId the zone item id 103 * @param zoneName the zone name 104 * @return the parameter definitions 105 * @throws ProcessingException if an error occurs 106 */ 107 @Callable 108 public Map<String, Object> getServiceParameterDefinitions(String serviceId, String pageId, String zoneItemId, String zoneName) throws ProcessingException 109 { 110 _setRequestAttribute(serviceId, pageId, zoneItemId, zoneName); 111 112 Map<String, Object> response = new HashMap<>(); 113 114 Service service = _serviceExtensionPoint.getExtension(serviceId); 115 116 response.put("id", serviceId); 117 response.put("url", service.getURL()); 118 response.put("label", service.getLabel()); 119 response.put("height", service.getCreationBoxHeight()); 120 response.put("width", service.getCreationBoxWidth()); 121 122 View serviceView = service.getView(); 123 124 SitemapElement sitemapElement = _resolver.resolveById(pageId); 125 126 Map<String, ViewParametersModel> serviceViewParametersModels = _viewParametersManager.getServiceViewParametersModels(sitemapElement.getSite().getSkinId(), serviceId); 127 128 Pair<ViewItemContainer, Integer> containerAndIndex = _getXSLTViewItemContainerAndIndex(serviceView); 129 if (containerAndIndex != null) 130 { 131 for (String viewName : serviceViewParametersModels.keySet()) 132 { 133 ViewParametersModel viewParameters = serviceViewParametersModels.get(viewName); 134 Collection< ? extends ModelItem> modelItems = viewParameters.getModelItems(); 135 136 String prefix = ViewParametersDAO.PREFIX_SERVICE + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR; 137 Optional<Integer> index = Optional.of(containerAndIndex.getRight() + 1); //add 1 to the XSLT parameter index to include the view parameters after the XSLT one 138 List<ModelItem> includeModelItems = _viewParametersManager.includeModelItems(modelItems, prefix, containerAndIndex.getLeft(), index); //add 1 to the XSL 139 _viewParametersManager.setDisableConditions(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME, viewName, includeModelItems); 140 } 141 } 142 143 response.put("parameters", serviceView.toJSON(DefinitionContext.newInstance().withEdition(true))); 144 145 return response; 146 } 147 148 private Pair<ViewItemContainer, Integer> _getXSLTViewItemContainerAndIndex(ViewItemContainer viewItemContainer) 149 { 150 List<ViewItem> viewItems = viewItemContainer.getViewItems(); 151 for (int i = 0; i < viewItems.size(); i++) 152 { 153 ViewItem viewItem = viewItems.get(i); 154 155 if (ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME.equals(viewItem.getName())) 156 { 157 return ImmutablePair.of(viewItemContainer, i); 158 } 159 else if (viewItem instanceof ViewItemContainer) 160 { 161 Pair<ViewItemContainer, Integer> xsltContainerAndIndex = _getXSLTViewItemContainerAndIndex((ViewItemContainer) viewItem); 162 if (xsltContainerAndIndex != null) 163 { 164 return xsltContainerAndIndex; 165 } 166 } 167 } 168 169 return null; 170 } 171 172 private void _setRequestAttribute(String serviceId, String pageId, String zoneItemId, String zoneName) 173 { 174 Request request = ContextHelper.getRequest(_context); 175 176 request.setAttribute(WebConstants.REQUEST_ATTR_SERVICE_ID, serviceId); 177 request.setAttribute(WebConstants.REQUEST_ATTR_PAGE_ID, pageId); 178 request.setAttribute(WebConstants.REQUEST_ATTR_ZONEITEM_ID, zoneItemId); 179 request.setAttribute(WebConstants.REQUEST_ATTR_ZONE_NAME, zoneName); 180 } 181 182 /** 183 * Get the service parameter values 184 * @param zoneItemId the zone item id 185 * @param serviceId the service Id 186 * @return the values 187 */ 188 @Callable 189 public Map<String, Object> getServiceParameterValues(String zoneItemId, String serviceId) 190 { 191 Service service = _serviceExtensionPoint.getExtension(serviceId); 192 ZoneItem zoneItem = _resolver.resolveById(zoneItemId); 193 194 Map<String, Object> response = new HashMap<>(); 195 Collection<ModelItem> serviceParameterDefinitions = service.getParameters().values(); 196 ModelAwareDataHolder dataHolder = zoneItem.getServiceParameters(); 197 198 Map<String, Object> values = _parametersManager.getParametersValues(serviceParameterDefinitions, dataHolder, ""); 199 values.putAll(_getServiceViewParametersValues(zoneItem)); 200 201 response.put("values", values); 202 203 List<Map<String, Object>> repeaters = _parametersManager.getRepeatersValues(serviceParameterDefinitions, dataHolder, ""); 204 response.put("repeaters", repeaters); 205 206 return response; 207 } 208 209 /** 210 * Get the service view parameters values 211 * @param zoneItem the zone item 212 * @return the values 213 */ 214 protected Map<String, Object> _getServiceViewParametersValues(ZoneItem zoneItem) 215 { 216 Map<String, Object> values = new HashMap<>(); 217 218 String skinId = zoneItem.getZone() 219 .getSitemapElement() 220 .getSite() 221 .getSkinId(); 222 223 Map<String, ViewParametersModel> serviceViewParametersModels = _viewParametersManager.getServiceViewParametersModels(skinId, zoneItem.getServiceId()); 224 for (String viewName : serviceViewParametersModels.keySet()) 225 { 226 ViewParametersModel serviceViewParameters = serviceViewParametersModels.get(viewName); 227 ModelAwareDataHolder serviceViewParametersHolder = zoneItem.getServiceViewParametersHolder(viewName); 228 229 Map<String, Object> serviceValues = _parametersManager.getParametersValues(serviceViewParameters.getModelItems(), serviceViewParametersHolder, ""); 230 String prefix = ViewParametersDAO.PREFIX_SERVICE + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR; 231 values.putAll(_parametersManager.addPrefixToParameters(serviceValues, prefix)); 232 } 233 234 return values; 235 } 236 237 /** 238 * Add the service to the given zone on given page 239 * @param pageId The page identifier 240 * @param zoneName The zone name 241 * @param serviceId The identifier of the service to add 242 * @param parameterValues the service parameter values. Can be empty 243 * @return The result with the identifiers of updated page, zone and zone item 244 * @throws IOException if an error occurred while saving parameters 245 */ 246 @Callable 247 public Map<String, Object> addService(String pageId, String zoneName, String serviceId, Map<String, Object> parameterValues) throws IOException 248 { 249 if (StringUtils.isEmpty(serviceId) || StringUtils.isEmpty(pageId) || StringUtils.isEmpty(zoneName)) 250 { 251 throw new IllegalArgumentException("ServiceId, PageId or ZoneName is missing"); 252 } 253 254 // Check the service 255 Service service = null; 256 try 257 { 258 service = _serviceExtensionPoint.getExtension(serviceId); 259 } 260 catch (IllegalArgumentException e) 261 { 262 throw new IllegalArgumentException("Service with id '" + serviceId + "' does not exist", e); 263 } 264 265 try 266 { 267 SitemapElement sitemapElement = _resolver.resolveById(pageId); 268 if (!(sitemapElement instanceof ModifiableSitemapElement modifiableSitemapElement)) 269 { 270 throw new IllegalArgumentException("Can not affect service on a non-modifiable page " + pageId); 271 } 272 273 if (sitemapElement.getTemplate() == null) 274 { 275 throw new IllegalArgumentException("Can not affect service on a non-container page " + pageId); 276 } 277 278 ModifiableZone zone; 279 if (modifiableSitemapElement.hasZone(zoneName)) 280 { 281 zone = modifiableSitemapElement.getZone(zoneName); 282 } 283 else 284 { 285 zone = modifiableSitemapElement.createZone(zoneName); 286 } 287 288 ModifiableZoneItem zoneItem = zone.addZoneItem(); 289 zoneItem.setType(ZoneType.SERVICE); 290 zoneItem.setServiceId(serviceId); 291 292 Map<String, List<I18nizableText>> allErrors = _setParameterValues(parameterValues, service, zoneItem); 293 294 Map<String, Object> results = new HashMap<>(); 295 if (!allErrors.isEmpty()) 296 { 297 results.put("errors", allErrors); 298 return results; 299 } 300 301 modifiableSitemapElement.saveChanges(); 302 303 Map<String, Object> eventParams = new HashMap<>(); 304 eventParams.put(ObservationConstants.ARGS_SITEMAP_ELEMENT, sitemapElement); 305 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_ID, zoneItem.getId()); 306 eventParams.put(ObservationConstants.ARGS_ZONE_TYPE, ZoneType.SERVICE); 307 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_SERVICE, serviceId); 308 _observationManager.notify(new Event(ObservationConstants.EVENT_ZONEITEM_ADDED, _currentUserProvider.getUser(), eventParams)); 309 310 results.put("id", sitemapElement.getId()); 311 results.put("zoneitem-id", zoneItem.getId()); 312 results.put("zone-name", zone.getName()); 313 314 return results; 315 } 316 catch (UnknownAmetysObjectException e) 317 { 318 throw new IllegalArgumentException("An error occured adding the service '" + serviceId + "' on the page '" + pageId + "'", e); 319 } 320 } 321 322 /** 323 * Edit the parameter values of the given service 324 * @param zoneItemId The identifier of the zone item holding the service 325 * @param serviceId The service identifier 326 * @param parameterValues the service parameter values to update 327 * @return The result with the identifiers of updated page, zone and zone item 328 * @throws IOException if an error occurs while saving parameters 329 */ 330 @Callable 331 public Map<String, Object> editServiceParameterValues(String zoneItemId, String serviceId, Map<String, Object> parameterValues) throws IOException 332 { 333 Service service = null; 334 try 335 { 336 service = _serviceExtensionPoint.getExtension(serviceId); 337 } 338 catch (IllegalArgumentException e) 339 { 340 throw new IllegalArgumentException("Service with id '" + serviceId + "' does not exist", e); 341 } 342 343 ZoneItem zoneItem = _resolver.resolveById(zoneItemId); 344 if (!(zoneItem instanceof ModifiableZoneItem)) 345 { 346 throw new IllegalArgumentException("Can not configure service on a non-modifiable zone item " + zoneItemId); 347 } 348 349 ModifiableZoneItem modifiableZoneItem = (ModifiableZoneItem) zoneItem; 350 351 Map<String, List<I18nizableText>> allErrors = _setParameterValues(parameterValues, service, modifiableZoneItem); 352 353 Map<String, Object> results = new HashMap<>(); 354 if (!allErrors.isEmpty()) 355 { 356 results.put("errors", allErrors); 357 return results; 358 } 359 360 modifiableZoneItem.saveChanges(); 361 362 Map<String, Object> eventParams = new HashMap<>(); 363 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM, zoneItem); 364 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_ID, zoneItem.getId()); 365 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_SERVICE, serviceId); 366 _observationManager.notify(new Event(ObservationConstants.EVENT_SERVICE_MODIFIED, _currentUserProvider.getUser(), eventParams)); 367 368 results.put("id", zoneItem.getZone().getSitemapElement().getId()); 369 results.put("zoneitem-id", zoneItem.getId()); 370 results.put("zone-name", zoneItem.getZone().getName()); 371 372 return results; 373 } 374 375 /** 376 * Set the parameter values for the service (with view parameters) 377 * @param parameterValues the parameter values 378 * @param service the service 379 * @param zoneItem the zone item 380 * @return the map of error 381 */ 382 protected Map<String, List<I18nizableText>> _setParameterValues(Map<String, Object> parameterValues, Service service, ModifiableZoneItem zoneItem) 383 { 384 ModifiableModelAwareDataHolder serviceDataHolder = zoneItem.getServiceParameters(); 385 Map<String, ModelItem> definitions = service.getParameters(); 386 Map<String, List<I18nizableText>> allErrors = _parametersManager.setParameterValues(serviceDataHolder, definitions.values(), parameterValues); 387 388 if (parameterValues.containsKey(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME)) 389 { 390 String viewName = (String) parameterValues.get(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME); 391 392 String skinId = zoneItem.getZone() 393 .getSitemapElement() 394 .getSite() 395 .getSkinId(); 396 Optional<ViewParametersModel> serviceViewParametersModel = _viewParametersManager.getServiceViewParametersModel(skinId, service.getId(), viewName); 397 398 if (serviceViewParametersModel.isPresent()) 399 { 400 ModifiableModelAwareDataHolder serviceParametersHolder = zoneItem.getServiceViewParametersHolder(viewName); 401 String prefix = ViewParametersDAO.PREFIX_SERVICE + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR; 402 Map<String, Object> viewParametersValues = _parametersManager.getParametersStartWithPrefix(parameterValues, prefix); 403 allErrors.putAll(_parametersManager.setParameterValues(serviceParametersHolder, serviceViewParametersModel.get().getModelItems(), viewParametersValues)); 404 } 405 } 406 407 return allErrors; 408 } 409}