Sitecore ECM 2.1: The target device layout does not include ‘Processes Personalization Tokens’ sublayout og ‘Target item’ rendering

When using Sitecore Email Campaign Manager, you might encounter the following error:

The target device layout does not include ‘Process Personalization Tokens’ sublayout and ‘Target Item’ rendering, you may encounter this error for every variant in this message, if you have admin right to fix variant ‘A’, please click here to start the Layout dialog.

ECM Error

ECM Error

The error does not only occur if the rendering and the sublayout is missing, but also if they are placed imporperly:

  • The “Target Item” render needs to be the first rendering.
  • The “Process Personalization Tokens” needs to be the last rendering.
Peoper placement of the 2 renderings

Peoper placement of the 2 renderings

To do this you must place the Process Persinalization Tokens sublayout in a non-editable placeholder. This placeholder must be placed at the bottom of the control stack, as described in the image above.

Furthermore, do not use __Standard Values. ECM cannot find the Target Item and Process Personalization Tokens controls if they are located in the __Standard Values of the message root item.

You must copy all the controls to the actual item and delete the controls from the layout field of the __Standard Values. If you have added controls to the item with the page editor you will need to merge the layouts from __Standard Values to the item. See this arcticle on how to merge layout details.

 

Posted in Sitecore 6, Sitecore 7 | Tagged , , , , | Leave a comment

Merge Sitecore layout details using XmlDeltas

One of the features that Sitecore introduced somewhere in the Sitecore 6.x range is “Layout Deltas“,  the possibility to merge an item’s layout details with the layout details on the item’s __Standard Values template.

It means that you can add sublayouts to the __Standard Values, and still modify the sublayouts on the actual item, without overwriting __Standard Values.

Because instead of copying all the layout details from the __Standard Values, Sitecore will only keep a difference list on the item, not the entire layout details. Sitecore then uses the XmlDeltas field type to merge the two.

If you look at the raw values in Sitecore, you will see that in the “Layout” field of your __Standard values, the contents look like this:

<r xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
  <d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}" l="{45D794F6-9DBB-4DBE-9826-606456562FB4}">
    <r id="{153F8C66-033E-456A-9C5F-A98D587C7A7E}" ph="" uid="{DE3402CB-9E79-4539-8C54-1DA8FB8D3733}" />
    <r ds="" id="{69EB7A6D-ACEF-4D06-A923-804918AA245E}" par="" ph="NewsletterHeadSection" uid="{CECCDC8D-3AF8-448D-A527-F8471C139FE9}" />
    <r id="{424A40CD-CAF3-45D9-87BA-CBE88C895A64}" ph="" uid="{48640574-E4FB-433B-97B4-0B202D609CB2}" />
    <r ds="" id="{A5975387-6B3A-4A43-BDF5-2A422057E518}" par="" ph="NewsletterBody" uid="{56CA9222-D31E-400A-B697-B620CF431DFF}" />
    <r id="{E429A2F3-DCFC-416E-ACB1-6BB7DE70C846}" ph="" uid="{7DBE3A38-12AD-42B7-8115-F50121D38B84}" />
    <r ds="" id="{175839E3-AEF4-4830-B679-0F51B90B438E}" par="" ph="NewsletterFooter" uid="{DA53887E-B21F-4C8B-A7D7-C3770B79DDF3}" />
  </d>
</r>

But the “Layout” field of the actual item look like this:

<r xmlns:p="p" xmlns:s="s" p:p="1">
  <d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}">
    <r uid="{7B831873-DE0D-46AA-AA38-9B5A72A3A1B7}" p:before="r[@uid='{7DBE3A38-12AD-42B7-8115-F50121D38B84}']" s:ds="{C57FBE62-5181-49B8-BF6A-E7CEE7BED209}" s:id="{7CCB9656-AE33-4609-92F0-1FDC5632BB8B}" s:ph="newsletterbody" />
  </d>
</r>

Notice the p:before attribue in the last XML? These are the attributes telling Sitecore how to apply the changes to the finished layout details.

