Sitecore OMS Multivariate Tests and Sublayouts

Published by on
Tags:

Sitecore have release the Online Marketing Suit. One of the nice aspects of OMS is that ability to test different page content to see which content users respond best to, this is called Multivariate testing.

I quickly show how this is setup. Firstly we create a the different items that contain the content variations. So in the picture below you can see that I have three different home nodes.

When the user visits the site they are taken to /sitecore/content/home but they may see content from /sitecore/content/home2 and /sitecore/content/home3. This content is injected by the multivariate test.

Now I have my content the next step is to create the multivariate test. This is setup using the Marketing Centre application. For this example I have setup an incremental test strategy that simply picks the next multivariate test item to display to the user:

    public class IncrementalMultiVariateTestStrategy : IMultiVariateTestStrategy
    {
        const string _cookieName ="aegeagaegaeWeafea2";

        #region IMultiVariateTestStrategy Members

        public Item GetTestVariableItem(Item item, Item multiVariateTest)
        {
            
            if (!multiVariateTest.HasChildren) return null;
            int index = Cookie == -1? 0 : Cookie;

            if (index >= multiVariateTest.Children.Count) index = 0;

            Cookie = index + 1;
            return multiVariateTest.Children[index];            
        }

        #endregion

        private int Cookie
        {
            get
            {
                int i = -1;

                if(HttpContext.Current.Request.Cookies[_cookieName]!= null)
                    int.TryParse(HttpContext.Current.Request.Cookies[_cookieName].Value, out i);

                return i;                               
            }
            set
            {
                HttpContext.Current.Response.Cookies.Add(new HttpCookie(_cookieName, value.ToString()));
            }
        }
    }

The image below shows the setup in the Marketing Centre with each test variable pointing at the relevant node.

Now I just need to setup the Sample Rendering to use the test on the homepage:

So publishing the site and doing a couple of refreshes we can see that the content of the sample rendering actually changes. So are multivariate test works!!

Ok so it works for XSLTs but what about a sublayout? Si I have created a simple sublayout with the following code behind:

public partial class layouts_MVTest : System.Web.UI.UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {
        output.Text = "Sublayout: " + Sitecore.Context.Item["Title"];
    }
}

I have also updated the home item to include the sublayout and set the sublayout to use the multivariate test:

Ok so saving this and publishing it we now get this output:

Notice how the XSLT content changes but the sublayout content does not change. So why does this happen? This is caused by how the Sublayouts and XSLTs resolve the current item.

