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