Surendra Sharma

Surendra Sharma

Search This Blog

Monday, October 9, 2017

Curious case of GatherContent module in Sitecore environment


Sitecore-GatherContent Integration
Sitecore-GatherContent Integration

I was trying to integrate GatherContent module in Sitecore.

By the way GatherContent is a collaboration tool where content is drafted, reviewed, analyzed and approved. After approval one can use this data in Sitecore.

It’s pretty simple module. Just download, install and configured it.
But after configuration, when I click on Test Connection- I was expecting either Pass or Fail result, but unfortunately I received error - "The remote server returned an error: (500) Internal Server Error."

I googled this GatherContent error for but was out of luck. So I tried to fix it with no directions. 

I started my investigation from configuration values in GatherContent item at “/sitecore/system/Modules/GatherContent”. Just to make sure, I filled same configuration values in another Sitecore instance and it was working fine. 

Now I was sure that it was my Sitecore project specific instance issue.

Next I checked Sitecore log files and found this error

Exception: System.Net.WebException
Message: The remote server returned an error: (500) Internal Server Error.
Source: System
   at System.Net.HttpWebRequest.GetResponse()
   at GatherContent.Connector.Website.Commands.TestConnection Command.Run(ClientPipelineArgs args)
  
This error gave me next pointer where I decided to decompile the "GatherContent.Connector.Website" DLL using "JetBrains dotPeek" tool.

I checked the code of Run() method in "GatherContent.Connector.Website.Commands.TestConnection" class as mentioned in above error message.

Below is a code for Run() method

Run() Method Code
Run() Method Code


As highlighted "/api/sitecore/mappings/try" is called by Run() method to check the HTTPStatus code. So this call is my next direction to scrutinize.

When I tried to access "http://sitecoreinstance/api/sitecore/mappings/try" from browser, I received error as "The controller for path '/api/sitecore/mappings/try' was not found or does not implement IController."

API Error
API Error


Till this point it was clear that as GatherContent is unable to find "/api/sitecore/mappings/try", that’s why reporting error "The remote server returned an error: (500)".

Next I decided to define all the controllers routing in Sitecore MVC project used by GatherCOntent.

Before that we need to find all controllers referred by GatherContent. Again our helper tool "JetBrains dotPeek" help here. Open DLL "GatherContent.Connector.WebControllers" and check controllers name in "GatherContent.Connector.WebControllers.Controllers" namespace as

GatherContent Controllers
GatherContent Controllers

Specify all these controller's Routing in your Sitecore MVC webproject Global.asax.cs file as


protected void Application_Start(object sender, EventArgs e)

{
    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
}

public static void RegisterRoutes(RouteCollection routes)
{
            routes.MapRoute(
    "base",
    "api/sitecore/base/{action}/{id}",
    new { controller = "base", id = UrlParameter.Optional }
    );
            routes.MapRoute(
    "droptree",
    "api/sitecore/droptree/{action}/{id}",
    new { controller = "droptree", id = UrlParameter.Optional }
    );

            routes.MapRoute(
    "import",
    "api/sitecore/import/{action}/{id}",
    new { controller = "import", id = UrlParameter.Optional }
    );

            routes.MapRoute(
    "mappings",
    "api/sitecore/mappings/{action}/{id}",
    new { controller = "mappings", id = UrlParameter.Optional }
    );

            routes.MapRoute(
    "update",
    "api/sitecore/update/{action}/{id}",
    new { controller = "update", id = UrlParameter.Optional }
    );

}

Build and publish this project.

Now try to test GatherContent connection and you should get "Success" message

GatherContent Test Connection Success Message
GatherContent Test Connection Success Message
Greattt, if you are reading this line, it means you are able to solve the issue which was not available on Google and GatherContent helpcenter.

I hope you like this Sitecore GatherContent investigation. Stay tuned for more Sitecore related details.

Till that happy Sitecoring :)

Please leave your comments or share this article if it’s useful for you.

Friday, October 6, 2017

Converting WFFM captured details to Sitecore experience contact profile



Some people see data as facts and figures. But it’s more than that, it’s the lifeblood and it contains history. And it’s trying to tell you something. 

So it’s very important for any organization if they are able to capture visitors’ details via some form and convert these visitors’ details into Sitecore contacts.

For this, create WFFM form with basic fields like first name, last name, email id etc.

Apply this WFFM form to any item which can render it on browser as

WFFM Form
WFFM Form

On submit, we can fire custom action on WFFM form. This action saves filled details into Sitecore as contact. Additionally we can also create a cookie with same details for smart tracking of visitors’ activities on any page.

Here is a ready-made code for WFFM custom action

