001/*
002 *  Copyright 2011 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.plugins.blog.repository;
017
018import java.util.ArrayList;
019import java.util.Collections;
020import java.util.NoSuchElementException;
021import java.util.Set;
022import java.util.SortedSet;
023import java.util.TreeSet;
024
025import org.ametys.core.util.I18nUtils;
026import org.ametys.plugins.blog.BlogCacheManager;
027import org.ametys.plugins.explorer.resources.ResourceCollection;
028import org.ametys.plugins.repository.AmetysObject;
029import org.ametys.plugins.repository.AmetysObjectIterable;
030import org.ametys.plugins.repository.AmetysObjectIterator;
031import org.ametys.plugins.repository.AmetysObjectResolver;
032import org.ametys.plugins.repository.AmetysRepositoryException;
033import org.ametys.plugins.repository.CollectionIterable;
034import org.ametys.plugins.repository.EmptyIterable;
035import org.ametys.plugins.repository.UnknownAmetysObjectException;
036import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder;
037import org.ametys.plugins.repository.data.holder.ModelLessDataHolder;
038import org.ametys.plugins.repository.data.holder.impl.DefaultModelLessDataHolder;
039import org.ametys.plugins.repository.data.repositorydata.RepositoryData;
040import org.ametys.plugins.repository.data.repositorydata.impl.MemoryRepositoryData;
041import org.ametys.plugins.repository.data.type.RepositoryModelItemType;
042import org.ametys.runtime.plugin.component.AbstractThreadSafeComponentExtensionPoint;
043import org.ametys.web.repository.page.Page;
044import org.ametys.web.repository.page.PagesContainer;
045import org.ametys.web.repository.page.UnknownZoneException;
046import org.ametys.web.repository.page.Zone;
047import org.ametys.web.repository.site.Site;
048import org.ametys.web.repository.sitemap.Sitemap;
049import org.ametys.web.service.ServiceExtensionPoint;
050import org.ametys.web.skin.SkinsManager;
051
052/**
053 * Virtual page representing the Post list by year page.
054 */
055public class VirtualYearsPage implements Page
056{
057    
058    /** The page name. */
059    public static final String NAME = "years";
060    
061    private AmetysObjectResolver _resolver;
062    private BlogCacheManager _cacheManager;
063    private I18nUtils _i18nUtils;
064    private String _i18nCatalogue;
065    private PagesContainer _root;
066    private String _title;
067    private SkinsManager _skinsManager;
068
069    private AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> _pageDataTypeExtensionPoint;
070    private AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> _zoneDataTypeExtensionPoint;
071    private ServiceExtensionPoint _serviceExtensionPoint;
072    private AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> _zoneItemDataTypeExtensionPoint;
073    
074    /**
075     * Constructor.
076     * @param resolver the {@link AmetysObjectResolver}.
077     * @param cacheManager the {@link BlogCacheManager}.
078     * @param skinsManager the {@link SkinsManager}.
079     * @param i18nCache the i18n utils
080     * @param i18nCatalogue the i18n catalogue.
081     * @param root the blog root page.
082     * @param title the page's title.
083     * @param pageDataTypeExtensionPoint the extension point with available data types for pages
084     * @param zoneDataTypeExtensionPoint the extension point with available data types for zones
085     * @param serviceExtensionPoint The service extension point
086     * @param zoneItemDataTypeExtensionPoint the extension point with available data types for zone items
087     */
088    public VirtualYearsPage(AmetysObjectResolver resolver, BlogCacheManager cacheManager, SkinsManager skinsManager,  I18nUtils i18nCache, String i18nCatalogue, String title, PagesContainer root, AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> pageDataTypeExtensionPoint, AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> zoneDataTypeExtensionPoint, ServiceExtensionPoint serviceExtensionPoint, AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> zoneItemDataTypeExtensionPoint)
089    {
090        _resolver = resolver;
091        _cacheManager = cacheManager;
092        _skinsManager = skinsManager;
093        _i18nUtils = i18nCache;
094        _i18nCatalogue = i18nCatalogue;
095        _root = root;
096        _title = title;
097        
098        _pageDataTypeExtensionPoint = pageDataTypeExtensionPoint;
099        _zoneDataTypeExtensionPoint = zoneDataTypeExtensionPoint;
100        _serviceExtensionPoint = serviceExtensionPoint;
101        _zoneItemDataTypeExtensionPoint = zoneItemDataTypeExtensionPoint;
102    }
103    
104    @Override
105    public int getDepth() throws AmetysRepositoryException
106    {
107        int depth = 0;
108        if (_root instanceof Page)
109        {
110            depth = ((Page) _root).getDepth();
111        }
112        
113        return depth + 1;
114    }
115
116    @Override
117    public Set<String> getReferers() throws AmetysRepositoryException
118    {
119        return null;
120    }
121
122    @Override
123    public ResourceCollection getRootAttachments() throws AmetysRepositoryException
124    {
125        return null;
126    }
127
128    @Override
129    public String getTemplate() throws AmetysRepositoryException
130    {
131        return null;
132    }
133
134    @Override
135    public String getTitle() throws AmetysRepositoryException
136    {
137        return _title;
138    }
139    
140    @Override
141    public String getLongTitle() throws AmetysRepositoryException
142    {
143        return _title;
144    }
145
146    @Override
147    public PageType getType() throws AmetysRepositoryException
148    {
149        return PageType.NODE;
150    }
151
152    @Override
153    public String getURL() throws AmetysRepositoryException
154    {
155        throw new UnsupportedOperationException("getURL not supported on virtual blog pages");
156    }
157
158    @Override
159    public LinkType getURLType() throws AmetysRepositoryException
160    {
161        throw new UnsupportedOperationException("getURLType not supported on virtual blog pages");
162    }
163
164    @Override
165    public Zone getZone(String name) throws UnknownZoneException, AmetysRepositoryException
166    {
167        throw new UnknownZoneException("There is no zone on the blog years page.");
168    }
169
170    @Override
171    public AmetysObjectIterable< ? extends Zone> getZones() throws AmetysRepositoryException
172    {
173        return new EmptyIterable<>();
174    }
175
176    @Override
177    public boolean hasZone(String name) throws AmetysRepositoryException
178    {
179        return false;
180    }
181
182    @Override
183    public AmetysObjectIterable<? extends Page> getChildrenPages() throws AmetysRepositoryException
184    {
185        ArrayList<Page> children = new ArrayList<>();
186        
187        SortedSet<Integer> years = new TreeSet<>(Collections.reverseOrder());
188        years.addAll(_cacheManager.getYears(getSiteName(), getSitemapName()));
189        
190        for (Integer year : years)
191        {
192            VirtualYearPage page = new VirtualYearPage(_resolver, _cacheManager, _skinsManager, _i18nUtils, _i18nCatalogue, year, Integer.toString(year), _root, _pageDataTypeExtensionPoint, _zoneDataTypeExtensionPoint, _serviceExtensionPoint, _zoneItemDataTypeExtensionPoint);
193            
194            children.add(page);
195        }
196        
197        return new CollectionIterable<>(children);
198    }
199
200    @Override
201    public AmetysObjectIterable< ? extends Page> getChildrenPages(boolean includeInvisiblePages) throws AmetysRepositoryException
202    {
203        return getChildrenPages();
204    }
205    
206    @Override
207    public Page getChildPageAt(int index) throws UnknownAmetysObjectException, AmetysRepositoryException
208    {
209        if (index < 0)
210        {
211            throw new AmetysRepositoryException("Child page index cannot be negative");
212        }
213        
214        AmetysObjectIterable< ? extends Page> childPages = getChildrenPages();
215        AmetysObjectIterator< ? extends Page> it = childPages.iterator();
216        
217        try
218        {
219            it.skip(index);
220            return it.next();
221        }
222        catch (NoSuchElementException e)
223        {
224            throw new UnknownAmetysObjectException("There's no child page at index " + index + " for page " + this.getId());
225        }
226    }
227    
228    @Override
229    public String getPathInSitemap() throws AmetysRepositoryException
230    {
231        StringBuilder path = new StringBuilder(_root.getPathInSitemap());
232        if (path.length() > 0)
233        {
234            path.append('/');
235        }
236        path.append(NAME);
237        
238        return path.toString();
239    }
240
241    @Override
242    public Site getSite() throws AmetysRepositoryException
243    {
244        return _root.getSite();
245    }
246
247    @Override
248    public String getSiteName() throws AmetysRepositoryException
249    {
250        return _root.getSiteName();
251    }
252
253    @Override
254    public Sitemap getSitemap() throws AmetysRepositoryException
255    {
256        return _root.getSitemap();
257    }
258
259    @Override
260    public String getSitemapName() throws AmetysRepositoryException
261    {
262        return _root.getSitemapName();
263    }
264
265    @SuppressWarnings("unchecked")
266    @Override
267    public <A extends AmetysObject> A getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException
268    {
269        if (path.isEmpty())
270        {
271            throw new AmetysRepositoryException("Path must be non empty");
272        }
273        
274        int slashPos = path.indexOf('/');
275        
276        String childName = slashPos == -1 ? path : path.substring(0, slashPos);
277        
278        int year;
279        try
280        {
281            year = Integer.parseInt(childName);
282        }
283        catch (NumberFormatException e)
284        {
285            throw new AmetysRepositoryException(childName + " is not a valid year.", e);
286        }
287        
288        VirtualYearPage page = new VirtualYearPage(_resolver, _cacheManager, _skinsManager, _i18nUtils, _i18nCatalogue, year, childName, _root, _pageDataTypeExtensionPoint, _zoneDataTypeExtensionPoint, _serviceExtensionPoint, _zoneItemDataTypeExtensionPoint);
289        
290        if (slashPos == -1)
291        {
292            return (A) page;
293        }
294        else
295        {
296            return (A) page.getChild(path.substring(slashPos + 1));
297        }
298    }
299
300    @SuppressWarnings("unchecked")
301    @Override
302    public AmetysObjectIterable<? extends AmetysObject> getChildren() throws AmetysRepositoryException
303    {
304        return getChildrenPages();
305    }
306
307    @Override
308    public boolean hasChild(String name) throws AmetysRepositoryException
309    {
310        try
311        {
312            int year = Integer.parseInt(name);
313            
314            return _cacheManager.hasYear(getSiteName(), getSitemapName(), year);
315        }
316        catch (NumberFormatException e)
317        {
318            // Ignore.
319        }
320        
321        return false;
322    }
323    
324    @Override
325    public String getId() throws AmetysRepositoryException
326    {
327        return "blog-category://" + NAME + "?rootId=" + _root.getId();
328    }
329    
330    @Override
331    public String getName() throws AmetysRepositoryException
332    {
333        return NAME;
334    }
335    
336    @SuppressWarnings("unchecked")
337    @Override
338    public PagesContainer getParent() throws AmetysRepositoryException
339    {
340        return _root;
341    }
342
343    @Override
344    public String getParentPath() throws AmetysRepositoryException
345    {
346        return _root.getPath();
347    }
348
349    @Override
350    public String getPath() throws AmetysRepositoryException
351    {
352        StringBuilder buff = new StringBuilder(getParentPath());
353        if (!getParentPath().isEmpty())
354        {
355            buff.append('/');
356        }
357        buff.append(NAME);
358        
359        return buff.toString();
360    }
361
362    public ModelLessDataHolder getDataHolder()
363    {
364        RepositoryData repositoryData = new MemoryRepositoryData(getName());
365        return new DefaultModelLessDataHolder(_pageDataTypeExtensionPoint, repositoryData);
366    }
367
368    @Override
369    public Set<String> getTags() throws AmetysRepositoryException
370    {
371        return Collections.emptySet();
372    }
373    
374    @Override
375    public boolean isVisible() throws AmetysRepositoryException
376    {
377        return true;
378    }
379    
380    public ModelAwareDataHolder getTemplateParametersHolder() throws AmetysRepositoryException
381    {
382        return null;
383    }
384}