001/* 002 * (C) Copyright 2006-2012 Nuxeo SA (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 * 014 * Contributors: 015 * Antoine Taillefer 016 */ 017 018package org.nuxeo.ecm.diff.content; 019 020import java.io.Serializable; 021import java.util.ArrayList; 022import java.util.LinkedHashMap; 023import java.util.List; 024import java.util.Map; 025 026import org.apache.commons.lang.StringUtils; 027import org.nuxeo.common.utils.URIUtils; 028import org.nuxeo.ecm.core.api.Blob; 029import org.nuxeo.ecm.core.api.DocumentLocation; 030import org.nuxeo.ecm.core.api.DocumentModel; 031import org.nuxeo.ecm.core.api.NuxeoException; 032import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 033import org.nuxeo.ecm.core.api.blobholder.DocumentBlobHolder; 034import org.nuxeo.ecm.core.api.blobholder.DocumentStringBlobHolder; 035import org.nuxeo.ecm.core.api.impl.DocumentLocationImpl; 036import org.nuxeo.ecm.diff.content.adapter.ContentDiffAdapterManager; 037import org.nuxeo.ecm.diff.content.adapter.MimeTypeContentDiffer; 038import org.nuxeo.ecm.platform.ui.web.rest.RestHelper; 039import org.nuxeo.ecm.platform.ui.web.rest.api.URLPolicyService; 040import org.nuxeo.ecm.platform.url.DocumentViewImpl; 041import org.nuxeo.ecm.platform.url.api.DocumentView; 042import org.nuxeo.ecm.platform.web.common.vh.VirtualHostHelper; 043import org.nuxeo.runtime.api.Framework; 044 045/** 046 * Helper for content diff. 047 */ 048public final class ContentDiffHelper { 049 050 public static final String CONTENT_DIFF_FANCYBOX_VIEW = "content_diff_fancybox"; 051 052 public static final String LABEL_URL_PARAM_NAME = "label"; 053 054 public static final String XPATH_URL_PARAM_NAME = "xPath"; 055 056 public static final String CONVERSION_TYPE_URL_PARAM_NAME = "conversionType"; 057 058 public static final String LOCALE_URL_PARAM_NAME = "locale"; 059 060 public static final String CONTENT_DIFF_URL_PREFIX = "restAPI/contentDiff/"; 061 062 public static final String DEFAULT_XPATH = "default"; 063 064 /** 065 * Final class constructor. 066 */ 067 private ContentDiffHelper() { 068 } 069 070 /** 071 * Gets the content diff fancy box URL. 072 * 073 * @param currentDoc the current doc 074 * @param propertyLabel the property label 075 * @param propertyXPath the property xpath 076 * @param conversionType the conversion type 077 * @return the content diff fancy box URL 078 */ 079 public static String getContentDiffFancyBoxURL(DocumentModel currentDoc, String propertyLabel, 080 String propertyXPath, String conversionType) { 081 DocumentLocation docLocation = new DocumentLocationImpl(currentDoc.getRepositoryName(), currentDoc.getRef()); 082 DocumentView docView = new DocumentViewImpl(docLocation, CONTENT_DIFF_FANCYBOX_VIEW); 083 docView.setPatternName("id"); 084 URLPolicyService urlPolicyService = Framework.getLocalService(URLPolicyService.class); 085 String docUrl = urlPolicyService.getUrlFromDocumentView(docView, VirtualHostHelper.getContextPathProperty()); 086 if (docUrl == null) { 087 throw new NuxeoException( 088 "Cannot get URL from document view, probably because of a missing urlPattern contribution."); 089 } 090 Map<String, String> requestParams = new LinkedHashMap<>(); 091 requestParams.put(LABEL_URL_PARAM_NAME, propertyLabel); 092 requestParams.put(XPATH_URL_PARAM_NAME, propertyXPath); 093 if (!StringUtils.isEmpty(conversionType)) { 094 requestParams.put(CONVERSION_TYPE_URL_PARAM_NAME, conversionType); 095 } 096 docUrl = URIUtils.addParametersToURIQuery(docUrl, requestParams); 097 return RestHelper.addCurrentConversationParameters(docUrl); 098 } 099 100 /** 101 * Gets the content diff URL. 102 * 103 * @param leftDoc the left doc 104 * @param rightDoc the right doc 105 * @param conversionType the conversion type 106 * @param locale the locale 107 * @return the content diff URL 108 */ 109 public static String getContentDiffURL(DocumentModel leftDoc, DocumentModel rightDoc, String conversionType, 110 String locale) { 111 112 return getContentDiffURL(leftDoc.getRepositoryName(), leftDoc, rightDoc, DEFAULT_XPATH, conversionType, locale); 113 } 114 115 /** 116 * Gets the content diff URL. 117 * 118 * @param leftDoc the left doc 119 * @param rightDoc the right doc 120 * @param propertyXPath the property xpath 121 * @param conversionType the conversion type 122 * @param locale the locale 123 * @return the content diff URL 124 */ 125 public static String getContentDiffURL(DocumentModel leftDoc, DocumentModel rightDoc, String propertyXPath, 126 String conversionType, String locale) { 127 128 return getContentDiffURL(leftDoc.getRepositoryName(), leftDoc, rightDoc, propertyXPath, conversionType, locale); 129 } 130 131 /** 132 * Gets the content diff URL. 133 * 134 * @param repositoryName the repository name 135 * @param leftDoc the left doc 136 * @param rightDoc the right doc 137 * @param propertyXPath the xpath 138 * @param conversionType the conversion type 139 * @param locale the locale 140 * @return the content diff URL 141 */ 142 public static String getContentDiffURL(String repositoryName, DocumentModel leftDoc, DocumentModel rightDoc, 143 String propertyXPath, String conversionType, String locale) { 144 145 if (propertyXPath == null) { 146 propertyXPath = DEFAULT_XPATH; 147 } 148 149 StringBuilder sb = new StringBuilder(); 150 151 sb.append(CONTENT_DIFF_URL_PREFIX); 152 sb.append(repositoryName); 153 sb.append("/"); 154 sb.append(leftDoc.getId()); 155 sb.append("/"); 156 sb.append(rightDoc.getId()); 157 sb.append("/"); 158 sb.append(propertyXPath); 159 sb.append("/"); 160 boolean isQueryParam = false; 161 if (!StringUtils.isEmpty(conversionType)) { 162 sb.append("?"); 163 sb.append(CONVERSION_TYPE_URL_PARAM_NAME); 164 sb.append("="); 165 sb.append(conversionType); 166 isQueryParam = true; 167 } 168 if (!StringUtils.isEmpty(locale)) { 169 sb.append(isQueryParam ? "&" : "?"); 170 sb.append(LOCALE_URL_PARAM_NAME); 171 sb.append("="); 172 sb.append(locale); 173 isQueryParam = true; 174 } 175 176 return sb.toString(); 177 } 178 179 /** 180 * Checks if the HTML conversion content diff is relevant for the specified property. 181 */ 182 public static boolean isDisplayHtmlConversion(Serializable property) { 183 184 // Always relevant except for the blacklisted mime types 185 if (isContentProperty(property)) { 186 Blob blob = (Blob) property; 187 String mimeType = blob.getMimeType(); 188 if (getHtmlConversionBlackListedMimeTypes().contains(mimeType)) { 189 return false; 190 } 191 } 192 return true; 193 } 194 195 /** 196 * Checks if the text conversion content diff is relevant for the specified property. 197 */ 198 public static boolean isDisplayTextConversion(Serializable property) { 199 200 // Must be a content property 201 if (!isContentProperty(property)) { 202 return false; 203 } 204 // Not relevant for the mime types associated to a content differ (see 205 // the mimeTypeContentDiffer extension point) 206 Blob blob = (Blob) property; 207 String mimeType = blob.getMimeType(); 208 209 ContentDiffAdapterManager contentDiffAdapterManager = Framework.getLocalService(ContentDiffAdapterManager.class); 210 MimeTypeContentDiffer mimeTypeContentDiffer = contentDiffAdapterManager.getContentDiffer(mimeType); 211 212 if (mimeTypeContentDiffer != null) { 213 return false; 214 } 215 return true; 216 } 217 218 /** 219 * Checks if the specified property is a content property, ie. {@code instanceof Blob}. 220 */ 221 public static boolean isContentProperty(Serializable property) { 222 return property instanceof Blob; 223 } 224 225 /** 226 * Gets the list of blacklisted mime types for HTML conversion. 227 * <p> 228 * For now: 229 * <ul> 230 * <li>PDF</li> 231 * <li>Office spreadsheet mime types</li> 232 * <li>Office presentation mime types</li> 233 * </ul> 234 * </p> 235 * 236 * @see https://jira.nuxeo.com/browse/NXP-9421 237 * @see https://jira.nuxeo.com/browse/NXP-9431 238 */ 239 protected static List<String> getHtmlConversionBlackListedMimeTypes() { 240 241 List<String> blackListedMimeTypes = new ArrayList<String>(); 242 243 // PDF 244 blackListedMimeTypes.add("application/pdf"); 245 246 // Office spreadsheet 247 blackListedMimeTypes.add("application/vnd.ms-excel"); 248 blackListedMimeTypes.add("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); 249 blackListedMimeTypes.add("application/vnd.sun.xml.calc"); 250 blackListedMimeTypes.add("application/vnd.sun.xml.calc.template"); 251 blackListedMimeTypes.add("application/vnd.oasis.opendocument.spreadsheet"); 252 blackListedMimeTypes.add("application/vnd.oasis.opendocument.spreadsheet-template"); 253 254 // Office presentation 255 blackListedMimeTypes.add("application/vnd.ms-powerpoint"); 256 blackListedMimeTypes.add("application/vnd.openxmlformats-officedocument.presentationml.presentation"); 257 blackListedMimeTypes.add("application/vnd.sun.xml.impress"); 258 blackListedMimeTypes.add("application/vnd.sun.xml.impress.template"); 259 blackListedMimeTypes.add("application/vnd.oasis.opendocument.presentation"); 260 blackListedMimeTypes.add("application/vnd.oasis.opendocument.presentation-template"); 261 262 return blackListedMimeTypes; 263 } 264 265 public static BlobHolder getBlobHolder(DocumentModel doc, String xPath) throws ContentDiffException { 266 // TODO: manage other property types than Blob / String? 267 Serializable prop = doc.getPropertyValue(xPath); 268 if (prop instanceof Blob) { 269 return new DocumentBlobHolder(doc, xPath); 270 } 271 if (prop instanceof String) { 272 // Default mime type is text/plain. For a Note, use the 273 // "note:mime_type" property, otherwise if the property value is 274 // HTML use text/html. 275 String mimeType = "text/plain"; 276 if ("note:note".equals(xPath)) { 277 mimeType = (String) doc.getPropertyValue("note:mime_type"); 278 } else { 279 if (HtmlGuesser.isHtml((String) prop)) { 280 mimeType = "text/html"; 281 } 282 } 283 return new DocumentStringBlobHolder(doc, xPath, mimeType); 284 } 285 throw new ContentDiffException(String.format("Cannot get BlobHolder for doc '%s' and xpath '%s'.", 286 doc.getTitle(), xPath)); 287 } 288}