You can do the merge yourself using the XmlDeltas field. This example merges the __Standard values onto the item itself:

using Sitecore.Data;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;

/// <summary>
/// Merge layouts
/// </summary>
/// <param name="destinationItem">Item that receives the merged layouts</param>
/// <param name="standardItem">Item to merge from (the Standard Values usually)</param>
private string MergeLayouts(Item destinationItem, Item standardItem)
{
  LayoutField layoutDestinationItem = destinationItem.Fields[Sitecore.FieldIDs.LayoutField];
  LayoutField layoutStandardItem = standardItem.Fields[Sitecore.FieldIDs.LayoutField];
  string deltaLayout = XmlDeltas.ApplyDelta(layoutDestinationItem.Value, layoutStandardItem.Value);
  destinationItem.Editing.BeginEdit();
  try
  {
    destinationItem.Fields[Sitecore.FieldIDs.LayoutField].Value = deltaLayout;
    destinationItem.Editing.EndEdit();
    return deltaLayout;
  }
  catch (Exception ex)
  {
    destinationItem.Editing.CancelEdit();
    throw new Exception("Updating '" + destinationItem.Paths.FullPath + "' failed: " + ex.ToString());
  }
}

It is the XmlDeltas.ApplyDelta that executes the merging.

Please notice that if you merge from __Standard Values, and later modify the item that was merges, Sitecore will identify the merge, and “unmerge” your merged item. It does so using the XmlDeltas.GetDelta() method:

string deltaLayout = XmlDeltas.GetDelta(layoutDestinationItem.Value, layoutStandardItem.Value);

MORE INFORMATION:

Posted in .net, c#, Sitecore 6, Sitecore 7 | Tagged , , , | 2 Comments

Avoid the use of Sitecore.Context.Item

Or: How Sitecore DMS and Personalization killed the Sitecore.Context.Item. This post is about how to properly ensure that you get data from the correct item when you develop websites that can be personalized.

The Sitecore.Context.Item returns the current item, i.e. the item that corresponds with the URL that the user has entered. Old Sitecore developers like me has been used to adding the fields of a page to the current item, and then accessing these fields directly using the sc:FieldRenderer:

<%@ Register TagPrefix="sc" 
    Namespace="Sitecore.Web.UI.WebControls" 
    Assembly="Sitecore.Kernel" %>

<sc:FieldRenderer ID="MyField" runat="server" 
FieldName="NameOfMyField"  />

This approach is no longer a good approach, as the the customer have been given access to DMS and personalization. Now you no longer know if the content that the usercontrol need, is part of the current item, or another item.

Personalize the Component

Personalize the Component

The customer can now acces any component he likes, and use personalization to alter the data source (or even the component itself). This breaks the pattern of assuming that content is part of the current item.

So what can we do?

Before we grab any field, we need to go through these steps:

  • Check the datasource of the current sublayout
  • If the datasource is set, use the item from the datasource
  • If not, use the current item

These 2 extension methods ease the process:

namespace SitecoreExtensions
{
  public static class SublayoutExtensions
  {
    public static Item GetDataSourceItem(this Sublayout sublayout)
    {
      string dataSource = sublayout.DataSource;
      if (string.IsNullOrEmpty(dataSource))
        return (Item) null;
      return Context.Database.GetItem(dataSource);
    }
  }

  public static class UserControlExtensions
  {
    public static Item GetDataSourceItem(this UserControl control)
    {
      Sublayout sublayout = control.Parent as Sublayout;
      return sublayout == null ? (Item) null : SublayoutExtensions.GetDataSourceItem(sublayout);
    }

    public static Item GetDataSourceOrContextItem(this UserControl control)
    {
      return UserControlExtensions.GetDataSourceItem(control) ?? Context.Item;
    }
  }
}

The SublayoutExtensions extend the Sitecore Sublayout. The GetDataSourceItem returns the item that the datasource points at, or null if the datasource is not set.

The UserControlExtensions extend the .NET UserControl (that really is the base of a Sitecore Sublayout). The GetDataSourceOrContextItem returns the datasource item, with a fallback to Sitecore.Context.Item.

