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}