/**
* =============================================================================
*
* ORCID (R) Open Source
* http://orcid.org
*
* Copyright (c) 2012-2014 ORCID, Inc.
* Licensed under an MIT-Style License (MIT)
* http://orcid.org/open-source-license
*
* This copyright and license information (including a link to the full license)
* shall be included in its entirety in all copies or substantial portion of
* the software.
*
* =============================================================================
*/
package org.orcid.core.manager.impl;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import org.apache.commons.codec.binary.Base64;
import org.orcid.core.locale.LocaleManager;
import org.orcid.core.manager.EmailMessage;
import org.orcid.core.manager.EmailMessageSender;
import org.orcid.core.manager.EncryptionManager;
import org.orcid.core.manager.LoadOptions;
import org.orcid.core.manager.NotificationManager;
import org.orcid.core.manager.OrcidProfileManager;
import org.orcid.core.manager.TemplateManager;
import org.orcid.jaxb.model.common_v2.SourceClientId;
import org.orcid.jaxb.model.message.OrcidProfile;
import org.orcid.jaxb.model.notification.amended_v2.NotificationAmended;
import org.orcid.jaxb.model.notification.permission_v2.NotificationPermission;
import org.orcid.jaxb.model.notification_v2.Notification;
import org.orcid.model.notification.institutional_sign_in_v2.NotificationInstitutionalConnection;
import org.orcid.persistence.dao.EmailDao;
import org.orcid.persistence.dao.NotificationDao;
import org.orcid.persistence.jpa.entities.EmailEntity;
import org.orcid.pojo.DigestEmail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.transaction.support.TransactionTemplate;
import com.google.common.collect.Lists;
/**
*
* @author Will Simpson
*
*/
public class EmailMessageSenderImpl implements EmailMessageSender {
private static final String DIGEST_FROM_ADDRESS = "update@notify.orcid.org";
private static final Logger LOGGER = LoggerFactory.getLogger(EmailMessageSenderImpl.class);
@Resource
private NotificationDao notificationDao;
@Resource
private NotificationDao notificationDaoReadOnly;
@Resource
private NotificationManager notificationManager;
@Resource
private MailGunManager mailGunManager;
@Resource
private EmailDao emailDao;
@Resource
private TransactionTemplate transactionTemplate;
@Resource
private TemplateManager templateManager;
@Resource
private OrcidProfileManager orcidProfileManager;
@Resource
private LocaleManager localeManager;
@Resource
private MessageSource messages;
@Resource
private OrcidUrlManager orcidUrlManager;
@Resource
private EncryptionManager encryptionManager;
@Override
public EmailMessage createDigest(String orcid, Collection<Notification> notifications) {
OrcidProfile orcidProfile = orcidProfileManager.retrieveOrcidProfile(orcid, LoadOptions.BIO_AND_INTERNAL_ONLY);
Locale locale = localeManager.getLocaleFromOrcidProfile(orcidProfile);
return createDigest(orcidProfile, notifications, locale);
}
@Override
public EmailMessage createDigest(OrcidProfile orcidProfile, Collection<Notification> notifications, Locale locale) {
int totalMessageCount = 0;
int orcidMessageCount = 0;
int addActivitiesMessageCount = 0;
int amendedMessageCount = 0;
int activityCount = 0;
Set<String> memberIds = new HashSet<>();
DigestEmail digestEmail = new DigestEmail();
for (Notification notification : notifications) {
digestEmail.addNotification(notification);
totalMessageCount++;
if (notification.getSource() == null) {
orcidMessageCount++;
} else {
SourceClientId clientId = notification.getSource().getSourceClientId();
if (clientId != null) {
memberIds.add(clientId.getPath());
}
}
if (notification instanceof NotificationPermission) {
addActivitiesMessageCount++;
NotificationPermission permissionNotification = (NotificationPermission) notification;
activityCount += permissionNotification.getItems().getItems().size();
permissionNotification.setEncryptedPutCode(encryptAndEncodePutCode(permissionNotification.getPutCode()));
} else if (notification instanceof NotificationInstitutionalConnection) {
notification.setEncryptedPutCode(encryptAndEncodePutCode(notification.getPutCode()));
} else if (notification instanceof NotificationAmended) {
amendedMessageCount++;
}
}
String emailName = notificationManager.deriveEmailFriendlyName(orcidProfile);
String subject = messages.getMessage("email.subject.digest", new String[] { emailName, String.valueOf(totalMessageCount) }, locale);
Map<String, Object> params = new HashMap<>();
params.put("locale", locale);
params.put("messages", messages);
params.put("messageArgs", new Object[0]);
params.put("orcidProfile", orcidProfile);
params.put("emailName", emailName);
params.put("digestEmail", digestEmail);
params.put("frequency", orcidProfile.getOrcidInternal().getPreferences().getSendEmailFrequencyDays());
params.put("totalMessageCount", String.valueOf(totalMessageCount));
params.put("orcidMessageCount", orcidMessageCount);
params.put("addActivitiesMessageCount", addActivitiesMessageCount);
params.put("activityCount", activityCount);
params.put("amendedMessageCount", amendedMessageCount);
params.put("memberIdsCount", memberIds.size());
params.put("baseUri", orcidUrlManager.getBaseUrl());
params.put("subject", subject);
String bodyText = templateManager.processTemplate("digest_email.ftl", params, locale);
String bodyHtml = templateManager.processTemplate("digest_email_html.ftl", params, locale);
EmailMessage emailMessage = new EmailMessage();
emailMessage.setSubject(subject);
emailMessage.setBodyText(bodyText);
emailMessage.setBodyHtml(bodyHtml);
return emailMessage;
}
private String encryptAndEncodePutCode(Long putCode) {
String encryptedPutCode = encryptionManager.encryptForExternalUse(String.valueOf(putCode));
try {
return Base64.encodeBase64URLSafeString(encryptedPutCode.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Problem base 64 encoding notification put code for notification id = " + putCode, e);
}
}
@Override
public void sendEmailMessages() {
LOGGER.info("About to send email messages");
List<String> orcidsWithMessagesToSend = notificationDaoReadOnly.findOrcidsWithNotificationsToSend();
for (final String orcid : orcidsWithMessagesToSend) {
try {
LOGGER.info("Sending messages for orcid: {}", orcid);
List<Notification> notifications = notificationManager.findUnsentByOrcid(orcid);
LOGGER.info("Found {} messages to send for orcid: {}", notifications.size(), orcid);
EmailMessage digestMessage = createDigest(orcid, notifications);
digestMessage.setFrom(DIGEST_FROM_ADDRESS);
EmailEntity primaryEmail = emailDao.findPrimaryEmail(orcid);
if (primaryEmail == null) {
LOGGER.info("No primary email for orcid: " + orcid);
return;
}
digestMessage.setTo(primaryEmail.getId());
boolean successfullySent = mailGunManager.sendEmail(digestMessage.getFrom(), digestMessage.getTo(), digestMessage.getSubject(),
digestMessage.getBodyText(), digestMessage.getBodyHtml());
if (successfullySent) {
flagAsSent(notifications);
}
} catch (RuntimeException e) {
LOGGER.warn("Problem sending email message to user: " + orcid, e);
}
}
LOGGER.info("Finished sending email messages");
}
private void flagAsSent(List<Notification> notifications) {
List<Long> notificationIds = new ArrayList<>();
for (Notification notification : notifications) {
notificationIds.add(notification.getPutCode());
}
List<List<Long>> batches = Lists.partition(notificationIds, 30000);
for (List<Long> batch : batches) {
notificationDao.flagAsSent(batch);
}
notificationDao.flush();
}
}