Sitecore 7 create item and update index at the same time

With Sitecore 7, the Sitecore index (based on Lucene.NET) have greatly improved, thus making the index even more usable.

One thing you should know though, is that when you add items to Sitecore using the Sitecore.Data.Items.Item.Add() method, the index is not immediately updated. The index is updated shortly after, but the time varies depending on your index update strategy.
The result is that if you query the index just after you added a new item, it is not a given that the item exists in the index.

There is a solution to this (of course, this is Sitecore). When you have added the new item, you must manually update the search index for this specific item.

I have made an Extension Method to show how this can be done:

using System;
using System.Linq;
using Lucene.Net.Index;
using Sitecore.ContentSearch;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;

namespace MyProject
{
  public static class ItemExtensions
  {
    public static Item AddAndUpdate(this Item item, string name, TemplateID templateID)
    {
      Assert.ArgumentNotNull(item, "item");
      Assert.IsTrue(item.Database.Name.ToLower() == "master", "Could not add item to " + item.Paths.FullPath + ": item is not in the 'master' database");
      Item newItem = item.Add(ItemUtil.ProposeValidItemName(name), templateID);
      Log.Audit("Item create: " + AuditFormatter.FormatItem(newItem), typeof(ItemExtensions));
      var tempItem = (SitecoreIndexableItem)newItem;
      ContentSearchManager.GetIndex("sitecore_master_index").Refresh(tempItem);
      return newItem;
    }

  }
}

The trick lies within these 2 lines of code:

var tempItem = (SitecoreIndexableItem)newItem;
ContentSearchManager.GetIndex("sitecore_master_index").Refresh(tempItem);

First the item is converted into a SitecoreIndexableItem, and then the master index is refreshed for this single item.

The extension method can be used like this:

using MyProject;

namespace MyNamespace
{
  public void AddItem(Item parent, string name, TemplateID templateID)
  {
    Item newItem = parent.AddAndUpdate(name, templateID);
  }
}

Please note that this will increase the time to create an item substantially, from barely mesureable milliseconds to a full second or so.

MORE TO READ:

 

 

Posted in .net, c#, Sitecore 7 | Tagged , , , , | Leave a comment

Unable to serialize the session state. In ‘StateServer’ and ‘SQLServer’ mode

When switching the sessionState mode of your web project from InProc to SQLServer you might encounter this error:

Exception: System.Web.HttpException
Message: Unable to serialize the session state. In ‘StateServer’ and ‘SQLServer’ mode, ASP.NET will serialize the session state objects, and as a result non-serializable objects or MarshalByRef objects are not permitted. The same restriction applies if similar serialization is done by the custom session state store in ‘Custom’ mode.

.NET handles the storage of session objects differently from InProc to SQLServer. When storing session objects in a SQL Server, .NET will serialize the objects. This is necessary because the session object needs to be transferred from server to server.

To support this, all you need is to mark the objects that are part of your sessions with the [Serializable]  attribute:

namespace MyNameSpace
{
  // Class is marked as serializable
  [Serializable]
  public class MyClass
  {
    // Some code here
  }
}

So how do you find the classes that need the [Serializable] attribute?

.NET is pretty good at telling which classes needs to be serializable in the nested exception:

Nested Exception

Exception: System.Runtime.Serialization.SerializationException
Message: Type ‘MyNamespace.MyClass‘ in Assembly ‘MyAssembly, Version=4.0.4.18681, Culture=neutral, PublicKeyToken=null’ is not marked as serializable.
Source: mscorlib

MORE TO READ:

NOTE TO SITECORE DEVELOPERS:

Sitecore does not as such support other modes than InProc session state. As Sitecore states (quote from the Sitecore Scaling Guide):

The Sitecore CMS user interfaces require in-process ASP.NET session management. For CM
instances, the value of the mode attribute of the /configuration/system.web/sessionState
element in the web.config file must be InProc.
In-process session management requires you to configure the CM load balancer for server affinity —
also known as sticky sessions. You can use other values for the mode attribute in the CD
environment.

