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