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.ImageResolverHelper; 038import org.ametys.cms.transformation.URIResolver; 039import org.ametys.core.util.FilenameUtils; 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 private String _getUri(String path, DefaultLink object, boolean absolute, boolean internal) 181 { 182 String siteName = object.getSiteName(); 183 184 if (internal) 185 { 186 return "cocoon://" + siteName + path; 187 } 188 else if (absolute) 189 { 190 if (_renderingContexthandler.getRenderingContext() == RenderingContext.FRONT) 191 { 192 Site site = _siteManager.getSite(siteName); 193 194 String[] aliases = site.getUrlAliases(); 195 return aliases[Math.abs(path.hashCode()) % aliases.length] + path; 196 } 197 198 return _prefixHandler.getAbsoluteUriPrefix() + "/" + siteName + path; 199 } 200 else 201 { 202 return _prefixHandler.getUriPrefix(siteName) + path; 203 } 204 } 205 206 @Override 207 public CHECK checkLink(String uri, boolean shortTest) 208 { 209 return CHECK.SUCCESS; 210 } 211 212 @Override 213 public I18nizableText getLabel(String uri) 214 { 215 try 216 { 217 Request request = ContextHelper.getRequest(_context); 218 Info info = _getInfo(uri, request); 219 return new I18nizableText("plugin.cms", "PLUGINS_CMS_LINK_METADATA_LABEL", Collections.singletonList(info.getDataPath())); 220 } 221 catch (UnknownAmetysObjectException e) 222 { 223 return new I18nizableText("plugin.cms", "PLUGINS_CMS_LINK_METADATA_UNKNOWN"); 224 } 225 } 226 227 private Info _getInfo(String uri, Request request) 228 { 229 Info info = new Info(); 230 231 Matcher matcher = _OBJECT_URI_PATTERN.matcher(uri); 232 233 // Test if the URI contains an object ID. 234 if (matcher.matches()) 235 { 236 info.setDataPath(matcher.group(1)); 237 String objectId = matcher.group(2); 238 239 DefaultLink link = _resolver.resolveById(objectId); 240 info.setLink(link); 241 } 242 else 243 { 244 throw new IllegalStateException("Missing objectId parameter to resolve a local link for uri " + request.getRequestURI()); 245 } 246 247 return info; 248 } 249 250 /** 251 * data information. 252 */ 253 protected class Info 254 { 255 private String _dataPath; 256 private DefaultLink _link; 257 258 /** 259 * Get the data path. 260 * @return the data path. 261 */ 262 public String getDataPath() 263 { 264 return _dataPath; 265 } 266 267 /** 268 * Set the data path. 269 * @param path the data path to set 270 */ 271 public void setDataPath(String path) 272 { 273 _dataPath = path; 274 } 275 276 /** 277 * Get the link. 278 * @return the link 279 */ 280 public DefaultLink getLink() 281 { 282 return _link; 283 } 284 285 /** 286 * Set the link. 287 * @param link the link to set 288 */ 289 public void setLink(DefaultLink link) 290 { 291 _link = link; 292 } 293 } 294}