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.collections.CollectionUtils;
039import org.apache.commons.io.FilenameUtils;
040import org.apache.commons.lang.StringUtils;
041import org.apache.commons.logging.Log;
042import org.apache.commons.logging.LogFactory;
043import org.jboss.seam.ScopeType;
044import org.jboss.seam.annotations.In;
045import org.jboss.seam.annotations.Install;
046import org.jboss.seam.annotations.Name;
047import org.jboss.seam.annotations.Scope;
048import org.jboss.seam.annotations.remoting.WebRemote;
049import org.jboss.seam.core.Events;
050import org.jboss.seam.faces.FacesMessages;
051import org.jboss.seam.international.StatusMessage;
052import org.nuxeo.common.utils.Base64;
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.decode(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);
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            blob.setFilename(uploadedFile.getName());
540            return blob;
541        }
542    }
543
544    public void validateMultiplesUpload() throws FileNotFoundException, IOException {
545        DocumentModel current = navigationContext.getCurrentDocument();
546        validateMultipleUploadForDocument(current);
547    }
548
549    @SuppressWarnings({ "unchecked", "rawtypes" })
550    public void validateMultipleUploadForDocument(DocumentModel current) throws FileNotFoundException,
551            IOException {
552        if (!current.hasSchema(FILES_SCHEMA)) {
553            return;
554        }
555        Collection<NxUploadedFile> nxuploadFiles = getUploadedFiles();
556        try {
557            ArrayList files = (ArrayList) current.getPropertyValue(FILES_PROPERTY);
558            if (nxuploadFiles != null) {
559                for (NxUploadedFile uploadItem : nxuploadFiles) {
560                    Blob blob = uploadItem.getBlob();
561                    FileUtils.configureFileBlob(blob);
562                    HashMap<String, Object> fileMap = new HashMap<String, Object>(2);
563                    fileMap.put("file", blob);
564                    fileMap.put("filename", blob.getFilename());
565                    if (!files.contains(fileMap)) {
566                        files.add(fileMap);
567                    }
568                }
569            }
570            current.setPropertyValue(FILES_PROPERTY, files);
571            documentActions.updateDocument(current, Boolean.TRUE);
572        } finally {
573            if (nxuploadFiles != null) {
574                for (NxUploadedFile uploadItem : nxuploadFiles) {
575                    File tempFile = uploadItem.getFile();
576                    // Tmp file that have been moved are assumed to not be temporary anymore
577                    if (tempFile != null && tempFile.exists() && tmpFilePaths.contains(tempFile.getPath())) {
578                        Framework.trackFile(tempFile, tempFile);
579                    }
580                }
581            }
582            tmpFilePaths.clear();
583        }
584    }
585
586    @SuppressWarnings({ "rawtypes" })
587    public void performAction(ActionEvent event) {
588        FacesContext context = FacesContext.getCurrentInstance();
589        ExternalContext eContext = context.getExternalContext();
590        String index = eContext.getRequestParameterMap().get("index");
591
592        try {
593            DocumentModel current = navigationContext.getCurrentDocument();
594            if (!current.hasSchema(FILES_SCHEMA)) {
595                return;
596            }
597            ArrayList files = (ArrayList) current.getPropertyValue(FILES_PROPERTY);
598            Object file = CollectionUtils.get(files, Integer.valueOf(index).intValue());
599            files.remove(file);
600            current.setPropertyValue(FILES_PROPERTY, files);
601            documentActions.updateDocument(current, Boolean.TRUE);
602        } catch (IndexOutOfBoundsException | NuxeoException e) {
603            log.error(e, e);
604            throw e;
605        }
606    }
607
608    public String validate() {
609
610        NxUploadedFile uploadedFile;
611        if (fileUploadHolder == null || fileUploadHolder.getUploadedFiles().isEmpty()
612                || (uploadedFile = fileUploadHolder.getUploadedFiles().iterator().next()) == null) {
613            facesMessages.add(StatusMessage.Severity.ERROR, messages.get("fileImporter.error.nullUploadedFile"));
614            return null;
615        }
616        try {
617            return addFile();
618        } catch (RecoverableClientException e) {
619            throw e;
620        } catch (NuxeoException e) {
621            throw new RecoverableClientException("Cannot validate, caught exception", "message.operation.fails.generic",
622                    null, e);
623        } finally {
624            if (uploadedFile != null && uploadedFile.getFile().exists()) {
625                Framework.trackFile(uploadedFile.getFile(), uploadedFile.getFile());
626            }
627        }
628    }
629
630    @Override
631    public InputStream getFileUpload() {
632        if (fileUploadHolder != null) {
633            return fileUploadHolder.getFileUpload();
634        } else {
635            return null;
636        }
637    }
638
639    @Override
640    public void setFileUpload(InputStream fileUpload) {
641        if (fileUploadHolder != null) {
642            fileUploadHolder.setFileUpload(fileUpload);
643        }
644    }
645
646    @Override
647    public String getFileName() {
648        if (fileUploadHolder != null) {
649            return fileUploadHolder.getFileName();
650        }
651        return null;
652    }
653
654    @Override
655    public void setFileName(String fileName) {
656        if (fileUploadHolder != null) {
657            fileUploadHolder.setFileName(fileName);
658        }
659    }
660
661    public DocumentModel getChangeableDocument() {
662        return navigationContext.getChangeableDocument();
663    }
664
665    public void setChangeableDocument(DocumentModel changeableDocument) {
666        navigationContext.setChangeableDocument(changeableDocument);
667    }
668
669    public Collection<NxUploadedFile> getUploadedFiles() {
670        if (fileUploadHolder != null) {
671            return fileUploadHolder.getUploadedFiles();
672        } else {
673            return null;
674        }
675    }
676
677    public void setUploadedFiles(Collection<NxUploadedFile> uploadedFiles) {
678        if (fileUploadHolder != null) {
679            fileUploadHolder.setUploadedFiles(uploadedFiles);
680        }
681        tmpFilePaths.clear();
682        if (uploadedFiles != null) {
683            for (NxUploadedFile file : uploadedFiles) {
684                tmpFilePaths.add(file.getFile().getPath());
685            }
686        }
687    }
688
689    @Override
690    @WebRemote
691    public String removeSingleUploadedFile() {
692        return removeAllUploadedFile();
693    }
694
695    @Override
696    public void setFileToRemove(String fileToRemove) {
697        this.fileToRemove = fileToRemove;
698    }
699
700    @Override
701    public String removeOneOrAllUploadedFiles(ActionEvent action) {
702        if (StringUtils.isBlank(fileToRemove)) {
703            return removeAllUploadedFile();
704        } else {
705            return removeUploadedFile(fileToRemove);
706        }
707    }
708
709    @Override
710    @WebRemote
711    public String removeAllUploadedFile() {
712        if (fileUploadHolder != null) {
713            Collection<NxUploadedFile> files = getUploadedFiles();
714            if (files != null) {
715                for (NxUploadedFile item : files) {
716                    item.getFile().delete();
717                }
718            }
719            setUploadedFiles(new ArrayList<NxUploadedFile>());
720        }
721        return "";
722    }
723
724    @Override
725    @WebRemote
726    public String removeUploadedFile(String fileName) {
727        NxUploadedFile fileToDelete = null;
728
729        // Retrieve only the real filename
730        // IE stores the full path of the file as the filename (ie.
731        // Z:\\path\\to\\file)
732        fileName = FilenameUtils.getName(fileName);
733        Collection<NxUploadedFile> files = getUploadedFiles();
734        if (files != null) {
735            for (NxUploadedFile file : files) {
736                String uploadedFileName = file.getName();
737                if (fileName.equals(uploadedFileName)) {
738                    fileToDelete = file;
739                    break;
740                }
741            }
742        }
743        if (fileToDelete != null) {
744            fileToDelete.getFile().delete();
745            files.remove(fileToDelete);
746            setUploadedFiles(files);
747        }
748        return "";
749    }
750
751}