001/* 002 * Copyright 2016 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.plugins.workspaces.documents; 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.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.avalon.framework.service.Serviceable; 027import org.apache.commons.lang.StringUtils; 028 029import org.ametys.cms.transformation.ConsistencyChecker.CHECK; 030import org.ametys.cms.transformation.ImageResolverHelper; 031import org.ametys.cms.transformation.URIResolver; 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.plugins.workspaces.project.objects.Project; 040import org.ametys.runtime.i18n.I18nizableText; 041import org.ametys.runtime.plugin.component.AbstractLogEnabled; 042import org.ametys.runtime.plugin.component.PluginAware; 043import org.ametys.web.URIPrefixHandler; 044 045/** 046 * {@link URIResolver} for type "project-resource". <br> 047 * These links point to a file from the resources of a project. 048 */ 049// FIXME refactor this class by subclassing kernel's ResourceURIResolver as soon as Workspaces is only compatible with 4.4 050// see WORKSPACES-645 051public class ProjectResourceURIResolver extends AbstractLogEnabled implements URIResolver, PluginAware, Serviceable, Contextualizable 052{ 053 /** The ametys object resolver. */ 054 protected AmetysObjectResolver _resolver; 055 /** The avalon context. */ 056 protected Context _context; 057 /** plugin name */ 058 protected String _pluginName; 059 /** The URI prefix handler */ 060 protected URIPrefixHandler _webPrefixHandler; 061 062 @Override 063 public void contextualize(Context context) throws ContextException 064 { 065 _context = context; 066 } 067 068 @Override 069 public void service(ServiceManager manager) throws ServiceException 070 { 071 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 072 _webPrefixHandler = (URIPrefixHandler) manager.lookup(URIPrefixHandler.ROLE); 073 } 074 075 public void setPluginInfo(String pluginName, String featureName, String id) 076 { 077 _pluginName = pluginName; 078 } 079 080 @Override 081 public String getType() 082 { 083 return "project-resource"; 084 } 085 086 @Override 087 public String resolve(String uri, boolean download, boolean absolute, boolean internal) 088 { 089 return _resolve(uri, download, absolute, internal, "resources", null); 090 } 091 092 @Override 093 public String resolveImage(String uri, int height, int width, boolean download, boolean absolute, boolean internal) 094 { 095 if (height == 0 && width == 0) 096 { 097 return resolve(uri, download, absolute, internal); 098 } 099 100 101 return _resolve(uri, download, absolute, internal, "resources-images", "_" + height + "x" + width); 102 } 103 104 @Override 105 public String resolveImageAsBase64(String uri, int height, int width) 106 { 107 return resolveImageAsBase64(uri, height, width, 0, 0, 0, 0); 108 } 109 110 @Override 111 public String resolveBoundedImage(String uri, int maxHeight, int maxWidth, boolean download, boolean absolute, boolean internal) 112 { 113 if (maxHeight == 0 && maxWidth == 0) 114 { 115 return resolve(uri, download, absolute, internal); 116 } 117 118 return _resolve(uri, download, absolute, internal, "resources-images", "_max" + maxHeight + "x" + maxWidth); 119 } 120 121 @Override 122 public String resolveBoundedImageAsBase64(String uri, int maxHeight, int maxWidth) 123 { 124 return resolveImageAsBase64(uri, 0, 0, maxHeight, maxWidth, 0, 0); 125 } 126 127 @Override 128 public String resolveCroppedImage(String uri, int cropHeight, int cropWidth, boolean download, boolean absolute, boolean internal) 129 { 130 if (cropHeight == 0 && cropWidth == 0) 131 { 132 return resolve(uri, download, absolute, internal); 133 } 134 135 return _resolve(uri, download, absolute, internal, "resources-images", "_crop" + cropHeight + "x" + cropWidth); 136 } 137 138 @Override 139 public String resolveCroppedImageAsBase64(String uri, int cropHeight, int cropWidth) 140 { 141 return resolveImageAsBase64(uri, 0, 0, 0, 0, cropHeight, cropWidth); 142 } 143 144 /** 145 * Creates a full uri 146 * @param uri the base uri, ie the resource id 147 * @param download true to create a forced download uri 148 * @param absolute true to create an absolute uri (if internal is false) 149 * @param internal true to create an internal uri 150 * @param prefix Prefix on the uri 151 * @param suffix Suffix on the uri 152 * @return The created uri 153 */ 154 protected String _resolve(String uri, boolean download, boolean absolute, boolean internal, String prefix, String suffix) 155 { 156 String version = null; 157 String path; 158 Resource resource = null; 159 try 160 { 161 String resourceId = uri; 162 int i = uri.indexOf(";"); 163 if (i != -1) 164 { 165 resourceId = uri.substring(0, i); 166 version = uri.substring(i + 1); 167 } 168 169 resource = (Resource) _resolver.resolveById(resourceId); 170 path = getResourcePath(resource); 171 } 172 catch (UnknownAmetysObjectException e) 173 { 174 getLogger().warn("Link to unexisting resource " + uri); 175 return ""; 176 } 177 178 int i = path.lastIndexOf("."); 179 String extension = i != -1 ? path.substring(i) : null; 180 path = i != -1 ? path.substring(0, i) : path; 181 182 StringBuilder result = new StringBuilder(); 183 184 result.append(getUriPrefix(resource, download, absolute, internal)); 185 186 String realPrefix = getRealPrefix(resource, prefix); 187 if (StringUtils.isNotEmpty(realPrefix)) 188 { 189 result.append("/_").append(realPrefix); 190 } 191 192 if (StringUtils.isNotEmpty(version) && resource instanceof VersionableAmetysObject) 193 { 194 result.append("/__version").append(version); 195 } 196 197 result.append(FilenameUtils.encodePath(path)); 198 199 if (suffix != null) 200 { 201 result.append(suffix); 202 } 203 204 if (extension != null) 205 { 206 result.append(extension); 207 } 208 209 return URIUtils.encodeURI(result.toString(), download ? Collections.singletonMap("download", "true") : null); 210 } 211 212 /** 213 * Get an image's bytes encoded as base64, optionally resized. 214 * @param uri the image URI. 215 * @param height the specified height. Ignored if negative. 216 * @param width the specified width. Ignored if negative. 217 * @param maxHeight the maximum image height. Ignored if height or width is specified. 218 * @param maxWidth the maximum image width. Ignored if height or width is specified. 219 * @param cropHeight The cropping height. Ignored if negative. 220 * @param cropWidth The cropping width. Ignored if negative. 221 * @return the image bytes encoded as base64. 222 */ 223 protected String resolveImageAsBase64(String uri, int height, int width, int maxHeight, int maxWidth, int cropHeight, int cropWidth) 224 { 225 try 226 { 227 Resource resource = (Resource) _resolver.resolveById(uri); 228 try (InputStream dataIs = resource.getInputStream()) 229 { 230 return ImageResolverHelper.resolveImageAsBase64(dataIs, resource.getMimeType(), height, width, maxHeight, maxWidth); 231 } 232 } 233 catch (UnknownAmetysObjectException e) 234 { 235 getLogger().warn("Link to unexisting resource " + uri); 236 return ""; 237 } 238 catch (Exception e) 239 { 240 throw new IllegalStateException(e); 241 } 242 } 243 244 @Override 245 public CHECK checkLink(String uri, boolean shortTest) 246 { 247 try 248 { 249 _resolver.resolveById(uri); 250 return CHECK.SUCCESS; 251 } 252 catch (UnknownAmetysObjectException e) 253 { 254 return CHECK.NOT_FOUND; 255 } 256 } 257 258 @Override 259 public I18nizableText getLabel(String uri) 260 { 261 try 262 { 263 Resource resource = (Resource) _resolver.resolveById(uri); 264 return new I18nizableText("plugin" + _pluginName, "PLUGINS_WORKSPACES_LINK_RESOURCE_LABEL", Collections.singletonList(resource.getResourcePath())); 265 } 266 catch (UnknownAmetysObjectException e) 267 { 268 return new I18nizableText("plugin" + _pluginName, "PLUGINS_WORKSPACES_LINK_RESOURCE_UNKNOWN"); 269 } 270 } 271 272 /** 273 * Get the URI prefix 274 * @param object The object 275 * @param download true if the pointed resource is to be downloaded. 276 * @param absolute true if the url must be absolute 277 * @param internal true to get an internal URI. 278 * @return the URI prefix 279 */ 280 protected String getUriPrefix (AmetysObject object, boolean download, boolean absolute, boolean internal) 281 { 282 Project project = null; 283 String projectName = null; 284 String siteName = null; 285 286 if (object instanceof Resource) 287 { 288 Resource resource = (Resource) object; 289 project = _getProject(resource); 290 projectName = project.getName(); 291 292 siteName = project.getSites().iterator().next().getName(); 293 } 294 295 if (internal) 296 { 297 return "cocoon://plugins/" + _pluginName + "/" + projectName; 298 } 299 else if (absolute) 300 { 301 return _webPrefixHandler.getAbsoluteUriPrefix(siteName) + "/_plugins/" + _pluginName + "/" + projectName; 302 } 303 else 304 { 305 return _webPrefixHandler.getUriPrefix(siteName) + "/_plugins/" + _pluginName + "/" + projectName; 306 } 307 } 308 309 /** 310 * Get the resource path 311 * @param resource the resource 312 * @return the path 313 */ 314 protected String getResourcePath (Resource resource) 315 { 316 return resource.getResourcePath(); 317 } 318 319 /** 320 * Get the real prefix 321 * @param resource the resource 322 * @param prefix the initial prefix 323 * @return the real prefix 324 */ 325 protected String getRealPrefix (Resource resource, String prefix) 326 { 327 return prefix; 328 } 329 330 /** 331 * Retrieves parent project 332 * @param resource The resource which belongs to a project 333 * @return The parent project 334 */ 335 protected Project _getProject (AmetysObject resource) 336 { 337 AmetysObject parent = resource.getParent(); 338 while (parent != null) 339 { 340 if (parent instanceof Project) 341 { 342 return (Project) parent; 343 } 344 345 parent = parent.getParent(); 346 } 347 return null; 348 } 349}