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