This means that the Sitecore client and page editor is not tested using other session state methods than InProc. But since the content delivery servers do not touch the Sitecore UI, it is up to you to make the front end code compatible with StateServer or SQLServer methods.

The best session state management for Sitecore CD servers would still be InProc, and let your load balancer use sticky sessions.

Posted in c#, General .NET, Sitecore | Tagged , , , , , , | Leave a comment

Stream Sitecore media items to HttpResponse

Here is a small tip for you who is doing API’s based on Sitecore. How to get an item from the Sitecore Media Library and stream it to the HttpResponse of a page.

This is an example of an .ashx HttpHandler class that gets the ID of an Sitecore Media Library item and streams the item. Add your own exception handling as you wish.

using System;
using System.Net;
using System.Web;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Configuration;

namespace MyProject
{
  public class GetImage : IHttpHandler
  {
    public void ProcessRequest(HttpContext context)
    {
      // ID of media item
      string id = context.Request.QueryString["id"];

      // Get media item
      MediaItem item = (MediaItem)Factory.GetDatabase("web").GetItem(id);

      // Get name to be shown when image is saved
      string imageName = item.Name + "." + item.Extension;

      context.Response.Clear();
      context.Response.ContentType = mediaItem.MimeType;
      context.Response.AppendHeader("Content-Disposition", string.Format("inline;filename=\"{0}\"", imageName));
      context.Response.StatusCode = (int)HttpStatusCode.OK;
      context.Response.BufferOutput = true;
      // Copy the media stream to the response output stream
      mediaItem.GetMediaStream().CopyTo(context.Response.OutputStream);
      // As momma always said: Always remember to flush
      context.Response.Flush();
      context.Response.End();
    }

    public bool IsReusable
    {
      get
      {
        return false;
      }
    }
}

The trick lies within the type casting of an Item to a MediaItem. The MediaItem contains a media stream that can be copied to the OutputStream of the HttpResponse.

Another trick is to construct a file name from the item name + the file extension found on the MediaItem. Adding this to the output stream will make the name appear as the default file name when users saves your file.

Posted in .net, c#, Sitecore 7 | Tagged , , , | Leave a comment

Sitecore Code Generator and O/R mapper

You probably have never used a code generator for the same reason I never used a code generator:

  • It was not developed by yourself
  • It’s not generating the code you need
  • You cannot extend it with the stuff you need
  • They create the same code for all of your Sitecore templates
  • It’s difficult to install
  • It was not developed by yourself.

A code generator that creates an O/R relationship between your Sitecore templates and your code can save you a ton of work though:

  • Automatically update your code when new templates are created
  • There is never any mismatch between your code and Sitecore templates
  • Code class names matches Sitecore template names
  • All field names are mapped correctly

That is why I introduce the open-source, freeware, use-as-you-please:

SITECOREORM, The object/relational code generator for Sitecore.

You can download it here for free, including the source code:

SitecoreORM O/R Mapper and Code Generator

SitecoreORM

http://marketplace.sitecore.net/en/Modules/SitecoreORM_Object-relational_Code_Generator.aspx

The SitecoreORM is a code generator framework that allows you to create code templates based on well-known user controls (.aspx) pages, and generate code from all of your Sitecore templates.

There is no Sitecore package; all you do is to place the code in your solution and call \sitecore modules\Shell\SitecoreORM\Execute.aspx to execute the code generation.

Out of the box, SitecoreORM generates simple classes that maps all fields on a Sitecore template, references to inherited templates, and a static struct of field names to be used by Sitecore’s sc:FieldRenderer.

But the idea behind SitecoreORM is that you define yourself how your classes should look like.

Changing the output is done by simply changing some .aspx pages. For example, the code template is an .aspx page. This is an example of how it looks (simplified, download the code for the complete example):

namespace <%# Namespace %>
{
  public class <%# ClassName %> : BaseItem
  {
    public <%# ClassName %>(Item innerItem)
    {
      InnerItem = innerItem;
    }

