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