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