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.HashMap; 021import java.util.Map; 022import java.util.regex.Matcher; 023import java.util.regex.Pattern; 024 025import org.apache.avalon.framework.context.Context; 026import org.apache.avalon.framework.context.ContextException; 027import org.apache.avalon.framework.context.Contextualizable; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.avalon.framework.service.Serviceable; 031import org.apache.cocoon.components.ContextHelper; 032import org.apache.cocoon.environment.Request; 033 034import org.ametys.cms.data.Binary; 035import org.ametys.cms.repository.Content; 036import org.ametys.cms.transformation.ConsistencyChecker.CHECK; 037import org.ametys.core.util.FilenameUtils; 038import org.ametys.core.util.URIUtils; 039import org.ametys.plugins.repository.AmetysObject; 040import org.ametys.plugins.repository.AmetysObjectResolver; 041import org.ametys.plugins.repository.UnknownAmetysObjectException; 042import org.ametys.plugins.repository.data.ametysobject.ModelAwareDataAwareAmetysObject; 043import org.ametys.plugins.repository.version.VersionableAmetysObject; 044import org.ametys.runtime.i18n.I18nizableText; 045import org.ametys.runtime.workspace.WorkspaceMatcher; 046 047/** 048 * {@link URIResolver} for type "attribute".<br> 049 * These links or images point to a file from the attribute of the current ametys object. 050 */ 051public class AttributeURIResolver extends AbstractURIResolver implements Serviceable, Contextualizable 052{ 053 private static final Pattern _OBJECT_URI_PATTERN = Pattern.compile("([^?]*)\\?objectId=(.*)"); 054 private static final Pattern _CONTENT_URI_PATTERN = Pattern.compile("([^?]*)\\?contentId=(.*)"); 055 056 /** The ametys object resolver */ 057 protected AmetysObjectResolver _resolver; 058 059 /** The context */ 060 protected Context _context; 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 } 073 074 @Override 075 public String getType() 076 { 077 return "attribute"; 078 } 079 080 @Override 081 protected String _resolve(String uri, String uriArgument, boolean download, boolean absolute, boolean internal) 082 { 083 Request request = ContextHelper.getRequest(_context); 084 085 AttributeInfo info = _getAttributeInfo(uri, request); 086 087 ModelAwareDataAwareAmetysObject ametysObject = info.getAmetysObject(); 088 String path = info.getPath(); 089 090 if (ametysObject == null) 091 { 092 throw new IllegalStateException("Cannot resolve a local link to an unknown ametys object for uri " + request.getRequestURI()); 093 } 094 095 String ametysObjectVersion = ""; 096 if (ametysObject instanceof VersionableAmetysObject) 097 { 098 ametysObjectVersion = ((VersionableAmetysObject) ametysObject).getRevision(); 099 } 100 101 Binary binary = ametysObject.getValue(path); 102 103 String filename = FilenameUtils.encodeName(binary.getFilename()); 104 105 String baseName = org.apache.commons.io.FilenameUtils.getBaseName(filename); 106 String extension = org.apache.commons.io.FilenameUtils.getExtension(filename); 107 108 StringBuilder resultPath = new StringBuilder(); 109 110 resultPath.append("/_object") 111 .append(FilenameUtils.encodePath(ametysObject.getPath())) 112 .append("/_attribute/") 113 .append(path) 114 .append("/") 115 .append(baseName) 116 .append(uriArgument) 117 .append(extension.isEmpty() ? "" : "." + extension); 118 119 String resultUri = getUri(resultPath.toString(), ametysObject, download, absolute, internal); 120 121 Map<String, String> params = new HashMap<>(); 122 params.put("objectId", ametysObject.getId()); 123 124 if (download) 125 { 126 params.put("download", "true"); 127 } 128 129 if (ametysObjectVersion != null) 130 { 131 params.put("contentVersion", ametysObjectVersion); 132 } 133 134 return internal ? URIUtils.buildURI(resultUri, params) : URIUtils.encodeURI(resultUri, params); 135 } 136 137 @Override 138 protected String resolveImageAsBase64(String uri, int height, int width, int maxHeight, int maxWidth, int cropHeight, int cropWidth) 139 { 140 try 141 { 142 Request request = ContextHelper.getRequest(_context); 143 144 AttributeInfo info = _getAttributeInfo(uri, request); 145 146 ModelAwareDataAwareAmetysObject ametysObject = info.getAmetysObject(); 147 String path = info.getPath(); 148 149 if (ametysObject == null) 150 { 151 throw new IllegalStateException("Cannot resolve a local link to an unknown ametys object for uri " + request.getRequestURI()); 152 } 153 154 Binary binary = ametysObject.getValue(path); 155 156 try (InputStream dataIs = binary.getInputStream()) 157 { 158 return ImageResolverHelper.resolveImageAsBase64(dataIs, binary.getMimeType(), height, width, maxHeight, maxWidth, cropHeight, cropWidth); 159 } 160 } 161 catch (Exception e) 162 { 163 throw new IllegalStateException(e); 164 } 165 } 166 167 /** 168 * Get the URI prefix 169 * @param path the resource path 170 * @param object The object 171 * @param download true if the pointed resource is to be downloaded. 172 * @param absolute true if the url must be absolute 173 * @param internal true to get an internal URI. 174 * @return the URI prefix 175 */ 176 protected String getUri(String path, ModelAwareDataAwareAmetysObject object, boolean download, boolean absolute, boolean internal) 177 { 178 if (internal) 179 { 180 return "cocoon://" + path; 181 } 182 else 183 { 184 Request request = ContextHelper.getRequest(_context); 185 String workspaceURI = (String) request.getAttribute(WorkspaceMatcher.WORKSPACE_URI); 186 String uriPrefix = request.getContextPath() + workspaceURI; 187 188 if (absolute && !uriPrefix.startsWith(request.getScheme())) 189 { 190 uriPrefix = request.getScheme() + "://" + request.getServerName() + (request.getServerPort() != 80 ? ":" + request.getServerPort() : "") + uriPrefix; 191 } 192 193 return uriPrefix + path; 194 } 195 } 196 197 @Override 198 public CHECK checkLink(String uri, boolean shortTest) 199 { 200 return CHECK.SUCCESS; 201 } 202 203 @Override 204 public I18nizableText getLabel(String uri) 205 { 206 try 207 { 208 Request request = ContextHelper.getRequest(_context); 209 210 AttributeInfo info = _getAttributeInfo(uri, request); 211 212 return new I18nizableText("plugin.cms", "PLUGINS_CMS_LINK_METADATA_LABEL", Collections.singletonList(info.getPath())); 213 } 214 catch (UnknownAmetysObjectException e) 215 { 216 return new I18nizableText("plugin.cms", "PLUGINS_CMS_LINK_METADATA_UNKNOWN"); 217 } 218 } 219 220 /** 221 * Get attribute path and ametys object. 222 * @param uri the attribute URI. 223 * @param request the request. 224 * @return the attribute info. 225 */ 226 protected AttributeInfo _getAttributeInfo(String uri, Request request) 227 { 228 AttributeInfo info = new AttributeInfo(); 229 230 Matcher matcher = _OBJECT_URI_PATTERN.matcher(uri); 231 Matcher contentMatcher = _CONTENT_URI_PATTERN.matcher(uri); 232 233 // Test if the URI contains an object ID. 234 if (matcher.matches()) 235 { 236 info.setPath(matcher.group(1)); 237 String objectId = matcher.group(2); 238 239 ModelAwareDataAwareAmetysObject object = _resolver.resolveById(objectId); 240 info.setAmetysObject(object); 241 } 242 else if (contentMatcher.matches()) 243 { 244 // Legacy: handle content ID. 245 info.setPath(contentMatcher.group(1)); 246 String objectId = contentMatcher.group(2); 247 248 Content object = _resolver.resolveById(objectId); 249 info.setAmetysObject(object); 250 } 251 else 252 { 253 // URI without object ID, take the content in the request attributes. 254 info.setPath(uri); 255 info.setAmetysObject((Content) request.getAttribute(Content.class.getName())); 256 } 257 258 return info; 259 } 260 261 /** 262 * Attribute information. 263 */ 264 protected class AttributeInfo 265 { 266 private String _path; 267 private ModelAwareDataAwareAmetysObject _ametysObject; 268 269 /** 270 * Get the attribute path. 271 * @return the attribute path 272 */ 273 public String getPath() 274 { 275 return _path; 276 } 277 278 /** 279 * Set the attribute path. 280 * @param path the attribute path to set 281 */ 282 public void setPath(String path) 283 { 284 _path = path; 285 } 286 287 /** 288 * Get the ametys object. 289 * @return the ametys object 290 */ 291 public ModelAwareDataAwareAmetysObject getAmetysObject() 292 { 293 return _ametysObject; 294 } 295 296 /** 297 * Set the ametys object. 298 * @param ametysObject the {@link AmetysObject} to set 299 */ 300 public void setAmetysObject(ModelAwareDataAwareAmetysObject ametysObject) 301 { 302 _ametysObject = ametysObject; 303 } 304 } 305}