    public Item InnerItem { get; private set; }

    public override FieldCollection Fields
    {
      get { return InnerItem.Fields; }
    }

    #region Inherited Templates
<%# ExecuteCodeProvider("SomeClass, SomeDll", "SomeParameter") %>
    #endregion
  }
}

Generating fields, inherited templates or other thing you need, is done using code providers, that you plug in. SitecoreORM uses reflection to find the code, and your class only needs to implement an ICodeProvider interface that has 1 method: Get().

Here is an example of an .aspx file that generates a Sitecore field (a CheckBox field):

<%@ Page Language="C#" AutoEventWireup="true" %>
<%@ Import Namespace="Sitecore.Data.Items" %>
<%@ Import Namespace="SitecoreORM.CodeProviders" %>
<%@ Import Namespace="SitecoreORM.Services" %>
<script runat="server">

  void Page_Load(object sender, System.EventArgs e)
  {
    DataBind();
  }

  private TemplateFieldItem Field
  {
    get { return GenerateFieldProperties.Field; }
  }

  private string PropertyName
  {
    get { return NameNormalizerService.GetValidName(Field.Name); }
  }

</script>

    /// <summary>
    /// <%# TemplateFieldItemCommentService.GetComment(Field) %>
    /// </summary>
    public Sitecore.Data.Fields.CheckboxField <%# PropertyName %>
    {
      get { return InnerItem.Fields["<%# Field.Name %>"]; }
    }

The SitecoreORM framework is written with the fewest lines of code that will do the job. The code is as simple as possible, making it easier to extend, and for you to understand.

Features of SitecoreORM includes:

  • O/R mapping Sitecore templates to C# code.
  • Every aspect of the code generation can be customized:
    • .aspx page based code generation allows you to choose the class format yourself.
    • Each field type from Sitecore can be mapped by .aspx pages, making it easy to determine how each field type from Sitecore should be mapped.
    • Inherited templates can be mapped as well.
    • Field names can be mapped to be used by sc:FieldRenderer
    • Plug in your own code; SitecoreORM uses reflection to find the code generators.
  • Customizable output using an include file (/app_config/include/sitecoreorm.config)
    • Determine class name suffixes
    • Determine name space prefixes
    • Determine removing of parts of template path from namespace
    • Determine file destination for generated classes
    • Specify which template folders to ignore (for example /system/ and /common/)
    • Specify which template folders to include
    • Specify file destination for each template folder to include, hence supporting component based development
  • Simple code. Less than 20 classes makes up the framework. 5 services and one repository does most of the job.

Check out SitecoreORM here:

http://marketplace.sitecore.net/en/Modules/SitecoreORM_Object-relational_Code_Generator.aspx

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

Sitecore.Data.ID The call is ambiguous

In Sitecore you cannot compare an ID with NULL; it will produce the following error:

The call is ambiguous between the following methods or properties: ‘Sitecore.Data.ID.operator ==(Sitecore.Data.ID, Sitecore.Data.ID)’ and ‘Sitecore.Data.ID.operator ==(Sitecore.Data.ID, Sitecore.Data.ShortID)’

I.e. the following code is invalid:

Sitecore.Data.ID someID = GetSomeID();
if (someID == null)
{
  // do code
}

Instead, you need to call Sitecore.Data.ID.IsNullOrEmpty():

ID someID = GetSomeID();
if (Sitecore.Data.ID.IsNullOrEmpty(someID))
{
  // do code
}

A quick tip for you.

Posted in c#, Sitecore, Sitecore 7 | Tagged , | 1 Comment

Get Sitecore placeholders and rendering hierarchy from a Sitecore item

This article explains how you can get the hierarchy of placeholders and render them to your screen for debugging or documentation purposes.

The Sitecore rendering engine allows you to place sublayouts inside placeholders. These sublayouts can contain more placeholders, thus creating a hierarchy of placeholders inside a page.
This hierarchy can get pretty complex. So the following code demonstrates how you can read the layout from an item, the placeholders from a layout or sublayout, which renderings will be rendered where and how to write it all to an XML document that will show you which sublayout contains which placeholders and which renderings is rendered where.

STEP 1: THE BASIC METHODS:

First i give you the basic methods for getting layout, placeholder and rendering information. These methods can be copied into your own project directly:

