/*
* Copyright (C) 2009-2017 Slava Semushin <slava.semushin@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package ru.mystamps.web.service;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.apache.commons.lang3.time.DatePrinter;
import org.apache.commons.lang3.time.FastDateFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;
import org.springframework.scheduling.annotation.Async;
import ru.mystamps.web.Url;
import ru.mystamps.web.service.dto.AdminDailyReport;
import ru.mystamps.web.service.dto.SendUsersActivationDto;
import ru.mystamps.web.service.exception.EmailSendingException;
public class MailServiceImpl implements MailService {
private static final Logger LOG = LoggerFactory.getLogger(MailServiceImpl.class);
private final JavaMailSender mailer;
private final MessageSource messageSource;
private final String adminEmail;
private final Locale adminLang;
private final String robotEmail;
private final boolean testMode;
private final DatePrinter shortDatePrinter;
public MailServiceImpl(
JavaMailSender mailer,
MessageSource messageSource,
String adminEmail,
Locale adminLang,
String robotEmail,
boolean testMode) {
this.mailer = mailer;
this.messageSource = messageSource;
this.adminEmail = adminEmail;
this.adminLang = adminLang;
this.robotEmail = robotEmail;
this.testMode = testMode;
this.shortDatePrinter = FastDateFormat.getInstance("dd.MM.yyyy", adminLang);
}
@Override
@Async
public void sendActivationKeyToUser(SendUsersActivationDto activation) {
Validate.isTrue(activation != null, "Activation must be non null");
Validate.isTrue(activation.getEmail() != null, "E-mail must be non null");
Validate.isTrue(activation.getLang() != null, "Language must be non null");
Validate.isTrue(activation.getActivationKey() != null, "Activation key must be non null");
sendMail(
activation.getEmail(),
getSubjectOfActivationMail(activation),
getTextOfActivationMail(activation),
"activation_key"
);
LOG.info(
"Email with activation code has been sent to {} (lang: {})",
activation.getEmail(),
activation.getLang()
);
}
@Override
@Async
public void sendDailyStatisticsToAdmin(AdminDailyReport report) {
sendMail(
adminEmail,
getSubjectOfDailyStatisticsMail(report),
getTextOfDailyStatisticsMail(report),
"daily_statistics"
);
String date = shortDatePrinter.format(report.getStartDate());
LOG.info(
"E-mail with daily statistics for {} has been sent to {} (lang: {})",
date,
adminEmail,
adminLang
);
}
@SuppressWarnings("PMD.UseObjectForClearerAPI")
private void sendMail(
final String email,
final String subject,
final String text,
final String tag) {
try {
// We're using MimeMessagePreparator only because of its capability of adding headers.
// Otherwise we would use SimpleMailMessage class.
MimeMessagePreparator preparator = new MimeMessagePreparator() {
@Override
@SuppressWarnings({
"PMD.SignatureDeclareThrowsException", "PMD.AccessorMethodGeneration"
})
public void prepare(MimeMessage mimeMessage) throws Exception {
MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "UTF-8");
message.setValidateAddresses(true);
message.setTo(email);
message.setFrom(new InternetAddress(robotEmail, "My Stamps", "UTF-8"));
message.setSubject(subject);
message.setText(text);
// see: http://documentation.mailgun.com/user_manual.html#sending-via-smtp
message.getMimeMessage().addHeader("X-Mailgun-Tag", tag);
if (testMode) {
message.getMimeMessage().addHeader("X-Mailgun-Drop-Message", "yes");
}
}
};
mailer.send(preparator);
} catch (MailException ex) {
throw new EmailSendingException("Can't send mail to e-mail " + email, ex);
}
}
private String getTextOfActivationMail(SendUsersActivationDto activation) {
String template = messageSource.getMessage("activation.text", null, activation.getLocale());
String activationUrl =
String.format("%s?key=%s", Url.ACTIVATE_ACCOUNT_PAGE, activation.getActivationKey());
Map<String, String> ctx = new HashMap<>();
ctx.put("site_url", testMode ? Url.SITE : Url.PUBLIC_URL);
ctx.put("activation_url_with_key", activationUrl);
ctx.put("expire_after_days", String.valueOf(CronService.PURGE_AFTER_DAYS));
StrSubstitutor substitutor = new StrSubstitutor(ctx);
return substitutor.replace(template);
}
private String getSubjectOfActivationMail(SendUsersActivationDto activation) {
return messageSource.getMessage("activation.subject", null, activation.getLocale());
}
private String getSubjectOfDailyStatisticsMail(AdminDailyReport report) {
String template = messageSource.getMessage("daily_stat.subject", null, adminLang);
String fromDate = shortDatePrinter.format(report.getStartDate());
Map<String, String> ctx = new HashMap<>();
ctx.put("date", fromDate);
put(ctx, "total_changes", report.countTotalChanges());
StrSubstitutor substitutor = new StrSubstitutor(ctx);
return substitutor.replace(template);
}
private String getTextOfDailyStatisticsMail(AdminDailyReport report) {
String template = messageSource.getMessage("daily_stat.text", null, adminLang);
String fromDate = shortDatePrinter.format(report.getStartDate());
String tillDate = shortDatePrinter.format(report.getEndDate());
Map<String, String> ctx = new HashMap<>();
ctx.put("from_date", fromDate);
ctx.put("to_date", tillDate);
put(ctx, "added_countries_cnt", report.getAddedCountriesCounter());
put(ctx, "untranslated_countries_cnt", report.getUntranslatedCountriesCounter());
put(ctx, "added_categories_cnt", report.getAddedCategoriesCounter());
put(ctx, "untranslated_categories_cnt", report.getUntranslatedCategoriesCounter());
put(ctx, "added_series_cnt", report.getAddedSeriesCounter());
put(ctx, "updated_series_cnt", report.getUpdatedSeriesCounter());
put(ctx, "updated_collections_cnt", report.getUpdatedCollectionsCounter());
put(ctx, "registration_requests_cnt", report.getRegistrationRequestsCounter());
put(ctx, "registered_users_cnt", report.getRegisteredUsersCounter());
put(ctx, "events_cnt", report.countEvents());
put(ctx, "not_found_cnt", report.getNotFoundCounter());
put(ctx, "failed_auth_cnt", report.getFailedAuthCounter());
put(ctx, "missing_csrf_cnt", report.getMissingCsrfCounter());
put(ctx, "invalid_csrf_cnt", report.getInvalidCsrfCounter());
put(ctx, "bad_request_cnt", -1L); // TODO: #122
return new StrSubstitutor(ctx).replace(template);
}
private static void put(Map<String, String> ctx, String key, long value) {
ctx.put(key, String.valueOf(value));
}
}