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