Sitecore MVC and WebForms for Marketers: Those Pesky Select Lists

Posted by

I’m working on a Sitecore 7.5 MVC site and was tasked with implementing the contact us page leveraging Web Forms For Marketers (WFFM).

At first I was a bit hesitant to work on this feature due to the challenges with WFFM on Sitecore 7.2 MVC – but then I was quickly surprised and excited as I found out Sitecore released WFFM 2.5 that has “full” MVC support for Sitecore 7.5. All my problems were solved (or so I thought…).

Here is a screenshot of the form in action in the page editor (kindly ignore that it is not styled just yet):

wffm

One of the requirements was that the user’s selection in the “Division” field must send an email to a predetermined, content-managed email address.

This requires:

1. Creating a new template type to store the display name and the email address.

2. Create a custom WFFM Save Action that will email the form submission to the division that the user selected.

Not too bad, right?

So to get started I created a “DivisionType” template to store the division name and email. The template has with two fields “DivisionTypeName” and “DivisionEmailAddress”. Then I created some items in the data section of my content tree:

/sitecore/content/CorporateSite/Global/Components/Data/DivisionTypes
item-tree

Next, I created the form in WFFM. For the division field, I set:

  • Field Type: Droplist
  • Field datasource: “Set Items by”
  • Selected root item: pointed to the parent DivisionTypes folder:
    /sitecore/content/CorporateSite/Global/Components/Data/DivisionTypes
  • Set the value column to “__ID”
  • Set the text column to “DivisionTypeName”
    wffm-division-field-setup

(I chose the “__ID” fieldinstead of the “DivisionEmailAddress” field because I didn’t want the actual email addresses to be the “value” of the select option in the html (it’s possible that a spider would pick up the email address and start sending spam to it). Instead I chose to use the item ID to look up the email address on the server – much safer).

Moving on: For the next step, I built my custom WFFM save action. I used JetBrains dotPeek to decompile and copy the “Save Email Message” save action code (since my functionality will be very similar, just dynamically setting the TO address based on the users selection). Here is the signature of my class:

public class SendEmailToDivisionSaveAction : SendMail
{ 
    protected override void ExecuteMail(ID form, AdaptedResultList fields)
        {
            ...
        } 
}

In Sitecore I created my custom save action class “SendEmailToDivisionSaveAction” in Visual Studio and wired it up in /sitecore/system/Modules/Web Forms for Marketers/Settings/Actions/Save Actions:

item-tree-2

I put a breakpoint at the beginning of the ExecuteMail() method, submitted the form, and inspected the “fields” parameter, expecting to see a Guid for the “division” field, but alas, it was null:

wffm-save-action-code-

So it seems to me there is a bug in WFFM for list types. Without the time (patience?) to submit a Sitecore support ticket, I decided to brute force it and pull the user’s selection out of HttpContext.Current.Request.Form post values instead.

To do so, I wrote a helper method: GetWFFMSubmittedFieldValue(Guid wffmFieldId). This method iterates through and finds at all the submitted “FieldId” values in the current request and gets the corresponding value.

Here is a screenshot of my form submission data in fiddler.

fiddler

See .Sections[0].Fields[3].Value ? Thats the ID of the divisionType the user selected!

So, without further ado: here is the code for the GetWFFMSubmittedFieldValue() method (the intent of this post – this ended up longer than I thought!). Just paste it into your custom save action class, provide it with the item ID of the WFFM field, and it will return the user’s selected value as a string:

///

       /// Gets the user submitted WFFM field value from the current Http Context by the WFFM field item ID.
       ///

private string GetWFFMSubmittedFieldValue(Guid wffmFieldId)        {            // Create arrays of all posted WFFM fields.            string[] fieldIdKeys = HttpContext.Current.Request.Form.AllKeys.Where(key => key.EndsWith(“].FieldId”)).ToArray();            string[] fieldValueKeys = HttpContext.Current.Request.Form.AllKeys.Where(key => key.EndsWith(“].Value”)).ToArray();            // Ensure we have the same number of keys and values.            if (fieldIdKeys.Any() && fieldIdKeys.Length == fieldValueKeys.Length)            {                int i = 0;                // Iterate through all the form ID key values, looking for the field id that matches the WFFM field id.                foreach (string fieldId in fieldIdKeys)                {                    Guid idValueGuid;                    string idValue = HttpContext.Current.Request.Form.GetValues(fieldIdKeys[i]).FirstOrDefault();                    if (!Guid.TryParse(idValue, out idValueGuid))                    {                        // not a valid sitecore ID. go to next item in list.                        continue;                    }                    // Does the field id match the “Division” field id?                    if (idValueGuid == wffmFieldId)                    {                        // Get the selected value                        string value = HttpContext.Current.Request.Form.GetValues(fieldValueKeys[i]).FirstOrDefault();                        if (!string.IsNullOrWhiteSpace(value))                        {                            return value;                        }                    }                    i++;                }            }            return string.Empty; // default to empty.        }

Then I used this method in another helper method to convert the ID to the actual email address:

///

        /// Gets the selected email address from the current http request.form.
        ///

public string GetSelectedDivisionEmailFromRequestForm()         {             string divisionTypeItemId = GetWFFMSubmittedFieldValue(ItemTree.System.Modules.Web_Forms_For_Marketers.CorporateSite.CorporateSite_Contact_Us.Division.ItemID.Guid);             Guid divisionTypeId;             if (!string.IsNullOrWhiteSpace(divisionTypeItemId) && Guid.TryParse(divisionTypeItemId, out divisionTypeId))             {                 IDivisionType divisionType = new SitecoreContext().GetItem(divisionTypeId);                 if (divisionType != null && !string.IsNullOrWhiteSpace(divisionType.DivisionEmailAddress))                 {                     return divisionType.DivisionEmailAddress;                 }             }             return string.Empty; // default to empty. }

Notes:

  •  IDivisionType is a TDS +  Glass.Mapper code generated class representing the DivisionType template.
  • ItemTree.System…. is also generated via TDS + glass mapper code using the itempaths.tt t4 code gen file. This elimitates the need for a constants class or hardcoded  guid.
  • new SitecoreContext().GetItem<>() is a glass.mapper method to get the strongly typed item.

Then I used the GetSelectedDivisionEmailFromRequestForm() method inside the ExecuteMail() method, and it all worked!

Here you can see the email, with the correct email address (using SMTP4DEV):

wffm-email

Another usage idea: You could use thisGetWFFMSubmittedFieldValue() method to grab the user’s responses to save to a database, post to a webservice, or create a sitecore item.

Can you think of any more usages of this method? Please let me know if you can think of any the comments below or on twitter @SteveStriga.

Cheers!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s