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