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}