diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsPage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsPage.java index 62254d186..e619b68ec 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsPage.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/UserAccountsPage.java @@ -3,8 +3,10 @@ package edu.cornell.mannlib.vitro.webapp.controller.accounts; import java.util.ArrayList; +import java.util.Calendar; import java.util.Collections; import java.util.Comparator; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -18,6 +20,7 @@ import org.apache.commons.logging.LogFactory; import com.hp.hpl.jena.ontology.OntModel; import edu.cornell.mannlib.vitro.webapp.beans.PermissionSet; +import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.ParamMap; @@ -32,6 +35,12 @@ import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailFactory; public abstract class UserAccountsPage { private static final Log log = LogFactory.getLog(UserAccountsPage.class); + /** + * After the account is created, or the password is reset, the user has this + * many days to repond to the email. + */ + protected static final int DAYS_TO_USE_PASSWORD_LINK = 90; + protected final VitroRequest vreq; protected final ServletContext ctx; protected final OntModel userAccountsModel; @@ -82,6 +91,17 @@ public abstract class UserAccountsPage { return (value != null); } + /** + * Treat the presence of a certain parameter, with a desired value, as a + * boolean flag. + * + * An example would be radio buttons with values of "yes" and + * "no". The expected value would be "yes". + */ + protected boolean isParameterAsExpected(String key, String expected) { + return expected.equals(getStringParameter(key, "")); + } + /** * Create a list of all known PermissionSets. */ @@ -107,12 +127,25 @@ public abstract class UserAccountsPage { map.put("add", UrlBuilder.getUrl("/accountsAdmin/add")); map.put("delete", UrlBuilder.getUrl("/accountsAdmin/delete")); map.put("createPassword", UrlBuilder.getUrl("/accounts/createPassword")); + map.put("resetPassword", UrlBuilder.getUrl("/accounts/resetPassword")); return map; } protected static String editAccountUrl(String uri) { - return UrlBuilder.getUrl("/accountsAdmin/edit", - new ParamMap("editAccount", uri)); + return UrlBuilder.getUrl("/accountsAdmin/edit", new ParamMap( + "editAccount", uri)); } + + protected Date figureExpirationDate() { + Calendar c = Calendar.getInstance(); + c.add(Calendar.DATE, DAYS_TO_USE_PASSWORD_LINK); + return c.getTime(); + } + + protected boolean checkPasswordLength(String pw) { + return pw.length() >= UserAccount.MIN_PASSWORD_LENGTH + && pw.length() <= UserAccount.MAX_PASSWORD_LENGTH; + } + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPage.java index b99e4c85b..556326c77 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPage.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPage.java @@ -68,8 +68,9 @@ public class UserAccountsAddPage extends UserAccountsPage { emailAddress = getStringParameter(PARAMETER_EMAIL_ADDRESS, ""); firstName = getStringParameter(PARAMETER_FIRST_NAME, ""); lastName = getStringParameter(PARAMETER_LAST_NAME, ""); - selectedRoleUri = getRoleChoices(); - associateWithProfile = getAssociateFlag(); + selectedRoleUri = getStringParameter(PARAMETER_ROLE, ""); + associateWithProfile = isParameterAsExpected( + PARAMETER_ASSOCIATE_WITH_PROFILE, "yes"); strategy.parseAdditionalParameters(); } @@ -126,22 +127,6 @@ public class UserAccountsAddPage extends UserAccountsPage { strategy.notifyUser(); } - /** What role are they asking for? */ - private String getRoleChoices() { - String[] roles = vreq.getParameterValues(PARAMETER_ROLE); - if ((roles == null) || (roles.length == 0)) { - return ""; - } else { - return roles[0]; - } - } - - /** Are they associating with an Individual profile? */ - private boolean getAssociateFlag() { - return "yes".equals(getStringParameter( - PARAMETER_ASSOCIATE_WITH_PROFILE, "no")); - } - public final ResponseValues showPage() { Map body = new HashMap(); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java index 9c9380e50..9de5f2c07 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAddPageStrategy.java @@ -6,8 +6,6 @@ import static javax.mail.Message.RecipientType.TO; import java.net.MalformedURLException; import java.net.URL; -import java.util.Calendar; -import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -25,7 +23,7 @@ import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailMessage; public abstract class UserAccountsAddPageStrategy extends UserAccountsPage { protected final UserAccountsAddPage page; - public static UserAccountsAddPageStrategy getInstance(VitroRequest vreq, + public static UserAccountsAddPageStrategy getInstance(VitroRequest vreq, UserAccountsAddPage page, boolean emailEnabled) { if (emailEnabled) { return new EmailStrategy(vreq, page); @@ -34,7 +32,8 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage { } } - public UserAccountsAddPageStrategy(VitroRequest vreq, UserAccountsAddPage page) { + public UserAccountsAddPageStrategy(VitroRequest vreq, + UserAccountsAddPage page) { super(vreq); this.page = page; } @@ -57,7 +56,6 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage { private static class EmailStrategy extends UserAccountsAddPageStrategy { public static final String CREATE_PASSWORD_URL = "/accounts/createPassword"; - private static final int DAYS_TO_ACTIVATE_ACCOUNT = 90; private boolean sentEmail; @@ -81,12 +79,6 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage { u.setPasswordLinkExpires(figureExpirationDate().getTime()); u.setStatus(Status.INACTIVE); } - - private Date figureExpirationDate() { - Calendar c = Calendar.getInstance(); - c.add(Calendar.DATE, DAYS_TO_ACTIVATE_ACCOUNT); - return c.getTime(); - } @Override protected void addMoreBodyValues(Map body) { @@ -117,9 +109,9 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage { String email = page.getAddedAccount().getEmailAddress(); String hash = page.getAddedAccount() .getPasswordLinkExpiresHash(); - String relativeUrl = UrlBuilder.getUrl(CREATE_PASSWORD_URL, "user", - email, "key", hash); - + String relativeUrl = UrlBuilder.getUrl(CREATE_PASSWORD_URL, + "user", email, "key", hash); + URL context = new URL(vreq.getRequestURL().toString()); URL url = new URL(context, relativeUrl); return url.toExternalForm(); @@ -127,7 +119,7 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage { return "error_creating_password_link"; } } - + @Override protected boolean wasPasswordEmailSent() { return sentEmail; @@ -156,10 +148,8 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage { @Override protected void parseAdditionalParameters() { - initialPassword = getStringParameter( - PARAMETER_INITIAL_PASSWORD, ""); - confirmPassword = getStringParameter( - PARAMETER_CONFIRM_PASSWORD, ""); + initialPassword = getStringParameter(PARAMETER_INITIAL_PASSWORD, ""); + confirmPassword = getStringParameter(PARAMETER_CONFIRM_PASSWORD, ""); } @Override @@ -184,6 +174,8 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage { protected void addMoreBodyValues(Map body) { body.put("initialPassword", initialPassword); body.put("confirmPassword", confirmPassword); + body.put("minimumLength", UserAccount.MIN_PASSWORD_LENGTH); + body.put("maximumLength", UserAccount.MAX_PASSWORD_LENGTH); } @Override diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAdminController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAdminController.java index b98974bb8..b17a01c32 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAdminController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsAdminController.java @@ -9,12 +9,15 @@ import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.Actions; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.usepages.ManageUserAccounts; +import edu.cornell.mannlib.vitro.webapp.beans.DisplayMessage; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RedirectResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; /** - * Parcel out the different actions required of the Administrators portion of the UserAccounts GUI. + * Parcel out the different actions required of the Administrators portion of + * the UserAccounts GUI. */ public class UserAccountsAdminController extends FreemarkerHttpServlet { private static final Log log = LogFactory @@ -64,7 +67,9 @@ public class UserAccountsAdminController extends FreemarkerHttpServlet { private ResponseValues handleEditRequest(VitroRequest vreq) { UserAccountsEditPage page = new UserAccountsEditPage(vreq); - if (page.isSubmit() && page.isValid()) { + if (page.isBogus()) { + return showHomePage(vreq, page.getBogusMessage()); + } else if (page.isSubmit() && page.isValid()) { page.updateAccount(); UserAccountsListPage listPage = new UserAccountsListPage(vreq); return listPage.showPageWithUpdatedAccount( @@ -87,4 +92,9 @@ public class UserAccountsAdminController extends FreemarkerHttpServlet { return page.showPage(); } + private ResponseValues showHomePage(VitroRequest vreq, String message) { + DisplayMessage.setMessage(vreq, message); + return new RedirectResponseValues("/"); + } + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPage.java index 039a63300..fe186286d 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPage.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPage.java @@ -2,75 +2,217 @@ package edu.cornell.mannlib.vitro.webapp.controller.accounts.admin; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsPage; +import edu.cornell.mannlib.vitro.webapp.controller.accounts.user.UserAccountsUserController; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; /** - * TODO present the form. Get the submission. + * TODO present the form. Get the submission. * - * TODO If email is available, present the reset flag with message templage, and send email + * TODO If email is available, present the reset flag with message templage, and + * send email * - * TODO if email is not available, allow password change with checks for validity + * TODO if email is not available, allow password change with checks for + * validity * - * TODO If successful, go to AccountsList with message and optional password message. + * TODO If successful, go to AccountsList with message and optional password + * message. * * TODO if unsuccessful, go back to the page, with errors. * * TODO How much of this can be shared with AddPage? Email templates? */ public class UserAccountsEditPage extends UserAccountsPage { + private static final Log log = LogFactory + .getLog(UserAccountsEditPage.class); + + private static final String PARAMETER_SUBMIT = "submitEdit"; + private static final String PARAMETER_USER_URI = "editAccount"; + private static final String PARAMETER_EMAIL_ADDRESS = "emailAddress"; + private static final String PARAMETER_FIRST_NAME = "firstName"; + private static final String PARAMETER_LAST_NAME = "lastName"; + private static final String PARAMETER_ROLE = "role"; + private static final String PARAMETER_ASSOCIATE_WITH_PROFILE = "associate"; + + private static final String ERROR_NO_EMAIL = "errorEmailIsEmpty"; + private static final String ERROR_EMAIL_IN_USE = "errorEmailInUse"; + private static final String ERROR_NO_FIRST_NAME = "errorFirstNameIsEmpty"; + private static final String ERROR_NO_LAST_NAME = "errorLastNameIsEmpty"; + private static final String ERROR_NO_ROLE = "errorNoRoleSelected"; + private static final String TEMPLATE_NAME = "userAccounts-edit.ftl"; + private final UserAccountsEditPageStrategy strategy; + + /* The request parameters */ + private boolean submit; + private String userUri = ""; + private String emailAddress = ""; + private String firstName = ""; + private String lastName = ""; + private String selectedRoleUri = ""; + private boolean associateWithProfile; + + private UserAccount userAccount; + + /** The result of checking whether this request is even appropriate. */ + private String bogusMessage = ""; + + /** The result of validating a "submit" request. */ + private String errorCode = ""; + public UserAccountsEditPage(VitroRequest vreq) { super(vreq); + + this.strategy = UserAccountsEditPageStrategy.getInstance(vreq, this, + isEmailEnabled(vreq)); + + parseRequestParameters(); + validateUserAccountInfo(); + + if (isSubmit() && !isBogus()) { + validateParameters(); + } } - public ResponseValues showPage() { - return new TemplateResponseValues(TEMPLATE_NAME); + private void parseRequestParameters() { + submit = isFlagOnRequest(PARAMETER_SUBMIT); + userUri = getStringParameter(PARAMETER_USER_URI, ""); + emailAddress = getStringParameter(PARAMETER_EMAIL_ADDRESS, ""); + firstName = getStringParameter(PARAMETER_FIRST_NAME, ""); + lastName = getStringParameter(PARAMETER_LAST_NAME, ""); + selectedRoleUri = getStringParameter(PARAMETER_ROLE, ""); + associateWithProfile = isParameterAsExpected( + PARAMETER_ASSOCIATE_WITH_PROFILE, "yes"); + + strategy.parseAdditionalParameters(); } - /** - * @return - */ - public UserAccount updateAccount() { - // TODO Auto-generated method stub - throw new RuntimeException("UserAccountsEditPage.updateAccount() not implemented."); + private void validateUserAccountInfo() { + userAccount = userAccountsDao.getUserAccountByUri(userUri); + if (userAccount == null) { + log.warn("Edit account for '" + userUri + + "' is bogus: no such user"); + bogusMessage = UserAccountsUserController.BOGUS_STANDARD_MESSAGE; + return; + } } - /** - * @return - */ - public boolean wasPasswordEmailSent() { - // TODO Auto-generated method stub - throw new RuntimeException("UserAccountsEditPage.wasPasswordEmailSent() not implemented."); + public boolean isBogus() { + return !bogusMessage.isEmpty(); } - /** - * @return - */ - public UserAccount getUpdatedAccount() { - // TODO Auto-generated method stub - throw new RuntimeException("UserAccountsEditPage.getUpdatedAccount() not implemented."); + public String getBogusMessage() { + return bogusMessage; } - /** - * @return - */ - public boolean isValid() { - // TODO Auto-generated method stub - throw new RuntimeException("UserAccountsEditPage.isValid() not implemented."); - } - - /** - * @return - */ public boolean isSubmit() { - // TODO Auto-generated method stub - throw new RuntimeException("UserAccountsEditPage.isSubmit() not implemented."); + return submit; } + private void validateParameters() { + if (emailAddress.isEmpty()) { + errorCode = ERROR_NO_EMAIL; + } else if (emailIsChanged() && isEmailInUse()) { + errorCode = ERROR_EMAIL_IN_USE; + } else if (firstName.isEmpty()) { + errorCode = ERROR_NO_FIRST_NAME; + } else if (lastName.isEmpty()) { + errorCode = ERROR_NO_LAST_NAME; + } else if (selectedRoleUri.isEmpty()) { + errorCode = ERROR_NO_ROLE; + } else { + errorCode = strategy.additionalValidations(); + } + } + + private boolean emailIsChanged() { + return !emailAddress.equals(userAccount.getEmailAddress()); + } + + private boolean isEmailInUse() { + return userAccountsDao.getUserAccountByEmail(emailAddress) != null; + } + + public boolean isValid() { + return errorCode.isEmpty(); + } + + public final ResponseValues showPage() { + Map body = new HashMap(); + + if (isSubmit()) { + body.put("emailAddress", emailAddress); + body.put("firstName", firstName); + body.put("lastName", lastName); + body.put("selectedRole", selectedRoleUri); + } else { + body.put("emailAddress", userAccount.getEmailAddress()); + body.put("firstName", userAccount.getFirstName()); + body.put("lastName", userAccount.getLastName()); + body.put("selectedRole", getExistingRoleUri()); + } + body.put("roles", buildRolesList()); + if (associateWithProfile) { + body.put("associate", Boolean.TRUE); + } + body.put("formUrls", buildUrlsMapWithEditUrl()); + + if (!errorCode.isEmpty()) { + body.put(errorCode, Boolean.TRUE); + } + + strategy.addMoreBodyValues(body); + + return new TemplateResponseValues(TEMPLATE_NAME, body); + } + + private String getExistingRoleUri() { + Set uris = userAccount.getPermissionSetUris(); + if (uris.isEmpty()) { + return ""; + } else { + return uris.iterator().next(); + } + } + + private Map buildUrlsMapWithEditUrl() { + Map map = buildUrlsMap(); + map.put("edit", editAccountUrl(userAccount.getUri())); + return map; + } + + public void updateAccount() { + userAccount.setEmailAddress(emailAddress); + userAccount.setFirstName(firstName); + userAccount.setLastName(lastName); + + userAccount.setPermissionSetUris(Collections.singleton(selectedRoleUri)); + + strategy.setAdditionalProperties(userAccount); + + userAccountsDao.updateUserAccount(userAccount); + + strategy.notifyUser(); + } + + public boolean wasPasswordEmailSent() { + return strategy.wasPasswordEmailSent(); + } + + public UserAccount getUpdatedAccount() { + return userAccount; + } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPageStrategy.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPageStrategy.java new file mode 100644 index 000000000..6de05de00 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsEditPageStrategy.java @@ -0,0 +1,209 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller.accounts.admin; + +import static javax.mail.Message.RecipientType.TO; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; +import edu.cornell.mannlib.vitro.webapp.beans.UserAccount.Status; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsPage; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder; +import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailFactory; +import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailMessage; + +/** + * Handle the variant details of the UserAccountsAddPage. + */ +public abstract class UserAccountsEditPageStrategy extends UserAccountsPage { + protected final UserAccountsEditPage page; + + public static UserAccountsEditPageStrategy getInstance(VitroRequest vreq, + UserAccountsEditPage page, boolean emailEnabled) { + if (emailEnabled) { + return new EmailStrategy(vreq, page); + } else { + return new NoEmailStrategy(vreq, page); + } + } + + public UserAccountsEditPageStrategy(VitroRequest vreq, + UserAccountsEditPage page) { + super(vreq); + this.page = page; + } + + protected abstract void parseAdditionalParameters(); + + protected abstract String additionalValidations(); + + protected abstract void addMoreBodyValues(Map body); + + protected abstract void setAdditionalProperties(UserAccount u); + + protected abstract void notifyUser(); + + protected abstract boolean wasPasswordEmailSent(); + + // ---------------------------------------------------------------------- + // Strategy to use if email is enabled. + // ---------------------------------------------------------------------- + + private static class EmailStrategy extends UserAccountsEditPageStrategy { + private static final String PARAMETER_RESET_PASSWORD = "resetPassword"; + + public static final String RESET_PASSWORD_URL = "/accounts/resetPassword"; + + private boolean resetPassword; + private boolean sentEmail; + + public EmailStrategy(VitroRequest vreq, UserAccountsEditPage page) { + super(vreq, page); + } + + @Override + protected void parseAdditionalParameters() { + resetPassword = isFlagOnRequest(PARAMETER_RESET_PASSWORD); + } + + @Override + protected String additionalValidations() { + // No additional validations + return ""; + } + + @Override + protected void setAdditionalProperties(UserAccount u) { + if (resetPassword) { + u.setPasswordLinkExpires(figureExpirationDate().getTime()); + } + } + + @Override + protected void addMoreBodyValues(Map body) { + body.put("emailIsEnabled", Boolean.TRUE); + if (resetPassword) { + body.put("resetPassword", Boolean.TRUE); + } + } + + @Override + protected void notifyUser() { + if (!resetPassword) { + return; + } + + Map body = new HashMap(); + body.put("userAccount", page.getUpdatedAccount()); + body.put("passwordLink", buildResetPasswordLink()); + body.put("subjectLine", "Reset password request"); + + FreemarkerEmailMessage email = FreemarkerEmailFactory + .createNewMessage(vreq); + email.addRecipient(TO, page.getUpdatedAccount().getEmailAddress()); + email.setSubject("Reset password request"); + email.setHtmlTemplate("userAccounts-resetPasswordEmail-html.ftl"); + email.setTextTemplate("userAccounts-resetPasswordEmail-text.ftl"); + email.setBodyMap(body); + email.send(); + + sentEmail = true; + } + + private String buildResetPasswordLink() { + try { + String email = page.getUpdatedAccount().getEmailAddress(); + String hash = page.getUpdatedAccount() + .getPasswordLinkExpiresHash(); + String relativeUrl = UrlBuilder.getUrl(RESET_PASSWORD_URL, + "user", email, "key", hash); + + URL context = new URL(vreq.getRequestURL().toString()); + URL url = new URL(context, relativeUrl); + return url.toExternalForm(); + } catch (MalformedURLException e) { + return "error_creating_password_link"; + } + } + + @Override + protected boolean wasPasswordEmailSent() { + return sentEmail; + } + + } + + // ---------------------------------------------------------------------- + // Strategy to use if email is not enabled. + // ---------------------------------------------------------------------- + + private static class NoEmailStrategy extends UserAccountsEditPageStrategy { + private static final String PARAMETER_NEW_PASSWORD = "newPassword"; + private static final String PARAMETER_CONFIRM_PASSWORD = "confirmPassword"; + + private static final String ERROR_WRONG_PASSWORD_LENGTH = "errorPasswordIsWrongLength"; + private static final String ERROR_PASSWORDS_DONT_MATCH = "errorPasswordsDontMatch"; + + private String newPassword; + private String confirmPassword; + + public NoEmailStrategy(VitroRequest vreq, UserAccountsEditPage page) { + super(vreq, page); + } + + @Override + protected void parseAdditionalParameters() { + newPassword = getStringParameter(PARAMETER_NEW_PASSWORD, ""); + confirmPassword = getStringParameter(PARAMETER_CONFIRM_PASSWORD, ""); + } + + @Override + protected String additionalValidations() { + if (newPassword.isEmpty() && confirmPassword.isEmpty()) { + return ""; + } else if (!checkPasswordLength()) { + return ERROR_WRONG_PASSWORD_LENGTH; + } else if (!newPassword.equals(confirmPassword)) { + return ERROR_PASSWORDS_DONT_MATCH; + } else { + return ""; + } + } + + private boolean checkPasswordLength() { + return newPassword.length() >= UserAccount.MIN_PASSWORD_LENGTH + && newPassword.length() <= UserAccount.MAX_PASSWORD_LENGTH; + } + + @Override + protected void addMoreBodyValues(Map body) { + body.put("newPassword", newPassword); + body.put("confirmPassword", confirmPassword); + body.put("minimumLength", UserAccount.MIN_PASSWORD_LENGTH); + body.put("maximumLength", UserAccount.MAX_PASSWORD_LENGTH); + } + + @Override + protected void setAdditionalProperties(UserAccount u) { + u.setMd5Password(newPassword); + u.setPasswordChangeRequired(true); + } + + @Override + protected void notifyUser() { + // Do nothing. + } + + @Override + protected boolean wasPasswordEmailSent() { + return false; + } + + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsListPage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsListPage.java index c263c0419..08476691d 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsListPage.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/admin/UserAccountsListPage.java @@ -105,6 +105,9 @@ public class UserAccountsListPage extends UserAccountsPage { body.put("newUserAccount", new UserAccountWrapper(userAccount, Collections. emptyList())); + if (emailWasSent) { + body.put("emailWasSent", Boolean.TRUE); + } return new TemplateResponseValues(TEMPLATE_NAME, body); } @@ -114,8 +117,17 @@ public class UserAccountsListPage extends UserAccountsPage { */ public ResponseValues showPageWithUpdatedAccount(UserAccount userAccount, boolean emailWasSent) { - throw new RuntimeException( - "UserAccountsListPage.showPageWithUpdatedAccount not implemented."); + UserAccountsSelection selection = UserAccountsSelector.select( + userAccountsModel, criteria); + Map body = buildTemplateBodyMap(selection); + + body.put("updatedUserAccount", new UserAccountWrapper(userAccount, + Collections. emptyList())); + if (emailWasSent) { + body.put("emailWasSent", Boolean.TRUE); + } + + return new TemplateResponseValues(TEMPLATE_NAME, body); } /** diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java index e524180a4..07264f328 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsCreatePasswordPage.java @@ -2,165 +2,68 @@ package edu.cornell.mannlib.vitro.webapp.controller.accounts.user; -import java.util.Date; +import static javax.mail.Message.RecipientType.TO; + import java.util.HashMap; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; import edu.cornell.mannlib.vitro.webapp.beans.UserAccount.Status; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; -import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsPage; import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator; -import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; -import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; +import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailFactory; +import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailMessage; /** * When the user clicks on the link in their notification email, handle their * request to create a password. */ -public class UserAccountsCreatePasswordPage extends UserAccountsPage { +public class UserAccountsCreatePasswordPage extends + UserAccountsPasswordBasePage { private static final Log log = LogFactory .getLog(UserAccountsCreatePasswordPage.class); - private static final String PARAMETER_SUBMIT = "submitCreatePassword"; - private static final String PARAMETER_USER = "user"; - private static final String PARAMETER_KEY = "key"; - private static final String PARAMETER_PASSWORD = "password"; - private static final String PARAMETER_CONFIRM_PASSWORD = "confirmPassword"; - private static final String TEMPLATE_NAME = "userAccounts-createPassword.ftl"; - private static final String ERROR_NO_PASSWORD = "errorPasswordIsEmpty"; - private static final String ERROR_WRONG_PASSWORD_LENGTH = "errorPasswordIsWrongLength"; - private static final String ERROR_PASSWORDS_DONT_MATCH = "errorPasswordsDontMatch"; - - private boolean submit; - private String userEmail = ""; - private String key = ""; - private String password = ""; - private String confirmPassword = ""; - - private UserAccount userAccount; - - /** The result of checking whether this request is even appropriate. */ - private String bogusMessage = ""; - - /** The result of validating a "submit" request. */ - private String errorCode = ""; - public UserAccountsCreatePasswordPage(VitroRequest vreq) { super(vreq); - - parseRequestParameters(); - validateUserAccountInfo(); - - if (isSubmit() && !isBogus()) { - validateParameters(); - } - } - - private void parseRequestParameters() { - submit = isFlagOnRequest(PARAMETER_SUBMIT); - userEmail = getStringParameter(PARAMETER_USER, ""); - key = getStringParameter(PARAMETER_KEY, ""); - password = getStringParameter(PARAMETER_PASSWORD, ""); - confirmPassword = getStringParameter(PARAMETER_CONFIRM_PASSWORD, ""); - } - - private void validateUserAccountInfo() { - userAccount = userAccountsDao.getUserAccountByEmail(userEmail); - if (userAccount == null) { - log.warn("Create password for '" + userEmail - + "' is bogus: no such user"); - bogusMessage = UserAccountsUserController.BOGUS_STANDARD_MESSAGE; - return; - } - - if (userAccount.getPasswordLinkExpires() == 0L) { - log.warn("Create password for '" + userEmail - + "' is bogus: password change is not pending."); - bogusMessage = "The account for " + userEmail - + " has already been activated."; - return; - } - - Date expirationDate = new Date(userAccount.getPasswordLinkExpires()); - if (expirationDate.before(new Date())) { - log.warn("Create password for '" + userEmail - + "' is bogus: expiration date has passed."); - bogusMessage = UserAccountsUserController.BOGUS_STANDARD_MESSAGE; - return; - } - - String expectedKey = userAccount.getPasswordLinkExpiresHash(); - if (!key.equals(expectedKey)) { - log.warn("Create password for '" + userEmail + "' is bogus: key (" - + key + ") doesn't match expected key (" + expectedKey - + ")"); - bogusMessage = UserAccountsUserController.BOGUS_STANDARD_MESSAGE; - return; - } - } - - private void validateParameters() { - if (password.isEmpty()) { - errorCode = ERROR_NO_PASSWORD; - } else if (!checkPasswordLength(password)) { - errorCode = ERROR_WRONG_PASSWORD_LENGTH; - } else if (!password.equals(confirmPassword)) { - errorCode = ERROR_PASSWORDS_DONT_MATCH; - } - } - - private boolean checkPasswordLength(String pw) { - return pw.length() >= UserAccount.MIN_PASSWORD_LENGTH - && pw.length() <= UserAccount.MAX_PASSWORD_LENGTH; - } - - public boolean isBogus() { - return bogusMessage.isEmpty(); - } - - public String getBogusMessage() { - return bogusMessage; - } - - public boolean isSubmit() { - return submit; - } - - public boolean isValid() { - return errorCode.isEmpty(); } public void createPassword() { - userAccount.setMd5Password(Authenticator.applyMd5Encoding(password)); + userAccount.setMd5Password(Authenticator.applyMd5Encoding(newPassword)); userAccount.setPasswordLinkExpires(0L); userAccount.setStatus(Status.ACTIVE); userAccountsDao.updateUserAccount(userAccount); log.debug("Set password on '" + userAccount.getEmailAddress() - + "' to '" + password + "'"); + + "' to '" + newPassword + "'"); + + notifyUser(); } - public final ResponseValues showPage() { + @Override + protected String passwordChangeNotPendingMessage() { + return "The account for " + userEmail + " has already been activated."; + } + + @Override + protected String templateName() { + return TEMPLATE_NAME; + } + + private void notifyUser() { Map body = new HashMap(); - - body.put("minimumLength", UserAccount.MIN_PASSWORD_LENGTH); - body.put("maximumLength", UserAccount.MAX_PASSWORD_LENGTH); body.put("userAccount", userAccount); - body.put("key", userAccount.getPasswordLinkExpiresHash()); - body.put("password", password); - body.put("confirmPassword", confirmPassword); - body.put("formUrls", buildUrlsMap()); + body.put("subjectLine", "Password successfully created."); - if (!errorCode.isEmpty()) { - body.put(errorCode, Boolean.TRUE); - } - - return new TemplateResponseValues(TEMPLATE_NAME, body); + FreemarkerEmailMessage email = FreemarkerEmailFactory + .createNewMessage(vreq); + email.addRecipient(TO, userAccount.getEmailAddress()); + email.setSubject("Password successfully created."); + email.setHtmlTemplate("userAccounts-passwordCreatedEmail-html.ftl"); + email.setTextTemplate("userAccounts-passwordCreatedEmail-text.ftl"); + email.setBodyMap(body); + email.send(); } - } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsPasswordBasePage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsPasswordBasePage.java new file mode 100644 index 000000000..2cf64c585 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsPasswordBasePage.java @@ -0,0 +1,150 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller.accounts.user; + +import static edu.cornell.mannlib.vitro.webapp.controller.accounts.user.UserAccountsUserController.BOGUS_STANDARD_MESSAGE; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.cornell.mannlib.vitro.webapp.beans.UserAccount; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.accounts.UserAccountsPage; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; + +/** + * Routines in common to the "Create Password" and "Reset Password" pages. + */ +public abstract class UserAccountsPasswordBasePage extends UserAccountsPage { + private static final Log log = LogFactory + .getLog(UserAccountsPasswordBasePage.class); + + private static final String PARAMETER_SUBMIT = "submit"; + private static final String PARAMETER_USER = "user"; + private static final String PARAMETER_KEY = "key"; + private static final String PARAMETER_NEW_PASSWORD = "newPassword"; + private static final String PARAMETER_CONFIRM_PASSWORD = "confirmPassword"; + + private static final String ERROR_NO_PASSWORD = "errorPasswordIsEmpty"; + private static final String ERROR_WRONG_PASSWORD_LENGTH = "errorPasswordIsWrongLength"; + private static final String ERROR_PASSWORDS_DONT_MATCH = "errorPasswordsDontMatch"; + + protected boolean submit; + protected String userEmail = ""; + protected String key = ""; + protected String newPassword = ""; + protected String confirmPassword = ""; + + protected UserAccount userAccount; + + /** The result of checking whether this request is even appropriate. */ + private String bogusMessage = ""; + + /** The result of validating a "submit" request. */ + private String errorCode = ""; + + protected UserAccountsPasswordBasePage(VitroRequest vreq) { + super(vreq); + + parseRequestParameters(); + validateUserAccountInfo(); + + if (isSubmit() && !isBogus()) { + validateParameters(); + } + } + + private void parseRequestParameters() { + submit = isFlagOnRequest(PARAMETER_SUBMIT); + userEmail = getStringParameter(PARAMETER_USER, ""); + key = getStringParameter(PARAMETER_KEY, ""); + newPassword = getStringParameter(PARAMETER_NEW_PASSWORD, ""); + confirmPassword = getStringParameter(PARAMETER_CONFIRM_PASSWORD, ""); + } + + public boolean isSubmit() { + return submit; + } + + private void validateUserAccountInfo() { + userAccount = userAccountsDao.getUserAccountByEmail(userEmail); + if (userAccount == null) { + log.warn("Password request for '" + userEmail + + "' is bogus: no such user"); + bogusMessage = BOGUS_STANDARD_MESSAGE; + return; + } + + if (userAccount.getPasswordLinkExpires() == 0L) { + log.warn("Password request for '" + userEmail + + "' is bogus: password change is not pending."); + bogusMessage = passwordChangeNotPendingMessage(); + return; + } + + Date expirationDate = new Date(userAccount.getPasswordLinkExpires()); + if (expirationDate.before(new Date())) { + log.warn("Password request for '" + userEmail + + "' is bogus: expiration date has passed."); + bogusMessage = BOGUS_STANDARD_MESSAGE; + return; + } + + String expectedKey = userAccount.getPasswordLinkExpiresHash(); + if (!key.equals(expectedKey)) { + log.warn("Password request for '" + userEmail + "' is bogus: key (" + + key + ") doesn't match expected key (" + expectedKey + + ")"); + bogusMessage = BOGUS_STANDARD_MESSAGE; + return; + } + } + + public boolean isBogus() { + return !bogusMessage.isEmpty(); + } + + public String getBogusMessage() { + return bogusMessage; + } + + private void validateParameters() { + if (newPassword.isEmpty()) { + errorCode = ERROR_NO_PASSWORD; + } else if (!checkPasswordLength(newPassword)) { + errorCode = ERROR_WRONG_PASSWORD_LENGTH; + } else if (!newPassword.equals(confirmPassword)) { + errorCode = ERROR_PASSWORDS_DONT_MATCH; + } + } + + public boolean isValid() { + return errorCode.isEmpty(); + } + + public final ResponseValues showPage() { + Map body = new HashMap(); + + body.put("minimumLength", UserAccount.MIN_PASSWORD_LENGTH); + body.put("maximumLength", UserAccount.MAX_PASSWORD_LENGTH); + body.put("userAccount", userAccount); + body.put("key", userAccount.getPasswordLinkExpiresHash()); + body.put("newPassword", newPassword); + body.put("confirmPassword", confirmPassword); + body.put("formUrls", buildUrlsMap()); + + if (!errorCode.isEmpty()) { + body.put(errorCode, Boolean.TRUE); + } + + return new TemplateResponseValues(templateName(), body); + } + + protected abstract String passwordChangeNotPendingMessage(); + protected abstract String templateName(); +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsResetPasswordPage.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsResetPasswordPage.java new file mode 100644 index 000000000..98bd350f0 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsResetPasswordPage.java @@ -0,0 +1,69 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller.accounts.user; + +import static javax.mail.Message.RecipientType.TO; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.cornell.mannlib.vitro.webapp.beans.UserAccount.Status; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.authenticate.Authenticator; +import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailFactory; +import edu.cornell.mannlib.vitro.webapp.email.FreemarkerEmailMessage; + +/** + * When the user clicks on the link in their notification email, handle their + * request to reset their password. + */ +public class UserAccountsResetPasswordPage extends UserAccountsPasswordBasePage { + private static final Log log = LogFactory + .getLog(UserAccountsResetPasswordPage.class); + + private static final String TEMPLATE_NAME = "userAccounts-resetPassword.ftl"; + + protected UserAccountsResetPasswordPage(VitroRequest vreq) { + super(vreq); + } + + public void resetPassword() { + userAccount.setMd5Password(Authenticator.applyMd5Encoding(newPassword)); + userAccount.setPasswordLinkExpires(0L); + userAccount.setStatus(Status.ACTIVE); + userAccountsDao.updateUserAccount(userAccount); + log.debug("Set password on '" + userAccount.getEmailAddress() + + "' to '" + newPassword + "'"); + + notifyUser(); + } + + @Override + protected String passwordChangeNotPendingMessage() { + return "The password for " + userEmail + " has already been reset."; + } + + @Override + protected String templateName() { + return TEMPLATE_NAME; + } + + private void notifyUser() { + Map body = new HashMap(); + body.put("userAccount", userAccount); + body.put("subjectLine", "Password changed."); + + FreemarkerEmailMessage email = FreemarkerEmailFactory + .createNewMessage(vreq); + email.addRecipient(TO, userAccount.getEmailAddress()); + email.setSubject("Password changed."); + email.setHtmlTemplate("userAccounts-passwordResetEmail-html.ftl"); + email.setTextTemplate("userAccounts-passwordResetEmail-text.ftl"); + email.setBodyMap(body); + email.send(); + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java index 6f4ea9e11..8b17be386 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/accounts/user/UserAccountsUserController.java @@ -22,6 +22,7 @@ public class UserAccountsUserController extends FreemarkerHttpServlet { public static final String BOGUS_STANDARD_MESSAGE = "Request failed. Please contact your system administrator."; private static final String ACTION_CREATE_PASSWORD = "/createPassword"; + private static final String ACTION_RESET_PASSWORD = "/resetPassword"; @Override protected Actions requiredActions(VitroRequest vreq) { @@ -39,6 +40,8 @@ public class UserAccountsUserController extends FreemarkerHttpServlet { if (ACTION_CREATE_PASSWORD.equals(action)) { return handleCreatePasswordRequest(vreq); + } else if (ACTION_RESET_PASSWORD.equals(action)) { + return handleResetPasswordRequest(vreq); } else { return handleInvalidRequest(vreq); } @@ -59,6 +62,21 @@ public class UserAccountsUserController extends FreemarkerHttpServlet { } + private ResponseValues handleResetPasswordRequest(VitroRequest vreq) { + UserAccountsResetPasswordPage page = new UserAccountsResetPasswordPage( + vreq); + if (page.isBogus()) { + return showHomePage(vreq, page.getBogusMessage()); + } else if (page.isSubmit() && page.isValid()) { + page.resetPassword(); + return showHomePage(vreq, + "Your password has been saved. Please log in."); + } else { + return page.showPage(); + } + + } + private ResponseValues handleInvalidRequest(VitroRequest vreq) { return showHomePage(vreq, BOGUS_STANDARD_MESSAGE); } diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-add.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-add.ftl index 4cbce0936..0b3131991 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-add.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-add.ftl @@ -68,6 +68,8 @@ +

Minimum of ${minimumLength} characters in length.

+ @@ -79,10 +81,6 @@ checked id="no-associate" /> -
- - - <#if emailIsEnabled??>

Note: An email will be sent to the address entered above diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-createPassword.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-createPassword.ftl index 6bd7b6a99..2b75cff0c 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-createPassword.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-createPassword.ftl @@ -2,7 +2,7 @@ <#-- Template for adding a user account --> -

Add new account

+

Create your Password

<#if errorPasswordIsEmpty??> <#assign errorMessage = "No password supplied." /> @@ -23,23 +23,23 @@ -
+
- Add new account + Please enter your new password for ${userAccount.emailAddress}
- - + +

Minimum of ${minimumLength} characters in length.

- +

* required fields

diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-edit.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-edit.ftl index 9df6b9905..9060a37ea 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-edit.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-edit.ftl @@ -2,5 +2,103 @@ <#-- Template for editing a user account --> -

Edit user account

+

Edit account

+ <#if errorEmailIsEmpty??> + <#assign errorMessage = "You must supply an email address." /> + + + <#if errorEmailInUse??> + <#assign errorMessage = "An account with that email address already exists." /> + + + <#if errorFirstNameIsEmpty??> + <#assign errorMessage = "You must supply a first name." /> + + + <#if errorLastNameIsEmpty??> + <#assign errorMessage = "You must supply a last name." /> + + + <#if errorNoRoleSelected??> + <#assign errorMessage = "You must select a role." /> + + + <#if errorPasswordIsEmpty??> + <#assign errorMessage = "No password supplied." /> + + + <#if errorPasswordIsWrongLength??> + <#assign errorMessage = "Password must be between ${minimumLength} and ${maximumLength} characters." /> + + + <#if errorPasswordsDontMatch??> + <#assign errorMessage = "Passwords do not match." /> + + + <#if errorMessage?has_content> + + + +
+
+ Edit new account + +
+ + + + + + + + + +

Roles *

+ <#list roles as role> + selected /> + +
+ + + <#if !emailIsEnabled??> + + + +

Minimum of ${minimumLength} characters in length.

+

Leaving this blank means that the password will not be changed.

+ + + + + +

Associate a profile with this account

+ checked id="associate" /> + + + checked id="no-associate" /> + + +
+ + + + <#if emailIsEnabled??> +

+ Note: A confirmation email with instructions for resetting a password + will be sent to the address entered above. + The password will not be reset until the user follows the link provided in this email. +

+ + + or Cancel + +

* required fields

+
+
+
+ +${stylesheets.add('')} \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-list.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-list.ftl index d59d8d20b..a0d476e8a 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-list.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-list.ftl @@ -47,8 +47,26 @@

A new account for ${newUserAccount.firstName} ${newUserAccount.lastName} - was successfully created. A notification email has been sent to ${newUserAccount.emailAddress} - with instructions for activating the account and creating a password. + was successfully created. + <#if emailWasSent?? > + A notification email has been sent to ${newUserAccount.emailAddress} + with instructions for activating the account and creating a password. + +

+
+ + +<#if updatedUserAccount?? > + diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail-html.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail-html.ftl index c6733d9d7..1773766e9 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail-html.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail-html.ftl @@ -1,6 +1,6 @@ <#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> -<#-- Confirmation that an account has been created. --> +<#-- Confirmation that an password has been created. --> @@ -16,7 +16,7 @@

- Yout new password associated with ${userAccount.emailAddress} has been created. + Your new password associated with ${userAccount.emailAddress} has been created.

diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail-text.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail-text.ftl index c44afae0d..2a339d4a9 100644 --- a/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail-text.ftl +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordCreatedEmail-text.ftl @@ -1,6 +1,6 @@ <#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> -<#-- Confirmation that an account has been created. --> +<#-- Confirmation that a password has been created. --> ${userAccount.firstName} ${userAccount.lastName} diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordResetEmail-html.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordResetEmail-html.ftl new file mode 100644 index 000000000..5212fbebb --- /dev/null +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordResetEmail-html.ftl @@ -0,0 +1,26 @@ +<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> + +<#-- Confirmation that an password has been reset. --> + + + + ${subjectLine} + + +

+ ${userAccount.firstName} ${userAccount.lastName} +

+ +

+ Password successfully changed. +

+ +

+ Your new password associated with ${userAccount.emailAddress} has been changed. +

+ +

+ Thank you. +

+ + \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordResetEmail-text.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordResetEmail-text.ftl new file mode 100644 index 000000000..cfe0cd00f --- /dev/null +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-passwordResetEmail-text.ftl @@ -0,0 +1,12 @@ +<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> + +<#-- Confirmation that a password has been reset. --> + +${userAccount.firstName} ${userAccount.lastName} + +Password successfully changed. + +Your new password associated with ${userAccount.emailAddress} +has been changed. + +Thank you. diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPassword.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPassword.ftl new file mode 100644 index 000000000..d0f8e75a5 --- /dev/null +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPassword.ftl @@ -0,0 +1,49 @@ +<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> + +<#-- Template for adding a user account --> + +

Reset your Password

+ + <#if errorPasswordIsEmpty??> + <#assign errorMessage = "No password supplied." /> + + + <#if errorPasswordIsWrongLength??> + <#assign errorMessage = "Password must be between ${minimumLength} and ${maximumLength} characters." /> + + + <#if errorPasswordsDontMatch??> + <#assign errorMessage = "Passwords do not match." /> + + + <#if errorMessage?has_content> + + + +
+
+ Please enter your new password for ${userAccount.emailAddress} + +
+ + + + + + +

Minimum of ${minimumLength} characters in length.

+ + + + + + +

* required fields

+
+
+
+ +${stylesheets.add('')} \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPasswordEmail-html.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPasswordEmail-html.ftl new file mode 100644 index 000000000..0fa37fba3 --- /dev/null +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPasswordEmail-html.ftl @@ -0,0 +1,40 @@ +<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> + +<#-- Notification that your password has been reset. --> + + + + ${subjectLine} + + +

+ ${userAccount.firstName} ${userAccount.lastName} +

+ +

+ We received a request to reset the password for your account (${userAccount.emailAddress}). + Please follow the instructions below to proceed with your password reset. +

+ +

+ If you did not request this new account you can safely ignore this email. + This request will expire if not acted upon for 30 days. +

+ +

+ Click the link below to reset your password using our secure server. +

+ +

+ ${passwordLink} +

+ +

+ If the link above doesn't work, you can copy and paste the link directly into your browser's address bar. +

+ +

+ Thank you! +

+ + \ No newline at end of file diff --git a/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPasswordEmail-text.ftl b/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPasswordEmail-text.ftl new file mode 100644 index 000000000..82624c327 --- /dev/null +++ b/webapp/web/templates/freemarker/body/accounts/userAccounts-resetPasswordEmail-text.ftl @@ -0,0 +1,19 @@ +<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> + +<#-- Notification that your password has been reset. --> + +${userAccount.firstName} ${userAccount.lastName} + +We received a request to reset the password for your account +(${userAccount.emailAddress}). +Please follow the instructions below to proceed with your password reset. + +If you did not request this new account you can safely ignore this email. +This request will expire if not acted upon for 30 days. + +Paste the link below into your browser's address bar to reset your password +using our secure server. + +${passwordLink} + +Thank you!