001/* 002 * (C) Copyright 2006-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 * 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.lang.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, 082 String propertyXPath, 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.getLocalService(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. 183 */ 184 public static boolean isDisplayHtmlConversion(Serializable property) { 185 186 // Always relevant except for the blacklisted mime types 187 if (isContentProperty(property)) { 188 Blob blob = (Blob) property; 189 String mimeType = blob.getMimeType(); 190 if (getHtmlConversionBlackListedMimeTypes().contains(mimeType)) { 191 return false; 192 } 193 } 194 return true; 195 } 196 197 /** 198 * Checks if the text conversion content diff is relevant for the specified property. 199 */ 200 public static boolean isDisplayTextConversion(Serializable property) { 201 202 // Must be a content property 203 if (!isContentProperty(property)) { 204 return false; 205 } 206 // Not relevant for the mime types associated to a content differ (see 207 // the mimeTypeContentDiffer extension point) 208 Blob blob = (Blob) property; 209 String mimeType = blob.getMimeType(); 210 211 ContentDiffAdapterManager contentDiffAdapterManager = Framework.getLocalService(ContentDiffAdapterManager.class); 212 MimeTypeContentDiffer mimeTypeContentDiffer = contentDiffAdapterManager.getContentDiffer(mimeType); 213 214 if (mimeTypeContentDiffer != null) { 215 return false; 216 } 217 return true; 218 } 219 220 /** 221 * Checks if the specified property is a content property, ie. {@code instanceof Blob}. 222 */ 223 public static boolean isContentProperty(Serializable property) { 224 return property instanceof Blob; 225 } 226 227 /** 228 * Gets the list of blacklisted mime types for HTML conversion. 229 * <p> 230 * For now: 231 * <ul> 232 * <li>PDF</li> 233 * <li>Office spreadsheet mime types</li> 234 * <li>Office presentation mime types</li> 235 * </ul> 236 * </p> 237 * 238 * @see https://jira.nuxeo.com/browse/NXP-9421 239 * @see https://jira.nuxeo.com/browse/NXP-9431 240 */ 241 protected static List<String> getHtmlConversionBlackListedMimeTypes() { 242 243 List<String> blackListedMimeTypes = new ArrayList<String>(); 244 245 // PDF 246 blackListedMimeTypes.add("application/pdf"); 247 248 // Office spreadsheet 249 blackListedMimeTypes.add("application/vnd.ms-excel"); 250 blackListedMimeTypes.add("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); 251 blackListedMimeTypes.add("application/vnd.sun.xml.calc"); 252 blackListedMimeTypes.add("application/vnd.sun.xml.calc.template"); 253 blackListedMimeTypes.add("application/vnd.oasis.opendocument.spreadsheet"); 254 blackListedMimeTypes.add("application/vnd.oasis.opendocument.spreadsheet-template"); 255 256 // Office presentation 257 blackListedMimeTypes.add("application/vnd.ms-powerpoint"); 258 blackListedMimeTypes.add("application/vnd.openxmlformats-officedocument.presentationml.presentation"); 259 blackListedMimeTypes.add("application/vnd.sun.xml.impress"); 260 blackListedMimeTypes.add("application/vnd.sun.xml.impress.template"); 261 blackListedMimeTypes.add("application/vnd.oasis.opendocument.presentation"); 262 blackListedMimeTypes.add("application/vnd.oasis.opendocument.presentation-template"); 263 264 return blackListedMimeTypes; 265 } 266 267 public static BlobHolder getBlobHolder(DocumentModel doc, String xPath) throws ContentDiffException { 268 // TODO: manage other property types than Blob / String? 269 Serializable prop = doc.getPropertyValue(xPath); 270 if (prop instanceof Blob) { 271 return new DocumentBlobHolder(doc, xPath); 272 } 273 if (prop instanceof String) { 274 // Default mime type is text/plain. For a Note, use the 275 // "note:mime_type" property, otherwise if the property value is 276 // HTML use text/html. 277 String mimeType = "text/plain"; 278 if ("note:note".equals(xPath)) { 279 mimeType = (String) doc.getPropertyValue("note:mime_type"); 280 } else { 281 if (HtmlGuesser.isHtml((String) prop)) { 282 mimeType = "text/html"; 283 } 284 } 285 return new DocumentStringBlobHolder(doc, xPath, mimeType); 286 } 287 throw new ContentDiffException(String.format("Cannot get BlobHolder for doc '%s' and xpath '%s'.", 288 doc.getTitle(), xPath)); 289 } 290}