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 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *     Florent Bonnet <florent.bonnet@nuxeo.com>
019 *     Estelle Giuly <egiuly@nuxeo.com>
020 *
021 * $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $
022 */
023
024package org.nuxeo.ecm.webapp.action;
025
026import java.io.IOException;
027import java.net.URI;
028import java.util.HashMap;
029import java.util.Iterator;
030import java.util.Map;
031
032import javax.faces.context.FacesContext;
033
034import org.apache.commons.lang.StringUtils;
035import org.apache.commons.logging.Log;
036import org.apache.commons.logging.LogFactory;
037import org.jboss.seam.ScopeType;
038import org.jboss.seam.annotations.In;
039import org.jboss.seam.annotations.Name;
040import org.jboss.seam.annotations.Scope;
041import org.jboss.seam.annotations.remoting.WebRemote;
042import org.jboss.seam.annotations.web.RequestParameter;
043import org.nuxeo.ecm.core.api.Blob;
044import org.nuxeo.ecm.core.api.CoreSession;
045import org.nuxeo.ecm.core.api.DocumentModel;
046import org.nuxeo.ecm.core.api.IdRef;
047import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
048import org.nuxeo.ecm.core.api.blobholder.DocumentBlobHolder;
049import org.nuxeo.ecm.core.blob.BlobManager;
050import org.nuxeo.ecm.core.blob.BlobManager.UsageHint;
051import org.nuxeo.ecm.core.convert.api.ConversionService;
052import org.nuxeo.ecm.core.convert.api.ConverterCheckResult;
053import org.nuxeo.ecm.core.convert.api.ConverterNotRegistered;
054import static org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry.PDF_MIMETYPE;
055import static org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry.PDF_EXTENSION;
056import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
057import org.nuxeo.ecm.platform.ui.web.cache.ThreadSafeCacheHolder;
058import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils;
059import org.nuxeo.runtime.api.Framework;
060
061@Name("conversionActions")
062@Scope(ScopeType.EVENT)
063public class ConversionActionBean implements ConversionAction {
064
065    private static final Log log = LogFactory.getLog(ConversionActionBean.class);
066
067    protected Map<String, ConverterCheckResult> pdfConverterForTypes;
068
069    @In(create = true, required = false)
070    CoreSession documentManager;
071
072    @In(create = true)
073    NavigationContext navigationContext;
074
075    @RequestParameter
076    private String docRef;
077
078    @RequestParameter
079    private String fileFieldFullName;
080
081    @RequestParameter
082    private String filename;
083
084    protected String pdfConverterName;
085
086    protected static final ThreadSafeCacheHolder<Boolean> exportableToPDFCache = new ThreadSafeCacheHolder<>(20);
087
088    public String display() {
089        return "view_file";
090    }
091
092    private DocumentModel getDocument() {
093        if (docRef == null) {
094            return navigationContext.getCurrentDocument();
095        } else {
096            return documentManager.getDocument(new IdRef(docRef));
097        }
098    }
099
100    private String getMimetypeFromDocument(String propertyName) {
101        Blob blob = (Blob) getDocument().getPropertyValue(propertyName);
102        return blob.getMimeType();
103    }
104
105    @Override
106    public void reCheckConverterAvailability() {
107        pdfConverterForTypes.clear();
108    }
109
110    public boolean isExportableToPDF(BlobHolder bh) {
111        if (bh == null) {
112            return false;
113        }
114        Blob blob = bh.getBlob();
115        return blob != null && isExportableToPDF(blob);
116    }
117
118    @Override
119    public boolean isExportableToPDF(Blob blob) {
120        if (blob == null) {
121            return false;
122        }
123        // check if there's a conversion available
124        if (getPDFConversionURL(blob) != null) {
125            return true;
126        }
127        String mimeType = blob.getMimeType();
128        return isMimeTypeExportableToPDF(mimeType);
129    }
130
131    protected String getPDFConversionURL(Blob blob) {
132        BlobManager blobManager = Framework.getService(BlobManager.class);
133        try {
134            URI uri = blobManager.getAvailableConversions(blob, UsageHint.DOWNLOAD).get(PDF_MIMETYPE);
135            if (uri != null) {
136                return uri.toString();
137            }
138        } catch (IOException e) {
139            log.error("Failed to retrieve available conversions", e);
140        }
141        return null;
142    }
143
144    protected boolean isMimeTypeExportableToPDF(String mimeType) {
145        // Don't bother searching for NO MIME type.
146        if (mimeType == null) {
147            return false;
148        }
149
150        // Initialize the converter check result map.
151        if (pdfConverterForTypes == null) {
152            pdfConverterForTypes = new HashMap<>();
153        }
154
155        // Check if there is any saved ConverterCheckResult for the desired
156        // MIME type.
157        if (pdfConverterForTypes.containsKey(mimeType)) {
158            return pdfConverterForTypes.get(mimeType).isAvailable();
159        }
160
161        try {
162            ConverterCheckResult pdfConverterAvailability;
163            ConversionService conversionService = Framework.getLocalService(ConversionService.class);
164            Iterator<String> converterNames = conversionService.getConverterNames(mimeType, PDF_MIMETYPE).iterator();
165            while (converterNames.hasNext()) {
166                pdfConverterName = converterNames.next();
167                pdfConverterAvailability = conversionService.isConverterAvailable(pdfConverterName, true);
168
169                // Save the converter availability for all the mime-types the
170                // converter
171                // supports.
172                for (String supMimeType : pdfConverterAvailability.getSupportedInputMimeTypes()) {
173                    pdfConverterForTypes.put(supMimeType, pdfConverterAvailability);
174                }
175
176                if (pdfConverterAvailability.isAvailable()) {
177                    return true;
178                }
179            }
180        } catch (ConverterNotRegistered e) {
181            log.error("Error while testing PDF converter availability", e);
182        }
183        return false;
184    }
185
186    @Override
187    @WebRemote
188    public boolean isFileExportableToPDF(String fieldName) {
189        DocumentModel doc = getDocument();
190        Boolean cacheResult = exportableToPDFCache.getFromCache(doc, fieldName);
191        boolean isSupported;
192        if (cacheResult == null) {
193            String mimetype = getMimetypeFromDocument(fieldName);
194            isSupported = isMimeTypeExportableToPDF(mimetype);
195            exportableToPDFCache.addToCache(doc, fieldName, isSupported);
196        } else {
197            isSupported = cacheResult;
198        }
199        return isSupported;
200    }
201
202    public String generatePdfFileFromBlobHolder(DocumentModel doc, BlobHolder bh) throws IOException {
203        // redirect to the conversion URL when available
204        Blob blob = bh.getBlob();
205        String url = getPDFConversionURL(blob);
206        if (url != null) {
207            try {
208                FacesContext.getCurrentInstance().getExternalContext().redirect(url);
209                return null;
210            } catch (IOException e) {
211                //
212            }
213        }
214        if (pdfConverterName == null) {
215            log.error("No PDF converter was found.");
216            return "pdf_generation_error";
217        }
218        Blob result = Framework.getService(ConversionService.class).convertBlobToPDF(blob);
219        if (result == null) {
220            log.error("Transform service didn't return any resulting documents which is not normal.");
221            return "pdf_generation_error";
222        }
223        String xpath;
224        if (bh instanceof DocumentBlobHolder) {
225            xpath = ((DocumentBlobHolder) bh).getXpath();
226        } else {
227            xpath = null;
228        }
229        ComponentUtils.download(doc, xpath, result, result.getFilename(), "pdfConversion");
230        return null;
231    }
232
233    @Override
234    @WebRemote
235    public String generatePdfFile() throws IOException {
236        DocumentModel doc = getDocument();
237        BlobHolder bh = new DocumentBlobHolder(doc, fileFieldFullName);
238        return generatePdfFileFromBlobHolder(doc, bh);
239    }
240
241    /**
242     * @since 7.3
243     */
244    public boolean isPDF(BlobHolder bh) {
245        if (bh == null) {
246            return false;
247        }
248        Blob blob = bh.getBlob();
249        return blob != null && isPDF(blob);
250    }
251
252    /**
253     * @since 7.3
254     */
255    public boolean isPDF(Blob blob) {
256        if (blob == null) {
257            return false;
258        }
259        String mimeType = blob.getMimeType();
260        if (StringUtils.isNotBlank(mimeType) && PDF_MIMETYPE.equals(mimeType)) {
261            return true;
262        } else {
263            String filename = blob.getFilename();
264            if (StringUtils.isNotBlank(filename) && filename.endsWith(PDF_EXTENSION)) {
265                // assume it's a pdf file
266                return true;
267            }
268        }
269        return false;
270    }
271
272    public void initialize() {
273        // NOP
274    }
275
276}