Index: lams_admin/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r00f07d4e073c8b6fb447a5fbf2fa03782ba8cb4b -r9ab81eba5d1a95a6619094dfe12184f5580971e8 --- lams_admin/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 00f07d4e073c8b6fb447a5fbf2fa03782ba8cb4b) +++ lams_admin/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 9ab81eba5d1a95a6619094dfe12184f5580971e8) @@ -168,6 +168,7 @@ msg.remove.from.subgroups = Removed users will also be removed from subcourse. label.global.roles = Global roles label.import = Import +label.import.email = Email users account activation emails msg.import.intro = Use this screen to bulk import users using an Excel spreadsheet. msg.import.1 = In the spreadsheet, columns marked with a (*) are mandatory. msg.import.2 = Download the user template to create users. @@ -677,3 +678,10 @@ sysadmin.lti.advantage.tool.public.key = Public key sysadmin.lti.advantage.tool.private.key = Private key sysadmin.add.staff.to.all.lessons = On staff login add them to all lessons in course +user.import.password.change.email.content.subject = Set up LAMS password +user.import.password.change.email.content.start = Hi {0}, +user.import.password.change.email.content.account.created = We have created an account for you on LAMS platform. Your user name is "{0}". Please follow this link to set up your password: {1} +user.import.password.change.email.content.link.label = SET UP LAMS PASSWORD +user.import.password.change.email.content.end = If the link expired, please go to main page and click "Forgot password" link. +user.import.password.change.email.content.thanks = Thank you +user.import.password.change.email.content.footer = LAMS Index: lams_admin/src/java/org/lamsfoundation/lams/admin/service/IImportService.java =================================================================== diff -u -r00f07d4e073c8b6fb447a5fbf2fa03782ba8cb4b -r9ab81eba5d1a95a6619094dfe12184f5580971e8 --- lams_admin/src/java/org/lamsfoundation/lams/admin/service/IImportService.java (.../IImportService.java) (revision 00f07d4e073c8b6fb447a5fbf2fa03782ba8cb4b) +++ lams_admin/src/java/org/lamsfoundation/lams/admin/service/IImportService.java (.../IImportService.java) (revision 9ab81eba5d1a95a6619094dfe12184f5580971e8) @@ -61,7 +61,7 @@ * Checks first row of spreadsheet and determines whether to parse as * a user or orgrole spreadsheet. */ - public List parseSpreadsheet(File fileItem, String sessionId) throws IOException; + public List parseSpreadsheet(File fileItem, String sessionId, boolean sendEmail) throws IOException; public List parseGroupSpreadsheet(File fileItem, String sessionId) throws IOException; @@ -71,11 +71,6 @@ public int getNumRows(File fileItem) throws IOException; /** - * Returns message results from parsing list of users in spreadsheet. - */ - public List parseUserSpreadsheet(File file, String sessionId) throws IOException; - - /** * Returns message results from parsing list of organisation memberships. */ public List parseRolesSpreadsheet(File file, String sessionId) throws IOException; Index: lams_admin/src/java/org/lamsfoundation/lams/admin/service/ImportService.java =================================================================== diff -u -r00f07d4e073c8b6fb447a5fbf2fa03782ba8cb4b -r9ab81eba5d1a95a6619094dfe12184f5580971e8 --- lams_admin/src/java/org/lamsfoundation/lams/admin/service/ImportService.java (.../ImportService.java) (revision 00f07d4e073c8b6fb447a5fbf2fa03782ba8cb4b) +++ lams_admin/src/java/org/lamsfoundation/lams/admin/service/ImportService.java (.../ImportService.java) (revision 9ab81eba5d1a95a6619094dfe12184f5580971e8) @@ -25,6 +25,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -45,6 +47,7 @@ import org.lamsfoundation.lams.themes.Theme; import org.lamsfoundation.lams.timezone.service.ITimezoneService; import org.lamsfoundation.lams.usermanagement.AuthenticationMethod; +import org.lamsfoundation.lams.usermanagement.ForgotPasswordRequest; import org.lamsfoundation.lams.usermanagement.Organisation; import org.lamsfoundation.lams.usermanagement.OrganisationState; import org.lamsfoundation.lams.usermanagement.OrganisationType; @@ -53,6 +56,10 @@ import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.usermanagement.dto.UserDTO; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; +import org.lamsfoundation.lams.util.Configuration; +import org.lamsfoundation.lams.util.ConfigurationKeys; +import org.lamsfoundation.lams.util.Emailer; +import org.lamsfoundation.lams.util.FileUtil; import org.lamsfoundation.lams.util.LanguageUtil; import org.lamsfoundation.lams.util.MessageService; import org.lamsfoundation.lams.util.ValidationUtil; @@ -110,6 +117,15 @@ private static final short ADMIN_BROWSE_ALL_USERS = 5; private static final short ADMIN_CHANGE_STATUS = 6; + private static String USER_IMPORT_PASSWORD_CHANGE_EMAIL_TEMPLATE_CONTENT; + private static final String USER_IMPORT_PASSWORD_CHANGE_EMAIL_PAGE_TITLE_PLACEHOLDER = "[PAGE_TITLE_PLACEHOLDER]"; + private static final String USER_IMPORT_PASSWORD_CHANGE_EMAIL_TOP_HEADER_PLACEHOLDER = "[TOP_HEADER_PLACEHOLDER]"; + private static final String USER_IMPORT_PASSWORD_CHANGE_EMAIL_CONTENT_START_PLACEHOLDER = "[CONTENT_START_PLACEHOLDER]"; + private static final String USER_IMPORT_PASSWORD_CHANGE_EMAIL_CONTENT_LINK_PLACEHOLDER = "[CONTENT_LINK_PLACEHOLDER]"; + private static final String USER_IMPORT_PASSWORD_CHANGE_EMAIL_CONTENT_END_PLACEHOLDER = "[CONTENT_END_PLACEHOLDER]"; + private static final String USER_IMPORT_PASSWORD_CHANGE_EMAIL_CONTENT_THANKS_PLACEHOLDER = "[CONTENT_THANKS_PLACEHOLDER]"; + private static final String USER_IMPORT_PASSWORD_CHANGE_EMAIL_FOOTER_PLACEHOLDER = "[FOOTER_PLACEHOLDER]"; + // class-wide variables List rowResult = new ArrayList<>(); private boolean emptyRow; @@ -139,9 +155,9 @@ } @Override - public List> parseSpreadsheet(File fileItem, String sessionId) throws IOException { + public List> parseSpreadsheet(File fileItem, String sessionId, boolean sendEmail) throws IOException { if (isUserSpreadsheet(fileItem)) { - return parseUserSpreadsheet(fileItem, sessionId); + return parseUserSpreadsheet(fileItem, sessionId, sendEmail); } else if (isRolesSpreadsheet(fileItem)) { return parseRolesSpreadsheet(fileItem, sessionId); } @@ -271,8 +287,8 @@ return endRow - startRow; } - @Override - public List> parseUserSpreadsheet(File fileItem, String sessionId) throws IOException { + private List> parseUserSpreadsheet(File fileItem, String sessionId, boolean sendEmail) + throws IOException { List> results = new ArrayList<>(); HSSFSheet sheet = getSheet(fileItem); int startRow = sheet.getFirstRowNum(); @@ -311,6 +327,9 @@ writeAuditLog(user, userDTO); ImportService.log.debug("Row " + i + " saved user: " + user.getLogin() + (generatedPassword.length() > 0 ? " with a generated password" : "")); + if (sendEmail) { + sendUserImportPasswordChangeEmail(user); + } } catch (Exception e) { ImportService.log.debug(e); rowResult.add(messageService.getMessage("error.fail.add")); @@ -351,6 +370,81 @@ ss.setAttribute(IImportService.STATUS_SUCCESSFUL, successful); } + /** + * Send an email with password change link so the user can set up their password after account got created. + */ + private void sendUserImportPasswordChangeEmail(User user) { + try { + if (USER_IMPORT_PASSWORD_CHANGE_EMAIL_TEMPLATE_CONTENT == null) { + USER_IMPORT_PASSWORD_CHANGE_EMAIL_TEMPLATE_CONTENT = Files + .readString(Paths.get(Configuration.get(ConfigurationKeys.LAMS_EAR_DIR), FileUtil.LAMS_WWW_DIR, + "userImportPasswordChangeEmailTemplate.html")); + } + + String key = RandomPasswordGenerator.generateForgotPasswordKey(); + + // all good, save the request in the db + ForgotPasswordRequest fp = new ForgotPasswordRequest(); + fp.setRequestDate(new Date()); + fp.setUserId(user.getUserId()); + fp.setRequestKey(key); + service.save(fp); + + // fill email content with given user's data + StringBuilder content = new StringBuilder(USER_IMPORT_PASSWORD_CHANGE_EMAIL_TEMPLATE_CONTENT); + String emailSubject = messageService.getMessage("user.import.password.change.email.content.subject"); + + int placeholderStart = content.indexOf(USER_IMPORT_PASSWORD_CHANGE_EMAIL_PAGE_TITLE_PLACEHOLDER); + int placeholderEnd = placeholderStart + USER_IMPORT_PASSWORD_CHANGE_EMAIL_PAGE_TITLE_PLACEHOLDER.length(); + content.replace(placeholderStart, placeholderEnd, emailSubject); + + placeholderStart = content.indexOf(USER_IMPORT_PASSWORD_CHANGE_EMAIL_TOP_HEADER_PLACEHOLDER); + placeholderEnd = placeholderStart + USER_IMPORT_PASSWORD_CHANGE_EMAIL_TOP_HEADER_PLACEHOLDER.length(); + content.replace(placeholderStart, placeholderEnd, emailSubject); + + placeholderStart = content.indexOf(USER_IMPORT_PASSWORD_CHANGE_EMAIL_CONTENT_START_PLACEHOLDER); + placeholderEnd = placeholderStart + USER_IMPORT_PASSWORD_CHANGE_EMAIL_CONTENT_START_PLACEHOLDER.length(); + content.replace(placeholderStart, placeholderEnd, + messageService.getMessage("user.import.password.change.email.content.start", + new Object[] { user.getFirstName() + " " + user.getLastName() })); + + StringBuilder link = new StringBuilder("") + .append(messageService.getMessage("user.import.password.change.email.content.link.label")) + .append(""); + + placeholderStart = content.indexOf(USER_IMPORT_PASSWORD_CHANGE_EMAIL_CONTENT_LINK_PLACEHOLDER); + placeholderEnd = placeholderStart + USER_IMPORT_PASSWORD_CHANGE_EMAIL_CONTENT_LINK_PLACEHOLDER.length(); + content.replace(placeholderStart, placeholderEnd, + messageService.getMessage("user.import.password.change.email.content.account.created", + new Object[] { user.getLogin(), link.toString() })); + + placeholderStart = content.indexOf(USER_IMPORT_PASSWORD_CHANGE_EMAIL_CONTENT_END_PLACEHOLDER); + placeholderEnd = placeholderStart + USER_IMPORT_PASSWORD_CHANGE_EMAIL_CONTENT_END_PLACEHOLDER.length(); + content.replace(placeholderStart, placeholderEnd, + messageService.getMessage("user.import.password.change.email.content.end")); + + placeholderStart = content.indexOf(USER_IMPORT_PASSWORD_CHANGE_EMAIL_CONTENT_THANKS_PLACEHOLDER); + placeholderEnd = placeholderStart + USER_IMPORT_PASSWORD_CHANGE_EMAIL_CONTENT_THANKS_PLACEHOLDER.length(); + content.replace(placeholderStart, placeholderEnd, + messageService.getMessage("user.import.password.change.email.content.thanks")); + + placeholderStart = content.indexOf(USER_IMPORT_PASSWORD_CHANGE_EMAIL_FOOTER_PLACEHOLDER); + placeholderEnd = placeholderStart + USER_IMPORT_PASSWORD_CHANGE_EMAIL_FOOTER_PLACEHOLDER.length(); + content.replace(placeholderStart, placeholderEnd, + messageService.getMessage("user.import.password.change.email.content.footer")); + + boolean isHtmlFormat = true; + + // log.info(content.toString()); + // send the email + Emailer.sendFromSupportEmail(emailSubject, user.getEmail(), content.toString(), isHtmlFormat); + } catch (Exception e) { + // failure handling + log.error("Problem sending email to: " + user.getLogin() + " with email: " + user.getEmail(), e); + } + } + @Override public List> parseRolesSpreadsheet(File fileItem, String sessionId) throws IOException { List> results = new ArrayList<>(); @@ -604,12 +698,6 @@ service.updatePassword(user, password); - if (generatedPassword.length() > 0) { - // if password was generated, make user change it on first log in - user.setChangePassword(true); - service.saveUser(user); - } - return user; } Index: lams_admin/src/java/org/lamsfoundation/lams/admin/web/controller/ImportExcelSaveController.java =================================================================== diff -u -r80371e446858031dcb9d7fa4456a79e8ab71693c -r9ab81eba5d1a95a6619094dfe12184f5580971e8 --- lams_admin/src/java/org/lamsfoundation/lams/admin/web/controller/ImportExcelSaveController.java (.../ImportExcelSaveController.java) (revision 80371e446858031dcb9d7fa4456a79e8ab71693c) +++ lams_admin/src/java/org/lamsfoundation/lams/admin/web/controller/ImportExcelSaveController.java (.../ImportExcelSaveController.java) (revision 9ab81eba5d1a95a6619094dfe12184f5580971e8) @@ -77,21 +77,23 @@ SessionManager.getSession().setAttribute(IImportService.IMPORT_FILE, file); // use a new thread only if number of users is > threshold if (importService.getNumRows(file) < IImportService.THRESHOLD) { - List results = importService.parseSpreadsheet(file, sessionId); + List results = importService.parseSpreadsheet(file, sessionId, importExcelForm.isSendEmail()); SessionManager.getSession(sessionId).setAttribute(IImportService.IMPORT_RESULTS, results); return "forward:/importuserresult.do"; } else { - Thread t = new Thread(new ImportExcelThread(sessionId)); + Thread t = new Thread(new ImportExcelThread(sessionId, importExcelForm.isSendEmail())); t.start(); return "import/status"; } } private class ImportExcelThread implements Runnable { private String sessionId; + private boolean sendEmail; - public ImportExcelThread(String sessionId) { + public ImportExcelThread(String sessionId, boolean sendEmail) { this.sessionId = sessionId; + this.sendEmail = sendEmail; } @Override @@ -101,7 +103,7 @@ IImportService importService = (IImportService) wac.getBean("importService"); try { File file = (File) SessionManager.getSession(sessionId).getAttribute(IImportService.IMPORT_FILE); - List results = importService.parseSpreadsheet(file, sessionId); + List results = importService.parseSpreadsheet(file, sessionId, sendEmail); SessionManager.getSession(sessionId).setAttribute(IImportService.IMPORT_RESULTS, results); } catch (Exception e) { log.warn("Exception in ImportExcelThread: ", e); Index: lams_admin/src/java/org/lamsfoundation/lams/admin/web/form/ImportExcelForm.java =================================================================== diff -u -r80371e446858031dcb9d7fa4456a79e8ab71693c -r9ab81eba5d1a95a6619094dfe12184f5580971e8 --- lams_admin/src/java/org/lamsfoundation/lams/admin/web/form/ImportExcelForm.java (.../ImportExcelForm.java) (revision 80371e446858031dcb9d7fa4456a79e8ab71693c) +++ lams_admin/src/java/org/lamsfoundation/lams/admin/web/form/ImportExcelForm.java (.../ImportExcelForm.java) (revision 9ab81eba5d1a95a6619094dfe12184f5580971e8) @@ -29,6 +29,7 @@ private Integer orgId; private String tmpFileUploadId; + private boolean sendEmail; public Integer getOrgId() { return orgId; @@ -45,4 +46,12 @@ public void setTmpFileUploadId(String tmpFileUploadId) { this.tmpFileUploadId = tmpFileUploadId; } + + public boolean isSendEmail() { + return sendEmail; + } + + public void setSendEmail(boolean sendEmail) { + this.sendEmail = sendEmail; + } } \ No newline at end of file Index: lams_admin/web/import/importexcel.jsp =================================================================== diff -u -r51902e5ec7e5e92493bdd4c6e58e211142b5f42e -r9ab81eba5d1a95a6619094dfe12184f5580971e8 --- lams_admin/web/import/importexcel.jsp (.../importexcel.jsp) (revision 51902e5ec7e5e92493bdd4c6e58e211142b5f42e) +++ lams_admin/web/import/importexcel.jsp (.../importexcel.jsp) (revision 9ab81eba5d1a95a6619094dfe12184f5580971e8) @@ -185,6 +185,12 @@ + +
+ +
Index: lams_central/src/java/org/lamsfoundation/lams/web/ForgotPasswordServlet.java =================================================================== diff -u -rb53d1132ca14c7e04df4a479cd6c94e8ce86100f -r9ab81eba5d1a95a6619094dfe12184f5580971e8 --- lams_central/src/java/org/lamsfoundation/lams/web/ForgotPasswordServlet.java (.../ForgotPasswordServlet.java) (revision b53d1132ca14c7e04df4a479cd6c94e8ce86100f) +++ lams_central/src/java/org/lamsfoundation/lams/web/ForgotPasswordServlet.java (.../ForgotPasswordServlet.java) (revision 9ab81eba5d1a95a6619094dfe12184f5580971e8) @@ -3,7 +3,6 @@ import java.io.IOException; import java.util.Date; import java.util.List; -import java.util.Properties; import javax.mail.MessagingException; import javax.mail.internet.AddressException; @@ -15,18 +14,13 @@ import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; -import org.hibernate.HibernateException; -import org.hibernate.id.Configurable; -import org.hibernate.id.IdentifierGenerator; -import org.hibernate.id.UUIDGenerator; -import org.hibernate.type.StringType; +import org.lamsfoundation.lams.integration.security.RandomPasswordGenerator; import org.lamsfoundation.lams.usermanagement.ForgotPasswordRequest; import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.lamsfoundation.lams.util.Configuration; import org.lamsfoundation.lams.util.ConfigurationKeys; import org.lamsfoundation.lams.util.Emailer; -import org.lamsfoundation.lams.util.FileUtilException; import org.lamsfoundation.lams.util.MessageService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.context.support.SpringBeanAutowiringSupport; @@ -172,7 +166,7 @@ if (!skipSendingEmail) { boolean isHtmlFormat = false; // generate a key for the request - String key = ForgotPasswordServlet.generateUniqueKey(); + String key = RandomPasswordGenerator.generateForgotPasswordKey(); // all good, save the request in the db ForgotPasswordRequest fp = new ForgotPasswordRequest(); @@ -258,22 +252,4 @@ request.setAttribute("showErrorMessage", showErrorMessage); request.getRequestDispatcher("/forgotPasswordProc.jsp").forward(request, response); } - - /** - * Generates the unique key used for the forgot password request - * - * @return a unique key - * @throws HibernateException - * @throws FileUtilException - * @throws IOException - */ - public static String generateUniqueKey() throws HibernateException { - Properties props = new Properties(); - - IdentifierGenerator uuidGen = new UUIDGenerator(); - ((Configurable) uuidGen).configure(StringType.INSTANCE, props, null); - - return ((String) uuidGen.generate(null, null)).toLowerCase(); - } - -} +} \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/integration/security/RandomPasswordGenerator.java =================================================================== diff -u -r00f07d4e073c8b6fb447a5fbf2fa03782ba8cb4b -r9ab81eba5d1a95a6619094dfe12184f5580971e8 --- lams_common/src/java/org/lamsfoundation/lams/integration/security/RandomPasswordGenerator.java (.../RandomPasswordGenerator.java) (revision 00f07d4e073c8b6fb447a5fbf2fa03782ba8cb4b) +++ lams_common/src/java/org/lamsfoundation/lams/integration/security/RandomPasswordGenerator.java (.../RandomPasswordGenerator.java) (revision 9ab81eba5d1a95a6619094dfe12184f5580971e8) @@ -20,11 +20,19 @@ */ package org.lamsfoundation.lams.integration.security; +import java.io.IOException; +import java.util.Properties; import java.util.Random; import org.apache.commons.lang.ArrayUtils; +import org.hibernate.HibernateException; +import org.hibernate.id.Configurable; +import org.hibernate.id.IdentifierGenerator; +import org.hibernate.id.UUIDGenerator; +import org.hibernate.type.StringType; import org.lamsfoundation.lams.util.Configuration; import org.lamsfoundation.lams.util.ConfigurationKeys; +import org.lamsfoundation.lams.util.FileUtilException; import org.lamsfoundation.lams.util.ValidationUtil; /** @@ -113,4 +121,21 @@ return result; } -} + + /** + * Generates the unique key used for the forgot password request + * + * @return a unique key + * @throws HibernateException + * @throws FileUtilException + * @throws IOException + */ + public static String generateForgotPasswordKey() { + Properties props = new Properties(); + + IdentifierGenerator uuidGen = new UUIDGenerator(); + ((Configurable) uuidGen).configure(StringType.INSTANCE, props, null); + + return ((String) uuidGen.generate(null, null)).toLowerCase(); + } +} \ No newline at end of file Index: lams_gradebook/src/java/org/lamsfoundation/lams/gradebook/service/GradebookService.java =================================================================== diff -u -rcde8c481e618581dcb2279191ef0b163e9abb80b -r9ab81eba5d1a95a6619094dfe12184f5580971e8 --- lams_gradebook/src/java/org/lamsfoundation/lams/gradebook/service/GradebookService.java (.../GradebookService.java) (revision cde8c481e618581dcb2279191ef0b163e9abb80b) +++ lams_gradebook/src/java/org/lamsfoundation/lams/gradebook/service/GradebookService.java (.../GradebookService.java) (revision 9ab81eba5d1a95a6619094dfe12184f5580971e8) @@ -1084,17 +1084,15 @@ @Override public String getReleaseMarksEmailContent(long lessonID, int userID) { - // temporary comment so template gets loaded every time - // eventually it will be loaded just once -// if (RELEASE_MARKS_EMAIL_TEMPLATE_CONTENT == null) { - try { - RELEASE_MARKS_EMAIL_TEMPLATE_CONTENT = Files - .readString(Paths.get(Configuration.get(ConfigurationKeys.LAMS_EAR_DIR), FileUtil.LAMS_WWW_DIR, - "gradebookReleaseLessonMarksEmailTemplate.html")); - } catch (Exception e) { - throw new RuntimeException("Can not read release marks email template", e); + if (RELEASE_MARKS_EMAIL_TEMPLATE_CONTENT == null) { + try { + RELEASE_MARKS_EMAIL_TEMPLATE_CONTENT = Files + .readString(Paths.get(Configuration.get(ConfigurationKeys.LAMS_EAR_DIR), FileUtil.LAMS_WWW_DIR, + "gradebookReleaseLessonMarksEmailTemplate.html")); + } catch (Exception e) { + throw new RuntimeException("Can not read release marks email template", e); + } } -// } User user = userService.getUserById(userID); Lesson lesson = lessonService.getLesson(lessonID); Index: lams_www/web/userImportPasswordChangeEmailTemplate.html =================================================================== diff -u --- lams_www/web/userImportPasswordChangeEmailTemplate.html (revision 0) +++ lams_www/web/userImportPasswordChangeEmailTemplate.html (revision 9ab81eba5d1a95a6619094dfe12184f5580971e8) @@ -0,0 +1,643 @@ + + + + + + + +[PAGE_TITLE_PLACEHOLDER] + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +