001/* 002 * Copyright 2020 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.parameters.view; 017 018import java.io.InputStream; 019import java.util.ArrayList; 020import java.util.HashMap; 021import java.util.LinkedHashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Optional; 025 026import org.apache.avalon.framework.activity.Disposable; 027import org.apache.avalon.framework.activity.Initializable; 028import org.apache.avalon.framework.component.Component; 029import org.apache.avalon.framework.configuration.Configuration; 030import org.apache.avalon.framework.configuration.ConfigurationException; 031import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 032import org.apache.avalon.framework.context.Context; 033import org.apache.avalon.framework.context.ContextException; 034import org.apache.avalon.framework.context.Contextualizable; 035import org.apache.avalon.framework.service.ServiceException; 036import org.apache.avalon.framework.service.ServiceManager; 037import org.apache.avalon.framework.service.Serviceable; 038import org.apache.cocoon.components.ContextHelper; 039import org.apache.cocoon.environment.Request; 040import org.apache.excalibur.source.Source; 041import org.apache.excalibur.source.SourceNotFoundException; 042 043import org.ametys.cms.content.GetContentAction; 044import org.ametys.cms.contenttype.ContentType; 045import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 046import org.ametys.core.util.filereloader.FileReloader; 047import org.ametys.core.util.filereloader.FileReloaderUtils; 048import org.ametys.plugins.repository.AmetysObjectResolver; 049import org.ametys.plugins.repository.model.parsing.RepeaterDefinitionParser; 050import org.ametys.runtime.model.Enumerator; 051import org.ametys.runtime.model.View; 052import org.ametys.runtime.parameter.Validator; 053import org.ametys.runtime.plugin.component.AbstractLogEnabled; 054import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 055import org.ametys.web.data.type.ModelItemTypeExtensionPoint; 056import org.ametys.web.parameters.ViewAndParametersParser; 057import org.ametys.web.parameters.ViewAndParametersParser.ViewAndParameters; 058import org.ametys.web.parameters.view.GlobalViewParametersManager.ViewParametersType; 059 060/** 061 * Manager for content view parameters 062 */ 063public class ContentViewParametersManager extends AbstractLogEnabled implements Component, Serviceable, Contextualizable, Initializable, Disposable 064{ 065 /** Avalon Role */ 066 public static final String ROLE = ContentViewParametersManager.class.getName(); 067 068 /** The file reloader utils */ 069 protected FileReloaderUtils _fileReloaderUtils; 070 071 /** The view parameter type extension point */ 072 protected ModelItemTypeExtensionPoint _viewParametersEP; 073 074 /** The view and parameters parser */ 075 protected ViewAndParametersParser _viewAndParametersParser; 076 077 /** The ametys object resolver */ 078 protected AmetysObjectResolver _resolver; 079 080 /** The avalon context */ 081 protected Context _context; 082 083 /** The service manager */ 084 protected ServiceManager _manager; 085 086 /** The content type extension point */ 087 protected ContentTypeExtensionPoint _contentTypeEP; 088 089 /** The object representing the view parameters of the different content type by skin */ 090 protected SkinsContentViewParameters _skinsContentViewParameters; 091 092 /** The view parameters manager */ 093 protected ViewParametersManager _viewParametersManager; 094 095 private List<ThreadSafeComponentManager> _components; 096 097 public void service(ServiceManager manager) throws ServiceException 098 { 099 _manager = manager; 100 _fileReloaderUtils = (FileReloaderUtils) manager.lookup(FileReloaderUtils.ROLE); 101 _viewParametersEP = (ModelItemTypeExtensionPoint) manager.lookup(ModelItemTypeExtensionPoint.ROLE_VIEW_PARAM); 102 _viewAndParametersParser = (ViewAndParametersParser) manager.lookup(ViewAndParametersParser.ROLE); 103 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 104 _contentTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE); 105 _viewParametersManager = (ViewParametersManager) manager.lookup(ViewParametersManager.ROLE); 106 } 107 108 public void contextualize(Context context) throws ContextException 109 { 110 _context = context; 111 } 112 113 public void initialize() throws Exception 114 { 115 _skinsContentViewParameters = new SkinsContentViewParameters(); 116 _components = new ArrayList<>(); 117 } 118 119 /** 120 * Get view parameters for a content type 121 * @param skinId the skin id 122 * @param contentTypeId the content type id 123 * @param viewName the view name 124 * @return the view parameters 125 */ 126 public Optional<ViewParametersModel> getViewParameters(String skinId, String contentTypeId, String viewName) 127 { 128 Request request = ContextHelper.getRequest(_context); 129 130 request.setAttribute(GetContentAction.RESULT_CONTENTTYPE, contentTypeId); 131 132 String sourceUrl = "content-view://" + contentTypeId + "/view-" + viewName + "/html.xml"; 133 try 134 { 135 _fileReloaderUtils.updateFile(sourceUrl, new ContentViewParametersReloader(skinId, contentTypeId, viewName, this)); 136 } 137 catch (SourceNotFoundException e) 138 { 139 // Do nothing, the parameter xml file doesn't exist 140 } 141 catch (Exception e) 142 { 143 getLogger().error("Failed to read the content view parameters file '{}'. It will be ignored", sourceUrl, e); 144 } 145 146 Optional<ViewParametersModel> viewParameters = _skinsContentViewParameters.getContentViewParameters(skinId, contentTypeId, viewName); 147 return _viewParametersManager.addGlobalViewParameters(skinId, ViewParametersType.CONTENTS, viewParameters); 148 } 149 150 /** 151 * Class representing a content view parameters reloader 152 */ 153 public static class ContentViewParametersReloader implements FileReloader 154 { 155 private String _skinId; 156 private String _contentTypeId; 157 private String _viewName; 158 private ContentViewParametersManager _contentViewParameterManager; 159 160 /** 161 * Constructor for the reloader 162 * @param skinId the skin id 163 * @param contentTypeId the content type id 164 * @param viewName the content view name 165 * @param manager the content view parameters manager 166 */ 167 public ContentViewParametersReloader (String skinId, String contentTypeId, String viewName, ContentViewParametersManager manager) 168 { 169 _skinId = skinId; 170 _contentTypeId = contentTypeId; 171 _viewName = viewName; 172 _contentViewParameterManager = manager; 173 } 174 175 /** 176 * Get the skin id 177 * @return the skin id 178 */ 179 public String getSkinId() 180 { 181 return _skinId; 182 } 183 184 /** 185 * Get the content type id 186 * @return the content type id 187 */ 188 public String getContentTypeId() 189 { 190 return _contentTypeId; 191 } 192 193 /** 194 * Get the content view name 195 * @return the content view name 196 */ 197 public String getViewName() 198 { 199 return _viewName; 200 } 201 202 /** 203 * Get the content view parameters manager 204 * @return the content view parameters manager 205 */ 206 public ContentViewParametersManager getContentViewParameterManager() 207 { 208 return _contentViewParameterManager; 209 } 210 211 public void updateFile(String sourceUrl, Source source, InputStream is) throws Exception 212 { 213 if (is != null && source != null) 214 { 215 ContentViewParametersManager manager = getContentViewParameterManager(); 216 217 manager._disposeComponents(); 218 219 String uri = source.getURI(); 220 221 ContentType contentType = manager._contentTypeEP.getExtension(getContentTypeId()); 222 String plugin = contentType.getPluginName(); 223 String catalog = "plugin." + plugin; 224 if (uri.startsWith("skin:")) 225 { 226 catalog = "skin." + getSkinId(); 227 } 228 229 Configuration conf = new DefaultConfigurationBuilder().build(is); 230 231 String viewParametersId = getSkinId() + "_" + getContentTypeId() + "_" + getViewName() + "_content_view_parameters"; 232 Configuration paramConfiguration = conf.getChild(ViewParametersManager.VIEW_PARAMETERS_CONTENT_CONF_NAME); 233 234 Optional<ViewParametersModel> viewParameters = manager._configureViewParameters(paramConfiguration, viewParametersId, plugin, catalog); 235 236 manager._skinsContentViewParameters.addContentViewParameters( 237 getSkinId(), 238 getContentTypeId(), 239 getViewName(), 240 viewParameters 241 ); 242 } 243 } 244 245 public String getId(String sourceUrl) 246 { 247 return ContentViewParametersManager.class.getName() + "#" + getSkinId() + "_" + getContentTypeId() + "_" + getViewName(); 248 } 249 } 250 251 /** 252 * Parse content view parameters from the configuration 253 * @param paramConfiguration the configuration 254 * @param viewParametersId the view parameters id 255 * @param plugin the plugin 256 * @param catalog the catalog 257 * @return the view parameters 258 * @throws ConfigurationException if a configuration error occurred 259 */ 260 protected Optional<ViewParametersModel> _configureViewParameters(Configuration paramConfiguration, String viewParametersId, String plugin, String catalog) throws ConfigurationException 261 { 262 ThreadSafeComponentManager<Validator> validatorManager = new ThreadSafeComponentManager<>(); 263 validatorManager.setLogger(getLogger()); 264 validatorManager.contextualize(_context); 265 validatorManager.service(_manager); 266 _components.add(validatorManager); 267 268 ThreadSafeComponentManager<Enumerator> enumeratorManager = new ThreadSafeComponentManager<>(); 269 enumeratorManager.setLogger(getLogger()); 270 enumeratorManager.contextualize(_context); 271 enumeratorManager.service(_manager); 272 _components.add(enumeratorManager); 273 274 ViewParametersModel viewParameters = new ViewParametersModel(viewParametersId, new View(), new LinkedHashMap<>()); 275 276 ViewParameterDefinitionParser elementDefinitionParser = new ViewParameterDefinitionParser(_viewParametersEP, enumeratorManager, validatorManager); 277 RepeaterDefinitionParser repeaterDefinitionParser = new RepeaterDefinitionParser(_viewParametersEP); 278 279 ViewAndParameters viewAndParameters = _viewAndParametersParser.parseParameters(paramConfiguration, plugin, catalog, viewParameters, elementDefinitionParser, repeaterDefinitionParser); 280 viewParameters.setView(viewAndParameters.getView()); 281 viewParameters.setModelItems(viewAndParameters.getParameters()); 282 283 try 284 { 285 elementDefinitionParser.lookupComponents(); 286 } 287 catch (Exception e) 288 { 289 throw new ConfigurationException("Unable to lookup parameter local components", paramConfiguration, e); 290 } 291 292 return Optional.ofNullable(viewParameters); 293 } 294 295 public void dispose() 296 { 297 _disposeComponents(); 298 } 299 300 private void _disposeComponents() 301 { 302 for (ThreadSafeComponentManager component : _components) 303 { 304 component.dispose(); 305 } 306 } 307 308 static class SkinsContentViewParameters 309 { 310 Map<String, SkinWrapper> _contentsBySkin; 311 312 SkinsContentViewParameters() 313 { 314 this._contentsBySkin = new HashMap<>(); 315 } 316 317 Optional<ViewParametersModel> getContentViewParameters(String skinId, String contentTypeId, String contentViewName) 318 { 319 SkinWrapper skin = _contentsBySkin.getOrDefault(skinId, new SkinWrapper()); 320 return skin.getContentViewParameters(contentTypeId, contentViewName); 321 } 322 323 void addContentViewParameters(String skinId, String contentTypeId, String contentViewName, Optional<ViewParametersModel> viewParameters) 324 { 325 if (!_contentsBySkin.containsKey(skinId)) 326 { 327 _contentsBySkin.put(skinId, new SkinWrapper()); 328 } 329 330 SkinWrapper skin = _contentsBySkin.get(skinId); 331 skin.addContentViewParameters(contentTypeId, contentViewName, viewParameters); 332 } 333 } 334 335 static class SkinWrapper 336 { 337 Map<String, ContentTypeWrapper> _viewsByContentTypes; 338 339 SkinWrapper() 340 { 341 this._viewsByContentTypes = new HashMap<>(); 342 } 343 344 Optional<ViewParametersModel> getContentViewParameters(String contentTypeId, String contentViewName) 345 { 346 ContentTypeWrapper contentType = _viewsByContentTypes.getOrDefault(contentTypeId, new ContentTypeWrapper()); 347 return contentType.getContentViewParameters(contentViewName); 348 } 349 350 void addContentViewParameters(String contentTypeId, String contentViewName, Optional<ViewParametersModel> viewParameters) 351 { 352 if (!_viewsByContentTypes.containsKey(contentTypeId)) 353 { 354 _viewsByContentTypes.put(contentTypeId, new ContentTypeWrapper()); 355 } 356 357 ContentTypeWrapper contentType = _viewsByContentTypes.get(contentTypeId); 358 contentType.addContentViewParameters(contentViewName, viewParameters); 359 } 360 } 361 362 static class ContentTypeWrapper 363 { 364 Map<String, Optional<ViewParametersModel>> _parametersByContentView; 365 366 ContentTypeWrapper() 367 { 368 this._parametersByContentView = new HashMap<>(); 369 } 370 371 Optional<ViewParametersModel> getContentViewParameters(String contentViewName) 372 { 373 return _parametersByContentView.getOrDefault(contentViewName, Optional.empty()); 374 } 375 376 void addContentViewParameters(String contentViewName, Optional<ViewParametersModel> viewParameters) 377 { 378 _parametersByContentView.put(contentViewName, viewParameters); 379 } 380 } 381}