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.util.HashMap;
020import java.util.HashSet;
021import java.util.Map;
022import java.util.Set;
023
024import org.apache.avalon.framework.CascadingRuntimeException;
025import org.apache.avalon.framework.component.Component;
026import org.apache.avalon.framework.context.Context;
027import org.apache.avalon.framework.context.ContextException;
028import org.apache.avalon.framework.context.Contextualizable;
029import org.apache.avalon.framework.logger.AbstractLogEnabled;
030import org.apache.avalon.framework.service.ServiceException;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.avalon.framework.service.Serviceable;
033import org.apache.avalon.framework.thread.ThreadSafe;
034import org.apache.cocoon.Constants;
035import org.apache.cocoon.components.ContextHelper;
036import org.apache.cocoon.environment.Request;
037import org.apache.commons.io.FileUtils;
038import org.apache.commons.lang.StringUtils;
039import org.apache.excalibur.source.SourceResolver;
040
041import org.ametys.runtime.servlet.RuntimeConfig;
042import org.ametys.web.repository.site.SiteManager;
043
044/**
045 * Manages the templates
046 */
047public class SkinsManager extends AbstractLogEnabled implements ThreadSafe, Serviceable, Component, Contextualizable
048{
049    /** The avalon role name */
050    public static final String ROLE = SkinsManager.class.getName();
051
052    /** The set of templates classified by skins */
053    protected Map<String, Skin> _skins = new HashMap<>();
054    
055    /** The avalon service manager */
056    protected ServiceManager _manager;
057    /** The excalibur source resolver */
058    protected SourceResolver _sourceResolver;
059    /** Avalon context */
060    protected Context _context;
061    /** Cocoon context */
062    protected org.apache.cocoon.environment.Context _cocoonContext;
063    /** The site manager */
064    protected SiteManager _siteManager;
065    
066    @Override
067    public void service(ServiceManager manager) throws ServiceException
068    {
069        _manager = manager;
070        _sourceResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE);
071    }
072    
073    @Override
074    public void contextualize(Context context) throws ContextException
075    {
076        _context = context;
077        _cocoonContext = (org.apache.cocoon.environment.Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);
078    }
079    
080    /**
081     * Get the list of existing skins
082     * @return A set of skin names. Can be null if there is an error.
083     */
084    public Set<String> getSkins()
085    {
086        try
087        {
088            Set<String> skins = new HashSet<>();
089
090            File skinsDir = new File(getSkinsLocation());
091            for (File child : skinsDir.listFiles())
092            {
093                if (_skinExists(child))
094                {
095                    skins.add(child.getName());
096                }
097            }
098            
099            return skins;
100        }
101        catch (Exception e)
102        {
103            getLogger().error("Can not determine the list of skins available", e);
104            return null;
105        }
106    }
107    
108    /**
109     * Get a skin
110     * @param id The id of the skin
111     * @return The skin or null if the skin does not exist
112     */
113    @SuppressWarnings("null")
114    public Skin getSkin(String id)
115    {
116        try
117        {
118            File skinsDir = new File (_getSkinLocation(id));
119            
120            boolean skinDirExists = _skinExists(skinsDir);
121            Skin skin = _skins.get(skinsDir.getAbsolutePath());
122            if (skin == null && skinDirExists)
123            {
124                skin = new Skin(id, new File (_getSkinLocation(id)));
125                _skins.put(skinsDir.getAbsolutePath(), skin);
126            }
127            else if (!skinDirExists)
128            {
129                _skins.put(skinsDir.getAbsolutePath(), null);
130                return null;
131            }
132            
133            skin.refreshValues();
134            return skin;
135        }
136        catch (Exception e)
137        {
138            throw new IllegalStateException("Can not create the skin DAO for skin '" + id + "'", e);
139        }
140    }
141    
142    /**
143     * Get the skins location
144     * @return the skin location
145     */
146    public String getSkinsLocation ()
147    {
148        try
149        {
150            Request request = ContextHelper.getRequest(_context);
151            String skinLocation = (String) request.getAttribute("skin-location");
152            if (skinLocation != null)
153            {
154                return new File(RuntimeConfig.getInstance().getAmetysHome(), skinLocation).getPath();
155            }
156        }
157        catch (CascadingRuntimeException e)
158        {
159            // Ignore
160        }
161        
162        return _cocoonContext.getRealPath("/skins");
163    }
164    
165    /**
166     * Get the skin name from request or <code>null</code> if not found
167     * @param request The request
168     * @return The skin name or <code>null</code>
169     */
170    public String getSkinNameFromRequest (Request request)
171    {
172        if (_siteManager == null)
173        {
174            try
175            {
176                _siteManager = (SiteManager) _manager.lookup(SiteManager.ROLE);
177            }
178            catch (ServiceException e)
179            {
180                throw new IllegalStateException(e);
181            }
182        }
183        
184        // First, search the skin name in the request attributes.
185        String skinName = (String) request.getAttribute("skin");
186        
187        // Then, test if the site name is present as a request attribute to deduce the skin name.
188        if (StringUtils.isEmpty(skinName))
189        {
190            String siteName = (String) request.getAttribute("site");
191            if (StringUtils.isEmpty(siteName))
192            {
193                siteName = (String) request.getAttribute("siteName");
194            }
195            
196            if (StringUtils.isNotEmpty(siteName))
197            {
198                skinName = _siteManager.getSite(siteName).getSkinId();
199            }
200        }
201        
202        return skinName;
203    }
204    
205    /**
206     * Get the skin location
207     * @param id The id of the skin
208     * @return the skin location
209     */
210    private String _getSkinLocation (String id)
211    {
212        Request request = ContextHelper.getRequest(_context);
213        String skinLocation = (String) request.getAttribute("skin-location");
214        if (skinLocation != null)
215        {
216            return FileUtils.getFile(RuntimeConfig.getInstance().getAmetysHome(), skinLocation, id).getPath();
217        }
218        
219        return _cocoonContext.getRealPath("/skins/" + id);
220    }
221
222    private boolean _skinExists(File skinsDir)
223    {
224        if (!skinsDir.exists() || !skinsDir.isDirectory())
225        {
226            return false;
227        }
228        
229        File templatesDir = new File(skinsDir, "templates");
230        if (!templatesDir.exists() || !templatesDir.isDirectory())
231        {
232            return false;
233        }
234        
235        return true;
236    }
237}