001/* 002 * (C) Copyright 2012 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * Contributors: 014 * Nuxeo - initial API and implementation 015 */ 016 017package org.nuxeo.ecm.platform.rendition.url; 018 019import java.util.ArrayList; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023import java.util.regex.Matcher; 024import java.util.regex.Pattern; 025 026import org.apache.commons.lang.StringUtils; 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029import org.nuxeo.common.utils.URIUtils; 030import org.nuxeo.ecm.core.api.DocumentLocation; 031import org.nuxeo.ecm.core.api.DocumentModel; 032import org.nuxeo.ecm.core.api.DocumentRef; 033import org.nuxeo.ecm.core.api.IdRef; 034import org.nuxeo.ecm.core.api.PathRef; 035import org.nuxeo.ecm.core.api.impl.DocumentLocationImpl; 036import org.nuxeo.ecm.platform.url.DocumentViewImpl; 037import org.nuxeo.ecm.platform.url.api.DocumentView; 038import org.nuxeo.ecm.platform.url.service.AbstractDocumentViewCodec; 039 040/** 041 * Base class for Rendition url codec. 042 * <p> 043 * This class is shared with Template rendering system. 044 * <p> 045 * Codec handling a document repository, id, view and additional request parameters. View is used to represent the 046 * Rendition name. 047 * <p> 048 * This codec supports both path abd id based urls. 049 * 050 * @since 5.6 051 * @author <a href="mailto:tdelprat@nuxeo.com">Tiry</a> 052 */ 053public class RenditionBasedCodec extends AbstractDocumentViewCodec { 054 055 protected static final Log log = LogFactory.getLog(DocumentRenditionCodec.class); 056 057 public static final int URL_MAX_LENGTH = 2000; 058 059 /** 060 * @since 6.0 061 */ 062 public static final String RENDITION_PARAM_NAME = "rendition"; 063 064 /** 065 * @since 6.0 066 */ 067 public static final String RENDITION_VIEW_ID = "rendition"; 068 069 public static final String PATH_URL_PATTERN = "/" // slash 070 + "([\\w\\.]+)" // server name (group 1) 071 + "(?:/(.*))?" // path (group 2) (optional) 072 + "@([\\w\\-\\.\\%]+)" // renditionName (group 3) 073 + "/?" // final slash (optional) 074 + "(?:\\?(.*)?)?"; 075 076 public static final String ID_URL_PATTERN = "/(\\w+)/([a-zA-Z_0-9\\-]+)(/([\\w\\-\\.\\%]+))?(/)?(\\?(.*)?)?"; 077 078 public static String getRenditionUrl(DocumentModel doc, String renditionName) { 079 DocumentView docView = new DocumentViewImpl(doc); 080 docView.setViewId(renditionName); 081 return new DocumentRenditionCodec().getUrlFromDocumentView(docView); 082 } 083 084 @Override 085 public DocumentView getDocumentViewFromUrl(String url) { 086 final Pattern pathPattern = Pattern.compile(getPrefix() + PATH_URL_PATTERN); 087 Matcher pathMatcher = pathPattern.matcher(url); 088 if (pathMatcher.matches()) { 089 090 final String server = pathMatcher.group(1); 091 String path = pathMatcher.group(2); 092 if (path != null) { 093 // add leading slash to make it absolute if it's not the root 094 path = "/" + URIUtils.unquoteURIPathComponent(path); 095 } else { 096 path = "/"; 097 } 098 final DocumentRef docRef = new PathRef(path); 099 100 final String renditionName = URIUtils.unquoteURIPathComponent(pathMatcher.group(3)); 101 102 // get other parameters 103 String query = pathMatcher.group(4); 104 Map<String, String> params = URIUtils.getRequestParameters(query); 105 if (params == null) { 106 params = new HashMap<String, String>(); 107 } 108 params.put(RENDITION_PARAM_NAME, renditionName); 109 final DocumentLocation docLoc = new DocumentLocationImpl(server, docRef); 110 return new DocumentViewImpl(docLoc, RENDITION_VIEW_ID, params); 111 } else { 112 final Pattern idPattern = Pattern.compile(getPrefix() + ID_URL_PATTERN); 113 Matcher idMatcher = idPattern.matcher(url); 114 if (idMatcher.matches()) { 115 if (idMatcher.groupCount() >= 4) { 116 117 final String server = idMatcher.group(1); 118 String uuid = idMatcher.group(2); 119 final DocumentRef docRef = new IdRef(uuid); 120 final String renditionName = URIUtils.unquoteURIPathComponent(idMatcher.group(4)); 121 122 // get other parameters 123 124 Map<String, String> params = null; 125 if (idMatcher.groupCount() > 6) { 126 String query = idMatcher.group(7); 127 params = URIUtils.getRequestParameters(query); 128 } 129 if (params == null) { 130 params = new HashMap<String, String>(); 131 } 132 params.put(RENDITION_PARAM_NAME, renditionName); 133 134 final DocumentLocation docLoc = new DocumentLocationImpl(server, docRef); 135 return new DocumentViewImpl(docLoc, RENDITION_VIEW_ID, params); 136 } 137 } 138 } 139 return null; 140 } 141 142 protected String getUrlFromDocumentViewWithId(DocumentView docView) { 143 DocumentLocation docLoc = docView.getDocumentLocation(); 144 if (docLoc != null) { 145 List<String> items = new ArrayList<String>(); 146 items.add(getPrefix()); 147 items.add(docLoc.getServerName()); 148 IdRef docRef = docLoc.getIdRef(); 149 if (docRef == null) { 150 return null; 151 } 152 items.add(docRef.toString()); 153 String renditionName = docView.getParameter(RENDITION_PARAM_NAME); 154 if (StringUtils.isBlank(renditionName)) { 155 // fall-back on view id 156 renditionName = docView.getViewId(); 157 } 158 if (renditionName != null) { 159 items.add(URIUtils.quoteURIPathComponent(renditionName, true)); 160 } 161 String uri = StringUtils.join(items, "/"); 162 Map<String, String> params = new HashMap<>(); 163 Map<String, String> dcparams = docView.getParameters(); 164 if (dcparams != null) { 165 params.putAll(dcparams); 166 } 167 if (params != null && params.containsKey(RENDITION_PARAM_NAME)) { 168 params.remove(RENDITION_PARAM_NAME); 169 } 170 return URIUtils.addParametersToURIQuery(uri, params); 171 } 172 return null; 173 } 174 175 @Override 176 public String getUrlFromDocumentView(DocumentView docView) { 177 178 // Use DocumentIdCodec if the document is a version 179 if ("true".equals(docView.getParameter("version"))) { 180 if (docView.getDocumentLocation().getIdRef() != null) { 181 return getUrlFromDocumentViewWithId(docView); 182 } 183 } 184 185 DocumentLocation docLoc = docView.getDocumentLocation(); 186 if (docLoc != null) { 187 List<String> items = new ArrayList<String>(); 188 items.add(getPrefix()); 189 items.add(docLoc.getServerName()); 190 PathRef docRef = docLoc.getPathRef(); 191 if (docRef == null) { 192 return null; 193 } 194 // this is a path, get rid of leading slash 195 String path = docRef.toString(); 196 if (path.startsWith("/")) { 197 path = path.substring(1); 198 } 199 if (path.length() > 0) { 200 items.add(URIUtils.quoteURIPathComponent(path, false)); 201 } 202 String uri = StringUtils.join(items, "/"); 203 String renditionName = docView.getParameter(RENDITION_PARAM_NAME); 204 if (StringUtils.isBlank(renditionName)) { 205 // fall-back on view id 206 renditionName = docView.getViewId(); 207 } 208 209 if (renditionName != null) { 210 uri += "@" + URIUtils.quoteURIPathComponent(renditionName, true); 211 } 212 213 Map<String, String> params = new HashMap<>(); 214 Map<String, String> dcparams = docView.getParameters(); 215 if (dcparams != null) { 216 params.putAll(dcparams); 217 } 218 if (dcparams != null && dcparams.containsKey(RENDITION_PARAM_NAME)) { 219 params.remove(RENDITION_PARAM_NAME); 220 } 221 String uriWithParam = URIUtils.addParametersToURIQuery(uri, params); 222 223 // If the URL with the Path codec is to long, it use the URL with 224 // the Id Codec. 225 if (uriWithParam.length() > URL_MAX_LENGTH) { 226 227 // If the DocumentLocation did not contains the document Id, it 228 // use the Path Codec even if the Url is too long for IE. 229 if (null == docView.getDocumentLocation().getIdRef()) { 230 log.error("The DocumentLocation did not contains the RefId."); 231 return uriWithParam; 232 } 233 234 return getUrlFromDocumentViewWithId(docView); 235 236 } else { 237 return uriWithParam; 238 } 239 } 240 return null; 241 } 242}