001/*
002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Florent Guillaume
016 */
017package org.nuxeo.ecm.platform.ui.web.component.file;
018
019import java.io.IOException;
020import java.util.ArrayList;
021import java.util.Arrays;
022import java.util.List;
023
024import javax.faces.application.Application;
025import javax.faces.component.UIComponent;
026import javax.faces.component.UIInput;
027import javax.faces.component.html.HtmlInputFile;
028import javax.faces.context.FacesContext;
029import javax.faces.context.ResponseWriter;
030import javax.faces.validator.Validator;
031
032import org.apache.commons.lang.StringUtils;
033import org.nuxeo.ecm.core.api.Blob;
034import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils;
035
036/**
037 * JSF File Upload mechanism based on a standard HTML {@code <input>} field.
038 *
039 * @since 7.2
040 */
041public class HTMLBlobUploader implements JSFBlobUploader {
042
043    protected static final String UPLOAD_FACET_NAME = "upload";
044
045    public HTMLBlobUploader(String id) {
046    }
047
048    @Override
049    public String getChoice() {
050        return InputFileChoice.UPLOAD;
051    }
052
053    @Override
054    public void hookSubComponent(UIInput parent) {
055        FacesContext faces = FacesContext.getCurrentInstance();
056        Application app = faces.getApplication();
057        ComponentUtils.initiateSubComponent(parent, UPLOAD_FACET_NAME,
058                app.createComponent(faces, HtmlInputFile.COMPONENT_TYPE, NXFileRenderer.RENDERER_TYPE));
059    }
060
061    @Override
062    public void encodeBeginUpload(UIInput parent, FacesContext context, String onClick) throws IOException {
063        UIComponent facet = parent.getFacet(UPLOAD_FACET_NAME);
064        if (!(facet instanceof HtmlInputFile)) {
065            return;
066        }
067        HtmlInputFile inputFile = (HtmlInputFile) facet;
068
069        // not ours to close
070        @SuppressWarnings("resource")
071        ResponseWriter writer = context.getResponseWriter();
072
073        // encode validators info
074        long sizeMax = 0L;
075        String sizeConstraint = null;
076        List<String> authorizedExtensions = new ArrayList<String>();
077        List<String> unauthorizedExtensions = new ArrayList<String>();
078        boolean hidden = false;
079        for (Validator val : parent.getValidators()) {
080            if (val instanceof InputFileSizeValidator) {
081                InputFileSizeValidator sizeVal = (InputFileSizeValidator) val;
082                long currentSizeMax = sizeVal.getMaxSizeBytes();
083                if (currentSizeMax > sizeMax) {
084                    sizeMax = currentSizeMax;
085                    sizeConstraint = sizeVal.getMaxSize();
086                }
087            } else if (val instanceof InputFileMimetypeValidator) {
088                InputFileMimetypeValidator extVal = (InputFileMimetypeValidator) val;
089                hidden = extVal.isHidden();
090                if (extVal.isAuthorized()) {
091                    authorizedExtensions.addAll(Arrays.asList(extVal.getExtensions()));
092                } else {
093                    unauthorizedExtensions.addAll(Arrays.asList(extVal.getExtensions()));
094                }
095            }
096        }
097        List<String> constraints = new ArrayList<String>();
098        if (sizeConstraint != null) {
099            constraints.add(ComponentUtils.translate(context, "label.inputFile.maxSize", sizeConstraint));
100        }
101        if (!hidden && (!authorizedExtensions.isEmpty() || !unauthorizedExtensions.isEmpty())) {
102            if (!authorizedExtensions.isEmpty()) {
103                constraints.add(ComponentUtils.translate(context, "label.inputFile.authorizedExtensions",
104                        StringUtils.join(authorizedExtensions.toArray(), ", ")));
105            }
106            if (!unauthorizedExtensions.isEmpty()) {
107                constraints.add(ComponentUtils.translate(context, "label.inputFile.unauthorizedExtensions",
108                        StringUtils.join(unauthorizedExtensions.toArray(), ", ")));
109            }
110        }
111        if (constraints.size() > 0) {
112            writer.write("(");
113            writer.write(StringUtils.join(constraints.toArray(), ", "));
114            writer.write(")");
115            writer.write(ComponentUtils.WHITE_SPACE_CHARACTER);
116        }
117
118        // encode upload component
119        inputFile.setOnclick(onClick);
120        // TODO: add size limit info
121        ComponentUtils.encodeComponent(context, inputFile);
122    }
123
124    @Override
125    public void validateUpload(UIInput parent, FacesContext context, InputFileInfo submitted) {
126        UIComponent facet = parent.getFacet(UPLOAD_FACET_NAME);
127        if (!(facet instanceof HtmlInputFile)) {
128            return;
129        }
130        HtmlInputFile inputFile = (HtmlInputFile) facet;
131        Object submittedFile = inputFile.getSubmittedValue();
132        if (!(submittedFile instanceof Blob)) {
133            return;
134        }
135        Blob sblob = (Blob) submittedFile;
136        if (sblob.getLength() == 0) {
137            String message = context.getPartialViewContext().isAjaxRequest() ? InputFileInfo.INVALID_WITH_AJAX_MESSAGE
138                    : InputFileInfo.INVALID_FILE_MESSAGE;
139            ComponentUtils.addErrorMessage(context, parent, message);
140            parent.setValid(false);
141            return;
142        }
143        submitted.setBlob(sblob);
144        submitted.setFilename(sblob.getFilename());
145        submitted.setMimeType(sblob.getMimeType());
146    }
147
148    @Override
149    public boolean isEnabled() {
150        return true;
151    }
152
153}