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.List; 022 023import org.apache.avalon.framework.activity.Initializable; 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.environment.Request; 037import org.apache.cocoon.xml.XMLUtils; 038import org.apache.excalibur.source.Source; 039import org.apache.excalibur.source.SourceResolver; 040import org.xml.sax.ContentHandler; 041import org.xml.sax.SAXException; 042 043import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 044import org.ametys.cms.tag.TagProviderExtensionPoint; 045import org.ametys.core.cache.AbstractCacheManager; 046import org.ametys.core.cache.Cache; 047import org.ametys.core.util.filereloader.FileReloader; 048import org.ametys.core.util.filereloader.FileReloaderUtils; 049import org.ametys.plugins.repository.AmetysObjectResolver; 050import org.ametys.runtime.i18n.I18nizableText; 051import org.ametys.runtime.plugin.component.PluginAware; 052import org.ametys.web.content.GetSiteAction; 053import org.ametys.web.filter.ContentFilterHelper; 054import org.ametys.web.filter.StaticWebContentFilter; 055import org.ametys.web.filter.WebContentFilter; 056import org.ametys.web.filter.WebContentFilter.AccessLimitation; 057import org.ametys.web.inputdata.InputData; 058import org.ametys.web.repository.page.Page; 059import org.ametys.web.repository.site.Site; 060import org.ametys.web.repository.site.SiteManager; 061 062/** 063 * This class generates an output with all existing filters defined on context's current page. 064 */ 065public class FilteredContentsInputData extends AbstractLogEnabled implements InputData, Serviceable, Contextualizable, ThreadSafe, PluginAware, FileReloader, Initializable 066{ 067 private static final String _CACHE_ID = FilteredContentsInputData.class.getName(); 068 069 /** The plugin name */ 070 protected String _pluginName; 071 /** The source resolver */ 072 protected SourceResolver _sourceResolver; 073 /** The repository */ 074 protected AmetysObjectResolver _resolver; 075 /** The extension point for content types */ 076 protected ContentTypeExtensionPoint _contentTypeEP; 077 /** The avalon context. */ 078 protected Context _context; 079 /** The site manager */ 080 protected SiteManager _siteManager; 081 /** The tag provider EP */ 082 protected TagProviderExtensionPoint _tagProviderEP; 083 084 private ContentFilterHelper _filterHelper; 085 private FileReloaderUtils _fileReloaderUtils; 086 private AbstractCacheManager _cacheManager; 087 088 @Override 089 public void service(ServiceManager smanager) throws ServiceException 090 { 091 _sourceResolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE); 092 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 093 _contentTypeEP = (ContentTypeExtensionPoint) smanager.lookup(ContentTypeExtensionPoint.ROLE); 094 _filterHelper = (ContentFilterHelper) smanager.lookup(ContentFilterHelper.ROLE); 095 _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE); 096 _tagProviderEP = (TagProviderExtensionPoint) smanager.lookup(TagProviderExtensionPoint.ROLE); 097 _fileReloaderUtils = (FileReloaderUtils) smanager.lookup(FileReloaderUtils.ROLE); 098 _cacheManager = (AbstractCacheManager) smanager.lookup(AbstractCacheManager.ROLE); 099 } 100 101 @Override 102 public void initialize() throws Exception 103 { 104 _cacheManager.createMemoryCache(_CACHE_ID, 105 new I18nizableText("plugin.web", "PLUGINS_WEB_FILTERED_CONTENTS_INPUT_DATA_CACHE_LABEL"), 106 new I18nizableText("plugin.web", "PLUGINS_WEB_FILTERED_CONTENTS_INPUT_DATA_CACHE_DESCRIPTION"), 107 true, 108 null); 109 } 110 111 @Override 112 public boolean isCacheable(Site site, Page page) 113 { 114 try 115 { 116 List<WebContentFilter> filters = configureFilters(site, page); 117 return filters.stream() 118 .map(WebContentFilter::getAccessLimitation) 119 .noneMatch(limitation -> limitation == AccessLimitation.USER_ACCESS); 120 } 121 catch (Exception e) 122 { 123 // unable to parse filters file 124 throw new IllegalArgumentException(e); 125 } 126 } 127 128 @Override 129 public void toSAX(ContentHandler contentHandler) throws SAXException, ProcessingException 130 { 131 Page page = (Page) ContextHelper.getRequest(_context).getAttribute(Page.class.getName()); 132 String siteName = (String) ContextHelper.getRequest(_context).getAttribute("site"); 133 String lang = (String) ContextHelper.getRequest(_context).getAttribute("sitemapLanguage"); 134 135 contentHandler.startDocument(); 136 XMLUtils.startElement(contentHandler, "Model"); 137 138 try 139 { 140 List<WebContentFilter> filters = configureFilters(_siteManager.getSite(siteName), page); 141 for (WebContentFilter filter : filters) 142 { 143 saxFilter(contentHandler, filter, siteName, lang, page); 144 } 145 } 146 catch (Exception e) 147 { 148 throw new ProcessingException("An error occurred while performing search on filtered contents", e); 149 } 150 151 XMLUtils.endElement(contentHandler, "Model"); 152 contentHandler.endDocument(); 153 } 154 155 /** 156 * Retrieves the configured filters 157 * @param site the current {@link Site} 158 * @param page the current {@link Page} 159 * @return The list of filters 160 * @throws Exception If an error occurred while parsing the file. 161 */ 162 protected List<WebContentFilter> configureFilters(Site site, Page page) throws Exception 163 { 164 String sourceUri = "skin:" + site.getSkinId() + "://templates/" + _getTemplate(page) + "/filters/default.xml"; 165 166 boolean forceReload = !_getCache().hasKey(sourceUri); 167 _fileReloaderUtils.updateFile(sourceUri, forceReload, this); 168 169 return _getCache().get(sourceUri); 170 } 171 172 private String _getTemplate(Page page) 173 { 174 // We can have a null page on specific pages (like login page) 175 if (page != null) 176 { 177 return page.getTemplate(); 178 } 179 180 Request request = ContextHelper.getRequest(_context); 181 return (String) request.getAttribute("template"); 182 } 183 184 /** 185 * SAX a filter 186 * @param contentHandler The content handler 187 * @param filter The filter to SAX 188 * @param siteName The current site name 189 * @param lang The current language 190 * @param page The current page 191 * @throws SAXException If an errors occurs while SAXing 192 * @throws IOException If an errors occurs 193 */ 194 @SuppressWarnings("deprecation") 195 protected void saxFilter (ContentHandler contentHandler, WebContentFilter filter, String siteName, String lang, Page page) throws SAXException, IOException 196 { 197 Request request = ContextHelper.getRequest(_context); 198 String currentSiteName = (String) request.getAttribute("site"); 199 String currentSkinName = (String) request.getAttribute("skin"); 200 String currentTemplateName = (String) request.getAttribute("template"); 201 String currentLanguage = (String) request.getAttribute("renderingLanguage"); 202 203 // Issue CMS-3391 204 request.setAttribute(GetSiteAction.OVERRIDE_SITE_REQUEST_ATTR, currentSiteName); 205 request.setAttribute(GetSiteAction.OVERRIDE_SKIN_REQUEST_ATTR, currentSkinName); 206 207 try 208 { 209 String id = filter.getId(); 210 211 XMLUtils.startElement(contentHandler, id); 212 _filterHelper.saxMatchingContents(contentHandler, filter, siteName, lang, page); 213 XMLUtils.endElement(contentHandler, id); 214 } 215 finally 216 { 217 request.removeAttribute(GetSiteAction.OVERRIDE_SITE_REQUEST_ATTR); 218 request.removeAttribute(GetSiteAction.OVERRIDE_SKIN_REQUEST_ATTR); 219 request.setAttribute("site", currentSiteName); 220 request.setAttribute("skin", currentSkinName); 221 request.setAttribute("template", currentTemplateName); 222 request.setAttribute("renderingLanguage", currentLanguage); 223 } 224 } 225 226 @Override 227 public void contextualize(Context context) throws ContextException 228 { 229 _context = context; 230 } 231 232 @Override 233 public void setPluginInfo(String pluginName, String featureName, String id) 234 { 235 _pluginName = pluginName; 236 } 237 238 public String getId(String sourceUrl) 239 { 240 return FilteredContentsInputData.class.getName() + "#" + sourceUrl; 241 } 242 243 public void updateFile(String sourceUrl, Source source, InputStream is) throws Exception 244 { 245 List<WebContentFilter> filters = new ArrayList<>(); 246 247 if (is != null) 248 { 249 Configuration configuration = new DefaultConfigurationBuilder().build(source.getInputStream()); 250 Configuration[] filterConfiguration = configuration.getChildren(); 251 252 for (Configuration filterConf : filterConfiguration) 253 { 254 if ("content".equals(filterConf.getAttribute("target"))) 255 { 256 StaticWebContentFilter filter = new StaticWebContentFilter (filterConf.getAttribute("id"), _resolver, _contentTypeEP, _siteManager, _tagProviderEP); 257 filter.configure(filterConf); 258 259 filters.add(filter); 260 } 261 } 262 } 263 264 _getCache().put(sourceUrl, filters); 265 } 266 267 private Cache<String, List<WebContentFilter>> _getCache() 268 { 269 return _cacheManager.get(_CACHE_ID); 270 } 271}