001/*
002 * (C) Copyright 2006-2007 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<Boolean>(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        if (blob == null) {
121            return false;
122        } else {
123            return isExportableToPDF(blob);
124        }
125    }
126
127    @Override
128    public boolean isExportableToPDF(Blob blob) {
129        if (blob == null) {
130            return false;
131        }
132        // check if there's a conversion available
133        if (getPDFConversionURL(blob) != null) {
134            return true;
135        }
136        String mimetype = blob.getMimeType();
137        return isMimeTypeExportableToPDF(mimetype);
138    }
139
140    protected String getPDFConversionURL(Blob blob) {
141        BlobManager blobManager = Framework.getService(BlobManager.class);
142        try {
143            URI uri = blobManager.getAvailableConversions(blob, UsageHint.DOWNLOAD).get(PDF_MIMETYPE);
144            if (uri != null) {
145                return uri.toString();
146            }
147        } catch (IOException e) {
148            log.error("Failed to retrieve available conversions", e);
149        }
150        return null;
151    }
152
153    protected boolean isMimeTypeExportableToPDF(String mimetype) {
154        // Don't bother searching for NO MIME type.
155        if (mimetype == null) {
156            return false;
157        }
158
159        // Initialize the converter check result map.
160        if (pdfConverterForTypes == null) {
161            pdfConverterForTypes = new HashMap<String, ConverterCheckResult>();
162        }
163
164        // Check if there is any saved ConverterCheckResult for the desired
165        // MIME type.
166        if (pdfConverterForTypes.containsValue(mimetype)) {
167            return pdfConverterForTypes.get(mimetype).isAvailable();
168        }
169
170        try {
171            ConverterCheckResult pdfConverterAvailability;
172            ConversionService conversionService = Framework.getLocalService(ConversionService.class);
173            Iterator<String> converterNames = conversionService.getConverterNames(mimetype, PDF_MIMETYPE).iterator();
174            while (converterNames.hasNext()) {
175                pdfConverterName = converterNames.next();
176                pdfConverterAvailability = conversionService.isConverterAvailable(pdfConverterName, true);
177
178                // Save the converter availability for all the mime-types the
179                // converter
180                // supports.
181                for (String supMimeType : pdfConverterAvailability.getSupportedInputMimeTypes()) {
182                    pdfConverterForTypes.put(supMimeType, pdfConverterAvailability);
183                }
184
185                if (pdfConverterAvailability.isAvailable()) {
186                    return true;
187                }
188            }
189        } catch (ConverterNotRegistered e) {
190            log.error("Error while testing PDF converter availability", e);
191        }
192        return false;
193    }
194
195    @Override
196    @WebRemote
197    public boolean isFileExportableToPDF(String fieldName) {
198        DocumentModel doc = getDocument();
199        Boolean cacheResult = exportableToPDFCache.getFromCache(doc, fieldName);
200        boolean isSupported;
201        if (cacheResult == null) {
202            String mimetype = getMimetypeFromDocument(fieldName);
203            isSupported = isMimeTypeExportableToPDF(mimetype);
204            exportableToPDFCache.addToCache(doc, fieldName, isSupported);
205        } else {
206            isSupported = cacheResult;
207        }
208        return isSupported;
209    }
210
211    public String generatePdfFileFromBlobHolder(DocumentModel doc, BlobHolder bh) {
212        // redirect to the conversion URL when available
213        Blob blob = bh.getBlob();
214        String url = getPDFConversionURL(blob);
215        if (url != null) {
216            try {
217                FacesContext.getCurrentInstance().getExternalContext().redirect(url);
218                return null;
219            } catch (IOException e) {
220                //
221            }
222        }
223        if (pdfConverterName == null) {
224            log.error("No PDF converter was found.");
225            return "pdf_generation_error";
226        }
227        BlobHolder result = Framework.getService(ConversionService.class).convert(pdfConverterName, bh, null);
228        if (result == null) {
229            log.error("Transform service didn't return any resulting documents which is not normal.");
230            return "pdf_generation_error";
231        }
232        String xpath;
233        if (bh instanceof DocumentBlobHolder) {
234            xpath = ((DocumentBlobHolder) bh).getXpath();
235        } else {
236            xpath = null;
237        }
238        String origFilename = new Path(bh.getFilePath()).lastSegment();
239        String filename = FileUtils.getCleanFileName(origFilename);
240        if (StringUtils.isBlank(filename)) {
241            filename = "file";
242        }
243        // add pdf extension
244        int pos = filename.lastIndexOf('.');
245        if (pos > 0) {
246            filename = filename.substring(0, pos);
247        }
248        filename += ".pdf";
249        ComponentUtils.download(doc, xpath, result.getBlob(), filename, "pdfConversion");
250        return null;
251    }
252
253    @Override
254    @WebRemote
255    public String generatePdfFile() {
256        DocumentModel doc = getDocument();
257        BlobHolder bh = new DocumentBlobHolder(doc, fileFieldFullName);
258        return generatePdfFileFromBlobHolder(doc, bh);
259    }
260
261    /**
262     * @since 7.3
263     */
264    public boolean isPDF(BlobHolder bh) {
265        if (bh == null) {
266            return false;
267        }
268        Blob blob = bh.getBlob();
269        return blob != null && isPDF(blob);
270    }
271
272    /**
273     * @since 7.3
274     */
275    public boolean isPDF(Blob blob) {
276        if (blob == null) {
277            return false;
278        }
279        String mimeType = blob.getMimeType();
280        if (StringUtils.isNotBlank(mimeType) && PDF_MIMETYPE.equals(mimeType)) {
281            return true;
282        } else {
283            String filename = blob.getFilename();
284            if (StringUtils.isNotBlank(filename) && filename.endsWith(PDF_EXTENSION)) {
285                // assume it's a pdf file
286                return true;
287            }
288        }
289        return false;
290    }
291
292    public void initialize() {
293        // NOP
294    }
295
296}