  /// <summary>
  /// Return all placeholder keys defined on one item
  /// </summary>
  private IEnumerable<string> GetPlaceholderKeys(Sitecore.Data.Items.Item item)
  {
    List<string> uniquePlaceholderKeys = new List<string>();
    Sitecore.Layouts.RenderingReference[] renderings = GetRenderingReferences(item, "default");
    foreach (var rendering in renderings)
    {
      if (!uniquePlaceholderKeys.Contains(rendering.Placeholder))
        uniquePlaceholderKeys.Add(rendering.Placeholder);
    }
    return uniquePlaceholderKeys;
  }

  /// <summary>
  /// Search for all placeholder controls in a specific file and return all the placeholder keys
  /// </summary>
  /// <param name="relativePath"></param>
  /// <returns></returns>
  private IEnumerable<string> GetPlaceholderKeysFromFile(string relativePath)
  {
    string text = System.IO.File.ReadAllText(HttpContext.Current.Server.MapPath(relativePath));
    string pattern = "<sc:Placeholder (.*?) />";
    MatchCollection matches = Regex.Matches(text, pattern, RegexOptions.IgnoreCase);
    foreach (Match match in matches)
    {
      string keyPattern = @"Key=""(.*?)""";
      Match keyMatch = Regex.Match(match.Value, keyPattern, RegexOptions.IgnoreCase);
      yield return keyMatch.Value.Replace("Key=", "").Replace("key=", "").Replace("\"", "");
    }
  }

  /// <summary>
  /// Return all renderings to be rendered in a specific placeholder on the "default" device
  /// </summary>
  private IEnumerable<Sitecore.Data.Items.RenderingItem> GetRenderings(string placeholderKey, Sitecore.Data.Items.Item item)
  {
    Sitecore.Layouts.RenderingReference[] renderings = GetRenderingReferences(item, "default");
    foreach (var rendering in renderings)
    {
      if (rendering.Placeholder == placeholderKey)
      {
        yield return rendering.RenderingItem;
      }
    }
  }

  /// <summary>
  /// Return all renderings from an item defined on a device
  /// </summary>
  private Sitecore.Layouts.RenderingReference[] GetRenderingReferences(Sitecore.Data.Items.Item item, string deviceName)
  {
    Sitecore.Data.Fields.LayoutField layoutField = item.Fields["__renderings"];
    Sitecore.Layouts.RenderingReference[] renderings = layoutField.GetReferences(GetDeviceItem(item.Database, deviceName));
    return renderings;
  }

  /// <summary>
  /// Get the layout from an item defined on a device
  /// </summary>
  private Sitecore.Data.Items.LayoutItem GetLayout(Sitecore.Data.Items.Item item, string deviceName)
  {
    Sitecore.Data.Fields.LayoutField layoutField = item.Fields["__renderings"];
    return new Sitecore.Data.Items.LayoutItem(item.Database.GetItem(layoutField.GetLayoutID(GetDeviceItem(item.Database, deviceName))));
  }

  /// <summary>
  /// Convert a Sitecore item to a Sublayout item
  /// </summary>
  private Sitecore.Data.Items.SublayoutItem GetSublayout(Sitecore.Data.Items.Item item)
  {
    return new Sitecore.Data.Items.SublayoutItem(item);
  }

