Click Tracking with Goals and Page Events Using Sitecore Analytics

Hello Sitecorians,

I have had a couple of clients want to tie in the clicks on their website to their Engagement Analytics report data to understand click tracking behavior of their website visitors.  As a universal solution, you can use JavaScript, Handler, Page Events, and the Sitecore API to tie this all together for your clients.

For the tracking you can use the goal or page events.However feel free to mix it up as you see fit with your solution.

Page Events: 

Page events are located under the “/sitecore/system/Settings/Analytics/Page Events”.You can create the new page events here.

Page Events

Triggered page events will appear in Experience profile.If you want to show this in  “Experience Analytics report” then , you have to register the page event as goal like below.

page event goal

Goals :

Goals are located under the “/sitecore/system/Marketing Control Panel/Goals” .We can create the new custom goals under this item.

new goal

For this example I have created the page event and goals.You can use any one of them.

Steps:

  • Create Httphandler for the register the events or goals.
  • Create the common AJAX function to call the handler.
  • Call the ajax function with passing the goal or page event ID while clicking the button.

 

Create the ClientEventTracker HttpHandler like below,

using Newtonsoft.Json;
using Sitecore.Analytics;
using Sitecore.Analytics.Data;
using Sitecore.Diagnostics;
using System;
using System.Web;
using System.Web.SessionState;

namespace Sitecore.Scientist.Analytics
{
    /// <summary>
    /// Summary description for ClientEventTracker
    /// </summary>
    public class ClientEventTracker : IHttpHandler, IRequiresSessionState
    {
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "application/javascript";
            TriggerEvent(context);
        }


        private static void TriggerEvent(HttpContext context)
        {
            try
            {
                string jsonData = context.Request["jsonData"];

                var clientEventData = JsonConvert.DeserializeObject<ClientEventData>(jsonData);
                if (clientEventData == null)
                    return;
                if (string.IsNullOrEmpty(clientEventData.EventName))
                {
                    return;
                }
                if (!Tracker.Enabled)
                {
                    return;
                }

                if (!Tracker.IsActive || Tracker.Current == null)
                {
                    Tracker.StartTracking(); // init tracker without tracking the endpoint as a page
                }

                if (Tracker.Current == null || Tracker.Current.CurrentPage == null)
                {
                    return;
                }
             
                if (!string.IsNullOrEmpty(clientEventData.PageEventId))
                {
                    var eventData = new PageEventData(clientEventData.EventName, Guid.Parse(clientEventData.PageEventId))
                    {
                        Text = !string.IsNullOrEmpty(clientEventData.Text) ? clientEventData.Text : string.Empty,
                        DataKey = !string.IsNullOrEmpty(clientEventData.Text) ? clientEventData.Text : string.Empty,
                        Data = !string.IsNullOrEmpty(clientEventData.Data) ? clientEventData.Data : string.Empty,
                    };
                    Tracker.Current.CurrentPage.Register(eventData);
                    return;
                }
                else if (string.IsNullOrEmpty(clientEventData.Text))
                {
                    Tracker.Current.CurrentPage.Register(clientEventData.EventName, string.Empty);
                    return;
                }
                else
                {
                    if (string.IsNullOrEmpty(clientEventData.Datakey) || string.IsNullOrEmpty(clientEventData.Data))
                    {
                        Tracker.Current.CurrentPage.Register(clientEventData.EventName, clientEventData.Text);
                        return;
                    }
                }
            }
            catch (Exception exception)
            {
                Log.Error(string.Concat("Sitecore.Scientist.Analytics.ClientEventTracker.TriggerEvent: error in event triggering. requestUrl: ", context.Request.Url.AbsolutePath), exception, typeof(ClientEventTracker));
            }
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }


    public class ClientEventData
    {
        public string PageEventId { get; set; }
        public string EventName { get; set; }
        public string Data { get; set; }
        public string Datakey { get; set; }
        public string Text { get; set; }
    }
}

