Step 1 / 2 of adding a new Upload Component. Change-Id: I137d7952a540db18a616da4a944a2a44f27435datags/8.0.0.alpha4
@@ -0,0 +1,39 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.v7.client.ui; | |||
import com.vaadin.client.ui.AbstractComponentConnector; | |||
import com.vaadin.client.ui.AbstractConnector; | |||
import com.vaadin.shared.communication.ServerRpc; | |||
/** | |||
* Legacy connector for Vaadin 7 compatibility connectors. Needed because | |||
* <code>immediate</code> has been removed from {@link AbstractConnector} in | |||
* Vaadin 8. | |||
* | |||
* @author Vaadin Ltd | |||
* @since 8.0 | |||
*/ | |||
public class AbstractLegacyComponentConnector | |||
extends AbstractComponentConnector { | |||
// overridden to be visible to VUpload in the same package. Without making | |||
// it public in VUploadConnector | |||
@Override | |||
protected <T extends ServerRpc> T getRpcProxy(Class<T> rpcInterface) { | |||
return super.getRpcProxy(rpcInterface); | |||
} | |||
} |
@@ -0,0 +1,394 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.v7.client.ui; | |||
import com.google.gwt.core.client.GWT; | |||
import com.google.gwt.core.client.Scheduler; | |||
import com.google.gwt.core.client.Scheduler.ScheduledCommand; | |||
import com.google.gwt.dom.client.DivElement; | |||
import com.google.gwt.dom.client.Document; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.dom.client.FormElement; | |||
import com.google.gwt.event.dom.client.ClickEvent; | |||
import com.google.gwt.event.dom.client.ClickHandler; | |||
import com.google.gwt.user.client.Command; | |||
import com.google.gwt.user.client.Event; | |||
import com.google.gwt.user.client.Timer; | |||
import com.google.gwt.user.client.ui.FileUpload; | |||
import com.google.gwt.user.client.ui.FlowPanel; | |||
import com.google.gwt.user.client.ui.FormPanel; | |||
import com.google.gwt.user.client.ui.Hidden; | |||
import com.google.gwt.user.client.ui.Panel; | |||
import com.google.gwt.user.client.ui.SimplePanel; | |||
import com.vaadin.client.ApplicationConnection; | |||
import com.vaadin.client.BrowserInfo; | |||
import com.vaadin.client.ConnectorMap; | |||
import com.vaadin.client.StyleConstants; | |||
import com.vaadin.client.VConsole; | |||
import com.vaadin.client.ui.VButton; | |||
import com.vaadin.v7.client.ui.upload.UploadConnector; | |||
import com.vaadin.v7.client.ui.upload.UploadIFrameOnloadStrategy; | |||
import com.vaadin.v7.shared.ui.upload.UploadServerRpc; | |||
/** | |||
* | |||
* Note, we are not using GWT FormPanel as we want to listen submitcomplete | |||
* events even though the upload component is already detached. | |||
* | |||
*/ | |||
public class VUpload extends SimplePanel { | |||
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 (BrowserInfo.get().isIE() | |||
&& 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. | |||
* <p> | |||
* For internal use only. May be removed or replaced in the future. | |||
*/ | |||
public FileUpload fu = new MyFileUpload(); | |||
Panel panel = new FlowPanel(); | |||
UploadIFrameOnloadStrategy onloadstrategy = GWT | |||
.create(UploadIFrameOnloadStrategy.class); | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
public ApplicationConnection client; | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
public String paintableId; | |||
/** | |||
* Button that initiates uploading. | |||
* <p> | |||
* For internal use only. May be removed or replaced in the future. | |||
*/ | |||
public final VButton submitButton; | |||
/** | |||
* When expecting big files, programmer may initiate some UI changes when | |||
* uploading the file starts. Bit after submitting file we'll visit the | |||
* server to check possible changes. | |||
* <p> | |||
* For internal use only. May be removed or replaced in the future. | |||
*/ | |||
public Timer t; | |||
/** | |||
* some browsers tries to send form twice if submit is called in button | |||
* click handler, some don't submit at all without it, so we need to track | |||
* if form is already being submitted | |||
*/ | |||
private boolean submitted = false; | |||
private boolean enabled = true; | |||
private boolean immediate; | |||
private Hidden maxfilesize = new Hidden(); | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
public FormElement element; | |||
private com.google.gwt.dom.client.Element synthesizedFrame; | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
public int nextUploadId; | |||
public VUpload() { | |||
super(com.google.gwt.dom.client.Document.get().createFormElement()); | |||
element = getElement().cast(); | |||
setEncoding(getElement(), FormPanel.ENCODING_MULTIPART); | |||
element.setMethod(FormPanel.METHOD_POST); | |||
setWidget(panel); | |||
panel.add(maxfilesize); | |||
panel.add(fu); | |||
submitButton = new VButton(); | |||
submitButton.addClickHandler(new ClickHandler() { | |||
@Override | |||
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); | |||
setStyleName(CLASSNAME); | |||
} | |||
private static native void setEncoding(Element form, String encoding) | |||
/*-{ | |||
form.enctype = encoding; | |||
}-*/; | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
public void setImmediate(boolean booleanAttribute) { | |||
if (immediate != booleanAttribute) { | |||
immediate = booleanAttribute; | |||
if (immediate) { | |||
fu.sinkEvents(Event.ONCHANGE); | |||
fu.sinkEvents(Event.ONFOCUS); | |||
} | |||
} | |||
setStyleName(getElement(), CLASSNAME + "-immediate", immediate); | |||
} | |||
private static native void fireNativeClick(Element element) | |||
/*-{ | |||
element.click(); | |||
}-*/; | |||
private static native void fireNativeBlur(Element element) | |||
/*-{ | |||
element.blur(); | |||
}-*/; | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
public void disableUpload() { | |||
setEnabledForSubmitButton(false); | |||
if (!submitted) { | |||
// Cannot disable the fileupload while submitting or the file won't | |||
// be submitted at all | |||
fu.getElement().setPropertyBoolean("disabled", true); | |||
} | |||
enabled = false; | |||
} | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
public void enableUpload() { | |||
setEnabledForSubmitButton(true); | |||
fu.getElement().setPropertyBoolean("disabled", false); | |||
enabled = true; | |||
if (submitted) { | |||
/* | |||
* An old request is still in progress (most likely cancelled), | |||
* ditching that target frame to make it possible to send a new | |||
* file. A new target frame is created later." | |||
*/ | |||
cleanTargetFrame(); | |||
submitted = false; | |||
} | |||
} | |||
private void setEnabledForSubmitButton(boolean enabled) { | |||
submitButton.setEnabled(enabled); | |||
submitButton.setStyleName(StyleConstants.DISABLED, !enabled); | |||
} | |||
/** | |||
* 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.getElement().setPropertyBoolean("disabled", !enabled); | |||
panel.add(fu); | |||
panel.add(submitButton); | |||
if (immediate) { | |||
fu.sinkEvents(Event.ONCHANGE); | |||
} | |||
} | |||
/** | |||
* Called by JSNI (hooked via {@link #onloadstrategy}) | |||
*/ | |||
private void onSubmitComplete() { | |||
/* Needs to be run dereferred to avoid various browser issues. */ | |||
Scheduler.get().scheduleDeferred(new Command() { | |||
@Override | |||
public void execute() { | |||
if (submitted) { | |||
if (client != null) { | |||
if (t != null) { | |||
t.cancel(); | |||
} | |||
VConsole.log("VUpload:Submit complete"); | |||
((UploadConnector) ConnectorMap.get(client) | |||
.getConnector(VUpload.this)) | |||
.getRpcProxy(UploadServerRpc.class) | |||
.poll(); | |||
} | |||
rebuildPanel(); | |||
submitted = false; | |||
enableUpload(); | |||
if (!isAttached()) { | |||
/* | |||
* Upload is complete when upload is already abandoned. | |||
*/ | |||
cleanTargetFrame(); | |||
} | |||
} | |||
} | |||
}); | |||
} | |||
ScheduledCommand startUploadCmd = new ScheduledCommand() { | |||
@Override | |||
public void execute() { | |||
element.submit(); | |||
submitted = true; | |||
disableUpload(); | |||
/* | |||
* Visit server a moment after upload has started to see possible | |||
* changes from UploadStarted event. Will be cleared on complete. | |||
* | |||
* Must get the id here as the upload can finish before the timer | |||
* expires and in that case nextUploadId has been updated and is | |||
* wrong. | |||
*/ | |||
final int thisUploadId = nextUploadId; | |||
t = new Timer() { | |||
@Override | |||
public void run() { | |||
// Only visit the server if the upload has not already | |||
// finished | |||
if (thisUploadId == nextUploadId) { | |||
VConsole.log( | |||
"Visiting server to see if upload started event changed UI."); | |||
client.updateVariable(paintableId, "pollForStart", | |||
thisUploadId, true); | |||
} | |||
} | |||
}; | |||
t.schedule(800); | |||
} | |||
}; | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
public void submit() { | |||
if (submitted || !enabled) { | |||
VConsole.log("Submit cancelled (disabled or already submitted)"); | |||
return; | |||
} | |||
if (fu.getFilename().length() == 0) { | |||
VConsole.log("Submitting empty selection (no file)"); | |||
} | |||
// flush possibly pending variable changes, so they will be handled | |||
// before upload | |||
client.sendPendingVariableChanges(); | |||
// This is done as deferred because sendPendingVariableChanges is also | |||
// deferred and we want to start the upload only after the changes have | |||
// been sent to the server | |||
Scheduler.get().scheduleDeferred(startUploadCmd); | |||
} | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
public void disableTitle(boolean disable) { | |||
if (disable) { | |||
// Disable title attribute for upload element. | |||
if (BrowserInfo.get().isChrome()) { | |||
// In Chrome title has to be set to " " to make it invisible | |||
fu.setTitle(" "); | |||
} else if (BrowserInfo.get().isFirefox()) { | |||
// In FF title has to be set to empty string to make it | |||
// invisible | |||
// Method setTitle removes title attribute when it's an empty | |||
// string, so setAttribute() should be used here | |||
fu.getElement().setAttribute("title", ""); | |||
} | |||
// For other browsers absent title doesn't show default tooltip for | |||
// input element | |||
} else { | |||
fu.setTitle(null); | |||
} | |||
} | |||
@Override | |||
protected void onAttach() { | |||
super.onAttach(); | |||
if (client != null) { | |||
ensureTargetFrame(); | |||
} | |||
} | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
public void ensureTargetFrame() { | |||
if (synthesizedFrame == null) { | |||
// Attach a hidden IFrame to the form. This is the target iframe to | |||
// which the form will be submitted. We have to create the iframe | |||
// using innerHTML, because setting an iframe's 'name' property | |||
// dynamically doesn't work on most browsers. | |||
DivElement dummy = Document.get().createDivElement(); | |||
dummy.setInnerHTML("<iframe src=\"javascript:''\" name='" | |||
+ getFrameName() | |||
+ "' style='position:absolute;width:0;height:0;border:0'>"); | |||
synthesizedFrame = dummy.getFirstChildElement(); | |||
Document.get().getBody().appendChild(synthesizedFrame); | |||
element.setTarget(getFrameName()); | |||
onloadstrategy.hookEvents(synthesizedFrame, this); | |||
} | |||
} | |||
private String getFrameName() { | |||
return paintableId + "_TGT_FRAME"; | |||
} | |||
@Override | |||
protected void onDetach() { | |||
super.onDetach(); | |||
if (!submitted) { | |||
cleanTargetFrame(); | |||
} | |||
} | |||
private void cleanTargetFrame() { | |||
if (synthesizedFrame != null) { | |||
Document.get().getBody().removeChild(synthesizedFrame); | |||
onloadstrategy.unHookEvents(synthesizedFrame); | |||
synthesizedFrame = null; | |||
} | |||
} | |||
} |
@@ -0,0 +1,112 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.v7.client.ui.upload; | |||
import com.google.gwt.event.dom.client.ChangeEvent; | |||
import com.google.gwt.event.dom.client.ChangeHandler; | |||
import com.vaadin.client.ApplicationConnection; | |||
import com.vaadin.client.Paintable; | |||
import com.vaadin.client.UIDL; | |||
import com.vaadin.client.communication.StateChangeEvent; | |||
import com.vaadin.shared.EventId; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.v7.client.ui.AbstractLegacyComponentConnector; | |||
import com.vaadin.v7.client.ui.VUpload; | |||
import com.vaadin.v7.shared.ui.upload.UploadClientRpc; | |||
import com.vaadin.v7.shared.ui.upload.UploadServerRpc; | |||
import com.vaadin.v7.shared.ui.upload.UploadState; | |||
@Connect(com.vaadin.v7.ui.Upload.class) | |||
public class UploadConnector extends AbstractLegacyComponentConnector | |||
implements Paintable { | |||
public UploadConnector() { | |||
registerRpc(UploadClientRpc.class, new UploadClientRpc() { | |||
@Override | |||
public void submitUpload() { | |||
getWidget().submit(); | |||
} | |||
}); | |||
} | |||
@Override | |||
protected void init() { | |||
super.init(); | |||
getWidget().fu.addChangeHandler(new ChangeHandler() { | |||
@Override | |||
public void onChange(ChangeEvent event) { | |||
if (hasEventListener(EventId.CHANGE)) { | |||
getRpcProxy(UploadServerRpc.class) | |||
.change(getWidget().fu.getFilename()); | |||
} | |||
} | |||
}); | |||
} | |||
@Override | |||
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { | |||
if (!isRealUpdate(uidl)) { | |||
return; | |||
} | |||
if (uidl.hasAttribute("notStarted")) { | |||
getWidget().t.schedule(400); | |||
return; | |||
} | |||
getWidget().setImmediate(getState().immediate); | |||
getWidget().client = client; | |||
getWidget().paintableId = uidl.getId(); | |||
getWidget().nextUploadId = uidl.getIntAttribute("nextid"); | |||
final String action = client | |||
.translateVaadinUri(uidl.getStringVariable("action")); | |||
getWidget().element.setAction(action); | |||
if (uidl.hasAttribute("buttoncaption")) { | |||
getWidget().submitButton | |||
.setText(uidl.getStringAttribute("buttoncaption")); | |||
getWidget().submitButton.setVisible(true); | |||
} else { | |||
getWidget().submitButton.setVisible(false); | |||
} | |||
getWidget().fu.setName(getWidget().paintableId + "_file"); | |||
if (!isEnabled() || isReadOnly()) { | |||
getWidget().disableUpload(); | |||
} else if (!uidl.getBooleanAttribute("state")) { | |||
// Enable the button only if an upload is not in progress | |||
getWidget().enableUpload(); | |||
getWidget().ensureTargetFrame(); | |||
} | |||
} | |||
@Override | |||
public void onStateChanged(StateChangeEvent stateChangeEvent) { | |||
super.onStateChanged(stateChangeEvent); | |||
getWidget().disableTitle(hasTooltip()); | |||
} | |||
@Override | |||
public VUpload getWidget() { | |||
return (VUpload) super.getWidget(); | |||
} | |||
@Override | |||
public UploadState getState() { | |||
return (UploadState) super.getState(); | |||
} | |||
} |
@@ -0,0 +1,39 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.v7.client.ui.upload; | |||
import com.vaadin.v7.client.ui.VUpload; | |||
public class UploadIFrameOnloadStrategy { | |||
public native void hookEvents(com.google.gwt.dom.client.Element iframe, | |||
VUpload upload) | |||
/*-{ | |||
iframe.onload = $entry(function() { | |||
upload.@com.vaadin.client.ui.VUpload::onSubmitComplete()(); | |||
}); | |||
}-*/; | |||
/** | |||
* @param iframe | |||
* the iframe whose onLoad event is to be cleaned | |||
*/ | |||
public native void unHookEvents(com.google.gwt.dom.client.Element iframe) | |||
/*-{ | |||
iframe.onload = null; | |||
}-*/; | |||
} |
@@ -0,0 +1,42 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.v7.client.ui.upload; | |||
import com.google.gwt.dom.client.Element; | |||
import com.vaadin.v7.client.ui.VUpload; | |||
/** | |||
* IE does not have onload, detect onload via readystatechange | |||
* | |||
*/ | |||
public class UploadIFrameOnloadStrategyIE extends UploadIFrameOnloadStrategy { | |||
@Override | |||
public native void hookEvents(Element iframe, VUpload upload) | |||
/*-{ | |||
iframe.onreadystatechange = $entry(function() { | |||
if (iframe.readyState == 'complete') { | |||
upload.@com.vaadin.client.ui.VUpload::onSubmitComplete()(); | |||
} | |||
}); | |||
}-*/; | |||
@Override | |||
public native void unHookEvents(Element iframe) | |||
/*-{ | |||
iframe.onreadystatechange = null; | |||
}-*/; | |||
} |
@@ -0,0 +1,26 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.v7.shared.ui.upload; | |||
import com.vaadin.shared.communication.ClientRpc; | |||
public interface UploadClientRpc extends ClientRpc { | |||
/** | |||
* Forces the upload the send selected file to the server. | |||
*/ | |||
void submitUpload(); | |||
} |
@@ -0,0 +1,38 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.v7.shared.ui.upload; | |||
import com.vaadin.shared.communication.ServerRpc; | |||
public interface UploadServerRpc extends ServerRpc { | |||
/** | |||
* Event sent when the file name of the upload component is changed. | |||
* | |||
* @param filename | |||
* The filename | |||
*/ | |||
void change(String filename); | |||
/** | |||
* Called to poll the server to see if any changes have been made e.g. when | |||
* starting upload | |||
* | |||
* @since 7.6 | |||
*/ | |||
void poll(); | |||
} |
@@ -0,0 +1,30 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.v7.shared.ui.upload; | |||
import com.vaadin.shared.AbstractComponentState; | |||
/** | |||
* Shared state for the Upload component. | |||
* | |||
* @since 7.6 | |||
*/ | |||
public class UploadState extends AbstractComponentState { | |||
{ | |||
primaryStyleName = "v-upload"; | |||
} | |||
} |
@@ -0,0 +1,77 @@ | |||
package com.vaadin.v7.tests.components.upload; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.OutputStream; | |||
import org.apache.commons.codec.digest.DigestUtils; | |||
import com.vaadin.tests.components.TestBase; | |||
import com.vaadin.tests.util.Log; | |||
import com.vaadin.v7.ui.Upload; | |||
import com.vaadin.v7.ui.Upload.FailedEvent; | |||
import com.vaadin.v7.ui.Upload.FailedListener; | |||
import com.vaadin.v7.ui.Upload.Receiver; | |||
import com.vaadin.v7.ui.Upload.SucceededEvent; | |||
import com.vaadin.v7.ui.Upload.SucceededListener; | |||
public class TestFileUpload extends TestBase implements Receiver { | |||
private Log log = new Log(5); | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||
@Override | |||
protected void setup() { | |||
Upload u = new Upload("Upload", new Upload.Receiver() { | |||
@Override | |||
public OutputStream receiveUpload(String filename, | |||
String mimeType) { | |||
return baos; | |||
} | |||
}); | |||
u.setId("UPL"); | |||
u.addFailedListener(new FailedListener() { | |||
@Override | |||
public void uploadFailed(FailedEvent event) { | |||
String hash = DigestUtils.md5Hex(baos.toByteArray()); | |||
log.log("<span style=\"color: red;\">Upload failed. Name: " | |||
+ event.getFilename() + ", Size: " + baos.size() | |||
+ ", md5: " + hash + "</span>"); | |||
baos.reset(); | |||
} | |||
}); | |||
u.addSucceededListener(new SucceededListener() { | |||
@Override | |||
public void uploadSucceeded(SucceededEvent event) { | |||
String hash = DigestUtils.md5Hex(baos.toByteArray()); | |||
log.log("Upload finished. Name: " + event.getFilename() | |||
+ ", Size: " + baos.size() + ", md5: " + hash); | |||
baos.reset(); | |||
} | |||
}); | |||
addComponent(log); | |||
addComponent(u); | |||
} | |||
@Override | |||
public OutputStream receiveUpload(String filename, String MIMEType) { | |||
getMainWindow().showNotification("Receiving upload"); | |||
return new ByteArrayOutputStream(); | |||
} | |||
@Override | |||
protected Integer getTicketNumber() { | |||
return 6465; | |||
} | |||
@Override | |||
protected String getDescription() { | |||
return "Creates and prints an MD5 hash of any uploaded file."; | |||
} | |||
} |
@@ -0,0 +1,126 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.v7.tests.components.upload; | |||
import java.io.BufferedWriter; | |||
import java.io.File; | |||
import java.io.FileWriter; | |||
import java.io.IOException; | |||
import java.math.BigInteger; | |||
import java.security.MessageDigest; | |||
import java.security.NoSuchAlgorithmException; | |||
import java.util.List; | |||
import org.junit.Assert; | |||
import org.junit.Test; | |||
import org.openqa.selenium.By; | |||
import org.openqa.selenium.WebElement; | |||
import org.openqa.selenium.internal.WrapsElement; | |||
import org.openqa.selenium.remote.DesiredCapabilities; | |||
import org.openqa.selenium.remote.LocalFileDetector; | |||
import org.openqa.selenium.remote.RemoteWebElement; | |||
import com.vaadin.testbench.elements.UploadElement; | |||
import com.vaadin.tests.tb3.MultiBrowserTest; | |||
public class TestFileUploadTest extends MultiBrowserTest { | |||
@Override | |||
public List<DesiredCapabilities> getBrowsersToTest() { | |||
// PhantomJS fails to upload files for unknown reasons | |||
return getBrowsersExcludingPhantomJS(); | |||
} | |||
@Test | |||
public void testUploadAnyFile() throws Exception { | |||
openTestURL(); | |||
File tempFile = createTempFile(); | |||
fillPathToUploadInput(tempFile.getPath()); | |||
getSubmitButton().click(); | |||
String expected = String.format( | |||
"1. Upload finished. Name: %s, Size: %s, md5: %s", | |||
tempFile.getName(), getTempFileContents().length(), | |||
md5(getTempFileContents())); | |||
String actual = getLogRow(0); | |||
Assert.assertEquals("Upload log row does not match expected", expected, | |||
actual); | |||
} | |||
private String md5(String string) throws NoSuchAlgorithmException { | |||
byte[] digest = MessageDigest.getInstance("MD5") | |||
.digest(string.getBytes()); | |||
BigInteger bigInt = new BigInteger(1, digest); | |||
String hashtext = bigInt.toString(16); | |||
return hashtext; | |||
} | |||
/** | |||
* @return The generated temp file handle | |||
* @throws IOException | |||
*/ | |||
private File createTempFile() throws IOException { | |||
File tempFile = File.createTempFile("TestFileUpload", ".txt"); | |||
BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile)); | |||
writer.write(getTempFileContents()); | |||
writer.close(); | |||
tempFile.deleteOnExit(); | |||
return tempFile; | |||
} | |||
private String getTempFileContents() { | |||
return "This is a test file!\nRow 2\nRow3"; | |||
} | |||
private void fillPathToUploadInput(String tempFileName) throws Exception { | |||
// create a valid path in upload input element. Instead of selecting a | |||
// file by some file browsing dialog, we use the local path directly. | |||
WebElement input = getInput(); | |||
setLocalFileDetector(input); | |||
input.sendKeys(tempFileName); | |||
} | |||
private WebElement getSubmitButton() { | |||
UploadElement upload = $(UploadElement.class).first(); | |||
WebElement submitButton = upload.findElement(By.className("v-button")); | |||
return submitButton; | |||
} | |||
private WebElement getInput() { | |||
return getDriver().findElement(By.className("gwt-FileUpload")); | |||
} | |||
private void setLocalFileDetector(WebElement element) throws Exception { | |||
if (getRunLocallyBrowser() != null) { | |||
return; | |||
} | |||
if (element instanceof WrapsElement) { | |||
element = ((WrapsElement) element).getWrappedElement(); | |||
} | |||
if (element instanceof RemoteWebElement) { | |||
((RemoteWebElement) element) | |||
.setFileDetector(new LocalFileDetector()); | |||
} else { | |||
throw new IllegalArgumentException( | |||
"Expected argument of type RemoteWebElement, received " | |||
+ element.getClass().getName()); | |||
} | |||
} | |||
} |