Wednesday 27 September 2017

Sitecore Powershell - New-Item with specific ID (ForceId not working)

I am writing some Sitecore Powershell scripts to migrate content from an old site to a new re-designed site with different set of templates.

As you would imagine, we should keep the old item IDs instead of creating new IDs in case we also want to maintain the references between the items (if any). If you have used the New-Item cmdlet before, you might have noticed that it supports a parameter: -ForceId.

I noticed that most of the items I created with the ForceId parameter set are having the same ID as the original item however some of them are not. And then I realised that those items were created using a branch template.

While Sitecore PSE does use the ItemManager.AddFromTemplate() method that has several overloads to support creating item with a specific ID, it's logic only works for normal template and not for the branch template. In the current version (4.6) of PSE, it has logic below (shortened to show the point):

// iD is the ID passed to the ForceId parameter

if (type != TemplateItemType.Template)
{
    // Logic to check if it is branch template removed for simplicity
    newItem = parentItem.Add(name, fromPath);
}
else
{
    newItem = ((iD != ID.Null) ? ItemManager.AddFromTemplate(name, fromPath.ID, item, iD) : ItemManager.AddFromTemplate(name, fromPath.ID, item));
}
As you can see from above code, when it is using branch template, it does not actually use the ID we passed to the ForceId parameter.

To make it work, I have written a function to help, lets call it New-ItemCustom:



function New-ItemCustom
{
    <#
        .SYNOPSIS
            Create a new Sitecore item based on template/branch ID. Specify
            the NewItemID to create the item with the specific ID.
    #>
    [CmdletBinding()]
    param 
    (
        [Parameter(Mandatory=$True)]
        $Name,
        [Parameter(Mandatory=$True)]
        [string]$TemplateID,
        [Parameter(Mandatory=$True)]
        $ParentItem,
        [string]$NewItemID = ""
    )
    
    $scTemplateID = New-Object -TypeName "Sitecore.Data.ID" -ArgumentList $TemplateID
    $newItem = $null
    
    if ($NewItemID -ne "") 
    {
        $scItemID = New-Object -TypeName "Sitecore.Data.ID" -ArgumentList $NewItemID
        $newItem = [Sitecore.Data.Managers.ItemManager]::AddFromTemplate($Name, $scTemplateID, $ParentItem, $scItemID)
    }
    else 
    {
        $newItem = [Sitecore.Data.Managers.ItemManager]::AddFromTemplate($Name, $scTemplateID, $ParentItem)
    }
    
    return $newItem
}


Usage:


New-ItemCustom -Name "new item name" -TemplateID "template/branch ID" -ParentItem (Get-Item -Path: .) -NewItemID "Sitecore Item ID here"



Hope this helps.

Wednesday 30 August 2017

Sitecore FXM, Profile and Pattern Matching

I was playing around with FXM and I was able to create placeholders in the non-Sitecore site and put Sitecore content into those placeholders, which is cool!

However it was not so lucky when setting up profiles at the beginning. For some reason (still unknown) the profile (and its keys, profile/pattern cards) I set up the first time did not with with the page filters and nothing got recorded in MongoDB's intractions collection, and of course in Experience Analytics only the visits are shown.

I then wrote a custom pipeline to loop through the profile cards associated with the matched filter and manually score them. This worked, the profile and the profile keys/values were recorded against the interaction however no pattern was recorded.

After studying the code, I realised that what I did in my custom pipeline was already catered for out-of-the-box so I was confused and consulted Sitecore support and they also could not figure out why so I was suggested to create another profile from scratch and remove the custom pipeline.

Surprisingly this worked! I could see profile values as well as pattern card recorded under the interaction and the matched pattern shows in Experience Analytics.

However soon I noticed that there is nothing displayed in Experience Profile. I used to see bunch anonymous records there in another version of Sitecore 8 but I cannot see anything here. Why?

