Sitecore Geo IP Services (SC 10.2) Updated

Many businesses require to implements GEO IP Services. Business Rules differ between locations. As Developers, Sitecore makes our lives easier by providing a Service that we can use to obtain the location of the website’s visitor.

The IP Geolocation provides information like country, state, city, and every visitor’s registered company name as well. Based on geodata, we can build modules that interact with our visitors and provide accurate information depending on where they are.

Installation and Configuration

Here you will find the official link to activate and set up the Sitecore IP Geolocation: Set up Sitecore IP Geolocation

In your Siecore Solution ensure you add the following Nuget Packages:

  • Sitecore.Kernel
  • Sitecore.Analytics

Solution

Sometimes when you work with lower environments or your local environment Geo IP is not available. I developed the following simple class to get the visitor’s country and city. We can add the next file to setup a city and country code when Sitecore IP Geolocation is not available for your local. I used Sitecore 10.2.


<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/" xmlns:env="http://www.sitecore.net/xmlconfig/env/">
  <sitecore>
    <settings env:require="Local">
      <setting name="Dev.IPGeolocation.IsDevMode" value="true"/>
      <setting name="Dev.IPGeolocation.City" value="Boston"/>
      <setting name="Dev.IPGeolocation.CountryCode" value="US"/>
    </settings>
  </sitecore>
</configuration>

The next class contains a method GetUserGeolocation to get user location information.

Noticed IGeoIPManager is injected on the constructor class.

 public class GeolocationService : IGeolocationService
    {
        private readonly IGeoIpManager _geoIpManager;

        public GeolocationService(IGeoIpManager geoIpManager)
        {
            _geoIpManager = geoIpManager;
        }
        /// <summary>
        /// Get User IP form Request Context.
        /// </summary>
        /// <returns>(string) IP Address</returns>
        private static string GetUserIP()
        {
            var ip = (HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != null
                      && HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != "")
                ? HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]
                : HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
            if (ip.Contains(","))
                ip = ip.Split(',').First().Trim();

            return ip.Trim();
        }

        /// <summary>
        ///   Fetch country from Sitecore IP Geolocation Service check 
        ///   setup at https://doc.sitecore.com/developers/93/sitecore-experience-manager/en/set-up-sitecore-ip-geolocation.html
        /// </summary>
        /// <returns></returns>
        public GeoData GetUserGeolocation()
        {
            GeoData geoData;
            bool IsDevEnviroment = Settings.GetBoolSetting("Dev.IPGeolocation.IsDevMode", false);
            if (!IsDevEnviroment)
            {
                try
                {
                    var result = _geoIpManager.GetGeoIpData(GetUserIP());
                    if (result.Status == GeoIpFetchDataStatus.Fetched &&
                        result.WhoIsInformation != null)
                    {
                        geoData = new GeoData()
                        {
                            City = result.WhoIsInformation.City,
                            CountryCode = result.WhoIsInformation.Country
                        };
                    }
                    else
                    {
                        throw new Exception("GeoIP whoIsInformation is null");
                    }
                }
                catch (Exception e)
                {
                    Log.Error("Error Getting IPAddress", e, typeof(GeolocationService));
                    if (Tracker.Current != null && Tracker.Current.Interaction != null && Tracker.Current.Interaction.GeoData != null)
                    {
                        geoData = new GeoData()
                        {
                            City = Tracker.Current.Interaction?.GeoData?.City,
                            CountryCode = Tracker.Current.Interaction?.GeoData?.Country
                        };

                    }
                    else
                    {
                        Log.Error("IPGeolocation: There is an issue with IP Geolocation Service", typeof(GeolocationService));
                        geoData = new GeoData()
                        {
                            City = null,
                            CountryCode = Settings.GetSetting("LocationEngine.IPGeolocation.CountryCode")
                        };
                    }
                }

            }
            else
            {
                Log.Info("IPGeolocation: Dev Mode is enabled", typeof(GeolocationService));
                geoData = new GeoData()
                {
                    City = Settings.GetSetting("Dev.IPGeolocation.City"),
                    CountryCode = Settings.GetSetting("Dev.IPGeolocation.CountryCode")
                };
            }
            Log.Info($"IPGeolocation: Country Code is {geoData.CountryCode}", typeof(GeolocationService));
            return geoData;
        }
    }

