001/* 002 * Copyright 2023 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.virtual; 017 018import java.util.Optional; 019 020import javax.xml.transform.TransformerConfigurationException; 021import javax.xml.transform.TransformerFactory; 022import javax.xml.transform.TransformerFactoryConfigurationError; 023import javax.xml.transform.dom.DOMResult; 024import javax.xml.transform.sax.SAXTransformerFactory; 025import javax.xml.transform.sax.TransformerHandler; 026 027import org.apache.avalon.framework.configuration.Configuration; 028import org.apache.avalon.framework.configuration.ConfigurationException; 029import org.apache.avalon.framework.configuration.DefaultConfigurationSerializer; 030import org.apache.commons.collections4.MapUtils; 031import org.apache.commons.lang3.LocaleUtils; 032import org.w3c.dom.Document; 033import org.w3c.dom.Element; 034import org.xml.sax.SAXException; 035 036import org.ametys.cms.data.holder.impl.DefaultModifiableModelAwareDataHolder; 037import org.ametys.cms.repository.Content; 038import org.ametys.cms.transformation.Configuration2XMLValuesTransformer; 039import org.ametys.plugins.repository.AmetysRepositoryException; 040import org.ametys.plugins.repository.data.extractor.ModelAwareValuesExtractor; 041import org.ametys.plugins.repository.data.extractor.xml.ModelAwareXMLValuesExtractor; 042import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder; 043import org.ametys.plugins.repository.data.holder.ModelLessDataHolder; 044import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder; 045import org.ametys.plugins.repository.data.holder.impl.DefaultModelLessDataHolder; 046import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData; 047import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 048import org.ametys.plugins.repository.data.repositorydata.impl.MemoryRepositoryData; 049import org.ametys.runtime.model.Model; 050import org.ametys.runtime.model.View; 051import org.ametys.runtime.model.type.DataContext; 052import org.ametys.web.parameters.view.ViewParametersManager; 053import org.ametys.web.parameters.view.ViewParametersModel; 054import org.ametys.web.repository.page.Zone; 055import org.ametys.web.repository.page.ZoneItem; 056import org.ametys.web.service.Service; 057 058/** 059 * A configurable zone item 060 */ 061public class ConfigurableVirtualZoneItem implements ZoneItem 062{ 063 /** The AbstractVirtualConfigurablePage */ 064 protected AbstractConfigurableVirtualPage _page; 065 /** The zone item name */ 066 protected String _name; 067 /** The VirtualPageZoneItemConfiguration */ 068 protected VirtualZoneItemConfiguration _configuration; 069 /** The VirtualPageZoneConfiguration */ 070 protected VirtualZoneConfiguration _zoneConfiguration; 071 /** The ZoneType */ 072 protected ZoneType _zoneType; 073 /** The scheme */ 074 private String _scheme; 075 /** The factory */ 076 private ConfigurableVirtualZoneItemFactory _factory; 077 078 079 /** 080 * The constructor of a configurable zone item 081 * @param page The AbstractVirtualConfigurablePage page 082 * @param configuration The virtual page's zone item's configuration 083 * @param scheme The scheme 084 * @param factory The factory 085 */ 086 public ConfigurableVirtualZoneItem(AbstractConfigurableVirtualPage page, VirtualZoneItemConfiguration configuration, String scheme, ConfigurableVirtualZoneItemFactory factory) 087 { 088 _configuration = configuration; 089 _zoneConfiguration = _configuration.getParentZoneConfiguration(); 090 _page = page; 091 _name = _configuration.getName(); 092 _zoneType = configuration.getZoneType(); 093 _scheme = scheme; 094 _factory = factory; 095 } 096 097 @Override 098 public String getViewName() throws AmetysRepositoryException 099 { 100 return _configuration.getView(); 101 } 102 103 @Override 104 public String getName() throws AmetysRepositoryException 105 { 106 return _name; 107 } 108 109 @Override 110 public String getPath() throws AmetysRepositoryException 111 { 112 return getParentPath() + "/ametys-internal:zoneItems/" + _configuration.getName(); 113 } 114 115 @Override 116 public String getParentPath() throws AmetysRepositoryException 117 { 118 return _page.getPath() + "/ametys-internal:zones/" + _configuration.getParentZoneName(); 119 } 120 121 @Override 122 public ZoneType getType() throws AmetysRepositoryException 123 { 124 return _zoneType; 125 } 126 127 @SuppressWarnings("unchecked") 128 @Override 129 public <C extends Content> C getContent() throws AmetysRepositoryException 130 { 131 if (_zoneType.equals(ZoneType.CONTENT)) 132 { 133 return (C) _page.getContent(); 134 } 135 136 throw new UnsupportedOperationException("This zoneitem is not a content zoneitem on this virtual page"); 137 } 138 139 @Override 140 public String getServiceId() throws AmetysRepositoryException 141 { 142 if (_zoneType.equals(ZoneType.SERVICE)) 143 { 144 return _configuration.getServiceId(); 145 } 146 147 throw new UnsupportedOperationException("This zoneitem is not a service zoneitem on this virtual page"); 148 } 149 150 @Override 151 public ModelAwareDataHolder getServiceParameters() throws AmetysRepositoryException 152 { 153 if (getType() != ZoneType.SERVICE) 154 { 155 throw new AmetysRepositoryException("Can't get service parameters from a not service zone item"); 156 } 157 158 String serviceId = getServiceId(); 159 160 Service service = _factory.getServiceExtensionPoint().getExtension(serviceId); 161 if (service == null) 162 { 163 throw new IllegalStateException("The virtual page uses unknown service '" + serviceId + "'"); 164 } 165 166 ModifiableRepositoryData repositoryData = new MemoryRepositoryData(SERVICE_PARAMETERS_DATA_NAME); 167 168 Configuration serviceParamsConf = _configuration.getConfiguration().getChild("parameters", true); 169 return _extractValues(repositoryData, service, serviceParamsConf); 170 } 171 172 @Override 173 public ModelLessDataHolder getDataHolder() 174 { 175 RepositoryData repositoryData = new MemoryRepositoryData(Zone.ZONEITEM_DATA_NAME); 176 return new DefaultModelLessDataHolder(_factory.getZoneItemDataTypeExtensionPoint(), repositoryData); 177 } 178 179 @Override 180 public String getId() throws AmetysRepositoryException 181 { 182 return _scheme + "://" + _name + "?pageId=" + _page.getId() + "&zoneId=" + _zoneConfiguration.getId(); 183 } 184 185 @SuppressWarnings("unchecked") 186 @Override 187 public Zone getParent() throws AmetysRepositoryException 188 { 189 return getZone(); 190 } 191 192 @Override 193 public Zone getZone() 194 { 195 return _factory.getZoneFactory().createZone(_page, _zoneConfiguration.getId()); 196 } 197 198 @Override 199 public ModelAwareDataHolder getZoneItemParametersHolder() throws AmetysRepositoryException 200 { 201 // No parameters are available on virtual zone item 202 return null; 203 } 204 205 @Override 206 public ModelAwareDataHolder getContentViewParametersHolder(String contentViewName) throws AmetysRepositoryException 207 { 208 if (getType() != ZoneType.CONTENT) 209 { 210 throw new AmetysRepositoryException("Can't get content view parameters from a not content zone item"); 211 } 212 213 Content content = getContent(); 214 215 ViewParametersManager viewParametersManager = _factory.getViewParametersManager(); 216 Optional<ViewParametersModel> contentViewParameters = viewParametersManager.getContentViewParametersModel(_getSkinId(), content, contentViewName); 217 if (contentViewParameters.isEmpty()) 218 { 219 contentViewParameters = Optional.of(new ViewParametersModel("content-no-param", new View(), MapUtils.EMPTY_SORTED_MAP)); 220 } 221 222 ModifiableRepositoryData repositoryData = new MemoryRepositoryData(ViewParametersManager.CONTENT_VIEW_PARAMETERS_COMPOSITE_NAME); 223 224 Configuration viewParamsConf = _configuration.getConfiguration().getChild("view-parameters", true); 225 return _extractValues(repositoryData, contentViewParameters.get(), viewParamsConf); 226 } 227 228 @Override 229 public ModelAwareDataHolder getServiceViewParametersHolder(String serviceViewName) throws AmetysRepositoryException 230 { 231 if (getType() != ZoneType.SERVICE) 232 { 233 throw new AmetysRepositoryException("Can't get service view parameters from a not service zone item"); 234 } 235 236 String serviceId = getServiceId(); 237 238 Service service = _factory.getServiceExtensionPoint().getExtension(serviceId); 239 if (service == null) 240 { 241 throw new IllegalStateException("The virtual page uses unknown service '" + serviceId + "'"); 242 } 243 244 ViewParametersManager viewParametersManager = _factory.getViewParametersManager(); 245 Optional<ViewParametersModel> serviceViewParameters = viewParametersManager.getServiceViewParametersModel(_getSkinId(), serviceId, serviceViewName); 246 if (serviceViewParameters.isEmpty()) 247 { 248 serviceViewParameters = Optional.of(new ViewParametersModel("service-no-param", new View(), MapUtils.EMPTY_SORTED_MAP)); 249 } 250 251 ModifiableRepositoryData repositoryData = new MemoryRepositoryData(ViewParametersManager.SERVICE_VIEW_PARAMETERS_COMPOSITE_NAME); 252 253 Configuration viewParamsConf = _configuration.getConfiguration().getChild("view-parameters", true); 254 return _extractValues(repositoryData, serviceViewParameters.get(), viewParamsConf); 255 } 256 257 private String _getSkinId() 258 { 259 return getZone().getSitemapElement().getSite().getSkinId(); 260 } 261 262 private ModifiableModelAwareDataHolder _extractValues(ModifiableRepositoryData repositoryData, Model model, Configuration configuration) 263 { 264 DataContext dataContext = DataContext.newInstance(); 265 dataContext.withLocale(LocaleUtils.toLocale(getParent().getSitemapElement().getSitemapName())); 266 267 ModifiableModelAwareDataHolder dataHolder = new DefaultModifiableModelAwareDataHolder(repositoryData, model); 268 269 try 270 { 271 // Configuration may requires interpretation before extraction of the value 272 // for example to translate i18n keys 273 Element xmlValues = _interpretConfiguration(configuration, dataContext); 274 275 // provide the result to XML values extractor 276 ModelAwareValuesExtractor extractor = new ModelAwareXMLValuesExtractor(xmlValues, model); 277 dataHolder.synchronizeValues(extractor.extractValues()); 278 } 279 catch (Exception e) 280 { 281 throw new AmetysRepositoryException("An error occured while retrieving parameters for the virtual page '" + _page.getId() + "'", e); 282 } 283 284 return dataHolder; 285 } 286 287 private Element _interpretConfiguration(Configuration contentCfg, DataContext dataContext) 288 throws SAXException, ConfigurationException 289 { 290 DOMResult domResult = new DOMResult(); 291 292 try 293 { 294 TransformerHandler th = ((SAXTransformerFactory) TransformerFactory.newInstance()).newTransformerHandler(); 295 th.setResult(domResult); 296 297 Configuration2XMLValuesTransformer handler = new Configuration2XMLValuesTransformer(th, dataContext, _factory.getI18nUtils()); 298 new DefaultConfigurationSerializer().serialize(handler, contentCfg); 299 Element values = ((Document) domResult.getNode()).getDocumentElement(); 300 return values; 301 } 302 catch (TransformerConfigurationException | TransformerFactoryConfigurationError e) 303 { 304 throw new IllegalStateException("Failed to retrive transformer handler. Impossible to interpret the configuration", e); 305 } 306 } 307}