001/*
002 *  Copyright 2022 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.jcr;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.Collections;
021import java.util.List;
022import java.util.NoSuchElementException;
023import java.util.Optional;
024
025import javax.jcr.Node;
026
027import org.apache.commons.collections4.MapUtils;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030import org.xml.sax.ContentHandler;
031import org.xml.sax.SAXException;
032
033import org.ametys.plugins.repository.AmetysObject;
034import org.ametys.plugins.repository.AmetysObjectIterable;
035import org.ametys.plugins.repository.AmetysObjectIterator;
036import org.ametys.plugins.repository.AmetysRepositoryException;
037import org.ametys.plugins.repository.CollectionIterable;
038import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
039import org.ametys.plugins.repository.RepositoryConstants;
040import org.ametys.plugins.repository.TraversableAmetysObject;
041import org.ametys.plugins.repository.UnknownAmetysObjectException;
042import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder;
043import org.ametys.plugins.repository.data.repositorydata.impl.JCRRepositoryData;
044import org.ametys.plugins.repository.jcr.DefaultTraversableAmetysObject;
045import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata;
046import org.ametys.runtime.model.View;
047import org.ametys.runtime.model.exception.BadItemTypeException;
048import org.ametys.runtime.model.exception.NotUniqueTypeException;
049import org.ametys.runtime.model.exception.UnknownTypeException;
050import org.ametys.runtime.model.type.DataContext;
051import org.ametys.web.parameters.view.ViewParametersManager;
052import org.ametys.web.parameters.view.ViewParametersModel;
053import org.ametys.web.repository.page.ModifiableSitemapElement;
054import org.ametys.web.repository.page.ModifiableZone;
055import org.ametys.web.repository.page.Page;
056import org.ametys.web.repository.page.SitemapElement;
057import org.ametys.web.repository.page.UnknownZoneException;
058
059/**
060 * {@link SitemapElement} implementation stored into a JCR node.
061 * 
062 * @param <F> the actual type of factory.
063 */
064public abstract class AbstractSitemapElement<F extends AbstractSitemapElementFactory> extends DefaultTraversableAmetysObject<F> implements ModifiableSitemapElement
065{
066    /** Constant for the zones node name. */
067    public static final String ZONES_NODE_NAME = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":zones";
068
069    /** Constant for the zones node type. */
070    public static final String ZONES_NODE_TYPE = RepositoryConstants.NAMESPACE_PREFIX + ":zones";
071
072    /** Constant for the zone node type. */
073    public static final String ZONE_NODE_TYPE = RepositoryConstants.NAMESPACE_PREFIX + ":zone";
074    
075    private static final Logger __LOGGER = LoggerFactory.getLogger(AbstractSitemapElement.class);
076
077    /**
078     * Creates an {@link AbstractSitemapElement}.
079     * 
080     * @param node the node backing this {@link AmetysObject}.
081     * @param parentPath the parent path in the Ametys hierarchy.
082     * @param factory the {@link DefaultPageFactory} which creates the
083     *            AmetysObject.
084     */
085    public AbstractSitemapElement(Node node, String parentPath, F factory)
086    {
087        super(node, parentPath, factory);
088    }
089    
090
091    @Override
092    public AmetysObjectIterable< ? extends Page> getChildrenPages() throws AmetysRepositoryException
093    {
094        return getChildrenPages(true);
095    }
096    
097    @Override
098    public AmetysObjectIterable< ? extends Page> getChildrenPages(boolean includeInvisiblePage) throws AmetysRepositoryException
099    {
100        List<Page> childrenPages = new ArrayList<>();
101        for (AmetysObject childObject : getChildren())
102        {
103            if (childObject instanceof Page)
104            {
105                Page page = (Page) childObject;
106                if (includeInvisiblePage || page.isVisible())
107                {
108                    childrenPages.add(page);
109                }
110            }
111        }
112        return new CollectionIterable<>(childrenPages);
113    }
114    
115    @Override
116    public Page getChildPageAt(int index) throws UnknownAmetysObjectException, AmetysRepositoryException
117    {
118        if (index < 0)
119        {
120            throw new AmetysRepositoryException("Child page index cannot be negative");
121        }
122        
123        AmetysObjectIterable< ? extends Page> childPages = getChildrenPages();
124        AmetysObjectIterator< ? extends Page> it = childPages.iterator();
125        
126        try
127        {
128            it.skip(index);
129            return it.next();
130        }
131        catch (NoSuchElementException e)
132        {
133            throw new UnknownAmetysObjectException("There's no child page at index " + index + " for sitemap " + this.getId());
134        }
135    }
136    
137    @Override
138    public AmetysObjectIterable<ModifiableZone> getZones() throws AmetysRepositoryException
139    {
140        try
141        {
142            return ((TraversableAmetysObject) getChild(ZONES_NODE_NAME)).getChildren();
143        }
144        catch (UnknownAmetysObjectException e)
145        {
146            Collection<ModifiableZone> emptyCollection = Collections.emptyList();
147            return new CollectionIterable<>(emptyCollection);
148        }
149    }
150
151    @Override
152    public boolean hasZone(String name) throws AmetysRepositoryException
153    {
154        if (hasChild(ZONES_NODE_NAME))
155        {
156            return ((TraversableAmetysObject) getChild(ZONES_NODE_NAME)).hasChild(name);
157        }
158        else
159        {
160            return false;
161        }
162    }
163    
164    @Override
165    public ModifiableZone getZone(String name) throws UnknownZoneException, AmetysRepositoryException
166    {
167        return ((TraversableAmetysObject) getChild(ZONES_NODE_NAME)).getChild(name);
168    }
169
170    @Override
171    public ModifiableZone createZone(String name) throws AmetysRepositoryException
172    {
173        ModifiableTraversableAmetysObject zones;
174        if (hasChild(ZONES_NODE_NAME))
175        {
176            zones = getChild(ZONES_NODE_NAME);
177        }
178        else
179        {
180            zones = createChild(ZONES_NODE_NAME, ZONES_NODE_TYPE);
181        }
182        
183        return zones.createChild(name, ZONE_NODE_TYPE); 
184    }
185
186    @Override
187    public ModifiableCompositeMetadata getMetadataHolder()
188    {
189        __LOGGER.warn("The getMetadataHolder method of AbstractSitemapElement is deprecated. Use the getDataHolder instead. StackTrace: ", new Exception());
190        return super.getMetadataHolder();
191    }
192    
193    public ModifiableModelAwareDataHolder getTemplateParametersHolder() throws AmetysRepositoryException
194    {
195        JCRRepositoryData zoneItemRepositoryData = new JCRRepositoryData(getNode());
196        
197        // Get the template Id
198        String templateId = getTemplate();
199        
200        // Get the skin Id
201        String skinId = getSite().getSkinId();
202        
203        ViewParametersManager viewParametersManager = _getFactory().getViewParametersManager();
204        Optional<ViewParametersModel> templateViewParameters = viewParametersManager.getTemplateViewParametersModel(skinId, templateId);
205        if (templateViewParameters.isEmpty())
206        {
207            templateViewParameters = Optional.of(new ViewParametersModel("template-no-param", new View(), MapUtils.EMPTY_SORTED_MAP));
208        }
209        
210        return viewParametersManager.getParametersHolder(zoneItemRepositoryData, templateViewParameters.get());
211    }
212    
213    /*
214     * The default implementation is overridden here to not use the getDataHolder()#dataToSAX implementation
215     * Each #dataToSAX method calls the next one and the implementation that has to change is the last one.
216     * If the getDataHolder() is used for one, the next ones will be called with the default implementation
217     */
218    @Override
219    public void dataToSAX(ContentHandler contentHandler, DataContext context) throws SAXException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException
220    {
221        for (String name : getDataNames())
222        {
223            dataToSAX(contentHandler, name, context);
224        }
225    }
226    
227    @Override
228    public void dataToSAX(ContentHandler contentHandler, String dataPath, DataContext context) throws SAXException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException
229    {
230        if (!ViewParametersManager.VIEW_PARAMETERS_COMPOSITE_NAME.equals(dataPath))
231        {
232            super.dataToSAX(contentHandler, dataPath, context);
233        }
234    }
235}