001/* 002 * Copyright 2017 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.linkdirectory; 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.transformation.AbstractURIResolver; 036import org.ametys.cms.transformation.ConsistencyChecker.CHECK; 037import org.ametys.cms.transformation.URIResolver; 038import org.ametys.core.util.FilenameUtils; 039import org.ametys.core.util.ImageResolverHelper; 040import org.ametys.core.util.URIUtils; 041import org.ametys.plugins.linkdirectory.repository.DefaultLink; 042import org.ametys.plugins.repository.AmetysObjectResolver; 043import org.ametys.plugins.repository.UnknownAmetysObjectException; 044import org.ametys.runtime.i18n.I18nizableText; 045import org.ametys.runtime.plugin.component.PluginAware; 046import org.ametys.web.URIPrefixHandler; 047import org.ametys.web.renderingcontext.RenderingContext; 048import org.ametys.web.renderingcontext.RenderingContextHandler; 049import org.ametys.web.repository.site.Site; 050import org.ametys.web.repository.site.SiteManager; 051 052/** 053 * {@link URIResolver} for type "link-data".<br> 054 * These links or images point to a file from the data of a {@link Link}. 055 */ 056public class LinkMetadataURIResolver extends AbstractURIResolver implements Serviceable, Contextualizable, PluginAware 057{ 058 private static final Pattern _OBJECT_URI_PATTERN = Pattern.compile("([^?]*)\\?objectId=(.*)"); 059 060 private AmetysObjectResolver _resolver; 061 private URIPrefixHandler _prefixHandler; 062 private RenderingContextHandler _renderingContexthandler; 063 private SiteManager _siteManager; 064 065 /** The context */ 066 private Context _context; 067 068 private String _pluginName; 069 070 public void setPluginInfo(String pluginName, String featureName, String id) 071 { 072 _pluginName = pluginName; 073 } 074 075 @Override 076 public void contextualize(Context context) throws ContextException 077 { 078 _context = context; 079 } 080 081 @Override 082 public void service(ServiceManager manager) throws ServiceException 083 { 084 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 085 _prefixHandler = (URIPrefixHandler) manager.lookup(URIPrefixHandler.ROLE); 086 _renderingContexthandler = (RenderingContextHandler) manager.lookup(RenderingContextHandler.ROLE); 087 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 088 } 089 090 @Override 091 public String getType() 092 { 093 return "link-data"; 094 } 095 096 @Override 097 protected String _resolve(String uri, String uriArgument, boolean download, boolean absolute, boolean internal) 098 { 099 try 100 { 101 Request request = ContextHelper.getRequest(_context); 102 103 Info info = _getInfo(uri, request); 104 105 DefaultLink link = info.getLink(); 106 String dataPath = info.getDataPath(); 107 108 if (link == null) 109 { 110 throw new IllegalStateException("Cannot resolve a local link to an unknown link for uri " + request.getRequestURI()); 111 } 112 113 Binary binary = link.getValue(dataPath); 114 115 String filename = FilenameUtils.encodeName(binary.getFilename()); 116 117 String baseName = org.apache.commons.io.FilenameUtils.getBaseName(filename); 118 String extension = org.apache.commons.io.FilenameUtils.getExtension(filename); 119 120 StringBuilder resultPath = new StringBuilder(); 121 122 resultPath.append("/_plugins/") 123 .append(_pluginName) 124 .append("/_links") 125 .append(FilenameUtils.encodePath(link.getPath())) 126 .append("/_data/") 127 .append(dataPath) 128 .append("/") 129 .append(baseName) 130 .append(uriArgument) 131 .append(extension.isEmpty() ? "" : "." + extension); 132 133 String result = _getUri(resultPath.toString(), link, absolute, internal); 134 135 Map<String, String> params = new HashMap<>(); 136 params.put("objectId", link.getId()); 137 if (download) 138 { 139 params.put("download", "true"); 140 } 141 142 return URIUtils.encodeURI(result, params); 143 } 144 catch (Exception e) 145 { 146 throw new IllegalStateException(e); 147 } 148 } 149 150 @Override 151 protected String resolveImageAsBase64(String uri, int height, int width, int maxHeight, int maxWidth, int cropHeight, int cropWidth) 152 { 153 try 154 { 155 Request request = ContextHelper.getRequest(_context); 156 157 Info info = _getInfo(uri, request); 158 159 DefaultLink link = info.getLink(); 160 String dataPath = info.getDataPath(); 161 162 if (link == null) 163 { 164 throw new IllegalStateException("Cannot resolve a local link to an unknown link for uri " + request.getRequestURI()); 165 } 166 167 Binary binary = link.getValue(dataPath); 168 169 try (InputStream dataIs = binary.getInputStream()) 170 { 171 return ImageResolverHelper.resolveImageAsBase64(dataIs, binary.getMimeType(), height, width, maxHeight, maxWidth, cropHeight, cropWidth); 172 } 173 } 174 catch (Exception e) 175 { 176 throw new IllegalStateException(e); 177 } 178 } 179 180 public String getMimeType(String uri) 181 { 182 try 183 { 184 Request request = ContextHelper.getRequest(_context); 185 186 Info info = _getInfo(uri, request); 187 188 DefaultLink link = info.getLink(); 189 String dataPath = info.getDataPath(); 190 191 if (link == null) 192 { 193 throw new IllegalStateException("Cannot resolve a local link to an unknown link for uri " + request.getRequestURI()); 194 } 195 196 Binary binary = link.getValue(dataPath); 197 198 return binary.getMimeType(); 199 } 200 catch (Exception e) 201 { 202 throw new IllegalStateException(e); 203 } 204 } 205 206 private String _getUri(String path, DefaultLink object, boolean absolute, boolean internal) 207 { 208 String siteName = object.getSiteName(); 209 210 if (internal) 211 { 212 return "cocoon://" + siteName + path; 213 } 214 else if (absolute) 215 { 216 if (_renderingContexthandler.getRenderingContext() == RenderingContext.FRONT) 217 { 218 Site site = _siteManager.getSite(siteName); 219 220 String[] aliases = site.getUrlAliases(); 221 return aliases[Math.abs(path.hashCode()) % aliases.length] + path; 222 } 223 224 return _prefixHandler.getAbsoluteUriPrefix() + "/" + siteName + path; 225 } 226 else 227 { 228 return _prefixHandler.getUriPrefix(siteName) + path; 229 } 230 } 231 232 @Override 233 public CHECK checkLink(String uri, boolean shortTest) 234 { 235 return CHECK.SUCCESS; 236 } 237 238 @Override 239 public I18nizableText getLabel(String uri) 240 { 241 try 242 { 243 Request request = ContextHelper.getRequest(_context); 244 Info info = _getInfo(uri, request); 245 return new I18nizableText("plugin.cms", "PLUGINS_CMS_LINK_METADATA_LABEL", Collections.singletonList(info.getDataPath())); 246 } 247 catch (UnknownAmetysObjectException e) 248 { 249 return new I18nizableText("plugin.cms", "PLUGINS_CMS_LINK_METADATA_UNKNOWN"); 250 } 251 } 252 253 private Info _getInfo(String uri, Request request) 254 { 255 Info info = new Info(); 256 257 Matcher matcher = _OBJECT_URI_PATTERN.matcher(uri); 258 259 // Test if the URI contains an object ID. 260 if (matcher.matches()) 261 { 262 info.setDataPath(matcher.group(1)); 263 String objectId = matcher.group(2); 264 265 DefaultLink link = _resolver.resolveById(objectId); 266 info.setLink(link); 267 } 268 else 269 { 270 throw new IllegalStateException("Missing objectId parameter to resolve a local link for uri " + request.getRequestURI()); 271 } 272 273 return info; 274 } 275 276 /** 277 * data information. 278 */ 279 protected class Info 280 { 281 private String _dataPath; 282 private DefaultLink _link; 283 284 /** 285 * Get the data path. 286 * @return the data path. 287 */ 288 public String getDataPath() 289 { 290 return _dataPath; 291 } 292 293 /** 294 * Set the data path. 295 * @param path the data path to set 296 */ 297 public void setDataPath(String path) 298 { 299 _dataPath = path; 300 } 301 302 /** 303 * Get the link. 304 * @return the link 305 */ 306 public DefaultLink getLink() 307 { 308 return _link; 309 } 310 311 /** 312 * Set the link. 313 * @param link the link to set 314 */ 315 public void setLink(DefaultLink link) 316 { 317 _link = link; 318 } 319 } 320}