Friday, 24 June 2016

Creating a Confirmation Email With EPiServer Forms

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();
        }
    }

Friday, 17 June 2016

Personalisation Packages in Umbraco

Had the pleasure of giving a talk at the Umbraco Codegarden 2016 festival this week, along with Theo Paraskevopoulos of Growcreate. As mentioned at the time have collected here links to the slides and various resources mentioned during the session.

Firstly the presentation itself can be viewed here.

The Personalisation Groups package can be downloaded from our.umbraco.org here and the source code is up on Github.

And you can read more about and download Pipeline CRM here.

Monday, 6 June 2016

Working with the Microsoft Bot Framework and LUIS

Again cross-posting an article I've written for my company blog on working with the Microsoft Bot Framework and LUIS.