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 */
016
017package org.ametys.plugins.linkdirectory;
018
019import java.io.InputStream;
020import java.util.HashMap;
021import java.util.LinkedHashMap;
022import java.util.Map;
023
024import org.apache.avalon.framework.component.Component;
025import org.apache.avalon.framework.configuration.Configurable;
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.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.avalon.framework.service.Serviceable;
032import org.apache.commons.lang3.StringUtils;
033import org.apache.excalibur.source.Source;
034
035import org.ametys.core.ui.Callable;
036import org.ametys.core.util.filereloader.FileReloader;
037import org.ametys.core.util.filereloader.FileReloaderUtils;
038import org.ametys.runtime.plugin.component.AbstractLogEnabled;
039import org.ametys.web.repository.site.Site;
040import org.ametys.web.repository.site.SiteManager;
041
042/**
043 * Component listing the available colors for a link
044 */
045public class LinkDirectoryColorsComponent extends AbstractLogEnabled implements Component, Serviceable, Configurable
046{
047    /** The component role */
048    public static final String ROLE = LinkDirectoryColorsComponent.class.getName();
049    
050    /** The site manager */
051    protected SiteManager _siteManager;
052    
053    /** The file reloader utils */
054    protected FileReloaderUtils _fileReloaderUtils;
055    
056    /** The colors configuration */
057    protected ColorsConfiguration _colorsConfiguration;
058    
059    /** The cache of colors for each skins */
060    protected Map<String, ColorsConfiguration> _colorsSkinCache;
061    
062    
063    public void service(ServiceManager manager) throws ServiceException
064    {
065        _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE);
066        _fileReloaderUtils = (FileReloaderUtils) manager.lookup(FileReloaderUtils.ROLE);
067    }
068    
069    @Override
070    public void configure(Configuration configuration) throws ConfigurationException
071    {
072        _colorsSkinCache = new HashMap<>();
073        if ("colors".equals(configuration.getName()))
074        {
075            _colorsConfiguration = _parseColors(configuration);
076        }
077        else
078        {
079            _colorsConfiguration = _parseColors(configuration.getChild("colors"));
080        }
081    }
082    
083    /**
084     * Get colors from site name
085     * @param siteName the site name
086     * @return the map of colors 
087     */
088    @Callable
089    public Map<String, Map<String, String>> getColors(String siteName)
090    {
091        // First get colors from skins if linkdirectory-colors.xml file exist in the skin configuration
092        ColorsConfiguration colorsConfFromSkin = _getColorsFromSkin(siteName);
093        if (colorsConfFromSkin != null)
094        {
095            return colorsConfFromSkin.getColorsMap();
096        }
097        
098        // Then get colors from WEB-INF/param/linkdirectory-colors.xml or in the plugin itself 
099        return _colorsConfiguration.getColorsMap();
100    }
101    
102    /**
103     * Get the default color key
104     * @param siteName the site name
105     * @return the default key
106     */
107    public String getDefaultKey(String siteName)
108    {
109     // First get colors from skins if linkdirectory-colors.xml file exist in the skin configuration
110        ColorsConfiguration colorsConfFromSkin = _getColorsFromSkin(siteName);
111        if (colorsConfFromSkin != null)
112        {
113            return colorsConfFromSkin.getDefaultKey();
114        }
115        
116        // Then get colors from WEB-INF/param/linkdirectory-colors.xml or in the plugin itself 
117        return _colorsConfiguration.getDefaultKey();
118    }
119    
120    /**
121     * Parse colors from configuration
122     * @param configuration the configuration
123     * @return the colors configuration
124     * @throws ConfigurationException if a configuration error occurred
125     */
126    protected ColorsConfiguration _parseColors(Configuration configuration) throws ConfigurationException
127    {
128        Map<String, Map<String, String>> colors = new LinkedHashMap<>();
129        String defaultKey = configuration.getAttribute("default", "");
130        for (Configuration colorConfiguration : configuration.getChildren("color"))
131        {
132            Map<String, String> color = new HashMap<>();
133            
134            for (Configuration child : colorConfiguration.getChildren())
135            {
136                String name = child.getName();
137                String value = child.getValue();
138                
139                color.put(name, value);
140            }
141
142            String id = colorConfiguration.getAttribute("id");
143            colors.put(id, color);
144            
145            if (StringUtils.isEmpty(defaultKey))
146            {
147                defaultKey = id;
148            }
149        }
150        
151        return new ColorsConfiguration(defaultKey, colors);
152    }
153    
154    /**
155     * Get colors configuration from skin
156     * @param siteName the site name
157     * @return the colors configuration
158     */
159    protected ColorsConfiguration _getColorsFromSkin(String siteName)
160    {
161        try
162        {
163            Site site = _siteManager.getSite(siteName);
164            String skinId = site.getSkinId();
165            
166            String file = "skin:" + skinId + "://conf/linkdirectory-colors.xml";
167            _fileReloaderUtils.updateFile(file, new LinkDirectoryReloader(skinId, this));
168            
169            return _colorsSkinCache.containsKey(skinId) ? _colorsSkinCache.get(skinId) : null;
170        }
171        catch (Exception e)
172        {
173            getLogger().error("An error occurred reading the configuration file", e);
174            return null;
175        }
176    }
177    
178    /**
179     * Class representing a link directory reloader
180     */
181    public static class LinkDirectoryReloader implements FileReloader
182    {
183        private String _skin;
184        private LinkDirectoryColorsComponent _component;
185     
186        /**
187         * Constructor
188         * @param skin the skin
189         * @param component the link directory colors component
190         */
191        public LinkDirectoryReloader (String skin, LinkDirectoryColorsComponent component)
192        {
193            _skin = skin;
194            _component = component;
195        }
196        
197        /**
198         * Get the skin
199         * @return the skin
200         */
201        public String getSkin()
202        {
203            return _skin;
204        }
205        
206        /**
207         * Get the link directory colors component
208         * @return the link directory colors component
209         */
210        public LinkDirectoryColorsComponent getComponent()
211        {
212            return _component;
213        }
214        
215        
216        public void updateFile(String sourceUrl, Source source, InputStream is) throws Exception
217        {
218            if (is != null)
219            {
220                LinkDirectoryColorsComponent component = getComponent();
221                // We pass in this method only if the file has been modified since the last time we parse colors
222                Configuration configuration = new DefaultConfigurationBuilder().build(is);
223                component._colorsSkinCache.put(getSkin(), component._parseColors (configuration));
224            }
225        }
226        
227        public String getId(String sourceUrl)
228        {
229            return LinkDirectoryReloader.class.getName() + "#" + getSkin();
230        }
231    }
232    
233    /**
234     * Class representing a colors configuration
235     */
236    public static class ColorsConfiguration 
237    {
238        private String _defaultKey;
239        private Map<String, Map<String, String>> _colors;
240     
241        /**
242         * Constructor
243         * @param defaultKey the default color key
244         * @param colors the map of colors
245         */
246        public ColorsConfiguration (String defaultKey, Map<String, Map<String, String>> colors)
247        {
248            _defaultKey = defaultKey;
249            _colors = colors;
250        }
251        
252        /**
253         * Get the default key
254         * @return the default key
255         */
256        public String getDefaultKey()
257        {
258            return _defaultKey;
259        }
260        
261        /**
262         * Get colors map
263         * @return the colors map
264         */
265        public Map<String, Map<String, String>> getColorsMap()
266        {
267            return _colors;
268        }
269    }
270}