I am using Sitecore 8.2 Update 4 and if you are not as careless as me and are paying attention to the release notes, you would notice that in the release notes of Sitecore 8.2 Update 3 there is a change to how the anonymous contacts are indexed:

    
<settings>

  
  <setting name="ContentSearch.Analytics.IndexAnonymousContacts" value="false"/>
</settings>
Note the comments and the default value was changed from true (prior to 8.2.3) to false. For sites that do not have WFFM (that identifies the contact when needed) nor custom contact identification process you might prefer this to be turned on in order to display the behaviour of the anonymous users. However if you do have the mechanism to identify the contact then this is suggested to be turned off to reduce impact on performance.

So by changing the value to true and make some interactions with the site, you will see the anonymous contacts listed in Experience Profile. Note that this only works for new intractions; if you want to see old data (before this was set to true) you will need to rebuild your reporting database.

I thought I would just blog this in case someone was struggling with this too!

Wednesday 16 August 2017

Sitecore Tracking Field Parsing - the Native Way

When we assign profile cards, goals or attributes (outcomes, events, etc) to a page content item, Sitecore stores the information in the Tracking (actual name __tracking) field under the Advanced section, as XML which looks like below (I am using the Sitecore Helix Utilities demo site for example):


Now if we switch to the raw value, we will see it is stored in XML, which I have formatted it for ease of viewing:
  
<tracking>
   <profile id="{D016ADEF-7737-4289-A85F-FE0055F49C8E}" name="Utilities Persona" presets="evan the eco-owner|100||">
      <key name="Sign-up for service" value="1" />
      <key name="Solar_Enviro Programs" value="5" />
      <key name="Budget_Savings Offers" value="4" />
      <key name="H of H_Community" value="3" />
   </profile>
</tracking>

There might be some cases where you want to parse this and get the information out of it and I have seen many developers parsing it by loading the value into XmlDocument and then read its nodes. This works fine however we would imagine Sitecore should have already had something doing this?

Answer is yes of course :) So, we can use the following code to get a strongly-typed object that represents this information.

// using Sitecore.Analytics.Data;

var pageItem = Sitecore.Context.Item;
var trackingField = new TrackingField(pageItem.Fields["__Tracking"]);

// Loop through all profiles (if multiple profile cards are assigned)
foreach (var profile in trackingField.Profiles)
{
    // Loop through all profile keys for current profile.
    // Here we want only the ones that has value greater than 0, for example.
    foreach (var k in profile.Keys.Where(o=> o.Value > 0))
    {
        // Do something
    }
}

The above example shows how to read profiles. If you have stuff like campaigns, goals, events that you need, in the TrackingField class there are collections for them as well:


Note that goals are same as events so you will find it under the Events IEnumerable.

Hope this helps!

Friday 30 June 2017

Find MongoDB contact and interactions using SC_ANALYTICS_GLOBAL_COOKIE

These days I was working on resolving some issues with Sitecore Analytics and need to find the corresponding contact stored in the Contacts collection database in MongoDB easily.

[Update] 15 Aug 2017: added query output for finding all interactions with provided contact ID.

Sitecore generates the analytics cookie value (in GUID) and stores it in the SC_ANALYTICS_GLOBAL_COOKIE cookie which looks like 95f08e09d56348c9bc285080e91be04b|False. That letters/digits combination is a short, lowered version of GUID which is an important identifier for the contact record that gets stored in MongoDB.

However when you try to find this in MongoDB (using Robomongo which is now Robo 3T, make sure to check the latest version), you will not find it because some parts of the GUID are shifted (i.e. first 8 characters, followed by next two groups of 4 letters).

