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