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.skin; 017 018import java.io.InputStream; 019import java.util.Collections; 020import java.util.HashMap; 021import java.util.HashSet; 022import java.util.Map; 023import java.util.Set; 024import java.util.regex.Pattern; 025 026import org.apache.avalon.framework.configuration.Configuration; 027import org.apache.avalon.framework.configuration.ConfigurationException; 028import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 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.excalibur.source.Source; 034import org.apache.excalibur.source.SourceResolver; 035 036import org.ametys.web.repository.page.Page; 037import org.ametys.web.repository.site.Site; 038 039/** 040 * This implementation of the templates handler is based uppon a configuration. 041 */ 042public class StaticTemplatesAssignmentHandler extends AbstractLogEnabled implements TemplatesAssignmentHandler, Serviceable 043{ 044 /** The skins manager */ 045 protected SkinsManager _skinsManager; 046 /** The source resolver */ 047 protected SourceResolver _srcResolver; 048 049 private Map<String, Map<String, AssignmentCondition>> _tplCache = new HashMap<>(); 050 private Map<String, Long> _lastConfUpdate = new HashMap<>(); 051 052 @Override 053 public void service(ServiceManager smanager) throws ServiceException 054 { 055 _skinsManager = (SkinsManager) smanager.lookup(SkinsManager.ROLE); 056 _srcResolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE); 057 } 058 059 @Override 060 public Set<String> getAvailablesTemplates(String skinName) 061 { 062 Skin skin = _skinsManager.getSkin(skinName); 063 if (skin == null) 064 { 065 getLogger().warn("The unexisting skin '" + skinName + "' is referenced (by a site?)"); 066 return Collections.emptySet(); 067 } 068 069 _refreshValues(skin); 070 071 return _tplCache.get(skinName).keySet(); 072 } 073 074 @Override 075 public Set<String> getAvailablesTemplates(Page page) 076 { 077 Set<String> availableTemplates = new HashSet<>(); 078 079 Site site = page.getSite(); 080 081 String skinName = site.getSkinId(); 082 Skin skin = _skinsManager.getSkin(skinName); 083 if (skin == null) 084 { 085 getLogger().warn("The site '" + site.getName() + "' reference the unexisting skin '" + site.getSkinId() + "'"); 086 return availableTemplates; 087 } 088 089 _refreshValues (skin); 090 091 Map<String, AssignmentCondition> templates = _tplCache.get(skinName); 092 for (String templateName : templates.keySet()) 093 { 094 if (templates.get(templateName).matchCondition(page)) 095 { 096 availableTemplates.add(templateName); 097 } 098 } 099 100 return availableTemplates; 101 } 102 103 /** 104 * Get the available templates for assignment 105 * @param skin The skin 106 */ 107 protected void _refreshValues(Skin skin) 108 { 109 Source source = null; 110 try 111 { 112 source = _srcResolver.resolveURI("context://skins/" + skin.getId() + "/conf/template_assignment.xml"); 113 if (source.exists()) 114 { 115 if (!_lastConfUpdate.containsKey(skin.getId()) || _lastConfUpdate.get(skin.getId()) < source.getLastModified()) 116 { 117 _lastConfUpdate.put(skin.getId(), source.getLastModified()); 118 119 try (InputStream is = source.getInputStream()) 120 { 121 Configuration configuration = new DefaultConfigurationBuilder().build(is); 122 _parseAvailableTemplates (skin, configuration); 123 } 124 } 125 } 126 else 127 { 128 _getAllTemplatesWithoutCondition(skin); 129 } 130 } 131 catch (Exception e) 132 { 133 getLogger().error("Unable to read the available templates configuration file", e); 134 } 135 } 136 137 private void _parseAvailableTemplates (Skin skin, Configuration configuration) throws ConfigurationException 138 { 139 boolean exclude = configuration.getChild("list", false) == null || "exclude".equals(configuration.getChild("list").getAttribute("mode", "include")); 140 141 if (exclude) 142 { 143 _getAllTemplatesWithoutCondition (skin); 144 } 145 146 Configuration[] tplList = configuration.getChild("list").getChildren("template"); 147 for (Configuration tplConf : tplList) 148 { 149 String reName = tplConf.getAttribute("name"); 150 Pattern namePattern = _getPattern (reName); 151 152 for (String templateName : skin.getTemplates()) 153 { 154 if (namePattern.matcher(templateName).matches()) 155 { 156 if (!exclude) 157 { 158 if (!_tplCache.containsKey(skin.getId())) 159 { 160 _tplCache.put(skin.getId(), new HashMap<String, AssignmentCondition>()); 161 } 162 Map<String, AssignmentCondition> m = _tplCache.get(skin.getId()); 163 m.put(templateName, new AssignmentCondition(skin.getId(), templateName)); 164 } 165 else 166 { 167 Map<String, AssignmentCondition> m = _tplCache.get(skin.getId()); 168 m.remove(templateName); 169 } 170 } 171 } 172 } 173 174 Set<String> findCondition = new HashSet<>(); 175 176 // Conditions 177 Configuration[] conditions = configuration.getChild("conditions").getChildren("condition"); 178 for (Configuration conditionConf : conditions) 179 { 180 String reName = conditionConf.getAttribute("template"); 181 Pattern namePattern = _getPattern (reName); 182 183 Set<String> skinTemplates = _tplCache.get(skin.getId()).keySet(); 184 for (String templateName : skinTemplates) 185 { 186 if (!findCondition.contains(templateName) && namePattern.matcher(templateName).matches()) 187 { 188 findCondition.add(templateName); 189 190 AssignmentCondition condition = _tplCache.get(skin.getId()).get(templateName); 191 192 String regexp = conditionConf.getChild("page").getAttribute("regexp_path", null); 193 if (regexp != null) 194 { 195 condition.setRegExpPath(regexp, false); 196 } 197 else 198 { 199 String reverseRegexp = conditionConf.getChild("page").getAttribute("reverse_regexp_path", null); 200 if (reverseRegexp != null) 201 { 202 condition.setRegExpPath(reverseRegexp, true); 203 } 204 } 205 } 206 } 207 } 208 } 209 210 private void _getAllTemplatesWithoutCondition (Skin skin) 211 { 212 Map<String, AssignmentCondition> m = new HashMap<>(); 213 _tplCache.put(skin.getId(), m); 214 215 for (String templateName : skin.getTemplates()) 216 { 217 m.put(templateName, new AssignmentCondition(skin.getId(), templateName)); 218 } 219 } 220 221 private Pattern _getPattern (String pattern) 222 { 223 String regexp = "^" + pattern.replaceAll("\\*", "([^/]*)") + "$"; 224 return Pattern.compile(regexp); 225 } 226 227 /** 228 * Class representing the condition for a template assignment 229 */ 230 public class AssignmentCondition 231 { 232 private String _skinName; 233 private String _templateName; 234 private String _rePath; 235 private boolean _reverse; 236 237 /** 238 * Constructor 239 * @param skinName the skin name 240 * @param templateName the template name 241 */ 242 public AssignmentCondition (String skinName, String templateName) 243 { 244 _skinName = skinName; 245 _templateName = templateName; 246 } 247 248 /** 249 * Set the RegExp for path 250 * @param rePath the RegExp for path 251 * @param reverse true to reverse mode 252 */ 253 public void setRegExpPath (String rePath, boolean reverse) 254 { 255 _rePath = (rePath.startsWith("^") ? "" : "^") + rePath + (rePath.startsWith("$") ? "" : "$"); 256 _reverse = reverse; 257 } 258 259 /** 260 * Test if page matches condition 261 * @param page the page to test 262 * @return true if page matches condition 263 */ 264 public boolean matchCondition (Page page) 265 { 266 if (_rePath == null) 267 { 268 return true; 269 } 270 271 if (!_reverse) 272 { 273 return Pattern.compile(_rePath).matcher(page.getPathInSitemap()).matches(); 274 } 275 else 276 { 277 return !Pattern.compile(_rePath).matcher(page.getPathInSitemap()).matches(); 278 } 279 } 280 281 /** 282 * Get the skin name 283 * @return the skin name 284 */ 285 public String getSkinName () 286 { 287 return _skinName; 288 } 289 290 /** 291 * Get the template name 292 * @return the template name 293 */ 294 public String getTemplateName () 295 { 296 return _templateName; 297 } 298 } 299 300}