package org.appfuse.webapp.pages; import de.agilecoders.wicket.webjars.request.resource.WebjarsJavaScriptResourceReference; import org.apache.wicket.authroles.authentication.AuthenticatedWebSession; import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.head.JavaScriptHeaderItem; import org.apache.wicket.markup.head.OnLoadHeaderItem; import org.apache.wicket.markup.html.*; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.*; import org.apache.wicket.markup.html.form.validation.FormComponentFeedbackBorder; import org.apache.wicket.model.Model; import org.apache.wicket.model.StringResourceModel; import org.apache.wicket.request.Url; import org.apache.wicket.request.cycle.RequestCycle; import org.appfuse.Constants; import org.appfuse.webapp.pages.components.PlaceholderBehavior; import org.appfuse.webapp.pages.components.RequiredBehavior; import org.wicketstuff.annotation.mount.MountPath; import java.util.Map; /** * A login page. * * @author Marcin ZajÄ…czkowski, 2010-09-02 */ @MountPath("login") public class Login extends AbstractWebPage { private TextField<String> usernameField; private TextField<String> passwordField; private CheckBox rememberMeCheckBox; @Override protected void onInitialize() { super.onInitialize(); add(createPageTitleTag("login.title")); //TODO: MZA: Add login hint with: http://code.google.com/p/visural-wicket/ Form<Void> loginForm = createLoginForm(); add(loginForm); loginForm.add(createFeedbackPanel()); loginForm.add(createPageHeading("login.heading")); createAndAddToLoginFormUsernameAndPasswordFields(loginForm); loginForm.add(createRememberMeGroup()); add(createSignupLabel()); } private void createAndAddToLoginFormUsernameAndPasswordFields(Form<Void> loginForm) { //Border to add red asterisk on error, FormComponentFeedbackIndicator could be used for something more. //TODO: MZA: Take a look at FormComponentFeedbackBorder markup (its wicket:body) as a hint for required label //TODO: MZA: How to change a background color of an input with validation error? loginForm.add(new FormComponentFeedbackBorder("border").add( (usernameField = new RequiredTextField<String>("username", Model.of(""))))); usernameField.add(new PlaceholderBehavior(getString("label.username"))); usernameField.add(new RequiredBehavior()); loginForm.add(passwordField = new PasswordTextField("password", Model.of(""))); passwordField.add(new PlaceholderBehavior(getString("label.password"))); passwordField.add(new RequiredBehavior()); } private Form<Void> createLoginForm() { return new Form<Void>("loginForm") { @Override protected void onSubmit() { authenticateUser(); } }; } //TODO: MZA: Move to if needed somewhere else @SuppressWarnings("unchecked") private Object getValueForKeyFromConfigOrReturnNullIfNoConfig(String configProperty) { Map<String, Object> config = (Map<String, Object>)getServletContext().getAttribute(Constants.CONFIG); if (config == null) { log.warn("{} context attribute is null.", Constants.CONFIG); return null; } else { return config.get(configProperty); } } @Override public void renderHead(IHeaderResponse response) { super.renderHead(response); addLoginJavaScriptToResponse(response); addInitDataOnLoadJavaScriptToResponse(response); } private void addLoginJavaScriptToResponse(IHeaderResponse response) { response.render(JavaScriptHeaderItem.forReference( new WebjarsJavaScriptResourceReference("jquery-cookie/current/jquery.cookie.js"))); response.render(JavaScriptHeaderItem.forUrl("scripts/login.js")); } private void addInitDataOnLoadJavaScriptToResponse(IHeaderResponse response) { response.render(OnLoadHeaderItem.forScript("initDataOnLoad()")); } private WebMarkupContainer createRememberMeGroup() { WebMarkupContainer rememberMeGroup = new WebMarkupContainer("rememberMeGroup"); rememberMeGroup.add(createRememberMeCheckboxInsideLabel()); boolean isRememberMeEnabled = isRememberMeEnabled(); rememberMeGroup.setVisible(isRememberMeEnabled); //TODO: MZA: How does RememberMe work? I don't see any cookie. return rememberMeGroup; } private FormComponentLabel createRememberMeCheckboxInsideLabel() { //TODO: MZA: A trick to have an input component inside a label - pack into a new component if needed somewhere else // https://cwiki.apache.org/WICKET/form-control-labels.html //TODO: MZA: How to keep fields? As class property? Local variable? just assign to a parent? rememberMeCheckBox = new CheckBox("rememberMe", new Model<Boolean>(Boolean.FALSE)); FormComponentLabel rememberMeLabel = new FormComponentLabel("rememberMeLabel", rememberMeCheckBox); Label rememberMeLabelSpan = new Label("rememberMeLabelSpan", getString("login.rememberMe")); rememberMeLabelSpan.setRenderBodyOnly(true); rememberMeLabel.add(rememberMeLabelSpan); rememberMeLabel.add(rememberMeCheckBox); return rememberMeLabel; } private boolean isRememberMeEnabled() { Boolean isRememberMeEnabled = (Boolean)getValueForKeyFromConfigOrReturnNullIfNoConfig("rememberMeEnabled"); return isRememberMeEnabled != null ? isRememberMeEnabled : false; } private Label createSignupLabel() { String absoluteSignupLink = RequestCycle.get().getUrlRenderer().renderFullUrl( Url.parse(urlFor(Signup.class, null).toString())); //TODO: MZA: There should be some better way to use URL inside a label (if not, make it an util method) String signupLabelText = new StringResourceModel("login.signup", this, null, new Object[] { absoluteSignupLink}).getString(); Label signupLabel = new Label("signupLabel", signupLabelText); signupLabel.setEscapeModelStrings(false); return signupLabel; } private void authenticateUser() { AuthenticatedWebSession session = AuthenticatedWebSession.get(); if (session.signIn(usernameField.getModelObject(), passwordField.getModelObject())) { setDefaultResponsePageIfNecessary(); } else { error(getString("errors.password.mismatch")); } } private void setDefaultResponsePageIfNecessary() { continueToOriginalDestination(); setResponsePage(getApplication().getHomePage()); } }