October 23, 2014

Injecting a Tracking Pixel into Sitecore's Email Campaign Manager

October 23, 2014

You can't escape the tracker. Source: https://flic.kr/p/7Eru61
Recently, I had a client request that I provide the ability to insert a tracking pixel into their Email Campaign Manger (ECM) emails.  The purpose of the tracking pixel is to allow a third party to collect information from the recipients and collate that data with data from other clients in order to get a better view of recipients across clients and brands anonymously.

Requirements

The requirements were pretty minimal and are as follows:

  • The tracking pixel includes several parameters that are built into the SRC of the image tag that is created.  One of the parameters is a hashed string of the recipient's email address for anonymous tracking purposes.  The implementation must provide the ability for the content author to change these parameters at design time via the Page Editor.
I additionally added a requirement of my own:
  • Avoid having to create a separate layout that would only be used when wanting to include the tracking pixel in an email.  The content authors should be allowed to create emails as they would ordinarily and if a requirement arises for the email to include a tracking pixel, they should be able to add one with ease.

Implementation Options

While researching options for this project, I came up with two possible approaches:

  1. Create a rendering that would handle the insertion of the tracking pixel into the body of the email.
  2. Hook into the subscriber:assigned event of ECM in order to inject content into the body of the message.

Implementation 

I decided to create a rendering that could be included in an email at design time.  This allows the content authors the ability to decide when and if a tracking pixel will be included in an outgoing email.  Had I tried to hook into the subscriber:assigned event, I would be running logic for each outgoing message that would determine whether to insert a tracking tag or not.  This logic would run even for those messages that did not contain a tracking pixel and would only slow down the dispatch process.

My solution consists of a number of Sitecore items and files that interact to get the tracking pixel populated and inserted into the email.  Basically, the implementation can be broken down into these steps:

  1. Update the placeholder settings for a placeholder in our ECM layout so that we can insert the tracking pixel into.
  2. Write a rendering that outputs the tracking pixel to the email.
  3. Create a patch include file to store configuration for the rendering and that creates a new pipeline processor.
  4. Write a pipeline processor to populate the tracking pixel with custom data based on the current recipient.

Update the Placeholder Settings to Allow the "TrackingPixel" Rendering

I had previously written a Responsive Email Template for my client that is now being used for every outgoing email.  I updated the existing "msg-body" placeholder setting to allow for the "Tracking Pixel" rendering.

Placeholder Settings

Write a Rendering that Outputs the Tracking Pixel to the Email

The rendering is where the HTML for the tracking pixel is created.  The pipeline processor later fills in individual values per email, but the static portion of the pixel is inserted here.
public class TrackingPixel : ClientBaseControl
    {
        #region Rendering Parameters

        public string CampaignId { get; set; }
        public string SiteId { get; set; }
        public string PlacementId { get; set; }
        public string ADGroupId { get; set; }
        public string CreativeId { get; set; }

        #endregion

        protected override void CreateChildControls()
        {
            // Ensure the tracking token and src are defined
            Assert.IsNotNullOrEmpty(Sitecore.Configuration.Settings.GetSetting("TrackingPixelSource"), "Tracking pixel source not defined in config file");
            Assert.IsNotNullOrEmpty(Sitecore.Configuration.Settings.GetSetting("TrackingPixelToken"), "Tracking pixel token not defined in config file");

            // Add a visual representation of the control to allow the content author to edit the tracking pixel parameters
            if (Sitecore.Context.PageMode.IsPageEditor || Sitecore.Context.PageMode.IsPageEditorEditing)
            {
                HtmlGenericControl div = new HtmlGenericControl("div");
                div.Attributes.Add("style", "background-color:red; width:150px;display: inline-block;color:white");
                div.InnerText = "TRACKING PIXEL";
                this.Controls.Add(div);
            }

            // Create the tracking pixel image
            HtmlImage image = new HtmlImage();
            image.Attributes.Add("height", "1");
            image.Attributes.Add("width", "1");
            image.Attributes.Add("border", "0");

            // Get the values of the token and SRC of the tracking pixel from the config file
            string src = Sitecore.Configuration.Settings.GetSetting("TrackingPixelSource");
            string anToken = Sitecore.Configuration.Settings.GetSetting("TrackingPixelToken"); 
            image.Attributes.Add("src", String.Format(src, CampaignId, SiteId, PlacementId, ADGroupId, CreativeId, anToken));

            // Add the tracking pixel to the page
            this.Controls.Add(image);
        }
    }

