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.ViewParametersModel; 058import org.ametys.web.parameters.view.ViewParametersDAO; 059import org.ametys.web.parameters.view.ViewParametersManager; 060import org.ametys.web.repository.page.Page.PageType; 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 View serviceView = service.getView(); 124 125 Page page = _resolver.resolveById(pageId); 126 127 Map<String, ViewParametersModel> serviceViewParametersModels = _viewParametersManager.getServiceViewParametersModels(page.getSite().getSkinId(), serviceId); 128 129 Pair<ViewItemContainer, Integer> containerAndIndex = _getXSLTViewItemContainerAndIndex(serviceView); 130 if (containerAndIndex != null) 131 { 132 for (String viewName : serviceViewParametersModels.keySet()) 133 { 134 ViewParametersModel viewParameters = serviceViewParametersModels.get(viewName); 135 Collection< ? extends ModelItem> modelItems = viewParameters.getModelItems(); 136 137 String prefix = ViewParametersDAO.PREFIX_SERVICE + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR; 138 Optional<Integer> index = Optional.of(containerAndIndex.getRight() + 1); //add 1 to the XSLT parameter index to include the view parameters after the XSLT one 139 List<ModelItem> includeModelItems = _viewParametersManager.includeModelItems(modelItems, prefix, containerAndIndex.getLeft(), index); //add 1 to the XSL 140 _viewParametersManager.setDisableConditions(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME, viewName, includeModelItems); 141 } 142 } 143 144 response.put("parameters", serviceView.toJSON(DefinitionContext.newInstance().withEdition(true))); 145 146 return response; 147 } 148 149 private Pair<ViewItemContainer, Integer> _getXSLTViewItemContainerAndIndex(ViewItemContainer viewItemContainer) 150 { 151 List<ViewItem> viewItems = viewItemContainer.getViewItems(); 152 for (int i = 0; i < viewItems.size(); i++) 153 { 154 ViewItem viewItem = viewItems.get(i); 155 156 if (ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME.equals(viewItem.getName())) 157 { 158 return ImmutablePair.of(viewItemContainer, i); 159 } 160 else if (viewItem instanceof ViewItemContainer) 161 { 162 Pair<ViewItemContainer, Integer> xsltContainerAndIndex = _getXSLTViewItemContainerAndIndex((ViewItemContainer) viewItem); 163 if (xsltContainerAndIndex != null) 164 { 165 return xsltContainerAndIndex; 166 } 167 } 168 } 169 170 return null; 171 } 172 173 private void _setRequestAttribute(String serviceId, String pageId, String zoneItemId, String zoneName) 174 { 175 Request request = ContextHelper.getRequest(_context); 176 177 request.setAttribute(WebConstants.REQUEST_ATTR_SERVICE_ID, serviceId); 178 request.setAttribute(WebConstants.REQUEST_ATTR_PAGE_ID, pageId); 179 request.setAttribute(WebConstants.REQUEST_ATTR_ZONEITEM_ID, zoneItemId); 180 request.setAttribute(WebConstants.REQUEST_ATTR_ZONE_NAME, zoneName); 181 } 182 183 /** 184 * Get the service parameter values 185 * @param zoneItemId the zone item id 186 * @param serviceId the service Id 187 * @return the values 188 */ 189 @Callable 190 public Map<String, Object> getServiceParameterValues(String zoneItemId, String serviceId) 191 { 192 Service service = _serviceExtensionPoint.getExtension(serviceId); 193 ZoneItem zoneItem = _resolver.resolveById(zoneItemId); 194 195 Map<String, Object> response = new HashMap<>(); 196 Collection<ModelItem> serviceParameterDefinitions = service.getParameters().values(); 197 ModelAwareDataHolder dataHolder = zoneItem.getServiceParameters(); 198 199 Map<String, Object> values = _parametersManager.getParametersValues(serviceParameterDefinitions, dataHolder, ""); 200 values.putAll(_getServiceViewParametersValues(zoneItem)); 201 202 response.put("values", values); 203 204 List<Map<String, Object>> repeaters = _parametersManager.getRepeatersValues(serviceParameterDefinitions, dataHolder, ""); 205 response.put("repeaters", repeaters); 206 207 return response; 208 } 209 210 /** 211 * Get the service view parameters values 212 * @param zoneItem the zone item 213 * @return the values 214 */ 215 protected Map<String, Object> _getServiceViewParametersValues(ZoneItem zoneItem) 216 { 217 Map<String, Object> values = new HashMap<>(); 218 219 String skinId = zoneItem.getZone() 220 .getPage() 221 .getSite() 222 .getSkinId(); 223 224 Map<String, ViewParametersModel> serviceViewParametersModels = _viewParametersManager.getServiceViewParametersModels(skinId, zoneItem.getServiceId()); 225 for (String viewName : serviceViewParametersModels.keySet()) 226 { 227 ViewParametersModel serviceViewParameters = serviceViewParametersModels.get(viewName); 228 ModelAwareDataHolder serviceViewParametersHolder = zoneItem.getServiceViewParametersHolder(viewName); 229 230 Map<String, Object> serviceValues = _parametersManager.getParametersValues(serviceViewParameters.getModelItems(), serviceViewParametersHolder, ""); 231 String prefix = ViewParametersDAO.PREFIX_SERVICE + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR; 232 values.putAll(_parametersManager.addPrefixToParameters(serviceValues, prefix)); 233 } 234 235 return values; 236 } 237 238 /** 239 * Add the service to the given zone on given page 240 * @param pageId The page identifier 241 * @param zoneName The zone name 242 * @param serviceId The identifier of the service to add 243 * @param parameterValues the service parameter values. Can be empty 244 * @return The result with the identifiers of updated page, zone and zone item 245 * @throws IOException if an error occurred while saving parameters 246 */ 247 @Callable 248 public Map<String, Object> addService(String pageId, String zoneName, String serviceId, Map<String, Object> parameterValues) throws IOException 249 { 250 if (StringUtils.isEmpty(serviceId) || StringUtils.isEmpty(pageId) || StringUtils.isEmpty(zoneName)) 251 { 252 throw new IllegalArgumentException("ServiceId, PageId or ZoneName is missing"); 253 } 254 255 // Check the service 256 Service service = null; 257 try 258 { 259 service = _serviceExtensionPoint.getExtension(serviceId); 260 } 261 catch (IllegalArgumentException e) 262 { 263 throw new IllegalArgumentException("Service with id '" + serviceId + "' does not exist", e); 264 } 265 266 try 267 { 268 Page page = _resolver.resolveById(pageId); 269 if (!(page instanceof ModifiablePage)) 270 { 271 throw new IllegalArgumentException("Can not affect service on a non-modifiable page " + pageId); 272 } 273 274 ModifiablePage modifiablePage = (ModifiablePage) page; 275 276 if (page.getType() != PageType.CONTAINER) 277 { 278 throw new IllegalArgumentException("Can not affect service on a non-container page " + pageId); 279 } 280 281 ModifiableZone zone; 282 if (page.hasZone(zoneName)) 283 { 284 zone = modifiablePage.getZone(zoneName); 285 } 286 else 287 { 288 zone = modifiablePage.createZone(zoneName); 289 } 290 291 ModifiableZoneItem zoneItem = zone.addZoneItem(); 292 zoneItem.setType(ZoneType.SERVICE); 293 zoneItem.setServiceId(serviceId); 294 295 Map<String, List<I18nizableText>> allErrors = _setParameterValues(parameterValues, service, zoneItem); 296 297 Map<String, Object> results = new HashMap<>(); 298 if (!allErrors.isEmpty()) 299 { 300 results.put("errors", allErrors); 301 return results; 302 } 303 304 modifiablePage.saveChanges(); 305 306 Map<String, Object> eventParams = new HashMap<>(); 307 eventParams.put(ObservationConstants.ARGS_PAGE, page); 308 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_ID, zoneItem.getId()); 309 eventParams.put(ObservationConstants.ARGS_ZONE_TYPE, ZoneType.SERVICE); 310 _observationManager.notify(new Event(ObservationConstants.EVENT_ZONEITEM_ADDED, _currentUserProvider.getUser(), eventParams)); 311 312 results.put("id", page.getId()); 313 results.put("zoneitem-id", zoneItem.getId()); 314 results.put("zone-name", zone.getName()); 315 316 return results; 317 } 318 catch (UnknownAmetysObjectException e) 319 { 320 throw new IllegalArgumentException("An error occured adding the service '" + serviceId + "' on the page '" + pageId + "'", e); 321 } 322 } 323 324 /** 325 * Edit the parameter values of the given service 326 * @param zoneItemId The identifier of the zone item holding the service 327 * @param serviceId The service identifier 328 * @param parameterValues the service parameter values to update 329 * @return The result with the identifiers of updated page, zone and zone item 330 * @throws IOException if an error occurs while saving parameters 331 */ 332 @Callable 333 public Map<String, Object> editServiceParameterValues(String zoneItemId, String serviceId, Map<String, Object> parameterValues) throws IOException 334 { 335 Service service = null; 336 try 337 { 338 service = _serviceExtensionPoint.getExtension(serviceId); 339 } 340 catch (IllegalArgumentException e) 341 { 342 throw new IllegalArgumentException("Service with id '" + serviceId + "' does not exist", e); 343 } 344 345 ZoneItem zoneItem = _resolver.resolveById(zoneItemId); 346 if (!(zoneItem instanceof ModifiableZoneItem)) 347 { 348 throw new IllegalArgumentException("Can not configure service on a non-modifiable zone item " + zoneItemId); 349 } 350 351 ModifiableZoneItem modifiableZoneItem = (ModifiableZoneItem) zoneItem; 352 353 Map<String, List<I18nizableText>> allErrors = _setParameterValues(parameterValues, service, modifiableZoneItem); 354 355 Map<String, Object> results = new HashMap<>(); 356 if (!allErrors.isEmpty()) 357 { 358 results.put("errors", allErrors); 359 return results; 360 } 361 362 modifiableZoneItem.saveChanges(); 363 364 Map<String, Object> eventParams = new HashMap<>(); 365 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM, zoneItem); 366 eventParams.put(ObservationConstants.ARGS_ZONE_ITEM_ID, zoneItem.getId()); 367 _observationManager.notify(new Event(ObservationConstants.EVENT_SERVICE_MODIFIED, _currentUserProvider.getUser(), eventParams)); 368 369 results.put("id", zoneItem.getZone().getPage().getId()); 370 results.put("zoneitem-id", zoneItem.getId()); 371 results.put("zone-name", zoneItem.getZone().getName()); 372 373 return results; 374 } 375 376 /** 377 * Set the parameter values for the service (with view parameters) 378 * @param parameterValues the parameter values 379 * @param service the service 380 * @param zoneItem the zone item 381 * @return the map of error 382 */ 383 protected Map<String, List<I18nizableText>> _setParameterValues(Map<String, Object> parameterValues, Service service, ModifiableZoneItem zoneItem) 384 { 385 ModifiableModelAwareDataHolder serviceDataHolder = zoneItem.getServiceParameters(); 386 Map<String, ModelItem> definitions = service.getParameters(); 387 Map<String, List<I18nizableText>> allErrors = _parametersManager.setParameterValues(serviceDataHolder, definitions.values(), parameterValues); 388 389 if (parameterValues.containsKey(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME)) 390 { 391 String viewName = (String) parameterValues.get(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME); 392 393 String skinId = zoneItem.getZone() 394 .getPage() 395 .getSite() 396 .getSkinId(); 397 Optional<ViewParametersModel> serviceViewParametersModel = _viewParametersManager.getServiceViewParametersModel(skinId, service.getId(), viewName); 398 399 if (serviceViewParametersModel.isPresent()) 400 { 401 ModifiableModelAwareDataHolder serviceParametersHolder = zoneItem.getServiceViewParametersHolder(viewName); 402 String prefix = ViewParametersDAO.PREFIX_SERVICE + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR + _viewParametersManager.normalizeViewName(viewName) + ViewParametersDAO.MODEL_ITEM_NAME_SEPARATOR; 403 Map<String, Object> viewParametersValues = _parametersManager.getParametersStartWithPrefix(parameterValues, prefix); 404 allErrors.putAll(_parametersManager.setParameterValues(serviceParametersHolder, serviceViewParametersModel.get().getModelItems(), viewParametersValues)); 405 } 406 } 407 408 return allErrors; 409 } 410}