To use the method, you must add the function to the item property of the sc:FieldRenderer:

<sc:FieldRenderer ID="MyField" runat="server" 
FieldName="NameOfMyField" 
Item="<%# this.GetDataSourceOrContextItem() %>" />

And you must remember to call DataBind().

This pattern is good because you never end up with a NULL item, as sc:FieldRenderer will crash if you feed it with a NULL value in the item property. There is no reason to crash the entire page just because one component is not configured correct.

From code-behind you simply replace Sitecore.Context.Item with this.GetDataSourceOrContextItem:

var text = Sitecore.Context.Item["NameOfMyField"]; // old way
var text = this.GetDataSourceOrContextItem()["NameOfMyField"]; // new way

I would not be surprised if Sitecore in the future would build these functions into sc:FieldRenderer, but untill then we have to do it ourselves.

Posted in c#, General .NET, Sitecore 6, Sitecore 7 | Tagged , , , , , , | 8 Comments

Custom rules and conditions for Sitecore personalization

In Sitecore it is possible to change the sublayouts or data sources on a page depending on conditions. This means that users can see the same page differently, depending on a set of rules. Sitecore comes standard with a set of standard rules (The user’s geographic place, has the user met any goals, has the user been referred etc.).

As always with Sitecore (well, except for the page editor) you can extend Sitecore’s functionality by creating your own rules.

THE EXAMPLE: QUERY STRING CONDITION

This is an example of a rule that checks for a certain QueryString (in this case a ZipCode). If the querystring contains a certain value, the condition is met.

First you need to set up the condition. Conditions are defined in folders below /sitecore/system/Settings/Rules/Segment Builder/Conditions:

My Custom Condition

My Custom Condition

The cryptic text attribute is used to explain and configure the condition at the same time:

Where the querystring parameter zipcode [operatorid,Operator,,compares to] [value,,,value]

The string is case sensitive. [operatorid,Operator,,compares to] gives you the options “equal to, not equal to, less than, greater than, …”, and [value,,,value] gives you the possibility to enter a value.

Then you need to write the code. This condition compares integers, so I inherit from the IntegerComparisonCondition. There are other conditions to inherit from as well, like the OrCondition, NotCondition, AndCondition, OperatorCondition, BinaryCondition etc.

namespace MyCode.Infrastructure
{
  public class CheckQuerystringCondition<T> : IntegerComparisonCondition<T> where T : RuleContext
  {
    protected override bool Execute(T ruleContext)
    {
      Assert.ArgumentNotNull(ruleContext, "ruleContext");
      var zipCodeQuerystringValue = HttpContext.Current.Request.QueryString["zipcode"];
      int zipCode;
      if (string.IsNullOrEmpty(zipCodeQuerystringValue))
      {
        return false;
      }

      if (!int.TryParse(zipCodeQuerystringValue out zipCode))
      {
        return false;
      }

      return Compare(zipCode);
    }
  }
}

The condition is now ready to be used.  To use it you need to find the layout of the page you wish to personalize. You can see if there is conditions on a layout by the little number mark:

Layout Details

Layout Details

Select the control to personalize and start personalizing. In this example I choose between 2 diferent sublayous depending on the condition. You can also choose to switch the data context (i.e. where the component gets its data from).

Personalize The Component

Personalize The Component

The personalization is pretty straight forward, and reminds of the way you would set up rules in Microsoft Outlook:

Rule Set Editor

Rule Set Editor

Thanks to Emil Klein for the code.

READ MORE:

Posted in .net, c#, Sitecore 6, Sitecore 7 | Tagged , , , , | 4 Comments

(System.Web.UI.HtmlControls.HtmlIframe) is not compatible with the type of control (System.Web.UI.HtmlControls.HtmlGenericControl)

Yesterday I got the following error:

Parser Error Message: The base class includes the field ‘IFrame’, but its type (System.Web.UI.HtmlControls.HtmlIframe) is not compatible with the type of control (System.Web.UI.HtmlControls.HtmlGenericControl).

Source Error:

