Creating a WFFM Save action with Field Mappings

The Sitecore Web Form for Marketers module offers content editors a flexible way to create data capture forms and then trigger certain actions to occur on submission. There’s a whole host of save action options out the box, such as sending an email, enrolling the user in an engagement plan or updating some user details.

However one save action that is often required is the ability to send the data onto a CRM system so that it can get to the people that need to act on it, rather than staying in Sitecore with the content editors. To do this your best option is to create a custom save action that can send on the information.

Creating a Save Action in Sitecore

Save actions in Sitecore are configured under /sitecore/system/Modules/Web Forms for Marketers/Settings/Actions/Save Actions. Here you can see all the standard ones that come out the box and add your own.

Right click Save Actions and insert a new Save Action. You will need to fill out the Assembly and Class fields so that Sitecore knows which bit of code to execute (details on creating this bit below).

Custom Save Action Sitecore Item

Adding Field Mappings

To really make your save action usable you will want to allow the content editor to map the fields on their form with the ones in the CRM, rather than relying on them both having the same name and hard coding the expected WFFM field names in your save action logic.

On your Save Action Item in Sitecore their are 2 fields to fill out to enable the editor in WFFM (Editor and QueryString). Luckily Sitecore provide a mapping editor out the box so there’s very little effort involved here.

Within the Editor filed add the value:

control:Forms.MappingFields

And within the querystring field, add your list of fields in the format fields=FieldName|FieldDisplayText

fields=FirstName|First Name,LastName|Last Name,EmailAddress|Email Address,CompanyName|Company Name

 

Custom Save Action Field Mappings

When the content editor now adds the save action to their form they will now be able to select a form field for each of these fields.

Custom Save Action Select Fields

Creating the Save Action in code

To create the save action you will need a class that inherits from either ISaveAction or WffmSaveAction. I’ve used WffmSaveAction as it already has some of the interface implemented for you.

The field list you added to the Querystring property of the save action in Sitecore will need to be added as public properties to your class. Sitecore will then populate each of these with the ID the field gets mapped to or null if it has no mapping.

Then all that’s left is to add an Execute method to populate your CRM’s model with the data coming in through the adaptedFields parameter and send it onto your CRM.


using Sitecore.Data;
using Sitecore.WFFM.Abstractions.Actions;
using Sitecore.WFFM.Actions.Base;
using System;

namespace MyProject.Forms
{
    public class SendFormToCrmSaveAction : WffmSaveAction
    {
        public string EmailAddress { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string CompanyName { get; set; }

        public override void Execute(ID formId, AdaptedResultList adaptedFields, ActionCallContext actionCallContext = null, params object[] data)
        {
            // Map values from adapted fields into enquiry model
            IEnquiry enquiry = new Enquiry();

            enquiry.Email = GetValue(this.EmailAddress, adaptedFields);
            enquiry.FirstName = GetValue(this.FirstName, adaptedFields);
            enquiry.LastName = GetValue(this.LastName, adaptedFields);
            enquiry.CompanyName = GetValue(this.CompanyName, adaptedFields);

            // Add logic to send data for CRM here
        }

        /// <summary>
        /// Get Value from field list data for a given form field id, or return null if not found
        /// </summary>

        /// <param name="formFieldId"></param>
        /// <param name="fields"></param>
        /// <returns></returns>
        private string GetValue(string formFieldId, AdaptedResultList fields)
        {
            if (string.IsNullOrWhiteSpace(formFieldId))
            {
                return null;
            }
            if (fields == null || fields.GetEntryByID(formFieldId) == null)
            {
                return null;
            }
                return fields.GetValueByFieldID(formFieldId);
            }
        }
    }
}

How I structure Sitecore Templates

Templates are the building blocks for a Sitecore installation, they’re amazingly flexible and with capabilities such as inheritance you can produce an elegant architecture, or alternatively a complete mess. Here’s how I like to structure things:

Base Templates

BaseTemplatesFirst off I have a folder of base templates. These are the building blocks for all the fields that will end up in a component data source or an actual page via inheritance. By setting them up as a base template we can re-use the same field definition for things like headings, but more importantly it keeps them focused on a specific purpose rather including details of the entire eventual item.

One additional rule I have for base templates is that they should only contain one data section within them. This in turn helps keep them focused on a specific purpose.

Page Templates

PageTemplatesNext we have pages. You guessed it, a page template is what a content editor will create an instance of when they make a page. It’s responsibility is to bring together default presentation details, the set of fields on the page and insert options for the pages beneath it.

All fields are inherited from base templates and standard values are used to define the defaults for the page.

Additionally to make the CMS experience as easy as possible for the content editors an icon should be set so that a page type can be visually identifiable.

Component Templates

