aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngo Kegel <ingo.kegel@ej-technologies.com>2014-10-28 13:45:19 +0300
committerHenri Sara <hesara@vaadin.com>2016-06-01 13:32:57 +0300
commitc0babd786c4d2dd3bb22e343691ce4b3aded0c3f (patch)
treeb2b53691e163bdb6d794248ce2cb8008b602d4c6
parentfb63a60cc40133a0062dd77b9694d80b1b39155c (diff)
downloadvaadin-framework-c0babd786c4d2dd3bb22e343691ce4b3aded0c3f.tar.gz
vaadin-framework-c0babd786c4d2dd3bb22e343691ce4b3aded0c3f.zip
New login form (#8171)
The legacy LoginForm is substituted with the new one which is compatible in API. The patch contains modified code from the addon https://vaadin.com/directory#addon/loginform Change-Id: I2178291c97c2f66840f832a0bf932271534beb49
-rw-r--r--all/src/main/templates/release-notes.html3
-rw-r--r--client/src/main/java/com/vaadin/client/ui/loginform/LoginFormConnector.java194
-rw-r--r--client/src/main/java/com/vaadin/client/ui/loginform/VLoginForm.java27
-rw-r--r--server/src/main/java/com/vaadin/ui/LoginForm.java492
-rw-r--r--shared/src/main/java/com/vaadin/shared/ui/loginform/LoginFormConstants.java31
-rw-r--r--shared/src/main/java/com/vaadin/shared/ui/loginform/LoginFormRpc.java23
-rw-r--r--shared/src/main/java/com/vaadin/shared/ui/loginform/LoginFormState.java28
7 files changed, 566 insertions, 232 deletions
diff --git a/all/src/main/templates/release-notes.html b/all/src/main/templates/release-notes.html
index 9d942d3134..a6ff88e79e 100644
--- a/all/src/main/templates/release-notes.html
+++ b/all/src/main/templates/release-notes.html
@@ -116,6 +116,9 @@
<li>The return type of BootstrapHandler.getWidgetsetForUI() has changed.</li>
<li>Vaadin shared no longer depends on a custom build of Guava. Any project that depends on Guava as a transitive dependency should use standard Guava.</li>
<li>Valo theme field error styles now apply to NativeSelect, ListSelect and TwinColSelect as well.</li>
+ <li>The LoginForm component has been rewritten to better support auto-completion of passwords on modern browsers.
+ Its API, look and DOM structure have changed somewhat, so any application level customizations of LoginForm need changes.
+ See also <a href="http://dev.vaadin.com/ticket/8171">#8171</a></li>
<li>The way we handle GWT dependencies has been completely changed. See <a href="#gwtdep">this section</a> for more details</li>
</ul>
<h3 id="knownissues">Known Issues and Limitations</h3>
diff --git a/client/src/main/java/com/vaadin/client/ui/loginform/LoginFormConnector.java b/client/src/main/java/com/vaadin/client/ui/loginform/LoginFormConnector.java
new file mode 100644
index 0000000000..da80287826
--- /dev/null
+++ b/client/src/main/java/com/vaadin/client/ui/loginform/LoginFormConnector.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2000-2014 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.client.ui.loginform;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyDownEvent;
+import com.google.gwt.event.dom.client.KeyDownHandler;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.FocusWidget;
+import com.google.gwt.user.client.ui.FormPanel;
+import com.vaadin.client.ComponentConnector;
+import com.vaadin.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.client.communication.StateChangeEvent;
+import com.vaadin.client.ui.AbstractSingleComponentContainerConnector;
+import com.vaadin.client.ui.VTextField;
+import com.vaadin.client.ui.button.ButtonConnector;
+import com.vaadin.client.ui.nativebutton.NativeButtonConnector;
+import com.vaadin.client.ui.textfield.TextFieldConnector;
+import com.vaadin.shared.Connector;
+import com.vaadin.shared.ui.Connect;
+import com.vaadin.shared.ui.loginform.LoginFormConstants;
+import com.vaadin.shared.ui.loginform.LoginFormRpc;
+import com.vaadin.shared.ui.loginform.LoginFormState;
+import com.google.gwt.core.client.Scheduler;
+
+@Connect(com.vaadin.ui.LoginForm.class)
+public class LoginFormConnector extends
+ AbstractSingleComponentContainerConnector {
+
+ private VTextField passwordField;
+ private VTextField userField;
+ private LoginFormRpc loginFormRpc;
+
+ @Override
+ public void updateCaption(ComponentConnector connector) {
+
+ }
+
+ @Override
+ public VLoginForm getWidget() {
+ return (VLoginForm) super.getWidget();
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+
+ loginFormRpc = getRpcProxy(LoginFormRpc.class);
+ getWidget().addSubmitCompleteHandler(new FormPanel.SubmitCompleteHandler() {
+ @Override
+ public void onSubmitComplete(FormPanel.SubmitCompleteEvent event) {
+ valuesChanged();
+ loginFormRpc.submitCompleted();
+ }
+ });
+ }
+
+ @Override
+ public LoginFormState getState() {
+ return (LoginFormState) super.getState();
+ }
+
+ @Override
+ public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+ ComponentConnector content = getContent();
+ if (content != null) {
+ getWidget().setWidget(getContentWidget());
+ }
+ }
+
+ @Override
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
+
+ LoginFormState state = getState();
+ userField = configureTextField(state.userNameFieldConnector, "username");
+ passwordField = configureTextField(state.passwordFieldConnector,
+ "password");
+ addSubmitButtonClickHandler(state.loginButtonConnector);
+ getWidget().setAction(
+ getResourceUrl(LoginFormConstants.LOGIN_RESOURCE_NAME));
+ }
+
+ private VTextField configureTextField(Connector connector, String id) {
+ if (connector != null) {
+ VTextField textField = ((TextFieldConnector) connector).getWidget();
+
+ textField.addKeyDownHandler(new SubmitKeyHandler());
+
+ Element element = textField.getElement();
+ String externalId = element.getId();
+ if (externalId == null || externalId.isEmpty()
+ || externalId.startsWith("gwt-")) {
+ element.setId(id);
+ }
+ DOM.setElementAttribute(element, "name", id);
+ DOM.setElementAttribute(element, "autocomplete", "on");
+
+ return textField;
+ } else {
+ return null;
+ }
+ }
+
+ private void loginLater() {
+ Scheduler.get().scheduleFixedDelay(new Scheduler.RepeatingCommand() {
+ @Override
+ public boolean execute() {
+ login();
+ return false;
+ }
+ }, 100);
+ }
+
+ private void login() {
+ getWidget().submit();
+ }
+
+ private void addSubmitButtonClickHandler(Connector buttonConnector) {
+ if (buttonConnector instanceof ButtonConnector) {
+ addSubmitButtonClickHandler(((ButtonConnector) buttonConnector)
+ .getWidget());
+ } else if (buttonConnector instanceof NativeButtonConnector) {
+ addSubmitButtonClickHandler(((NativeButtonConnector) buttonConnector)
+ .getWidget());
+ }
+ }
+
+ private void addSubmitButtonClickHandler(FocusWidget button) {
+ button.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ login();
+ }
+ });
+ }
+
+ private void valuesChanged() {
+ if (passwordField != null) {
+ passwordField.valueChange(true);
+ }
+ if (userField != null) {
+ userField.valueChange(true);
+ }
+ }
+
+ private class SubmitKeyHandler implements KeyDownHandler {
+
+ private int previousKeyCode;
+
+ @Override
+ public void onKeyDown(KeyDownEvent event) {
+ int keyCode = event.getNativeKeyCode();
+ if (keyCode == KeyCodes.KEY_ENTER) {
+ if (isInAutoComplete()) {
+ previousKeyCode = keyCode;
+ } else {
+ loginLater();
+ }
+ } else {
+ previousKeyCode = keyCode;
+ }
+ }
+
+ private boolean isInAutoComplete() {
+ switch (previousKeyCode) {
+ case KeyCodes.KEY_PAGEUP:
+ case KeyCodes.KEY_PAGEDOWN:
+ case KeyCodes.KEY_UP:
+ case KeyCodes.KEY_DOWN:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+}
diff --git a/client/src/main/java/com/vaadin/client/ui/loginform/VLoginForm.java b/client/src/main/java/com/vaadin/client/ui/loginform/VLoginForm.java
new file mode 100644
index 0000000000..e3dccd1ba5
--- /dev/null
+++ b/client/src/main/java/com/vaadin/client/ui/loginform/VLoginForm.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2000-2014 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.client.ui.loginform;
+
+import com.google.gwt.user.client.ui.FormPanel;
+
+public class VLoginForm extends FormPanel {
+
+ public VLoginForm() {
+ getElement().setId("loginForm");
+ setMethod(METHOD_POST);
+ }
+}
diff --git a/server/src/main/java/com/vaadin/ui/LoginForm.java b/server/src/main/java/com/vaadin/ui/LoginForm.java
index 1f0e3fc3b3..9df5f7343e 100644
--- a/server/src/main/java/com/vaadin/ui/LoginForm.java
+++ b/server/src/main/java/com/vaadin/ui/LoginForm.java
@@ -13,181 +13,48 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
+
package com.vaadin.ui;
-import java.io.IOException;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.Map;
-import com.vaadin.server.ConnectorResource;
-import com.vaadin.server.ExternalResource;
-import com.vaadin.server.VaadinRequest;
-import com.vaadin.server.VaadinResponse;
-import com.vaadin.server.VaadinService;
-import com.vaadin.server.VaadinServletService;
-import com.vaadin.shared.ApplicationConstants;
+import com.vaadin.server.StreamResource;
+import com.vaadin.shared.ui.loginform.LoginFormConstants;
+import com.vaadin.shared.ui.loginform.LoginFormRpc;
+import com.vaadin.shared.ui.loginform.LoginFormState;
/**
- * LoginForm is a Vaadin component to handle common problem among Ajax
- * applications: browsers password managers don't fill dynamically created forms
- * like all those UI elements created by Vaadin.
+ * Login form with auto-completion and auto-fill for all major browsers. You can
+ * derive from this class and implement the
+ * {@link #createContent(com.vaadin.ui.TextField, com.vaadin.ui.PasswordField, com.vaadin.ui.Button)}
+ * method to build the layout using the text fields and login button that are
+ * passed to that method. The supplied components are specially treated so that
+ * they work with password managers.
* <p>
- * For developer it is easy to use: add component to a desired place in you UI
- * and add LoginListener to validate form input. Behind the curtain LoginForm
- * creates an iframe with static html that browsers detect.
+ * If you need to change the URL as part of the login procedure, call
+ * {@link #setLoginMode(LoginMode)} with the argument {@link LoginMode#DEFERRED}
+ * in your implementation of
+ * {@link #createContent(com.vaadin.ui.TextField, com.vaadin.ui.PasswordField, com.vaadin.ui.Button)
+ * createContent}.
* <p>
- * Login form is by default 100% width and height, so consider using it inside a
- * sized {@link Panel} or {@link Window}.
+ * To customize the fields or to replace them with your own implementations, you
+ * can override {@link #createUsernameField()}, {@link #createPasswordField()}
+ * and {@link #createLoginButton()}. These methods are called automatically and
+ * cannot be called by your code. Captions can be reset by overriding
+ * {@link #getUsernameFieldCaption()}, {@link #getPasswordFieldCaption()} and
+ * {@link #getLoginButtonCaption()}.
* <p>
- * Login page html can be overridden by replacing protected getLoginHTML method.
- * As the login page is actually an iframe, styles must be handled manually. By
- * default component tries to guess the right place for theme css.
- *
+ * Note that the API of LoginForm changed significantly in Vaadin 7.7.
+ *
* @since 5.3
- * @deprecated As of 7.0. This component no longer fulfills its duty reliably in
- * the supported browsers and a {@link VerticalLayout} with two
- * {@link TextField}s can be used instead.
*/
-@Deprecated
-public class LoginForm extends CustomComponent {
-
- private String usernameCaption = "Username";
- private String passwordCaption = "Password";
- private String loginButtonCaption = "Login";
-
- private Embedded iframe = new Embedded();
-
- @Override
- public boolean handleConnectorRequest(final VaadinRequest request,
- final VaadinResponse response, String path) throws IOException {
- if (!path.equals("login")) {
- return super.handleConnectorRequest(request, response, path);
- }
- final StringBuilder responseBuilder = new StringBuilder();
-
- getUI().accessSynchronously(new Runnable() {
- @Override
- public void run() {
- String method = VaadinServletService.getCurrentServletRequest()
- .getMethod();
- if (method.equalsIgnoreCase("post")) {
- responseBuilder.append(handleLogin(request));
- } else {
- responseBuilder.append(getLoginHTML());
- }
- }
- });
-
- if (responseBuilder.length() > 0) {
- response.setContentType("text/html; charset=utf-8");
- response.setCacheTime(-1);
- response.getWriter().write(responseBuilder.toString());
- return true;
- } else {
- return false;
- }
- }
-
- private String handleLogin(VaadinRequest request) {
- // Ensure UI.getCurrent() works in listeners
-
- Map<String, String[]> parameters = VaadinService.getCurrentRequest()
- .getParameterMap();
-
- HashMap<String, String> params = new HashMap<String, String>();
- // expecting single params
- for (Iterator<String> it = parameters.keySet().iterator(); it.hasNext();) {
- String key = it.next();
- String value = (parameters.get(key))[0];
- params.put(key, value);
- }
- LoginEvent event = new LoginEvent(LoginForm.this, params);
- fireEvent(event);
-
- return "<html><body>Login form handled."
- + "<script type='text/javascript'>parent.parent.vaadin.forceSync();"
- + "</script></body></html>";
- }
-
- public LoginForm() {
- iframe.setType(Embedded.TYPE_BROWSER);
- iframe.setSizeFull();
- setSizeFull();
- setCompositionRoot(iframe);
- addStyleName("v-loginform");
- }
-
- @Override
- public void beforeClientResponse(boolean initial) {
- // Generate magic URL now when UI id and connectorId are known
- iframe.setSource(new ExternalResource(
- ApplicationConstants.APP_PROTOCOL_PREFIX
- + ApplicationConstants.APP_PATH + '/'
- + ConnectorResource.CONNECTOR_PATH + '/'
- + getUI().getUIId() + '/' + getConnectorId() + "/login"));
- super.beforeClientResponse(initial);
- }
-
- /**
- * Returns byte array containing login page html. If you need to override
- * the login html, use the default html as basis. Login page sets its target
- * with javascript.
- *
- * @return byte array containing login page html
- */
- protected String getLoginHTML() {
- return "<!DOCTYPE html>\n"
- + "<html>"
- + "<head><script type='text/javascript'>"
- + "var setTarget = function() {"
- + "var uri = window.location;"
- + "var f = document.getElementById('loginf');"
- + "document.forms[0].action = uri;document.forms[0].username.focus();};"
- + ""
- + "var styles = window.parent.document.styleSheets;"
- + "for(var j = 0; j < styles.length; j++) {\n"
- + "if(styles[j].href) {"
- + "var stylesheet = document.createElement('link');\n"
- + "stylesheet.setAttribute('rel', 'stylesheet');\n"
- + "stylesheet.setAttribute('type', 'text/css');\n"
- + "stylesheet.setAttribute('href', styles[j].href);\n"
- + "document.getElementsByTagName('head')[0].appendChild(stylesheet);\n"
- + "}"
- + "}\n"
- + "function submitOnEnter(e) { var keycode = e.keyCode || e.which;"
- + " if (keycode == 13) {document.forms[0].submit();} } \n"
- + "</script>"
- + "</head><body onload='setTarget();' style='margin:0;padding:0; background:transparent;' class=\""
- + ApplicationConstants.GENERATED_BODY_CLASSNAME
- + "\">"
- + "<div class='v-app v-app-loginpage "
- + getUIThemeClassName()
- + "' style=\"background:transparent;\">"
- + "<iframe name='logintarget' style='width:0;height:0;"
- + "border:0;margin:0;padding:0;display:block'></iframe>"
- + "<form id='loginf' target='logintarget' onkeypress=\"submitOnEnter(event)\" method=\"post\">"
- + "<div>"
- + usernameCaption
- + "</div><div >"
- + "<input class='v-textfield v-widget' style='display:block;' type='text' name='username'></div>"
- + "<div>"
- + passwordCaption
- + "</div>"
- + "<div><input class='v-textfield v-widget' style='display:block;' type='password' name='password'></div>"
- + "<div><div onclick=\"document.forms[0].submit();\" tabindex=\"0\" class=\"v-button\" role=\"button\" ><span class=\"v-button-wrap\"><span class=\"v-button-caption\">"
- + loginButtonCaption
- + "</span></span></div></div></form></div>" + "</body></html>";
- }
-
- private String getUIThemeClassName() {
- if (getUI() != null) {
- return getUI().getTheme();
- }
- return "";
- }
+public class LoginForm extends AbstractSingleComponentContainer {
/**
* This event is sent when login form is submitted.
@@ -225,14 +92,28 @@ public class LoginForm extends CustomComponent {
* This method is fired on each login form post.
*
* @param event
+ * Login event
*/
- public void onLogin(LoginForm.LoginEvent event);
+ public void onLogin(LoginEvent event);
}
- private static final Method ON_LOGIN_METHOD;
+ /**
+ * Internal stream source for the login URL - always returns "Success" and
+ * ignores the values received.
+ */
+ private static class LoginStreamSource implements StreamResource.StreamSource {
+ @Override
+ public InputStream getStream() {
+ try {
+ return new ByteArrayInputStream("<html>Success</html>".toString().getBytes(
+ "UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ return null;
+ }
+
+ }
+ }
- private static final String UNDEFINED_HEIGHT = "140px";
- private static final String UNDEFINED_WIDTH = "200px";
static {
try {
@@ -245,121 +126,268 @@ public class LoginForm extends CustomComponent {
}
}
+ private static final Method ON_LOGIN_METHOD;
+
+ private boolean initialized;
+
+ private String usernameCaption = "Username";
+ private String passwordCaption = "Password";
+ private String loginButtonCaption = "Login";
+
/**
- * Adds LoginListener to handle login logic
+ * Customize the user name field. Only for overriding, do not call.
*
- * @param listener
+ * @return the user name field
+ * @since 7.7
*/
- public void addLoginListener(LoginListener listener) {
- addListener(LoginEvent.class, listener, ON_LOGIN_METHOD);
+ protected TextField createUsernameField() {
+ checkInitialized();
+ TextField field = new TextField(getUsernameCaption());
+ field.focus();
+ return field;
}
/**
- * @deprecated As of 7.0, replaced by
- * {@link #addLoginListener(LoginListener)}
- **/
- @Deprecated
- public void addListener(LoginListener listener) {
- addLoginListener(listener);
+ * Returns the caption set with {@link #setUsernameCaption(String)}. Note
+ * that this method might not match what is shown to the user if
+ * {@link #createUsernameField()} has been overridden.
+ *
+ * @return user name field caption
+ */
+ public String getUsernameCaption() {
+ return usernameCaption;
}
/**
- * Removes LoginListener
+ * Set the caption of the user name field. Note that the caption can only be
+ * set with this method before the login form has been initialized
+ * (attached).
+ * <p>
+ * As an alternative to calling this method, the method
+ * {@link #createUsernameField()} can be overridden.
*
- * @param listener
+ * @param cap
+ * new caption
*/
- public void removeLoginListener(LoginListener listener) {
- removeListener(LoginEvent.class, listener, ON_LOGIN_METHOD);
+ public void setUsernameCaption(String cap) {
+ usernameCaption = cap;
}
/**
- * @deprecated As of 7.0, replaced by
- * {@link #removeLoginListener(LoginListener)}
- **/
- @Deprecated
- public void removeListener(LoginListener listener) {
- removeLoginListener(listener);
+ * Customize the password field. Only for overriding, do not call.
+ *
+ * @return the password field
+ * @since 7.7
+ */
+ protected PasswordField createPasswordField() {
+ checkInitialized();
+ return new PasswordField(getPasswordCaption());
}
- @Override
- public void setWidth(float width, Unit unit) {
- super.setWidth(width, unit);
- if (iframe != null) {
- if (width < 0) {
- iframe.setWidth(UNDEFINED_WIDTH);
- } else {
- iframe.setWidth("100%");
- }
- }
+ /**
+ * Returns the caption set with {@link #setPasswordCaption(String)}. Note
+ * that this method might not match what is shown to the user if
+ * {@link #createPasswordField()} has been overridden.
+ *
+ * @return password field caption
+ */
+ public String getPasswordCaption() {
+ return passwordCaption;
}
- @Override
- public void setHeight(float height, Unit unit) {
- super.setHeight(height, unit);
- if (iframe != null) {
- if (height < 0) {
- iframe.setHeight(UNDEFINED_HEIGHT);
- } else {
- iframe.setHeight("100%");
- }
- }
+ /**
+ * Set the caption of the password field. Note that the caption can only be
+ * set with this method before the login form has been initialized
+ * (attached).
+ * <p>
+ * As an alternative to calling this method, the method
+ * {@link #createPasswordField()} can be overridden.
+ *
+ * @param cap new caption
+ */
+ public void setPasswordCaption(String cap) {
+ passwordCaption = cap;
+ ;
}
/**
- * Returns the caption for the user name field.
+ * Customize the login button. Only for overriding, do not call.
*
- * @return String
+ * @return the login button
+ * @since 7.7
*/
- public String getUsernameCaption() {
- return usernameCaption;
+ protected Button createLoginButton() {
+ checkInitialized();
+ return new Button(getLoginButtonCaption());
}
/**
- * Sets the caption to show for the user name field. The caption cannot be
- * changed after the form has been shown to the user.
+ * Returns the caption set with {@link #setLoginButtonCaption(String)}. Note
+ * that this method might not match what is shown to the user if
+ * {@link #createLoginButton()} has been overridden.
*
- * @param usernameCaption
+ * @return login button caption
*/
- public void setUsernameCaption(String usernameCaption) {
- this.usernameCaption = usernameCaption;
+ public String getLoginButtonCaption() {
+ return loginButtonCaption;
}
/**
- * Returns the caption for the password field.
+ * Set the caption of the login button. Note that the caption can only be
+ * set with this method before the login form has been initialized
+ * (attached).
+ * <p>
+ * As an alternative to calling this method, the method
+ * {@link #createLoginButton()} can be overridden.
*
- * @return String
+ * @param cap new caption
*/
- public String getPasswordCaption() {
- return passwordCaption;
+ public void setLoginButtonCaption(String cap) {
+ loginButtonCaption = cap;
+ }
+
+ @Override
+ protected LoginFormState getState() {
+ return (LoginFormState) super.getState();
+ }
+
+ @Override
+ public void attach() {
+ super.attach();
+ init();
+ }
+
+ private void checkInitialized() {
+ if (initialized) {
+ throw new IllegalStateException(
+ "Already initialized. The create methods may not be called explicitly.");
+ }
}
/**
- * Sets the caption to show for the password field. The caption cannot be
- * changed after the form has been shown to the user.
+ * Create the content for the login form with the supplied user name field,
+ * password field and the login button. You cannot use any other text fields
+ * or buttons for this purpose. To replace these components with your own
+ * implementations, override {@link #createUsernameField()},
+ * {@link #createPasswordField()} and {@link #createLoginButton()}. If you
+ * only want to change the default captions, override
+ * {@link #getUsernameFieldCaption()}, {@link #getPasswordFieldCaption()}
+ * and {@link #getLoginButtonCaption()}. You do not have to use the login
+ * button in your layout.
*
- * @param passwordCaption
+ * @param userNameField
+ * the user name text field
+ * @param passwordField
+ * the password field
+ * @param loginButton
+ * the login button
+ * @return content component
+ * @since 7.7
*/
- public void setPasswordCaption(String passwordCaption) {
- this.passwordCaption = passwordCaption;
+ protected Component createContent(TextField userNameField,
+ PasswordField passwordField, Button loginButton) {
+ VerticalLayout layout = new VerticalLayout();
+ layout.setSpacing(true);
+ layout.setMargin(true);
+ layout.addComponent(userNameField);
+ layout.addComponent(passwordField);
+ layout.addComponent(loginButton);
+ return layout;
+ }
+
+ private void init() {
+ if (initialized) {
+ return;
+ }
+
+ LoginFormState state = getState();
+ state.userNameFieldConnector = createUsernameField();
+ state.passwordFieldConnector = createPasswordField();
+ state.loginButtonConnector = createLoginButton();
+
+ StreamResource resource = new StreamResource(new LoginStreamSource(),
+ LoginFormConstants.LOGIN_RESOURCE_NAME);
+ resource.setMIMEType("text/html; charset=utf-8");
+ resource.setCacheTime(-1);
+ setResource(LoginFormConstants.LOGIN_RESOURCE_NAME, resource);
+
+ registerRpc(new LoginFormRpc() {
+ @Override
+ public void submitCompleted() {
+ login();
+ }
+ });
+
+ initialized = true;
+
+ setContent(createContent(getUsernameField(), getPasswordField(),
+ getLoginButton()));
+ }
+
+ private TextField getUsernameField() {
+ return (TextField) getState().userNameFieldConnector;
+ }
+
+ private PasswordField getPasswordField() {
+ return (PasswordField) getState().passwordFieldConnector;
+ }
+
+ private Button getLoginButton() {
+ return (Button) getState().loginButtonConnector;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * Handle the login. In deferred mode, this method is called after the dummy
+ * POST request that triggers the password manager has been completed. In
+ * direct mode (the default setting), it is called directly when the user
+ * hits the enter key or clicks on the login button. In the latter case, you
+ * cannot change the URL in the method or the password manager will not be
+ * triggered.
+ */
+ private void login() {
+ HashMap<String, String> params = new HashMap<String, String>();
+ params.put("username", getUsernameField().getValue());
+ params.put("password", getPasswordField().getValue());
+ LoginEvent event = new LoginEvent(LoginForm.this, params);
+ fireEvent(event);
}
/**
- * Returns the caption for the login button.
+ * Adds LoginListener to handle login logic
*
- * @return String
+ * @param listener
*/
- public String getLoginButtonCaption() {
- return loginButtonCaption;
+ public void addLoginListener(LoginListener listener) {
+ addListener(LoginEvent.class, listener, ON_LOGIN_METHOD);
+ }
+
+ /**
+ * @deprecated As of 7.0, replaced by
+ * {@link #addLoginListener(LoginListener)}
+ **/
+ @Deprecated
+ public void addListener(LoginListener listener) {
+ addLoginListener(listener);
}
/**
- * Sets the caption (button text) to show for the login button. The caption
- * cannot be changed after the form has been shown to the user.
+ * Removes LoginListener
*
- * @param loginButtonCaption
+ * @param listener
*/
- public void setLoginButtonCaption(String loginButtonCaption) {
- this.loginButtonCaption = loginButtonCaption;
+ public void removeLoginListener(LoginListener listener) {
+ removeListener(LoginEvent.class, listener, ON_LOGIN_METHOD);
+ }
+
+ /**
+ * @deprecated As of 7.0, replaced by
+ * {@link #removeLoginListener(LoginListener)}
+ **/
+ @Deprecated
+ public void removeListener(LoginListener listener) {
+ removeLoginListener(listener);
}
}
diff --git a/shared/src/main/java/com/vaadin/shared/ui/loginform/LoginFormConstants.java b/shared/src/main/java/com/vaadin/shared/ui/loginform/LoginFormConstants.java
new file mode 100644
index 0000000000..9f0c256881
--- /dev/null
+++ b/shared/src/main/java/com/vaadin/shared/ui/loginform/LoginFormConstants.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2000-2014 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.shared.ui.loginform;
+
+import java.io.Serializable;
+
+/**
+ * Constants for LoginForm
+ *
+ * @since 7.7
+ * @author Vaadin Ltd
+ */
+public class LoginFormConstants implements Serializable {
+
+ public static final String LOGIN_RESOURCE_NAME = "loginForm";
+
+}
+
diff --git a/shared/src/main/java/com/vaadin/shared/ui/loginform/LoginFormRpc.java b/shared/src/main/java/com/vaadin/shared/ui/loginform/LoginFormRpc.java
new file mode 100644
index 0000000000..76f88170a5
--- /dev/null
+++ b/shared/src/main/java/com/vaadin/shared/ui/loginform/LoginFormRpc.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2000-2014 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.shared.ui.loginform;
+
+import com.vaadin.shared.communication.ServerRpc;
+
+public interface LoginFormRpc extends ServerRpc {
+ void submitCompleted();
+}
diff --git a/shared/src/main/java/com/vaadin/shared/ui/loginform/LoginFormState.java b/shared/src/main/java/com/vaadin/shared/ui/loginform/LoginFormState.java
new file mode 100644
index 0000000000..1dee06a12d
--- /dev/null
+++ b/shared/src/main/java/com/vaadin/shared/ui/loginform/LoginFormState.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2000-2014 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.shared.ui.loginform;
+
+import com.vaadin.shared.AbstractComponentState;
+import com.vaadin.shared.Connector;
+import com.vaadin.shared.communication.URLReference;
+
+public class LoginFormState extends AbstractComponentState {
+ public Connector userNameFieldConnector;
+ public Connector passwordFieldConnector;
+ public Connector loginButtonConnector;
+ public URLReference loginResource;
+}