Development(s) and Reflection(s)
Daily discoveries, observations, frustrations, aha moments in a life of a .Net/Sitecore developer. Read at your own risk.
Friday, December 28, 2018
Lost in Translation - Building Your Language and Site Context from Scratch
This post is somewhat of a sequel to my previous post around multi-site and multi-language setup. When there is only one site and only one language things are pretty straightforward in terms of site context. Once multiple sites are introduced every time we are getting an item in Sitecore and/or building a link we need to make sure we do it within the correct site and correct language. For the most part Sitecore does it out of the box but there are cases when this functionality breaks down.
Here's an example. Suppose we have a multi-level navigation module that we assemble in the following manner:
1. an ajax call is made to the backend
2. backend method gets the item hierarchy from Sitecore
3. backend method returns serialized items as Json
4. in our java script code we generate navigation html with all the data we just got from Sitecore
Sounds easy, right? Not quite. By the time we arrive at step 2 we are stuck with null values for any item we try to get. We have lost our site context.
Let's bring it back! First thing to do is to pass some values from the view along with the ajax call. This could be done multiple ways. Below is just one way of doing so. We will add hidden values that would carry things like current item id, context database and language.
<input type="hidden" id="currentItem" value="@Sitecore.Context.Item.ID" />
<input type="hidden" id="currentDb" value="@Sitecore.Context.Database" />
<input type="hidden" id="currentLanguage" value="@Sitecore.Context.Site.Language" />
Then in the ajax call we need to make sure we pass these values to our backend method. Read the values first:
var currentItem = document.getElementById('currentItem').value;
var currentDb = document.getElementById('currentDb').value;
var currentLan = document.getElementById('currentLanguage').value;
and send them on their way:
var urlString = "yourUrlHere?item=" + currentItem + "&db=" + currentDb + "&lan=" + currentLan;
$.ajax({
url: urlString...... and so on
And now we can rebuild our context and get what we need from the sitecore tree making sure we grab a correct site and a correct language.
public ActionResult GetJson()
{
//read query string params from the url
string currentItem = Request.QueryString["item"];
string currentDb = Request.QueryString["db"];
string currentLan = Request.QueryString["lan"];
// get current database, get current item and its pathvar db = Sitecore.Configuration.Factory.GetDatabase(currentDb);
Item item = db.GetItem(currentItem);
string itemPath = item.Paths.FullPath;
//build sitecore context
SiteInfo site = SiteContextFactory.Sites
.Where(s => s.RootPath != "" & itemPath.StartsWith(s.RootPath, StringComparison.OrdinalIgnoreCase))
.Where(s => s.Language == currentLan)
.OrderByDescending(s => s.RootPath.Length)
.FirstOrDefault();
var website = new SiteContext(site);
// now that we have our context let's throw in some url options that you might need depending on how you want it set up
using (new SiteContextSwitcher(website))
{
var options = LinkManager.GetDefaultUrlOptions();
options.AlwaysIncludeServerUrl = true; // could be false, depends on requirements
options.Site = new SiteContext(site);
options.SiteResolving = true;
options.LanguageEmbedding = LanguageEmbedding.Never; // could be Always or As Needed depending on requirements
// from here on we can get our items in the proper context and language and build links using the url options we just built. And of course serialize it all and return as json.
}
}
Friday, December 14, 2018
Sustainable Growth: Going Hybrid for a Multi-site and Multi-lingual Sitecore setup
As a website evolves and its audience grows making your content available in multiple languages becomes a top priority. The most common way to achieve this goal is to use Sitecore's out of the box language versioning functionality where the tree is shared and the same item in can have translated versions. But what if it's not only your content that differs between various country sites but also presentation? What if your sites intended for various countries are independent from each other and possibly share some components but not all? What you can do is set up a site in such a way that tree is split between countries meaning each site has its own site definition with it's own hierarchy.
Now let's consider a more sophisticated arrangement where not only we have several country sites but each site needs to have content in multiple languages. Also, country sites need to be independent from one another and have their own site settings and their own hierarchy. In addition to these requirements we are also asked to maintain the same host name for all our global sites.
What are they thinking? They are asking for the moon! Not really. What our stakeholders are asking is a hybrid approach that will allows us to combine shared tree with split tree setup.
Let's walk through an example. Our company XYZ Incorporated is going through an expansion and we are building a site for a global audience. For now let's add a Japanese site keeping in mind that we plan to have content for Japan in two languages: Japanese and English. Obviously, the English version of the Japanese site would be different from the English version of our US site, therefore we cannot reuse the US node as a starting point.
In order to accommodate this we have to create a custom culture for English in Japan. You can install it on your system by using C# code or run a Powershell script for example. Once it's available we can add a new language under System Languages node in sitecore using our new culture.
What you see highlighted in red is English (Japan). It can be represented with /en-us in our url.
Our next step is to add site definitions for each country (familiar task) but wait, because we need multiple languages for multiple countries we need to use language-culture to determine a site. What do I mean?
We create two site definitions for each of our language-cultures. In other words we would have a xyz.jp site and xyz.jp.en site both using the same host name with either /ja-jp or /en-jp in the url to indicate a language. We need to make sure we add virtualFolder and physicalFolder to our site definitions using language-culture combination. But also indicate a language separately.
I know, it's getting complicated but believe me in the end the XYZ corporation will have a beautiful and versatile site their global audience would love!
Let's continue. We created nodes, we added site definitions for each language in each country. Are we done? Almost. Now we need to add a config patch file and set AlwaysStripLanguage setting to false.
Now we are done and the content team can start building pages for our brand new Japanese site and use content in Japanese as well as English languages.
Tuesday, October 30, 2018
What to look forward to with Sitecore 9.1
We all heard about it. We all read about it. We are all excited about it! No, I am not talking about a reason why Ariana Grande broke up with her boyfriend. I am talking about a long awaited Sitecore 9.1’s SIF overhaul! Is it going to be a one-click install? No. But close. SIF 2.0 will install most of the required prerequisites automatically. Imagine that!
SIF 2.0 is only one of the features Sitecore community has been waiting for. Data
Exchange Framework, even though not a brand-new addition, has seen expansion and improvements between earlier versions and 9.1. For instance, integration with Salesforce Marketing Cloud will support bi-directional exchange of experience data such as persona information and click events. This data can now be used, for example, to trigger email campaigns within Salesforce Marketing Cloud.
Sitecore headless solves a major problem for Front End developers. They can now develop and deploy their code to any platform in a headless configuration using their own languages and frameworks of choices. Their solution can be fully disconnected from Sitecore and be self-contained which allows easy management and deployment. Front End developers can have access to pure content without all the overhead that comes with a Sitecore installation. API only approach to web content management is utilized in Commerce where services are supported through the API and delivered vis JSS.
Universal Tracker is a new player in the headless innovation. It is a tracking service based on Web/Rest API technology allowing to build headless applications without sacrificing Sitecore’s personalization, analytics, and A/B testing features.
If you are looking for a quicker way to launch a Sitecore website you are of course familiar with Sitecore
Experience Accelerator or SXA. In 9.1 SXA offers WCAG 2.0 compliant websites creation along with wireframes and gray scale modes.
In Sitecore 9.1 authentication has had a major refactor. All authentication in Sitecore 9.1 is handled by a separate standalone .NET Core application. This .NET core app is based on the IdentityServer4 framework and supports Single Sign-On with OpenID.
9.1 brings expansion of deployment
options for your xDB. Deploy it on Microsoft SQL Server, Microsoft SQL Azure as well as MongoDB if you are on the schema-less side.
Various Sitecore
Forms enhancements also became available with 9.1 For example, you can add conditions to form elements. Which means you can create a conditional logic based on user’s inputs on the form and hide/show components based on user’s interactions.
Last but not least is long-awaited Machine Learning. Architected via integration with Microsoft Machine Learning Server it allows flexible integration with other ML engines and algorithms such as Salesforce Marketing Cloud, Einstein, or IBM Watson. One of the latest features in Sitecore Machine Learning is Content Tagging that creates information taxonomies used in automated personalization recommendations.
Sitecore 9.1 also delivers miscellaneous improvements in Content Search, Core architectural changes, scalability and infrastructure improvements. So much to look forward to!
Sitecore 9.1 also delivers miscellaneous improvements in Content Search, Core architectural changes, scalability and infrastructure improvements. So much to look forward to!
Wednesday, March 14, 2018
Sitecore + Async != Nightmare
This is a sequel to my previous blog post on controller renderings in Sitecore and MVC views (http://blog.olgakogan.net/2016/11/integrating-isolated-mvc-app-with-sitecore.html). My next challenge was to make async calls. The key to this functionality is registering each route involved in async calls separately. Let's look at all the pieces of the puzzle.
In my view I have a label which displays my email. Next to it there's a button that opens up a text box where you can enter a new email.
In my 'Account' controller I have a method called 'UpdateEmail' that is reading a query string parameter called 'email'. This method records the new email in the database.
Upon a click of the Save button a block of javascript code invokes this method asynchronously:
When testing I was getting a 404 error which was not surprising because with Sitecore I need to have a navigable item with a controller rendering on it that points to my account controller and the 'UpdateEmail' method.
I tried registering this route individually and it worked! In the RegisterRoutes class, inside Register method I added the following:
I think this is a small price to pay for having a way to do async calls with sitecore: register each route involved in those calls separately.
In my view I have a label which displays my email. Next to it there's a button that opens up a text box where you can enter a new email.
In my 'Account' controller I have a method called 'UpdateEmail' that is reading a query string parameter called 'email'. This method records the new email in the database.
Upon a click of the Save button a block of javascript code invokes this method asynchronously:
var urlString = "/account/updateemail/?email=" + model.Email;
$.getJSON(urlString, function (data)
{
// Do your stuff here such as hide the div
// with the text box and Save and Cancel buttons.
}
{
// Do your stuff here such as hide the div
// with the text box and Save and Cancel buttons.
}
routes.MapRoute("AccountUpdateEmail", "account/updateemail", new {controller = "Account", action = "UpdateEmail"});
Thursday, November 23, 2017
Integrating an isolated MVC app with a Sitecore site.
I was working on an MVC app that was later intended to be integrated into a sitecore site. The latter step kept being postponed partially because it seemed to have many unknowns. The advantage I had was using Team Development for Sitecore which allows leveraging source control and significantly simplifies deployment. Once I started I realized it was less complicated than I expected. Here are the steps I followed for this proof of concept setup:
1. Set up a blank Sitecore instance (in my case it was 8.1 rev. 160302 ).
1. Set up a blank Sitecore instance (in my case it was 8.1 rev. 160302 ).
2. Create an item in sitecore tree that you intend to be navigable.
3. This step is optional. In Visual Studio remove Debug and Release config files from your project.
4. Set the Web.config file's Build Action property to 'None' and Copy to Output Directory to 'Do not copy'.
5. Add a TDS project to your solution and set up it's properties to build out to your output directory (webroot).
6. Disable default route in RouteConfig
7. Add references to Sitecore.Kernel and Sitecore.MVC to your project
8. Create an MVC layout (.cshtml file). In this layout add a placeholder.
9. Make a controller rendering that points to a controller and action method
10. In presentation details add this new rendering you just created to the placeholder in your MVC layout. In my case it was 'body'.
11. Publish your site and navigate to your 'page' (navigable item you made in step 2).
There are a couple of ways to check that your setup is truly working and you have the ability to use sitecore fully.
1. Check that you have access to Sitecore context by accessing for instance an item name. In any of your models add a member and then assign a sitecore items' name to it.
MyAccountModel model = new MyAccountModel()
{
ItemName = Sitecore.Context.Item.Name
};
Then in your view display that information:
<h1>You navigated to page: @Model.ItemName</h1>
2. Make your rendering cacheable in sitecore and add a time counter to your view:
<h1>@DateTime.Now.Ticks</h1>
As you refresh your page the count should not change. Then set the rendering's Vary By Query String to true and navigate to your item with any query string parameter. Your counter should update.
You are done! Your MVC application is fully integrated into sitecore.
3. This step is optional. In Visual Studio remove Debug and Release config files from your project.
4. Set the Web.config file's Build Action property to 'None' and Copy to Output Directory to 'Do not copy'.
5. Add a TDS project to your solution and set up it's properties to build out to your output directory (webroot).
6. Disable default route in RouteConfig
7. Add references to Sitecore.Kernel and Sitecore.MVC to your project
8. Create an MVC layout (.cshtml file). In this layout add a placeholder.
9. Make a controller rendering that points to a controller and action method
10. In presentation details add this new rendering you just created to the placeholder in your MVC layout. In my case it was 'body'.
11. Publish your site and navigate to your 'page' (navigable item you made in step 2).
There are a couple of ways to check that your setup is truly working and you have the ability to use sitecore fully.
1. Check that you have access to Sitecore context by accessing for instance an item name. In any of your models add a member and then assign a sitecore items' name to it.
MyAccountModel model = new MyAccountModel()
{
ItemName = Sitecore.Context.Item.Name
};
Then in your view display that information:
<h1>You navigated to page: @Model.ItemName</h1>
2. Make your rendering cacheable in sitecore and add a time counter to your view:
<h1>@DateTime.Now.Ticks</h1>
As you refresh your page the count should not change. Then set the rendering's Vary By Query String to true and navigate to your item with any query string parameter. Your counter should update.
You are done! Your MVC application is fully integrated into sitecore.
Thursday, March 17, 2016
Hedgehog Persona Tool Part VI
(Squared Euclidean) Distance and going the extra mile to get it.
The yellow one almost maxes out on Analytics but quite low on Customer Relations and Campaigns and Targeting keys. The pink one is obviously very different: high on Innovation and Customer Relations and very low on Technology and Analytics. If we were to find out how we can measure this difference we would use Sitecore's GetDistance method from Sitecore.Analytics.Patterns namespace in Sitecore.Analytics assembly. I decompiled this method and this is what it looks like:
Pattern's Space.Dimensions is a number of Profile Keys. In our case it would be 6. Following the logic we see that each Profile Key value is compared between the two patterns and this result is multiplied by itself. Then added to a cumulative value which is our distance.
Here's a snapshot from watching two keys and how they compare to each other. The first key ([0]) happens to be Analytics.
Here's a great blog post on visualizing pattern cards by Adam Conn. You can download his module and watch the current visit's pattern change before your eyes!
Hedgehog Persona Tool takes this process of pattern matching to a whole new level. As previously discussed, Sitecore patterns are preset, and may not accurately reflect visitor behavior. You can compare this process to a conference planning: you expect visitors from certain states and prepare badges (Pattern Cards) for them with state names. But as visitors come you might realize that some of them arrive from totally different areas and your badges (Pattern Cards) are irrelevant. The Hedgehog Persona Tool allows us to see the accurate visitor behavior in real time. Each visit stored in Mongo is turned into a pattern, matched against other patterns and then assigned to a cluster with similar patterns for a much more detailed and accurate analysis.
If you'd like more information about persona development and marketing strategy, reach out to Hedgehog Digital Marketing Innovation Team.
In my previous blog post I touched on the concept of Squared Euclidean Distance between patterns. So what is this Distance and why do we care? It is the numerical representation of a difference between two Sitecore patterns. Going back to an example from Persona Tool let's look at several patterns:
Here's a snapshot from watching two keys and how they compare to each other. The first key ([0]) happens to be Analytics.
Hedgehog Persona Tool takes this process of pattern matching to a whole new level. As previously discussed, Sitecore patterns are preset, and may not accurately reflect visitor behavior. You can compare this process to a conference planning: you expect visitors from certain states and prepare badges (Pattern Cards) for them with state names. But as visitors come you might realize that some of them arrive from totally different areas and your badges (Pattern Cards) are irrelevant. The Hedgehog Persona Tool allows us to see the accurate visitor behavior in real time. Each visit stored in Mongo is turned into a pattern, matched against other patterns and then assigned to a cluster with similar patterns for a much more detailed and accurate analysis.
If you'd like more information about persona development and marketing strategy, reach out to Hedgehog Digital Marketing Innovation Team.
Thursday, March 3, 2016
Hedgehog Persona Tool Part V
Look who's getting engaged!
The Persona Tool helps us know our visitors better, and the evolution of the Persona Tool brings more and more new features. The one I want to focus on this time is the exceptional, extraordinary and essential Engagement Value (EV) per cluster of visits.
Once we pick one of our Sitecore profiles (i.e. Audience Segment) in the dropdown we would see all our visits with Audience Segment data combined into one cluster that represents an average visit to our site. Let's take it one step further and reduce our distance. I know: what on earth is distance? For our current purposes, a distance here is a difference between one pattern and another. Take a look at the radar charts in the image below and observe that the three figures are easy to differentiate. The difference between each one is represented as a numerical value in Sitecore: that is our 'distance'.
There is plenty of information in Engagement Value as a concept out there on the web. For example, I really like Martina Welander's post on it Sitecore's marketing features for developers: What is 'Engagement Value'? Essentially we set up Goals in Sitecore and assign a value to each goal. All those goals might have various values from low to high. Then we associate certain content or events (such as downloading a document) with goals. As users achieve them (i.e. download those documents) Sitecore stores that value (Engagement Value) inside Interactions collection in Mongo for each visit.
In our specific example we can see that the largest cluster (the one contaning a whopping 61% of our visits) has the largest average Engagement Value (17.56). It is not always the case. Depending on your data you might discover that a small slice of your site's visitors is actually the most 'engaged'!
Wouldn't that be great to know who they are in terms of their behavior on the site? What content do they view? What is their pattern? Well, this is what this cluster's radar chart is all about: it shows us the pattern of our most active (or 'engaged') visitors.
Subscribe to:
Posts (Atom)