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