public class CreateMongoContacts : ISaveAction
{
    public ID ActionID { get; set; }
    public string UniqueKey { get; set; }
    public ActionType ActionType { get; private set; }
    public ActionState QueryState(ActionQueryContext queryContext)
    {
        return ActionState.Enabled;
    }

    public void Execute(ID formId, AdaptedResultList adaptedFields, ActionCallContext actionCallContext = null, params object[] data)
    {
        try
        {
            string firstName = adaptedFields.GetValueByFieldID("{E46B75C1-BF08-4A87-9BE7-776BD65F25C4}");
            string lastName = adaptedFields.GetValueByFieldID("{966EDDC4-B907-4DDA-82C7-F3A8853514AF}");
            string emailAddress = adaptedFields.GetValueByFieldID("{709521AA-D63B-430F-8928-DF35FD7238EE}");

            MongoContacts(firstName, lastName, emailAddress);
        }
        catch (Exception ex)
        {
            Log.Error("Execute Error", ex, this);
        }
    }


    private void MongoContacts(string FirstName, string LastName, string EmailAddress)
    {
        try
        {
            string visitorEmail = EmailAddress;
            string visitorName = FirstName;
            Contact contact = Tracker.Current.Contact;
            string contactId = contact.ContactId.ToString();
            //Email Address
            if (Sitecore.Analytics.Tracker.Current.Contact != null)
            {
                var emailFacet = Tracker.Current.Contact.GetFacet<IContactEmailAddresses>("Emails");

                if (!emailFacet.Entries.Contains("Work Email"))
                {
                    IEmailAddress email = emailFacet.Entries.Create("Work Email");
                    email.SmtpAddress = visitorEmail;
                    emailFacet.Preferred = visitorEmail;
                }
                else
                {
                    IElementDictionary<IEmailAddress> entries = emailFacet.Entries;
                    var emailAddress = entries["Work Email"];
                    emailAddress.SmtpAddress = visitorEmail;
                }
            }
            //Personal Information
            if (Sitecore.Analytics.Tracker.Current.Contact != null || visitorName != null || visitorName != "")
            {
                var personalFacet = Tracker.Current.Contact.GetFacet<IContactPersonalInfo>("Personal");
                personalFacet.FirstName = FirstName;
            }

            if (Sitecore.Analytics.Tracker.Current.Contact != null || visitorName != null || visitorName != "")
            {
                var personalFacet = Tracker.Current.Contact.GetFacet<IContactPersonalInfo>("Personal");
                personalFacet.Surname = LastName;
            }

            //Identify a unKnown Visitor
            var identifiers = Sitecore.Analytics.Tracker.Current.Contact.Identifiers;
            if (identifiers.IdentificationLevel == ContactIdentificationLevel.Anonymous)
            {
                Sitecore.Analytics.Tracker.Current.Session.Identify(visitorEmail);
            }
            ContactManager contactManager = (ContactManager)Sitecore.Configuration.Factory.CreateObject("tracking/contactManager", true);
            contactManager.SaveAndReleaseContactToXdb(contact);

            HttpCookie formCookies = new HttpCookie("contactprofile");
            formCookies.Values["ContactId"] = contactId;
            formCookies.Values["EmailId"] = EmailAddress;
            formCookies.Values["FirstName"] = FirstName;
            formCookies.Values["LastName"] = LastName;
            formCookies.Expires = DateTime.Now.AddYears(1);
            HttpContext.Current.Response.Cookies.Remove("contactprofile");
            HttpContext.Current.Response.SetCookie(formCookies);
        }
        catch (Exception ex)
        {
            Log.Error("MongoContacts Error", ex, this);
        }
    }
}


To get this analytics data, login to Sitecore and click on “Experience Profile” icon on dashboard

Experience Profile
Experience Profile


Find contact by entering email id or first name or last name in search box. Click on selected contact.

Note:- You will get contacts only when current session is expired. To expire session forcefully, you can either delete cookies or close browser or restart IIS. Wait for some time or have a coffee break :) .

Contacts in Experience Profile
Contacts in Experience Profile
 
This should open visitor’s profile where voilaaa one can get visitor interaction history data in terms of first name, last name and email id.

Contact in Sitecore
Contact in Sitecore

In conclusion, WFFM form only store user entered data as a single record. But Sitecore experience profile collect all user activities on website. Hence it’s good place to convert any anonymous profile with captured user information for easy identification and tracking.

I hope you like this Sitecore article. Stay tuned for more Sitecore related details.

Till that happy Sitecoring :)

Please leave your comments or share this article if it’s useful for you. Waiting for your feedback.