Sitecore 10 Form Email Sending with SMTP

 


Quick and simple implementation for an SMTP email sending for Sitecore Forms in Sitecore 10.

First, you need to create the custom save action, SendEmail.cs:

namespace Sitecore.Foundation.SitecoreForms.CustomSaveActions
{
using System;
using System.Collections.Generic;
using System.Net.Mail;
using Sitecore.Data;
using Sitecore.Diagnostics;
using Sitecore.ExperienceForms.Models;
using Sitecore.ExperienceForms.Mvc.Models.Fields;
using Sitecore.ExperienceForms.Processing;
using Sitecore.ExperienceForms.Processing.Actions;
using Sitecore.Foundation.SitecoreForms.Models;
/// <summary>
/// Sitecore forms custom save action for email sending
/// </summary>
public class SendEmail : SubmitActionBase<SendEmailActionData>
{
public SendEmail(ISubmitActionData submitActionData) : base(submitActionData)
{ }
/// <summary>
/// Send email custom save action functionalities
/// </summary>
/// <param name="data"></param>
/// <param name="formSubmitContext"></param>
/// <returns></returns>
protected override bool Execute(SendEmailActionData data, FormSubmitContext formSubmitContext)
{
try
{
var emailTemplate = Sitecore.Context.Database.GetItem(new ID(data.ReferenceId));
//Replace keywords in 'Subject' from form fields
var emailSubject = ReplaceKeywords(emailTemplate[Templates.FormEmail.Subject], formSubmitContext);
//Replace 'From' email address from form fields
var fromEmailAddress = ReplaceKeywords(emailTemplate[Templates.FormEmail.From], formSubmitContext);
//Replace keywords in 'FromDisplayName' from form fields
var fromDisplayName = ReplaceKeywords(emailTemplate[Templates.FormEmail.FromDisplayName], formSubmitContext);
//Replace 'TO' email addresses from form fields
var toEmailAddresses = ReplaceKeywords(emailTemplate[Templates.FormEmail.To], formSubmitContext);
//Replace 'CC' email addresses from form fields
var ccEmailAddresses = ReplaceKeywords(emailTemplate[Templates.FormEmail.CC], formSubmitContext);
//Replace 'BCC' email addresses from form fields
var bccEmailAddresses = ReplaceKeywords(emailTemplate[Templates.FormEmail.BCC], formSubmitContext);
//Replace email message body from form fields
var message = ReplaceKeywords(emailTemplate[Templates.FormEmail.Body], formSubmitContext);
this.Send(fromEmailAddress, fromDisplayName, toEmailAddresses, ccEmailAddresses, bccEmailAddresses, emailSubject, message, true);
Log.Debug(string.Format("Email sent with following details for form id: {0}: Subject- {1} | FromAddress – {2} | FromDisplyName – {3} | ToAddress – {4} | CCAddress – {5} | BCCAddress – {6}",
formSubmitContext.FormId.ToString(), emailSubject, fromEmailAddress, fromDisplayName, toEmailAddresses, ccEmailAddresses, bccEmailAddresses), this);
return true;
}
catch (Exception ex)
{
Log.Error(string.Format("Exception occured executing send email custom save action for form id: {0}.", formSubmitContext.FormId.ToString()), ex, this);
return false;
}
}
/// <summary>
/// Replace keywords from the form input data
/// </summary>
/// <param name="original"></param>
/// <param name="formSubmitContext"></param>
/// <returns></returns>
protected string ReplaceKeywords(string original, FormSubmitContext formSubmitContext)
{
var returnString = original;
foreach (var viewModel in formSubmitContext.Fields)
{
if (returnString.Contains("{" + viewModel.Name + "}"))
{
var type = viewModel.GetType();
string valueToReplace = string.Empty;
// InputViewModel<string> types
if (type.IsSubclassOf(typeof(InputViewModel<string>)))
{
var field = (InputViewModel<string>)viewModel;
valueToReplace = field.Value ?? string.Empty; ;
}
// InputViewModel<List<string>> types
else if (type.IsSubclassOf(typeof(InputViewModel<List<string>>)))
{
var field = (InputViewModel<List<string>>)viewModel;
valueToReplace = (field.Value != null) ? string.Join(", ", field.Value) : string.Empty;
}
// InputViewModel<bool> types
else if (type.IsSubclassOf(typeof(InputViewModel<bool>)))
{
var field = (InputViewModel<bool>)viewModel;
valueToReplace = field.Value.ToString();
}
// InputViewModel<DateTime?> types
else if (type.IsSubclassOf(typeof(InputViewModel<DateTime?>)))
{
var field = (InputViewModel<DateTime?>)viewModel;
valueToReplace = field.Value?.ToString() ?? string.Empty;
}
// InputViewModel<DateTime> types
else if (type.IsSubclassOf(typeof(InputViewModel<DateTime>)))
{
var field = (InputViewModel<DateTime>)viewModel;
valueToReplace = field.Value.ToString();
}
// InputViewModel<double?> types
else if (type.IsSubclassOf(typeof(InputViewModel<double?>)))
{
var field = (InputViewModel<double?>)viewModel;
valueToReplace = field.Value?.ToString() ?? string.Empty;
}
returnString = returnString.Replace("{" + viewModel.Name + "}", valueToReplace);
}
}
return returnString;
}
/// <summary>
/// Send the email based to parameters
/// </summary>
/// <param name="fromAddress"></param>
/// <param name="fromName"></param>
/// <param name="toAddresses"></param>
/// <param name="ccAddresses"></param>
/// <param name="bccAddresses"></param>
/// <param name="subject"></param>
/// <param name="message"></param>
/// <param name="isHtml"></param>
private void Send(string fromAddress, string fromName, string toAddresses, string ccAddresses, string bccAddresses, string subject, string message, bool isHtml)
{
var mailMessage = new MailMessage();
mailMessage.From = new MailAddress(fromAddress, fromName);
var toAddressList = toAddresses.Split(',');
foreach (var addressItem in toAddressList)
{
if (!string.IsNullOrEmpty(addressItem))
{
mailMessage.To.Add(new MailAddress(addressItem));
}
}
var ccAddressList = ccAddresses.Split(',');
foreach (var addressItem in ccAddressList)
{
if (!string.IsNullOrEmpty(addressItem))
{
mailMessage.CC.Add(new MailAddress(addressItem));
}
}
var bccAddressList = bccAddresses.Split(',');
foreach (var addressItem in bccAddressList)
{
if (!string.IsNullOrEmpty(addressItem))
{
mailMessage.Bcc.Add(new MailAddress(addressItem));
}
}
mailMessage.Subject = subject;
mailMessage.IsBodyHtml = isHtml;
mailMessage.Body = message;
MainUtil.SendMail(mailMessage);
}
}
}

