Customizing tracking and e-commerce

The Google Analytics for Episerver contains built-in metrics, but you also can develop custom metrics through attributes and code. This topic describes how to extend the Google Analytics for Episerver add-on and what data to track, integrated with a Episerver CMS website and Episerver Commerce, using both Google services standard Ecommerce and Enhanced Ecommerce tracking.

Note: If you as a developer intend to extend the add-on through the APIs, you need to install it from the NuGet feed into Visual Studio.

Technical overview

From a technical perspective, the Google Analytics for Episerver Add-on is responsible for querying the Google Analytics REST API to present data in the dashboard and page-specific gadget in edit view and to render the tracking JavaScript to the pages on the site. 

  • The metric factory aggregates metrics and define lists and graphs in the gadget.
  • The gadget data repository combines metrics to build Google queries.
  • The Episerver query helper provides access to Google API authenticated with Episerver.

Google data is built up through tracking script on the website. The GData API is the Google C# client API to query analytics data. Google data can be queried directly using Google API. Episerver is reusing authentication data.

Code generated from admin view

When configuring the domains to track in admin view, the following code is generated:

  • A single domain. Track a single domain with the Tracking ID. No setDomainName command is rendered to tracking code.
  • One domain with multiple subdomains. Track one domain with multiple subdomains with the Tracking ID. The setDomainName command is rendered with stripped-first-domain-segment of current Request.Url.Authority, if the remaining part after it has been stripped still is a valid domain authority.
    • A page view (to a.epi.com, b.epi.com, c.epi.com) is set up as shown in the following example:
      _gaq.push(['_setDomainName', epi.com']);
    • A page view (to a.epi.com:8080, b.epi.com:8080, c.epi.com:8080) is set up as shown in the following example:
      _gaq.push(['_setDomainName', epi.com:8080']);
    • A page view (to a.demo.epi.com, b.demo.epi.com, c.demo.epi.com) is set up as shown in the following example:
      _gaq.push(['_setDomainName', demo.epi.com']);
    • An unchanged page view (to epi.com) is set up as shown in the following example:
      _gaq.push(['_setDomainName', epi.com']);
  • Multiple top-level domains. Track multiple domains with the same Tracking ID.
    • A page view (to epi.com, epi.net, epi.org) is set up accordingly as shown in the following example:
      _gaq.push(['_setDomainName', epi.com']); _gaq.push(['_setDomainName',epi.net]); _gaq.push(['_setDomainName', epi.org]);

 Adding tracking script on the master page

To add a tracking script on the master page for Web Forms and MVC, refer to the Client resources section in the Episerver CMS SDK > Developer Guide.

Remove existing tracking script on your website to avoid duplicated tracking scripts running on your website, for example, from the master page. Google Analytics for Episerver adds a tracking script to the page when a Tracking ID is configured in the administrative interface of Episerver CMS. 

Customizing tracking

The Episerver Google Analytics add-on uses a ClientResourceRegister class to add the necessary Google Analytics JavaScript to the header of the page.

  • If you use MVC, make sure you have the following code somewhere in your head tag:
    @Html.RequiredClientResources(EPiServer.Framework.Web.RenderingTags.Header)
  • If you use WebForms, add the following code in your head tag:
    <EPiServer:RequiredClientResources RenderingArea="Header" ID="RequiredResourcesHeader" runat="server" />

 
If you do not use the RequiredClientResources registration, you need to render the script yourself, see Rendering the script manually.

You can extend the generated script by adding AnalyticsInteraction objects or using a class implementing the IPluginScript class registered with the service locator.

AnalyticsInteraction is a class that allows registering Google Analytics events and actions. The interactions are typically page specific and there are Extension methods on the HttpContext and HttpContextBase classes to help you add them. Example:

HttpContext.AddAnalyticsEvent("button", "click", "nav buttons", 4, clearWhenContextChanged: true);

This renders the following script:

ga('send', 'event', 'button', 'click', 'nav buttons', 4);

With an IPluginScript class, you can also add custom JavaScript to all pages from one place, see example in Setting the currency.

In summary, the add-on will render something like this (for Universal Analytics Tracking):

 
Overriding the script generation with custom tracking

 

The class responsible for rendering the script to the page is the GASyntax class, which has support for the following:

  • Google Analytics Classic tracking mode
  • Google Analytics Universal tracking mode
  • A completely custom mode

If you are starting implementing tracking from the beginning, you should selext to use the Google Analytics Universal mode. If you have an existing Google Analytics account that cannot be upgraded, you should go with the Classic mode.

You do not have to change to custom mode to extend the Classic or Universal modes, you can add your own script using AnalyticsInteractions. If the selected renderer does not fulfil the need, but the mode does (Classic or Universal), you can register your own implementation using the Service Locator:

context.Container.Configure(c => c.For<UniversalSyntax>().Use<MyCustomUASyntax>());

 
Rendering the script manually

In case you need more control over the script rendering you can get the script code directly using one of the extension methods in the EPiServer.GoogleAnalytics.Helpers.Extensions class.

  • For Web Forms, use public static string GetAnalyticsTrackingScript(this PageBase page, HttpContext context)
  • For MVC, use public static MvcHtmlString AnalyticsTrackingScript(this System.Web.Mvc.HtmlHelper html, PageData currentPage)

Both methods return the Google Analytics script based on the configured syntax (Classic or Universal Analytics).

You can also build the following script:

var scriptBuilder = new ScriptBuilder(EPiServer.Web.SiteDefinition.Current, () =>
{
	
	return new SiteTrackerSettings
	{
	// specify syntax you want to generate tracking script
	TrackingScriptOption = TrackingScriptOption.Universal,
	TrackingId = "UA-XXXXXX-1"
	};
});
var requiresScriptReference = false;
var context = new HttpContextWrapper(HttpContext.Current);
var script = scriptBuilder.BuildTrackingScript(out requiresScriptReference, context, CurrentPage);
// The script variable now contains tracking script with UA syntax
// You can modify it or render it directly

Note: If you render the script manually, and still have the RequiredClientResource registration for Header, use the “Custom Tracking Script” setting for the add-on, or you will get two tracking scripts on your page.

Customizing e-commerce tracking

With the Google Analytics Universal Tracking, you have the following options for tracking e-commerce actions and data:

The Enhanced Ecommerce tracking option is the most feature rich, and if you do not have any existing Ecommerce tracking implemented, you should use the Enhanced Ecommerce tracking.

You should not use both Ecommerce tracking and Enhanced Ecommerce tracking at the same time.

In contrast to tracking page views, all e-commerce tracking require more data to be sent. Because Episerver Commerce implementations vary, and there is no standard cart or checkout page, the Google Analytics for Episerver add-on cannot add e-commerce tracking script automatically to the page. It will help you generate the correct script, but you will have to add it to the relevant pages in your solution.

Tracking a product detail view

The Google Analytics documentation shows in the following example how the script should look:

ga('create', 'UA-XXXXX-Y');
ga('require', 'ec');
ga('ec:addProduct', {
	'id': 'P12345',
	'name': 'Android Warhol T-Shirt',
	'category': 'Apparel',
	'brand': 'Google',
	'variant': 'black'
});
ga('ec:setAction', 'detail');
	// Send product details view with the initial page view.
ga('send', 'pageview');

The script above includes the Enhanced Ecommerce plugin (ga('require', 'ec');), prepares the data, indicate what action it is tracking and sends that in addition to the page view itself in one go to the Google Analytics servers.

With the Google Analytics add-on, each script line is an AnalyticsInteraction, and you can add these using HttpContext and HttpContextBase extension methods. In the following code, the addProduct activity is populated using a dictionary that holds the required values and added using the AddAnalyticsCustom extension method.

public void TrackProductDetail(HttpContextBase context, string code, string name, string category = null, string brand = null, string variant = null, double price = 0)
{
	var dict = new Dictionary<string, object>();
	if (code != null) dict.Add("id", code);
	if (name != null) dict.Add("name", name);
	if (category != null) dict.Add("category", category);
	if (brand != null) dict.Add("brand", brand);
	if (variant != null) dict.Add("variant", variant);
	if (price > 0) dict.Add("price", price.ToString());
	
	context.AddAnalyticsCustom("ec:addProduct", true, dict);
	context.AddAnalyticsCustom("ec:setAction", true, "detail");
}

Call it from your controller (or Web Form) as follows:

TrackProductDetail(HttpContext.Current, "P12345", "Android Warhol T-Shirt", "Apparel", "Google", "black", 0);

The “true” parameter in the call to AddAnalyticsCustom tells the script generator to remove the interaction after the script has been generated for the request. If you set it to false, the interaction is added to the session, and rendered on subsequent requests to all pages. You should put global settings into a IPluginScript class instead, as the order of the interactions will be hard to predict across page requests.

The project is using a custom IPluginScript that registers ga('require', 'ec'); for all requests, which is included on pages just after the ga('create', 'UA-XXXXX-Y'); call. You do not have to generate these script lines from your controller code, see Setting the currency.

The last step is to send it all off, and since that will be done using the standard page view command, you do not have to include that the script, the add-on will always end the script block with the ga("send", "pageview"); command.

Tracking the checkout

A checkout process typically ends with a “Thank you” page. This is a good place to track purchases with Google Analytics. If the checkout ends with a redirect to that page, it can retrieve the order number and track it using an extension method on HttpContext as shown in the example below.

This example renders script using the Ecommerce, not the Enhanced Ecommerce syntax. If you configured your account to use Enhanced Ecommerce syntax, go to the Tracking the checkout using Enhanced Ecommerce syntax section.

These examples are referring to the Episerver Commerce Sample templates.

In the Episerver Commerce sample website, the following happens after the checkout has been done:

  • Logged customers are redirected to the Order administration page.
  • Anonymous customers are redirected to the CheckoutConfirmationStep page.

The following example is for the anonymous use case, which is redirected to the CheckoutConfirmationStep page (you should add the example code for logged in users in Order.aspx and Order.aspx.cs).

Example of tracking using server code:

  1. Edit the CheckoutConfirmationStep.aspx.cs file.
  2. Add the following string directive on the top of file:
    using EPiServer.GoogleAnalytics.Commerce.Helpers;
    Add the following code to Page_Load:
  3. // Registers the order to be tracked by the Google Analytics tracking script added by the module, and generate tracking script on current page only
    protected void Page_Load(object sender, EventArgs e)
    {
    	var order = OrderContext.Current.GetPurchaseOrders(SecurityContext.Current.CurrentUserId).OrderBy(o => o.OrderGroupId).LastOrDefault();
    	if (order == null)
    		return;
    	var order = Mediachase.Commerce.Orders.OrderContext.Current.GetPurchaseOrders(Mediachase.Commerce.Security.SecurityContext.Current.CurrentUserId).OrderBy(o =>
    	o.OrderGroupId).LastOrDefault();
    	
    	Context.AddAnalyticsTransaction(order, CurrentPage.ContentLink);
    }
    Build the project, perform a purchase, and you should start seeing transaction data in Google Analytics reports.
  4. The AddAnalyticsTransaction method detects which Google Analytics tracking mode that is being used, and will render the correct script for both Classic and Universal Analytics using the Ecommerce plugin.

Note: You do not have to add the ga('require', 'ecommerce'); script, as this will be automatically by the EPiServer.GoogleAnalytics.Commerce assembly through the CommercePluginScript class.

Tracking the checkout using the Enhanced Ecommerce syntax

The following example uses the Enhanced Ecommerce library for Google Analytics and the examples below refer to the Episerver Commerce sample templates.

  1. First create a new IPluginScript class to render the ga('require', 'ec'); script before any other custom Google Analytics script on the page.
    public class RequireEnhancedCommercePlugin : IPluginScript
    {
    	public string GetScript()
    	{
    		string script = "ga('require', 'ec');";
    		return script;
    	}
    }
    Register the class with the Service Locator to override the default behavior from the CommercePluginScript class and add ga('require', 'ecommerce'); (which refers to the regular Ecommerce plugin and not the Enhanced Ecommerce plugin). Register the class with the Service Locator as follows:
    [InitializableModule]
    [ModuleDependency(typeof(FrameworkInitialization))]
    public class InitializationModule : IConfigurableModule
    {
    	...
    	/// <summary>
    	/// Setup StructureMap configuration, auto called in website start
    	/// </summary>
    	/// <param name="context"></param>
    	public void ConfigureContainer(ServiceLocation.ServiceConfigurationContext context)
    	{
    		context.Container.Configure(c =>
    			{
    				c.For<UniversalSyntax>().Use<MyCustomUASynctax>();
    				c.For<IPluginScript>().Use<RequireEnhancedCommercePlugin>();
    			});
    	}
    	...
    }
  2. To make it easier to add the tracking to the page, add a helper class that knows how to render the correct Enhanced Ecommerce syntax as follows:
    public class TrackingHelper
    {
    	public void TrackTransaction(HttpContextBase context, PurchaseOrder order)
    	{
    		var orderForm = order.OrderForms[0];
    
    		foreach (LineItem item in orderForm.LineItems)
    		{
    		context.AddAnalyticsInteraction(GetAddProduct(item));
    		}
    
    		var orderId = order.TrackingNumber;
    		var dict = new Dictionary<string, object>();
    		dict.Add("id", order.TrackingNumber);
    		dict.Add("affiliation", CommerceExtensions.GetAffiliation(order.AffiliateId));
    		dict.Add("revenue", order.Total);
    		dict.Add("tax", order.TaxTotal);
    		dict.Add("shipping", order.ShippingTotal);
    		context.AddAnalyticsCustom("ec:setAction', 'purchase", true, dict);
    	}
    
    	private AnalyticsInteraction GetAddProduct(LineItem item)
    	{
    		var dict = new Dictionary<string, object>();
    		dict.Add("id", item.CatalogEntryId);
    		dict.Add("name", item.DisplayName);
    		dict.Add("category", item.CatalogNode);
    		dict.Add("price", item.PlacedPrice);
    		dict.Add("quantity", item.Quantity);
    
    		return new AnalyticsInteraction("ec:addProduct", dict) { ClearWhenContextChanged = true };
    	}
    }
  3. Use the new tracking methods in the EPiServer.Commerce.Sample template by calling TrackTransaction in CheckoutConfirmationStep.aspx.cs as follows:
    protected void Page_Load(object sender, EventArgs e)
    {
    	var order = 
    	OrderContext.Current.GetPurchaseOrders(SecurityContext.Current.CurrentUserId).OrderBy(o => o.OrderGroupId).LastOrDefault();
    
    	if (order == null)
    		return;
    	liPONumber.Text = order.TrackingNumber.ToString(CultureInfo.InvariantCulture);
    	liTotal.Text = new Money(order.Total, order.BillingCurrency).ToString();
    
    	var tracking = new TrackingHelper();
    	tracking.TrackTransaction(new HttpContextWrapper(Context), order);
    }

Setting the currency

If you run a multi-market website supporting different currencies for your customers, you need to track the products and transactions using the correct currency. Your Google Analytics account will have one default currency (USD if you have not changed it), and all tracking that do not specify the currency will use the default currency.

This is no problem if all your purchases are in the same currency, you can change the currency setting in the Google Analytics admin interface at any time. However, if you sell the same product in two different markets, and do not specify the currency, the Google Analytics reports for revenue will be wrong.

Setting the currency for a transaction is easy with Universal Tracking, and you can do this for all requests by using the currency for the current market. The easiest way is to create an IPluginScript class that adds the currency.

The following example shows how to set currency based on the current market for the visitor, and also adds a “require” for the Enhanced Ecommerce plugin script:

/// <summary>
/// Tells Google Anayltics to inject the Enhanced Ecommerce script
/// and use the currency for the current market
/// </summary>
public class RequireEnhancedCommercePlugin : IPluginScript
{
	public string GetScript()
	{

		ICurrentMarket currentMarket = ServiceLocator.Current.GetInstance<ICurrentMarket>();
		IMarket market = currentMarket.GetCurrentMarket();

		// Require Enhanced Ecommerce AND set currency
		string script = "ga('require', 'ec');";
		script = script + string.Format("ga('set', '&cu', '{0}');", market.DefaultCurrency);
		return script;
	}
}

Before the class above is used, you need to register the class as the IPluginScript implementation to be used by the website:

[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class InitializeAnalytics : IConfigurableModule
{
	public void Initialize(InitializationEngine context)
	{
	}

	public void Preload(string[] parameters) { }

	public void ConfigureContainer(ServiceConfigurationContext context)
	{
		context.Container.Configure(c => c.For<IPluginScript>().Use<RequireEnhancedCommercePlugin>());
	}

	public void Uninitialize(InitializationEngine context)
	{
	}
}

This adds the following script lines after the initial tracking script:

ga('require', 'ec');
ga('set', '&cu', 'USD');

If you support several currencies for a single market, you may want to set the currency as an AnalyticsInteraction on the page where you track Ecommerce related activites.

Refer to the Google Analytics documentation about tracking currencies.

Last updated: Nov 25, 2014