Line 14: <iframe id=”IFrame” frameborder=”0″  runat=”server” visible=”false”/>

Reason:

The code was written using .NET 3.5 but executed in the .NET 4.5 runtime. From .NET 4.5, Microsoft decided to change the iframe from a HtmlGenericControl to its own control, a HtmlIframe.

They did this with a wide range of controls for example System.Web.UI.HtmlControls.HtmlTableCell and System.Web.UI.HtmlControls.HtmlAnchor.

Solution:

You need to recompile the code using the .net 4.5 runtime.
And when doing this, you need to change the *.designer.cs file reference from:

protected global::System.Web.UI.HtmlControls.HtmlGenericControl IFrame;

To:

protected global::System.Web.UI.HtmlControls.HtmlIframe IFrame;

A bug has been reported that the VS2012 does not make this change itself, but all I had to do was to rename the ID of the IFrame control, and VS figured it out for me.

More information:

Posted in .net, c#, General .NET | Tagged , , , | 7 Comments

A potentially dangerous Request.QueryString value was detected from the client

One of my colleagues encountered this error in Sitecore 6.6:

A potentially dangerous Request.QueryString value was detected from the client

You might think that this error was caused by the Microsoft AntiCSRF implementation by Sitecore.  But it is not, it’s actually caused by .NET 4.0:

According to Microsoft, they have changed the ASP.NET Request validation:

In ASP.NET 4, by default, request validation is enabled for all requests, because it is enabled before the BeginRequest phase of an HTTP request. As a result, request validation applies to requests for all ASP.NET resources, not just .aspx page requests. This includes requests such as Web service calls and custom HTTP handlers. Request validation is also active when custom HTTP modules are reading the contents of an HTTP request. Source: http://www.asp.net/whitepapers/aspnet4/breaking-changes#0.1__Toc256770147

If you encounter this error, you need to switch the RequestValidationMode back to the good old mode:

<httpRuntime requestValidationMode="2.0" />

Read more here:

Thanks to Anders Laub Christoffersen for the tip.

Posted in .net, General .NET, Sitecore 6, Sitecore 7 | 1 Comment

Programming for Sitecore DMS Engagement Plans

Sitecore DMS Engagement plans allow you to control some of the specific ways in which your website interacts and communicates with the visitors to your website. Think of engagement plans as a configurable flow state engine. It allows you to push the responsibility of automation to your customer.

Engagement plans are especially usefull when you have named visitors, ie users with a username and an email.

Sample Engagement Plan

Engagement plans are controlled through the Sitecore.Analytics.Automation.VisitorManager class and the Sitecore.Analytics.Tracker class.

The Tracker class can be used for connecting a named user to the DMS visitor:

using Sitecore.Analytics;
using Sitecore.Analytics.Automation;

private void CheckAndSetTracker(string userName)
{
  if (Tracker.IsActive)
  {
    Tracker.Visitor.ExternalUser = userName;
  }
}

The userName is a fully qualified name, ie extranet\user.

To assign a user to an engagement plan, simply call VisitorManager.AddVisitor with the ID of the engagement plan state:

public bool AssignToEngagementPlan(User user, ID engagementPlanIdStartState)
{
  bool addVisitor = VisitorManager.AddVisitor(user.Name, engagementPlanIdStartState);
  return addVisitor;
}

The User is the Sitecore.User.

usually you would assign the user to the first step of your engagement plan, but you can in fact assign the user to any state you wish.

You can also move a visitor from one state to another:

VisitorManager.MoveVisitor(userName, source, destination);

You can also search for a specific user in an engagement plan to see is the user is alreay assigned to any state in that engagement plan:

private bool UserIsInAnyEngagementPlanState(string userName, ID engagementPlan, out ID stateId)
{
  var result = false;
  stateId = null;
  foreach (Item state in GetEngagementPlanItem(engagementPlan).Children)
  {
    result = VisitorManager.GetStateVisitors(state.ID).Any(visitor => visitor.Equals(userName));
     if (result)
    {
      stateId = state.ID;
      break;
    }
  }
  return result;
}

