codeflood logo

Filtering ECM Dispatcher

The Email Campaign Manager (ECM) for Sitecore is a great tool for sending bulk email and newsletters to your users and subscribers. I had an interesting question from a client recently about the Email Campaign Manager.

“How can I send an email to a subset of the subscribers?”

I’ll give you a scenario to describe the request. Let’s say I have a user base of national partners, but I only want to send a newsletter to a subset of them such as only those in Victoria.

When an ECM mail is sent it is sent to an entire target audience which includes all users within a role. There are no options for limiting the subscribers in the target audience to which the mail is sent, other than a random selection to test the mail.

Using out-of-the-box tools the only way to meet my scenario above would be:

  1. Create a new target audience for my Victorian partners
  2. Export all my users to a CSV file using the ECM export users tool
  3. Filter the users using Excel
  4. Upload the altered file back into ECM using the user import tool, adding the users to the new target audience
  5. Send the mail to the new target audience.

If this was just a temporary one-time email I would then need to delete the target audience and associated roles. This sounds like a lot of work to filter a subset of the users.

OK, so what if instead of originally uploading a single file of all my partners and adding them to a single partners target audience (and hence role) I could upload my partners by state, then create a new role which contains all the state partner roles…still sounds kind of clunky…especially if I want to segment my users more dynamically.

So I got to digging into ECM to work out how I could filter the users based on their custom properties. I needed to be able to assess each user as the mail was generated then abort the send if the user’s properties didn’t match the criteria for the newsletter. Luckily for me ECM uses a pipeline to process each individual email as it’s sent, so it was just a matter of tapping into the SendEmail pipeline to do the filtering.

OK, let’s put it all together.

First thing is it extend the user profile to accommodate any custom properties I want to filter on. Although the user object can accept any custom properties assigned through code, to have Sitecore update the UI to allow viewing and updating those properties we need to update the profile.

In the Sitecore desktop use the database changer to change to the core database, then open the content editor and navigate down to the template for the user profile being used for your subscribers. Sitecore uses the items under the /sitecore/system/Settings/Security/Profiles path in the core database for the custom properties shown for a user profile. By default ECM will use the Subscriber item in this folder, so navigate to the template this item is based on. To this template we’ll add an additional field called tags which we’ll use to store any kind of segmenting data for the user such as state or campaign participation.

image

Next we’ll need to ensure the users in Sitecore have the required data in the tags custom property. Now that the profile has been updated the user tools such as the user editor and the ECM import tool will show the field. Luckily the ECM user import tool will match users in the file with existing users on the user’s email, so this tool can be used to update the existing users in the system.

Once the users have been updated we need to update the newsletter template to allow entering tags against it to filter users on. I decided to put these tags on the newsletter template rather than updating the send email dialogs to make sure that if an already dispatched newsletter is duplicated back to the drafts folder to be reused, it doesn’t accidentally get sent to the wrong users.

Jump back into the master database and locate the ECM newsletter template at /sitecore/templates/Email Campaign/Messages/Pre-existing Page. Add to this template a field called User Tags which will hold a comma separated list of tags a user may contain in their tags custom property to have the email sent to them.

image

Now when we create a newsletter we can switch to the content tab of the newsletter to see the fields directly, then enter a list of tags the user may contain in their custom profile.

image

Now, onto the code!

We need to create a pipeline processor for the SendMail pipeline which will do the filtering of the users. The below class is one example of a processor which will do this. I’ve implemented this processor to “OR” the tags on the newsletter so the above newsletter would get sent to all my users that have the tag “victoria” OR “new south wales” OR “queensland” in their list of tags.

using System;
using System.Linq;
using Sitecore.Modules.EmailCampaign;
using Sitecore.Modules.EmailCampaign.Core.Pipelines;

namespace FilteringDispatch
{
  public class FilterSubscribers
  {
    public void Process(SendMessageArgs args)
    {
      var message = args.ECMMessage as MessageItem;
      if (message != null)
      {
        if (!string.IsNullOrEmpty(message.InnerItem["user tags"]))
        {
          var messageTags = message.InnerItem["user tags"].Split(
            new char[]{','},
            StringSplitOptions.RemoveEmptyEntries);

          var userTags = 
            message.PersonalizationContact.Profile.GetCustomProperty("tags").Split(
            new char[]{','},
            StringSplitOptions.RemoveEmptyEntries);

          var sendToUser = false;
          foreach(var messageTag in messageTags)
          {
            if (userTags.Contains(messageTag))
            {
              sendToUser = true;
              break;
            }
          }

          if(!sendToUser)
            args.AbortPipeline();
        }
      }
    }
  }
}

