001 // Copyright 2007, 2008, 2009, 2011 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 015 package org.apache.tapestry5.upload.components; 016 017 import org.apache.tapestry5.*; 018 import org.apache.tapestry5.annotations.*; 019 import org.apache.tapestry5.corelib.base.AbstractField; 020 import org.apache.tapestry5.corelib.mixins.RenderDisabled; 021 import org.apache.tapestry5.ioc.annotations.Inject; 022 import org.apache.tapestry5.services.ComponentDefaultProvider; 023 import org.apache.tapestry5.services.FieldValidatorDefaultSource; 024 import org.apache.tapestry5.services.FormSupport; 025 import org.apache.tapestry5.services.Request; 026 import org.apache.tapestry5.services.javascript.JavaScriptSupport; 027 import org.apache.tapestry5.upload.services.MultipartDecoder; 028 import org.apache.tapestry5.upload.services.UploadedFile; 029 030 import java.util.Locale; 031 032 /** 033 * A component to upload a file. 034 */ 035 @SuppressWarnings({"UnusedDeclaration"}) 036 @Events(EventConstants.VALIDATE) 037 public class Upload extends AbstractField 038 { 039 public static final String MULTIPART_ENCTYPE = "multipart/form-data"; 040 041 /** 042 * The uploaded file. Note: This is only guaranteed to be valid while processing the form submission. Subsequently 043 * the content may have been cleaned up. 044 */ 045 @Parameter(required = true, principal = true, autoconnect = true) 046 private UploadedFile value; 047 048 /** 049 * The object that will perform input validation. The "validate:" binding prefix is generally used to provide this 050 * object in a declarative fashion. 051 */ 052 @Parameter(defaultPrefix = BindingConstants.VALIDATE) 053 @SuppressWarnings("unchecked") 054 private FieldValidator<Object> validate; 055 056 @Environmental 057 private ValidationTracker tracker; 058 059 @Inject 060 private MultipartDecoder decoder; 061 062 @Environmental 063 private FormSupport formSupport; 064 065 @Inject 066 private ComponentDefaultProvider defaultProvider; 067 068 @Inject 069 private ComponentResources resources; 070 071 @Inject 072 private Locale locale; 073 074 @Inject 075 private FieldValidationSupport fieldValidationSupport; 076 077 @SuppressWarnings("unused") 078 @Mixin 079 private RenderDisabled renderDisabled; 080 081 @Inject 082 @Path("upload.js") 083 private Asset uploadScript; 084 085 @Inject 086 private Request request; 087 088 @Environmental 089 private JavaScriptSupport javaScriptSupport; 090 091 /** 092 * Computes a default value for the "validate" parameter using {@link FieldValidatorDefaultSource}. 093 */ 094 final Binding defaultValidate() 095 { 096 return defaultProvider.defaultValidatorBinding("value", resources); 097 } 098 099 public Upload() 100 { 101 } 102 103 // For testing 104 Upload(UploadedFile value, FieldValidator<Object> validate, MultipartDecoder decoder, ValidationTracker tracker, 105 ComponentResources resources, FieldValidationSupport fieldValidationSupport) 106 { 107 this.value = value; 108 if (validate != null) this.validate = validate; 109 this.decoder = decoder; 110 this.tracker = tracker; 111 this.resources = resources; 112 this.fieldValidationSupport = fieldValidationSupport; 113 } 114 115 @SuppressWarnings({"unchecked"}) 116 @Override 117 protected void processSubmission(String controlName) 118 { 119 UploadedFile uploaded = decoder.getFileUpload(controlName); 120 121 if (uploaded != null) 122 { 123 if (uploaded.getFileName() == null || uploaded.getFileName().length() == 0) uploaded = null; 124 } 125 126 try 127 { 128 fieldValidationSupport.validate(uploaded, resources, validate); 129 } catch (ValidationException ex) 130 { 131 tracker.recordError(this, ex.getMessage()); 132 } 133 134 value = uploaded; 135 } 136 137 /** 138 * Render the upload tags. 139 * 140 * @param writer Writer to output markup 141 */ 142 protected void beginRender(MarkupWriter writer) 143 { 144 formSupport.setEncodingType(MULTIPART_ENCTYPE); 145 146 writer.element("input", "type", "file", "name", getControlName(), "id", getClientId()); 147 148 validate.render(writer); 149 150 resources.renderInformalParameters(writer); 151 152 decorateInsideField(); 153 154 // TAPESTRY-2453 155 if (request.isXHR()) 156 { 157 javaScriptSupport.importJavaScriptLibrary(uploadScript); 158 javaScriptSupport.addInitializerCall("injectedUpload", getClientId()); 159 } 160 } 161 162 public void afterRender(MarkupWriter writer) 163 { 164 writer.end(); 165 } 166 167 public UploadedFile getValue() 168 { 169 return value; 170 } 171 172 Upload injectDecorator(ValidationDecorator decorator) 173 { 174 setDecorator(decorator); 175 176 return this; 177 } 178 179 Upload injectRequest(Request request) 180 { 181 this.request = request; 182 183 return this; 184 } 185 186 Upload injectFormSupport(FormSupport formSupport) 187 { 188 // We have our copy ... 189 this.formSupport = formSupport; 190 191 // As does AbstractField 192 setFormSupport(formSupport); 193 194 return this; 195 } 196 197 Upload injectFieldValidator(FieldValidator<Object> validator) 198 { 199 this.validate = validator; 200 201 return this; 202 } 203 }