Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. The ASF licenses this file to You * under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. For additional information regarding * copyright in this work, please see the NOTICE file in the top level * directory of this distribution. * * Source file modified from the original ASF source; all changes made * are also under Apache License. */ package org.tightblog.service; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.MessageSource; import org.springframework.stereotype.Component; import org.tightblog.config.DynamicProperties; import org.tightblog.domain.User; import org.tightblog.domain.UserWeblogRole; import org.tightblog.domain.Weblog; import org.tightblog.domain.WeblogEntry; import org.tightblog.domain.WeblogEntryComment; import org.tightblog.domain.WeblogRole; import org.tightblog.domain.WebloggerProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.mail.MailAuthenticationException; import org.springframework.mail.MailSendException; import org.springframework.mail.javamail.JavaMailSender; import org.thymeleaf.context.Context; import org.thymeleaf.spring5.SpringTemplateEngine; import org.tightblog.repository.UserRepository; import org.tightblog.repository.UserWeblogRoleRepository; import org.tightblog.repository.WeblogEntryCommentRepository; import org.tightblog.repository.WebloggerPropertiesRepository; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.SendFailedException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; @Component @EnableConfigurationProperties(DynamicProperties.class) public class EmailService { private static Logger log = LoggerFactory.getLogger(EmailService.class); private UserManager userManager; private UserRepository userRepository; private UserWeblogRoleRepository userWeblogRoleRepository; private WeblogManager weblogManager; private WeblogEntryCommentRepository weblogEntryCommentRepository; private URLService urlService; private JavaMailSender mailSender; private SpringTemplateEngine standardTemplateEngine; private MessageSource messages; private WebloggerPropertiesRepository webloggerPropertiesRepository; private DynamicProperties dp; private boolean mailEnabled; @Autowired public EmailService(UserManager userManager, UserRepository userRepository, UserWeblogRoleRepository userWeblogRoleRepository, WeblogManager weblogManager, URLService urlService, JavaMailSender mailSender, SpringTemplateEngine standardTemplateEngine, MessageSource messages, DynamicProperties dp, WebloggerPropertiesRepository webloggerPropertiesRepository, WeblogEntryCommentRepository weblogEntryCommentRepository, @Value("${mail.enabled:false}") boolean mailEnabled) { this.userManager = userManager; this.userRepository = userRepository; this.userWeblogRoleRepository = userWeblogRoleRepository; this.webloggerPropertiesRepository = webloggerPropertiesRepository; this.weblogManager = weblogManager; this.weblogEntryCommentRepository = weblogEntryCommentRepository; this.urlService = urlService; this.mailSender = mailSender; this.standardTemplateEngine = standardTemplateEngine; this.messages = messages; this.dp = dp; this.mailEnabled = mailEnabled; } /** * Sends a newly registered user an activation code to confirm the email * entered at registration time is one the user has access to. This step * is before the account approval process if the latter has been activated. * * @param user user to send activation email to. */ public void sendUserActivationEmail(User user) { if (!mailEnabled) { log.warn("Cannot send user activation email to {} because mail.enabled=false; either enable" + " or have a blog server admin activate account from User Admin page.", user); return; } String activationURL = dp.getAbsoluteUrl() + "/tb-ui/app/login?activationCode=" + user.getActivationCode(); Context ctx = new Context(); ctx.setVariable("emailType", "UserActivation"); ctx.setVariable("userName", user.getUserName()); ctx.setVariable("activationURL", activationURL); String message = standardTemplateEngine.process("emails/CommonEmailLayout", ctx); String subject = messages.getMessage("user.account.activation.mail.subject", null, Locale.getDefault()); String[] to = new String[] { user.getEmailAddress() }; sendMessage(null, to, null, subject, message); } /** * Sends emails to weblogger admins when someone registers for a blog account and the * system is configured to require admin approval of new registrants * * @param user new user account requiring approval before it can be used. */ public void sendRegistrationApprovalRequest(User user) { if (!mailEnabled) { return; } String userAdminURL = urlService.getActionURL("/tb-ui/app/admin", "userAdmin", null, null); // send to blog server admins List<User> admins = userRepository.findAdmins(); String[] to = admins.stream().map(User::getEmailAddress).toArray(String[]::new); String subject = messages.getMessage("mailMessage.approveRegistrationSubject", new Object[] { user.getScreenName() }, Locale.getDefault()); Context ctx = new Context(); ctx.setVariable("emailType", "RegistrationApprovalRequest"); ctx.setVariable("screenName", user.getScreenName()); ctx.setVariable("emailAddress", user.getEmailAddress()); ctx.setVariable("userAdminURL", userAdminURL); String message = standardTemplateEngine.process("emails/CommonEmailLayout", ctx); sendMessage(null, to, null, subject, message); } /** * Sends an email to a newly approved user, letting him know he can now log into * the weblogger. * * @param user user whose account was just approved */ public void sendRegistrationApprovedNotice(User user) { if (!mailEnabled) { return; } String[] to = new String[] { user.getEmailAddress() }; String subject = messages.getMessage("mailMessage.registrationApprovedSubject", new Object[] { user.getScreenName() }, Locale.getDefault()); Context ctx = new Context(); ctx.setVariable("emailType", "RegistrationApprovedNotice"); ctx.setVariable("screenName", user.getScreenName()); ctx.setVariable("userName", user.getUserName()); ctx.setVariable("loginURL", urlService.getLoginURL()); String message = standardTemplateEngine.process("emails/CommonEmailLayout", ctx); sendMessage(null, to, null, subject, message); } /** * Sends an email to a newly rejected user, letting him know his account wasn't approved. * * @param user user whose account was not approved. */ public void sendRegistrationRejectedNotice(User user) { if (!mailEnabled) { return; } String[] to = new String[] { user.getEmailAddress() }; String subject = messages.getMessage("mailMessage.registrationRejectedSubject", null, Locale.getDefault()); Context ctx = new Context(); ctx.setVariable("emailType", "RegistrationRejectedNotice"); ctx.setVariable("screenName", user.getScreenName()); String message = standardTemplateEngine.process("emails/CommonEmailLayout", ctx); sendMessage(null, to, null, subject, message); } /** * Sends email to owners and publishers of a blog whenever someone with draft rights * submits a blog entry for review. * * @param entry pending WeblogEntry to review. */ public void sendPendingEntryNotice(WeblogEntry entry) { if (!mailEnabled) { return; } String from = entry.getCreator().getEmailAddress(); // build list of reviewers (website users with at least publish role) List<User> weblogUsers = userWeblogRoleRepository.findByWeblogAndStatusEnabled(entry.getWeblog()); List<String> reviewers = new ArrayList<>(); weblogUsers.forEach(user -> { if (userManager.checkWeblogRole(user, entry.getWeblog(), WeblogRole.POST) && user.getEmailAddress() != null) { reviewers.add(user.getEmailAddress()); } }); String[] to = reviewers.toArray(new String[0]); String subject = messages.getMessage("weblogEntry.pendingEntrySubject", new Object[] { entry.getWeblog().getName(), entry.getWeblog().getHandle() }, entry.getWeblog().getLocaleInstance()); Context ctx = new Context(entry.getWeblog().getLocaleInstance()); ctx.setVariable("emailType", "PendingEntryNotice"); ctx.setVariable("entryTitle", entry.getTitle()); ctx.setVariable("screenName", entry.getCreator().getScreenName()); String entryEditURL = urlService.getEntryEditURL(entry); ctx.setVariable("editURL", entryEditURL); String message = standardTemplateEngine.process("emails/CommonEmailLayout", ctx); sendMessage(from, to, new String[] { from }, subject, message); } /** * Sends email to owners and publishers of a blog whenever a blog reader leaves a comment * that needs to be moderated. * * @param comment pending comment to review. * @param commentNotes any spam or validation issues found, to be placed within the email. */ public void sendPendingCommentNotice(WeblogEntryComment comment, Map<String, List<String>> commentNotes) { if (!mailEnabled || comment.isApproved()) { return; } WeblogEntry entry = comment.getWeblogEntry(); Weblog weblog = entry.getWeblog(); User user = entry.getCreator(); Context ctx = new Context(weblog.getLocaleInstance()); ctx.setVariable("comment", comment); String commentURL = urlService.getWeblogEntryCommentsURL(entry); ctx.setVariable("commentURL", commentURL); ctx.setVariable("messages", commentNotes); Map<String, String> parameters = new HashMap<>(); parameters.put("bean.entryId", entry.getId()); String manageURL = urlService.getActionURL("/tb-ui/app/authoring", "comments", weblog, parameters); ctx.setVariable("manageURL", manageURL); String homeURL = urlService.getActionURL("/tb-ui/app", "home", null, null); ctx.setVariable("homeURL", homeURL); String msg = standardTemplateEngine.process("emails/PendingCommentNotice", ctx); String subject = messages.getMessage("email.comment.moderate.title", null, weblog.getLocaleInstance()); subject += entry.getTitle(); List<UserWeblogRole> bloggerList = userWeblogRoleRepository.findByWeblogAndEmailCommentsTrue(weblog); String[] sendToList = bloggerList.stream().map(UserWeblogRole::getUser).map(User::getEmailAddress) .collect(Collectors.toList()).toArray(new String[bloggerList.size()]); String from = user.getEmailAddress(); sendMessage(from, sendToList, null, subject, msg); } /** * Sends an email to members of a weblog and prior commenters who selected "notify me" whenever * a new comment appears (after approval if moderation required for it.) * * @param comment new comment to announce */ public void sendNewPublishedCommentNotification(WeblogEntryComment comment) { if (!mailEnabled || !webloggerPropertiesRepository.findOrNull().isUsersCommentNotifications() || !comment.isApproved()) { return; } WeblogEntry entry = comment.getWeblogEntry(); Weblog weblog = entry.getWeblog(); User user = entry.getCreator(); // build list of email addresses to send notification to Map<String, String> subscribers = new HashMap<>(); // Get all the subscribers to this comment thread List<WeblogEntryComment> priorComments = weblogEntryCommentRepository .findByWeblogEntryAndStatusApproved(entry).stream() // don't send a routing email to the person who made the comment. .filter(pc -> !comment.getEmail().equalsIgnoreCase(pc.getEmail())).collect(Collectors.toList()); for (WeblogEntryComment priorComment : priorComments) { // if user has commented twice, count the most recent notify setting if (priorComment.getNotify()) { log.info("Add to subscribers list: {}", priorComment.getEmail()); subscribers.put(priorComment.getEmail().toLowerCase(), priorComment.getId()); } else { // remove user who doesn't want to be notified log.info("Remove from subscribers list: {}", priorComment.getEmail()); subscribers.remove(priorComment.getEmail().toLowerCase()); } } String from = user.getEmailAddress(); String subject = messages.getMessage("email.comment.title", new Object[] { entry.getTitle() }, weblog.getLocaleInstance()); // send message to blog members (same email for everyone) if (// if must moderate on, blogger(s) already got pending email, good enough. !WebloggerProperties.CommentPolicy.MUSTMODERATE.equals(weblog.getAllowComments())) { List<UserWeblogRole> bloggerList = userWeblogRoleRepository.findByWeblogAndEmailCommentsTrue(weblog); Context ctx = getPublishedCommentNotificationContext(comment, null); String msg = standardTemplateEngine.process("emails/NewCommentNotification", ctx); String[] bloggerEmailAddrs = bloggerList.stream().map(UserWeblogRole::getUser) .map(User::getEmailAddress).collect(Collectors.toList()) .toArray(new String[bloggerList.size()]); sendMessage(from, bloggerEmailAddrs, null, subject, msg); } // now send to subscribers (different email each with different unsubscribe link) if (subscribers.size() > 0) { for (Map.Entry<String, String> subscriber : subscribers.entrySet()) { Context ctx = getPublishedCommentNotificationContext(comment, subscriber); String msg = standardTemplateEngine.process("emails/NewCommentNotification", ctx); sendMessage(from, null, new String[] { subscriber.getKey() }, subject, msg); } } } private Context getPublishedCommentNotificationContext(WeblogEntryComment comment, Map.Entry<String, String> subscriber) { WeblogEntry entry = comment.getWeblogEntry(); Weblog weblog = entry.getWeblog(); Context ctx = new Context(weblog.getLocaleInstance()); ctx.setVariable("comment", comment); String commentURL = urlService.getWeblogEntryCommentsURL(entry); ctx.setVariable("commentURL", commentURL); if (subscriber != null) { ctx.setVariable("unsubscribeURL", urlService.getCommentNotificationUnsubscribeURL(subscriber.getValue())); } return ctx; } /** * A list of newly moderated and approved comments, informing the commenter * that his comment is now on the weblog. * * @param comments list of comments */ public void sendYourCommentWasApprovedNotifications(List<WeblogEntryComment> comments) { if (!mailEnabled || comments == null || comments.size() < 1) { return; } for (WeblogEntryComment comment : comments) { // Send email notifications because a new comment has been approved sendNewPublishedCommentNotification(comment); // Send approval notification to author of approved comment sendYourCommentWasApprovedNotification(comment); } } private void sendYourCommentWasApprovedNotification(WeblogEntryComment wec) { WeblogEntry entry = wec.getWeblogEntry(); Context ctx = new Context(); ctx.setVariable("emailType", "CommentApproved"); ctx.setVariable("commentURL", urlService.getCommentURL(entry, wec.getTimestamp())); String message = standardTemplateEngine.process("emails/CommonEmailLayout", ctx); // send message to author of approved comment String subject = messages.getMessage("email.comment.commentApproved", null, entry.getWeblog().getLocaleInstance()); sendMessage(null, new String[] { wec.getEmail() }, null, subject, message); } /** * This method is used to send an HTML Message * * @param from e-mail address of sender * @param to e-mail address(es) of recipients * @param subject subject of e-mail * @param content the body of the e-mail */ private void sendMessage(String from, String[] to, String[] cc, String subject, String content) { try { MimeMessage message = mailSender.createMimeMessage(); // n.b. any default from address is expected to be determined by caller. if (!StringUtils.isEmpty(from)) { InternetAddress sentFrom = new InternetAddress(from); message.setFrom(sentFrom); log.debug("e-mail from: {}", sentFrom); } if (to != null) { InternetAddress[] sendTo = new InternetAddress[to.length]; for (int i = 0; i < to.length; i++) { sendTo[i] = new InternetAddress(to[i]); log.debug("sending e-mail to: {}", to[i]); } message.setRecipients(Message.RecipientType.TO, sendTo); } if (cc != null) { InternetAddress[] copyTo = new InternetAddress[cc.length]; for (int i = 0; i < cc.length; i++) { copyTo[i] = new InternetAddress(cc[i]); log.debug("copying e-mail to: {}", cc[i]); } message.setRecipients(Message.RecipientType.CC, copyTo); } message.setSubject((subject == null) ? "(no subject)" : subject, "UTF-8"); message.setContent(content, "text/html; charset=utf-8"); message.setSentDate(new java.util.Date()); // First collect all the addresses together. boolean bFailedToSome = false; SendFailedException sendex = new SendFailedException("Unable to send message to some recipients"); try { // Send to the list of remaining addresses, ignoring the addresses attached to the message mailSender.send(message); } catch (MailAuthenticationException | MailSendException ex) { bFailedToSome = true; sendex.setNextException(ex); } if (bFailedToSome) { throw sendex; } } catch (MessagingException e) { log.error("ERROR: Problem sending email with subject {}", subject, e); } } }