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        OperationContext oc = new OperationContext(session);
119        oc.push(Constants.O_DOCUMENT, doc);
120
121        try {
122            BlobHolder bh = doc.getAdapter(BlobHolder.class);
123            if (bh != null) {
124                try {
125                    Blob blob = bh.getBlob();
126                    if (blob != null) {
127                        oc.push(Constants.O_BLOB, blob);
128                    }
129                } catch (Exception e) {
130                    if (!definition.isEmptyBlobAllowed()) {
131                        throw new NuxeoException("No Blob available", e);
132                    }
133                }
134            } else {
135                if (!definition.isEmptyBlobAllowed()) {
136                    throw new NuxeoException("No Blob available");
137                }
138            }
139
140            Blob blob = (Blob) as.run(oc, definition.getOperationChain());
141            if (blob != null && StringUtils.isBlank(blob.getFilename())) {
142                String filename = getFilenameWithExtension(doc.getTitle(), blob.getMimeType(), "bin");
143                blob.setFilename(filename);
144            }
145            List<Blob> blobs = new ArrayList<Blob>();
146            blobs.add(blob);
147            return blobs;
148
149        } catch (Exception e) {
150            throw new NuxeoException("Exception while running the operation chain: "
151                    + definition.getOperationChain(), e);
152        }
153    }
154
155    /**
156     * Generates the optional {@link org.nuxeo.ecm.platform.rendition.Constants#RENDITION_VARIANT_PROPERTY
157     * RENDITION_VARIANT_PROPERTY} value for a given {@link RenditionDefinition}.
158     *
159     * @param doc the target document
160     * @param definition the rendition definition to use
161     * @return the generated {@link org.nuxeo.ecm.platform.rendition.Constants#RENDITION_VARIANT_PROPERTY
162     *         RENDITION_VARIANT_PROPERTY} value, or {@code null}
163     * @since 8.1
164     */
165    // TODO move this into a base abstract rendition provider
166    public static String getVariant(DocumentModel doc, RenditionDefinition definition) {
167        if (VARIANT_POLICY_USER.equals(definition.getVariantPolicy())) {
168            NuxeoPrincipal principal = (NuxeoPrincipal) doc.getCoreSession().getPrincipal();
169            if (principal.isAdministrator()) {
170                return org.nuxeo.ecm.platform.rendition.Constants.RENDITION_VARIANT_PROPERTY_ADMINISTRATOR_USER;
171            } else {
172                return org.nuxeo.ecm.platform.rendition.Constants.RENDITION_VARIANT_PROPERTY_USER_PREFIX
173                        + principal.getName();
174            }
175        }
176        return null;
177    }
178
179    /**
180     * Generate a revised filename whose extension is either based on the supplied mimeType if applicable or the
181     * supplied default extension.
182     *
183     * @param filename  the filename to use
184     * @param mimeType  the mimeType from which the assigned extension is derived
185     * @param defaultExtension  the default extension to be assigned if the mimeType has no corresponding extension
186     * @return the filename with the revised extension
187     * @since 7.4
188     */
189    public static String getFilenameWithExtension(String filename, String mimeType, String defaultExtension) {
190        String baseName = FilenameUtils.getBaseName(filename);
191        MimetypeRegistry mimetypeRegistry = Framework.getLocalService(MimetypeRegistry.class);
192        MimetypeEntry mimeTypeEntry = mimetypeRegistry.getMimetypeEntryByMimeType(mimeType);
193        List<String> extensions = mimeTypeEntry.getExtensions();
194        String extension;
195        if (!extensions.isEmpty()) {
196            extension = extensions.get(0);
197        } else {
198            extension = defaultExtension;
199        }
200        return (extension == null) ? filename : baseName + "." + extension;
201    }
202
203}