diff options
Diffstat (limited to 'server/src/com/vaadin/ui/LoginForm.java')
-rw-r--r-- | server/src/com/vaadin/ui/LoginForm.java | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/server/src/com/vaadin/ui/LoginForm.java b/server/src/com/vaadin/ui/LoginForm.java new file mode 100644 index 0000000000..db7e5f9dd9 --- /dev/null +++ b/server/src/com/vaadin/ui/LoginForm.java @@ -0,0 +1,353 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.ui; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +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.Application; +import com.vaadin.terminal.ApplicationResource; +import com.vaadin.terminal.DownloadStream; +import com.vaadin.terminal.RequestHandler; +import com.vaadin.terminal.WrappedRequest; +import com.vaadin.terminal.WrappedResponse; +import com.vaadin.terminal.gwt.client.ApplicationConnection; + +/** + * 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. + * <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. + * <p> + * Login form is by default 100% width and height, so consider using it inside a + * sized {@link Panel} or {@link Window}. + * <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. + * + * @since 5.3 + */ +public class LoginForm extends CustomComponent { + + private String usernameCaption = "Username"; + private String passwordCaption = "Password"; + private String loginButtonCaption = "Login"; + + private Embedded iframe = new Embedded(); + + private ApplicationResource loginPage = new ApplicationResource() { + + @Override + public Application getApplication() { + return LoginForm.this.getApplication(); + } + + @Override + public int getBufferSize() { + return getLoginHTML().length; + } + + @Override + public long getCacheTime() { + return -1; + } + + @Override + public String getFilename() { + return "login"; + } + + @Override + public DownloadStream getStream() { + return new DownloadStream(new ByteArrayInputStream(getLoginHTML()), + getMIMEType(), getFilename()); + } + + @Override + public String getMIMEType() { + return "text/html; charset=utf-8"; + } + }; + + private final RequestHandler requestHandler = new RequestHandler() { + @Override + public boolean handleRequest(Application application, + WrappedRequest request, WrappedResponse response) + throws IOException { + String requestPathInfo = request.getRequestPathInfo(); + if ("/loginHandler".equals(requestPathInfo)) { + response.setCacheTime(-1); + response.setContentType("text/html; charset=utf-8"); + response.getWriter() + .write("<html><body>Login form handled." + + "<script type='text/javascript'>parent.parent.vaadin.forceSync();" + + "</script></body></html>"); + + Map<String, String[]> parameters = request.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(params); + fireEvent(event); + return true; + } + return false; + } + }; + + public LoginForm() { + iframe.setType(Embedded.TYPE_BROWSER); + iframe.setSizeFull(); + setSizeFull(); + setCompositionRoot(iframe); + addStyleName("v-loginform"); + } + + /** + * 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 byte[] getLoginHTML() { + String appUri = getApplication().getURL().toString(); + + try { + return ("<!DOCTYPE html PUBLIC \"-//W3C//DTD " + + "XHTML 1.0 Transitional//EN\" " + + "\"http://www.w3.org/TR/xhtml1/" + + "DTD/xhtml1-transitional.dtd\">\n" + "<html>" + + "<head><script type='text/javascript'>" + + "var setTarget = function() {" + "var uri = '" + + appUri + + "loginHandler" + + "'; 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=\"" + + ApplicationConnection.GENERATED_BODY_CLASSNAME + + "\">" + + "<div class='v-app v-app-loginpage' style=\"background:transparent;\">" + + "<iframe name='logintarget' style='width:0;height:0;" + + "border:0;margin:0;padding:0;'></iframe>" + + "<form id='loginf' target='logintarget' onkeypress=\"submitOnEnter(event)\" method=\"post\">" + + "<div>" + + usernameCaption + + "</div><div >" + + "<input class='v-textfield v-connector' style='display:block;' type='text' name='username'></div>" + + "<div>" + + passwordCaption + + "</div>" + + "<div><input class='v-textfield v-connector' 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>") + .getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 encoding not avalable", e); + } + } + + @Override + public void attach() { + super.attach(); + getApplication().addResource(loginPage); + getApplication().addRequestHandler(requestHandler); + iframe.setSource(loginPage); + } + + @Override + public void detach() { + getApplication().removeResource(loginPage); + getApplication().removeRequestHandler(requestHandler); + + super.detach(); + } + + /** + * This event is sent when login form is submitted. + */ + public class LoginEvent extends Event { + + private Map<String, String> params; + + private LoginEvent(Map<String, String> params) { + super(LoginForm.this); + this.params = params; + } + + /** + * Access method to form values by field names. + * + * @param name + * @return value in given field + */ + public String getLoginParameter(String name) { + if (params.containsKey(name)) { + return params.get(name); + } else { + return null; + } + } + } + + /** + * Login listener is a class capable to listen LoginEvents sent from + * LoginBox + */ + public interface LoginListener extends Serializable { + /** + * This method is fired on each login form post. + * + * @param event + */ + public void onLogin(LoginForm.LoginEvent event); + } + + private static final Method ON_LOGIN_METHOD; + + private static final String UNDEFINED_HEIGHT = "140px"; + private static final String UNDEFINED_WIDTH = "200px"; + + static { + try { + ON_LOGIN_METHOD = LoginListener.class.getDeclaredMethod("onLogin", + new Class[] { LoginEvent.class }); + } catch (final java.lang.NoSuchMethodException e) { + // This should never happen + throw new java.lang.RuntimeException( + "Internal error finding methods in LoginForm"); + } + } + + /** + * Adds LoginListener to handle login logic + * + * @param listener + */ + public void addListener(LoginListener listener) { + addListener(LoginEvent.class, listener, ON_LOGIN_METHOD); + } + + /** + * Removes LoginListener + * + * @param listener + */ + public void removeListener(LoginListener listener) { + removeListener(LoginEvent.class, listener, ON_LOGIN_METHOD); + } + + @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%"); + } + } + } + + @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%"); + } + } + } + + /** + * Returns the caption for the user name field. + * + * @return String + */ + public String getUsernameCaption() { + return usernameCaption; + } + + /** + * Sets the caption to show for the user name field. The caption cannot be + * changed after the form has been shown to the user. + * + * @param usernameCaption + */ + public void setUsernameCaption(String usernameCaption) { + this.usernameCaption = usernameCaption; + } + + /** + * Returns the caption for the password field. + * + * @return String + */ + public String getPasswordCaption() { + return passwordCaption; + } + + /** + * Sets the caption to show for the password field. The caption cannot be + * changed after the form has been shown to the user. + * + * @param passwordCaption + */ + public void setPasswordCaption(String passwordCaption) { + this.passwordCaption = passwordCaption; + } + + /** + * Returns the caption for the login button. + * + * @return String + */ + public String getLoginButtonCaption() { + return loginButtonCaption; + } + + /** + * 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. + * + * @param loginButtonCaption + */ + public void setLoginButtonCaption(String loginButtonCaption) { + this.loginButtonCaption = loginButtonCaption; + } + +} |