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; 020import java.util.regex.Matcher; 021import java.util.regex.Pattern; 022 023import org.apache.avalon.framework.context.Context; 024import org.apache.avalon.framework.context.ContextException; 025import org.apache.avalon.framework.context.Contextualizable; 026import org.apache.avalon.framework.logger.AbstractLogEnabled; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.avalon.framework.service.Serviceable; 030import org.apache.cocoon.components.ContextHelper; 031import org.apache.cocoon.environment.Request; 032 033import org.ametys.cms.URIPrefixHandler; 034import org.ametys.cms.repository.Content; 035import org.ametys.cms.transformation.ConsistencyChecker.CHECK; 036import org.ametys.core.util.FilenameUtils; 037import org.ametys.core.util.URIUtils; 038import org.ametys.plugins.explorer.resources.Resource; 039import org.ametys.plugins.repository.AmetysObject; 040import org.ametys.plugins.repository.AmetysObjectResolver; 041import org.ametys.plugins.repository.UnknownAmetysObjectException; 042import org.ametys.runtime.i18n.I18nizableText; 043 044/** 045 * {@link URIResolver} for type "attachment".<br> 046 * These links point to a file from the attachments of the current Content. 047 */ 048public class AttachmentURIResolver extends AbstractLogEnabled implements URIResolver, Serviceable, Contextualizable 049{ 050 /** Regular expression for contents stored under plugins */ 051 protected static final Pattern __PLUGIN_CONTENT_PATTERN = Pattern.compile("^/ametys:plugins/([^/]+)/ametys:contents/(.*)$"); 052 /** The ametys resolver */ 053 protected AmetysObjectResolver _resolver; 054 /** The avalon context */ 055 protected Context _context; 056 /** The URI prefix handler */ 057 protected URIPrefixHandler _prefixHandler; 058 059 @Override 060 public void contextualize(Context context) throws ContextException 061 { 062 _context = context; 063 } 064 065 @Override 066 public void service(ServiceManager manager) throws ServiceException 067 { 068 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 069 _prefixHandler = (URIPrefixHandler) manager.lookup(URIPrefixHandler.ROLE); 070 } 071 072 @Override 073 public String getType() 074 { 075 return "attachment-content"; 076 } 077 078 @Override 079 public String resolve(String uri, boolean download, boolean absolute, boolean internal) 080 { 081 Request request = ContextHelper.getRequest(_context); 082 String path; 083 Content content = null; 084 String contentName = null; 085 086 try 087 { 088 Resource resource = (Resource) _resolver.resolveById(uri); 089 path = resource.getResourcePath(); 090 091 content = (Content) request.getAttribute(Content.class.getName()); 092 contentName = content.getName(); 093 } 094 catch (UnknownAmetysObjectException e) 095 { 096 getLogger().warn("Link to unexisting resource " + uri); 097 return ""; 098 } 099 catch (Exception e) 100 { 101 throw new IllegalStateException(e); 102 } 103 104 StringBuilder resultPath = new StringBuilder(); 105 resultPath.append(getUriPrefix(content, download, absolute, internal)); 106 107 Matcher m = __PLUGIN_CONTENT_PATTERN.matcher(content.getPath()); 108 if (m.matches()) 109 { 110 String pluginName = m.group(1); 111 resultPath.append("/") 112 .append(pluginName) 113 .append("/_plugin-attachments/"); 114 } 115 else 116 { 117 resultPath.append("/_attachments/"); 118 } 119 120 resultPath.append(contentName) 121 .append(path); 122 123 String encodePath = FilenameUtils.encodePath(resultPath.toString()); 124 return URIUtils.encodeURI(encodePath, download ? Collections.singletonMap("download", "true") : null); 125 } 126 127 /** 128 * Get the URI prefix 129 * @param object The object 130 * @param download true if the pointed resource is to be downloaded. 131 * @param absolute true if the url must be absolute 132 * @param internal true to get an internal URI. 133 * @return the URI prefix 134 */ 135 protected String getUriPrefix (AmetysObject object, boolean download, boolean internal, boolean absolute) 136 { 137 if (internal) 138 { 139 return "cocoon://"; 140 } 141 else if (absolute) 142 { 143 return _prefixHandler.getAbsoluteUriPrefix(); 144 } 145 else 146 { 147 return _prefixHandler.getUriPrefix(); 148 } 149 } 150 151 @Override 152 public String resolveImage(String uri, int height, int width, boolean download, boolean absolute, boolean internal) 153 { 154 if (height == 0 && width == 0) 155 { 156 return resolve(uri, download, absolute, internal); 157 } 158 StringBuilder uriArgument = new StringBuilder(); 159 uriArgument.append("_").append(height).append("x").append(width); 160 return _resolveImage(uri, uriArgument.toString(), download, absolute, internal); 161 } 162 163 @Override 164 public String resolveImageAsBase64(String uri, int height, int width) 165 { 166 return resolveImageAsBase64(uri, height, width, 0, 0, 0, 0); 167 } 168 169 @Override 170 public String resolveBoundedImage(String uri, int maxHeight, int maxWidth, boolean download, boolean absolute, boolean internal) 171 { 172 if (maxHeight == 0 && maxWidth == 0) 173 { 174 return resolve(uri, download, absolute, internal); 175 } 176 StringBuilder uriArgument = new StringBuilder(); 177 uriArgument.append("_max").append(maxHeight).append("x").append(maxWidth); 178 return _resolveImage(uri, uriArgument.toString(), download, absolute, internal); 179 } 180 181 @Override 182 public String resolveBoundedImageAsBase64(String uri, int maxHeight, int maxWidth) 183 { 184 return resolveImageAsBase64(uri, 0, 0, maxHeight, maxWidth, 0, 0); 185 } 186 187 @Override 188 public String resolveCroppedImage(String uri, int cropHeight, int cropWidth, boolean download, boolean absolute, boolean internal) 189 { 190 if (cropHeight == 0 && cropWidth == 0) 191 { 192 return resolve(uri, download, absolute, internal); 193 } 194 StringBuilder uriArgument = new StringBuilder(); 195 uriArgument.append("_crop").append(cropHeight).append("x").append(cropWidth); 196 return _resolveImage(uri, uriArgument.toString(), download, absolute, internal); 197 } 198 199 /** 200 * Resolves a link URI for rendering image.<br> 201 * The output must be a properly encoded path, relative to the webapp context, accessible from a browser. 202 * @param uri the link URI. 203 * @param uriArgument the argument to append to the uri 204 * @param download true if the pointed resource is to be downloaded. 205 * @param absolute true if the url must be absolute 206 * @param internal true to get an internal URI. 207 * @return the path to the image. 208 */ 209 protected String _resolveImage(String uri, String uriArgument, boolean download, boolean absolute, boolean internal) 210 { 211 Request request = ContextHelper.getRequest(_context); 212 String path; 213 Content content = null; 214 String contentName = null; 215 try 216 { 217 Resource resource = (Resource) _resolver.resolveById(uri); 218 path = resource.getResourcePath(); 219 220 content = (Content) request.getAttribute(Content.class.getName()); 221 contentName = content.getName(); 222 223 } 224 catch (UnknownAmetysObjectException e) 225 { 226 getLogger().warn("Link to unexisting resource " + uri); 227 return ""; 228 } 229 catch (Exception e) 230 { 231 throw new IllegalStateException(e); 232 } 233 234 int i = path.lastIndexOf("."); 235 String extension = path.substring(i); 236 path = path.substring(0, i); 237 238 StringBuilder result = new StringBuilder(); 239 result.append(getUriPrefix(content, download, internal, absolute)); 240 241 Matcher m = __PLUGIN_CONTENT_PATTERN.matcher(content.getPath()); 242 if (m.matches()) 243 { 244 String pluginName = m.group(1); 245 result.append("/") 246 .append(pluginName) 247 .append("/_plugin-attachments-images/"); 248 } 249 else 250 { 251 result.append("/_attachments-images/"); 252 } 253 254 255 result.append(contentName) 256 .append(path) 257 .append(uriArgument) 258 .append(extension); 259 260 String encodePath = URIUtils.encodePath(result.toString()); 261 return URIUtils.encodeURI(encodePath, download ? Collections.singletonMap("download", "true") : null); 262 } 263 264 @Override 265 public String resolveCroppedImageAsBase64(String uri, int cropHeight, int cropWidth) 266 { 267 return resolveImageAsBase64(uri, 0, 0, 0, 0, cropHeight, cropWidth); 268 } 269 270 /** 271 * Get an image's bytes encoded as base64, optionally resized. 272 * @param uri the image URI. 273 * @param height the specified height. Ignored if negative. 274 * @param width the specified width. Ignored if negative. 275 * @param maxHeight the maximum image height. Ignored if height or width is specified. 276 * @param maxWidth the maximum image width. Ignored if height or width is specified. 277 * @param cropHeight The cropping height. Ignored if negative. 278 * @param cropWidth The cropping width. Ignored if negative. 279 * @return the image bytes encoded as base64. 280 */ 281 protected String resolveImageAsBase64(String uri, int height, int width, int maxHeight, int maxWidth, int cropHeight, int cropWidth) 282 { 283 try 284 { 285 Resource resource = (Resource) _resolver.resolveById(uri); 286 287 try (InputStream dataIs = resource.getInputStream()) 288 { 289 return ImageResolverHelper.resolveImageAsBase64(dataIs, resource.getMimeType(), height, width, maxHeight, maxWidth, cropHeight, cropWidth); 290 } 291 } 292 catch (UnknownAmetysObjectException e) 293 { 294 getLogger().warn("Link to unexisting resource " + uri); 295 return ""; 296 } 297 catch (Exception e) 298 { 299 throw new IllegalStateException(e); 300 } 301 } 302 303 @Override 304 public CHECK checkLink(String uri, boolean shortTest) 305 { 306 try 307 { 308 _resolver.resolveById(uri); 309 return CHECK.SUCCESS; 310 } 311 catch (UnknownAmetysObjectException e) 312 { 313 return CHECK.NOT_FOUND; 314 } 315 } 316 317 @Override 318 public I18nizableText getLabel(String uri) 319 { 320 try 321 { 322 Resource resource = (Resource) _resolver.resolveById(uri); 323 return new I18nizableText("plugin.cms", "PLUGINS_CMS_LINK_ATTACHMENT_LABEL", Collections.singletonList(resource.getResourcePath())); 324 } 325 catch (UnknownAmetysObjectException e) 326 { 327 return new I18nizableText("plugin.cms", "PLUGINS_CMS_LINK_ATTACHMENT_UNKNOWN"); 328 } 329 } 330}