001/* 002 * (C) Copyright 2006-2016 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 */ 017package org.nuxeo.ecm.platform.preview.adapter.base; 018 019import java.io.File; 020import java.io.IOException; 021import java.io.InputStream; 022import java.util.Collections; 023import java.util.List; 024 025import org.apache.commons.codec.digest.DigestUtils; 026import org.apache.logging.log4j.LogManager; 027import org.apache.logging.log4j.Logger; 028import org.nuxeo.ecm.core.api.Blob; 029import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 030import org.nuxeo.ecm.core.api.blobholder.DocumentBlobHolder; 031import org.nuxeo.ecm.core.api.model.PropertyNotFoundException; 032import org.nuxeo.ecm.core.convert.api.ConversionException; 033import org.nuxeo.ecm.core.convert.api.ConversionService; 034import org.nuxeo.ecm.platform.mimetype.MimetypeDetectionException; 035import org.nuxeo.ecm.platform.mimetype.MimetypeNotFoundException; 036import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry; 037import org.nuxeo.ecm.platform.preview.adapter.MimeTypePreviewer; 038import org.nuxeo.ecm.platform.preview.adapter.PreviewAdapterManager; 039import org.nuxeo.ecm.platform.preview.api.NothingToPreviewException; 040import org.nuxeo.ecm.platform.preview.api.PreviewException; 041import org.nuxeo.runtime.api.Framework; 042import org.nuxeo.runtime.services.config.ConfigurationService; 043 044/** 045 * Base class for preview based on "on the fly" HTML transformers 046 * 047 * @author tiry 048 */ 049public class ConverterBasedHtmlPreviewAdapter extends AbstractHtmlPreviewAdapter { 050 051 private static final Logger log = LogManager.getLogger(ConverterBasedHtmlPreviewAdapter.class); 052 053 protected String defaultFieldXPath; 054 055 /** 056 * @deprecated since 11.1. Use {@link Framework#getService(Class)} with {@link MimetypeRegistry} instead. 057 */ 058 @Deprecated 059 protected MimetypeRegistry mimeTypeService; 060 061 /** 062 * @since 8.10 063 */ 064 protected static final String ALLOW_ZIP_PREVIEW = "nuxeo.preview.zip.enabled"; 065 066 public ConversionService getConversionService() { 067 return Framework.getService(ConversionService.class); 068 } 069 070 protected static String getMimeType(Blob blob) { 071 if (blob == null) { 072 return null; 073 } 074 075 String srcMT = blob.getMimeType(); 076 if (srcMT == null || srcMT.startsWith("application/octet-stream")) { 077 // call MT Service 078 try { 079 MimetypeRegistry mtr = Framework.getService(MimetypeRegistry.class); 080 srcMT = mtr.getMimetypeFromFilenameAndBlobWithDefault(blob.getFilename(), blob, 081 "application/octet-stream"); 082 log.debug("mime type service returned " + srcMT); 083 } catch (MimetypeDetectionException e) { 084 log.warn("error while calling Mimetype service", e); 085 } 086 } 087 return srcMT; 088 } 089 090 protected String getMimeType(String xpath) { 091 BlobHolder blobHolder2preview = getBlobHolder2preview(xpath); 092 Blob blob = getBlob2preview(blobHolder2preview); 093 return getMimeType(blob); 094 } 095 096 protected String getDefaultPreviewFieldXPath() { 097 return defaultFieldXPath; 098 } 099 100 public void setDefaultPreviewFieldXPath(String xPath) { 101 defaultFieldXPath = xPath; 102 } 103 104 @Override 105 public List<Blob> getPreviewBlobs() throws PreviewException { 106 return getPreviewBlobs(getDefaultPreviewFieldXPath()); 107 } 108 109 @Override 110 public boolean hasPreview(String xpath) { 111 String srcMT; 112 try { 113 srcMT = getMimeType(xpath); 114 } catch (NothingToPreviewException e) { 115 return false; 116 } 117 if ("application/zip".equals(srcMT) 118 && Framework.getService(ConfigurationService.class).isBooleanFalse(ALLOW_ZIP_PREVIEW)) { 119 return false; 120 } 121 MimeTypePreviewer mtPreviewer = getPreviewManager().getPreviewer(srcMT); 122 return mtPreviewer != null || getConversionService().getConverterName(srcMT, "text/html") != null; 123 } 124 125 @Override 126 public List<Blob> getPreviewBlobs(String xpath) throws PreviewException { 127 BlobHolder blobHolder2preview = getBlobHolder2preview(xpath); 128 Blob blob2Preview; 129 try { 130 blob2Preview = getBlob2preview(blobHolder2preview); 131 } catch (NothingToPreviewException e) { 132 return Collections.emptyList(); 133 } 134 135 String srcMT = getMimeType(xpath); 136 log.debug("Source type for HTML preview =" + srcMT); 137 MimeTypePreviewer mtPreviewer = getPreviewManager().getPreviewer(srcMT); 138 if (mtPreviewer != null) { 139 List<Blob> result = mtPreviewer.getPreview(blob2Preview, adaptedDoc); 140 if (result != null) { 141 return result; 142 } 143 } 144 145 String converterName = getConversionService().getConverterName(srcMT, "text/html"); 146 if (converterName == null) { 147 log.debug("No dedicated converter found, using generic"); 148 converterName = "any2html"; 149 } 150 151 BlobHolder result; 152 try { 153 result = getConversionService().convert(converterName, blobHolder2preview, null); 154 setMimeType(result); 155 setDigest(result); 156 return result.getBlobs(); 157 } catch (ConversionException e) { 158 throw new PreviewException(e.getMessage(), e); 159 } 160 } 161 162 /** 163 * @since 5.7.3 164 */ 165 private Blob getBlob2preview(BlobHolder blobHolder2preview) throws PreviewException { 166 Blob blob2Preview; 167 try { 168 blob2Preview = blobHolder2preview.getBlob(); 169 } catch (PropertyNotFoundException e) { 170 blob2Preview = null; 171 } 172 if (blob2Preview == null) { 173 throw new NothingToPreviewException("Can not preview a document without blob"); 174 } else { 175 return blob2Preview; 176 } 177 } 178 179 /** 180 * Returns a blob holder suitable for a preview. 181 * 182 * @since 5.7.3 183 */ 184 private BlobHolder getBlobHolder2preview(String xpath) { 185 if ((xpath == null) || ("default".equals(xpath))) { 186 return adaptedDoc.getAdapter(BlobHolder.class); 187 } else { 188 return new DocumentBlobHolder(adaptedDoc, xpath); 189 } 190 } 191 192 protected void setMimeType(BlobHolder result) { 193 for (Blob blob : result.getBlobs()) { 194 if ((blob.getMimeType() == null || blob.getMimeType().startsWith("application/octet-stream")) 195 && blob.getFilename().endsWith("html")) { 196 String mimeTpye = getMimeType(blob); 197 blob.setMimeType(mimeTpye); 198 } 199 } 200 } 201 202 protected void setDigest(BlobHolder result) { 203 for (Blob blob : result.getBlobs()) { 204 if (blob.getDigest() == null) { 205 try (InputStream stream = blob.getStream()) { 206 String digest = DigestUtils.md5Hex(stream); 207 blob.setDigest(digest); 208 } catch (IOException e) { 209 log.warn("Unable to compute digest of blob.", e); 210 } 211 } 212 } 213 } 214 215 public String getMimeType(File file) throws ConversionException { 216 try { 217 return Framework.getService(MimetypeRegistry.class).getMimetypeFromFile(file); 218 } catch (ConversionException e) { 219 throw new ConversionException("Could not get MimeTypeRegistry"); 220 } catch (MimetypeNotFoundException | MimetypeDetectionException e) { 221 return "application/octet-stream"; 222 } 223 } 224 225 /** 226 * @deprecated since 11.1. Use {@link Framework#getService(Class)} with {@link MimetypeRegistry} instead. 227 */ 228 @Deprecated 229 public MimetypeRegistry getMimeTypeService() throws ConversionException { 230 if (mimeTypeService == null) { 231 mimeTypeService = Framework.getService(MimetypeRegistry.class); 232 } 233 return mimeTypeService; 234 } 235 236 @Override 237 public void cleanup() { 238 239 } 240 241 @Override 242 public boolean cachable() { 243 return true; 244 } 245 246 @Override 247 public boolean hasBlobToPreview() throws PreviewException { 248 String xpath = getDefaultPreviewFieldXPath(); 249 Blob blob2Preview; 250 try { 251 blob2Preview = getBlob2preview(getBlobHolder2preview(xpath)); 252 } catch (NothingToPreviewException e) { 253 return false; 254 } 255 String srcMT = getMimeType(xpath); 256 if ("application/zip".equals(srcMT) 257 && Framework.getService(ConfigurationService.class).isBooleanFalse(ALLOW_ZIP_PREVIEW)) { 258 return false; 259 } 260 return blob2Preview != null; 261 } 262 263}