Had what probably seemed quite a straightforward request for an EPiServer site in development that's using EPiServer Forms - an add-on that allows editors to create their own forms to receive input from the site visitors and review the submissions in the back-office. They wanted a confirmation email to go to the recipient.
Of course though, we don't know what fields the editors are going to create. There may not be one for the visitor's email address. nd even if there were, how can we know which field is the one to use?
I came up with a solution to this that requires some simple conventions for the editor to follow but is quite straightforward for them to set up.
The first step was to create a new model for the form, inheriting from the base one provided but adding additional properties for the confirmation email details:
public class FormContainerWithConfirmationEmailBlock : FormContainerBlock, IHasConfirmationEmail { [Display( Name = "Tick to send a confirmation message to the user following sign-up", Order = 40)] public virtual bool SendConfirmationEmail { get; set; } [Display( Name = "Confirmation email subject", GroupName = ContentGroupNames.Content, Order = 45)] [StringLength(200)] public virtual string ConfirmationEmailSubject { get; set; } [Display( Name = "Confirmation email copy", Order = 50)] [PropertySettings(typeof(BasicTinyMceSettings))] public virtual XhtmlString ConfirmationEmailCopy { get; set; } public override void SetDefaultValues(ContentType contentType) { base.SetDefaultValues(contentType); AllowAnonymousSubmission = true; } }
Then to allow for that model to be rendered, I similarly created a controller delegating to the provided base controller:
public class FormContainerWithConfirmationEmailBlockController : FormContainerBlockController { }
And a view delegating to the user control that provides the front-end and back-office rendering functionality.
@model FormContainerWithConfirmationEmailBlock @Html.Partial("~/modules/_protected/EPiServer.Forms/Views/ElementBlocks/FormContainerBlock.ascx", Model)
Secondly I created a new field element type, that behaves just like a text box (I copied the user control TextboxElementBlock.ascx and saved it under a new name), but is a "special" one that the editors will need to use if they want the confirmation email to function. This is added to the form for the collection of the visitor's email address.
public class VisitorsEmailAddressElementBlock : TextboxElementBlock { }
Finally I needed to hook into the form submission event to process the confirmation email. The following code:
- Checks to see if the form submission comes from a form block type that can be configured with a confirmation email
- Checks to see if the confirmation email has been configured
- Checks that the "special" field holding the visitor's email address is present and has been completed
- And if all those are the case, sends the email
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))] public class FormSubmissionInitialization : IInitializableModule { public void Initialize(InitializationEngine context) { var eventRegistry = ServiceLocator.Current.GetInstance<FormsEvents>(); eventRegistry.FormsSubmissionFinalized += OnFormsSubmissionFinalized; } public void Preload(string[] parameters) { } public void Uninitialize(InitializationEngine context) { var eventRegistry = ServiceLocator.Current.GetInstance<FormsEvents>(); eventRegistry.FormsSubmissionFinalized += OnFormsSubmissionFinalized; } private static void OnFormsSubmissionFinalized(object sender, FormsEventArgs e) { HandleConfirmationEmail((FormsSubmittedEventArgs)e); } private static void HandleConfirmationEmail(FormsSubmittedEventArgs eventArgs) { // Even though we get back the full form block details later, do a quick cast based check here // to see if we are working with a form that could have confirmation emails configured var formContent = eventArgs.FormsContent as FormContainerWithConfirmationEmailBlock; if (IsConfirmationEmailConfigured(formContent)) { int fieldIdForVisitorsEmailAddress; if (TryGetFieldIdForVisitorsEmailAddress(formContent, out fieldIdForVisitorsEmailAddress)) { string recipientEmail; if (TryExtractVisitorsEmailAddress(eventArgs, fieldIdForVisitorsEmailAddress, out recipientEmail)) { // Send the email (the component called below is a simple wrapper for populating an email sending via SMTP) var confirmationEmailSender = ServiceLocator.Current.GetInstance<IConfirmationEmailSender>(); confirmationEmailSender.SendConfirmationEmail("editable-form-confirmation.txt", formContent.ConfirmationEmailSubject, formContent.ConfirmationEmailCopy, recipientEmail); } } } } private static bool IsConfirmationEmailConfigured(FormContainerWithConfirmationEmailBlock formContent) { return formContent != null && formContent.SendConfirmationEmail; } private static bool TryGetFieldIdForVisitorsEmailAddress(FormContainerWithConfirmationEmailBlock formContent, out int fieldId) { fieldId = 0; var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>(); foreach (var element in formContent.ElementsArea.Items) { VisitorsEmailAddressElementBlock visitorsEmailAddressElementBlock; if (contentLoader.TryGet<VisitorsEmailAddressElementBlock>(element.ContentLink, out visitorsEmailAddressElementBlock)) { fieldId = element.ContentLink.ID; return true; } } return false; } private static bool TryExtractVisitorsEmailAddress(FormsSubmittedEventArgs eventArgs, int fieldIdForVisitorsEmailAddress, out string recipientEmailAddress) { var formData = eventArgs.SubmissionData.Data; recipientEmailAddress = formData["__field_" + fieldIdForVisitorsEmailAddress]?.ToString(); return recipientEmailAddress.IsValidEmailAddress(); } }
Trying to implement this, but you don't define the IHasConfirmationEmail interface.
ReplyDeleteWe're going back 4 years here so not sure I can recall all the details of this - but I have managed to find the code this came from and the interface is defined like this:
Deletepublic interface IHasConfirmationEmail
{
bool SendConfirmationEmail { get; }
string ConfirmationEmailSubject { get; }
XhtmlString ConfirmationEmailCopy { get; }
}