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.FilenameUtils;
033import org.ametys.core.util.URIUtils;
034import org.ametys.plugins.explorer.resources.Resource;
035import org.ametys.plugins.repository.AmetysObject;
036import org.ametys.plugins.repository.AmetysObjectResolver;
037import org.ametys.plugins.repository.UnknownAmetysObjectException;
038import org.ametys.plugins.repository.version.VersionableAmetysObject;
039import org.ametys.runtime.i18n.I18nizableText;
040
041/**
042 * {@link URIResolver} for type "explorer". <br>
043 * These links point to a file from the resources explorer.
044 */
045public class ResourceURIResolver extends AbstractLogEnabled implements URIResolver, Serviceable, Contextualizable
046{
047    /** The ametys object resolver. */
048    protected AmetysObjectResolver _resolver;
049    /** The avalon context. */
050    protected Context _context;
051    /** The URI prefix handler */
052    protected URIPrefixHandler _prefixHandler;
053    
054    @Override
055    public void contextualize(Context context) throws ContextException
056    {
057        _context = context;
058    }
059
060    @Override
061    public void service(ServiceManager manager) throws ServiceException
062    {
063        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
064        _prefixHandler = (URIPrefixHandler) manager.lookup(URIPrefixHandler.ROLE);
065    }
066    
067    @Override
068    public String getType()
069    {
070        return "explorer";
071    }
072
073    @Override
074    public String resolve(String uri, boolean download, boolean absolute, boolean internal)
075    {
076        return _resolve(uri, download, absolute, internal, "resources", null);
077    }
078    
079    @Override
080    public String resolveImage(String uri, int height, int width, boolean download, boolean absolute, boolean internal)
081    {
082        if (height == 0 && width == 0)
083        {
084            return resolve(uri, download, absolute, internal);
085        }
086        
087        
088        return _resolve(uri, download, absolute, internal, "resources-images", "_" + height + "x" + width);
089    }
090    
091    @Override
092    public String resolveImageAsBase64(String uri, int height, int width)
093    {
094        return resolveImageAsBase64(uri, height, width, 0, 0, 0, 0);
095    }
096
097    @Override
098    public String resolveBoundedImage(String uri, int maxHeight, int maxWidth, boolean download, boolean absolute, boolean internal)
099    {
100        if (maxHeight == 0 && maxWidth == 0)
101        {
102            return resolve(uri, download, absolute, internal);
103        }
104        
105        return _resolve(uri, download, absolute, internal, "resources-images", "_max" + maxHeight + "x" + maxWidth);
106    }
107    
108    @Override
109    public String resolveBoundedImageAsBase64(String uri, int maxHeight, int maxWidth)
110    {
111        return resolveImageAsBase64(uri, 0, 0, maxHeight, maxWidth, 0, 0);
112    }
113
114    @Override
115    public String resolveCroppedImage(String uri, int cropHeight, int cropWidth, boolean download, boolean absolute, boolean internal)
116    {
117        if (cropHeight == 0 && cropWidth == 0)
118        {
119            return resolve(uri, download, absolute, internal);
120        }
121        
122        return _resolve(uri, download, absolute, internal, "resources-images", "_crop" + cropHeight + "x" + cropWidth);
123    }
124    
125    @Override
126    public String resolveCroppedImageAsBase64(String uri, int cropHeight, int cropWidth)
127    {
128        return resolveImageAsBase64(uri, 0, 0, 0, 0, cropHeight, cropWidth);
129    }
130    
131    /**
132     * Get the URI prefix
133     * @param object The object
134     * @param download true if the pointed resource is to be downloaded.
135     * @param absolute true if the url must be absolute
136     * @param internal true to get an internal URI.
137     * @return the URI prefix
138     */
139    protected String getUriPrefix (AmetysObject object, boolean download, boolean internal, boolean absolute)
140    {
141        if (internal)
142        {
143            return "cocoon://";
144        }
145        else if (absolute)
146        {
147            return _prefixHandler.getAbsoluteUriPrefix();
148        }
149        else
150        {
151            return _prefixHandler.getUriPrefix();
152        }
153    }
154    
155    /**
156     * Creates a full uri
157     * @param uri the base uri, ie the resource id
158     * @param download true to create a forced download uri
159     * @param absolute true to create an absolute uri (if internal is false)
160     * @param internal true to create an internal uri
161     * @param prefix Prefix on the uri
162     * @param suffix Suffix on the uri
163     * @return The created uri
164     */
165    protected String _resolve(String uri, boolean download, boolean absolute, boolean internal, String prefix, String suffix)
166    {
167        String version = null;
168        String path;
169        Resource resource = null;
170        try
171        {
172            String resourceId = uri;
173            int i = uri.indexOf(";");
174            if (i != -1)
175            {
176                resourceId = uri.substring(0, i);
177                version = uri.substring(i + 1);
178            }
179            
180            resource = (Resource) _resolver.resolveById(resourceId);
181            path = getResourcePath(resource);
182        }
183        catch (UnknownAmetysObjectException e)
184        {
185            getLogger().warn("Link to unexisting resource " + uri);
186            return "";
187        }
188        
189        int i = path.lastIndexOf(".");
190        String extension = i != -1 ? path.substring(i) : null;
191        path = i != -1 ? path.substring(0, i) : path; 
192        
193        StringBuilder result = new StringBuilder();
194        
195        result.append(getUriPrefix(resource, download, absolute, internal));
196        
197        String realPrefix = getRealPrefix(resource, prefix);
198        if (StringUtils.isNotEmpty(realPrefix))
199        {
200            result.append("/_").append(realPrefix);
201        }
202        
203        if (StringUtils.isNotEmpty(version) && resource instanceof VersionableAmetysObject)
204        {
205            result.append("/__version").append(version);
206        }
207        
208        result.append(FilenameUtils.encodePath(path));
209        
210        if (suffix != null)
211        {
212            result.append(suffix);
213        }
214        
215        if (extension != null)
216        {
217            result.append(extension);
218        }
219        
220        return URIUtils.encodeURI(result.toString(), 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}