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