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