diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java index b9de38980..f6c7be151 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerHttpServlet.java @@ -233,6 +233,9 @@ public class FreemarkerHttpServlet extends VitroHttpServlet { // We can't use shared variables in the Freemarker configuration to store anything // except theme-specific data, because multiple portals or apps might share the same theme. So instead // we'll get all the shared variables here, and put them in both root and body maps. + // If we can eliminate this use case and use shared variables, it would simplify the implementation greatly. + // See also directives, where since there are no shared variables we have to manually put elements + // of the data model into the directive template model. public Map getSharedVariables(VitroRequest vreq, Map bodyMap) { Map map = new HashMap(); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/widgets/LoginWidget.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/widgets/LoginWidget.java index 5132ecb3c..9dadba5f4 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/widgets/LoginWidget.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/widgets/LoginWidget.java @@ -2,7 +2,7 @@ package edu.cornell.mannlib.vitro.webapp.web.widgets; -import java.util.HashMap; +import java.io.IOException; import java.util.Map; import javax.servlet.ServletContext; @@ -11,18 +11,159 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import edu.cornell.mannlib.vedit.beans.LoginStatusBean; +import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean; +import edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean.State; import freemarker.core.Environment; +import freemarker.template.TemplateModel; public class LoginWidget extends Widget { private static final Log log = LogFactory.getLog(LoginWidget.class); + private static enum Macro { + LOGIN("loginForm"), + FORCE_PASSWORD_CHANGE("forcePasswordChange"), + SERVER_ERROR("error"); + + private final String macroName; + + Macro(String macroName) { + this.macroName = macroName; + } + + public String toString() { + return macroName; + } + + } + + private static enum TemplateVariable { + LOGIN_NAME("loginName"), + FORM_ACTION("formAction"), + INFO_MESSAGE("infoMessage"), + ERROR_MESSAGE("errorMessage"), + CANCEL_URL("cancelUrl"); + + private final String variableName; + + TemplateVariable(String variableName) { + this.variableName = variableName; + } + + public String toString() { + return variableName; + } + + } + @Override protected WidgetTemplateValues process(Environment env, Map params, HttpServletRequest request, ServletContext context) { - Map map = new HashMap(); - map.put("fruit", "bananas"); - return new WidgetTemplateValues (getMarkupMacroName(), map); + + WidgetTemplateValues values = null; + TemplateModel urls = null; + + try { + urls = env.getDataModel().get("urls"); + State state = getCurrentLoginState(request); + log.debug("State on exit: " + state); + + switch (state) { + case LOGGED_IN: + return null; + case FORCED_PASSWORD_CHANGE: + values = showPasswordChangeScreen(request); + break; + default: + values = showLoginScreen(request); + } + } catch (Exception e) { + log.error(e); + values = showError(e); + } + values.put("urls", urls); + return values; + + } + + /** + * User is just starting the login process. Be sure that we have a + * {@link LoginProcessBean} with the correct status. Show them the login + * screen. + */ + private WidgetTemplateValues showLoginScreen(HttpServletRequest request) + throws IOException { + LoginProcessBean bean = LoginProcessBean.getBean(request); + bean.setState(State.LOGGING_IN); + log.trace("Going to login screen: " + bean); + + WidgetTemplateValues values = new WidgetTemplateValues(Macro.LOGIN.toString()); + values.put(TemplateVariable.FORM_ACTION.toString(), getAuthenticateUrl(request)); + values.put(TemplateVariable.LOGIN_NAME.toString(), bean.getUsername()); + + String infoMessage = bean.getInfoMessage(); + if (!infoMessage.isEmpty()) { + values.put(TemplateVariable.INFO_MESSAGE.toString(), infoMessage); + } + String errorMessage = bean.getErrorMessage(); + if (!errorMessage.isEmpty()) { + values.put(TemplateVariable.ERROR_MESSAGE.toString(), errorMessage); + } + + return values; + } + + /** + * The user has given the correct password, but now they are required to + * change it (unless they cancel out). + */ + private WidgetTemplateValues showPasswordChangeScreen(HttpServletRequest request) { + LoginProcessBean bean = LoginProcessBean.getBean(request); + bean.setState(State.FORCED_PASSWORD_CHANGE); + log.trace("Going to password change screen: " + bean); + + WidgetTemplateValues values = new WidgetTemplateValues( + Macro.FORCE_PASSWORD_CHANGE.toString()); + values.put(TemplateVariable.FORM_ACTION.toString(), getAuthenticateUrl(request)); + values.put(TemplateVariable.CANCEL_URL.toString(), getCancelUrl(request)); + + String errorMessage = bean.getErrorMessage(); + if (!errorMessage.isEmpty()) { + values.put(TemplateVariable.ERROR_MESSAGE.toString(), errorMessage); + } + return values; + } + + private WidgetTemplateValues showError(Exception e) { + WidgetTemplateValues values = new WidgetTemplateValues( + Macro.SERVER_ERROR.toString()); + values.put(TemplateVariable.ERROR_MESSAGE.toString(), "Internal server error:
" + e); + return values; + } + + /** + * Where are we in the process? Logged in? Not? Somewhere in between? + */ + private State getCurrentLoginState(HttpServletRequest request) { + if (LoginStatusBean.getBean(request).isLoggedIn()) { + return State.LOGGED_IN; + } else { + return LoginProcessBean.getBean(request).getState(); + } + } + + /** What's the URL for this servlet? */ + private String getAuthenticateUrl(HttpServletRequest request) { + String contextPath = request.getContextPath(); + return contextPath + "/authenticate"; + } + + /** What's the URL for this servlet, with the cancel parameter added? */ + private String getCancelUrl(HttpServletRequest request) { + String contextPath = request.getContextPath(); + String urlParams = "?cancel=true"; + return contextPath + "/authenticate" + urlParams; } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/widgets/Widget.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/widgets/Widget.java index 1f288cd81..4e58cf722 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/widgets/Widget.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/widgets/Widget.java @@ -56,6 +56,11 @@ public abstract class Widget { ServletContext context = (ServletContext) env.getCustomAttribute("context"); WidgetTemplateValues values = process(env, params, request, context); + // The widget process() method may determine that nothing should display for the widget: + // for example, the login widget doesn't display if the user is already logged in. + if (values == null) { + return ""; + } String widgetName = params.get("name").toString(); // getWidgetName(); return processMacroToString(env, widgetName, values); } @@ -106,7 +111,9 @@ public abstract class Widget { } catch (Throwable th) { log.error("Could not process widget " + widgetName, th); } - return out.toString(); + String output = out.toString(); + log.debug("Macro output: " + output); + return output; } private String processMacroToString(Environment env, String widgetName, String macroName, Map map) { @@ -134,9 +141,14 @@ public abstract class Widget { protected static class WidgetTemplateValues { private final String macroName; private final Map map; + + public WidgetTemplateValues(String macroName) { + this.macroName = macroName; + this.map = new HashMap(); + } - public WidgetTemplateValues(String templateName, Map map) { - this.macroName = templateName; + public WidgetTemplateValues(String macroName, Map map) { + this.macroName = macroName; this.map = map; } diff --git a/webapp/web/css/login.css b/webapp/web/css/login.css index ff45f7884..41d673b39 100644 --- a/webapp/web/css/login.css +++ b/webapp/web/css/login.css @@ -133,12 +133,8 @@ color:#900; } -/* In IE6/7, .hidden defined in screen.css doesn't override - * display: inline-block defined for #adminDashboard .pageBodyGroup, - * so define again with context selector here. - */ -#adminDashboard .pageBodyGroup.hidden { - display: none !important; +#loginFormAndLinks { + display: none; } p.passwordNote { diff --git a/webapp/web/js/login/loginUtils.js b/webapp/web/js/login/loginUtils.js index 946adf807..8172f98e2 100644 --- a/webapp/web/js/login/loginUtils.js +++ b/webapp/web/js/login/loginUtils.js @@ -3,7 +3,7 @@ $(document).ready(function(){ // login form is hidden by default; use JavaScript to reveal - $("#formLogin").removeClass("hidden"); + $("#loginFormAndLinks").show(); // focus on email or newpassword field $('.focus').focus(); diff --git a/webapp/web/js/login/loginUtils.js.merge-left.r5323 b/webapp/web/js/login/loginUtils.js.merge-left.r5323 deleted file mode 100644 index 946adf807..000000000 --- a/webapp/web/js/login/loginUtils.js.merge-left.r5323 +++ /dev/null @@ -1,11 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -$(document).ready(function(){ - - // login form is hidden by default; use JavaScript to reveal - $("#formLogin").removeClass("hidden"); - - // focus on email or newpassword field - $('.focus').focus(); - -}); diff --git a/webapp/web/js/login/loginUtils.js.merge-right.r5325 b/webapp/web/js/login/loginUtils.js.merge-right.r5325 deleted file mode 100644 index 67d998f9e..000000000 --- a/webapp/web/js/login/loginUtils.js.merge-right.r5325 +++ /dev/null @@ -1,28 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -$(document).ready(function(){ - - // login form is hidden by default; use JavaScript to reveal - $("#formLogin").removeClass("hidden"); - - // focus on email or newpassword field - $('.focus').focus(); - -}); - -//The above code for revealing the login form doesn't work IE 6 or 7. The code below fix the problem - -var Browser = { - Version: function() { - var version; - if (navigator.appVersion.indexOf("MSIE") != -1) - version = parseFloat(navigator.appVersion.split("MSIE")[1]); - return version; - } -} - -if (Browser.Version() <= 7) { - document.getElementById('formLogin').style.display = 'block'; -} - - diff --git a/webapp/web/js/login/loginUtils.js.working b/webapp/web/js/login/loginUtils.js.working deleted file mode 100644 index 76117467e..000000000 --- a/webapp/web/js/login/loginUtils.js.working +++ /dev/null @@ -1,12 +0,0 @@ -/* $This file is distributed under the terms of the license in /doc/license.txt$ */ - -$(document).ready(function(){ - - // login form is hidden by default; use JavaScript to reveal - $("#formLogin").removeClass("hidden"); - - // focus on email or newpassword field - $('.focus').focus(); - -}); - diff --git a/webapp/web/templates/freemarker/body/login/login-forcedPasswordChange.ftl b/webapp/web/templates/freemarker/body/login/login-forcedPasswordChange.ftl deleted file mode 100644 index 3f9da839f..000000000 --- a/webapp/web/templates/freemarker/body/login/login-forcedPasswordChange.ftl +++ /dev/null @@ -1,22 +0,0 @@ -<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> - -<#-- Log in template for accessing site admin --> - -
-

Create Your New Password

- - <#if errorMessage??> -
-

${errorMessage}

-
- - -
- - -

Minimum of 6 characters in length.

- - - or Cancel -
-
diff --git a/webapp/web/templates/freemarker/body/login/login-form.ftl b/webapp/web/templates/freemarker/body/login/login-form.ftl deleted file mode 100644 index f37a65503..000000000 --- a/webapp/web/templates/freemarker/body/login/login-form.ftl +++ /dev/null @@ -1,35 +0,0 @@ -<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> - -<#-- Template for login form --> - - - - diff --git a/webapp/web/templates/freemarker/body/login/login-main.ftl b/webapp/web/templates/freemarker/body/login/login-main.ftl deleted file mode 100644 index 5ec0b2071..000000000 --- a/webapp/web/templates/freemarker/body/login/login-main.ftl +++ /dev/null @@ -1,10 +0,0 @@ -<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> - -<#-- Main template for the login panel --> - -<#if loginTemplate??> - <#include loginTemplate> - - ${stylesheets.add("/css/login.css")} - ${scripts.add("/js/jquery.js", "/js/login/loginUtils.js")} - \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-login.ftl b/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-login.ftl new file mode 100644 index 000000000..9be45b524 --- /dev/null +++ b/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-login.ftl @@ -0,0 +1,8 @@ +<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> + +<#-- Main template for the login page --> + +<#-- Use this container to define styles that apply to the login widget only on the dedicated login page. --> +
+ <@widget name="login" /> +
diff --git a/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-main.ftl b/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-main.ftl index f696e83c4..ddfcbacdc 100644 --- a/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-main.ftl +++ b/webapp/web/templates/freemarker/body/siteAdmin/siteAdmin-main.ftl @@ -7,7 +7,7 @@
- <#include "login-main.ftl"> + <#include "siteAdmin-login.ftl"> <#include "siteAdmin-dataInput.ftl"> <#include "siteAdmin-siteConfiguration.ftl"> <#include "siteAdmin-ontologyEditor.ftl"> diff --git a/webapp/web/templates/freemarker/widgets/widget-login.ftl b/webapp/web/templates/freemarker/widgets/widget-login.ftl index 4075bbcd2..046a49023 100644 --- a/webapp/web/templates/freemarker/widgets/widget-login.ftl +++ b/webapp/web/templates/freemarker/widgets/widget-login.ftl @@ -3,25 +3,47 @@ <#-- Login widget --> <#macro assets> - <#if ! loginName??> + <#-- RY This test should be replaced by controller logic which doesn't display any assets if the user is logged in. + See NIHVIVO-1357. This test does nothing, since loginName has not been put into the data model. + <#if ! loginName?has_content> --> ${stylesheets.add("/css/login.css")} - <#-- define any js files needed for the login widget - ${scripts.add("")} - ${headScripts.add("")} --> - + ${scripts.add("/js/jquery.js", "/js/login/loginUtils.js")} + <#-- ${headScripts.add("")} --> + <#-- --> -<#macro markup> - <#if ! loginName??> -
-

Log in

+<#macro loginForm> -
+
+

Log in

+ + + + <#if infoMessage??> +

${infoMessage}

+ + + <#if errorMessage??> +
+

${errorMessage}

+
+ + + - - --> \ No newline at end of file +
+
+ + +<#macro forcePasswordChange> +
+

Log in

+ + <#if errorMessage??> +
+

${errorMessage}

+
+ + + + + +

Minimum of 6 characters in length.

+ + + or Cancel + +
+ + +<#macro error> +

There was an error in the system.

+ \ No newline at end of file