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 204 Map<String, Object> response = new HashMap<>(); 205 Collection<ModelItem> serviceParameterDefinitions = service.getParameters().values(); 206 ModelAwareDataHolder dataHolder = zoneItem.getServiceParameters(); 207 208 Map<String, Object> values = _parametersManager.getParametersValues(serviceParameterDefinitions, dataHolder, ""); 209 values.putAll(_getServiceViewParametersValues(zoneItem)); 210 211 response.put("values", values); 212 213 List<Map<String, Object>> repeaters = _parametersManager.getRepeatersValues(serviceParameterDefinitions, dataHolder, ""); 214 response.put("repeaters", repeaters); 215 216 return response; 217 } 218 219 /** 220 * Get the service view parameters values 221 * @param zoneItem the zone item 222 * @return the values 223 */ 224 protected Map<String, Object> _getServiceViewParametersValues(ZoneItem zoneItem) 225 { 226 Map<String, Object> values = new HashMap<>(); 227 228 String skinId = zoneItem.getZone() 229 .getSitemapElement() 230 .getSite() 231 .getSkinId(); 232 233 Map<String, ViewParametersModel> serviceViewParametersModels = _viewParametersManager.getServiceViewParametersModels(skinId, zoneItem.getServiceId()); 234 for (String viewName : serviceViewParametersModels.keySet()) 235 { 236 ViewParametersModel serviceViewParameters = serviceViewParametersModels.get(viewName); 237 ModelAwareDataHolder serviceViewParametersHolder = zoneItem.getServiceViewParametersHolder(viewName); 238 239 Map<String, Object> serviceValues = _parametersManager.getParametersValues(serviceViewParameters.getModelItems(), serviceViewParametersHolder, ""); 240 String prefix = ViewParametersDAO.PREFIX_SERVICE + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR; 241 values.putAll(_parametersManager.addPrefixToParameters(serviceValues, prefix)); 242 } 243 244 return values; 245 } 246 247 /** 248 * Add the service to the given zone on given page 249 * @param pageId The page identifier 250 * @param zoneName The zone name 251 * @param serviceId The identifier of the service to add 252 * @param parameterValues the service parameter values. Can be empty 253 * @return The result with the identifiers of updated page, zone and zone item 254 * @throws IOException if an error occurred while saving parameters 255 */ 256 @Callable 257 public Map<String, Object> addService(String pageId, String zoneName, String serviceId, Map<String, Object> parameterValues) throws IOException 258 { 259 if (StringUtils.isEmpty(serviceId) || StringUtils.isEmpty(pageId) || StringUtils.isEmpty(zoneName)) 260 { 261 throw new IllegalArgumentException("ServiceId, PageId or ZoneName is missing"); 262 } 263 264 // Check the service 265 Service service = null; 266 try 267 { 268 service = _serviceExtensionPoint.getExtension(serviceId); 269 } 270 catch (IllegalArgumentException e) 271 { 272 throw new IllegalArgumentException("Service with id '" + serviceId + "' does not exist", e); 273 } 274 275 try 276 { 277 SitemapElement sitemapElement = _resolver.resolveById(pageId); 278 if (!(sitemapElement instanceof ModifiableSitemapElement modifiableSitemapElement)) 279 { 280 throw new IllegalArgumentException("Can not affect service on a non-modifiable page " + pageId); 281 } 282 283 if (sitemapElement.getTemplate() == null) 284 { 285 throw new IllegalArgumentException("Can not affect service on a non-container page " + pageId); 286 } 287 288 ModifiableZone zone; 289 if (modifiableSitemapElement.hasZone(zoneName)) 290 { 291 zone = modifiableSitemapElement.getZone(zoneName); 292 } 293 else 294 { 295 zone = modifiableSitemapElement.createZone(zoneName); 296 } 297 298 ModifiableZoneItem zoneItem = zone.addZoneItem(); 299 zoneItem.setType(ZoneType.SERVICE); 300 zoneItem.setServiceId(serviceId); 301 302 Map<String, List<I18nizableText>> allErrors = _setParameterValues(parameterValues, service, zoneItem); 303 304 Map<String, Object> results = new HashMap<>(); 305 if (!allErrors.isEmpty()) 306 { 307 results.put("errors", allErrors); 308 return results; 309 } 310 311 modifiableSitemapElement.saveChanges(); 312 313 Map<String, Object> eventParams = new HashMap<>(); 314 eventParams.put(ObservationConstants.ARGS_SITEMAP_ELEMENT, sitemapElement); 315 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_ID, zoneItem.getId()); 316 eventParams.put(ObservationConstants.ARGS_ZONE_TYPE, ZoneType.SERVICE); 317 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_SERVICE, serviceId); 318 _observationManager.notify(new Event(ObservationConstants.EVENT_ZONEITEM_ADDED, _currentUserProvider.getUser(), eventParams)); 319 320 results.put("id", sitemapElement.getId()); 321 results.put("zoneitem-id", zoneItem.getId()); 322 results.put("zone-name", zone.getName()); 323 324 return results; 325 } 326 catch (UnknownAmetysObjectException e) 327 { 328 throw new IllegalArgumentException("An error occured adding the service '" + serviceId + "' on the page '" + pageId + "'", e); 329 } 330 } 331 332 /** 333 * Edit the parameter values of the given service 334 * @param zoneItemId The identifier of the zone item holding the service 335 * @param serviceId The service identifier 336 * @param parameterValues the service parameter values to update 337 * @return The result with the identifiers of updated page, zone and zone item 338 * @throws IOException if an error occurs while saving parameters 339 */ 340 @Callable 341 public Map<String, Object> editServiceParameterValues(String zoneItemId, String serviceId, Map<String, Object> parameterValues) throws IOException 342 { 343 Service service = null; 344 try 345 { 346 service = _serviceExtensionPoint.getExtension(serviceId); 347 } 348 catch (IllegalArgumentException e) 349 { 350 throw new IllegalArgumentException("Service with id '" + serviceId + "' does not exist", e); 351 } 352 353 ZoneItem zoneItem = _resolver.resolveById(zoneItemId); 354 if (!(zoneItem instanceof ModifiableZoneItem)) 355 { 356 throw new IllegalArgumentException("Can not configure service on a non-modifiable zone item " + zoneItemId); 357 } 358 359 ModifiableZoneItem modifiableZoneItem = (ModifiableZoneItem) zoneItem; 360 361 Map<String, List<I18nizableText>> allErrors = _setParameterValues(parameterValues, service, modifiableZoneItem); 362 363 Map<String, Object> results = new HashMap<>(); 364 if (!allErrors.isEmpty()) 365 { 366 results.put("errors", allErrors); 367 return results; 368 } 369 370 modifiableZoneItem.saveChanges(); 371 372 Map<String, Object> eventParams = new HashMap<>(); 373 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM, zoneItem); 374 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_ID, zoneItem.getId()); 375 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_SERVICE, serviceId); 376 _observationManager.notify(new Event(ObservationConstants.EVENT_SERVICE_MODIFIED, _currentUserProvider.getUser(), eventParams)); 377 378 results.put("id", zoneItem.getZone().getSitemapElement().getId()); 379 results.put("zoneitem-id", zoneItem.getId()); 380 results.put("zone-name", zoneItem.getZone().getName()); 381 382 return results; 383 } 384 385 /** 386 * Set the parameter values for the service (with view parameters) 387 * @param parameterValues the parameter values 388 * @param service the service 389 * @param zoneItem the zone item 390 * @return the map of error 391 */ 392 protected Map<String, List<I18nizableText>> _setParameterValues(Map<String, Object> parameterValues, Service service, ModifiableZoneItem zoneItem) 393 { 394 ModifiableModelAwareDataHolder serviceDataHolder = zoneItem.getServiceParameters(); 395 Map<String, ModelItem> definitions = service.getParameters(); 396 Map<String, List<I18nizableText>> allErrors = _parametersManager.setParameterValues(serviceDataHolder, definitions.values(), parameterValues); 397 398 if (parameterValues.containsKey(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME)) 399 { 400 String viewName = (String) parameterValues.get(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME); 401 402 String skinId = zoneItem.getZone() 403 .getSitemapElement() 404 .getSite() 405 .getSkinId(); 406 Optional<ViewParametersModel> serviceViewParametersModel = _viewParametersManager.getServiceViewParametersModel(skinId, service.getId(), viewName); 407 408 if (serviceViewParametersModel.isPresent()) 409 { 410 ModifiableModelAwareDataHolder serviceParametersHolder = zoneItem.getServiceViewParametersHolder(viewName); 411 String prefix = ViewParametersDAO.PREFIX_SERVICE + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR; 412 Map<String, Object> viewParametersValues = _parametersManager.getParametersStartWithPrefix(parameterValues, prefix); 413 allErrors.putAll(_parametersManager.setParameterValues(serviceParametersHolder, serviceViewParametersModel.get().getModelItems(), viewParametersValues)); 414 } 415 } 416 417 return allErrors; 418 } 419}