001/*
002 * (C) Copyright 2015 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 * Contributors:
017 *     <a href="mailto:tdelprat@nuxeo.com">Tiry</a>
018 */
019package org.nuxeo.ecm.platform.rendition.extension;
020
021import java.util.ArrayList;
022import java.util.List;
023
024import org.apache.commons.io.FilenameUtils;
025import org.apache.commons.lang.StringUtils;
026import org.apache.commons.logging.Log;
027import org.apache.commons.logging.LogFactory;
028import org.nuxeo.ecm.automation.AutomationService;
029import org.nuxeo.ecm.automation.OperationContext;
030import org.nuxeo.ecm.automation.core.Constants;
031import org.nuxeo.ecm.core.api.Blob;
032import org.nuxeo.ecm.core.api.CoreSession;
033import org.nuxeo.ecm.core.api.DocumentModel;
034import org.nuxeo.ecm.core.api.NuxeoException;
035import org.nuxeo.ecm.core.api.NuxeoPrincipal;
036import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
037import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry;
038import org.nuxeo.ecm.platform.rendition.service.RenditionDefinition;
039import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeEntry;
040import org.nuxeo.runtime.api.Framework;
041
042/**
043 * Class introduced to share code between sync and lazy automation based renditions
044 *
045 * @author <a href="mailto:tdelprat@nuxeo.com">Tiry</a>
046 * @since 7.2
047 */
048public class AutomationRenderer {
049
050    protected static final Log log = LogFactory.getLog(AutomationRenderer.class);
051
052    // TODO move this into a base abstract rendition provider
053    private static final String VARIANT_POLICY_USER = "user";
054
055    /**
056     * Test if the Rendition is available on the given DocumentModel
057     *
058     * @param doc the target {@link DocumentModel}
059     * @param def the {@link RenditionDefinition} to use
060     * @return The test result
061     */
062    public static boolean isRenditionAvailable(DocumentModel doc, RenditionDefinition def) {
063        String chain = def.getOperationChain();
064        if (chain == null) {
065            log.error("Can not run Automation rendition if chain is not defined");
066            return false;
067        }
068        AutomationService as = Framework.getLocalService(AutomationService.class);
069
070        try {
071            if (as.getOperation(chain) == null) {
072                log.error("Chain " + chain + " is not defined : rendition can not be used");
073                return false;
074            }
075        } catch (Exception e) {
076            log.error("Unable to test Rendition availability", e);
077            return false;
078        }
079
080        if (!def.isEmptyBlobAllowed()) {
081            BlobHolder bh = doc.getAdapter(BlobHolder.class);
082            if (bh == null) {
083                return false;
084            }
085            try {
086                Blob blob = bh.getBlob();
087                if (blob == null) {
088                    return false;
089                }
090            } catch (Exception e) {
091                log.error("Unable to get Blob to test Rendition availability", e);
092                return false;
093            }
094        }
095        return true;
096    }
097
098    /**
099     * Generate the rendition Blobs for a given {@link RenditionDefinition}. Return is a List of Blob for bigger
100     * flexibility (typically HTML rendition with resources)
101     *
102     * @param doc the target {@link DocumentModel}
103     * @param definition the {@link RenditionDefinition} to use
104     * @param session the {@link CoreSession} to use
105     * @return The list of Blobs
106     */
107    public static List<Blob> render(DocumentModel doc, RenditionDefinition definition, CoreSession session) {
108
109        String chain = definition.getOperationChain();
110        if (chain == null) {
111            throw new NuxeoException("no operation defined");
112        }
113
114        if (session == null) {
115            session = doc.getCoreSession();
116        }
117        AutomationService as = Framework.getLocalService(AutomationService.class);
118        try (OperationContext oc = new OperationContext(session)) {
119            oc.push(Constants.O_DOCUMENT, doc);
120
121            BlobHolder bh = doc.getAdapter(BlobHolder.class);
122            if (bh != null) {
123                try {
124                    Blob blob = bh.getBlob();
125                    if (blob != null) {
126                        oc.push(Constants.O_BLOB, blob);
127                    }
128                } catch (Exception e) {
129                    if (!definition.isEmptyBlobAllowed()) {
130                        throw new NuxeoException("No Blob available", e);
131                    }
132                }
133            } else {
134                if (!definition.isEmptyBlobAllowed()) {
135                    throw new NuxeoException("No Blob available");
136                }
137            }
138
139            Blob blob = (Blob) as.run(oc, definition.getOperationChain());
140            if (blob != null && StringUtils.isBlank(blob.getFilename())) {
141                String filename = getFilenameWithExtension(doc.getTitle(), blob.getMimeType(), "bin");
142                blob.setFilename(filename);
143            }
144            List<Blob> blobs = new ArrayList<Blob>();
145            blobs.add(blob);
146            return blobs;
147
148        } catch (Exception e) {
149            throw new NuxeoException("Exception while running the operation chain: " + definition.getOperationChain(),
150                    e);
151        }
152    }
153
154    /**
155     * Generates the optional {@link org.nuxeo.ecm.platform.rendition.Constants#RENDITION_VARIANT_PROPERTY
156     * RENDITION_VARIANT_PROPERTY} value for a given {@link RenditionDefinition}.
157     *
158     * @param doc the target document
159     * @param definition the rendition definition to use
160     * @return the generated {@link org.nuxeo.ecm.platform.rendition.Constants#RENDITION_VARIANT_PROPERTY
161     *         RENDITION_VARIANT_PROPERTY} value, or {@code null}
162     * @since 8.1
163     */
164    // TODO move this into a base abstract rendition provider
165    public static String getVariant(DocumentModel doc, RenditionDefinition definition) {
166        if (VARIANT_POLICY_USER.equals(definition.getVariantPolicy())) {
167            NuxeoPrincipal principal = (NuxeoPrincipal) doc.getCoreSession().getPrincipal();
168            if (principal.isAdministrator()) {
169                return org.nuxeo.ecm.platform.rendition.Constants.RENDITION_VARIANT_PROPERTY_ADMINISTRATOR_USER;
170            } else {
171                return org.nuxeo.ecm.platform.rendition.Constants.RENDITION_VARIANT_PROPERTY_USER_PREFIX
172                        + principal.getName();
173            }
174        }
175        return null;
176    }
177
178    /**
179     * Generate a revised filename whose extension is either based on the supplied mimeType if applicable or the
180     * supplied default extension.
181     *
182     * @param filename  the filename to use
183     * @param mimeType  the mimeType from which the assigned extension is derived
184     * @param defaultExtension  the default extension to be assigned if the mimeType has no corresponding extension
185     * @return the filename with the revised extension
186     * @since 7.4
187     */
188    public static String getFilenameWithExtension(String filename, String mimeType, String defaultExtension) {
189        String baseName = FilenameUtils.getBaseName(filename);
190        MimetypeRegistry mimetypeRegistry = Framework.getLocalService(MimetypeRegistry.class);
191        MimetypeEntry mimeTypeEntry = mimetypeRegistry.getMimetypeEntryByMimeType(mimeType);
192        List<String> extensions = mimeTypeEntry.getExtensions();
193        String extension;
194        if (!extensions.isEmpty()) {
195            extension = extensions.get(0);
196        } else {
197            extension = defaultExtension;
198        }
199        return (extension == null) ? filename : baseName + "." + extension;
200    }
201
202}