Finally, GeoData is a model class that contains the location information.


 public class GeoData
    {
        public string CountryCode { get; set; }
        public string City { get; set; }
    }

I hope this information will be usefully when you work with Sitecore Geo IP Services.

Read more

Sitecore Forms: ReCaptcha Enterprise Support

Sitecore is a powerful DXP it is desiged for enterprise-level websites. As Marketers you would need to set up your forms and Sitecore provides a solution out-of-the-box to easily create your own forms by drag and drop your form controls like: Text, Buttons, Inputs and customize submit actions. However, your site is vulerable to bot attacks and some companies prefers to use Google’s reCaptcha to prevent bot traffict to their websites.

I’ve been working on a Sitecore forms module, which support the ReCaptcha Enterprise version within Sitecore Forms. If you want to contribute in the module development please refer the following Github Repo:

https://github.com/RobertoArmas/Sitecore-Forms-ReCaptcha

Installation

To make it easily to install you need to add a Nuget Package in your Webiste project.

Install-Package SitecoreModules.Foundation.Forms

Configuration

To setup the module it is required to add Google ReCaptcha Keys Refer to this google docs: https://cloud.google.com/recaptcha-enterprise/docs/keys

Once you installed the nuget package. The installation will add a config file: \Website\App_Config\Include\Foundation\SitecoreModules.Foundation.Forms.ReCaptchaEnterprise.nonprod.config.example

You need to clone it and change it accordingly your environment.

SettingValue
Foundation.Forms.ReCaptchaEnterprise.Api.Urlhttps://www.google.com/recaptcha/enterprise.js?render={0}
Foundation.Forms.ReCaptchaEnterprise.Api.SiteVerifyhttps://recaptchaenterprise.googleapis.com/v1/projects/[PROJECT-ID]/assessments?key={0}
Foundation.Forms.ReCaptchaEnterprise.PublicKeyV3[Public – V3]
Foundation.Forms.ReCaptchaEnterprise.PublicKeyV2[Public – V2]
Foundation.Forms.ReCaptchaEnterprise.ApiKey[Api Key] – This is used for Server Side Validation
Foundation.Forms.ReCaptchaEnterprise.ScoreDefault to 0.5.

Finally, you need to ensure that forms.recaptcha.js is added on standard values to load the js functionality that is required to work, as the following screenshot.

Usage

This module is easy to use. All you need to do is Drag and Drop the control named: “ReCaptcha Enterprise” which is located under Security section.

Then you can configure the module by click on it, then select the mode, it could be:

  • Invisible: Provides a better user experience, reCaptcha v3 is invisible for webiste visitors.
  • Checkbox: Use a challenge if Google detects a bot behaviour.

Finally, save your form. I hope you find this modue useful on your projects.

Read more

Sitecore Hackathon 2022

I’m very happy to participate in Sitecore Hackathon 2022!. I participated with my teammate Daniel Pastor in the team Send Pizza and Coffee and I’m proud of what we archived. This year there were 3 topics for Sitecore Hackathon ideation.

  • Build and e-commerce Minimum Viable Product to sell community t-shirts
  • Extend the Sitecore Command Line (CLI) Plugin
  • Best addition to the Sitecore MVP Site.

Additionally we were thinking in a problematic that business have to facing during the COVID-19. One of the coolest rewards after becoming a Sitecore MVP has always been the Sitecore Swag we receive. However, due to the pandemic and changes from year to year, the delivery process can be a little confusing. We decided to make a “Sitecore MVP Swag tracker” page with some modules where the user could track the delivery status of their care package.

The value of real-time systems came into focus in 2020 as companies responded to the disruptions due to COVID-19. Nowadays, people want to have information at hand efficiently, this creates confidence in technological users, the information must be in real time, the actual generation want the information right now.

This module will display the status of the delivery in real time from an external push service which could come from a mobile app from the delivery service.

Our proposal was focused on the user experience, they could know where the MVP swag package is in the delivery process. Our technology is based in web-sockets using Pusher as key platform to our solution succeed.

