Skip to content

Instantly share code, notes, and snippets.

@davidtruchet
Last active June 18, 2017 01:49
Show Gist options
  • Save davidtruchet/6c9f28da7136225e16da016f9c39f43a to your computer and use it in GitHub Desktop.
Save davidtruchet/6c9f28da7136225e16da016f9c39f43a to your computer and use it in GitHub Desktop.
AEM Custom Email Service
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:OsgiConfig"
job.enabled="{Boolean}true"
scheduler.expression="0 0 12 1/1 * ? *"
site.root.path="/content/site/global"
site.logo="http://logos-download.com/yourlogo.png"
email.receipt="some@email.com"
email.template="/etc/notification/email/html/site/pageActivationNotificationTemplate.txt"/>
package com.site.dotcom.service.notification;
import java.util.Map;
public interface NotificationEmailService {
void sendEmail(String emailTemplate, String emailReceipt, Map<String, String> properties);
void sendEmailToGroup(String emailTemplate, String emailGroup, Map<String, String> properties);
}
package com.site.dotcom.service.notification.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.text.StrLookup;
import org.apache.commons.mail.HtmlEmail;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.osgi.framework.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.day.cq.commons.mail.MailTemplate;
import com.day.cq.mailer.MessageGateway;
import com.day.cq.mailer.MessageGatewayService;
import com.site.dotcom.service.notification.NotificationEmailService;
import com.site.dotcom.service.utils.UserUtils;
@Service(NotificationEmailService.class)
@Component(immediate = true, metatype = true)
@Properties({ @Property(name = Constants.SERVICE_VENDOR, value = "Site"),
@Property(name = Constants.SERVICE_DESCRIPTION, value = "Service responsable to send email notifications.") })
public class NotificationEmailServiceImpl implements NotificationEmailService {
private static final Logger log = LoggerFactory.getLogger(NotificationEmailServiceImpl.class);
@Reference
private ResourceResolverFactory resolverFactory;
@Reference
private MessageGatewayService messageGatewayService;
public void sendEmail(String emailTemplate, String emailReceipt, Map<String, String> properties) {
ResourceResolver resourceResolver = null;
try {
Map<String, Object> param = new HashMap<String, Object>();
param.put(ResourceResolverFactory.SUBSERVICE, this.getClass().getName());
resourceResolver = resolverFactory.getServiceResourceResolver(param);
ArrayList<InternetAddress> emailRecipients = new ArrayList<InternetAddress>();
emailRecipients.add(new InternetAddress(emailReceipt));
sendEmail(emailTemplate, emailRecipients, resourceResolver, properties);
} catch (LoginException e) {
log.error("Could not login into the repository", e);
} catch (AddressException e) {
log.error("Error with internet address when trying to send email", e);
} finally {
if (resourceResolver != null && resourceResolver.isLive()) {
resourceResolver.close();
}
}
}
public void sendEmailToGroup(String emailTemplate, String groupName, Map<String, String> properties) {
ResourceResolver resourceResolver = null;
try {
Map<String, Object> param = new HashMap<String, Object>();
param.put(ResourceResolverFactory.SUBSERVICE, this.getClass().getName());
resourceResolver = resolverFactory.getServiceResourceResolver(param);
UserManager um = resourceResolver.adaptTo(UserManager.class);
List<String> emailAddress = new ArrayList<String>();
Authorizable notifyAuth = um.getAuthorizable(groupName);
if (notifyAuth != null && notifyAuth.isGroup()) {
Group group = (Group) notifyAuth;
emailAddress.addAll(getEmailAddress(group, resourceResolver));
} else if (notifyAuth != null && !notifyAuth.isGroup()) {
String email = UserUtils.getProfileProperty(notifyAuth, resourceResolver, "email");
if (StringUtils.isNotEmpty(email)) {
emailAddress.add(email);
}
}
ArrayList<InternetAddress> emailRecipients = getEmailRecipients(emailAddress);
sendEmail(emailTemplate, emailRecipients, resourceResolver, properties);
} catch (LoginException e) {
log.error("Could not login into the repository", e);
} catch (RepositoryException e) {
log.error("Repository exception", e);
} finally {
if (resourceResolver != null && resourceResolver.isLive()) {
resourceResolver.close();
}
}
}
private List<String> getEmailAddress(Group group, ResourceResolver resolver) {
List<String> emailAddress = new ArrayList<String>();
Iterator<Authorizable> members;
try {
members = group.getMembers();
while (members.hasNext()) {
Authorizable member = (Authorizable) members.next();
if (member.isGroup()) {
emailAddress.addAll(getEmailAddress((Group) member, resolver));
} else {
String email = UserUtils.getProfileProperty(member, resolver, "email");
if (StringUtils.isNotEmpty(email)) {
emailAddress.add(email);
}
}
}
} catch (RepositoryException e) {
log.error("Could not access to repository", e);
}
return emailAddress;
}
private ArrayList<InternetAddress> getEmailRecipients(List<String> recipients) {
ArrayList<InternetAddress> emailRecipients = new ArrayList<InternetAddress>();
for (String recipient : recipients) {
try {
emailRecipients.add(new InternetAddress(recipient));
} catch (AddressException e) {
log.error(e.getMessage(), e);
}
}
return emailRecipients;
}
private void sendEmail(String emailTemplate, ArrayList<InternetAddress> emailRecipients, ResourceResolver resolver,
Map<String, String> emailProperties) {
// Getting the Email template.
Resource templateRsrc = resolver.getResource(emailTemplate);
MailTemplate mailTemplate = MailTemplate.create(templateRsrc.getPath(), resolver.adaptTo(Session.class));
try {
MessageGateway<HtmlEmail> messageGateway = messageGatewayService.getGateway(HtmlEmail.class);
// Creating the Email.
HtmlEmail email = new HtmlEmail();
email = mailTemplate.getEmail(StrLookup.mapLookup(emailProperties), HtmlEmail.class);
email.setTo(emailRecipients);
messageGateway.send(email);
} catch (Exception e) {
log.error("Fatal error while sending email: ", e);
}
}
}
package com.site.dotcom.service.notification.job;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.day.cq.commons.Externalizer;
import com.day.cq.search.PredicateGroup;
import com.day.cq.search.Query;
import com.day.cq.search.QueryBuilder;
import com.day.cq.search.result.Hit;
import com.day.cq.search.result.SearchResult;
import com.day.cq.wcm.api.Page;
import com.site.dotcom.service.notification.NotificationEmailService;
@Component(immediate = true, metatype = true)
@Service(Runnable.class)
@Properties({ @Property(name = Constants.SERVICE_VENDOR, value = "Site"),
@Property(name = "scheduler.expression", value = "0 0/2 * 1/1 * ? *", label = "Cron expression", description = "Scheduler's cron expression"),
@Property(name = "scheduler.concurrent", boolValue = false, label = "Allow concurrent executions", description = "Allow concurrent executions of this Scheduled Service") })
public class PageActivationNotificationJob implements Runnable {
private static final Logger LOG = LoggerFactory.getLogger(PageActivationNotificationJob.class);
@Property(boolValue = false, description = "Is the service enabled?", label = "Enabled")
public static final String ENABLED = "job.enabled";
private boolean enabled = false;
@Property(label = "Site Root Path", description = "Site Root path.")
public static final String PROPERTY_DAM_FOLDER = "site.root.path";
private String siteRootPath;
@Property(label = "Email Template", description = "Email template path.")
public static final String PROPERTY_EMAIL_TEMPLATE = "email.template";
private String emailTemplate;
@Property(label = "Site Logo", description = "Site logo path.")
public static final String PROPERTY_SITE_LOGO = "site.logo";
private String logoPath;
@Property(label = "Email Recepit", description = "Email receipt.")
public static final String PROPERTY_EMAIL_RECEIPT = "email.receipt";
private String emailReceipt;
@Reference
private ResourceResolverFactory resolverFactory;
@Reference
private NotificationEmailService notificationEmailService;
@Reference
private QueryBuilder queryBuilder;
@Reference
private Externalizer externalizer;
@Override
public void run() {
if (!enabled) {
LOG.info("service not enabled, or not on author server, exiting");
return;
}
ResourceResolver resourceResolver = null;
try {
Map<String, Object> param = new HashMap<String, Object>();
param.put(ResourceResolverFactory.SUBSERVICE, this.getClass().getName());
resourceResolver = resolverFactory.getServiceResourceResolver(param);
Collection<Page> pages = getPageHits(resourceResolver);
if (pages.isEmpty())
return;
Map<String, String> properties = new HashMap<String, String>();
properties.put("logoPath", logoPath);
properties.put("pages", buildHTML(pages, resourceResolver));
notificationEmailService.sendEmail(emailTemplate, emailReceipt, properties);
} catch (Exception e) {
LOG.error("Could not get ResourceResolver instance or handle page modification", e);
} finally {
if (resourceResolver != null && resourceResolver.isLive()) {
resourceResolver.close();
}
}
}
private String buildHTML(Collection<Page> pages, ResourceResolver resolver) {
StringBuilder html = new StringBuilder();
html.append("<div>");
html.append("<ul>\n");
for (Page page : pages) {
String pageLink = getAbsolutePageLink(page, resolver);
html.append("<li>\n<a href=\"" + pageLink + "\">");
html.append(this.getTitle(page));
html.append("</a>\n");
html.append("<p> Last activated by " + getLastReplicatedBy(page) + " on " + getDates(page) + "</p>");
}
html.append("</ul>\n");
html.append("</div>");
return html.toString();
}
private String getDates(Page page) {
ValueMap properties = page.getContentResource().adaptTo(ValueMap.class);
Date date = properties.get("cq:lastReplicated", Date.class);
String dateFormatted = "";
if (date != null) {
SimpleDateFormat formatter = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss");
dateFormatted = formatter.format(date.getTime());
}
return dateFormatted;
}
private String getLastReplicatedBy(Page page) {
ValueMap properties = page.getContentResource().adaptTo(ValueMap.class);
return properties.get("cq:lastReplicatedBy", String.class);
}
private String getAbsolutePageLink(Page page, ResourceResolver resolver) {
StringBuilder absolutePageLink = new StringBuilder();
absolutePageLink.append(externalizer.authorLink(resolver, page.getPath()));
return absolutePageLink.toString();
}
private Collection<Page> getPageHits(ResourceResolver resolver) throws RepositoryException {
Calendar yesterday = Calendar.getInstance();
yesterday.add(Calendar.DAY_OF_MONTH, -1);
Map<String, String> map = new HashMap<String, String>();
map.put("path", siteRootPath);
map.put("type", "cq:Page");
map.put("1_daterange.property", "jcr:content/cq:lastReplicated");
map.put("1_daterange.lowerBound", "" + yesterday.getTimeInMillis());
map.put("1_daterange.lowerOperation", ">=");
map.put("2_property", "jcr:content/cq:lastReplicationAction");
map.put("2_property.value", "Activate");
Query query = queryBuilder.createQuery(PredicateGroup.create(map), resolver.adaptTo(Session.class));
query.setStart(0);
query.setHitsPerPage(1000);
SearchResult results = query.getResult();
return getPages(resolver, results.getHits());
}
private String getTitle(Page page) {
ValueMap properties = page.getContentResource().adaptTo(ValueMap.class);
return properties.get("jcr:title", String.class);
}
private Collection<Page> getPages(ResourceResolver resolver, Collection<Hit> hits) throws RepositoryException {
List<Page> pages = new ArrayList<Page>();
if (hits.size() > 0) {
for (Hit hit : hits) {
Resource res = resolver.getResource(hit.getPath());
Page page = null;
if (null != res) {
page = (Page) res.adaptTo(Page.class);
if (null != page) {
pages.add(page);
}
}
}
}
return pages;
}
@Activate
protected void activate(final BundleContext bundleContext, final Map<String, Object> properties) {
configureService(properties);
}
protected void configureService(Map<String, Object> properties) {
try {
this.enabled = PropertiesUtil.toBoolean(properties.get(ENABLED), this.enabled);
this.siteRootPath = PropertiesUtil.toString(properties.get(PROPERTY_DAM_FOLDER), this.siteRootPath);
if (this.siteRootPath == null) {
throw new ServiceException("Missing property: " + PROPERTY_DAM_FOLDER);
}
this.emailTemplate = PropertiesUtil.toString(properties.get(PROPERTY_EMAIL_TEMPLATE), this.emailTemplate);
if (this.emailTemplate == null) {
throw new ServiceException("Missing property: " + PROPERTY_EMAIL_TEMPLATE);
}
this.logoPath = PropertiesUtil.toString(properties.get(PROPERTY_SITE_LOGO), this.logoPath);
if (this.logoPath == null) {
throw new ServiceException("Missing property: " + PROPERTY_SITE_LOGO);
}
this.emailReceipt = PropertiesUtil.toString(properties.get(PROPERTY_EMAIL_RECEIPT), this.emailReceipt);
if (this.emailReceipt == null) {
throw new ServiceException("Missing property: " + PROPERTY_EMAIL_RECEIPT);
}
} catch (Exception ex) {
LOG.error("error reading configuration property", ex);
}
}
}
Subject: Page Activation Report
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body style="white-space: normal;">
<div style="background-color:#e5e5e5;margin:10px 0">
<div style="direction:ltr;text-align:left;font-family:'Open sans','Arial',sans-serif;color:#444;background-color:white;padding:1.5em;border-radius:1em;max-width:580px;margin:auto">
<table style="background:white;" width="600" border="0" cellpadding="0" cellspacing="0" align="center">
<tbody>
<tr>
<td>
<div style="width:90px;margin:10px auto">
<img src="${logoPath}" alt="Site" height="50" class="logo">
</div>
<div style="padding-bottom:3px">
<p>
<span style="font-family:'Open sans','Arial',sans-serif;font-weight:bold;font-size:small;">Hello PLL liftandshift users:
</span>
</p>
<p>
This is an automated email to notify you what pages were activated.
</p>
${pages}
</div>
<div>
<p>
<span style="font-family:'Open sans','Arial',sans-serif;font-weight:bold;font-size:small;">Thank you,<br>AEM
</span>
</p>
</div>
<br><br>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
package com.site.dotcom.service.utils;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(UserUtils.class);
public static String getProfileProperty(Authorizable authorizable, ResourceResolver resolver, String propertyName) {
try {
Resource profile = resolver.getResource(authorizable.getPath() + "/profile");
ValueMap profileProperties = profile.adaptTo(ValueMap.class);
if (profileProperties.containsKey(propertyName)) {
return profileProperties.get(propertyName, String.class);
}
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
return null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment