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}