Web-sockets provides a channel to broadcast to the users the information about their package tracking with the following events:

  • On Track: Send the deliver man location in real-time, the idea is that each delivery man have a mobile application that sends the geo location.
  • In Order Completed: This event notify to the website’s user that the order is completed and delivered at the door.

Finally, we provide a Sitecore Form which could be use in case that something wrong happens with the delivery, and the people in charge could get in contact.

You’ll find more information about our project in the following link:

https://github.com/Sitecore-Hackathon/2022-Send-Pizza-and-Coffee

Read more

Create your first JSS module with NextJS – Part 1

In Article I’ll show you how to create a module in Sitecore using JSS and NextJS. For who is not familiar with this topics. I explain a little bit in my previous blogpost.

Prepare your environment

Before create a new component, you need to connect your Sitecore website with JSS front end project. The easy solution is using Docker and create a new project based on sitecore.nextjs.gettingstarted template.

If you don’t want to use docker you need to ensure that you have installed Sitecore Headless Services (formally the JSS Server Components).

Create a Json Rendering

Json Rendering let you create a rendering which connects to your React Module. First, you need to create your Json Rendering for this example, I’ll create a "Blue Box" component.

Template: /sitecore/templates/Foundation/JavaScript Services/Json Rendering

In experience editor, to allow content authors the ability to select the new component you can added in the placeholder settings as a normal component in Sitecore.

In your Front End Solution (NextJS Solution), you can add a new file /src/components/BlueBox.tsx, we can create the component as React Functional Component.

To import assets or styles you can use import statement and create your modular style file like [Module-Name].module.css.

In order to support inline-editing through experience editor you can use the Text component which is part of '@sitecore-jss/sitecore-jss-react' library.

Do not forget to start your Front-End server with jss start.

If you want to ensure that Data Source is not empty you can use the withDataSourceCheck() hook

Finally, you can add the rendering in your JSS Application Route Page and you are ready to go. Stay tuned and I will enter in details using Placeholders in JSS Application.

Read more

Getting Started Sitecore JSS with NextJS

JAMStack is a modern way to build website and apps that delivers better performance. JSS enables Sitecore to be a powerful Headless CMS and Next.js gives you the best developer experience to build a hybrid static & server rendering.

In this article, I’ll talk about how to get started on Sitecore JSS and Next.js. For this experiment, I followed the instructions provided by Sitecore in the following link:

Walkthrough: Setting up a development environment with the Sitecore Containers template for Next.js

Once you follow the steps you’ll be able to log in to Sitecore CM and browse the website.

During the initial setup, I found some issues related to Docker configuration and I found this article really useful:

Sitecore 10 on docker – common issues – Error cotidianam (wordpress.com)

If you have the latest Docker Desktop, please disable Use Docker Compose V2.

In order to start up JSS project you should navigate to src\rendering folder which contains the NextJS project.

You can run the following command and then you are ready to go.

jss start

I found Docker containers as an easy way to set up a Sitecore environment you don’t need to worry about to install Solr, create the certificates, etc.

Next.js is the future of web development, it is the most famous framework to build super fast and SEO-efficient JAMStack websites with the following important benefits for me:

  • Faster time to market – make and build an MVP (Minimun Viable Product) is much faster.
  • Performance – NextJS generates an static website that runs on NodeJS. Improved page load speed which means higher conversion rate.
  • Live Reload – Working with those frameworks, I love that development is fast you don’t need to wait to compile, it is a live-editing experience.
  • Fully omnichannel– you don’t need to worried about to build API’s for website or mobile applications. You can reuse some React components for your mobile application if you are using web-based technologies.

In future blog posts, I could prepare tutorials about Sitecore module development. Stay tuned!

Read more

Sitecore CDP Decision Models

Sitecore CDP is a Customer Data Platform, which means that we can store data related to our customers.

This valuable data is very useful for analysis and allows you to make assertive marketing decisions. Sitecore CDP allows you to create Decision Tables or even Decision Template which are programmable.

Here we can see some components added to the Decision Model Canvas, you can find more info https://doc.sitecore.com/cdp/en/users/sitecore-customer-data-platform/using-decision-model-components-in-sitecore-cdp.html

