package fr.ippon.tatami.service;
import fr.ippon.tatami.domain.Group;
import fr.ippon.tatami.domain.User;
import fr.ippon.tatami.domain.status.AbstractStatus;
import fr.ippon.tatami.domain.status.Status;
import fr.ippon.tatami.repository.DomainRepository;
import fr.ippon.tatami.repository.UserRepository;
import fr.ippon.tatami.security.AuthenticationService;
import fr.ippon.tatami.security.TatamiUserDetailsService;
import fr.ippon.tatami.service.dto.StatusDTO;
import fr.ippon.tatami.service.util.DomainUtil;
import org.apache.velocity.app.VelocityEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.core.env.Environment;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.ui.velocity.VelocityEngineUtils;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.mail.MessagingException;
import java.util.*;
/**
* Send e-mails.
* <p/>
* Templates are implemented with velocity.
* Emails localisation is based on system locale, this could be improved by storing a preferred locale
* for each user.
*
* @author Julien Dubois
*/
@Service
public class MailService {
private static final Logger log = LoggerFactory.getLogger(MailService.class);
@Inject
private Environment env;
@Inject
private MessageSource mailMessageSource;
@Inject
private AuthenticationService authenticationService;
@Inject
private UserService userService;
@Inject
private DomainRepository domainRepository;
private String host;
private int port;
private String smtpUser;
private String smtpPassword;
private String smtpTls;
private String from;
private String tatamiUrl;
private Locale locale;
// TODO: this can be used for external mail template configuration
private final String templateRoot = "/META-INF/tatami/mails/";
private final String templateSuffix = "Email";
@Inject
private VelocityEngine velocityEngine;
@Inject
private TatamiUserDetailsService tatamiUserDetailsService;
@PostConstruct
public void init() {
this.host = env.getProperty("smtp.host");
if (!env.getProperty("smtp.port").equals("")) {
this.port = env.getProperty("smtp.port", Integer.class);
}
this.smtpUser = env.getProperty("smtp.user");
this.smtpPassword = env.getProperty("smtp.password");
this.smtpTls = env.getProperty("smtp.tls");
this.from = env.getProperty("smtp.from");
this.tatamiUrl = env.getProperty("tatami.url");
// TODO : we should probably let the user choose which language he wants to use for email, and store this as a preference
this.locale = Locale.getDefault();
}
@Async
public void sendRegistrationEmail(String registrationKey, User user) {
String registrationUrl = tatamiUrl + "/#/register?key=" + registrationKey;
log.debug("Sending registration e-mail to User '{}', Url='{}' " +
"with locale : '{}'", user.getLogin(), registrationUrl, locale);
Map<String, Object> model = new HashMap<String, Object>();
model.put("user", user);
model.put("registrationUrl", registrationUrl);
sendTextFromTemplate(user.getLogin(), model, "registration", this.locale);
}
@Async
public void sendInvitationEmail(String email, User user) {
log.debug("Sending invitation e-mail to email '{}'", email);
Map<String, Object> model = new HashMap<String, Object>();
model.put("user", user.getLogin());
model.put("invitationUrl", tatamiUrl);
sendTextFromTemplate(user.getLogin(), model, "invitationMessage", this.locale);
}
@Async
public void sendLostPasswordEmail(String registrationKey, User user) {
String url = tatamiUrl + "/#/register?key=" + registrationKey;
log.debug("Sending lost password e-mail to User '{}', Url='{}' with locale : '{}'", user.getLogin(), url, locale);
Map<String, Object> model = new HashMap<String, Object>();
model.put("user", user);
model.put("reinitUrl", url);
sendTextFromTemplate(user.getLogin(), model, "lostPassword", this.locale);
}
@Async
public void sendValidationEmail(User user, String password) {
log.debug("Sending validation e-mail to User '{}', non-encrypted Password='{}' " +
"with locale : '{}'", user.getLogin(), password, locale);
Map<String, Object> model = new HashMap<String, Object>();
model.put("user", user);
model.put("password", password);
sendTextFromTemplate(user.getLogin(), model, "validation", this.locale);
}
@Async
public void sendPasswordReinitializedEmail(User user, String password) {
log.debug("Sending password re-initialization e-mail to User '{}', non-encrypted Password='{}' " +
"with locale : '{}'", user.getLogin(), password, locale);
Map<String, Object> model = new HashMap<String, Object>();
model.put("user", user);
model.put("password", password);
sendTextFromTemplate(user.getLogin(), model, "passwordReinitialized", this.locale);
}
@Async
public void sendUserPrivateMessageEmail(User mentionnedUser, Status status) {
log.debug("Sending Private Message e-mail to User '{}' " +
"with locale : '{}'", mentionnedUser.getLogin(), locale);
String url = tatamiUrl + "/#/home/status/" + status.getStatusId();
Map<String, Object> model = new HashMap<String, Object>();
model.put("user", mentionnedUser);
model.put("status", status);
model.put("statusUrl", url);
sendTextFromTemplate(mentionnedUser.getLogin(), model, "userPrivateMessage", this.locale);
}
@Async
public void sendUserMentionEmail(User mentionnedUser, Status status) {
log.debug("Sending Mention e-mail to User '{}' with locale : '{}'", mentionnedUser.getLogin(), locale);
String url = tatamiUrl + "/#/home/status/" + status.getStatusId();
Map<String, Object> model = new HashMap<String, Object>();
model.put("user", mentionnedUser);
model.put("status", status);
model.put("statusUrl", url);
sendTextFromTemplate(mentionnedUser.getLogin(), model, "userMention", this.locale);
}
@Async
public void sendDailyDigestEmail(User user, List<StatusDTO> statuses, int nbStatus,
Collection<User> suggestedUsers) {
log.debug("Sending daily digest e-mail to User '{}'", user.getLogin());
Map<String, Object> model = new HashMap<String, Object>();
model.put("user", user);
model.put("tatamiUrl", tatamiUrl);
model.put("statuses", statuses);
model.put("nbStatus", nbStatus);
model.put("suggestedUsers", suggestedUsers);
sendTextFromTemplate(user.getLogin(), model, "dailyDigest", this.locale);
}
@Async
public void sendWeeklyDigestEmail(User user, List<StatusDTO> statuses, int nbStatus,
Collection<User> suggestedUsers,
Collection<Group> suggestedGroup) {
log.debug("Sending weekly digest e-mail to User '{}'", user.getLogin());
Map<String, Object> model = new HashMap<String, Object>();
model.put("user", user);
model.put("tatamiUrl", tatamiUrl);
model.put("statuses", statuses);
model.put("nbStatus", nbStatus);
model.put("suggestedUsers", suggestedUsers);
model.put("suggestedGroups", suggestedGroup);
sendTextFromTemplate(user.getLogin(), model, "weeklyDigest", this.locale);
}
@Async
public void sendReportedStatusEmail(String emailReporting, AbstractStatus status) {
log.debug("Sending reported status e-mail to User '{}' with locale : '{}'", locale);
String url = tatamiUrl + "/#/home/status/" + status.getStatusId();
Map<String, Object> model = new HashMap<String, Object>();
model.put("status", status);
model.put("statusUrl", url);
for(String email : tatamiUserDetailsService.getAdminUsers()) {
sendTextFromTemplate(email, model, "reportedStatus", this.locale);
}
}
@Async
public void sendDeactivatedEmail(String email) {
log.debug("Sending deactivation e-mail to User '{}' with locale : '{}'", email, locale);
Map<String, Object> model = new HashMap<String, Object>();
model.put("deactivatedEmail", email);
model.put("username", DomainUtil.getUsernameFromLogin(email));
sendTextFromTemplate(email, model, "deactivatedUser", this.locale);
}
public boolean connectSmtpServer() {
if (host != null && !host.equals("")) {
JavaMailSenderImpl sender = configureJavaMailSender();
try {
sender.getSession().getTransport().connect();
return true;
} catch (MessagingException e) {
return false;
}
} else {
return false;
}
}
private void sendEmail(String email, String subject, String text) {
if (host != null && !host.equals("")) {
JavaMailSenderImpl sender = configureJavaMailSender();
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(email);
message.setFrom(from);
message.setSubject(subject);
message.setText(text);
try {
sender.send(message);
log.debug("Sent e-mail to User '{}'!", email);
} catch (MailException e) {
log.warn("Warning! SMTP server error, could not send e-mail.");
log.debug("SMTP Error : {}", e.getMessage());
log.debug("Did you configure your SMTP settings in /META-INF/tatami/tatami.properties ?");
}
} else {
log.debug("SMTP server is not configured in /META-INF/tatami/tatami.properties");
}
}
private JavaMailSenderImpl configureJavaMailSender() {
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost(host);
sender.setPort(port);
sender.setUsername(smtpUser);
sender.setPassword(smtpPassword);
if (smtpTls != null && !smtpTls.equals("")) {
Properties sendProperties = new Properties();
sendProperties.setProperty("mail.smtp.starttls.enable", "true");
sender.setJavaMailProperties(sendProperties);
}
return sender;
}
/**
* Generate and send the mail corresponding to the given template.
*/
private void sendTextFromTemplate(String email, Map<String, Object> model, String template, Locale locale) {
model.put("messages", mailMessageSource);
model.put("locale", locale);
String subject = mailMessageSource.getMessage(template + ".title", null, locale);
String text = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, templateRoot + template + templateSuffix,
"utf-8", model);
log.debug("e-mail text '{}", text);
sendEmail(email, subject, text);
}
}