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