Now need to create the ClientEventTracker.js like below and refer this script file inside head tag in Layout.cshtml.

var eventTracker = false;

function AnalyticsPageEvent(jsonData) {
    this.jsonData = jsonData;
    this.trigger = function () {
        var queryString = '';

        if (!this.jsonData) {
            return;
        }
        queryString += '&' + 'jsonData' + '=' + JSON.stringify(this.jsonData);
        if (queryString != '') {
            var url = '/sitecore/analytics/ClientEventTracker.ashx' + '?ra=' + eventTracker.randomstring() + queryString;
            eventTracker.request(url);
        }
    };
}

function EventTracker() {
    this.request = function (url) {
        var script = new ClientEventScript(url, true);
        script.load();
    };

    this.randomstring = function () {
        var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        var text = "";

        for (var i = 0; i < 32; i++) {
            text += possible.charAt(Math.floor(Math.random() * possible.length));
        }

        return text;
    };
}

function ClientEventScript(src, async) {
    this.src = src;
    this.async = async;

    this.load = function () {
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = this.src;
        script.async = this.async;

        var ssc = document.getElementsByTagName('script')[0];
        ssc.parentNode.insertBefore(script, ssc);
    };
}
eventTracker = new EventTracker();

Finally we have to create the our custom.js file for the button click event trigger AJAX functions.

var Scientist = Scientist || {};

$(document).ready(function () {
    Scientist.Booking.Init();
});

Scientist.Booking = {
    Init: function () {

        $(".booking-button").click(function () {

            Scientist.Booking.TrackBookingEvent(this);

        });

    },

    TrackBookingEvent: function (element) {

        var dataContainer = $(element);

        var jsonObject = {
            PageEventId: dataContainer.data("goal"),
            EventName: "Hotel Booking",
            Data: "Booking",
            Datakey: "HotelName",
            Text:"Booking button clicked"
        };
        var analyticsEvent = new AnalyticsPageEvent(jsonObject);
        analyticsEvent.trigger();
    }
};

I have created the button in my view like below,

button html

So while clicking this button it will make the ajax call to register the goal.Once the session ends , you can see the data in reports.

goal report

If you can’t see data,Just wait for session end or abandon the session forcefully.

You can check the details in experience profile as well.

 

Download the Source code from Github.

Sitecore 9 Forms Part-1 – Form with Email Exist Validation

Hello Sitecorians,

Hope you’re playing around with Sitecore 9 and with the newly introduced cool features in it.There is lot to do with the Sitecore forms .I will explain that in upcoming posts.In this post i am going to explain the basic sitecore 9 form with Email exist validation.

Every subscription form email exist validation is very basic requirement.We can do that in very simple way.Please follow my steps,

How to do ?

  1. Create the new validator class “EmailExistValidator”
using Microsoft.Extensions.DependencyInjection;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.DependencyInjection;
using Sitecore.ExperienceForms.Data;
using Sitecore.ExperienceForms.Mvc.Models.Fields;
using Sitecore.ExperienceForms.Mvc.Models.Validation;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;

namespace Sitecore.Scientist.Forms.Validator
{
    public class EmailExistValidator : ValidationElement<string>
    {
        public readonly string FormTemplateId = "{6ABEE1F2-4AB4-47F0-AD8B-BDB36F37F64C}";

        private IFormDataProvider _dataProvider;


        protected virtual IFormDataProvider FormDataProvider
        {
            get
            {
                IFormDataProvider formDataProvider = this._dataProvider;
                if (formDataProvider == null)
                {
                    IFormDataProvider service = ServiceLocator.ServiceProvider.GetService<IFormDataProvider>();
                    IFormDataProvider formDataProvider1 = service;
                    this._dataProvider = service;
                    formDataProvider = formDataProvider1;
                }
                return formDataProvider;
            }
        }

