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