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