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.xslt;
017
018import java.util.Optional;
019
020import org.apache.avalon.framework.activity.Initializable;
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.cocoon.components.ContextHelper;
029import org.apache.cocoon.environment.Request;
030import org.apache.commons.lang3.StringUtils;
031
032import org.ametys.cms.transformation.URIResolver;
033import org.ametys.cms.transformation.URIResolverExtensionPoint;
034
035/**
036 * This component resolve links and give a static hack access for xslt calls
037 */
038public class ResolveURIComponent extends AbstractLogEnabled implements Serviceable, Initializable, Contextualizable
039{
040    private static ResolveURIComponent _instance;
041    
042    private static Context _context;
043    
044    private URIResolverExtensionPoint _linkResolverExtensionPoint;
045    
046    @Override
047    public void contextualize(Context context) throws ContextException
048    {
049        _context = context;
050    }
051    
052    @Override
053    public void service(ServiceManager manager) throws ServiceException
054    {
055        _linkResolverExtensionPoint = (URIResolverExtensionPoint) manager.lookup(URIResolverExtensionPoint.ROLE);
056    }
057    
058    @Override
059    public void initialize() throws Exception
060    {
061        _instance = this;
062    }
063    
064    /**
065     * Resolve an uri upon the LinkResolverExtensionPoint
066     * @param type Type name (defined by the extension to use)
067     * @param uri URI depending on the type
068     * @return The uri resolved, or the uri if there is no resolver adapted
069     */
070    public static String resolve(String type, String uri)
071    {
072        return resolve(type, uri, false);
073    }
074
075    /**
076     * Resolve an uri upon the LinkResolverExtensionPoint
077     * @param type Type name (defined by the extension to use)
078     * @param uri URI depending on the type
079     * @param download Is this uri for download purposes.
080     * @return The uri resolved, or the uri if there is no resolver adapted
081     */
082    public static String resolve(String type, String uri, boolean download)
083    {
084        // FIXME CMS-2611 Force absolute 
085        Request request = ContextHelper.getRequest(_context);
086        boolean absolute = request.getAttribute("forceAbsoluteUrl") != null ? (Boolean) request.getAttribute("forceAbsoluteUrl") : false;
087        
088        return resolve(type, uri, download, absolute);
089    }
090    
091    /**
092     * Resolve an uri upon the LinkResolverExtensionPoint
093     * @param type Type name (defined by the extension to use)
094     * @param uri URI depending on the type
095     * @param download Is this uri for download purposes.
096     * @param absolute true to generate absolute url
097     * @return The uri resolved, the empty string if the uri could not be resolved, or the uri itself if there is no resolver adapted
098     */
099    public static String resolve(String type, String uri, boolean download, boolean absolute)
100    {
101        return resolve(type, uri, download, absolute, false);
102    }
103    
104    /**
105     * Resolve an uri upon the LinkResolverExtensionPoint
106     * @param type Type name (defined by the extension to use)
107     * @param uri URI depending on the type
108     * @param download Is this uri for download purposes.
109     * @param absolute true to generate absolute url
110     * @param internal true to get an internal URI.
111     * @return The uri resolved, the empty string if the uri could not be resolved, or the uri itself if there is no resolver adapted
112     */
113    public static String resolve(String type, String uri, boolean download, boolean absolute, boolean internal)
114    {
115        Request request = ContextHelper.getRequest(_context);
116        Object forceRemoteUrl = request.getAttribute("forceRemoteUrl");
117        
118        boolean remote = forceRemoteUrl != null
119                && (forceRemoteUrl instanceof Boolean && (Boolean) forceRemoteUrl 
120                        || Boolean.valueOf((String) forceRemoteUrl));
121        
122        String proxiedUri = remote ? type + ";" + uri : uri;
123        URIResolver uriResolver = _instance._linkResolverExtensionPoint.getResolverForType(remote ? "remote" : type);
124        if (uriResolver != null)
125        {
126            try
127            {
128                request.removeAttribute("forceRemoteUrl");
129                return uriResolver.resolve(proxiedUri, download, absolute, internal);
130            }
131            catch (Exception e)
132            {
133                _instance.getLogger().warn("Error resolving the uri '" + uri + "' with type " + type, e);
134                return "";
135            }
136            finally
137            {
138                request.setAttribute("forceRemoteUrl", forceRemoteUrl);
139            }
140        }
141        else
142        {
143            return uri;
144        }
145    }
146    
147    /**
148     * Resolve an uri upon the LinkResolverExtensionPoint
149     * @param type Type name (defined by the extension to use)
150     * @param uri URI depending on the type
151     * @param height the height
152     * @param width the width
153     * @return The uri resolved, or the uri if there is no resolver adapted
154     */
155    public static String resolveImage(String type, String uri, int height, int width)
156    {
157        return resolveImage (type, uri, height, width, false);
158    }
159    
160    /**
161     * Resolve an uri upon the LinkResolverExtensionPoint
162     * @param type Type name (defined by the extension to use)
163     * @param uri URI depending on the type
164     * @param height the height
165     * @param width the width
166     * @param download Is this uri for download purposes.
167     * @return The uri resolved, or the uri if there is no resolver adapted
168     */
169    public static String resolveImage(String type, String uri, int height, int width, boolean download)
170    {
171        // FIXME CMS-2611 Force absolute 
172        Request request = ContextHelper.getRequest(_context);
173        boolean absolute = request.getAttribute("forceAbsoluteUrl") != null ? (Boolean) request.getAttribute("forceAbsoluteUrl") : false;
174        
175        return resolveImage(type, uri, height, width, download, absolute);
176    }
177    
178    /**
179     * Resolve an uri upon the LinkResolverExtensionPoint
180     * @param type Type name (defined by the extension to use)
181     * @param uri URI depending on the type
182     * @param height the height
183     * @param width the width
184     * @param download Is this uri for download purposes.
185     * @param absolute true to generate absolute url
186     * @return The uri resolved, the empty string if the uri could not be resolved, or the uri itself if there is no resolver adapted
187     */
188    public static String resolveImage(String type, String uri, int height, int width, boolean download, boolean absolute)
189    {
190        return resolveImage(type, uri, height, width, download, absolute, false);
191    }
192    
193    /**
194     * Resolve an uri upon the LinkResolverExtensionPoint
195     * @param type Type name (defined by the extension to use)
196     * @param uri URI depending on the type
197     * @param height the height
198     * @param width the width
199     * @param download Is this uri for download purposes.
200     * @param absolute true to generate absolute url
201     * @param internal true to get an internal URI.
202     * @return The uri resolved, the empty string if the uri could not be resolved, or the uri itself if there is no resolver adapted
203     */
204    public static String resolveImage(String type, String uri, int height, int width, boolean download, boolean absolute, boolean internal)
205    {
206        // FIXME CMS-4059 Force base64 encoding. 
207        Request request = ContextHelper.getRequest(_context);
208        boolean encodeBase64 = request.getAttribute("forceBase64Encoding") != null ? (Boolean) request.getAttribute("forceBase64Encoding") : false;
209        
210        Object forceRemoteUrl = request.getAttribute("forceRemoteUrl");
211        boolean remote = forceRemoteUrl != null
212                        && (forceRemoteUrl instanceof Boolean && (Boolean) forceRemoteUrl
213                                || Boolean.valueOf((String) forceRemoteUrl));
214        
215        String proxiedUri = remote ? type + ";" + uri : uri;
216        URIResolver uriResolver = _instance._linkResolverExtensionPoint.getResolverForType(remote ? "remote" : type);
217        if (uriResolver != null)
218        {
219            try
220            {
221                request.removeAttribute("forceRemoteUrl");
222                if (encodeBase64)
223                {
224                    return uriResolver.resolveImageAsBase64(proxiedUri, height, width);
225                }
226                else
227                {
228                    return uriResolver.resolveImage(proxiedUri, height, width, download, absolute, internal);
229                }
230            }
231            catch (Exception e)
232            {
233                _instance.getLogger().warn("Error resolving the image of uri '" + uri + "' with type " + type, e);
234                return "";
235            }
236            finally
237            {
238                request.setAttribute("forceRemoteUrl", forceRemoteUrl);
239            }
240        }
241        else
242        {
243            return uri;
244        }
245    }
246    
247    /**
248     * Resolve an uri upon the LinkResolverExtensionPoint return it as a base64-encoded string.
249     * @param type Type name (defined by the extension to use)
250     * @param uri URI depending on the type
251     * @param height the height
252     * @param width the width
253     * @return a base64-encoded string representing the image or empty string if the uri could not be resolved, or the uri itself if there is no resolver adapted
254     */
255    public static String resolveImageAsBase64(String type, String uri, int height, int width)
256    {
257        URIResolver uriResolver = _instance._linkResolverExtensionPoint.getResolverForType(type);
258        if (uriResolver != null)
259        {
260            try
261            {
262                return uriResolver.resolveImageAsBase64(uri, height, width);
263            }
264            catch (Exception e)
265            {
266                _instance.getLogger().warn("Error resolving the image as base64 of uri '" + uri + "' with type " + type, e);
267                return "";
268            }
269        }
270        else
271        {
272            return uri;
273        }
274    }
275    
276    /**
277     * Resolve an uri upon the LinkResolverExtensionPoint
278     * @param type Type name (defined by the extension to use)
279     * @param uri URI depending on the type
280     * @param maxHeight the max height
281     * @param maxWidth the max width
282     * @return The uri resolved, or the uri if there is no resolver adapted
283     */
284    public static String resolveBoundedImage(String type, String uri, int maxHeight, int maxWidth)
285    {
286        return resolveBoundedImage (type, uri, maxHeight, maxWidth, false);
287    }
288    
289    /**
290     * Resolve an uri upon the LinkResolverExtensionPoint
291     * @param type Type name (defined by the extension to use)
292     * @param uri URI depending on the type
293     * @param maxHeight the max height
294     * @param maxWidth the max width
295     * @param download Is this uri for download purposes.
296     * @return The uri resolved, or the uri if there is no resolver adapted
297     */
298    public static String resolveBoundedImage(String type, String uri, int maxHeight, int maxWidth, boolean download)
299    {
300        // FIXME CMS-2611 Force absolute 
301        Request request = ContextHelper.getRequest(_context);
302        boolean absolute = request.getAttribute("forceAbsoluteUrl") != null ? (Boolean) request.getAttribute("forceAbsoluteUrl") : false;
303        
304        return resolveBoundedImage(type, uri, maxHeight, maxWidth, download, absolute);
305    }
306    
307    /**
308     * Resolve an uri upon the LinkResolverExtensionPoint
309     * @param type Type name (defined by the extension to use)
310     * @param uri URI depending on the type
311     * @param maxHeight the max height
312     * @param maxWidth the max width
313     * @param download Is this uri for download purposes.
314     * @param absolute true to generate absolute url
315     * @return The uri resolved, the empty string if the uri could not be resolved, or the uri itself if there is no resolver adapted
316     */
317    public static String resolveBoundedImage(String type, String uri, int maxHeight, int maxWidth, boolean download, boolean absolute)
318    {
319        return resolveBoundedImage(type, uri, maxHeight, maxWidth, download, absolute, false);
320    }
321    
322    /**
323     * Resolve an uri upon the LinkResolverExtensionPoint
324     * @param type Type name (defined by the extension to use)
325     * @param uri URI depending on the type
326     * @param maxHeight the max height
327     * @param maxWidth the max width
328     * @param download Is this uri for download purposes.
329     * @param absolute true to generate absolute url
330     * @param internal true to get an internal URI.
331     * @return The uri resolved, the empty string if the uri could not be resolved, or the uri itself if there is no resolver adapted
332     */
333    public static String resolveBoundedImage(String type, String uri, int maxHeight, int maxWidth, boolean download, boolean absolute, boolean internal)
334    {
335        // FIXME CMS-4059 Force base64 encoding. 
336        Request request = ContextHelper.getRequest(_context);
337        boolean encodeBase64 = request.getAttribute("forceBase64Encoding") != null ? (Boolean) request.getAttribute("forceBase64Encoding") : false;
338        
339        Object forceRemoteUrl = request.getAttribute("forceRemoteUrl");
340        boolean remote = forceRemoteUrl != null
341                        && (forceRemoteUrl instanceof Boolean && (Boolean) forceRemoteUrl
342                                || Boolean.valueOf((String) forceRemoteUrl));
343        
344        String proxiedUri = remote ? type + ";" + uri : uri;
345        URIResolver uriResolver = _instance._linkResolverExtensionPoint.getResolverForType(remote ? "remote" : type);
346        if (uriResolver != null)
347        {
348            try
349            {
350                request.removeAttribute("forceRemoteUrl");
351                if (encodeBase64)
352                {
353                    return uriResolver.resolveBoundedImageAsBase64(proxiedUri, maxHeight, maxWidth);
354                }
355                else
356                {
357                    return uriResolver.resolveBoundedImage(proxiedUri, maxHeight, maxWidth, download, absolute, internal);
358                }
359            }
360            catch (Exception e)
361            {
362                _instance.getLogger().warn("Error resolving the image of uri '" + uri + "' with type " + type, e);
363                return "";
364            }
365            finally
366            {
367                request.setAttribute("forceRemoteUrl", forceRemoteUrl);
368            }
369        }
370        else
371        {
372            return uri;
373        }
374    }
375    
376    /**
377     * Resolve an uri upon the LinkResolverExtensionPoint return it as a base64-encoded string.
378     * @param type Type name (defined by the extension to use)
379     * @param uri URI depending on the type
380     * @param maxHeight the max height
381     * @param maxWidth the max width
382     * @return a base64-encoded string representing the image or empty string if the uri could not be resolved, or the uri itself if there is no resolver adapted
383     */
384    public static String resolveBoundedImageAsBase64(String type, String uri, int maxHeight, int maxWidth)
385    {
386        URIResolver uriResolver = _instance._linkResolverExtensionPoint.getResolverForType(type);
387        if (uriResolver != null)
388        {
389            try
390            {
391                return uriResolver.resolveBoundedImageAsBase64(uri, maxHeight, maxWidth);
392            }
393            catch (Exception e)
394            {
395                _instance.getLogger().warn("Error resolving the bounded image as base64 of uri '" + uri + "' with type " + type, e);
396                return "";
397            }
398        }
399        else
400        {
401            return uri;
402        }
403    }
404
405    /**
406     * Resolve an uri upon the LinkResolverExtensionPoint
407     * @param type Type name (defined by the extension to use)
408     * @param uri URI depending on the type
409     * @param cropHeight the crop height
410     * @param cropWidth the crop width
411     * @return The uri resolved, or the uri if there is no resolver adapted
412     */
413    public static String resolveCroppedImage(String type, String uri, int cropHeight, int cropWidth)
414    {
415        return resolveCroppedImage (type, uri, cropHeight, cropWidth, false);
416    }
417    
418    /**
419     * Resolve an uri upon the LinkResolverExtensionPoint
420     * @param type Type name (defined by the extension to use)
421     * @param uri URI depending on the type
422     * @param cropHeight the crop height
423     * @param cropWidth the crop width
424     * @param download Is this uri for download purposes.
425     * @return The uri resolved, or the uri if there is no resolver adapted
426     */
427    public static String resolveCroppedImage(String type, String uri, int cropHeight, int cropWidth, boolean download)
428    {
429        // FIXME CMS-2611 Force absolute 
430        Request request = ContextHelper.getRequest(_context);
431        boolean absolute = request.getAttribute("forceAbsoluteUrl") != null ? (Boolean) request.getAttribute("forceAbsoluteUrl") : false;
432        
433        return resolveCroppedImage(type, uri, cropHeight, cropWidth, download, absolute);
434    }
435    
436    /**
437     * Resolve an uri upon the LinkResolverExtensionPoint
438     * @param type Type name (defined by the extension to use)
439     * @param uri URI depending on the type
440     * @param cropHeight the crop height
441     * @param cropWidth the crop width
442     * @param download Is this uri for download purposes.
443     * @param absolute true to generate absolute url
444     * @return The uri resolved, the empty string if the uri could not be resolved, or the uri itself if there is no resolver adapted
445     */
446    public static String resolveCroppedImage(String type, String uri, int cropHeight, int cropWidth, boolean download, boolean absolute)
447    {
448        return resolveCroppedImage(type, uri, cropHeight, cropWidth, download, absolute, false);
449    }
450    
451    /**
452     * Resolve an uri upon the LinkResolverExtensionPoint
453     * @param type Type name (defined by the extension to use)
454     * @param uri URI depending on the type
455     * @param cropHeight the crop height
456     * @param cropWidth the crop width
457     * @param download Is this uri for download purposes.
458     * @param absolute true to generate absolute url
459     * @param internal true to get an internal URI.
460     * @return The uri resolved, the empty string if the uri could not be resolved, or the uri itself if there is no resolver adapted
461     */
462    public static String resolveCroppedImage(String type, String uri, int cropHeight, int cropWidth, boolean download, boolean absolute, boolean internal)
463    {
464        // FIXME CMS-4059 Force base64 encoding. 
465        Request request = ContextHelper.getRequest(_context);
466        boolean encodeBase64 = request.getAttribute("forceBase64Encoding") != null ? (Boolean) request.getAttribute("forceBase64Encoding") : false;
467        
468        URIResolver uriResolver = _instance._linkResolverExtensionPoint.getResolverForType(type);
469        if (uriResolver != null)
470        {
471            try
472            {
473                if (encodeBase64)
474                {
475                    return uriResolver.resolveCroppedImageAsBase64(uri, cropHeight, cropWidth);
476                }
477                else
478                {
479                    return uriResolver.resolveCroppedImage(uri, cropHeight, cropWidth, download, absolute, internal);
480                }
481            }
482            catch (Exception e)
483            {
484                _instance.getLogger().warn("Error resolving the image of uri '" + uri + "' with type " + type, e);
485                return "";
486            }
487        }
488        else
489        {
490            return uri;
491        }
492    }
493    
494    /**
495     * Resolve an uri upon the LinkResolverExtensionPoint return it as a base64-encoded string.
496     * @param type Type name (defined by the extension to use)
497     * @param uri URI depending on the type
498     * @param cropHeight the crop height
499     * @param cropWidth the crop width
500     * @return a base64-encoded string representing the image or empty string if the uri could not be resolved, or the uri itself if there is no resolver adapted
501     */
502    public static String resolveCroppedImageAsBase64(String type, String uri, int cropHeight, int cropWidth)
503    {
504        URIResolver uriResolver = _instance._linkResolverExtensionPoint.getResolverForType(type);
505        if (uriResolver != null)
506        {
507            try
508            {
509                return uriResolver.resolveCroppedImageAsBase64(uri, cropHeight, cropWidth);
510            }
511            catch (Exception e)
512            {
513                _instance.getLogger().warn("Error resolving the cropped image as base64 of uri '" + uri + "' with type " + type, e);
514                return "";
515            }
516        }
517        else
518        {
519            return uri;
520        }
521    }
522    
523    /**
524     * Get the mime type.
525     * @param type Type name (defined by the extension to use)
526     * @param uri URI depending on the type
527     * @return the mime type or application/octet-stream if not found
528     */
529    public static String getMimeType(String type, String uri)
530    {
531        URIResolver uriResolver = _instance._linkResolverExtensionPoint.getResolverForType(type);
532        return Optional.ofNullable(uriResolver)
533                .map(r -> _getMimeTypeSafe(r, uri))
534                .filter(StringUtils::isNotEmpty)
535                .orElse("application/octet-stream");
536    }
537    
538    private static String _getMimeTypeSafe(URIResolver uriResolver, String uri)
539    {
540        try
541        {
542            return uriResolver.getMimeType(uri);
543        }
544        catch (Exception e)
545        {
546            _instance.getLogger().warn("Error resolving the image to get the mime type of uri '" + uri + "' with type " + uriResolver.getType(), e);
547            return null;
548        }
549    }
550}