Basically best practices mentioned to begin from bottom to top that makes easily to read. Based on that I could say in this example we have three sections.

  • Model Inputs: Here we can add Input Data like guest data or connect to a Data System API’s.
  • Transform and Extract Data: We can add programable boxes to add some logic to validate data, extract fields like GetPropensity, or Order Related Data like HasOrders from the Guest Object.
  • Decision Table, Knowledge Source: At the very top we have the Decision Table.

Decision Table Overview

Client groups according revenue. Adapted from: Esteban-Talaya y Mondéjar-Jiménez (2017). Kotler and Armstrong (2018).

Here we can implement some Marketing Strategies for example if we have a “Butterfly client” we can give them an offer. or if we have a client which is a “unfamiliar” we can give them a lower offer or even decide to do not invest on them.

Our decision table has inputs and outputs:

  • Input: Propensity, Has Orders.
  • Output: Offers

This presented Decision table is a basic example how you can make automated decisions, and how powerful is this platform for your business. It helps you to maintain a good relationship with your clients.

Read more

Sitecore CDP Register Events

Sitecore CDP is a great platform that allows you to register Customer Data for analysis and build Decision Models to create Customer Experiences.

As a developer our first steps are to capture data, it could be some integrations like visiting a page. In this article I will show you how to send events to Sitecore CDP.

Step 1. Create your Point of Sale

Click on System Settings > Point of Sale then click on Create

Fill the form:

  • Name your Point Of Sale: Your Point of Sale Name.
  • Market: Information about the market.
  • Brand: You can fill with your brand.
  • Language: Target language
  • Timeout (in minutes) : Once an user is inactive to close the browser there is a timeout setting that is useful to understand when the user abandon the session.

Step 2: Setup Javascript Library in your Website


var _boxeverq = _boxeverq || [];

// Define the Boxever settings 
var _boxever_settings = {
  client_key: '[YOUR_CLIENT_KEY]', 
  target: 'https://api.boxever.com/v[YOUR_API_VERSION]', 
  cookie_domain: '[YOUR_WEBSITE_DOMAIN]', 
  web_flow_target:  "https://[YOUR_WEBFLOW_TARGET].cloudfront.net",
  pointOfSale: "[YOUR_POINT_OF_SALE]"
};

(function() {
  var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true;  
  s.src = '[CDN]/boxever-1.4.1.min.js';
  var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x);
})();


Add this script into your website and this will inject Sitecore CDP JavaScript Library.

Step 3: Add Event in your website page.

// Place an anonymous function in the Boxever queue
_boxeverq.push(function() {
  var viewEvent = {
    browser_id: Boxever.getID(),
    channel: "WEB",
    type: "VIEW",
    language: "EN",
    currency: "USD",
    page: "/", // Your page path
    pos: "[YOUR_POINT_OF_SALE]"
  };
   //Add UTM params
    viewEvent = Boxever.addUTMParams(viewEvent);
  // Invoke event create
  // (<event msg>, <callback function>, <format>)
  Boxever.eventCreate(viewEvent, function(data) {}, "json");
});

Step 4: Visit your website

Finally if you visit your website and then go to Sitecore CDP Platform you will see a new event registered for your target user.

Here you can find more information about how to customize an event:

https://doc.sitecore.com/cdp/en/developers/sitecore-customer-data-platform–data-model-2-1/send-a-custom-event-to-sitecore-cdp.html

I hope this article will be helpful to start your Sitecore CDP implementations.

Read more

Sitecore Experience Editor Tips! – Part 2

This article will explain how you can improve the Content Author’s experience. Actually, content authors need to change content or settings (like checkbox, drop list) on data source which probably is not accessible by the Experience Editor´s UI. What content authors usually do in those cases, is open Content Editor and browse the data source and edit it, but at this point content author exits the experience editor.

In order to improve their lives, it´s recommended to use a Custom Experience Button.

You can create an Experience Button from the content editor using the following steps:

  1. Select Core Database
Switch to Core DB
  1. Go to path /sitecore/content/Applications/WebEdit/Custom Experience Buttons

3. You can create a folder with your site name that contains the experience buttons for your module.

  1. Create an item like [Your-Module-Name] with /sitecore/templates/System/WebEdit/Field Editor Button Template.
  • Header: You can fill with “Edit”
  • Icon: Choose an icon, edit.png fits.
  • Fields: Pipe-separated list of field names to be edited by the Field Edtior
  • Tooltip: you can fill with “Edit”.