  /// <summary>
  /// Get the device item from a device name
  /// </summary>
  private Sitecore.Data.Items.DeviceItem GetDeviceItem(Sitecore.Data.Database db, string deviceName)
  {
    return db.Resources.Devices.GetAll().Where(d => d.Name.ToLower() == deviceName.ToLower()).First();
  }

  /// <summary>
  /// Get all placeholder settings that defines the specified placeholderKey
  /// </summary>
  private IEnumerable<Sitecore.Data.Items.Item> GetPlaceholderSettings(Sitecore.Data.Database db, string placeholderKey)
  {
    Sitecore.Data.Items.Item root = db.GetItem("/sitecore/layout/Placeholder Settings");
    foreach (Sitecore.Data.Items.Item descendant in root.Axes.GetDescendants())
    {
      if (descendant.Template.Key != "placeholder")
        continue;
      if (descendant["Placeholder Key"].ToLowerInvariant() == placeholderKey.ToLowerInvariant())
        yield return descendant;
    }
  }

Each of these methods can be used individually, but will reference each other some times.

To get all placeholders (i.e. the placeholder “keys” which is the unique definition of a placeholder) that contain renderings, call GetPlaceHolderKeys().

To get all placeholders that is defined in a file, call GetPlaceholderKeysFromFile(). This method uses a Regular Expression to extract all the <sc:Placeholder /> definitions found inside a source file.

To get all renderings that is rendered to a specific placeholder, call GetRenderings().

STEP 2: GET THE HIERACHY OF RENDERINGS:

Here is how to use the methods. These 2 functions will render an XML structure of the hierarchy of layouts, sublayouts, placeholder etc. You should replace /sitecore/templates/somepage/__Standard Values with the page you wish to get the hierarchy from:

  void Page_Load(object sender, System.EventArgs e)
  {
    Sitecore.Data.Database db = Sitecore.Configuration.Factory.GetDatabase("master");
    Sitecore.Data.Items.Item item = db.GetItem("/sitecore/templates/somepage/__Standard Values");

    Response.Write(string.Format(@"<template name=""{0}"">", GetLayout(item, "default").Name));
    foreach (string s in GetPlaceholderKeysFromFile(GetLayout(item, "default").FilePath))
    {
      RenderPlaceholders(s, item);
    }
    Response.Write("</template>");
  }

