Introducing the Send Batch Upload and Merge Module

Being on my new team is a breath of fresh air, and with a bunch of new challenges in the composable space I've got a lot to write about. My very first task was to remove manual work when combining email lists in Send. I accomplished this by building the Send Batch Upload and Merge module, and I'll take you through how and why it was created.

 

Why Do I Need This Module?

We have a few email lists with somewhere in the area of 500,000 members in total. There's an effort to combine some of these lists which Send will let you do by exporting from one then importing to the other, but the Tags column will get overwritten if the member already exists, instead of just appending them. What this means is if we want to preserve tags from both lists for existing recipients, it must be done manually.

To combine lists and keep all tags, our must go into a list and choose Export to CSV for both the source and destination list.


 

In Excel, function must be built that will add the tags from the destination export to the recipient in the source export, so they're not lost during import. One this source list is ready they go into "Perform bulk actions" and then "Import Members into List".


  

The full guide is on the Sitecore Send documentation site, but again the important thing to note is existing recipients will lose their tags. Not on my watch! 

Let's go through the construction of this module.


Adding a Start Menu Button

Calling PowerShell from the start menu is simple and adds a bit of ease for the User. To do so switch to the Core database, and in Content Editor navigate to /sitecore/content/Documents and settings/All users/Start menu/Right. From here, I copied a Divider item and named it Divider 4. Under that, I created a “Start menu folder” item called Integrations (since I expect more buttons in the future). For now, there's only one child item, which will be called “Send - Batch Update” based on the “Start menu action” template.

The only important field is the Message, but this is what I used for all four:

Display name: Send - Batch Update  
Icon: /~/icon/office/32x32/cloud_upload.png
Message: item:executescript(id=$Target,script={4C4F4EED-7701-4DDA-9548-B4ECD78DE93C}, scriptDb=master)
Tool tip: Update a Send email list from your CSV file

The message field is being instructed to execute the script found in the item based on the provided Guid in the master database. This means the item must be serialized in the module for this to work. Other methods can find an item based on type, or path, from config and others, but this is easiest for me. If it's not clear by now, you also need Sitecore PowerShell installed for this module to work.

The items should appear in Core like this:


And here's how it will look:




Getting Data and Executing the Task

Ok, so now we have the User running a script in an easy way. Let's look into what it needs, piece by piece. 

I created a folder at /sitecore/system/Modules/PowerShell/Script Library/Integrations for this and any other future work. Under it, I created a “PowerShell Script Library” folder followed by a “PowerShell Script” item called “Batch Email List Update”.

 


The script is going gather information and call some custom code, which will be covered in the next article. For now let's concentrate on the script. Now, I'm no expert in PowerShell by far, but this works, so no hate mail!

First, I'm setting variables such as the assembly for supporting code, an upload path for the CSV.

$assemblyPath = $AppPath + "bin\SitecoreFundamentals.SendBatchUploadAndMerge.dll"
Add-Type -LiteralPath $assemblyPath
$itemGuid = New-Guid
$destinationPath = $AppPath + "\App_Data\Integrations\Send\BatchUploads\" + $itemGuid

This first step in the User's experience is asking for the CSV file. 

$uploadedFile = Receive-File -Path $destinationPath -Overwrite



Next I ask if the User wants the upload to run in report or execute mode. I added this option so it will report on what will happen without applying any changes. It's a good safety measure.

$options = @{
    "Report only"        = "report"
    "Execute and report" = "execute"
}
$props = @{
    Parameters  = @(
        @{Name = "selectedOption"; Title = "Please choose an option for this CSV."; Options = $options; }
    )
    Title       = "Mode selection"
    Description = "Please choose."
    Width       = 300
    Height      = 300
    ShowHints   = $true
}
Read-Variable @props | out-null



Finally I ask for the email list they want to update by showing all current email lists:

try {
    $sendTasks = New-Object SitecoreFundamentals.SendBatchUploadAndMerge.Tasks.Send
    $emailLists = $sendTasks.GetEmailListsForSelectionAsync().Result
    $orderedEmailLists = [ordered]@{}
    $emailLists.GetEnumerator() | Sort-Object Key | ForEach-Object {
        $orderedEmailLists[$_.Key] = $_.Value
    }
    $props2 = @{
        Parameters  = @(
            @{Name = "emailListId"; Title = "Please select which email list to update."; Options = $orderedEmailLists; }
        )
        Title       = "Email list selection"
        Description = "Please choose."
        Width       = 300
        Height      = 300
        ShowHints   = $true
    }
    Read-Variable @props2 | out-null



And now the program runs! The $result is, you guessed it, the result of the task as a Tuple<bool, string>. 

    $result = $sendTasks.BatchEmailListUpdateAsync($uploadedFile, $emailListId, $selectedOption).Result

The rest is the output being presented to the User and some cleanup.

}
finally {
    if ($SendGetwayObject) {
        $SendGetwayObject.Dispose()
    }
}
if (Test-Path -Path $destinationPath -PathType Container) {
  Remove-Item -Path $destinationPath -Recurse -Force -Confirm:$false
}
Write-Host $result.Item2
if ($result.Item1) {
    Show-Alert -Title "The operation is complete. Please click View Script Results for more information."
}
else {
    Show-Alert -Title "There are errors in the upload. Please click View Script Results for more information."
}



Execution Results

You can see above both modes were run. If you're wondering about batches mentioned below, the execute mode uses batches incase over 1000 subscribers are included, but we'll get into that later. 




In the next article I'll walk you through the code supporting this script. See you there!