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; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.net.MalformedURLException; 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Map; 025 026import javax.xml.parsers.SAXParserFactory; 027 028import org.apache.avalon.framework.configuration.Configuration; 029import org.apache.avalon.framework.configuration.ConfigurationException; 030import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; 031import org.apache.avalon.framework.service.ServiceException; 032import org.apache.avalon.framework.service.ServiceManager; 033import org.apache.excalibur.source.Source; 034import org.apache.excalibur.source.SourceResolver; 035import org.xml.sax.XMLReader; 036 037import org.ametys.cms.tag.StaticTagProvider; 038import org.ametys.cms.tag.Tag; 039import org.ametys.cms.tag.Tag.TagVisibility; 040import org.ametys.cms.tag.TagTargetType; 041import org.ametys.runtime.i18n.I18nizableText; 042import org.ametys.web.repository.site.Site; 043import org.ametys.web.repository.site.SiteManager; 044import org.ametys.web.skin.SkinsManager; 045 046/** 047 * This class represents the tags provide by the skin 048 */ 049public class SkinTagProvider extends StaticTagProvider 050{ 051 /** The source resolver */ 052 protected SourceResolver _resolver; 053 054 /** The tags */ 055 protected Map<String, List<String>> _skinLocalIds; 056 /** The tags */ 057 protected Map<String, Map<String, Tag>> _skinTags; 058 059 private SiteManager _siteManager; 060 private SkinsManager _skinsManager; 061 062 @Override 063 public void service(ServiceManager smanager) throws ServiceException 064 { 065 super.service(smanager); 066 _resolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE); 067 _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE); 068 _skinsManager = (SkinsManager) smanager.lookup(SkinsManager.ROLE); 069 } 070 071 @Override 072 public void configure(Configuration configuration) throws ConfigurationException 073 { 074 _skinLocalIds = new HashMap<>(); 075 076 _id = configuration.getAttribute("id"); 077 _label = configureLabel(configuration, "plugin." + _pluginName); 078 _description = configureDescription(configuration, "plugin." + _pluginName); 079 080 if (_skinTags == null) 081 { 082 _skinTags = new HashMap<>(); 083 } 084 085 for (String skinName : _skinsManager.getSkins()) 086 { 087 try 088 { 089 initializeTags(skinName); 090 } 091 catch (Exception e) 092 { 093 throw new ConfigurationException("Unable to load tags configuration values for skin " + skinName, e); 094 } 095 } 096 } 097 098 @Override 099 public Map<String, Tag> getTags(Map<String, Object> contextualParameters) 100 { 101 if (contextualParameters.get("siteName") == null) 102 { 103 // contextualParameters#containsKey not sufficient here because the in some case the siteName key can be set to null 104 return null; 105 } 106 107 Site site = _siteManager.getSite((String) contextualParameters.get("siteName")); 108 109 if (site == null) 110 { 111 String errorMessage = "Unable to load tags configuration values for site " + (String) contextualParameters.get("siteName"); 112 getLogger().error(errorMessage); 113 return null; 114 } 115 116 String skin = site.getSkinId(); 117 118 if (!_skinTags.containsKey(skin)) 119 { 120 try 121 { 122 initializeTags(skin); 123 } 124 catch (Exception e) 125 { 126 String errorMessage = "Unable to load tags configuration values for skin " + skin; 127 getLogger().error(errorMessage, e); 128 } 129 } 130 131 return _skinTags.get(skin); 132 } 133 134 /** 135 * Initialize a skin's tags from the tags file. 136 * @param skinName the name of the skin to initialize tags. 137 * @throws Exception if an error occurs. 138 */ 139 protected void initializeTags(String skinName) throws Exception 140 { 141 Source src = null; 142 try 143 { 144 SAXParserFactory factory = SAXParserFactory.newInstance(); 145 146 XMLReader reader = factory.newSAXParser().getXMLReader(); 147 148 DefaultConfigurationBuilder confBuilder = new DefaultConfigurationBuilder(reader); 149 150 src = _getTagsFile(skinName); 151 if (src.exists()) 152 { 153 try (InputStream is = src.getInputStream()) 154 { 155 Configuration configuration = confBuilder.build(is, src.getURI()); 156 157 if (!_skinLocalIds.containsKey(skinName)) 158 { 159 _skinLocalIds.put(skinName, new ArrayList<String>()); 160 } 161 162 Map<String, Tag> tags = configureTags(configuration, skinName, null, "skin." + skinName); 163 _skinTags.put(skinName, tags); 164 } 165 } 166 } 167 finally 168 { 169 _resolver.release(src); 170 } 171 } 172 173 private Source _getTagsFile(String skin) throws MalformedURLException, IOException 174 { 175 Source src; 176 src = _resolver.resolveURI("context://skins/" + skin + "/conf/tags.xml"); 177 if (!src.exists()) 178 { 179 Source otherSrc = _resolver.resolveURI("context://skins/" + skin + "/tags/tags.xml"); 180 if (otherSrc.exists()) 181 { 182 src = otherSrc; 183 getLogger().warn("In skin '" + skin + "' the tags.xml file location is obsolete. It is tags/tags.xml, it should be conf/tags.xml"); 184 } 185 } 186 return src; 187 } 188 189 /** 190 * Configure tag from the passed configuration 191 * @param configuration The configuration 192 * @param skinName the skin name 193 * @param parent The parent tag if any 194 * @param defaultCatalogue The default catalog for i18n 195 * @return a Set of {@link Tag} 196 * @throws ConfigurationException if configuration is invalid 197 */ 198 protected Map<String, Tag> configureTags (Configuration configuration, String skinName, Tag parent, String defaultCatalogue) throws ConfigurationException 199 { 200 Map<String, Tag> tags = new HashMap<>(); 201 202 Configuration[] tagsConfiguration = configuration.getChildren("tag"); 203 for (Configuration tagConfiguration : tagsConfiguration) 204 { 205 String id = tagConfiguration.getAttribute("id", null); 206 if (id == null) 207 { 208 getLogger().error("Missing attributed named \"id\" for tag configuration at " + tagConfiguration.getLocation() + ". Tag is ignored."); 209 } 210 else if (!Tag.NAME_PATTERN.matcher(id).matches()) 211 { 212 getLogger().error("Invalid tag ID \"" + id + "\" for tag configuration at " + tagConfiguration.getLocation() + ". It must match the pattern " + Tag.NAME_PATTERN.toString() + ". Tag is ignored."); 213 } 214 else if (_skinLocalIds.get(skinName).contains(id)) 215 { 216 getLogger().error("A tag with the ID \"" + id + "\" for tag configuration at " + tagConfiguration.getLocation() + " already exists. Tag is ignored."); 217 } 218 else 219 { 220 _skinLocalIds.get(skinName).add(id); 221 222 String typeName = tagConfiguration.getAttribute("target", null); 223 if (typeName != null) 224 { 225 TagTargetType targetType = _targetTypeEP.getTagTargetType(typeName); 226 227 TagVisibility visibility = TagVisibility.PUBLIC; 228 if (tagConfiguration.getAttribute("private", "").equals("true")) 229 { 230 visibility = TagVisibility.PRIVATE; 231 } 232 I18nizableText label = configureLabel (tagConfiguration, defaultCatalogue); 233 I18nizableText description = configureDescription (tagConfiguration, defaultCatalogue); 234 Tag tag = new Tag(id, id, parent, label, description, visibility, targetType); 235 tags.put(id, tag); 236 237 // Recursive configuration 238 Map<String, Tag> childTags = configureTags(tagConfiguration, skinName, tag, defaultCatalogue); 239 tag.setTags(childTags); 240 } 241 else 242 { 243 getLogger().error("Missing attributed named \"target\" for tag configuration of id \"" + id + "\" at " + tagConfiguration.getLocation() + ". Tag is ignored."); 244 } 245 } 246 247 } 248 249 return tags; 250 } 251}