ComponentTemplatesA component template is the equivalent of a page template but for data sources.

Like page templates all the fields are inherited from base templates and standard values are used to define the defaults for the page. An icon should also be set to make content types easily identifiable by content editors.

Folder Templates

FolderTemplatesFolder templates are often overlooked but they are an essential part of creating a decent user experience.

Folder templates are created to define the insert options for components and site settings rather than having them set on the site content tree.

Where relevant a folder template should also include itself as one of the insert options so that content editors can organise their content into sub-folders.

Parameter Templates

ParameterTemplatesWhen a component needs some config to customise its look and feel that is not content, it can be better to use Rendering Parameters rather than a data source.

Site Setting Templates

SettingsTemplateA Site configuration template is useful to contain various global settings for a site. This could include things such as the Site Logo, Google Analytics account details etc. Settings should be segmented into logical sections that have been defined in Base Templates and then inherited.

It can also be useful to separate some settings into their own template item depending on the scenario of what the setting is for.

Group Into Folders

With each these template types created, you’ll end up with a tree structure that looks something like this.

AllTemplates

Pipelines – remember the big picture

Sitecore pipelines are great. With them you can relatively easily add and remove functionality as you wish. Pipelines like httpRequestBegin, httpRequestProcessed and mvc.beginRequest are also really useful if you need some logic to run on a page load that shouldn’t really be part of a rendering. This could be anything from login checks to updating the way 404 pages are returned. However you do need to remember the big picture of what you are changing.

Pipelines don’t just effect the processes of the website your building, Sitecore uses them too. That means when you add a new processor to the httpRequestBegin pipeline, that’s going to effect every request in the admin cms too. Just checking the context item also isn’t enough as some things. e.g. opening a node in a treeview, will have the context of the node you clicked on!

Adding this snippet of code to the begining of your process should keep you safe though.

using Sitecore.Diagnostics;
using Sitecore.Pipelines.HttpRequest;
using Sitecore;
using Sitecore.SecurityModel;

namespace CustomPiplelineNamespace
{
    public class CustomPipeline : HttpRequestProcessor
    {
        public override void Process(HttpRequestArgs args)
        {
            //Check args isn't null
            Assert.ArgumentNotNull(args, "args");
            //Check we have a site
            if (Context.Site == null)
                return;
            //Check the sites domain isn't one for sitecore
            if (Context.Site.Domain != DomainManager.GetDomain("sitecore"))
                return;
            //Check that we're not in a redirect
            if (args.Url.FilePathWithQueryString.ToUpperInvariant().Contains("redirected=true".ToUpperInvariant()))
                return;
            //Check that we're not in the page editor
            if (Context.PageMode.IsPageEditor)
                return;

            // DO CODE
        }
    }
}

Sitecore xDB – How to get count of interactions

If you need to rebuild your Path Analyzer maps, Sitecore have a knowledge base article on how to do it here – https://kb.sitecore.net/articles/766858

In the FAQ section it gives some info on how long you should expect it to take

Depending on the processing power of your server, your XDB configuration, this could take from minutes to hours. Most of the time is spent on bringing over and de-serializing XDB interaction data on the Processing Server.

For the reference, it takes approximately 2 hours on XDB with 7M interactions, running on a single server.

What it doesn’t tell you though is how you find out how many interactions your site has to process. The data is stored in analytics collection in your Mongo DB, so go there an then run…

db.getCollection('Interactions').find({}).count()

Data Source vs Rendering Parameters

Control Properties

On the face of it being able to specify Rendering Parameters, or a Data Source on a control achieves a very similar goal. You want to componentise your page and not fill up your page type item with fields for specific renderings. Everything should be isolated in it’s own little compartment.

Linking a rendering control to a Data Source does this by moving all your data to a completely separate item, that can be re-used over multiple pages and even swapped out to do personalization or A/B testing.

Equally, using Rendering Parameters does this just as well. Just like a Data Source, you can create a template for the fields that need to be entered, and the data is kept in the same place that a link to a Data Source would be specified.

But what should I use?

The easiest way I can describe it is by asking, are you storing content or something else?

If the answer is content then you most likely want a data source. Alternatively, if it’s something else like some config for a filter, or a background colour you will most likely want a rendering parameter (but there could be exceptions).

Data Sources

Data Sources have some distinct advantages over a Rendering Parameter including:

  • Data can be edited in the Experience Editor inline in your page
  • Language versions are very easy to understand by content editors
  • Easily used for personalization and testing
  • Can be reused on as many controls as you like

These things all make a Data Source a perfect option for anything content related. Without them you loose some big features related to content and ease of use. There are times non-content is also a good option. e.g. A hero banner on a page could be used across multiple pages and as well a reusing the text, if a background colour is also customizable you would want to be re-using that too.

