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