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