001/*
002 *  Copyright 2015 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.posts;
017
018import java.io.IOException;
019import java.time.ZonedDateTime;
020import java.util.Collection;
021import java.util.Iterator;
022import java.util.LinkedHashSet;
023import java.util.Set;
024
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.cocoon.ProcessingException;
028import org.apache.cocoon.components.source.impl.SitemapSource;
029import org.apache.cocoon.environment.ObjectModelHelper;
030import org.apache.cocoon.environment.Request;
031import org.apache.cocoon.generation.ServiceableGenerator;
032import org.apache.cocoon.xml.AttributesImpl;
033import org.apache.cocoon.xml.XMLUtils;
034import org.apache.excalibur.source.SourceResolver;
035import org.xml.sax.SAXException;
036
037import org.ametys.cms.content.ContentHelper;
038import org.ametys.cms.repository.Content;
039import org.ametys.core.util.DateUtils;
040import org.ametys.core.util.IgnoreRootHandler;
041import org.ametys.plugins.blog.BlogCacheManager;
042import org.ametys.plugins.blog.BlogCacheManager.Post;
043import org.ametys.plugins.blog.BlogConstants;
044import org.ametys.plugins.repository.AmetysObjectResolver;
045import org.ametys.web.repository.page.Page;
046import org.ametys.web.repository.site.Site;
047import org.ametys.web.repository.site.SiteManager;
048
049/**
050 * Generates posts from the cache.
051 */
052public class PostsGenerator extends ServiceableGenerator
053{
054    /** The blog cache manager. */
055    protected BlogCacheManager _blogCache;
056    
057    /** The ametys object resolver. */
058    protected AmetysObjectResolver _ametysResolver;
059    
060    /** The site Manager. */
061    protected SiteManager _siteManager;
062    
063    /** The source resolver. */
064    protected SourceResolver _sourceResolver;
065
066    /** The content helper. */
067    protected ContentHelper _contentHelper;
068    
069    @Override
070    public void service(ServiceManager serviceManager) throws ServiceException
071    {
072        super.service(serviceManager);
073        _blogCache = (BlogCacheManager) serviceManager.lookup(BlogCacheManager.ROLE);
074        _ametysResolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
075        _sourceResolver = (SourceResolver) serviceManager.lookup(SourceResolver.ROLE);
076        _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE);
077        _contentHelper = (ContentHelper) manager.lookup(ContentHelper.ROLE);
078    }
079    
080    @Override
081    public void generate() throws IOException, SAXException, ProcessingException
082    {
083        Request request = ObjectModelHelper.getRequest(objectModel);
084        
085        // Get site, language and page in request attributes. Can be null if the filter does not need them.
086        Page page = (Page) request.getAttribute(Page.class.getName());
087        String siteName = parameters.getParameter("site", page.getSiteName());
088        String language = parameters.getParameter("lang", page.getSitemapName());
089        
090        String type = parameters.getParameter("type", "all");
091        int year = parameters.getParameterAsInteger("year", -1);
092        int month = parameters.getParameterAsInteger("month", -1);
093        
094        String tagName = parameters.getParameter("tagName", "");
095        
096        Site site = _siteManager.getSite(siteName);
097        Long postsPerPage = site.getValue("posts-service-max-count", false, Long.valueOf(BlogConstants.DEFAULT_POST_COUNT_PER_PAGE));
098        
099        String viewName = parameters.getParameter("viewName", "");
100        
101        AttributesImpl attrs = new AttributesImpl();
102        attrs.addCDATAAttribute("posts-per-page", Long.toString(postsPerPage));
103        
104        contentHandler.startDocument();
105        XMLUtils.startElement(contentHandler, "contents", attrs);
106        
107        // Get all the posts.
108        Collection<String> postIds = getPosts(siteName, language, type, year, month, tagName, BlogConstants.DEFAULT_POST_TOTAL_COUNT);
109        
110        for (String postId : postIds)
111        {
112            // Resolve the content.
113            Content postContent = _ametysResolver.resolveById(postId);
114            
115            saxContent(postContent, viewName);
116        }
117        
118        XMLUtils.endElement(contentHandler, "contents");
119        contentHandler.endDocument();
120    }
121    
122    /**
123     * SAX a content in its specific view
124     * @param content The content to SAX
125     * @param viewName The view to use
126     * @throws SAXException If an error occurs while SAXing
127     * @throws IOException If an error occurs while retrieving content.
128     */
129    public void saxContent(Content content, String viewName) throws SAXException, IOException
130    {
131        AttributesImpl attrs = new AttributesImpl();
132        attrs.addCDATAAttribute("id", content.getId());
133        attrs.addCDATAAttribute("name", content.getName());
134        attrs.addCDATAAttribute("title", content.getTitle());
135        attrs.addCDATAAttribute("lastModifiedAt", DateUtils.zonedDateTimeToString(content.getLastModified()));
136        
137        XMLUtils.startElement(contentHandler, "content", attrs);
138        
139        String uri = _contentHelper.getContentHtmlViewUrl(content, viewName);
140        SitemapSource src = null;
141        
142        try
143        {
144            src = (SitemapSource) _sourceResolver.resolveURI(uri);
145            src.toSAX(new IgnoreRootHandler(contentHandler));
146        }
147        finally
148        {
149            _sourceResolver.release(src);
150        }
151        
152        XMLUtils.endElement(contentHandler, "content");
153    }
154    
155    /**
156     * Get all the posts for the given criteria.
157     * @param siteName the site name.
158     * @param language the language.
159     * @param type the search type ('all', 'year', 'month' or 'tag').
160     * @param year the year in case of a 'year' or 'month' search.
161     * @param month the month, in case of a 'month' search.
162     * @param tagName the tag name in case of a 'tag' search.
163     * @return the post IDs.
164     */
165    protected Collection<String> getPosts(String siteName, String language, String type, int year, int month, String tagName)
166    {
167        return getPosts(siteName, language, type, year, month, tagName, 0);
168    }
169    
170    /**
171     * Get a limited number of posts for the given criteria.
172     * @param siteName the site name.
173     * @param language the language.
174     * @param type the search type ('all', 'year', 'month' or 'tag').
175     * @param year the year in case of a 'year' or 'month' search.
176     * @param month the month, in case of a 'month' search.
177     * @param tagName the tag name in case of a 'tag' search.
178     * @param maxCount the maximum count of posts to retrieve.
179     * @return the post IDs.
180     */
181    protected Collection<String> getPosts(String siteName, String language, String type, int year, int month, String tagName, int maxCount)
182    {
183        Set<String> posts = new LinkedHashSet<>();
184
185        boolean displayFuturePosts = true;
186        if (siteName != null)
187        {
188            Site site = _siteManager.getSite(siteName);
189            displayFuturePosts = site.getValue("display-future-posts", false, false);
190        }
191        
192        Iterator<Post> postIt = null;
193        if ("all".equals(type))
194        {
195            postIt = _blogCache.getSortedPosts(siteName, language).iterator();
196        }
197        else if ("year".equals(type))
198        {
199            postIt = _blogCache.getSortedPostsByYear(siteName, language, year).iterator();
200        }
201        else if ("month".equals(type))
202        {
203            postIt = _blogCache.getSortedPostsByMonth(siteName, language, year, month).iterator();
204        }
205        else if ("tag".equals(type))
206        {
207            postIt = _blogCache.getSortedPostsByTag(siteName, language, tagName, true).iterator();
208        }
209        
210        if (postIt != null)
211        {
212            int count = 0;
213            while (postIt.hasNext() && (maxCount == 0 || count < maxCount))
214            {
215                Post post = postIt.next();
216                if (displayFuturePosts || !_isFuturePost(post))
217                {
218                    posts.add(post.getId());
219                    count++;
220                }
221            }
222        }
223        
224        return posts;
225    }
226    
227    private boolean _isFuturePost (Post post)
228    {
229        ZonedDateTime postDate = post.getDate();
230        return postDate != null && postDate.isAfter(ZonedDateTime.now());
231    }
232}