Monthly Archives: October 2015

View visitors in real-time in Sitecore using SignalR and MongoDB

VisitorsInRealtimeMap

In this post I would like to continue on the real-time subject, I see a great potential in using data in real-time. So why not combine my previous posts, Real-time in Sitecore using SignalR and MongoDB(where I showed you guys how to setup real-time and storing the data in Mongodb) and Make a Google Map SPEAK component in Sitecore.

How about show current visitors on a map in real-time. It’s almost like watching popcorn popping 🙂
RealtimeVisitorsAnimated

Add methods to the SignalR Hub – RealTimeHub

In SignalR we need to let Sitecore users be able to listen to the visitor changes. In the class RealTimeHub we will add the AuthenticateListener() method which will be called from the client script in the new speak component. The method will authenticate the Sitecore user( called Listeners) and add the user to the “Listeners” group.

public async Task<bool> AuthenticateListener()
{
    IHubContextService hubService = this._hubContextService.Resolve(Context);

    if (!hubService.IsUserAuthenticated())
        return false;

    await SubscribeToChannel(RealTimeConstants.Signalr.Groups.Listeners);

    return true;
}

public async Task SubscribeToChannel(string channel)
{
    await Groups.Add(Context.ConnectionId, channel);
}

Next will be to create the “notify” methods when the visitor changes in the website. The methods will make client calls to the new SPEAK component.

this.Clients.Group(RealTimeConstants.Signalr.Groups.Listeners).onVisitorChanged(visitor);
this.Clients.Group(RealTimeConstants.Signalr.Groups.Listeners).onVisitorHasLeft(visitor);

The onVisitorChanged will be called in SendClientLocationData() method in class RealTimeHub when we get the geodata from the visitor.

The onVisitorHasLeft will be called when the visitors session has ended.

We will also add a method that presents current visitors for the “Listeners”.

public async Task ShowAllVisitors()
{
    IHubContextService hubService = this._hubContextService.Resolve(Context);

    if (!hubService.IsUserAuthenticated())
        return;
        
    await this._realTimeUserRepository.Collection().Find(v => !v.IsToBeDeleted).ForEachAsync((visitor, i) =>
    {
        this.Clients.Group(RealTimeConstants.Signalr.Groups.Listeners).onVisitorChanged(visitor);
    });
}

Create SPEAK component

The SignalR hub is now ready, lets start doing the SPEAK component. First we need to create a SPEAK page(VisitorsInRealtimeLayout):
VisitorsInRealTimeLayout
Here is the VisitorsInRealtimeLayout in “design layout”:
VisitorsInRealTimeLayoutDesign
We will use the map component, GoogleMapSpeak to present the visitors. The SubPageCode(the js file) will do the magic, connecting to SignalR to get visitor data.

To make SignalR work in SPEAK I added its “file paths” to the require.config.

require.config({
    paths: {
        "signalr.core": "/Scripts/jquery.signalR-2.2.0.min",
        "signalr.hubs": "/signalr/hubs?"
    },
    shim: {
        "signalr.core": {
            deps: ["jquery"],
            exports: "$.connection"
        },
        "signalr.hubs": {
            deps: ["signalr.core"]
        }
    }
});


define(["sitecore", "jquery", "signalr.hubs"], function (sitecore, jQuery) {

    var activeInfoWindow = null;

    var visitorsInRealtimePageCode = sitecore.Definitions.App.extend({

        initialized: function () {

            var self = this;

            self.setupRealtime();

        },
        setupRealtime: function () {

            var self = this;

            self.contactIconPath = "/sitecore/api/ao/v1/contacts/{0}/image?w=40&h=40";

            self.connection = jQuery.hubConnection();

            self.realTimeConnector = self.connection.createHubProxy("RealTimeConnector");

            self.realTimeConnector.on("onVisitorChanged", function (data) {

                if (activeInfoWindow != null)
                    activeInfoWindow.close();

                var infoWindow = new google.maps.InfoWindow();

                var visitor = data;

                if (visitor == null) {
                    console.log("No visitors");
                    return;
                }

                var marker =self.GoogleMapSpeak1.addMarkerToMap(visitor.ContactId,
                    visitor.RealTimeMetaData.GeoCoordinates.Coordinates[0],
                    visitor.RealTimeMetaData.GeoCoordinates.Coordinates[1],
                    self.stringFormat(self.contactIconPath, visitor.ContactId));


                infoWindow.setContent(self.renderHtmlContactInfo(visitor.ContactId));

                infoWindow.open(self.GoogleMapSpeak1.get("speakMap"), marker);

                marker.addListener('click', function () {
                    if (activeInfoWindow != null)
                        activeInfoWindow.close();

                    infoWindow.open(self.GoogleMapSpeak1.get("speakMap"), marker);
                });

                activeInfoWindow = infoWindow;
                
                console.log(visitor.ContactId);
                console.log(visitor.RealTimeMetaData.GeoCoordinates.Coordinates);

            });

            self.realTimeConnector.on("onVisitorHasLeft", function (data) {

                var visitor = data;

                if (visitor == null) {
                    console.log("No visitor");
                    return;
                }

                self.GoogleMapSpeak1.removeMarkerFromMap(visitor.ContactId);


                console.log(visitor.ContactId);
                console.log(visitor.RealTimeMetaData.GeoCoordinates.Coordinates);

            });

            self.realTimeConnector.on("onWhoIs", function () {

                self.realTimeConnector.invoke("AuthenticateListener").done(function (result) {

                    if (!result) {
                        console.log("Not authenticated, not allowed to see visitors");
                        return;
                    }

                    self.realTimeConnector.invoke("ShowAllVisitors").done();

                });

            });

            self.connection.start().done();

        },
        renderHtmlContactInfo: function (contactId) {

            var self = this;

            return self.stringFormat("<div><h4>{0}</h4>", contactId) +
                self.stringFormat("<p><a href='/sitecore/client/Applications/ExperienceProfile/contact?cid={0}'>Get detailed info</a></p>", contactId) +
                "</div></div>";

        },
        stringFormat: function () {
            var s = arguments[0];
            for (var i = 0; i < arguments.length - 1; i++) {
                var reg = new RegExp("\\{" + i + "\\}", "gm");
                s = s.replace(reg, arguments[i + 1]);
            }
            return s;
        }
    });

    return visitorsInRealtimePageCode;
});

Let me give you a quick explanation on how the script works. 🙂
When the client is connected to the RealtimeHub(On the server side), the hub will make a client call to the WhoIs method. Right now nothing is done here but in the future we could log/store the “Listeners”. Next the AuthenticateListener method will be called(which is described in the beginning of the post) and when the “Listener” is authenticated it will make a server call to ShowAllVisitors(which is described in the beginning of the post). The ShowAllVisitors method will loop through the visitors and make a client call to onVisitorChanged. Method onVisitorChanged is also called when a new visitor connects.

In onVisitorChanged we will present the visitor on a map by using the GoogleMapSpeak control.

When the visitors session has ended, a client call will be made to onVisitorHasLeft. The visitor will be removed from the map by using the GoogleMapSpeak control.

That’s all for now folks 🙂

Advertisements