001/*
002 * (C) Copyright 2006-2013 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 *     Andreas Kalogeropoulos
016 *     Anahide Tchertchian
017 *     Thierry Delprat
018 *     Florent Guillaume
019 */
020package org.nuxeo.ecm.webapp.filemanager;
021
022import java.io.File;
023import java.io.FileNotFoundException;
024import java.io.IOException;
025import java.io.InputStream;
026import java.util.ArrayList;
027import java.util.Collection;
028import java.util.HashMap;
029import java.util.List;
030import java.util.Map;
031
032import javax.faces.context.ExternalContext;
033import javax.faces.context.FacesContext;
034import javax.faces.event.ActionEvent;
035
036import org.apache.commons.collections.CollectionUtils;
037import org.apache.commons.io.FilenameUtils;
038import org.apache.commons.lang.StringUtils;
039import org.apache.commons.logging.Log;
040import org.apache.commons.logging.LogFactory;
041import org.jboss.seam.ScopeType;
042import org.jboss.seam.annotations.In;
043import org.jboss.seam.annotations.Install;
044import org.jboss.seam.annotations.Name;
045import org.jboss.seam.annotations.Scope;
046import org.jboss.seam.annotations.remoting.WebRemote;
047import org.jboss.seam.core.Events;
048import org.jboss.seam.faces.FacesMessages;
049import org.jboss.seam.international.StatusMessage;
050import org.nuxeo.common.utils.Base64;
051import org.nuxeo.ecm.core.api.Blob;
052import org.nuxeo.ecm.core.api.Blobs;
053import org.nuxeo.ecm.core.api.CoreSession;
054import org.nuxeo.ecm.core.api.DocumentModel;
055import org.nuxeo.ecm.core.api.DocumentRef;
056import org.nuxeo.ecm.core.api.IdRef;
057import org.nuxeo.ecm.core.api.NuxeoException;
058import org.nuxeo.ecm.core.api.RecoverableClientException;
059import org.nuxeo.ecm.core.api.impl.blob.FileBlob;
060import org.nuxeo.ecm.core.api.security.SecurityConstants;
061import org.nuxeo.ecm.core.schema.FacetNames;
062import org.nuxeo.ecm.platform.filemanager.api.FileManager;
063import org.nuxeo.ecm.platform.types.TypeManager;
064import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
065import org.nuxeo.ecm.platform.ui.web.api.UserAction;
066import org.nuxeo.ecm.platform.ui.web.util.files.FileUtils;
067import org.nuxeo.ecm.platform.web.common.exceptionhandling.ExceptionHelper;
068import org.nuxeo.ecm.webapp.clipboard.ClipboardActions;
069import org.nuxeo.ecm.webapp.contentbrowser.DocumentActions;
070import org.nuxeo.ecm.webapp.helpers.EventManager;
071import org.nuxeo.ecm.webapp.helpers.EventNames;
072import org.nuxeo.runtime.api.Framework;
073import org.richfaces.event.FileUploadEvent;
074import org.richfaces.model.UploadedFile;
075
076@Name("FileManageActions")
077@Scope(ScopeType.EVENT)
078@Install(precedence = Install.FRAMEWORK)
079public class FileManageActionsBean implements FileManageActions {
080
081    private static final Log log = LogFactory.getLog(FileManageActionsBean.class);
082
083    public static final String TRANSF_ERROR = "TRANSF_ERROR";
084
085    public static final String SECURITY_ERROR = "SECURITY_ERROR";
086
087    public static final String MOVE_ERROR = "MOVE_ERROR";
088
089    public static final String COPY_ERROR = "COPY_ERROR";
090
091    public static final String PASTE_ERROR = "PASTE_ERROR";
092
093    public static final String MOVE_IMPOSSIBLE = "MOVE_IMPOSSIBLE";
094
095    public static final String MOVE_PUBLISH = "MOVE_PUBLISH";
096
097    public static final String MOVE_OK = "MOVE_OK";
098
099    protected static final String FILES_SCHEMA = "files";
100
101    protected static final String FILES_PROPERTY = FILES_SCHEMA + ":files";
102
103    // TODO NXP-13568: this should not be hardcoded on the doc type
104    protected static final String SECTION_DOCTYPE = "Section";
105
106    @In(create = true, required = false)
107    protected CoreSession documentManager;
108
109    @In(create = true)
110    protected TypeManager typeManager;
111
112    @In(create = true)
113    protected NavigationContext navigationContext;
114
115    @In(create = true)
116    protected transient DocumentActions documentActions;
117
118    @In(create = true)
119    protected ClipboardActions clipboardActions;
120
121    @In(create = true, required = false)
122    protected UploadItemHolder fileUploadHolder;
123
124    @In(create = true, required = false)
125    protected UploadItemHolderCycleManager fileUploadHolderCycle;
126
127    /**
128     * Helper field to get the filename to remove.
129     *
130     * @since 5.9.2
131     */
132    protected String fileToRemove;
133
134    @In(create = true, required = false)
135    protected FacesMessages facesMessages;
136
137    @In(create = true)
138    protected Map<String, String> messages;
139
140    protected FileManager fileManager;
141
142    /**
143     * Used to keep track of the path of the uploaded file (NXP-16745)
144     */
145    protected List<String> tmpFilePaths = new ArrayList<String>();
146
147    protected FileManager getFileManagerService() {
148        if (fileManager == null) {
149            fileManager = Framework.getService(FileManager.class);
150        }
151        return fileManager;
152    }
153
154    @Override
155    public String display() {
156        return "view_documents";
157    }
158
159    /**
160     * Creates a document from the file held in the fileUploadHolder. Takes responsibility for the fileUploadHolder
161     * temporary file.
162     */
163    @Override
164    public String addFile() {
165        NxUploadedFile uploadedFile = fileUploadHolder.getUploadedFiles().iterator().next();
166        Blob blob = uploadedFile.getBlob();
167        if (blob == null || blob.getFilename() == null) {
168            facesMessages.add(StatusMessage.Severity.ERROR, messages.get("fileImporter.error.nullUploadedFile"));
169            return navigationContext.getActionResult(navigationContext.getCurrentDocument(), UserAction.AFTER_CREATE);
170        }
171        FileUtils.configureFileBlob(blob);
172        DocumentModel currentDocument = navigationContext.getCurrentDocument();
173        String path = currentDocument.getPathAsString();
174        DocumentModel createdDoc = null;
175        try {
176            createdDoc = getFileManagerService().createDocumentFromBlob(documentManager, blob, path, true,
177                    blob.getFilename());
178        } catch (IOException e) {
179            throw new NuxeoException("Can not write blob for" + blob.getFilename(), e);
180        }
181        EventManager.raiseEventsOnDocumentSelected(createdDoc);
182        Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, currentDocument);
183
184        facesMessages.add(StatusMessage.Severity.INFO, messages.get("document_saved"),
185                messages.get(createdDoc.getType()));
186        return navigationContext.getActionResult(createdDoc, UserAction.AFTER_CREATE);
187    }
188
189    @Override
190    @Deprecated
191    // TODO: update the Seam remoting-based desktop plugins to stop calling
192    // this method
193    @WebRemote
194    public boolean canWrite() {
195        // let the FolderImporter and FileImporter plugin handle the security
196        // checks to avoid hardcoded behavior
197        return true;
198    }
199
200    protected String getErrorMessage(String errorType, String errorInfo) {
201        return getErrorMessage(errorType, errorInfo, "message.operation.fails.generic");
202    }
203
204    protected String getErrorMessage(String errorType, String errorInfo, String errorLabel) {
205        return String.format("%s |(%s)| %s", errorType, errorInfo, messages.get(errorLabel));
206    }
207
208    /**
209     * @deprecated use addBinaryFileFromPlugin with a Blob argument API to avoid loading the content in memory
210     */
211    @Override
212    @Deprecated
213    @WebRemote
214    public String addFileFromPlugin(String content, String mimetype, String fullName, String morePath,
215            Boolean UseBase64) {
216        byte[] bcontent;
217        if (UseBase64.booleanValue()) {
218            bcontent = Base64.decode(content);
219        } else {
220            bcontent = content.getBytes();
221        }
222        return addBinaryFileFromPlugin(bcontent, mimetype, fullName, morePath);
223    }
224
225    @Override
226    @WebRemote
227    public String addBinaryFileFromPlugin(Blob blob, String fullName, String morePath) {
228        DocumentModel currentDocument = navigationContext.getCurrentDocument();
229        String curPath = currentDocument.getPathAsString();
230
231        String path = curPath + morePath;
232        return createDocumentFromBlob(blob, fullName, path);
233    }
234
235    @Override
236    @WebRemote
237    public String addBinaryFileFromPlugin(Blob blob, String fullName, DocumentModel targetContainer)
238            {
239        return createDocumentFromBlob(blob, fullName, targetContainer.getPathAsString());
240    }
241
242    protected String createDocumentFromBlob(Blob blob, String fullName, String path) {
243        DocumentModel createdDoc;
244        try {
245            createdDoc = getFileManagerService().createDocumentFromBlob(documentManager, blob, path, true, fullName);
246        } catch (NuxeoException | IOException t) {
247            Throwable unwrappedError = ExceptionHelper.unwrapException(t);
248            if (ExceptionHelper.isSecurityError(unwrappedError)) {
249                // security check failed
250                log.debug("No permissions creating " + fullName);
251                return getErrorMessage(SECURITY_ERROR, fullName, "Error.Insuffisant.Rights");
252            } else {
253                // log error stack trace for server side debugging while giving
254                // a generic and localized error message to the client
255                log.error("Error importing " + fullName, t);
256                return getErrorMessage(TRANSF_ERROR, fullName);
257            }
258        }
259        if (createdDoc == null) {
260            log.error("could not create the document " + fullName);
261            return getErrorMessage(TRANSF_ERROR, fullName);
262        }
263        // update the context, raise events to update the seam context
264        DocumentModel currentDocument = navigationContext.getCurrentDocument();
265        if (currentDocument.getRef().equals(createdDoc.getRef())) {
266            navigationContext.updateDocumentContext(createdDoc);
267        }
268        Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, currentDocument);
269        EventManager.raiseEventsOnDocumentSelected(createdDoc);
270        return createdDoc.getName();
271    }
272
273    /**
274     * @deprecated Use addBinaryFileFromPlugin(Blob, String, String) to avoid loading the data in memory as a Bytes
275     *             array
276     */
277    @Deprecated
278    public String addBinaryFileFromPlugin(byte[] content, String mimetype, String fullName, String morePath)
279            {
280        Blob blob = Blobs.createBlob(content);
281        return addBinaryFileFromPlugin(blob, fullName, morePath);
282    }
283
284    @Override
285    @WebRemote
286    public String addFolderFromPlugin(String fullName, String morePath) {
287        try {
288            DocumentModel currentDocument = navigationContext.getCurrentDocument();
289
290            String curPath = currentDocument.getPathAsString();
291            if (!currentDocument.isFolder()) {
292                curPath = curPath.substring(0, curPath.lastIndexOf('/'));
293            }
294            String path = curPath + morePath;
295
296            DocumentModel createdDoc;
297            try {
298                createdDoc = getFileManagerService().createFolder(documentManager, fullName, path);
299            } catch (NuxeoException | IOException t) {
300                Throwable unwrappedError = ExceptionHelper.unwrapException(t);
301                if (ExceptionHelper.isSecurityError(unwrappedError)) {
302                    // security check failed
303                    log.debug("No permissions creating folder " + fullName);
304                    return getErrorMessage(SECURITY_ERROR, fullName, "Error.Insuffisant.Rights");
305                } else {
306                    log.error("Couldn't create the folder " + fullName);
307                    return getErrorMessage(TRANSF_ERROR, fullName);
308                }
309            }
310
311            if (createdDoc == null) {
312                log.error("Couldn't create the folder " + fullName);
313                return getErrorMessage(TRANSF_ERROR, fullName);
314            }
315
316            EventManager.raiseEventsOnDocumentSelected(createdDoc);
317            Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, currentDocument);
318            return createdDoc.getName();
319        } catch (RecoverableClientException e) {
320            throw e;
321        } catch (NuxeoException e) {
322            throw new RecoverableClientException("Cannot validate, caught exception", "error.db.fs", null, e);
323        }
324    }
325
326    @WebRemote
327    protected String checkMoveAllowed(DocumentRef docRef, DocumentRef containerRef) {
328
329        DocumentModel doc = documentManager.getDocument(docRef);
330        DocumentModel container = documentManager.getDocument(containerRef);
331
332        // check that we are not trying to move a folder inside itself
333
334        if ((container.getPathAsString() + "/").startsWith(doc.getPathAsString() + "/")) {
335            facesMessages.add(StatusMessage.Severity.WARN, messages.get("move_impossible"));
336            return MOVE_IMPOSSIBLE;
337        }
338        if (!doc.isProxy() && container.hasFacet(FacetNames.PUBLISH_SPACE) && !doc.hasFacet(FacetNames.PUBLISH_SPACE)) {
339            // we try to do a publication check browse in sections
340            if (!documentManager.hasPermission(containerRef, SecurityConstants.ADD_CHILDREN)) {
341                // only publish via D&D if this can be done directly (no wf)
342                // => need to have write access
343                facesMessages.add(StatusMessage.Severity.WARN, messages.get("move_insuffisant_rights"));
344                // TODO: this should be PUBLISH_IMPOSSIBLE
345                return MOVE_IMPOSSIBLE;
346            }
347
348            if (doc.hasFacet(FacetNames.PUBLISHABLE)) {
349                return MOVE_PUBLISH;
350            } else {
351                facesMessages.add(StatusMessage.Severity.WARN, messages.get("publish_impossible"));
352                // TODO: this should be PUBLISH_IMPOSSIBLE
353                return MOVE_IMPOSSIBLE;
354            }
355        }
356        // this is a real move operation (not a publication)
357
358        // check the right to remove the document from the source container
359        if (!documentManager.hasPermission(doc.getParentRef(), SecurityConstants.REMOVE_CHILDREN)
360                || !documentManager.hasPermission(doc.getRef(), SecurityConstants.REMOVE)) {
361            facesMessages.add(StatusMessage.Severity.WARN, messages.get("move_impossible"));
362            return MOVE_IMPOSSIBLE;
363        }
364
365        // check that we have the right to create the copy in the target
366        if (!documentManager.hasPermission(containerRef, SecurityConstants.ADD_CHILDREN)) {
367            facesMessages.add(StatusMessage.Severity.WARN, messages.get("move_insuffisant_rights"));
368            return MOVE_IMPOSSIBLE;
369        }
370
371        if (doc.isProxy()) {
372            if (!container.hasFacet(FacetNames.PUBLISH_SPACE)) {
373                // do not allow to move a published document back in a
374                // workspace
375                facesMessages.add(StatusMessage.Severity.WARN, messages.get("move_impossible"));
376                return MOVE_IMPOSSIBLE;
377            }
378        } else {
379            // check allowed content types constraints for non-proxy documents
380            if (!typeManager.isAllowedSubType(doc.getType(), container.getType(), container)) {
381                facesMessages.add(StatusMessage.Severity.WARN, messages.get("move_impossible"));
382                return MOVE_IMPOSSIBLE;
383            }
384        }
385
386        return MOVE_OK;
387    }
388
389    @Override
390    @WebRemote
391    public String moveWithId(String docId, String containerId) {
392        try {
393            String debug = "move " + docId + " into " + containerId;
394            log.debug(debug);
395            if (docId.startsWith("docRef:")) {
396                docId = docId.split("docRef:")[1];
397            }
398            if (docId.startsWith("docClipboardRef:")) {
399                docId = docId.split("docClipboardRef:")[1];
400            }
401            DocumentRef srcRef = new IdRef(docId);
402            String dst = containerId;
403            if (dst.startsWith("docRef:")) {
404                dst = dst.split("docRef:")[1];
405            }
406            if (dst.startsWith("nodeRef:")) {
407                dst = dst.split("nodeRef:")[1];
408            }
409            DocumentRef dstRef = new IdRef(dst);
410
411            String moveStatus = checkMoveAllowed(srcRef, dstRef);
412
413            if (moveStatus.equals(MOVE_IMPOSSIBLE)) {
414                return debug;
415            }
416
417            String action = "document_moved";
418
419            if (moveStatus.equals(MOVE_PUBLISH)) {
420                DocumentModel srcDoc = documentManager.getDocument(srcRef);
421                DocumentModel dstDoc = documentManager.getDocument(dstRef);
422                documentManager.publishDocument(srcDoc, dstDoc);
423                action = "document_published";
424            } else {
425                documentManager.move(srcRef, dstRef, null);
426            }
427
428            // delCopyWithId(docId);
429            documentManager.save();
430            DocumentModel currentDocument = navigationContext.getCurrentDocument();
431            EventManager.raiseEventsOnDocumentChildrenChange(currentDocument);
432
433            // notify current container
434            Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, currentDocument);
435            // notify the other container
436            DocumentModel otherContainer = documentManager.getDocument(dstRef);
437            Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED, otherContainer);
438
439            facesMessages.add(StatusMessage.Severity.INFO, messages.get(action),
440                    messages.get(documentManager.getDocument(srcRef).getType()));
441
442            return debug;
443        } catch (RecoverableClientException e) {
444            throw e;
445        } catch (NuxeoException e) {
446            throw new RecoverableClientException("Cannot validate, caught exception", "message.operation.fails.generic",
447                    null, e);
448        }
449    }
450
451    @Override
452    @WebRemote
453    public String copyWithId(String docId) {
454        try {
455            String debug = "copying " + docId;
456            log.debug(debug);
457            if (docId.startsWith("docRef:")) {
458                docId = docId.split("docRef:")[1];
459            }
460            if (docId.startsWith("docClipboardRef:")) {
461                docId = docId.split("docClipboardRef:")[1];
462            }
463            DocumentRef srcRef = new IdRef(docId);
464            DocumentModel srcDoc = documentManager.getDocument(srcRef);
465            List<DocumentModel> docsToAdd = new ArrayList<DocumentModel>();
466            docsToAdd.add(srcDoc);
467            clipboardActions.putSelectionInWorkList(docsToAdd, Boolean.TRUE);
468            return debug;
469        } catch (RecoverableClientException e) {
470            throw e;
471        } catch (NuxeoException e) {
472            throw new RecoverableClientException("Cannot validate, caught exception", "message.operation.fails.generic",
473                    null, e);
474        }
475
476    }
477
478    @Override
479    @WebRemote
480    public String pasteWithId(String docId) {
481        try {
482            String debug = "pasting " + docId;
483            log.debug(debug);
484            if (docId.startsWith("pasteRef_")) {
485                docId = docId.split("pasteRef_")[1];
486            }
487            if (docId.startsWith("docClipboardRef:")) {
488                docId = docId.split("docClipboardRef:")[1];
489            }
490            DocumentRef srcRef = new IdRef(docId);
491            DocumentModel srcDoc = documentManager.getDocument(srcRef);
492            List<DocumentModel> pasteDocs = new ArrayList<DocumentModel>();
493            pasteDocs.add(srcDoc);
494            clipboardActions.pasteDocumentList(pasteDocs);
495            return debug;
496        } catch (RecoverableClientException e) {
497            throw e;
498        } catch (NuxeoException e) {
499            throw new RecoverableClientException("Cannot validate, caught exception", "message.operation.fails.generic",
500                    null, e);
501        }
502    }
503
504    public void processUpload(FileUploadEvent uploadEvent) {
505        try {
506            if (fileUploadHolder != null) {
507                FileBlob blob = getBlob(uploadEvent);
508                tmpFilePaths.add(blob.getFile().getPath());
509                fileUploadHolder.getUploadedFiles().add(new NxUploadedFile(blob));
510            } else {
511                log.error("Unable to reach fileUploadHolder");
512            }
513        } catch (IOException e) {
514            log.error(e, e);
515        }
516    }
517
518    protected static String getJSFUploadTmpDirPath() {
519        String jstTmpFileDir = Framework.getProperty(NUXEO_JSF_TMP_DIR_PROP);
520        if (StringUtils.isBlank(jstTmpFileDir)) {
521            jstTmpFileDir = null;
522        }
523        return jstTmpFileDir;
524    }
525
526    public static FileBlob getBlob(FileUploadEvent uploadEvent) throws IOException {
527        // copy to a temporary file we own
528        // TODO check how we can reuse RichFaces' temporary file
529        String jstTmpFileDir = getJSFUploadTmpDirPath();
530        File tmpDir = null;
531        if (jstTmpFileDir != null) {
532            tmpDir = new File(jstTmpFileDir);
533        }
534        UploadedFile uploadedFile = uploadEvent.getUploadedFile();
535        try (InputStream in = uploadedFile.getInputStream()) {
536            FileBlob blob = new FileBlob(in, uploadedFile.getContentType(), null, tmpDir);
537            blob.setFilename(uploadedFile.getName());
538            return blob;
539        }
540    }
541
542    public void validateMultiplesUpload() throws FileNotFoundException, IOException {
543        DocumentModel current = navigationContext.getCurrentDocument();
544        validateMultipleUploadForDocument(current);
545    }
546
547    @SuppressWarnings({ "unchecked", "rawtypes" })
548    public void validateMultipleUploadForDocument(DocumentModel current) throws FileNotFoundException,
549            IOException {
550        if (!current.hasSchema(FILES_SCHEMA)) {
551            return;
552        }
553        Collection<NxUploadedFile> nxuploadFiles = getUploadedFiles();
554        try {
555            ArrayList files = (ArrayList) current.getPropertyValue(FILES_PROPERTY);
556            if (nxuploadFiles != null) {
557                for (NxUploadedFile uploadItem : nxuploadFiles) {
558                    Blob blob = uploadItem.getBlob();
559                    FileUtils.configureFileBlob(blob);
560                    HashMap<String, Object> fileMap = new HashMap<String, Object>(2);
561                    fileMap.put("file", blob);
562                    fileMap.put("filename", blob.getFilename());
563                    if (!files.contains(fileMap)) {
564                        files.add(fileMap);
565                    }
566                }
567            }
568            current.setPropertyValue(FILES_PROPERTY, files);
569            documentActions.updateDocument(current, Boolean.TRUE);
570        } finally {
571            if (nxuploadFiles != null) {
572                for (NxUploadedFile uploadItem : nxuploadFiles) {
573                    File tempFile = uploadItem.getFile();
574                    // Tmp file that have been moved are assumed to not be temporary anymore
575                    if (tempFile != null && tempFile.exists() && tmpFilePaths.contains(tempFile.getPath())) {
576                        Framework.trackFile(tempFile, tempFile);
577                    }
578                }
579            }
580            tmpFilePaths.clear();
581        }
582    }
583
584    @SuppressWarnings({ "rawtypes" })
585    public void performAction(ActionEvent event) {
586        FacesContext context = FacesContext.getCurrentInstance();
587        ExternalContext eContext = context.getExternalContext();
588        String index = eContext.getRequestParameterMap().get("index");
589
590        try {
591            DocumentModel current = navigationContext.getCurrentDocument();
592            if (!current.hasSchema(FILES_SCHEMA)) {
593                return;
594            }
595            ArrayList files = (ArrayList) current.getPropertyValue(FILES_PROPERTY);
596            Object file = CollectionUtils.get(files, Integer.valueOf(index).intValue());
597            files.remove(file);
598            current.setPropertyValue(FILES_PROPERTY, files);
599            documentActions.updateDocument(current, Boolean.TRUE);
600        } catch (IndexOutOfBoundsException | NuxeoException e) {
601            log.error(e, e);
602            throw e;
603        }
604    }
605
606    public String validate() {
607
608        NxUploadedFile uploadedFile;
609        if (fileUploadHolder == null || fileUploadHolder.getUploadedFiles().isEmpty()
610                || (uploadedFile = fileUploadHolder.getUploadedFiles().iterator().next()) == null) {
611            facesMessages.add(StatusMessage.Severity.ERROR, messages.get("fileImporter.error.nullUploadedFile"));
612            return null;
613        }
614        try {
615            return addFile();
616        } catch (RecoverableClientException e) {
617            throw e;
618        } catch (NuxeoException e) {
619            throw new RecoverableClientException("Cannot validate, caught exception", "message.operation.fails.generic",
620                    null, e);
621        } finally {
622            if (uploadedFile != null && uploadedFile.getFile().exists()) {
623                Framework.trackFile(uploadedFile.getFile(), uploadedFile.getFile());
624            }
625        }
626    }
627
628    @Override
629    public InputStream getFileUpload() {
630        if (fileUploadHolder != null) {
631            return fileUploadHolder.getFileUpload();
632        } else {
633            return null;
634        }
635    }
636
637    @Override
638    public void setFileUpload(InputStream fileUpload) {
639        if (fileUploadHolder != null) {
640            fileUploadHolder.setFileUpload(fileUpload);
641        }
642    }
643
644    @Override
645    public String getFileName() {
646        if (fileUploadHolder != null) {
647            return fileUploadHolder.getFileName();
648        }
649        return null;
650    }
651
652    @Override
653    public void setFileName(String fileName) {
654        if (fileUploadHolder != null) {
655            fileUploadHolder.setFileName(fileName);
656        }
657    }
658
659    public DocumentModel getChangeableDocument() {
660        return navigationContext.getChangeableDocument();
661    }
662
663    public void setChangeableDocument(DocumentModel changeableDocument) {
664        navigationContext.setChangeableDocument(changeableDocument);
665    }
666
667    public Collection<NxUploadedFile> getUploadedFiles() {
668        if (fileUploadHolder != null) {
669            return fileUploadHolder.getUploadedFiles();
670        } else {
671            return null;
672        }
673    }
674
675    public void setUploadedFiles(Collection<NxUploadedFile> uploadedFiles) {
676        if (fileUploadHolder != null) {
677            fileUploadHolder.setUploadedFiles(uploadedFiles);
678        }
679        tmpFilePaths.clear();
680        if (uploadedFiles != null) {
681            for (NxUploadedFile file : uploadedFiles) {
682                tmpFilePaths.add(file.getFile().getPath());
683            }
684        }
685    }
686
687    @Override
688    @WebRemote
689    public String removeSingleUploadedFile() {
690        return removeAllUploadedFile();
691    }
692
693    @Override
694    public void setFileToRemove(String fileToRemove) {
695        this.fileToRemove = fileToRemove;
696    }
697
698    @Override
699    public String removeOneOrAllUploadedFiles(ActionEvent action) {
700        if (StringUtils.isBlank(fileToRemove)) {
701            return removeAllUploadedFile();
702        } else {
703            return removeUploadedFile(fileToRemove);
704        }
705    }
706
707    @Override
708    @WebRemote
709    public String removeAllUploadedFile() {
710        if (fileUploadHolder != null) {
711            Collection<NxUploadedFile> files = getUploadedFiles();
712            if (files != null) {
713                for (NxUploadedFile item : files) {
714                    item.getFile().delete();
715                }
716            }
717            setUploadedFiles(new ArrayList<NxUploadedFile>());
718        }
719        return "";
720    }
721
722    @Override
723    @WebRemote
724    public String removeUploadedFile(String fileName) {
725        NxUploadedFile fileToDelete = null;
726
727        // Retrieve only the real filename
728        // IE stores the full path of the file as the filename (ie.
729        // Z:\\path\\to\\file)
730        fileName = FilenameUtils.getName(fileName);
731        Collection<NxUploadedFile> files = getUploadedFiles();
732        if (files != null) {
733            for (NxUploadedFile file : files) {
734                String uploadedFileName = file.getName();
735                if (fileName.equals(uploadedFileName)) {
736                    fileToDelete = file;
737                    break;
738                }
739            }
740        }
741        if (fileToDelete != null) {
742            fileToDelete.getFile().delete();
743            files.remove(fileToDelete);
744            setUploadedFiles(files);
745        }
746        return "";
747    }
748
749}