To make things easier, you could run a C# Program query in LINQPad by pasting the below code in, or visit my fiddle page.

    
    void Main()
    {
       // Value from SC_ANALYTICS_GLOBAL_COOKIE (you can copy the |True or |False part which will be trimed too)
       var guidFromCookie = "95f08e09d56348c9bc285080e91be04b|False".Split(new char[] {'|'})[0]; 
       var mongoDbLUUID = string.Format("{0}-{1}-{2}-{3}-{4}",
                              Flip(guidFromCookie.Substring(0,8)), 
                              Flip(guidFromCookie.Substring(8,4)), 
                              Flip(guidFromCookie.Substring(12,4)),
                              guidFromCookie.Substring(16, 4),
                              guidFromCookie.Substring(20,12));
  
       Console.WriteLine("MongoDB query:");
       Console.WriteLine("db.Contacts.find({" + string.Format("_id:LUUID('{0}')", mongoDbLUUID) + "})");
       Console.WriteLine("All interactions with this contact:");
       Console.WriteLine("db.Interactions.find({" + string.Format("ContactId:LUUID('{0}')", mongoDbLUUID) + "})");
    }

    string Flip(string s)
    {
       var output = "";
       for (var i=s.Length-2; i>=0; i-=2)
       {
           output += s.Substring(i, 2);
       }
 
       return output;
    }

The code will output a query which you can paste directly to Robo 3T.

Thursday 11 May 2017

CSS rule that could break your Sitecore 8.2 Experience Editor

Today when I was using the Experience Editor on my Sitecore 8.2 Update 3 site and I found that nothing of the following works:

  • Assigning renderings to placeholders
  • Some of the buttons in the ribbon does nothing when clicked
  • Etc, etc
I have checked Chrome console logs... no javascript errors, no 500 or 404 under network tab. Checked Sitecore logs... no exceptions were thrown.

What could it be? I have started scratching my head...

Checking here, checking there, asking my colleague and starting removing whatever is on the page one by one.

At the end we figured out that there is one CSS rule:

body > iframe { display: none;}

We removed it and the Experience Editor starts working again. Why?

This rule effectively hide any iframes that are directly under the body tag, whether they are created dynamically by external javascripts or by yourself. Unfortunately Sitecore Experience Editor also has an iframe directly under body for serving the modal dialog. Therefore when this rule is present, the functionality behind the buttons (that pop up a modal dialog) actually works but the dialog is not showing.

We have only tested this on Sitecore 8.2 so not sure if it affects earlier 8.x versions. The iframe used by Sitecore looks like:

  
  <iframe allowtransparency="true" 
         frameborder="0" 
         id="jqueryModalDialogsFrame" 
         src="/sitecore/shell/Controls/JqueryModalDialogs.html">
      ...
  </iframe>

So once we know this, we can get around this by excluding this from the CSS rule:

body > iframe:not(#jqueryModalDialogsFrame) { display: none;}
Hopefully this helps!

Monday 1 May 2017

Sitecore Habitat and Fortis

If you don't know what is Habitat yet, please have a look at its Github: https://github.com/Sitecore/Habitat.

By default, Habitat does not use any mapping tool (such as Glass Mapper, Fortis, or the emerging Synthesis) so keep it simple however it does not mean that Sitecore doesn't prefer using one of those.

I have created a fork of the original Habitat solution and made it Fortis-enabled. A Fortis.Foundation module is added to the foundation layer which is responsible for the registration of Fortis and the code-generation of the Fortis model based on Unicorn.

You can find the repo here: https://github.com/codingdennis/Habitat/tree/feature/fortis-integration.

[Updated 2017-05-03] I have also updated this Habitat to support Sitecore 8.2 Update 3, check https://github.com/codingdennis/Habitat/tree/feature/fortis-integration-sc82u3 if required.

Happy Habitating!

Wednesday 29 March 2017

Sitecore SIM 1.5

There were heaps of development work undergoing since the last release 1.4 Update-3 about a year ago. Alen is in the process of merging of everything from develop to master and we should expect version 1.5 to be released soon which I was lucky enough to also contribute a small portion to it, which allows prefixing the SQL database names so multiple databases could be created for different instances.

Keep an eye on the SIM's GitHub for any updates. You can also find it in Sitecore Marketplace.