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