  private void RenderPlaceholders(string placeholderKey, Sitecore.Data.Items.Item item)
  {
    IEnumerable<Sitecore.Data.Items.Item> placeholderSettings = GetPlaceholderSettings(item.Database, placeholderKey);
    int settingsCount = placeholderSettings.Count();
    bool editable = placeholderSettings.Any(i => i["Editable"] == "1");
    Response.Write(string.Format(@"<placeholder key=""{0}"" editable=""{1}"">", placeholderKey, settingsCount, editable));

    foreach (var rendering in GetRenderings(placeholderKey, item))
    {
      Response.Write(string.Format(@"<sublayout name=""{0}"">", rendering.Name));
      foreach (string s in GetPlaceholderKeysFromFile(GetSublayout(rendering.InnerItem).FilePath))
      {
        RenderPlaceholders(s, item);
      }
      Response.Write(@"</sublayout>");
    }
    Response.Write(@"</placeholder>");
  }

The method will output something like this:

<template name="Default">
  <placeholder key="SeoRegion" editable="0">
    <sublayout name="MetaTags"></sublayout>
    <sublayout name="DocumentDescription"></sublayout>
    <sublayout name="OpenGraph"></sublayout>
  </placeholder>
  <placeholder key="phPageHolder" editable="1">
    <sublayout name="DefaultPage">
      <placeholder key="DefaultColumn1" editable="0">
        <sublayout name="DefaultPage">
        </sublayout>
      </placeholder>
    </sublayout>
  </placeholder>
  <placeholder key="ScriptsRegion" editable="0">
    <sublayout name="GoogleAnalytics"></sublayout>
  </placeholder>
</template>

That’s it. Happy coding.

Posted in c#, General .NET, Sitecore 5, Sitecore 6, Sitecore 7 | Tagged , , , , , | Leave a comment

Register local search terms in Sitecore DMS

When installing Sitecore and DMS, Sitecore comes pre-installed with an Executive Insight Dashboard Report that will generate a report containing which search words users have executed in your local search page.

All you need to do is to register a Page Event called “Search” on your search result page:

using Sitecore.Analytics;
using Sitecore.Analytics.Data;

protected void RegisterSearchPageEvent(string searchQuery)
{
  if (!Tracker.IsActive || Tracker.Visitor == null || Tracker.Visitor.CurrentVisit == null)
    return;

  var page = Tracker.Visitor.CurrentVisit.CurrentPage;
  if (Tracker.Visitor.CurrentVisit.PreviousPage != null)
    page = Tracker.Visitor.CurrentVisit.PreviousPage;

  page.Register(new PageEventData("Search")
  {
    Data = searchQuery,
    DataKey = searchQuery.ToLowerInvariant(),
    Text = searchQuery
  });
}

What happens behind the scenes is that the search terms (the parameter “searchQuery” is registered as a page event on the current page. The page event “Search” is defined in Sitecore:

Page Event "Search"

Page Event “Search”

And a SQL Query Report is defined to get the visits per data query.

SQL Query

SQL Query

And the Executive Dashboard is configured to display the search terms from this SQL Query (you will find the report under “Site Search“):

Dashboard displaying the search terms

Dashboard displaying the search terms

Please note that the report will not display any data below 50 hits, unless you configure this in the .config file located here:

\sitecore\shell\Applications\Reports\Dashboard\Configuration.config

Look for the setting called “MinimumVisitsFilter“. This filter determines how many (or how few) visits are required before they are considered a significance.

In the demo above, I have set the value to “5″:

<ConfigurationParams>
  ...
  <MinimumVisitsFilter value="5" />
  ...
</ConfigurationParams>

Also note that this setting influences every report in the Executive Dashboard, so be careful not to set it too low or it will slow down other reports significantly.

READ MORE:

 

 

Posted in .net, c#, Sitecore 6, Sitecore 7 | Tagged , , , , | Leave a comment

JavaScript ReferenceError: Can’t find variable: __doPostBack

This error can occur in asp.net pages. In certain situations .net will fail to create the __doPostBack JavaScript function and the hidden __EVENTVALIDATION input field. The situations that can cause this are:

  • When using asp:ImageButton or asp:LinkButton controls AND:
  • When viewing the webpage using IE11
  • When viewing the webpage using an iPhone or an iPad that was updated to the latest iOS 7.

The problem revolves around the BrowserDefinitions in .NET. .NET uses these Browser Definitions to identify the capabilities of the current browser. As per default, if a browser is not known (i.e. not defined in the BrowserDefinitions), .net assumes that the browser have no JavaScript capabilities, and therefore the __doPostBack function is not needed.

Assuming that a browser is not JavaScript compatible by default in the year 2013 is probably pretty stupid. However, this is the reality and we need to cope with it. 

The browserdefintions are defined in the belly of .net (for example here: c:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\Browsers).

The later a .net version you have, the newer the .net version, the more likely it is that the browser is known. .NET 4.5 should know about  IE11 and the latest iPhone and iPad browsers.

POSSIBLE SOLUTION: SETTING THE CLIENTTARGET=UPLEVEL ON THE .ASPX PAGE

There is a way to override the automatic detection of browser capabilities. Add the following to the <% Page %> directive on all of your .aspx pages:

<%@ Page Language="C#"
    AutoEventWireup="true"
    CodeBehind="somecodebehind"
    ClientTarget="uplevel"
    Inherits="somecode" %>

Setting ClientTarget=”uplevel” forces the page to generate JavaScript as it assumes that the browser has at least the capabilities of IE6.

MORE TO READ:

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

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