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.cms.transformation;
017
018import java.io.InputStream;
019import java.util.Collections;
020
021import org.apache.avalon.framework.context.Context;
022import org.apache.avalon.framework.context.ContextException;
023import org.apache.avalon.framework.context.Contextualizable;
024import org.apache.avalon.framework.logger.AbstractLogEnabled;
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.avalon.framework.service.Serviceable;
028import org.apache.commons.lang.StringUtils;
029
030import org.ametys.cms.URIPrefixHandler;
031import org.ametys.cms.transformation.ConsistencyChecker.CHECK;
032import org.ametys.core.util.URLEncoder;
033import org.ametys.plugins.explorer.resources.Resource;
034import org.ametys.plugins.repository.AmetysObject;
035import org.ametys.plugins.repository.AmetysObjectResolver;
036import org.ametys.plugins.repository.UnknownAmetysObjectException;
037import org.ametys.plugins.repository.version.VersionableAmetysObject;
038import org.ametys.runtime.i18n.I18nizableText;
039
040/**
041 * {@link URIResolver} for type "explorer". <br>
042 * These links point to a file from the resources explorer.
043 */
044public class ResourceURIResolver extends AbstractLogEnabled implements URIResolver, Serviceable, Contextualizable
045{
046    /** The ametys object resolver. */
047    protected AmetysObjectResolver _resolver;
048    /** The avalon context. */
049    protected Context _context;
050    /** The URI prefix handler */
051    protected URIPrefixHandler _prefixHandler;
052    
053    @Override
054    public void contextualize(Context context) throws ContextException
055    {
056        _context = context;
057    }
058
059    @Override
060    public void service(ServiceManager manager) throws ServiceException
061    {
062        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
063        _prefixHandler = (URIPrefixHandler) manager.lookup(URIPrefixHandler.ROLE);
064    }
065    
066    @Override
067    public String getType()
068    {
069        return "explorer";
070    }
071
072    @Override
073    public String resolve(String uri, boolean download, boolean absolute, boolean internal)
074    {
075        return _resolve(uri, download, absolute, internal, "resources", null);
076    }
077    
078    @Override
079    public String resolveImage(String uri, int height, int width, boolean download, boolean absolute, boolean internal)
080    {
081        if (height == 0 && width == 0)
082        {
083            return resolve(uri, download, absolute, internal);
084        }
085        
086        
087        return _resolve(uri, download, absolute, internal, "resources-images", "_" + height + "x" + width);
088    }
089    
090    @Override
091    public String resolveImageAsBase64(String uri, int height, int width)
092    {
093        return resolveImageAsBase64(uri, height, width, 0, 0);
094    }
095    
096    @Override
097    public String resolveBoundedImage(String uri, int maxHeight, int maxWidth, boolean download, boolean absolute, boolean internal)
098    {
099        if (maxHeight == 0 && maxWidth == 0)
100        {
101            return resolve(uri, download, absolute, internal);
102        }
103        
104        return _resolve(uri, download, absolute, internal, "resources-images", "_max" + maxHeight + "x" + maxWidth);
105    }
106    
107    @Override
108    public String resolveBoundedImageAsBase64(String uri, int maxHeight, int maxWidth)
109    {
110        return resolveImageAsBase64(uri, 0, 0, maxHeight, maxWidth);
111    }
112    
113    /**
114     * Get the URI prefix
115     * @param object The object
116     * @param download true if the pointed resource is to be downloaded.
117     * @param absolute true if the url must be absolute
118     * @param internal true to get an internal URI.
119     * @return the URI prefix
120     */
121    protected String getUriPrefix (AmetysObject object, boolean download, boolean internal, boolean absolute)
122    {
123        if (internal)
124        {
125            return "cocoon://";
126        }
127        else if (absolute)
128        {
129            return _prefixHandler.getAbsoluteUriPrefix();
130        }
131        else
132        {
133            return _prefixHandler.getUriPrefix();
134        }
135    }
136    
137    /**
138     * Creates a full uri
139     * @param uri the base uri, ie the resource id
140     * @param download true to create a forced download uri
141     * @param absolute true to create an absolute uri (if internal is false)
142     * @param internal true to create an internal uri
143     * @param prefix Prefix on the uri
144     * @param suffix Suffix on the uri
145     * @return The created uri
146     */
147    protected String _resolve(String uri, boolean download, boolean absolute, boolean internal, String prefix, String suffix)
148    {
149        String version = null;
150        String path;
151        Resource resource = null;
152        try
153        {
154            String resourceId = uri;
155            int i = uri.indexOf(";");
156            if (i != -1)
157            {
158                resourceId = uri.substring(0, i);
159                version = uri.substring(i + 1);
160            }
161            
162            resource = (Resource) _resolver.resolveById(resourceId);
163            path = getResourcePath(resource);
164        }
165        catch (UnknownAmetysObjectException e)
166        {
167            getLogger().warn("Link to unexisting resource " + uri);
168            return "";
169        }
170        
171        int i = path.lastIndexOf(".");
172        String extension = i != -1 ? path.substring(i) : null;
173        path = i != -1 ? path.substring(0, i) : path; 
174        
175        StringBuilder result = new StringBuilder();
176        
177        result.append(getUriPrefix(resource, download, absolute, internal));
178        
179        String realPrefix = getRealPrefix(resource, prefix);
180        if (StringUtils.isNotEmpty(realPrefix))
181        {
182            result.append("/_").append(realPrefix);
183        }
184        
185        if (StringUtils.isNotEmpty(version) && resource instanceof VersionableAmetysObject)
186        {
187            result.append("/__version").append(version);
188        }
189        
190        result.append(path);
191        
192        if (suffix != null)
193        {
194            result.append(suffix);
195        }
196        
197        if (extension != null)
198        {
199            result.append(extension);
200        }
201        
202        String encodedPath = URLEncoder.encodePath(result.toString());
203        return URLEncoder.encodeURI(encodedPath, download ? Collections.singletonMap("download", "true") : null);
204    }
205    
206    /**
207     * Get the resource path 
208     * @param resource the resource
209     * @return the path
210     */
211    protected String getResourcePath (Resource resource)
212    {
213        return resource.getResourcePath();
214    }
215    
216    /**
217     * Get the real prefix
218     * @param resource the resource
219     * @param prefix the initial prefix
220     * @return the real prefix
221     */
222    protected String getRealPrefix (Resource resource, String prefix)
223    {
224        return prefix;
225    }
226    
227    /**
228     * Get an image's bytes encoded as base64, optionally resized. 
229     * @param uri the image URI.
230     * @param height the specified height. Ignored if negative.
231     * @param width the specified width. Ignored if negative.
232     * @param maxHeight the maximum image height. Ignored if height or width is specified.
233     * @param maxWidth the maximum image width. Ignored if height or width is specified.
234     * @return the image bytes encoded as base64.
235     */
236    protected String resolveImageAsBase64(String uri, int height, int width, int maxHeight, int maxWidth)
237    {
238        try
239        {
240            Resource resource = (Resource) _resolver.resolveById(uri);
241            try (InputStream dataIs = resource.getInputStream())
242            {
243                return ImageResolverHelper.resolveImageAsBase64(dataIs, resource.getMimeType(), height, width, maxHeight, maxWidth);
244            }
245        }
246        catch (UnknownAmetysObjectException e)
247        {
248            getLogger().warn("Link to unexisting resource " + uri);
249            return "";
250        }
251        catch (Exception e)
252        {
253            throw new IllegalStateException(e);
254        }
255    }
256
257    @Override
258    public CHECK checkLink(String uri, boolean shortTest)
259    {
260        try
261        {
262            _resolver.resolveById(uri);
263            return CHECK.SUCCESS;
264        }
265        catch (UnknownAmetysObjectException e)
266        {
267            return CHECK.NOT_FOUND;
268        }
269    }
270    
271    @Override
272    public I18nizableText getLabel(String uri)
273    {
274        try
275        {
276            Resource resource = (Resource) _resolver.resolveById(uri);
277            return new I18nizableText("plugin.cms", "PLUGINS_CMS_LINK_RESOURCE_LABEL", Collections.singletonList(resource.getResourcePath()));
278        }
279        catch (UnknownAmetysObjectException e)
280        {
281            return new I18nizableText("plugin.cms", "PLUGINS_CMS_LINK_RESOURCE_UNKNOWN");
282        }
283    }
284}