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