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