/* * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.nuxeo.ecm.platform.preview.adapter.base; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.List; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.core.api.Blob; import org.nuxeo.ecm.core.api.blobholder.BlobHolder; import org.nuxeo.ecm.core.api.blobholder.DocumentBlobHolder; import org.nuxeo.ecm.core.api.model.PropertyNotFoundException; import org.nuxeo.ecm.core.convert.api.ConversionException; import org.nuxeo.ecm.core.convert.api.ConversionService; import org.nuxeo.ecm.platform.mimetype.MimetypeDetectionException; import org.nuxeo.ecm.platform.mimetype.MimetypeNotFoundException; import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry; import org.nuxeo.ecm.platform.preview.adapter.MimeTypePreviewer; import org.nuxeo.ecm.platform.preview.adapter.PreviewAdapterManager; import org.nuxeo.ecm.platform.preview.api.NothingToPreviewException; import org.nuxeo.ecm.platform.preview.api.PreviewException; import org.nuxeo.runtime.api.Framework; import org.nuxeo.runtime.services.config.ConfigurationService; /** * Base class for preview based on "on the fly" HTML transformers * * @author tiry */ public class ConverterBasedHtmlPreviewAdapter extends AbstractHtmlPreviewAdapter { private static final Log log = LogFactory.getLog(ConverterBasedHtmlPreviewAdapter.class); protected String defaultFieldXPath; protected MimetypeRegistry mimeTypeService; /** * @since 8.10 */ protected static final String ALLOW_ZIP_PREVIEW = "nuxeo.preview.zip.enabled"; public ConversionService getConversionService() { return Framework.getService(ConversionService.class); } @Override protected PreviewAdapterManager getPreviewManager() { return Framework.getService(PreviewAdapterManager.class); } protected static String getMimeType(Blob blob) { if (blob == null) { return null; } String srcMT = blob.getMimeType(); if (srcMT == null || srcMT.startsWith("application/octet-stream")) { // call MT Service try { MimetypeRegistry mtr = Framework.getService(MimetypeRegistry.class); srcMT = mtr.getMimetypeFromFilenameAndBlobWithDefault(blob.getFilename(), blob, "application/octet-stream"); log.debug("mime type service returned " + srcMT); } catch (MimetypeDetectionException e) { log.warn("error while calling Mimetype service", e); } } return srcMT; } protected String getMimeType(String xpath) { BlobHolder blobHolder2preview = getBlobHolder2preview(xpath); Blob blob = getBlob2preview(blobHolder2preview); return getMimeType(blob); } protected String getDefaultPreviewFieldXPath() { return defaultFieldXPath; } public void setDefaultPreviewFieldXPath(String xPath) { defaultFieldXPath = xPath; } @Override public List<Blob> getPreviewBlobs() throws PreviewException { return getPreviewBlobs(getDefaultPreviewFieldXPath()); } @Override public boolean hasPreview(String xpath) { String srcMT; try { srcMT = getMimeType(xpath); } catch (NothingToPreviewException e) { return false; } if ("application/zip".equals(srcMT) && !Framework.getService(ConfigurationService.class).isBooleanPropertyTrue(ALLOW_ZIP_PREVIEW)) { return false; } MimeTypePreviewer mtPreviewer = getPreviewManager().getPreviewer(srcMT); return mtPreviewer != null || getConversionService().getConverterName(srcMT, "text/html") != null; } @Override public List<Blob> getPreviewBlobs(String xpath) throws PreviewException { BlobHolder blobHolder2preview = getBlobHolder2preview(xpath); Blob blob2Preview; try { blob2Preview = getBlob2preview(blobHolder2preview); } catch (NothingToPreviewException e) { return Collections.emptyList(); } String srcMT = getMimeType(xpath); log.debug("Source type for HTML preview =" + srcMT); MimeTypePreviewer mtPreviewer = getPreviewManager().getPreviewer(srcMT); if (mtPreviewer != null) { return mtPreviewer.getPreview(blob2Preview, adaptedDoc); } String converterName = getConversionService().getConverterName(srcMT, "text/html"); if (converterName == null) { log.debug("No dedicated converter found, using generic"); converterName = "any2html"; } BlobHolder result; try { result = getConversionService().convert(converterName, blobHolder2preview, null); setMimeType(result); setDigest(result); return result.getBlobs(); } catch (ConversionException e) { throw new PreviewException(e.getMessage(), e); } } /** * @since 5.7.3 */ private Blob getBlob2preview(BlobHolder blobHolder2preview) throws PreviewException { Blob blob2Preview; try { blob2Preview = blobHolder2preview.getBlob(); } catch (PropertyNotFoundException e) { blob2Preview = null; } if (blob2Preview == null) { throw new NothingToPreviewException("Can not preview a document without blob"); } else { return blob2Preview; } } /** * Returns a blob holder suitable for a preview. * * @since 5.7.3 */ private BlobHolder getBlobHolder2preview(String xpath) { if ((xpath == null) || ("default".equals(xpath))) { return adaptedDoc.getAdapter(BlobHolder.class); } else { return new DocumentBlobHolder(adaptedDoc, xpath); } } protected void setMimeType(BlobHolder result) { for (Blob blob : result.getBlobs()) { if ((blob.getMimeType() == null || blob.getMimeType().startsWith("application/octet-stream")) && blob.getFilename().endsWith("html")) { String mimeTpye = getMimeType(blob); blob.setMimeType(mimeTpye); } } } protected void setDigest(BlobHolder result) { for (Blob blob : result.getBlobs()) { if (blob.getDigest() == null) { try (InputStream stream = blob.getStream()) { String digest = DigestUtils.md5Hex(stream); blob.setDigest(digest); } catch (IOException e) { log.warn("Unable to compute digest of blob.", e); } } } } public String getMimeType(File file) throws ConversionException { try { return getMimeTypeService().getMimetypeFromFile(file); } catch (ConversionException e) { throw new ConversionException("Could not get MimeTypeRegistry"); } catch (MimetypeNotFoundException e) { return "application/octet-stream"; } catch (MimetypeDetectionException e) { return "application/octet-stream"; } } public MimetypeRegistry getMimeTypeService() throws ConversionException { if (mimeTypeService == null) { mimeTypeService = Framework.getService(MimetypeRegistry.class); } return mimeTypeService; } @Override public void cleanup() { } @Override public boolean cachable() { return true; } @Override public boolean hasBlobToPreview() throws PreviewException { String xpath = getDefaultPreviewFieldXPath(); Blob blob2Preview; try { blob2Preview = getBlob2preview(getBlobHolder2preview(xpath)); } catch (NothingToPreviewException e) { return false; } String srcMT = getMimeType(xpath); if ("application/zip".equals(srcMT) && !Framework.getService(ConfigurationService.class).isBooleanPropertyTrue(ALLOW_ZIP_PREVIEW)) { return false; } return blob2Preview != null; } }