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