That’s basically it. It’s very easy to work with once you get the hang of it.

READ MORE:

 

Posted in c#, Sitecore 6, Sitecore 7 | Tagged , , , , , | 7 Comments

.NET DateTime to JSON UNIX JavaScript datetime

When posting datetimes to web services or REST services, you might need to convert the standard .NET DateTime to a UNIX format.

Please note than neither REST, nor JavaScript has its own DateTime format. But some systems based on REST (Java applications for example) have a love affair with the UNIX Epoch datetime format, which is the number of seconds since 1/1/1970.

So in order to convert a standard .NET DateTime to a number of seconds, you need to calculate the TimeSpan between the current DateTime and 1/1/1970:

public string ToUnixEpoch(DateTime dateTime)
{
  DateTime d1 = new DateTime(1970, 1, 1);
  DateTime d2 = dateTime.ToUniversalTime();
  TimeSpan ts = new TimeSpan(d2.Ticks - d1.Ticks);
  return ts.TotalMilliseconds.ToString("#");
}

The string returned contains the integer of the timespan.

In some systems you need to add /Date()/ around your timespan, so the resulting UNIX Epoch string looks like this:

/Date(1234567891234)/

To convert the UNIX datetime to a .NET DateTime you reverse the process:

public DateTime FromUnixEpoch(long epochTime)
{
    var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    return epoch.AddSeconds(epochTime);
}

Some systems will return the UTC time, not the time from the current timezone. To convert from UTC to the current timezone, use the TimeZone class from .NET:

TimeZone.CurrentTimeZone.ToLocalTime(yourutctime)

 

Posted in c#, General .NET | Tagged , , , , | 1 Comment

Sitecore 6.6: CSRF form field is missing

In the latest version of Sitecore 6.6 (release 13.04.04) I sometimes get this error:

Exception: Sitecore.Security.AntiCsrf.Exceptions.PotentialCsrfException
Message: CSRF form field is missing.
Source: Sitecore.Security.AntiCsrf
at Sitecore.Security.AntiCsrf.SitecoreAntiCsrfModule.RaiseError(Exception ex, HttpContext context)
at Sitecore.Security.AntiCsrf.SitecoreAntiCsrfModule.PreRequestHandlerExecute(Object sender, EventArgs e)
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

The issue seemes to be related to an implementation of AntiCSRF, a Microsoft Public License library that prevents Cross Site Request Forgery.

HOW TO FIX IT:

The fix is easy. Clear your cookies, clear the browser cache, close the browser and try again.

UPDATE:

The clever guys at Sitecore Support have come up with this (untested) quick fix that you can try:

Please add these lines to the Sitecore.AntiCsrf.config file (website/app_config/include/Sitecore.AntiCsrf.config):

<ignore wildcard="/sitecore/shell/*Applications/Security/User*Manager*?*Cart_Users_Callback=yes"/>
<ignore wildcard="/sitecore/shell/*Applications/Security/Role*Manager*?*Cart_Roles_Callback=yes"/>
<ignore wildcard="/sitecore/shell/*Applications/Security/Domain*Manager*?*Cart_Domains_Callback=yes"/>
<ignore wildcard="/sitecore/shell/~/xaml/Sitecore.Shell.Applications.Security.SelectAccount*Cart_*_Roles_Callback=yes"/>
<ignore wildcard="/sitecore/shell/~/xaml/Sitecore.Shell.Applications.Security.SelectAccount*Cart_*_Users_Callback=yes"/>

UPDATE 2:

The tough guy could choose to disable AntiCSRF completely. Add the following line in the /App_Config/Include/Sitecore.AntiCSRF.config file:

<?xml version="1.0"?>
<configuration>
  <sitecore>
    <AntiCsrf>
      <rules>
        <rule name="shell">
          <!-- Ingore AntiCSRF completely -->
          <ignore wildcard="/sitecore/*"/>
        </rule>
      </rules>
    </AntiCsrf>
  </sitecore>
</configuration>
Posted in Sitecore 6 | Tagged , , , | 10 Comments