Quick explanation:
SendEmail is the custom action that builds the email,

ReplaceKeywords, replace the entries in email,

Send will use the SMTP to send the emails.

We need the model which has the reference for the form, the SendEmailActionData.cs:

namespace Sitecore.Foundation.SitecoreForms.Models
{
using System;
/// <summary>
/// Model class which contains the email template Id selected in the custom save action
/// </summary>
public class SendEmailActionData
{
public Guid ReferenceId { get; set; }
}
}

We need the Templates.cs which contains the fields for our custom email template:

namespace Sitecore.Foundation.SitecoreForms
{
using Sitecore.Data;
public static class Templates
{
public static class FormEmail
{
public static readonly ID Subject = new ID("{5E4A8BC4-8476-400C-8B48-4AB032788070}");
public static readonly ID From = new ID("{6EC3EEFC-2623-4D39-8F36-53C483E30C2F}");
public static readonly ID FromDisplayName = new ID("{9CE897F8-3F2C-4DB5-A2F0-D0E28E4F4B37}");
public static readonly ID To = new ID("{F78378F0-2562-4446-93D9-746E4644890D}");
public static readonly ID CC = new ID("{1966440E-930D-4504-8DA1-EFB8509C7246}");
public static readonly ID BCC = new ID("{02D56134-442D-40F8-BB30-7C17FB2F1721}");
public static readonly ID Body = new ID("{33FA2379-0CF8-45CF-A5F5-1DC94534A5D6}");
}
}
}

We need the SendEmail.js js file, for the custom send email pop up in sitecore forms wizard, and needs to be copied under sitecore\shell\client\Applications\FormsBuilder\Layouts\Actions folder:

(function (speak) {
var parentApp = window.parent.Sitecore.Speak.app.findApplication('EditActionSubAppRenderer');
speak.pageCode([], function () {
return {
initialized: function () {
this.on({
"loaded": this.loadDone
}, this);
this.ItemTreeView.on("change:SelectedItem", this.changedSelectedItemId, this);
if (parentApp) {
parentApp.loadDone(this, this.HeaderTitle.Text, this.HeaderSubtitle.Text);
}
},
changedSelectedItemId: function () {
var isSelectable = !!this.ItemTreeView.SelectedItem;
parentApp.setSelectability(this, isSelectable, this.ItemTreeView.SelectedItemId);
},
loadDone: function (parameters) {
this.Parameters = parameters || {};
this.ItemTreeView.SelectedItemId = this.Parameters.referenceId;
},
getData: function () {
this.Parameters.referenceId = this.ItemTreeView.SelectedItemId;
return this.Parameters;
},
getDescription: function () {
return this.ItemTreeView.SelectedItem.$displayName;
},
};
});
})(Sitecore.Speak);

And finally the configuration file for SMTP:

<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
<sitecore>
<settings>
<setting name="MailServer">
<patch:attribute name="value">127.0.0.1</patch:attribute>
</setting>
<!–<setting name="MailServerUserName">
<patch:attribute name="value"></patch:attribute>
</setting>–>
<!–<setting name="MailServerPassword">
<patch:attribute name="value"></patch:attribute>
</setting>–>
<!–<setting name="MailServerPort">
<patch:attribute name="value"></patch:attribute>
</setting>–>
<!–<setting name="MailServerUseSsl">
<patch:attribute name="value">false</patch:attribute>
</setting>–>
</settings>
</sitecore>
</configuration>

For testing purposes, for the SMTP server on the local machine, I’ve used the fake SMTP which you can download from here. It’s really straightforward to use. 

From default, it’s running on localhost (127.0.0.1), and the default port is 25(it’s editable). You can change also the email messages folder marked in the following screenshot:

Clicking to the Start server button starts the SMTP server on your local machine.

From the Sitecore side, for items, I’ve created a Sitecore package for fast download/install, and the complete project you can find in my repo.

To use this email, at submit button you need to click Send Email Action as screenshot below:

At apply it will appear a pop up with email templates:


If in Sitecore Forms Editor when you select Send Email, and at pop up the Email Template Tree it’s empty, 

It means that the source it’s not right, and for that, you need to adjust that from /Sitecore/client/Applications/FormsBuilder/Components/Layouts/Actions/Send Email/PageSettings/ItemTreeView item adding the ID of the parent of your Email templates folder to StaticData – Content retrieved from a specific location in the content tree of the specified content database [shared] field:



I hope it helps, and it was quick to finish this task!

Have a good coding, check out my other posts and see you at the next one! ðŸ˜€


Comments