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.skin;
017
018import java.io.File;
019import java.io.FileInputStream;
020import java.io.InputStream;
021import java.util.Date;
022import java.util.LinkedHashMap;
023import java.util.Map;
024
025import org.apache.avalon.framework.configuration.Configuration;
026import org.apache.avalon.framework.configuration.ConfigurationException;
027import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031import org.ametys.runtime.i18n.I18nizableText;
032
033/**
034 * Represent a skin template. 
035 */
036public class SkinTemplate
037{
038    private static Logger _logger = LoggerFactory.getLogger(SkinTemplate.class);
039    
040    /** The skin id */
041    protected String _skinId;
042    /** Template id (e.g. the directory name) */
043    protected String _id;
044    /** The template directory */
045    protected File _file;
046    /** Template label */
047    protected I18nizableText _label;
048    /** Template description */
049    protected I18nizableText _description;
050    /** Template thumbnail 16px */
051    protected String _smallImage;
052    /** Template thumbnail 32px */
053    protected String _mediumImage;
054    /** Template thumbnail 48px */
055    protected String _largeImage;
056    /** Template zones. the key is the zone id */
057    protected Map<String, SkinTemplateZone> _zones;
058        
059    /** The last time the file was loaded */
060    protected long _lastConfUpdate; 
061
062    /**
063     * Creates a template
064     * @param skinId The skin id
065     * @param templateId The template id
066     * @param file The template file
067     */
068    public SkinTemplate(String skinId, String templateId, File file)
069    {
070        this._skinId = skinId;
071        this._id = templateId;
072        this._file = file;
073    }
074    
075    /**
076     * The configuration default values (if configuration file does not exist or is unreadable)
077     */
078    protected void _defaultValues()
079    {
080        _lastConfUpdate = new Date().getTime();
081        
082        this._label = new I18nizableText(this._id);
083        this._description = new I18nizableText("");
084        this._smallImage = "/plugins/web/resources/img/skin/template_16.png";
085        this._mediumImage = "/plugins/web/resources/img/skin/template_32.png";
086        this._largeImage = "/plugins/web/resources/img/skin/template_48.png";
087        this._zones = new LinkedHashMap<>();
088    }
089    
090    /**
091     * Refresh the conf values
092     */
093    public void refreshValues()
094    {
095        File configurationFile = null;
096        try
097        {
098            configurationFile = new File (_file, "/template.xml");
099            if (configurationFile.exists())
100            {
101                if (_lastConfUpdate < configurationFile.lastModified())
102                {
103                    _defaultValues();
104
105                    _lastConfUpdate = configurationFile.lastModified();
106                    try (InputStream is = new FileInputStream(configurationFile))
107                    {
108                        Configuration configuration = new DefaultConfigurationBuilder().build(is);
109                        
110                        this._label = _configureI18n(configuration.getChild("label", false), this._label);
111                        this._description = _configureI18n(configuration.getChild("description", false), this._description);
112                        this._smallImage = _configureThumbnail(configuration.getChild("thumbnail").getChild("small").getValue(null), this._smallImage);
113                        this._mediumImage = _configureThumbnail(configuration.getChild("thumbnail").getChild("medium").getValue(null), this._mediumImage);
114                        this._largeImage = _configureThumbnail(configuration.getChild("thumbnail").getChild("marge").getValue(null), this._largeImage);
115                        
116                        this._zones = new LinkedHashMap<>();
117                        for (Configuration zoneConfiguration : configuration.getChild("zones").getChildren("zone"))
118                        {
119                            String zoneId = zoneConfiguration.getAttribute("id");
120                            String zoneType = zoneConfiguration.getAttribute("type", SkinTemplateZone.TYPE_PRIMARY);
121                            I18nizableText label = _configureI18n(zoneConfiguration.getChild("label", false), new I18nizableText(zoneId));
122                            I18nizableText description = _configureI18n(zoneConfiguration.getChild("description", false), new I18nizableText(zoneId));
123                            String smallImage = _configureThumbnail(zoneConfiguration.getChild("thumbnail").getChild("small").getValue(null), "/plugins/web/resources/img/skin/zone_16.png");
124                            String mediumImage = _configureThumbnail(zoneConfiguration.getChild("thumbnail").getChild("medium").getValue(null), "/plugins/web/resources/img/skin/zone_32.png");
125                            String largeImage = _configureThumbnail(zoneConfiguration.getChild("thumbnail").getChild("marge").getValue(null), "/plugins/web/resources/img/skin/zone_48.png");
126                            
127                            String inheritanceString = zoneConfiguration.getAttribute("inherit", null);
128                            
129                            SkinTemplateZone zone = new SkinTemplateZone(this._skinId, this._id, zoneId, zoneType, label, description, smallImage, mediumImage, largeImage, inheritanceString);
130                            _zones.put(zoneId, zone);
131                        }
132                    }
133                }
134            }
135            else
136            {
137                _defaultValues();
138            }
139        }
140        catch (Exception e)
141        {
142            _defaultValues();
143            if (_logger.isWarnEnabled())
144            {
145                _logger.warn("Cannot read the configuration file templates/" + this._id + "/template.xml for the skin '" + this._id + "'. Continue as if file was not existing", e);
146            }
147        }
148    }
149    
150    private String _configureThumbnail(String value, String defaultImage)
151    {
152        if (value == null)
153        {
154            return defaultImage;
155        }
156        else
157        {
158            return "/skins/" + this._skinId + "/templates/" + this._id + "/resources/" + value;
159        }
160    }
161
162    private I18nizableText _configureI18n(Configuration child, I18nizableText defaultValue) throws ConfigurationException
163    {
164        if (child != null)
165        {
166            String value = child.getValue();
167            if (child.getAttributeAsBoolean("i18n", false))
168            {
169                return new I18nizableText("skin." + this._skinId, value);
170            }
171            else
172            {
173                return new I18nizableText(value);
174            }
175        }
176        else
177        {
178            return defaultValue;
179        }
180    }
181
182    /**
183     * The template id
184     * @return the id
185     */
186    public String getId()
187    {
188        return _id;
189    }
190    
191    /**
192     * The template label
193     * @return The label
194     */
195    public I18nizableText getLabel()
196    {
197        return _label;
198    }
199    /**
200     * The template description
201     * @return The description. Can not be null but can be empty
202     */
203    public I18nizableText getDescription()
204    {
205        return _description;
206    }
207    
208    /**
209     * The small image file uri
210     * @return The small image file uri
211     */
212    public String getSmallImage()
213    {
214        return _smallImage;
215    }
216
217    /**
218     * The medium image file uri
219     * @return The medium image file uri
220     */
221    public String getMediumImage()
222    {
223        return _mediumImage;
224    }
225    
226    /**
227     * The large image file uri
228     * @return The large image file uri
229     */
230    public String getLargeImage()
231    {
232        return _largeImage;
233    }
234    
235    /**
236     * The zones defined in by the template def
237     * @return The zones
238     */
239    public Map<String, SkinTemplateZone> getZones()
240    {
241        return _zones;
242    }
243    
244    /**
245     * The zone identifier by its id
246     * @param zoneId The id of the zone definition to get
247     * @return The zone or null if no zone has this name
248     */
249    public SkinTemplateZone getZone(String zoneId)
250    {
251        return _zones.get(zoneId);
252    }
253    
254    /**
255     * Get the template's file directory
256     * @return the template's file directory
257     */
258    public File getFile ()
259    {
260        return _file;
261    }
262    
263    /**
264     * Get the primary default zone.
265     * That is the first primary zone, or the 'default' zone if it exists and is primary
266     * @return The default zone or null if there is no primary zone
267     */
268    public String getDefaultZoneId()
269    {
270        String defaultZoneId = null;
271        
272        Map<String, SkinTemplateZone> zones = this.getZones();
273        for (SkinTemplateZone zone : zones.values())
274        {
275            if (SkinTemplateZone.TYPE_PRIMARY.equals(zone.getType())
276                && ("default".equals(zone.getId()) || defaultZoneId == null))
277            {
278                defaultZoneId = zone.getId();
279            }
280        }
281        
282        return defaultZoneId;
283    }
284}
285