The above code first extracts the MessageItem the newsletter is being sent for which is the newsletter item itself that contains the user tags field. First the newsletter is checked to see if any tags have been defined. If no tags were defined, then exit the processor. Next the tags from the newsletter are extracted, then the tags from the user’s custom property are extracted. The tags from the newsletter are iterated and the user tags are checked to see if any match. At the end of the processor we check to see if the user had an appropriate tag and if not the pipeline is aborted. The effect of aborting the pipeline is that any remaining processors of the pipeline don’t run.

This processor needs to be inserted into the SendMail pipeline as the first processor so if it’s aborted the send processor isn’t processed. This can be done by using the following configuration patch file.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <SendEmail>
        <processor patch:before="processor[1]"
          type="FilteringDispatch.FilterSubscribers,FilteringDispatch" />
      </SendEmail>
    </pipelines>
  </sitecore>
</configuration>

And that’s it!

Now when ECM sends an email the above processor will check each user to ensure they have an appropriate tag before sending them the email.

Comments

Erwin Raets

There seems to be a problem with the import tool in that it does not automatically set the user profile to "subscriber"... There is a workaround by altering the Domains.config file by setting the defaultProfileItemID to the guid used by the subscriber profile (or any other profile you have made for that matter) Domains.config is under the App_Config/Security folder. Add the following line:
If you import now you will see the custom properties you have defined on the profile...
I am also looking into a way to send the correct language to the subscriber based on the subscriber's default language, and how to set that language when importing...

Erwin Raets

OK, the sending can be done as described in the manual, using the subscriber:assigned event. There you can set the targetLanguage for each email basically like this:
public partial class EmailLanguageSelector { public void SetLanguage(object sender, EventArgs args) { MailMessageItem message = Event.ExtractParameter(args, 0) as MailMessageItem;
if (message == null) return;
if (message.PersonalizationContact != null) { var thecontact = message.PersonalizationContact; message.TargetLanguage = Language.Parse(thecontact.Profile.ContentLanguage); } } }
You need to edit the ecm config file to hook into the event:

Erwin Raets

&lt;event name="subscriber:assigned"&gt; &lt;handler type="TienseSuikerSitecore.tiensesuiker.classes.EmailLanguageSelector, TienseSuikerSitecore" method="SetLanguage" /&gt; &lt;/event&gt;
Sorry, forgot about HTML markup :-)
This should do the tricck...

Alistair Deneys

Thanks for the heads up Erwin. It's funny cause the import user UI uses the profile fields defined for the subscriber profile, so you would assume it would set that for the profile of the user. Even though the profile isn't set, the data is still there cause it's stored in custom fields. The profile is a Sitecore concept, used to allow editing of the user's custom properties in the Sitecore user manager.

Alistair Deneys

Thanks for this approach Erwin. That will come in handy when needing to send newsletters in multiple languages.

Instead of using the sendmail pipeline, you should use the DispatchNewsletter instead. Before the DeployAnalytics step, you could insert your filtering step, add or delete recipients from the task.
This ensures that the right number of recipients, is shown in DMS, and dms events is handled.

Alistair Deneys

Thanks for the info Thor, Makes much more sense doing it that way. I'll have to update so the stats are captured properly.

Amit

Can anyone suggest if there is a way to implement the filtering with very less/no coding involved.

Alistair Deneys

Hi Amit, As I described in the first part of the post, you would need to export your users using the export users tool in ECM, use excel to do your filtering and save a new filtered user file, then upload that file using the import users tool in ECM and add the users to a new filtered users target audience. A little clunky (hence my post), but it will work.

[...] took two approaches to this.  First we found the following post on Filtering ECM on Alistair Deneys&#8217; blog.  This seemed to be a decent option, but on further examination we realised that this was running [...]

Leave a comment

All fields are required.