001/* 002 * Copyright 2018 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.linkdirectory; 017 018import java.util.Collection; 019import java.util.List; 020import java.util.Map; 021import java.util.stream.Collectors; 022 023import org.apache.avalon.framework.context.Context; 024import org.apache.avalon.framework.context.ContextException; 025import org.apache.avalon.framework.context.Contextualizable; 026import org.apache.avalon.framework.logger.AbstractLogEnabled; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.avalon.framework.service.Serviceable; 030import org.apache.cocoon.ProcessingException; 031import org.apache.cocoon.components.ContextHelper; 032import org.apache.cocoon.environment.Request; 033import org.apache.cocoon.xml.AttributesImpl; 034import org.apache.cocoon.xml.XMLUtils; 035import org.apache.commons.lang3.StringUtils; 036import org.xml.sax.ContentHandler; 037import org.xml.sax.SAXException; 038 039import org.ametys.core.user.CurrentUserProvider; 040import org.ametys.core.user.UserIdentity; 041import org.ametys.plugins.linkdirectory.LinkDirectoryThemesInputDataHelper.ConfiguredThemesInputData; 042import org.ametys.plugins.linkdirectory.LinkDirectoryThemesInputDataHelper.ThemesInputData; 043import org.ametys.plugins.linkdirectory.repository.DefaultLink; 044import org.ametys.web.inputdata.InputData; 045import org.ametys.web.repository.page.Page; 046import org.ametys.web.repository.site.Site; 047 048/** 049 * Input data for the link directory user preferences in thumbnails mode 050 */ 051public class LinkDirectoryInputData extends AbstractLogEnabled implements Contextualizable, InputData, Serviceable 052{ 053 /** The current user provider */ 054 protected CurrentUserProvider _currentUserProvider; 055 056 /** The Avalon context */ 057 private Context _context; 058 059 private DirectoryHelper _directoryHelper; 060 061 /** The link directory themes input data helper */ 062 protected LinkDirectoryThemesInputDataHelper _linkDirectoryThemesInputDataHelper; 063 064 @Override 065 public void contextualize(Context context) throws ContextException 066 { 067 _context = context; 068 } 069 070 @Override 071 public void service(ServiceManager manager) throws ServiceException 072 { 073 _linkDirectoryThemesInputDataHelper = (LinkDirectoryThemesInputDataHelper) manager.lookup(LinkDirectoryThemesInputDataHelper.ROLE); 074 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 075 _directoryHelper = (DirectoryHelper) manager.lookup(DirectoryHelper.ROLE); 076 } 077 078 @Override 079 public boolean isCacheable(Site site, Page currentPage) 080 { 081 Request request = ContextHelper.getRequest(_context); 082 083 String template = _getTemplate(request); 084 if (template == null) 085 { 086 return true; 087 } 088 089 try 090 { 091 ConfiguredThemesInputData configuredThemesInputData = _linkDirectoryThemesInputDataHelper.getThemesInputData(site.getSkinId()); 092 List<ThemesInputData> themesInputData = _getThemesForSkinAndTemplate(configuredThemesInputData, template); 093 if (themesInputData.isEmpty()) 094 { 095 // The current template is not configured for a link directory input data 096 return true; 097 } 098 099 for (ThemesInputData themeInputData : themesInputData) 100 { 101 if (themeInputData.configurable() || themeInputData.displayUserLinks()) 102 { 103 // The applications are configurable 104 return false; 105 } 106 } 107 108 // Find the configured theme ids for this template 109 String language = _directoryHelper.getLanguage(request); 110 List<String> configuredThemesNames = themesInputData.stream() 111 .map(inputData -> _getConfiguredThemes(inputData, language)) 112 .flatMap(Collection::stream) 113 .collect(Collectors.toList()); 114 String siteName = _directoryHelper.getSiteName(request); 115 116 return !_directoryHelper.hasRestrictions(siteName, language, configuredThemesNames) && !_directoryHelper.hasInternalUrl(siteName, language, configuredThemesNames); 117 } 118 catch (Exception e) 119 { 120 getLogger().error("An error occurred while retrieving information from the skin configuration", e); 121 // Configuration file is not readable => toSAX method will not generate any xml 122 return true; 123 } 124 } 125 126 @Override 127 public void toSAX(ContentHandler contentHandler) throws ProcessingException 128 { 129 Request request = ContextHelper.getRequest(_context); 130 131 // Get the current user's login if he is in the front office 132 UserIdentity user = _currentUserProvider.getUser(); 133 134 String template = _getTemplate(request); 135 if (template == null) 136 { 137 getLogger().info("There is no current template"); 138 return; 139 } 140 141 String skinId = _getSkin(request); 142 if (skinId == null) 143 { 144 getLogger().info("There is no current skin"); 145 return; 146 } 147 148 try 149 { 150 contentHandler.startDocument(); 151 152 ConfiguredThemesInputData configuredThemesInputData = _linkDirectoryThemesInputDataHelper.getThemesInputData(skinId); 153 List<ThemesInputData> themesInputDatas = _getThemesForSkinAndTemplate(configuredThemesInputData, template); 154 155 // Is there an error in the configuration file ? 156 String error = configuredThemesInputData.error(); 157 if (StringUtils.isNotBlank(error)) 158 { 159 AttributesImpl attrs = new AttributesImpl(); 160 attrs.addCDATAAttribute("error", error); 161 XMLUtils.createElement(contentHandler, "linkDirectory", attrs); 162 } 163 else 164 { 165 String language = _directoryHelper.getLanguage(request); 166 String siteName = _directoryHelper.getSiteName(request); 167 for (ThemesInputData themesInputData : themesInputDatas) 168 { 169 AttributesImpl attrs = new AttributesImpl(); 170 attrs.addCDATAAttribute("applicable", Boolean.TRUE.toString()); 171 attrs.addCDATAAttribute("configurable", String.valueOf(themesInputData.configurable())); 172 attrs.addCDATAAttribute("displayUserLinks", String.valueOf(themesInputData.displayUserLinks())); 173 attrs.addCDATAAttribute("id", themesInputData.id()); 174 175 XMLUtils.startElement(contentHandler, "linkDirectory", attrs); 176 177 List<String> configuredThemesNames = _getConfiguredThemes(themesInputData, language); 178 if (configuredThemesNames != null) 179 { 180 Map<String, List<String>> themesMap = _directoryHelper.getThemesMap(configuredThemesNames, siteName, language); 181 List<String> correctThemesIds = themesMap.get("themes"); 182 List<String> unknownThemesNames = themesMap.get("unknown-themes"); 183 184 _saxThemes(contentHandler, correctThemesIds, unknownThemesNames); 185 _saxLinks(contentHandler, user, request, correctThemesIds, themesInputData.displayUserLinks(), themesInputData.configurable(), themesInputData.id()); 186 } 187 188 XMLUtils.endElement(contentHandler, "linkDirectory"); 189 } 190 } 191 } 192 catch (Exception e) 193 { 194 getLogger().error("An exception occurred during the processing of the link directory's input data" , e); 195 } 196 } 197 198 private void _saxThemes(ContentHandler contentHandler, List<String> themeIds, List<String> unknownThemesNames) throws SAXException 199 { 200 if (!themeIds.isEmpty()) 201 { 202 XMLUtils.startElement(contentHandler, "themes"); 203 for (String themeId : themeIds) 204 { 205 XMLUtils.createElement(contentHandler, "theme", themeId); 206 } 207 XMLUtils.endElement(contentHandler, "themes"); 208 } 209 210 if (!unknownThemesNames.isEmpty()) 211 { 212 AttributesImpl attr = new AttributesImpl(); 213 attr.addCDATAAttribute("count", Integer.toString(unknownThemesNames.size())); 214 XMLUtils.createElement(contentHandler, "unknown-themes", attr, StringUtils.join(unknownThemesNames, ", ")); 215 } 216 } 217 218 private void _saxLinks(ContentHandler contentHandler, UserIdentity user, Request request, List<String> themeIds, boolean displayUserLinks, boolean configurable, String specificContext) throws ProcessingException 219 { 220 String language = _directoryHelper.getLanguage(request); 221 String siteName = _directoryHelper.getSiteName(request); 222 try 223 { 224 // SAX common links 225 List<DefaultLink> links = _directoryHelper.getLinks(themeIds, siteName, language); 226 227 List<DefaultLink> userLinks = null; 228 if (user != null && displayUserLinks) 229 { 230 userLinks = _directoryHelper.getUserLinks(siteName, language, user).stream().collect(Collectors.toList()); 231 } 232 233 234 // SAX the user own links 235 XMLUtils.startElement(contentHandler, "links"); 236 237 try 238 { 239 String storageContext = siteName + "/" + language; 240 if (StringUtils.isNotEmpty(specificContext)) 241 { 242 storageContext += "/" + specificContext; 243 } 244 _directoryHelper.saxLinks(siteName, contentHandler, links, userLinks, themeIds, configurable, _directoryHelper.getContextVars(request), storageContext, user); 245 } 246 catch (Exception e) 247 { 248 getLogger().error("An exception occurred while saxing the links", e); 249 } 250 251 XMLUtils.endElement(contentHandler, "links"); 252 } 253 catch (Exception e) 254 { 255 throw new ProcessingException("An error occurred while retrieving or saxing the links", e); 256 } 257 } 258 259 /** 260 * Retrieve the configured themes names defined in the skin file link-directory.xml for the current input data and the current language 261 * @param themesInputData Can be an empty {@link String} 262 * @param lang language to filter by. Themes with lang=null will always be returned. 263 * @return the list of configured themes ids, can be empty, cannot be null 264 */ 265 private List<String> _getConfiguredThemes(ThemesInputData themesInputData, String lang) 266 { 267 return themesInputData.themes() 268 .stream() 269 .filter(t -> t.get("lang") == null || t.get("lang").equals(lang)) 270 .map(t -> t.get("id")) 271 .collect(Collectors.toList()); 272 } 273 274 private List<ThemesInputData> _getThemesForSkinAndTemplate(ConfiguredThemesInputData configuredThemesInputData, String template) 275 { 276 List<ThemesInputData> themesInputDatas = configuredThemesInputData.themesInputDatas(); 277 if (themesInputDatas == null) 278 { 279 return List.of(); 280 } 281 282 return themesInputDatas.stream() 283 .filter(t -> _filterByTemplate(t, template)) 284 .collect(Collectors.toList()); 285 } 286 287 private boolean _filterByTemplate(ThemesInputData theme, String template) 288 { 289 List<String> templates = theme.templates(); 290 return templates.contains(template) || templates.contains(LinkDirectoryThemesInputDataHelper.WILDCARD); 291 } 292 293 /** 294 * Get the current template 295 * @param request the request 296 * @return the current template 297 */ 298 private String _getTemplate(Request request) 299 { 300 return (String) request.getAttribute("template"); 301 } 302 303 /** 304 * Get the current skin 305 * @param request the request 306 * @return the current skin 307 */ 308 private String _getSkin(Request request) 309 { 310 return (String) request.getAttribute("skin"); 311 } 312}