I suspect that most Sitecore developers (this is true of where I work and for myself) are used to getting the current item via Sitecore.Context.Item within a sublayout. However if you look at the code that the XSLT uses to render itself (Sitecore.Web.UI.WebControls.XslFile there is a GetItem method inherited from the Sitecore.Web.UI.WebControl class. Within this method the actual item passed to the XSLT is resolved based on the WebControl.DataSource property. The Multivariate Test actually changes the DataSource property to point at the new content item.

So how do we get this value in the our Sublayout? Well we have actually look at the parent control of our user control:

Sitecore.Web.UI.WebControls.Sublayout parent = (Sitecore.Web.UI.WebControls.Sublayout)this.Parent;
string sourceItem = parent.DataSource;

This then gives us the ID of the item injected by the multivariate tests. So what does this mean in terms of development? Well if you want to use multivariate tests on a site that you already developed you will have to replace any reference to Sitecore.Context.Item with a custom solution that looks at the parents DataSource property:

    public Item Source
    {
        get
        {
            Sitecore.Web.UI.WebControls.Sublayout parent = (Sitecore.Web.UI.WebControls.Sublayout)this.Parent;
            Guid sourceItem = new Guid(parent.DataSource);
            return Sitecore.Context.Database.GetItem( new Sitecore.Data.ID(sourceItem)) ?? Sitecore.Context.Item;        }
    
    }

What about sub-items, for example you have the following code in your XSLT:

      >ul<
      >xsl:for-each select="./item"<
        >li<
          >sc:text field="Title"<>/sc:text<
        >/li<
      >/xsl:for-each<
    >/ul<

Well this actually just breaks as shown in this images below and only works when the original item is requested. The solution is to proxy the subitems below the new nodes:

I have summarised the problems you will have to think about when using mutlivariate tests on your site:

  • Sitecore.Context.Item - any code that uses this reference will not pick up multivariate test content. This code will need to be changed.
  • Sub items don't exist - sub items may not exist beneath the alternative content items. You will need to recreate to proxy these items.
  • Other items will not see the alternative content. E.g. if you have a rendering in another item that relies on content in an item that is using multivariate test this item will only see the original item and not the alternative content.

The multivariate tests are a powerful addition to the Sitecore platform but if you plan to use them on your site you need to think how they will be used before you start coding. Their usage will determine how you design both your renderings and sublayouts. For simplicity it is best to only use multivariate tests on renderings and sublayouts that only have a dependency on the current item.

Using a default language in Sitecore 6.2

Published by on
Tags:

I had written a previous blog about setting up a default language for Sitecore, however this was for Sitecore 5.3.x . The Sitecore 6 API is slightly different. I have detailed the Sitecore 6 solution below.

Firstly create the following class:


    public class SqlServerDataProvider : Sitecore.Data.SqlServer.SqlServerDataProvider
    {

        public SqlServerDataProvider(string connectionString)
            : base(connectionString)
        {
        }
        protected override void LoadItemFields(string itemCondition, string fieldCondition, object[] parameters, Sitecore.Collections.SafeDictionary prefetchData)
        {
            base.LoadItemFields(itemCondition, fieldCondition, parameters, prefetchData);

            //set the default language to use
            Language defaultLang;
            Language.TryParse("en", out defaultLang);

            //full list of languages the site uses
            LanguageCollection coll = this.GetLanguages();

            foreach (var data in prefetchData)
            {
                if (data.Value != null)
                {
                    var uris = data.Value.GetVersionUris();
                    //check the item contains the default language before doing anything
                    if (uris.Cast().Any(x => x.Language == defaultLang))
                    {
                        //get the default language fields 
                        var defaultUri = uris.Cast().First(x => x.Language == defaultLang);
                        //loop through each language
                        foreach (Language lang in coll)
                        {
                            //check if language doesn't exist
                            if (!uris.Cast().Any(x => x.Language == lang))
                            {
                                //update language with default language values
                                FieldList fields = data.Value.GetFieldList("en", defaultUri.Version.Number);
                                foreach (var key in fields.GetFieldIDs())
                                {
                                    data.Value.AddField(lang.Name, 1, key, fields[key]);
                                }



                            }
                        }
                    }
                }
            }
        }
    }

The main difference is the change in the for loop. In Sitecore 5.3.x the GetFieldsList method returned a collection of field objects that had two properties, the Key which was the field name and a Value property. This has been replaced with an object that wraps a HashList of ID's and Values. This makes more sense since field names could be change and break code but ID's will be consistent.

FieldList fields = data.Value.GetFieldList("en", defaultUri.Version.Number);
foreach (var key in fields.GetFieldIDs())
{
  data.Value.AddField(lang.Name, 1, key, fields[key]);
}

The XML describing the data provider in the web.config has also changed, add the provider detailed below:


      
$(1)
$(1)

Then alter the provider used by the database from:


  $(id)
  Network/16x16/earth.png
  true
  
    

An update the the provider to:

  $(id)
  Network/16x16/earth.png
  true
  
    

Create Sitecore Database User

Published by on
Tags:

This script creates a SQL Server database user for the Sitecore 6.2 databases manually. You will need to find and replace %username% and %password% with your values.

CREATE LOGIN %username% WITH PASSWORD = '%password%'

USE Sitecore_Master
GO

CREATE USER %username% FOR LOGIN %username%
GO

GRANT EXECUTE TO %username%
GO

sp_addrolemember 'db_datareader', '%username%'
GO

sp_addrolemember 'db_datawriter', '%username%'
GO

USE Sitecore_Web
GO

CREATE USER %username% FOR LOGIN %username%
GO

GRANT EXECUTE TO %username%
GO

sp_addrolemember 'db_datareader', '%username%'
GO

sp_addrolemember 'db_datawriter', '%username%'
GO

USE Sitecore_Master
GO

CREATE USER %username% FOR LOGIN %username%
GO

GRANT EXECUTE TO %username%
GO

sp_addrolemember 'db_datareader', '%username%'
GO

sp_addrolemember 'db_datawriter', '%username%'
GO

Get all XML attributes using XSLT

Published by on
Tags:

If you want to be able to get all the attributes on an XML element including their names you can use the following XSTL:


	 - 
	

Sitecore 6 XHTML Validation and the Embed HTML Element

Published by on
Tags:

The embed HTML element is not a valid XHTML element, this creates a problem when you want to insert objects into your Rich Text fields such as You Tube videos. To validate XHTML sitecore uses the file /sitecore/shell/schemas/sitecore xhtml.xsd. By editing this file we can allow usage of the embed element.

Beneath the /xs:schema node add a definition for the embed element:

 <xs:element name="embed">
    <xs:complexType mixed="true">
      <!-- from http://www.htmlref.com/Reference/AppA/tag_embed.htm -->
      <xs:attribute name="align" type="xs:string" />
      <xs:attribute name="alt" type="xs:string" />
      <xs:attribute name="class" type="xs:string" />
      <xs:attribute name="code" type="xs:string" />
      <xs:attribute name="codebase" type="xs:string" />
      <xs:attribute name="height" type="xs:string" />
      <xs:attribute name="hspace" type="xs:string" />
      <xs:attribute name="id" type="xs:string" />
      <xs:attribute name="language" type="xs:string" />
      <xs:attribute name="name" type="xs:string" />
      <xs:attribute name="src" type="xs:string" />
      <xs:attribute name="style" type="xs:string" />
      <xs:attribute name="title" type="xs:string" />
      <xs:attribute name="unselectable" type="xs:string" />
      <xs:attribute name="vspace" type="xs:string" />
      <xs:attribute name="width" type="xs:string" /
      <!-- used by you tube -->
      <xs:attribute name="type" type="xs:string" />
      <xs:attribute name="allowscriptaccess" type="xs:string" />
      <xs:attribute name="allowfullscreen" type="xs:string" />
    </xs:complexType>
  </xs:element>

This definition was created based on the attributes list at http://www.htmlref.com/Reference/AppA/tag_embed.htm and some attributes used by You Tube. You may need to define additional attributes as needed.

Next we need to add a reference to the new embed definition in the object definition. Find the definition and replace the first part with the following which has the embed reference:

  <xs:element name="object">
    <xs:complexType mixed="true">
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element ref="embed" />
        <xs:element ref="param" />
        <xs:group ref="block" />
        <xs:element ref="form" />
        <xs:group ref="inline" />
        <xs:group ref="misc" />
      </xs:choice>
   ...

Save the file and your embed element problems should disappear. Remember however that doing this will mean that your site is no longer XHTML strict.

 

Projects

Have you read?

Manually using Sitecore XSLT Renderings



Blogs by date