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.

Wednesday, 30 March 2016

Slack Slash Commands and ASP.Net Web Hooks

Cross-posting a link to a piece written for my company's blog on Slack Slash Commands and ASP.Net Web Hooks.

Monday, 15 February 2016

Getting Started with MS Dynamics API

This is mostly a "note to self" post following some research and prototyping into working with the Microsoft Dynamics API from ASP.Net but might also be useful for anyone else that stumbles across it trying to get things working on this front.

Accessing a Dynamics Instance

First step for me was to get access to an instance of MS Dynamics to work with by signing up for a free trial. This gives access to a fully featured set up of Dynamics that you can work with for 30 days.

You'll need to sign up with a user name and password and get assigned a sub-domain that will be of the form *.onmicrosoft.com. All of these will be needed for accessing via the API.

Working with the API

Starting with an empty ASP.Net MVC application, the first step is to install a NuGet package:

PM> Install-Package Microsoft.CrmSdk.Extensions

And then add references to the following assemblies:

  • System.IdentityModel.dll
  • System.Data.Services.dll
  • System.Data.Services.Client.dll
  • System.Runtime.Caching.dll
  • System.Runtime.Serialization.dll

Make sure you have .Net 4.5.2 installed and the project set up to target this version of the framework.

With that in place, the following code that creates a new Account record in Dynamics should run successfully:

 public ActionResult Index()
 {
  var crmConnection = CrmConnection.Parse("Url=https://mydomain.crm4.dynamics.com; Username=username; Password=password;");
  var service = new OrganizationService(crmConnection);

  var account = new Entity("account");
  account["name"] = "Test Account";

  var accountId = service.Create(account);

  var result = "Account created with Id: " + accountId;
  return Content(result);
 }

Using Strong Typing

Whilst the above is fine for simple integrations to work with Dynamics data in a more maintainable way it's likely we'd want to make use of strong typing.

To get this working I downloaded the CRM SDK (file: MicrosoftDynamicsCRM2016SDK.exe)

And then from a command prompt opened at C:\Program Files\MS Dynamics SDK\SDK\Bin:

CrmSvcUtil.exe /out:c:\temp\Xrm.cs /url:https://mydomain.crm4.dynamics.com/XRMServices/2011/Organization.svc /username:username /password:password /namespace:Xrm /serviceContextName:XrmServiceContext

This generated a file called Xrm.cs which can be copied into the project. And then code such as the following can be run:

 public ActionResult Index()
 {
  var crmConnection = CrmConnection.Parse("Url=https://mydomain.crm4.dynamics.com; Username=username; Password=password;");
  var service = new OrganizationService(crmConnection);

  var account = new Account
  {
   Name = "Test Account 2",
  };
  account.Id = service.Create(account);

  var result = "Account created with Id: " + account.Id;
  return Content(result);
 }

References

Thursday, 31 December 2015

Working with View Models in Umbraco and EPiServer

Cross-posting an article I've written for my company Zone's Medium blog. I've written about techniques for using strongly typed view models with Umbraco here before - in this article I recap some of that and extend the discussion to EPiServer CMS too.

You can read and comment on the article here.

Friday, 9 October 2015

ASP.Net 5 and C# 6: Slides and Resources

ASP.Net 5 and C# 6: Slides and Resources

Had the pleasure of speaking yesterday at the London Umbraco meet-up hosted at Zone on the topic of ASP.Net 5 and C# 6. For anyone who was there and wants a reference, or who wasn't and is interested in an introduction to this area, I'll post a few resources.

Firstly the slides from the talk are available here.

And then here's a number of links to blogs and other online resources that I've used in researching the topic and preparing the talk:

You can even watch it if you like.

Thanks to Ali, Ravi for organisation last night and all the attendees.