001/*
002 *  Copyright 2010 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.web.filter;
017
018import java.io.IOException;
019import java.text.DateFormat;
020import java.text.SimpleDateFormat;
021import java.util.Date;
022import java.util.Iterator;
023import java.util.Locale;
024
025import org.apache.avalon.framework.parameters.ParameterException;
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.cocoon.ProcessingException;
029import org.apache.cocoon.components.source.impl.SitemapSource;
030import org.apache.cocoon.environment.ObjectModelHelper;
031import org.apache.cocoon.environment.Request;
032import org.apache.cocoon.generation.ServiceableGenerator;
033import org.apache.cocoon.xml.XMLUtils;
034import org.apache.excalibur.source.SourceResolver;
035import org.xml.sax.SAXException;
036
037import org.ametys.cms.filter.ContentFilter;
038import org.ametys.cms.repository.Content;
039import org.ametys.core.util.IgnoreRootHandler;
040import org.ametys.plugins.repository.AmetysObjectIterable;
041import org.ametys.runtime.i18n.I18nizableText;
042import org.ametys.web.renderingcontext.RenderingContext;
043import org.ametys.web.renderingcontext.RenderingContextHandler;
044import org.ametys.web.repository.content.WebContent;
045import org.ametys.web.repository.page.Page;
046import org.ametys.web.repository.site.Site;
047import org.ametys.web.repository.site.SiteManager;
048import org.ametys.web.repository.sitemap.Sitemap;
049
050/**
051 * Generates SAX events for RSS feed for contents matching a {@link ContentFilter} set in request attributes
052 */
053public class RSSGenerator extends ServiceableGenerator
054{
055    private static final int _DEFAULT_MAX_COUNT = 20;
056    
057    /** The content filter helper. */
058    protected ContentFilterHelper _contentFilterHelper;
059    
060    private SourceResolver _resolver;
061    private SiteManager _siteManager;
062    private RenderingContextHandler _renderingContextHandler;
063    
064    @Override
065    public void service(ServiceManager smanager) throws ServiceException
066    {
067        super.service(smanager);
068        _contentFilterHelper = (ContentFilterHelper) smanager.lookup(ContentFilterHelper.ROLE);
069        _resolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE);
070        _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE);
071        _renderingContextHandler = (RenderingContextHandler) manager.lookup(RenderingContextHandler.ROLE);
072    }
073    
074    @Override
075    public void generate() throws IOException, SAXException, ProcessingException
076    {
077        Request request = ObjectModelHelper.getRequest(objectModel);
078        
079        // Get filter in request attributes
080        ContentFilter filter = (ContentFilter) request.getAttribute("filter");
081        
082        String siteName;
083        String lang;
084        try
085        {
086            // Get site and language in sitemap parameters. Can not be null.
087            siteName = parameters.getParameter("siteName");
088            lang = parameters.getParameter("lang");
089        }
090        catch (ParameterException e)
091        {
092            throw new ProcessingException("Missing the site name and/or the language parameters to generate RSS feed", e);
093        }
094        
095        // The path can be null if the filter does not need the current page.
096        String path = parameters.getParameter("path", null);
097        
098        Site site = _siteManager.getSite(siteName);
099        Sitemap sitemap = site.getSitemap(lang);
100        Page page = null;
101        if (path != null)
102        {
103            page = sitemap.getChild(path);
104        }
105        
106        contentHandler.startDocument();
107        XMLUtils.startElement(contentHandler, "RssFeed");
108        
109        if (filter != null && filter instanceof WebContentFilter)
110        {
111            WebContentFilter webFilter = (WebContentFilter) filter;
112            
113            I18nizableText title = webFilter.getTitle();
114            if (title != null)
115            {
116                title.toSAX(contentHandler, "title");
117            }
118            
119            I18nizableText description = webFilter.getDescription();
120            if (description != null)
121            {
122                description.toSAX(contentHandler, "description");
123            }
124            
125            DateFormat dfRFC = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
126            XMLUtils.createElement(contentHandler, "pubDate", dfRFC.format(new Date()));
127            
128            AmetysObjectIterable<Content> contents = webFilter.getMatchingContents(siteName, lang, page);
129            Iterator<Content> it = contents.iterator();
130            
131            int maxCount = filter.getLength();
132            if (maxCount == Integer.MAX_VALUE)
133            {
134                maxCount = _DEFAULT_MAX_COUNT;
135            }
136            
137            int index = 0;
138            while (it.hasNext() && index < maxCount)
139            {
140                Content content = it.next();
141                if (_contentFilterHelper.isContentValid(content, page, webFilter))
142                {
143                    _saxItem(content, filter.getView(), siteName);
144                    index++;
145                }
146            }
147        }
148        
149        XMLUtils.endElement(contentHandler, "RssFeed");
150        contentHandler.endDocument();
151    }
152    
153    /**
154     * Determines if a content is orphan
155     * @param content the content to test
156     * @return true if the content is orphan
157     */
158    protected boolean isOrphan(Content content)
159    {
160        if (content instanceof WebContent)
161        {
162            return ((WebContent) content).getReferencingPages().size() == 0;
163        }
164        return true;
165    }
166    
167    private void _saxItem (Content content, String viewName, String siteName) throws SAXException, IOException
168    {
169        RenderingContext currentContext = _renderingContextHandler.getRenderingContext();
170        if (currentContext == RenderingContext.BACK)
171        {
172            // Issue CMS-2642
173            _renderingContextHandler.setRenderingContext(RenderingContext.PREVIEW);
174        }
175        
176        String uri = "cocoon:/_content.rss?contentId=" + content.getId() + "&viewName=" + viewName + "&siteName=" + siteName;
177        SitemapSource src = null;
178        
179        try
180        {
181            src = (SitemapSource) _resolver.resolveURI(uri);
182            src.toSAX(new IgnoreRootHandler(contentHandler));
183        }
184        finally
185        {
186            _resolver.release(src);
187            _renderingContextHandler.setRenderingContext(currentContext);
188        }
189    }
190}