Rendering Parameters

Likewise Rendering Parameters also have there benefits:

  • Doesn’t create an extra item to publish. You just have to publish the page your editing, not the related data source
  • Easy to access in the control properties of the item your looking at, without having to find the related data source

These features make Rendering Parameters ideal for the non-content config of a control and removes non-content fields from your item templates. The thing to be aware of though is you can’t easily do A/B testing or personalization with a rendering parameter, so while it’s nice for your items to only contain content, that might not be the only thing that’s being tested.

How about both?

There is also nothing to stop you using a Rendering Parameter and a Data Source at the same time.

You could have a situation when a Data Source is being used to populated the content on multiple different controls. Each control may have some additional config needed, such as text colour or text size. Keeping this data in the data source might become problematic as on one rendering your text may need to be black, whereas on another it may need to be white. By moving these fields to Rendering Parameters, you keep the benefit of a shared Data Source for content, while the Rendering Parameter takes care of the presentation config.

Sitecore html cache not clearing on publish

So you’ve got separate content management and content delivery servers, but when you publish the change is only visible on the content management box.

A likely cause is that you’ve enabled some caching but haven’t updated the config files to clear the cache on your content delivery server.

Sitecores config files contain a list of handlers for what should happen when the event publish:end and publish:end:remote are triggered. Publish end is for the content management server, whereas publish end remote is for your delivery servers. The handler we’re interested in is Sitecore.Publishing.HtmlCacheClearer which contains a list of sites to have the cache’s cleared on.

By default this will contain one entry for website, the default name given to your site in the sites config when you install sitecore. However you will have changed this if your solution supports multiple sites, or if you changed it as part of some future planning to support multiple sites. If your site is missing, just add it to the live (via a patch file of course)

<!-- Html Cache clear on publish events -->
<!-- Force FULL cache clear on publish-->
<event name="publish:end">
 <handler type="Sitecore.Publishing.HtmlCacheClearer, Sitecore.Kernel" method="ClearCache" patch:source="BaseSettings.config">
  <sites hint="list">
    <site>SiteOne</site>
    <site>SiteTwo</site>
    <site>SiteThree</site>
  </sites>
 </handler>
</event>
<!-- Html Cache clear on publish events -->
<!-- Force FULL cache clear on publish-->
<event name="publish:end:remote">
 <handler type="Sitecore.Publishing.HtmlCacheClearer, Sitecore.Kernel" method="ClearCache" patch:source="BaseSettings.config">
  <sites hint="list">
   <site>SiteOne</site>
   <site>SiteTwo</site>
   <site>SiteThree</site>
  </sites>
 </handler>
</event>

Note: in the sample above I have removed all other handlers to simplify the example. You should not remove these from your solution.

For more info on cache clearing and optimising it, see John Wests blog series on the subject here https://community.sitecore.net/technical_blogs/b/sitecorejohn_blog/posts/sitecore-output-cache-clearing-optimization-1-8-introduction-john-west-sitecore-blog

Update local Git branch list

Ever had the issue where your Git repo has a branch that your local hasn’t picked up on? This can often happen when a branch has just been created.

The solution is simple just run…

$ git remote update origin

Displaying the cost of a uCommerce custom IShippingMethodService

I just found this bog post on how to get the shipping cost for a custom shipping method you may have implemented using IShippingMethodService and thought I would share it here.

http://www.davejsaunders.com/2014/10/20/display-the-cost-of-your-ucommerce-custom-shipping-service.html

A common requirement when displaying shipping methods is to show the price against each, and for the built in uCommerce shipping methods there’s a handy GetPriceForCurrency() method on the shipping method object. However if that shipping method is a custom one, it just returns 0.

 

var allShippingMethods = TransactionLibrary.GetShippingMethods(country);
var currency = purchaseOrder.BillingCurrency;

foreach (var shippingMethod in allShippingMethods)
{
	var cost = shippingMethod.GetPriceForCurrency(currency);
}

The correct way to get your shipping price is to manually call your service to get the price to be calculated.

As we don’t know which implementation of IShippingMethodService will be used we first need to call GetShippingMethodService() to get it.

After that we just need to populate a new Shipment object we the details our custom method needs and then call the CalculateShippingPrice method.

var purchaseOrder = TransactionLibrary.GetBasket().PurchaseOrder;
var allShippingMethods = TransactionLibrary.GetShippingMethods(country);

foreach (var shippingMethod in allShippingMethods)
{
  // Get the IShippingMethodService for this ShippingMethod
  var shippingService = shippingMethod.GetShippingMethodService();

  // Construct a fake shipping method to call the service with
  var shipment = new Shipment
  {
	  ShippingMethod = shippingMethod,
          OrderLines = purchaseOrder.OrderLines,
	  PurchaseOrder = purchaseOrder,
	  ShipmentAddress = purchaseOrder.BillingAddress
  };

  var shippingMethodPrice
 	 = shippingService.CalculateShippingPrice(shipment);
}

