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.tags.inputdata;
017
018import java.io.IOException;
019import java.io.InputStream;
020import java.util.ArrayList;
021import java.util.Iterator;
022import java.util.List;
023
024import org.apache.avalon.framework.configuration.Configuration;
025import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
026import org.apache.avalon.framework.context.Context;
027import org.apache.avalon.framework.context.ContextException;
028import org.apache.avalon.framework.context.Contextualizable;
029import org.apache.avalon.framework.logger.AbstractLogEnabled;
030import org.apache.avalon.framework.service.ServiceException;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.avalon.framework.service.Serviceable;
033import org.apache.avalon.framework.thread.ThreadSafe;
034import org.apache.cocoon.ProcessingException;
035import org.apache.cocoon.components.ContextHelper;
036import org.apache.cocoon.xml.AttributesImpl;
037import org.apache.cocoon.xml.XMLUtils;
038import org.apache.excalibur.source.Source;
039import org.apache.excalibur.source.SourceNotFoundException;
040import org.apache.excalibur.source.SourceResolver;
041import org.xml.sax.ContentHandler;
042import org.xml.sax.SAXException;
043
044import org.ametys.core.right.RightManager;
045import org.ametys.core.user.CurrentUserProvider;
046import org.ametys.plugins.repository.AmetysObjectIterable;
047import org.ametys.plugins.repository.AmetysObjectResolver;
048import org.ametys.plugins.repository.AmetysRepositoryException;
049import org.ametys.runtime.plugin.component.PluginAware;
050import org.ametys.web.filter.PageFilter;
051import org.ametys.web.filter.StaticPageFilter;
052import org.ametys.web.inputdata.InputData;
053import org.ametys.web.pageaccess.RestrictedPagePolicy;
054import org.ametys.web.renderingcontext.RenderingContext;
055import org.ametys.web.renderingcontext.RenderingContextHandler;
056import org.ametys.web.repository.page.Page;
057import org.ametys.web.repository.page.Page.PageType;
058import org.ametys.web.repository.site.Site;
059import org.ametys.web.repository.site.SiteManager;
060
061/**
062 * This class generates an output with all existing filters defined on context's current page.
063 */
064public class FilteredPagesInputData extends AbstractLogEnabled implements InputData, Serviceable, Contextualizable, ThreadSafe, PluginAware
065{
066    /** The plugin name */
067    protected String _pluginName;
068    /** The source resolver */
069    protected SourceResolver _sourceResolver;
070    /** The repository */
071    protected AmetysObjectResolver _resolver;
072    /** The avalon context. */
073    protected Context _context;
074    /** The right manager */
075    protected RightManager _rightManager;
076    /** The ametys sites manager */
077    protected SiteManager _siteManager;
078    /** The rendering context handler. */
079    protected RenderingContextHandler _renderingContextHandler;
080    /** The current user provider */
081    protected CurrentUserProvider _currentUserProvider;
082    
083    @Override
084    public void service(ServiceManager smanager) throws ServiceException
085    {
086        _sourceResolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE);
087        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
088        _rightManager = (RightManager) smanager.lookup(RightManager.ROLE);
089        _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE);
090        _renderingContextHandler = (RenderingContextHandler) smanager.lookup(RenderingContextHandler.ROLE);
091        _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE);
092    }
093    
094    @Override
095    public boolean isCacheable(Site site, Page page)
096    {
097        return site.getRestrictedPagePolicy() == RestrictedPagePolicy.DISPLAYED;
098    }
099    
100    @Override
101    public void toSAX(ContentHandler contentHandler) throws SAXException, ProcessingException
102    {
103        Page page = (Page) ContextHelper.getRequest(_context).getAttribute(Page.class.getName());
104        String siteName = (String) ContextHelper.getRequest(_context).getAttribute("site");
105        String lang = (String) ContextHelper.getRequest(_context).getAttribute("sitemapLanguage");      
106        
107        contentHandler.startDocument();
108        XMLUtils.startElement(contentHandler, "PageModel");
109
110        try
111        {
112            List<PageFilter> filters = configureFilters();
113            for (PageFilter filter : filters)
114            {
115                saxFilter(contentHandler, filter, siteName, lang, page);
116            }
117        }
118        catch (IOException e)
119        {
120            throw new ProcessingException("An error occurred while performing search on filtered contents", e);
121        }
122        
123        XMLUtils.endElement(contentHandler, "PageModel");
124        contentHandler.endDocument();
125    }
126    
127    /**
128     * SAX a filter
129     * @param contentHandler The content handler
130     * @param filter The filter to SAX
131     * @param siteName The current site name
132     * @param lang The current language
133     * @param currentPage The current page
134     * @throws SAXException If an errors occurs while SAXing
135     * @throws IOException If an errors occurs
136     */
137    protected void saxFilter (ContentHandler contentHandler, PageFilter filter, String siteName, String lang, Page currentPage)  throws SAXException, IOException
138    {
139        RestrictedPagePolicy policy = _siteManager.getSite(siteName).getRestrictedPagePolicy();
140        RenderingContext currentContext = _renderingContextHandler.getRenderingContext();
141        
142        String id = filter.getId();
143        
144        XMLUtils.startElement(contentHandler, id);
145        AmetysObjectIterable<Page> pages = filter.getMatchingPages(siteName, lang, currentPage);
146        Iterator<Page> it = pages.iterator();
147        
148        int index = 0;
149        while (it.hasNext() && index < filter.getLength())
150        {
151            Page page = it.next();
152            
153            // In back-office or in preview or live mode, access is always granted.
154            if (currentContext == RenderingContext.BACK || currentContext == RenderingContext.PREVIEW 
155                    || policy == RestrictedPagePolicy.DISPLAYED || isPageAccessible(page))
156            {
157                saxPage(contentHandler, page);
158                index++;
159            }
160        }
161        
162        XMLUtils.endElement(contentHandler, id);
163    }
164    
165    /**
166     * Test the page accessible by the current user.
167     * @param page the page
168     * @return true if the page is accessible, false otherwise.
169     */
170    protected boolean isPageAccessible (Page page)
171    {
172        return _rightManager.hasReadAccess(_currentUserProvider.getUser(), page);
173    }
174    
175    /**
176     * Retrieves the configured filters
177     * @return The list of filters
178     * @throws ProcessingException If an error occurred while parsing the file.
179     */
180    protected List<PageFilter> configureFilters () throws ProcessingException
181    {
182        List<PageFilter> filters = new ArrayList<>();
183        
184        Source source = null;
185        
186        try
187        {
188            source = _sourceResolver.resolveURI("template://filters/default.xml");
189            if (source.exists())
190            {
191                try (InputStream is = source.getInputStream())
192                {
193                    Configuration configuration = new DefaultConfigurationBuilder().build(source.getInputStream());
194                    Configuration[] filterConfiguration = configuration.getChildren();
195                    
196                    for (Configuration filterConf : filterConfiguration)
197                    {
198                        if ("page".equals(filterConf.getAttribute("target")))
199                        {
200                            StaticPageFilter filter = new StaticPageFilter (filterConf.getAttribute("id"), _resolver);
201                            filter.configure(filterConf);
202                            
203                            filters.add(filter);
204                        }
205                    }
206                }                
207            }
208            
209            return filters;
210        }
211        catch (SourceNotFoundException e)
212        {
213            return filters;
214        }
215        catch (Exception e)
216        {
217            throw new ProcessingException("Unable to parse filters file" , e);
218        }
219        finally
220        {
221            if (_sourceResolver != null && source != null)
222            {
223                _sourceResolver.release(source);
224            }
225        }
226    }
227
228    /**
229     * SAX a page
230     * @param contentHandler The content handler
231     * @param page The page to SAX
232     * @throws SAXException if an error occurs while saxing
233     * @throws AmetysRepositoryException if failed to get page's properties
234     */
235    protected void saxPage(ContentHandler contentHandler, Page page) throws SAXException, AmetysRepositoryException
236    {
237        AttributesImpl attrs = new AttributesImpl();
238        attrs.addCDATAAttribute("id", page.getId());
239        attrs.addCDATAAttribute("name", page.getName());
240        attrs.addCDATAAttribute("path", page.getPathInSitemap());
241        attrs.addCDATAAttribute("title", page.getTitle());
242        attrs.addCDATAAttribute("long-title", page.getLongTitle());
243        
244        PageType type = page.getType();
245        
246        attrs.addCDATAAttribute("type", type.toString().toLowerCase());
247        
248        if (type == PageType.LINK)
249        {
250            attrs.addCDATAAttribute("url", page.getURL());
251            attrs.addCDATAAttribute("urlType", page.getURLType().toString().toLowerCase());
252        }
253
254        for (String tag : page.getTags())
255        {
256            attrs.addCDATAAttribute(tag, "true");
257        }
258        
259        XMLUtils.createElement(contentHandler, "page", attrs);
260    }
261    
262    @Override
263    public void contextualize(Context context) throws ContextException
264    {
265        _context = context;
266    }
267
268    @Override
269    public void setPluginInfo(String pluginName, String featureName, String id)
270    {
271        _pluginName = pluginName;
272    }
273    
274}