001/*
002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     <a href="mailto:tdelprat@nuxeo.com">Tiry</a>
016 */
017package org.nuxeo.ecm.platform.rendition.extension;
018
019import java.util.ArrayList;
020import java.util.List;
021
022import org.apache.commons.io.FilenameUtils;
023import org.apache.commons.lang.StringUtils;
024import org.apache.commons.logging.Log;
025import org.apache.commons.logging.LogFactory;
026import org.nuxeo.ecm.automation.AutomationService;
027import org.nuxeo.ecm.automation.OperationContext;
028import org.nuxeo.ecm.automation.core.Constants;
029import org.nuxeo.ecm.core.api.Blob;
030import org.nuxeo.ecm.core.api.CoreSession;
031import org.nuxeo.ecm.core.api.DocumentModel;
032import org.nuxeo.ecm.core.api.NuxeoException;
033import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
034import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry;
035import org.nuxeo.ecm.platform.rendition.service.RenditionDefinition;
036import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeEntry;
037import org.nuxeo.runtime.api.Framework;
038
039/**
040 * Class introduced to share code between sync and lazy automation based renditions
041 *
042 * @author <a href="mailto:tdelprat@nuxeo.com">Tiry</a>
043 * @since 7.2
044 */
045public class AutomationRenderer {
046
047    protected static final Log log = LogFactory.getLog(AutomationRenderer.class);
048
049    /**
050     * Test if the Rendition is available on the given DocumentModel
051     *
052     * @param doc the target {@link DocumentModel}
053     * @param def the {@link RenditionDefinition} to use
054     * @return The test result
055     */
056    public static boolean isRenditionAvailable(DocumentModel doc, RenditionDefinition def) {
057        String chain = def.getOperationChain();
058        if (chain == null) {
059            log.error("Can not run Automation rendition if chain is not defined");
060            return false;
061        }
062        AutomationService as = Framework.getLocalService(AutomationService.class);
063
064        try {
065            if (as.getOperation(chain) == null) {
066                log.error("Chain " + chain + " is not defined : rendition can not be used");
067                return false;
068            }
069        } catch (Exception e) {
070            log.error("Unable to test Rendition availability", e);
071            return false;
072        }
073
074        if (!def.isEmptyBlobAllowed()) {
075            BlobHolder bh = doc.getAdapter(BlobHolder.class);
076            if (bh == null) {
077                return false;
078            }
079            try {
080                Blob blob = bh.getBlob();
081                if (blob == null) {
082                    return false;
083                }
084            } catch (Exception e) {
085                log.error("Unable to get Blob to test Rendition availability", e);
086                return false;
087            }
088        }
089        return true;
090    }
091
092    /**
093     * Generate the rendition Blobs for a given {@link RenditionDefinition}. Return is a List of Blob for bigger
094     * flexibility (typically HTML rendition with resources)
095     *
096     * @param doc the target {@link DocumentModel}
097     * @param definition the {@link RenditionDefinition} to use
098     * @param session the {@link CoreSession} to use
099     * @return The list of Blobs
100     */
101    public static List<Blob> render(DocumentModel doc, RenditionDefinition definition, CoreSession session) {
102
103        String chain = definition.getOperationChain();
104        if (chain == null) {
105            throw new NuxeoException("no operation defined");
106        }
107
108        if (session == null) {
109            session = doc.getCoreSession();
110        }
111        AutomationService as = Framework.getLocalService(AutomationService.class);
112        OperationContext oc = new OperationContext(session);
113        oc.push(Constants.O_DOCUMENT, doc);
114
115        try {
116            BlobHolder bh = doc.getAdapter(BlobHolder.class);
117            if (bh != null) {
118                try {
119                    Blob blob = bh.getBlob();
120                    if (blob != null) {
121                        oc.push(Constants.O_BLOB, blob);
122                    }
123                } catch (Exception e) {
124                    if (!definition.isEmptyBlobAllowed()) {
125                        throw new NuxeoException("No Blob available", e);
126                    }
127                }
128            } else {
129                if (!definition.isEmptyBlobAllowed()) {
130                    throw new NuxeoException("No Blob available");
131                }
132            }
133
134            Blob blob = (Blob) as.run(oc, definition.getOperationChain());
135            if (blob != null && StringUtils.isBlank(blob.getFilename())) {
136                String filename = getFilenameWithExtension(doc.getTitle(), blob.getMimeType(), "bin");
137                blob.setFilename(filename);
138            }
139            List<Blob> blobs = new ArrayList<Blob>();
140            blobs.add(blob);
141            return blobs;
142
143        } catch (Exception e) {
144            throw new NuxeoException("Exception while running the operation chain: "
145                    + definition.getOperationChain(), e);
146        }
147    }
148
149    /**
150     * Generate a revised filename whose extension is either based on the supplied mimeType if applicable or the
151     * supplied default extension.
152     *
153     * @param filename  the filename to use
154     * @param mimeType  the mimeType from which the assigned extension is derived
155     * @param defaultExtension  the default extension to be assigned if the mimeType has no corresponding extension
156     * @return the filename with the revised extension
157     * @since 7.4
158     */
159    public static String getFilenameWithExtension(String filename, String mimeType, String defaultExtension) {
160        String baseName = FilenameUtils.getBaseName(filename);
161        MimetypeRegistry mimetypeRegistry = Framework.getLocalService(MimetypeRegistry.class);
162        MimetypeEntry mimeTypeEntry = mimetypeRegistry.getMimetypeEntryByMimeType(mimeType);
163        List<String> extensions = mimeTypeEntry.getExtensions();
164        String extension;
165        if (!extensions.isEmpty()) {
166            extension = extensions.get(0);
167        } else {
168            extension = defaultExtension;
169        }
170        return (extension == null) ? filename : baseName + "." + extension;
171    }
172
173}