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, 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, 0, 0);
111    }
112
113    @Override
114    public String resolveCroppedImage(String uri, int cropHeight, int cropWidth, boolean download, boolean absolute, boolean internal)
115    {
116        if (cropHeight == 0 && cropWidth == 0)
117        {
118            return resolve(uri, download, absolute, internal);
119        }
120        
121        return _resolve(uri, download, absolute, internal, "resources-images", "_crop" + cropHeight + "x" + cropWidth);
122    }
123    
124    @Override
125    public String resolveCroppedImageAsBase64(String uri, int cropHeight, int cropWidth)
126    {
127        return resolveImageAsBase64(uri, 0, 0, 0, 0, cropHeight, cropWidth);
128    }
129    
130    /**
131     * Get the URI prefix
132     * @param object The object
133     * @param download true if the pointed resource is to be downloaded.
134     * @param absolute true if the url must be absolute
135     * @param internal true to get an internal URI.
136     * @return the URI prefix
137     */
138    protected String getUriPrefix (AmetysObject object, boolean download, boolean internal, boolean absolute)
139    {
140        if (internal)
141        {
142            return "cocoon://";
143        }
144        else if (absolute)
145        {
146            return _prefixHandler.getAbsoluteUriPrefix();
147        }
148        else
149        {
150            return _prefixHandler.getUriPrefix();
151        }
152    }
153    
154    /**
155     * Creates a full uri
156     * @param uri the base uri, ie the resource id
157     * @param download true to create a forced download uri
158     * @param absolute true to create an absolute uri (if internal is false)
159     * @param internal true to create an internal uri
160     * @param prefix Prefix on the uri
161     * @param suffix Suffix on the uri
162     * @return The created uri
163     */
164    protected String _resolve(String uri, boolean download, boolean absolute, boolean internal, String prefix, String suffix)
165    {
166        String version = null;
167        String path;
168        Resource resource = null;
169        try
170        {
171            String resourceId = uri;
172            int i = uri.indexOf(";");
173            if (i != -1)
174            {
175                resourceId = uri.substring(0, i);
176                version = uri.substring(i + 1);
177            }
178            
179            resource = (Resource) _resolver.resolveById(resourceId);
180            path = getResourcePath(resource);
181        }
182        catch (UnknownAmetysObjectException e)
183        {
184            getLogger().warn("Link to unexisting resource " + uri);
185            return "";
186        }
187        
188        int i = path.lastIndexOf(".");
189        String extension = i != -1 ? path.substring(i) : null;
190        path = i != -1 ? path.substring(0, i) : path; 
191        
192        StringBuilder result = new StringBuilder();
193        
194        result.append(getUriPrefix(resource, download, absolute, internal));
195        
196        String realPrefix = getRealPrefix(resource, prefix);
197        if (StringUtils.isNotEmpty(realPrefix))
198        {
199            result.append("/_").append(realPrefix);
200        }
201        
202        if (StringUtils.isNotEmpty(version) && resource instanceof VersionableAmetysObject)
203        {
204            result.append("/__version").append(version);
205        }
206        
207        result.append(path);
208        
209        if (suffix != null)
210        {
211            result.append(suffix);
212        }
213        
214        if (extension != null)
215        {
216            result.append(extension);
217        }
218        
219        String encodedPath = URLEncoder.encodePath(result.toString());
220        return URLEncoder.encodeURI(encodedPath, download ? Collections.singletonMap("download", "true") : null);
221    }
222    
223    /**
224     * Get the resource path 
225     * @param resource the resource
226     * @return the path
227     */
228    protected String getResourcePath (Resource resource)
229    {
230        return resource.getResourcePath();
231    }
232    
233    /**
234     * Get the real prefix
235     * @param resource the resource
236     * @param prefix the initial prefix
237     * @return the real prefix
238     */
239    protected String getRealPrefix (Resource resource, String prefix)
240    {
241        return prefix;
242    }
243    
244    /**
245     * Get an image's bytes encoded as base64, optionally resized. 
246     * @param uri the image URI.
247     * @param height the specified height. Ignored if negative.
248     * @param width the specified width. Ignored if negative.
249     * @param maxHeight the maximum image height. Ignored if height or width is specified.
250     * @param maxWidth the maximum image width. Ignored if height or width is specified.
251     * @param cropHeight The cropping height. Ignored if negative.
252     * @param cropWidth The cropping width. Ignored if negative.
253     * @return the image bytes encoded as base64.
254     */
255    protected String resolveImageAsBase64(String uri, int height, int width, int maxHeight, int maxWidth, int cropHeight, int cropWidth)
256    {
257        try
258        {
259            Resource resource = (Resource) _resolver.resolveById(uri);
260            try (InputStream dataIs = resource.getInputStream())
261            {
262                return ImageResolverHelper.resolveImageAsBase64(dataIs, resource.getMimeType(), height, width, maxHeight, maxWidth);
263            }
264        }
265        catch (UnknownAmetysObjectException e)
266        {
267            getLogger().warn("Link to unexisting resource " + uri);
268            return "";
269        }
270        catch (Exception e)
271        {
272            throw new IllegalStateException(e);
273        }
274    }
275
276    @Override
277    public CHECK checkLink(String uri, boolean shortTest)
278    {
279        try
280        {
281            _resolver.resolveById(uri);
282            return CHECK.SUCCESS;
283        }
284        catch (UnknownAmetysObjectException e)
285        {
286            return CHECK.NOT_FOUND;
287        }
288    }
289    
290    @Override
291    public I18nizableText getLabel(String uri)
292    {
293        try
294        {
295            Resource resource = (Resource) _resolver.resolveById(uri);
296            return new I18nizableText("plugin.cms", "PLUGINS_CMS_LINK_RESOURCE_LABEL", Collections.singletonList(resource.getResourcePath()));
297        }
298        catch (UnknownAmetysObjectException e)
299        {
300            return new I18nizableText("plugin.cms", "PLUGINS_CMS_LINK_RESOURCE_UNKNOWN");
301        }
302    }
303}