Goal
Provide Send Mail feature in Assets Console - http://localhost:4502/assets.html, so users can send free form emails about selected assets without leaving the AEM context...
FakeSMTP configured as mail server, for more info check this
Demo | Package Install | Github
Email Modal
Fake SMTP Server
Fake SMTP Server intercepting emails sent to localhost on port 25
Configure FakeSMTP properties in AEM
http://localhost:4502/system/console/configMgr/com.day.cq.mailer.DefaultMailService
Solution
1) Create email template nt:file /apps/eaem-assets-send-mail/mail-templates/send-assets.html with the following code...
Subject: ${subject} <table style="width:100%" width="100%" bgcolor="#ffffff" style="background-color:#ffffff;" border="0" cellpadding="0" cellspacing="0"> <tr> <td style="width:100%">${body}</td> </tr> </table>
2) Create a servlet apps.experienceaem.assets.SendMailServlet for sending emails...
package apps.experienceaem.assets; import javax.jcr.Session; import javax.servlet.Servlet; import javax.servlet.ServletException; import com.day.cq.commons.jcr.JcrConstants; import com.day.cq.mailer.MessageGatewayService; import org.apache.commons.lang3.StringUtils; import org.apache.jackrabbit.api.security.user.Authorizable; import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ValueMap; import org.apache.sling.api.servlets.SlingAllMethodsServlet; import org.apache.sling.api.servlets.SlingSafeMethodsServlet; import org.apache.sling.jcr.base.util.AccessControlUtil; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.osgi.framework.Constants; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.day.cq.commons.mail.MailTemplate; import com.day.cq.mailer.MessageGateway; import org.apache.commons.lang.text.StrLookup; import org.apache.commons.mail.Email; import org.apache.commons.mail.HtmlEmail; import javax.jcr.Session; import javax.mail.internet.InternetAddress; import java.util.*; import java.io.IOException; @Component( name = "Experience AEM Send Mail Servlet", immediate = true, service = Servlet.class, property = { "sling.servlet.methods=POST", "sling.servlet.paths=/bin/experience-aem/send-mail" } ) public class SendMailServlet extends SlingAllMethodsServlet { private final Logger logger = LoggerFactory.getLogger(getClass()); private static String EMAIL_TEMPLATE_PATH = "/apps/eaem-assets-send-mail/mail-templates/send-assets.html"; @Reference private MessageGatewayService messageGatewayService; @Override protected void doPost(final SlingHttpServletRequest req, final SlingHttpServletResponse resp) throws ServletException, IOException { ResourceResolver resourceResolver = req.getResourceResolver(); try{ String to = req.getParameter("./to"); String subject = req.getParameter("./subject"); String body = req.getParameter("./body"); Map<String, String> emailParams = new HashMap<String,String>(); emailParams.put("subject", subject); emailParams.put("body", body.replaceAll("\r\n", "")); sendMail(resourceResolver, emailParams, to); }catch(Exception e){ logger.error("Error sending email", e); } } private Email sendMail(ResourceResolver resolver, Map<String, String> emailParams, String recipientEmail) throws Exception{ MailTemplate mailTemplate = MailTemplate.create(EMAIL_TEMPLATE_PATH, resolver.adaptTo(Session.class)); if (mailTemplate == null) { throw new Exception("Template missing - " + EMAIL_TEMPLATE_PATH); } Email email = mailTemplate.getEmail(StrLookup.mapLookup(emailParams), HtmlEmail.class); email.setTo(Collections.singleton(new InternetAddress(recipientEmail))); MessageGateway<Email> messageGateway = messageGatewayService.getGateway(email.getClass()); messageGateway.send(email); return email; } }
3) Add the action bar button Send Mail at path /apps/eaem-assets-send-mail/content/send-mail-but with following configuration...
<?xml version="1.0" encoding="UTF-8"?> <jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" granite:rel="cq-damadmin-admin-actions-send-mail-activator" jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/collection/action" icon="email" target=".cq-damadmin-admin-childpages" text="Send Mail" variant="actionBar"> <data jcr:primaryType="nt:unstructured" text="Email sent..."/> </jcr:root>
4) Add the send mail modal /apps/eaem-assets-send-mail/send-mail-dialog, a form with To, Subject and Body. When user selects assets and clicks on Send Mail button the modal opens with the assets paths added to content field (email body). User can add additional content before clicking Send....
<?xml version="1.0" encoding="UTF-8"?> <jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" jcr:primaryType="cq:Page"> <jcr:content jcr:mixinTypes="[sling:VanityPath]" jcr:primaryType="nt:unstructured" jcr:title="Experience AEM Send Mail" sling:resourceType="granite/ui/components/coral/foundation/page"> <head jcr:primaryType="nt:unstructured"> <favicon jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/page/favicon"/> <viewport jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/admin/page/viewport"/> <clientlibs jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/includeclientlibs" categories="[coralui3,granite.ui.coral.foundation,granite.ui.shell,dam.gui.admin.coral,eaem.assets.send.mail]"/> </head> <body jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/page/body"> <items jcr:primaryType="nt:unstructured"> <content jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form" action="/bin/experience-aem/send-mail" foundationForm="{Boolean}true" maximized="{Boolean}true" method="post" novalidate="{Boolean}true" style="vertical"> <successresponse jcr:primaryType="nt:unstructured" jcr:title="Success" sling:resourceType="granite/ui/components/coral/foundation/form/responses/openprompt" open="/assets.html" redirect="/apps/eaem-assets-send-mail/send-mail-dialog.html" text="Email sent"/> <items jcr:primaryType="nt:unstructured"> <wizard jcr:primaryType="nt:unstructured" jcr:title="Compose" sling:resourceType="granite/ui/components/coral/foundation/wizard"> <items jcr:primaryType="nt:unstructured"> <container granite:class="eaem-send-mail-form" jcr:primaryType="nt:unstructured" jcr:title="Compose" sling:resourceType="granite/ui/components/coral/foundation/container"> <items jcr:primaryType="nt:unstructured"> <actionbar jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/container"> <items jcr:primaryType="nt:unstructured"> <to jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/textfield" fieldDescription="Comma separated 'To' list..." fieldLabel="To" name="./to"/> <subject jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/textfield" fieldDescription="Enter Subject..." fieldLabel="Subject" name="./subject"/> <body jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/textarea" cols="15" fieldDescription="Email body content" fieldLabel="Content" name="./body" rows="22"/> </items> </actionbar> </items> <parentConfig jcr:primaryType="nt:unstructured"> <prev granite:class="foundation-wizard-control" jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/anchorbutton" href="/aem/start.html" text="Cancel"> <granite:data jcr:primaryType="nt:unstructured" foundation-wizard-control-action="cancel"/> </prev> <next granite:class="foundation-wizard-control" jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/button" text="Send" type="submit" variant="primary"> <granite:data jcr:primaryType="nt:unstructured" foundation-wizard-control-action="next"/> </next> </parentConfig> </container> </items> </wizard> </items> </content> </items> </body> </jcr:content> </jcr:root>
6) Create file (nt:file) /apps/eaem-assets-send-mail/send-mail-dialog/js.txt, add
dialog-send-mail.css
7) Create file (nt:file) /apps/eaem-assets-send-mail/send-mail-dialog/dialog-send-mail.css, add the following code...
.eaem-send-mail-form{ margin: 10px 40px 0px 40px; height: 87%; } .eaem-send-mail-col1{ width: 30%; margin-top: 10px; overflow-y: auto; } .eaem-send-mail-col2{ height: 65%; padding: 1rem; overflow: hidden; width: 65%; margin-top: 30px; margin-left: 50px; text-align: center; background: white; } .eaem-send-mail-apply{ margin-top: 20px; margin-left: 88%; } .eaem-send-mail-apply-modal { width: 50%; margin-left: -50%; height: 63%; margin-top: -50%; box-sizing: content-box; z-index: 10100; } .eaem-send-mail-apply-modal > iframe { width: 100%; height: 100%; border: 1px solid #888; }
8) Create file (nt:file) /apps/eaem-assets-send-mail/send-mail-dialog/js.txt, add
dialog-send-mail.js
9) Create file (nt:file) /apps/eaem-assets-send-mail/send-mail-dialog/dialog-send-mail.js, add the following code...
(function ($, $document) { var BUTTON_URL = "/apps/eaem-assets-send-mail/content/send-mail-but.html", SHARE_ACTIVATOR = "cq-damadmin-admin-actions-adhocassetshare-activator", SEND_MAIL_SERVLET = "/bin/experience-aem/send-mail", SEND_MAIL_URL = "/apps/eaem-assets-send-mail/send-mail-dialog.html", CANCEL_CSS = "[data-foundation-wizard-control-action='cancel']", SENDER = "experience-aem", REQUESTER = "requester", $mailModal, url = document.location.pathname; if( url.indexOf("/assets.html") == 0 ){ $document.on("foundation-selections-change", addSendMail); }else if(url.indexOf(SEND_MAIL_URL) == 0){ handleSendMailDialog(); } function handleSendMailDialog(){ $document.on("foundation-contentloaded", fillDefaultValues); $document.on("click", CANCEL_CSS, sendCancelMessage); $document.submit(sendMailSentMessage); } function sendMailSentMessage(){ var message = { sender: SENDER, action: "send" }; getParent().postMessage(JSON.stringify(message), "*"); } function sendCancelMessage(){ var message = { sender: SENDER, action: "cancel" }; getParent().postMessage(JSON.stringify(message), "*"); } function getParent() { if (window.opener) { return window.opener; } return parent; } function closeModal(event){ event = event.originalEvent || {}; if (_.isEmpty(event.data) || _.isEmpty($mailModal)) { return; } var message, action; try{ message = JSON.parse(event.data); }catch(err){ return; } if (!message || message.sender !== SENDER) { return; } var modal = $mailModal.data('modal'); modal.hide(); modal.$element.remove(); if(message.action == "send"){ showAlert("Email sent...", $mailModal.mailSentMessage); } } function showAlert(message, title, callback){ var fui = $(window).adaptTo("foundation-ui"), options = [{ id: "ok", text: "OK", primary: true }]; message = message || "Unknown Error"; title = title || "Error"; fui.prompt(title, message, "default", options, callback); } function fillDefaultValues(){ var queryParams = queryParameters(), form = $("form")[0]; setWidgetValue(form, "[name='./subject']", queryParams.subject); setWidgetValue(form, "[name='./body']", queryParams.body); } function setWidgetValue(form, selector, value){ Coral.commons.ready(form.querySelector(selector), function (field) { field.value = _.isEmpty(value) ? "" : decodeURIComponent(value); }); } function queryParameters() { var result = {}, param, params = document.location.search.split(/\?|\&/); params.forEach( function(it) { if (_.isEmpty(it)) { return; } param = it.split("="); result[param[0]] = param[1]; }); return result; } function addSendMail(){ $.ajax(BUTTON_URL).done(addButton); } function addButton(html) { var $eActivator = $("." + SHARE_ACTIVATOR); if ($eActivator.length == 0) { return; } var $mail = $(html).css("margin-left", "20px").insertBefore($eActivator); $mail.click(openModal); $(window).off('message', closeModal).on('message', closeModal); } function openModal(){ var actionConfig = ($(this)).data("foundationCollectionAction"); var $items = $(".foundation-selections-item"), assetPaths = []; $items.each(function () { assetPaths.push($(this).data("foundationCollectionItemId")); }); var body = "Please review the following assets... \n\n" + assetPaths.join("\n"); showMailModal(getModalIFrameUrl("Experience AEM: Review Assets...", body), actionConfig.data.text); } function showMailModal(url, mailSentMessage){ var $iframe = $('<iframe>'), $modal = $('<div>').addClass('eaem-send-mail-apply-modal coral-Modal'); $iframe.attr('src', url).appendTo($modal); $modal.appendTo('body').modal({ type: 'default', buttons: [], visible: true }); $mailModal = $modal; $mailModal.mailSentMessage = mailSentMessage; } function getModalIFrameUrl(subject, body){ var url = Granite.HTTP.externalize(SEND_MAIL_URL) + "?" + REQUESTER + "=" + SENDER; url = url + "&subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body); return url; } }(jQuery, jQuery(document)));
No comments:
Post a Comment