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