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.Locale;
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.w3c.dom.Document;
031import org.w3c.dom.Element;
032import org.xml.sax.SAXException;
033
034import org.ametys.cms.data.holder.impl.DefaultModifiableModelAwareDataHolder;
035import org.ametys.cms.repository.Content;
036import org.ametys.cms.transformation.Configuration2XMLValuesTransformer;
037import org.ametys.plugins.repository.AmetysRepositoryException;
038import org.ametys.plugins.repository.data.extractor.ModelAwareValuesExtractor;
039import org.ametys.plugins.repository.data.extractor.xml.ModelAwareXMLValuesExtractor;
040import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder;
041import org.ametys.plugins.repository.data.holder.ModelLessDataHolder;
042import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder;
043import org.ametys.plugins.repository.data.holder.impl.DefaultModelLessDataHolder;
044import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData;
045import org.ametys.plugins.repository.data.repositorydata.RepositoryData;
046import org.ametys.plugins.repository.data.repositorydata.impl.MemoryRepositoryData;
047import org.ametys.runtime.model.type.DataContext;
048import org.ametys.web.repository.page.Zone;
049import org.ametys.web.repository.page.ZoneItem;
050import org.ametys.web.service.Service;
051
052/**
053 * A configurable zone item
054 */
055public class ConfigurableVirtualZoneItem implements ZoneItem
056{
057    /** The AbstractVirtualConfigurablePage */
058    protected AbstractConfigurableVirtualPage _page;
059    /** The zone item name */
060    protected String _name;
061    /** The VirtualPageZoneItemConfiguration */
062    protected VirtualZoneItemConfiguration _configuration;
063    /** The VirtualPageZoneConfiguration */
064    protected VirtualZoneConfiguration _zoneConfiguration;
065    /** The ZoneType */
066    protected ZoneType _zoneType;
067    /** The scheme */
068    private String _scheme;
069    /** The factory */
070    private ConfigurableVirtualZoneItemFactory _factory;
071
072    
073    /**
074     * The constructor of a configurable zone item
075     * @param page The AbstractVirtualConfigurablePage page
076     * @param configuration The virtual page's zone item's configuration
077     * @param scheme The scheme
078     * @param factory The factory
079     */
080    public ConfigurableVirtualZoneItem(AbstractConfigurableVirtualPage page, VirtualZoneItemConfiguration configuration, String scheme, ConfigurableVirtualZoneItemFactory factory)
081    {
082        _configuration = configuration;
083        _zoneConfiguration = _configuration.getParentZoneConfiguration();
084        _page = page;
085        _name = _configuration.getName();
086        _zoneType = configuration.getZoneType();
087        _scheme = scheme;
088        _factory = factory;
089    }
090
091    @Override
092    public String getViewName() throws AmetysRepositoryException
093    {
094        return _configuration.getView();
095    }
096    
097    @Override
098    public String getName() throws AmetysRepositoryException 
099    {
100        return _name;
101    }
102
103    @Override
104    public String getPath() throws AmetysRepositoryException 
105    {
106        return getParentPath() + "/ametys-internal:zoneItems/" + _configuration.getName();
107    }
108
109    @Override
110    public String getParentPath() throws AmetysRepositoryException 
111    {
112        return _page.getPath() + "/ametys-internal:zones/" + _configuration.getParentZoneName();
113    }
114
115    @Override
116    public ZoneType getType() throws AmetysRepositoryException 
117    {
118        return _zoneType;
119    }
120
121    @SuppressWarnings("unchecked")
122    @Override
123    public <C extends Content> C getContent() throws AmetysRepositoryException 
124    {
125        if (_zoneType.equals(ZoneType.CONTENT)) 
126        {
127            return (C) _page.getContent();
128        }
129        
130        throw new UnsupportedOperationException("This zoneitem is not a content zoneitem on this virtual page");
131    }
132
133    @Override
134    public String getServiceId() throws AmetysRepositoryException 
135    {
136        if (_zoneType.equals(ZoneType.SERVICE))
137        {
138            return _configuration.getServiceId();
139        }
140        
141        throw new UnsupportedOperationException("This zoneitem is not a service zoneitem on this virtual page");
142    }
143
144    @Override
145    public ModelAwareDataHolder getServiceParameters() throws AmetysRepositoryException 
146    {
147        if (_zoneType.equals(ZoneType.SERVICE))
148        {
149            ModifiableRepositoryData repositoryData = new MemoryRepositoryData(SERVICE_PARAMETERS_DATA_NAME);
150            Service service = _factory.getServiceExtensionPoint().getExtension(getServiceId());
151            if (service == null)
152            {
153                throw new IllegalStateException("The virtual page is using the unexisting service '" + getServiceId() + "'");
154            }
155            
156            ModifiableModelAwareDataHolder dataHolder = new DefaultModifiableModelAwareDataHolder(repositoryData, service);
157            
158            DataContext dataContext = DataContext.newInstance();
159            dataContext.withLocale(Locale.forLanguageTag(getParent().getSitemapElement().getSitemapName()));
160            
161            try 
162            {
163                // Configuration may requires interpretation before extraction of the value
164                // for example to translate i18n keys
165                Element xmlValues = _interpretConfiguration(_configuration.getConfiguration(), dataContext);
166                
167                // provide the result to XML values extractor
168                ModelAwareValuesExtractor extractor = new ModelAwareXMLValuesExtractor(xmlValues, service);
169                dataHolder.synchronizeValues(extractor.extractValues());
170            } 
171            catch (Exception e) 
172            {
173                throw new AmetysRepositoryException("An error occured while retrieving service parameters of the virtual page '" + _page.getId() + "'", e);
174            }
175            
176            return dataHolder;
177        }
178        
179        throw new UnsupportedOperationException("This zoneitem is not a service zoneitem on this virtual page");
180    }
181    
182    private Element _interpretConfiguration(Configuration contentCfg, DataContext dataContext)
183            throws SAXException, ConfigurationException
184    {
185        DOMResult domResult = new DOMResult();
186        
187        try
188        {
189            TransformerHandler th = ((SAXTransformerFactory) TransformerFactory.newInstance()).newTransformerHandler();
190            th.setResult(domResult);
191            
192            Configuration2XMLValuesTransformer handler = new Configuration2XMLValuesTransformer(th, dataContext, _factory.getI18nUtils());
193            new DefaultConfigurationSerializer().serialize(handler, contentCfg);
194            Element values = ((Document) domResult.getNode()).getDocumentElement();
195            return values;
196        }
197        catch (TransformerConfigurationException | TransformerFactoryConfigurationError e)
198        {
199            throw new IllegalStateException("Failed to retrive transformer handler. Impossible to interpret the configuration", e);
200        }
201    }
202    
203    @Override
204    public ModelLessDataHolder getDataHolder()
205    {
206        RepositoryData repositoryData = new MemoryRepositoryData(Zone.ZONEITEM_DATA_NAME);
207        return new DefaultModelLessDataHolder(_factory.getZoneItemDataTypeExtensionPoint(), repositoryData);
208    }
209
210    @Override
211    public String getId() throws AmetysRepositoryException 
212    {
213        return _scheme + "://" + _name + "?pageId=" + _page.getId() + "&zoneId=" + _zoneConfiguration.getId();
214    }
215
216    @SuppressWarnings("unchecked")
217    @Override
218    public Zone getParent() throws AmetysRepositoryException 
219    {
220        return getZone();
221    }
222    
223    @Override
224    public Zone getZone() 
225    {
226        return _factory.getZoneFactory().createZone(_page, _zoneConfiguration.getId());
227    }
228
229    @Override
230    public ModelAwareDataHolder getZoneItemParametersHolder() throws AmetysRepositoryException 
231    {
232        // No parameters are available on virtual zone item
233        return null;
234    }
235
236    @Override
237    public ModelAwareDataHolder getContentViewParametersHolder(String contentViewName) throws AmetysRepositoryException 
238    {
239        // No parameters are available on virtual zone item
240        return null;
241    }
242
243    @Override
244    public ModelAwareDataHolder getServiceViewParametersHolder(String serviceViewName) throws AmetysRepositoryException 
245    {
246        // No parameters are available on virtual zone item
247        return null;
248    }
249}