- implemented interruption of upload #963 - added generics - changed deprecated gwt methods to 1.6 handlers - immediate mode now works (also styled as "one button upload") svn changeset:8442/svn branch:6.0tags/6.7.0.beta1
@@ -1436,6 +1436,30 @@ div.v-tree-node-leaf { | |||
clear: left; | |||
} | |||
/* ./WebContent/VAADIN/themes/base/upload/upload.css */ | |||
.v-upload-immediate { | |||
position: relative; | |||
width: 10em; | |||
margin:0; | |||
} | |||
.v-upload-immediate input { | |||
opacity: 0; | |||
filter: alpha(opacity=0); | |||
z-index:2; | |||
position: absolute; | |||
right:0; | |||
height:21px; | |||
text-align: right; | |||
} | |||
.v-upload-immediate button { | |||
position:relative; | |||
left:0; | |||
top:0; | |||
width:100%; | |||
} | |||
/* ./WebContent/VAADIN/themes/base/window/window.css */ | |||
.v-window { | |||
background: #fff; |
@@ -0,0 +1,22 @@ | |||
.v-upload-immediate { | |||
position: relative; | |||
width: 10em; | |||
margin:0; | |||
} | |||
.v-upload-immediate input { | |||
opacity: 0; | |||
filter: alpha(opacity=0); | |||
z-index:2; | |||
position: absolute; | |||
right:0; | |||
height:21px; | |||
text-align: right; | |||
} | |||
.v-upload-immediate button { | |||
position:relative; | |||
left:0; | |||
top:0; | |||
width:100%; | |||
} | |||
@@ -1436,6 +1436,30 @@ div.v-tree-node-leaf { | |||
clear: left; | |||
} | |||
/* ./WebContent/VAADIN/themes/base/upload/upload.css */ | |||
.v-upload-immediate { | |||
position: relative; | |||
width: 10em; | |||
margin:0; | |||
} | |||
.v-upload-immediate input { | |||
opacity: 0; | |||
filter: alpha(opacity=0); | |||
z-index:2; | |||
position: absolute; | |||
right:0; | |||
height:21px; | |||
text-align: right; | |||
} | |||
.v-upload-immediate button { | |||
position:relative; | |||
left:0; | |||
top:0; | |||
width:100%; | |||
} | |||
/* ./WebContent/VAADIN/themes/base/window/window.css */ | |||
.v-window { | |||
background: #fff; |
@@ -1436,6 +1436,30 @@ div.v-tree-node-leaf { | |||
clear: left; | |||
} | |||
/* ./WebContent/VAADIN/themes/base/upload/upload.css */ | |||
.v-upload-immediate { | |||
position: relative; | |||
width: 10em; | |||
margin:0; | |||
} | |||
.v-upload-immediate input { | |||
opacity: 0; | |||
filter: alpha(opacity=0); | |||
z-index:2; | |||
position: absolute; | |||
right:0; | |||
height:21px; | |||
text-align: right; | |||
} | |||
.v-upload-immediate button { | |||
position:relative; | |||
left:0; | |||
top:0; | |||
width:100%; | |||
} | |||
/* ./WebContent/VAADIN/themes/base/window/window.css */ | |||
.v-window { | |||
background: #fff; |
@@ -4,30 +4,53 @@ | |||
package com.vaadin.terminal.gwt.client.ui; | |||
import com.google.gwt.event.dom.client.ClickEvent; | |||
import com.google.gwt.event.dom.client.ClickHandler; | |||
import com.google.gwt.user.client.Element; | |||
import com.google.gwt.user.client.Event; | |||
import com.google.gwt.user.client.Timer; | |||
import com.google.gwt.user.client.ui.Button; | |||
import com.google.gwt.user.client.ui.ClickListener; | |||
import com.google.gwt.user.client.ui.FileUpload; | |||
import com.google.gwt.user.client.ui.FlowPanel; | |||
import com.google.gwt.user.client.ui.FormHandler; | |||
import com.google.gwt.user.client.ui.FormPanel; | |||
import com.google.gwt.user.client.ui.FormSubmitCompleteEvent; | |||
import com.google.gwt.user.client.ui.FormSubmitEvent; | |||
import com.google.gwt.user.client.ui.Hidden; | |||
import com.google.gwt.user.client.ui.Panel; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteHandler; | |||
import com.google.gwt.user.client.ui.FormPanel.SubmitHandler; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.Paintable; | |||
import com.vaadin.terminal.gwt.client.UIDL; | |||
public class VUpload extends FormPanel implements Paintable, ClickListener, | |||
FormHandler { | |||
public class VUpload extends FormPanel implements Paintable, | |||
SubmitCompleteHandler, SubmitHandler { | |||
private final class MyFileUpload extends FileUpload { | |||
@Override | |||
public void onBrowserEvent(Event event) { | |||
super.onBrowserEvent(event); | |||
if (event.getTypeInt() == Event.ONCHANGE) { | |||
if (immediate && fu.getFilename() != null | |||
&& !"".equals(fu.getFilename())) { | |||
submit(); | |||
} | |||
} else if (event.getTypeInt() == Event.ONFOCUS) { | |||
// IE and user has clicked on hidden textarea part of upload | |||
// field. Manually open file selector, other browsers do it by | |||
// default. | |||
fireNativeClick(fu.getElement()); | |||
// also remove focus to enable hack if user presses cancel | |||
// button | |||
fireNativeBlur(fu.getElement()); | |||
} | |||
} | |||
} | |||
public static final String CLASSNAME = "v-upload"; | |||
/** | |||
* FileUpload component that opens native OS dialog to select file. | |||
*/ | |||
FileUpload fu = new FileUpload(); | |||
FileUpload fu = new MyFileUpload(); | |||
Panel panel = new FlowPanel(); | |||
@@ -56,18 +79,34 @@ public class VUpload extends FormPanel implements Paintable, ClickListener, | |||
private boolean enabled = true; | |||
private boolean immediate; | |||
private Hidden maxfilesize = new Hidden(); | |||
public VUpload() { | |||
super(); | |||
setEncoding(FormPanel.ENCODING_MULTIPART); | |||
setMethod(FormPanel.METHOD_POST); | |||
setWidget(panel); | |||
panel.add(maxfilesize); | |||
panel.add(fu); | |||
submitButton = new Button(); | |||
submitButton.addClickListener(this); | |||
submitButton.setStyleName("v-button"); | |||
submitButton.addClickHandler(new ClickHandler() { | |||
public void onClick(ClickEvent event) { | |||
if (immediate) { | |||
// fire click on upload (eg. focused button and hit space) | |||
fireNativeClick(fu.getElement()); | |||
} else { | |||
submit(); | |||
} | |||
} | |||
}); | |||
panel.add(submitButton); | |||
addFormHandler(this); | |||
addSubmitCompleteHandler(this); | |||
addSubmitHandler(this); | |||
setStyleName(CLASSNAME); | |||
} | |||
@@ -76,10 +115,12 @@ public class VUpload extends FormPanel implements Paintable, ClickListener, | |||
if (client.updateComponent(this, uidl, true)) { | |||
return; | |||
} | |||
setImmediate(uidl.getBooleanAttribute("immediate")); | |||
this.client = client; | |||
paintableId = uidl.getId(); | |||
setAction(client.getAppUri()); | |||
submitButton.setText(uidl.getStringAttribute("buttoncaption")); | |||
submitButton.setHTML("<span class=\"v-button-caption\">" | |||
+ uidl.getStringAttribute("buttoncaption") + "</span>"); | |||
fu.setName(paintableId + "_file"); | |||
if (uidl.hasAttribute("disabled") || uidl.hasAttribute("readonly")) { | |||
@@ -87,16 +128,76 @@ public class VUpload extends FormPanel implements Paintable, ClickListener, | |||
} else if (uidl.getBooleanAttribute("state")) { | |||
enableUploaod(); | |||
} | |||
} | |||
private void setImmediate(boolean booleanAttribute) { | |||
if (immediate != booleanAttribute) { | |||
immediate = booleanAttribute; | |||
if (immediate) { | |||
fu.sinkEvents(Event.ONCHANGE); | |||
fu.sinkEvents(Event.ONFOCUS); | |||
} | |||
} | |||
setStyleName(getElement(), CLASSNAME + "-immediate", immediate); | |||
} | |||
public void onClick(Widget sender) { | |||
submit(); | |||
private static native void fireNativeClick(Element element) | |||
/*-{ | |||
element.click(); | |||
}-*/; | |||
private static native void fireNativeBlur(Element element) | |||
/*-{ | |||
element.blur(); | |||
}-*/; | |||
protected void disableUpload() { | |||
submitButton.setEnabled(false); | |||
fu.setVisible(false); | |||
enabled = false; | |||
} | |||
public void onSubmit(FormSubmitEvent event) { | |||
protected void enableUploaod() { | |||
submitButton.setEnabled(true); | |||
fu.setVisible(true); | |||
enabled = true; | |||
} | |||
/** | |||
* Re-creates file input field and populates panel. This is needed as we | |||
* want to clear existing values from our current file input field. | |||
*/ | |||
private void rebuildPanel() { | |||
panel.remove(submitButton); | |||
panel.remove(fu); | |||
fu = new MyFileUpload(); | |||
fu.setName(paintableId + "_file"); | |||
fu.setVisible(enabled); | |||
panel.add(fu); | |||
panel.add(submitButton); | |||
if (immediate) { | |||
fu.sinkEvents(Event.ONCHANGE); | |||
} | |||
} | |||
public void onSubmitComplete(SubmitCompleteEvent event) { | |||
if (client != null) { | |||
if (t != null) { | |||
t.cancel(); | |||
} | |||
ApplicationConnection.getConsole().log("Submit complete"); | |||
client.sendPendingVariableChanges(); | |||
} | |||
rebuildPanel(); | |||
submitted = false; | |||
enableUploaod(); | |||
} | |||
public void onSubmit(SubmitEvent event) { | |||
if (fu.getFilename().length() == 0 || submitted || !enabled) { | |||
event.setCancelled(true); | |||
event.cancel(); | |||
ApplicationConnection | |||
.getConsole() | |||
.log( | |||
@@ -125,28 +226,4 @@ public class VUpload extends FormPanel implements Paintable, ClickListener, | |||
t.schedule(800); | |||
} | |||
protected void disableUpload() { | |||
submitButton.setEnabled(false); | |||
fu.setVisible(false); | |||
enabled = false; | |||
} | |||
protected void enableUploaod() { | |||
submitButton.setEnabled(true); | |||
fu.setVisible(true); | |||
enabled = true; | |||
} | |||
public void onSubmitComplete(FormSubmitCompleteEvent event) { | |||
if (client != null) { | |||
if (t != null) { | |||
t.cancel(); | |||
} | |||
ApplicationConnection.getConsole().log("Submit complete"); | |||
client.sendPendingVariableChanges(); | |||
} | |||
submitted = false; | |||
enableUploaod(); | |||
} | |||
} |
@@ -0,0 +1,288 @@ | |||
/* | |||
@ITMillApache2LicenseForJavaFiles@ | |||
*/ | |||
package com.vaadin.tests; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.FileNotFoundException; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.lang.management.ManagementFactory; | |||
import java.lang.management.MemoryMXBean; | |||
import com.vaadin.Application; | |||
import com.vaadin.terminal.StreamResource; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.Label; | |||
import com.vaadin.ui.Layout; | |||
import com.vaadin.ui.Link; | |||
import com.vaadin.ui.Panel; | |||
import com.vaadin.ui.ProgressIndicator; | |||
import com.vaadin.ui.Upload; | |||
import com.vaadin.ui.VerticalLayout; | |||
import com.vaadin.ui.Window; | |||
import com.vaadin.ui.Button.ClickEvent; | |||
import com.vaadin.ui.Upload.FailedEvent; | |||
import com.vaadin.ui.Upload.FailedListener; | |||
import com.vaadin.ui.Upload.FinishedEvent; | |||
import com.vaadin.ui.Upload.FinishedListener; | |||
import com.vaadin.ui.Upload.StartedEvent; | |||
import com.vaadin.ui.Upload.StartedListener; | |||
import com.vaadin.ui.Upload.SucceededEvent; | |||
import com.vaadin.ui.Upload.SucceededListener; | |||
public class TestForStyledUpload extends Application implements | |||
Upload.FinishedListener, FailedListener, SucceededListener, | |||
StartedListener { | |||
Layout main = new VerticalLayout(); | |||
TmpFileBuffer buffer = new TmpFileBuffer(); | |||
Panel status = new Panel("Uploaded file:"); | |||
private final Upload up; | |||
private final Label l; | |||
private final Label transferred = new Label(""); | |||
private final ProgressIndicator pi = new ProgressIndicator(); | |||
private final Label memoryStatus; | |||
public TestForStyledUpload() { | |||
main | |||
.addComponent(new Label( | |||
"Clicking on button b updates information about upload components status or same with garbage collector.")); | |||
up = new Upload(null, buffer); | |||
up.setButtonCaption("Select file"); | |||
up.setImmediate(true); | |||
up.addListener((FinishedListener) this); | |||
up.addListener((FailedListener) this); | |||
up.addListener((SucceededListener) this); | |||
up.addListener((StartedListener) this); | |||
up.addListener(new Upload.ProgressListener() { | |||
public void updateProgress(long readBytes, long contentLenght) { | |||
pi.setValue(new Float(readBytes / (float) contentLenght)); | |||
refreshMemUsage(); | |||
transferred.setValue("Transferred " + readBytes + " of " | |||
+ contentLenght); | |||
} | |||
}); | |||
final Button b = new Button("Update status", this, "readState"); | |||
final Button c = new Button("Update status with gc", this, "gc"); | |||
main.addComponent(up); | |||
l = new Label("Idle"); | |||
main.addComponent(l); | |||
pi.setVisible(false); | |||
pi.setPollingInterval(300); | |||
main.addComponent(pi); | |||
main.addComponent(transferred); | |||
memoryStatus = new Label(); | |||
main.addComponent(memoryStatus); | |||
status.setVisible(false); | |||
main.addComponent(status); | |||
Button cancel = new Button("Cancel current upload"); | |||
cancel.addListener(new Button.ClickListener() { | |||
public void buttonClick(ClickEvent event) { | |||
buffer.cancel(); | |||
} | |||
}); | |||
main.addComponent(cancel); | |||
final Button restart = new Button("Restart demo application"); | |||
restart.addListener(new Button.ClickListener() { | |||
public void buttonClick(ClickEvent event) { | |||
TestForStyledUpload.this.close(); | |||
} | |||
}); | |||
main.addComponent(restart); | |||
main.addComponent(b); | |||
main.addComponent(c); | |||
} | |||
public void gc() { | |||
Runtime.getRuntime().gc(); | |||
readState(); | |||
} | |||
public void readState() { | |||
final StringBuffer sb = new StringBuffer(); | |||
if (up.isUploading()) { | |||
sb.append("Uploading..."); | |||
sb.append(up.getBytesRead()); | |||
sb.append("/"); | |||
sb.append(up.getUploadSize()); | |||
sb.append(" "); | |||
sb.append(Math.round(100 * up.getBytesRead() | |||
/ (double) up.getUploadSize())); | |||
sb.append("%"); | |||
} else { | |||
sb.append("Idle"); | |||
} | |||
l.setValue(sb.toString()); | |||
refreshMemUsage(); | |||
} | |||
public void uploadFinished(FinishedEvent event) { | |||
status.removeAllComponents(); | |||
final InputStream stream = buffer.getStream(); | |||
if (stream == null) { | |||
status.addComponent(new Label( | |||
"Upload finished, but output buffer is null!!")); | |||
} else { | |||
status | |||
.addComponent(new Label("<b>Name:</b> " | |||
+ event.getFilename(), Label.CONTENT_XHTML)); | |||
status.addComponent(new Label("<b>Mimetype:</b> " | |||
+ event.getMIMEType(), Label.CONTENT_XHTML)); | |||
status.addComponent(new Label("<b>Size:</b> " + event.getLength() | |||
+ " bytes.", Label.CONTENT_XHTML)); | |||
status.addComponent(new Link("Download " + buffer.getFileName(), | |||
new StreamResource(buffer, buffer.getFileName(), this))); | |||
status.setVisible(true); | |||
} | |||
} | |||
public interface Buffer extends StreamResource.StreamSource, | |||
Upload.Receiver { | |||
String getFileName(); | |||
} | |||
public class TmpFileBuffer implements Buffer { | |||
String mimeType; | |||
String fileName; | |||
private File file; | |||
private FileInputStream stream; | |||
public TmpFileBuffer() { | |||
final String tempFileName = "upload_tmpfile_" | |||
+ System.currentTimeMillis(); | |||
try { | |||
file = File.createTempFile(tempFileName, null); | |||
} catch (final IOException e) { | |||
// TODO Auto-generated catch block | |||
e.printStackTrace(); | |||
} | |||
} | |||
public void cancel() { | |||
up.interruptUpload(); | |||
} | |||
public InputStream getStream() { | |||
if (file == null) { | |||
return null; | |||
} | |||
try { | |||
stream = new FileInputStream(file); | |||
return stream; | |||
} catch (final FileNotFoundException e) { | |||
// TODO Auto-generated catch block | |||
e.printStackTrace(); | |||
} | |||
return null; | |||
} | |||
/** | |||
* @see com.vaadin.ui.Upload.Receiver#receiveUpload(String, String) | |||
*/ | |||
public OutputStream receiveUpload(String filename, String MIMEType) { | |||
fileName = filename; | |||
mimeType = MIMEType; | |||
try { | |||
return new FileOutputStream(file); | |||
} catch (final FileNotFoundException e) { | |||
// TODO Auto-generated catch block | |||
e.printStackTrace(); | |||
} | |||
return null; | |||
} | |||
/** | |||
* Returns the fileName. | |||
* | |||
* @return String | |||
*/ | |||
public String getFileName() { | |||
return fileName; | |||
} | |||
/** | |||
* Returns the mimeType. | |||
* | |||
* @return String | |||
*/ | |||
public String getMimeType() { | |||
return mimeType; | |||
} | |||
} | |||
public void uploadFailed(FailedEvent event) { | |||
pi.setVisible(false); | |||
l.setValue("Upload was interrupted"); | |||
} | |||
public void uploadSucceeded(SucceededEvent event) { | |||
pi.setVisible(false); | |||
l.setValue("Finished upload, idle"); | |||
System.out.println(event); | |||
} | |||
private void refreshMemUsage() { | |||
// memoryStatus.setValue("Not available in Java 1.4"); | |||
StringBuffer mem = new StringBuffer(); | |||
MemoryMXBean mmBean = ManagementFactory.getMemoryMXBean(); | |||
mem.append("Heap (M):"); | |||
mem.append(mmBean.getHeapMemoryUsage().getUsed() / 1048576); | |||
mem.append(" | Non-Heap (M):"); | |||
mem.append(mmBean.getNonHeapMemoryUsage().getUsed() / 1048576); | |||
memoryStatus.setValue(mem.toString()); | |||
} | |||
public void uploadStarted(StartedEvent event) { | |||
pi.setVisible(true); | |||
l.setValue("Started uploading file " + event.getFilename()); | |||
} | |||
@Override | |||
public void init() { | |||
Window w = new Window(); | |||
w.setTheme("runo"); | |||
w.addComponent(main); | |||
setMainWindow(w); | |||
} | |||
} |
@@ -4,6 +4,7 @@ | |||
package com.vaadin.ui; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.io.Serializable; | |||
@@ -20,13 +21,16 @@ import com.vaadin.terminal.UploadStream; | |||
/** | |||
* Component for uploading files from client to server. | |||
* | |||
* <p> | |||
* The visible component consists of a file name input box and a browse button | |||
* and an upload submit button to start uploading. | |||
* | |||
* <p> | |||
* The Upload component needs a java.io.OutputStream to write the uploaded data. | |||
* You need to implement the Upload.Receiver interface and return the output | |||
* stream in the receiveUpload() method. | |||
* | |||
* <p> | |||
* You can get an event regarding starting (StartedEvent), progress | |||
* (ProgressEvent), and finishing (FinishedEvent) of upload by implementing | |||
* StartedListener, ProgressListener, and FinishedListener, respectively. The | |||
@@ -34,10 +38,16 @@ import com.vaadin.terminal.UploadStream; | |||
* to separate between these two cases, you can use SucceededListener | |||
* (SucceededEvenet) and FailedListener (FailedEvent). | |||
* | |||
* <p> | |||
* The upload component does not itself show upload progress, but you can use | |||
* the ProgressIndicator for providing progress feedback by implementing | |||
* ProgressListener and updating the indicator in updateProgress(). | |||
* | |||
* <p> | |||
* Setting upload component immediate initiates the upload as soon as a file is | |||
* selected, instead of the common pattern of file selection field and upload | |||
* button. | |||
* | |||
* @author IT Mill Ltd. | |||
* @version | |||
* @VERSION@ | |||
@@ -81,7 +91,9 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
* upload | |||
*/ | |||
private ProgressListener progressListener; | |||
private LinkedHashSet progressListeners; | |||
private LinkedHashSet<ProgressListener> progressListeners; | |||
private boolean interrupted = false; | |||
/* TODO: Add a default constructor, receive to temp file. */ | |||
@@ -169,6 +181,9 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
fireUpdateProgress(totalBytes, contentLength); | |||
} | |||
} | |||
if (interrupted) { | |||
throw new Exception("Upload interrupted by other thread"); | |||
} | |||
} | |||
// upload successful | |||
@@ -182,8 +197,15 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
} catch (final Exception e) { | |||
synchronized (application) { | |||
// Download interrupted | |||
try { | |||
// still try to close output stream | |||
out.close(); | |||
} catch (IOException e1) { | |||
// NOP | |||
} | |||
fireUploadInterrupted(filename, type, totalBytes, e); | |||
endUpload(); | |||
interrupted = false; | |||
} | |||
} | |||
} | |||
@@ -697,7 +719,7 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
*/ | |||
public void addListener(ProgressListener listener) { | |||
if (progressListeners == null) { | |||
progressListeners = new LinkedHashSet(); | |||
progressListeners = new LinkedHashSet<ProgressListener>(); | |||
} | |||
progressListeners.add(listener); | |||
} | |||
@@ -792,8 +814,9 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
// this is implemented differently than other listeners to maintain | |||
// backwards compatibility | |||
if (progressListeners != null) { | |||
for (Iterator it = progressListeners.iterator(); it.hasNext();) { | |||
ProgressListener l = (ProgressListener) it.next(); | |||
for (Iterator<ProgressListener> it = progressListeners.iterator(); it | |||
.hasNext();) { | |||
ProgressListener l = it.next(); | |||
l.updateProgress(totalBytes, contentLength); | |||
} | |||
} | |||
@@ -880,13 +903,24 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
isUploading = true; | |||
} | |||
/** | |||
* Interrupts the upload currently being received. The interruption will be | |||
* done by the receiving tread so this method will return immediately and | |||
* the actual interrupt will happen a bit later. | |||
*/ | |||
public void interruptUpload() { | |||
if (isUploading) { | |||
interrupted = true; | |||
} | |||
} | |||
/** | |||
* Go into state where new uploading can begin. | |||
* | |||
* Warning: this is an internal method used by the framework and should not | |||
* be used by user of the Upload component. | |||
*/ | |||
public void endUpload() { | |||
private void endUpload() { | |||
isUploading = false; | |||
contentLength = -1; | |||
} | |||
@@ -960,11 +994,17 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
} | |||
/** | |||
* File uploads usually have button that starts actual upload progress. This | |||
* method is used to set text in that button. | |||
* In addition to the actual file chooser, upload components have button | |||
* that starts actual upload progress. This method is used to set text in | |||
* that button. | |||
* | |||
* <p> | |||
* <strong>Note</strong> the string given is set as is to the button. HTML | |||
* formatting is not stripped. Be sure to properly validate your value | |||
* according to your needs. | |||
* | |||
* @param buttonCaption | |||
* text for uploads button. | |||
* text for upload components button. | |||
*/ | |||
public void setButtonCaption(String buttonCaption) { | |||
this.buttonCaption = buttonCaption; |