5. Add your Experience Button in your desired Rendering.

6. Now, in Experience Editor you can see a new Button in the options. If you clicked you will find a form to fill all fields that you declared in the Experience Button.

This will increse the content authors experence using Experience Editor, especially in the fields that are not in-line editing.

Read more

Sitecore Experience Editor Tips! – Part 1

I want to talk about a common problem that you could face when you develop for Experience Editor.

Imagine that you build your module and it requires Datasource attached to the rendering. If you didn´t add any validation in the view just for the experience editor. Content authors probably experience some errors or couldn’t see the module like the following example:

This is a simple controller which generate a model based on RenderingContext.Current.Rendering.Item.

  public class HeadlineAndDescriptionController : Controller
    {
        private readonly IViewPathResolver _viewPathResolver;
        private readonly IModelMapper _modelMapper;

        public HeadlineAndDescriptionController(IViewPathResolver viewPathResolver, IModelMapper modelMapper)
        {
            this._viewPathResolver = viewPathResolver;
            this._modelMapper = modelMapper;
        }

        public ActionResult Index()
        {
            var model = _modelMapper.MapItemToNew<HeadlineAndDescription>(RenderingContext.Current.Rendering.Item);
            return View(this._viewPathResolver.ResolveViewPath(RenderingContext.Current.Rendering.RenderingItem), model);
        }
    }

We are going to add the Rendering on a Sample Page, noticed that the Data Source is empty.

Then if we open the Experience Editor, it generates a bad experience for the content author because we coudn´t find anything on the view in order to associate the desired data source.

Solution:

In your controller you can add [RequireDatasource] filter.

  [RequireDatasource]
        public ActionResult Index()
        {
            var model = _modelMapper.MapItemToNew<HeadlineAndDescription>(RenderingContext.Current.Rendering.Item);
            return View(this._viewPathResolver.ResolveViewPath(RenderingContext.Current.Rendering.RenderingItem), model);
        }

This is a simple filter but makes difference for Content Authors:

 public class RequireDatasource : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (RenderingContext.CurrentOrNull != null &&
                (string.IsNullOrEmpty(RenderingContext.Current.Rendering.DataSource) || RenderingContext.Current.Rendering.Item == null))
            {
                // Check Experience Editor
                if (Context.PageMode.IsExperienceEditorEditing)
                {
                    filterContext.Result = new ContentResult() { Content = @"<p class=""rmc-select-datasource"">[Module: " + RenderingContext.Current.Rendering.RenderingItem.Name + " ("+ filterContext.ActionDescriptor.ActionName+ "): No Datasource Found, Please select Datasource Item]</p>", ContentType = "text/html" };

                }
                else
                {
                    filterContext.Result = new EmptyResult();
                }
            }
        }
    }

Finally, if you observe a module with message : “Module: {ModuleName} (Method) : No Datasource found, Please select Datasource item”. And content authors know that they need to add a Data source.

I hope this will be helpful to improve the experience for your Content Authors while they are editing.

Read more

Sitecore Shared Sessions

In this article, I will talk about a Sitecore Shared Sessions configuration. Many times when you deploy your changes to production environment. You will notice that the application pool was recycled. To avoid this problem, you can configure Redis to store the user’s sessions.

This configuration is pretty simple you will need to follow this walkthrough.

Once you enabled shared sessions using Redis you will notice when you deploy your new releases, the users that are navigating to your website don’t required to enter their credentials again.

But following this approach, as developer you will need to take care about the models that you are building for use as session variables.

[Serializable]
    public class Location
    {
        [RawValueOnly]
        public ID ID { get; set; }
        [RawValueOnly]
        public string CountryCode { get; set; }
        [RawValueOnly]
        public string CountryName { get; set; }

        [RawValueOnly]
        public string City { get; set; }

    }

In the example, Imagine that you want to store a location variable into your session. You would need to create a class but the important thing here is do not forget to include [Serializable].

If you do not serialize your model, once you want to retrieve your session variable website will crash.

I hope this information will be useful for you.

Read more