        public override IEnumerable<ModelClientValidationRule> ClientValidationRules
        {
            get
            {
                if (string.IsNullOrEmpty(this.FormId))
                {
                    yield break;
                }
            }
        }

        protected virtual string FormId
        {
            get;
            set;
        }

        protected virtual string Title
        {
            get;
            set;
        }
        protected virtual string FieldName
        {
            get;
            set;
        }
        public EmailExistValidator(ValidationDataModel validationItem) : base(validationItem)
        {

        }

        public override void Initialize(object validationModel)
        {
            base.Initialize(validationModel);
            StringInputViewModel stringInputViewModel = validationModel as StringInputViewModel;
            if (stringInputViewModel != null)
            {
                var fieldItem = Context.Database.GetItem(ID.Parse(stringInputViewModel.ItemId));
                var formItem = GetFormItem(fieldItem);
                if (formItem != null)
                {
                    this.FormId = formItem.ID.ToString();
                }
                this.Title = stringInputViewModel.Title;
                this.FieldName = stringInputViewModel.Name;
            }
        }

        public override ValidationResult Validate(object value)
        {
            if (value == null)
            {
                return ValidationResult.Success;
            }
            if (!string.IsNullOrEmpty(this.FormId))
            {
                var formId = Guid.Parse(this.FormId);
                var data = this.FormDataProvider.GetEntries(formId, null, null);
                foreach (var item in data)
                {
                    var emailValue = item.Fields.Where(x => x.FieldName == this.FieldName).FirstOrDefault();
                    if (emailValue != null && emailValue.Value.ToLower() == value.ToString().ToLower())
                    {
                        return new ValidationResult(this.FormatMessage(new object[] { this.Title }));
                    }

                }
                return ValidationResult.Success;
            }
            return new ValidationResult(this.FormatMessage(new object[] { this.Title }));
        }
        public Item GetFormItem(Item sitecoreItem)
        {
            if (sitecoreItem == null)
                return null;
            Item parent = sitecoreItem.Parent;
            if (parent.TemplateID == ID.Parse(this.FormTemplateId))
            {
                return parent;
            }
            else if (ItemIDs.RootID == parent.ID)
            {
                return null;
            }
            else
            {
                return GetFormItem(parent);
            }
        }
    }
}

2. Now we have to create the EmailExistValidator under the “/sitecore/system/Settings/Forms/Validations” in master database.

3. Just duplicate the Emailvalidator and update the values like below screenshot

EmailExistValidator

4. Create the new form with Email field ,

Subscribe form

5. Add the newly created “EmailExistValidator” validation to the email field.

add validation

6 . We are almost done.Lets go for the testing.

Exist Validation

415w

Thanks for visiting my blog.Download the source code from Github.

Redirect Module for Sitecore 9

Simple redirect module for the Sitecore 9 and Sitecore 8. It has the following options for the redirect

  1. Sitecore Internal Items Redirect
  2. Regex Options
  3. Broken Links

It Supports 3 type of redirection

  1. Permanent Redirect (301)
  2. Temporary Redirect (302)
  3. Server Transfer

How to Use the Redirect Module

  1. Download and install the Redirect Module from the market place.Alternatively you can download from Simple Redirect Module-V2.0.1.zip
  2. Add these entry to your Site config file.
  3. You can keep the separate settings for each when you are using the multi site.

redirectSettingsId=”{23F429BA-9BBF-4FDD-9BCD-1E7E46A0D029}”

redirect settings.png

How to Use the Redirect Module

Internal Item Redirect

  1. Add the new redirect item

Redirect

Map the target Item and Redirect Url

Redirect internal

2. Regex

Redirect Regex

For Ex. ^/test3(/.*)?$ – /test$1

Target Url : http://sitecoresite/test3/home4?test=1

Redirect Url :  http://sitecoresite/test/home4?test=1

3. Redirect Map

Redirect Map

Download the Source code from Github.