Last active
June 18, 2017 01:49
-
-
Save davidtruchet/6c9f28da7136225e16da016f9c39f43a to your computer and use it in GitHub Desktop.
AEM Custom Email Service
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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"/> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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