    001// Copyright 2007-2013 The Apache Software Foundation
    002//
    003// Licensed under the Apache License, Version 2.0 (the "License");
    004// you may not use this file except in compliance with the License.
    005// You may obtain a copy of the License at
    006//
    007//     http://www.apache.org/licenses/LICENSE-2.0
    008//
    009// Unless required by applicable law or agreed to in writing, software
    010// distributed under the License is distributed on an "AS IS" BASIS,
    011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012// See the License for the specific language governing permissions and
    013// limitations under the License.
    014
    015package org.apache.tapestry5.upload.components;
    016
    017import org.apache.tapestry5.*;
    018import org.apache.tapestry5.annotations.Events;
    019import org.apache.tapestry5.annotations.Mixin;
    020import org.apache.tapestry5.annotations.Parameter;
    021import org.apache.tapestry5.annotations.Path;
    022import org.apache.tapestry5.corelib.base.AbstractField;
    023import org.apache.tapestry5.corelib.mixins.RenderDisabled;
    024import org.apache.tapestry5.http.services.Request;
    025import org.apache.tapestry5.ioc.annotations.Inject;
    026import org.apache.tapestry5.services.FieldValidatorDefaultSource;
    027import org.apache.tapestry5.services.FormSupport;
    028import org.apache.tapestry5.upload.services.MultipartDecoder;
    029import org.apache.tapestry5.upload.services.UploadedFile;
    030
    031import java.util.Locale;
    032
    033/**
    034 * A component to upload a file.
    035 */
    036@SuppressWarnings({"UnusedDeclaration"})
    037@Events(EventConstants.VALIDATE)
    038public class Upload extends AbstractField
    039{
    040    public static final String MULTIPART_ENCTYPE = "multipart/form-data";
    041
    042    /**
    043     * The uploaded file. Note: This is only guaranteed to be valid while processing the form submission. Subsequently
    044     * the content may have been cleaned up.
    045     */
    046    @Parameter(required = true, principal = true, autoconnect = true)
    047    private UploadedFile value;
    048
    049    /**
    050     * The object that will perform input validation. The "validate:" binding prefix is generally used to provide this
    051     * object in a declarative fashion.
    052     */
    053    @Parameter(defaultPrefix = BindingConstants.VALIDATE)
    054    @SuppressWarnings("unchecked")
    055    private FieldValidator<Object> validate;
    056
    057    @Inject
    058    private MultipartDecoder decoder;
    059
    060    @Inject
    061    private Locale locale;
    062
    063    @SuppressWarnings("unused")
    064    @Mixin
    065    private RenderDisabled renderDisabled;
    066
    067    /**
    068     * Computes a default value for the "validate" parameter using {@link FieldValidatorDefaultSource}.
    069     */
    070    final Binding defaultValidate()
    071    {
    072        return defaultProvider.defaultValidatorBinding("value", resources);
    073    }
    074
    075    public Upload()
    076    {
    077    }
    078
    079    // For testing
    080    Upload(UploadedFile value, FieldValidator<Object> validate, MultipartDecoder decoder, ValidationTracker tracker,
    081           ComponentResources resources, FieldValidationSupport fieldValidationSupport)
    082    {
    083        this.value = value;
    084        if (validate != null)
    085        {
    086            this.validate = validate;
    087        }
    088        this.decoder = decoder;
    089        this.validationTracker = tracker;
    090        this.resources = resources;
    091        this.fieldValidationSupport = fieldValidationSupport;
    092    }
    093
    094    @SuppressWarnings({"unchecked"})
    095    @Override
    096    protected void processSubmission(String controlName)
    097    {
    098        UploadedFile uploaded = decoder.getFileUpload(controlName);
    099
    100        if (uploaded != null && (uploaded.getFileName() == null || uploaded.getFileName().length() == 0))
    101        {
    102            uploaded = null;
    103        }
    104
    105        try
    106        {
    107            fieldValidationSupport.validate(uploaded, resources, validate);
    108        } catch (ValidationException ex)
    109        {
    110            validationTracker.recordError(this, ex.getMessage());
    111        }
    112
    113        value = uploaded;
    114    }
    115
    116    /**
    117     * Render the upload tags.
    118     *
    119     * @param writer
    120     *         Writer to output markup
    121     */
    122    protected void beginRender(MarkupWriter writer)
    123    {
    124        formSupport.setEncodingType(MULTIPART_ENCTYPE);
    125
    126        writer.element("input", "type", "file", "name", getControlName(), "id", getClientId(), "class", cssClass);
    127
    128        validate.render(writer);
    129
    130        resources.renderInformalParameters(writer);
    131
    132        decorateInsideField();
    133
    134        // TAPESTRY-2453
    135        if (request.isXHR())
    136        {
    137            javaScriptSupport.require("t5/core/injected-upload").with(getClientId());
    138        }
    139    }
    140
    141    /** @since 5.4 */
    142    @Override
    143    public boolean isRequired()
    144    {
    145        return validate.isRequired();
    146    }
    147
    148    public void afterRender(MarkupWriter writer)
    149    {
    150        writer.end();
    151    }
    152
    153    public UploadedFile getValue()
    154    {
    155        return value;
    156    }
    157
    158    Upload injectDecorator(ValidationDecorator decorator)
    159    {
    160        setDecorator(decorator);
    161
    162        return this;
    163    }
    164
    165    Upload injectRequest(Request request)
    166    {
    167        this.request = request;
    168
    169        return this;
    170    }
    171
    172    Upload injectFormSupport(FormSupport formSupport)
    173    {
    174        // We have our copy ...
    175        this.formSupport = formSupport;
    176
    177        // As does AbstractField
    178        setFormSupport(formSupport);
    179
    180        return this;
    181    }
    182
    183    Upload injectFieldValidator(FieldValidator<Object> validator)
    184    {
    185        this.validate = validator;
    186
    187        return this;
    188    }
    189}