Redirect to https using URL Rewrite

There’s always been reasons for pages to be served using https rather than http, such as login pages, payment screens etc. Now more than ever it’s become advisable to have entire sites running in https. Server speeds have increased to a level where the extra processing involved in encrypting page content is less of a concern, and Google now also gives a boost to a pages page ranking in Google (not necessarily significant, but every little helps).

If all your pages work in https and http you’ll also need to make sure one does a redirect to the other, otherwise rather than getting the tiny page rank boost from Google, you’ll be suffering from having duplicate pages on your site.

Redirecting to https with URL Rewrite

To set up a rule to redirect all pages from is relatively simple, just add the following to your IIS URL Rewrite rules.

<rule name="Redirect to HTTPS" stopProcessing="true">
  <conditions>
    <add input="{HTTPS}" pattern="^OFF$" />
  </conditions>
  <action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" appendQueryString="false" />
</rule>

The conditions will ensure any page not on https will be caught and the redirect will do a 301 to the same page but on https.

301 Moved Permanently or 303 See Other

I’ve seen some posts/examples and discussions surrounding if the redirect type should be a 301 or a 303 when you redirect to https.

Personally I would choose 301 Moved Permanently as you want search engines etc to all update and point to the new url. You’ve decided that your url from now on should be https, it’s not a temporary redirection and you want any link ranking to be transfered to the new url.

Excluding some URL’s

There’s every chance you don’t actually want every url to redirect to https. You may have a specific folder that can be accessed on either for compatibility with some other “thing”. This can be accomplished by adding a match rule that is negated. e.g.

<rule name="Redirect to HTTPS" stopProcessing="true">
  <match url="images" negate="true" />
  <conditions>
    <add input="{HTTPS}" pattern="^OFF$" />
  </conditions>
  <action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" appendQueryString="false" />
</rule>

In this example any url with the word images in would be excluded from the rewrite rule.

Sitecore: Programmatically adding contacts to a list

From Sitecore 8 the EXM module now uses lists to manage mailing lists rather than roles against a user. The built in Subscription form control that comes with EXM has also been updated to add contacts to this list. However the subscription control remains WebForms only, so if you implementing an MVC solution you’re going to need to write your own. There’s also many other scenarios where you may want to programmatically create and add a contact to a list.

Under the hood, contact lists aren’t even a list at all. Rather they are actually just a Facet on the Contact record that contains the list id’s for all the lists the contact is a part of. You can see this by looking in the contacts collection in the analytics mongo db.

List Tag on Contact

Or in the Contacts table in the Reporting SQL db.

List Tag on Contact SQL

If you wanted to add a contact to a list you could in theory just add the relevant tag to the contact record like this:

public void AddContactToList(Contact contact, Item list)
{
   using (new SecurityDisabler())
   {
      contact.Tags.Set("ContactLists", list.ID.ToString());
   }
}

But I wouldn’t. The problem with this approach is your going to miss out any logic that will handle updating the counts of contacts in contact lists. Best to use one of the provided list api’s instead.

Adding a contact to a list

Sitecore has a ContactListManager object that has a method to associate contacts with lists. All you need to do is create an instance of it and pass it a list of contacts.

public void AddContactToList(ContactData contact, ContactList list)
{
    ContactListManager listManager = Sitecore.Configuration.Factory.CreateObject("contactListManager", false) as ContactListManager;

    List<ContactData> contactList = new List<ContactData>();
    contactList.Add(contact);

    listManager.AssociateContacts(list, contactList);
}

Removing  a contact from a list

Just like adding a contact, there’s also a handy method for removing one too.

public void RemoveContactFromList(ContactData contact, ContactList list)
{
    ContactListManager listManager = Sitecore.Configuration.Factory.CreateObject("contactListManager", false) as ContactListManager;

    List<ContactData> contactList = new List<ContactData>();
    contactList.Add(contact);

    listManager.RemoveContactAssociations(list, contactList);
}

What’s that ContactData object?

Chances are you don’t have a ContactData object (Sitecore.ListManagement.ContentSearch.Model.ContactData) and instead probably have a tracking contact (Sitecore.Analytics.Tracking.Contact). For the purposes of adding and removing a contact from a list, all your ContactData object really needs is its identifier, which you can do with the following:

public ContactData ConvertContactToContactData(Sitecore.Analytics.Tracking.Contact contact)
{
    return new ContactData()
    {
        Identifier = contact.Identifiers.Identifier
    };
}