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