To (briefly) walk you through the code above, we create some public variables to hold rendering parmeters that are to be passed in, we create the rendering output, we ensure that the configuration file is present, we check for page editor mode, we create the HtmlImage element that is the pixel tracker, we format the pixel tracker and we add the pixel tracker to the page.

Note: Because the tracking pixel is essentially a hidden element, we want to ensure that we can edit this in the page editor.  I check for the page editor mode and upon finding it, display a slightly obnoxious red div element that can be selected by a content author for editing purposes.  This especially comes in handy when you need to select the rendering to change the rendering parameters.  When the email is sent, this div is not displayed.

The Page Editor-only mode of the tracking pixel.

Create a Patch Include File to Store Configuration for the Rendering and that Creates a New Pipeline Processor

To maximize code reuse and to allow for a potential future implementations of different vendors of tracking pixels, I store the SRC of the tracking pixel in a patch include file.  I additionally store the placeholder token string that will be replaced by the hashed string value of the recipient email address.

<setting name="VendorName_TrackingPixelSource" value="http://vendorname.com/pixel/3964/?%26col={0},{1},{2},{3},{4}%26id={5}" />

<setting name="VendorName_TrackingPixelToken" value="**HASHED_EMAIL**" />

The configuration file also creates a pipleline processor that is used to populate the tracking pixel rendering.  This processor runs as the first step of ECM's SendEmail pipeline as follows:
    <pipelines>
      <SendEmail>
        <processor type="Client.Division.Common.PipelineProcessors.PopulateTrackingPixel, Client.Division.Common" x:before="*[1]" />
      </SendEmail>
    </pipelines>

Write a Pipeline Processor to Populate the Tracking Pixel with Custom Data Based on the Current Recipient

The pipeline processor is injected into the "SendEmail" ECM pipeline.  It accepts the "SendMessageArgs" arguments and as part of those arguments, it contains the "MailMessageItem" object.  This object is where we can get access to the 'body' and 'to' fields of the outgoing email.

class PopulateTrackingPixel
    {
        public void Process(SendMessageArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            MailMessageItem message = args.EcmMessage as MailMessageItem;
            
            if (message != null)
            {
                if (!String.IsNullOrEmpty(message.Body))
                {
                    // Ensure the tracking token and src are defined
                    Assert.IsNotNullOrEmpty(Sitecore.Configuration.Settings.GetSetting("TrackingPixelToken"), "Tracking pixel token not defined in config file");

                    // Find the placeholder and replace with the tracking pixel
                    string anToken = Sitecore.Configuration.Settings.GetSetting("TrackingPixelToken"); 

                    args.EcmMessage.Body = message.Body.Replace(anToken, EmailUtil.HashEmailAddress(message.To));
                }
            }
        }
    }

We want the 'body' so that we can take the content, which now contains our tracking pixel rendering, and replace the "**HASHED_EMAIL**" string with an actual hashed email string based on the actual recipient.  We take the "to" and pass that into a custom "HashEmailAddress" method, which returns the hashed email address.

Other Relevant Artifacts

Rendering Parameters

The tracking pixel requires some additional parameters to be passed back to the third party tracking company that further identifies things like "campaignID", "siteID", "placementID", etc.  These are set when the content author places the rendering on the page so that they can be unique to each email campaign.

Email Utility Class

The email utility class contains methods common to email-related tasks.  In this case, I created a couple of methods to allow the hashing of email addresses.

How it's Used

Adding a tracking pixel to an ECM email is now easy with the tracking pixel artifacts in place.  A content author creates an email, uses the "add a new component" icon in the ECM ribbon, adds the tracking pixel rendering to the proper placeholder, supplies parameters via the rendering parameters popup and boom, tracking pixels on all outgoing emails.

Whether it's a tracking pixel or some other form of customized code that you need injected into your emails, I hope this helps!

0 comments:

Post a Comment