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