How to enable DMS in Sitecore

As a  Sitecore developer I have been working with Sitecore analytics  for a while now, but every now and then I am inquired about a very basic question “How to do enable DMS in my Sitecore installation?”

While Sitecore has done a great job posting details around how this can be done  not to mention John West’s blogs that moreover cover all that one would need to explore around in Sitecore, I still wanted to  share the steps  so that more people can know about it.

Now Sitecore brought the DMS with the release of Sitecore 6.5 version, prior to that it was know as Sitecore OMS.

Sitecore’s Digital marketing system is designed to manage online marketing needs for your Sitecore website.

Broadly outlined it provides  the following features

  • Website Analytics
  • Content Personalization
  • Campaign management
  • Engagement Automation
  • Integrated Email marketing
  • Engagement Automation

You can find more details around DMS here.

How to Enable DMS

  1. Make sure you have Sitecore 6.5 with DMS installed on your machine.
    • A Simple way to check that will be to open the “Start Menu” and look for “Marketing Center” and “Engagement Analytics”

  1. Make sure you download and unzip the database zip file that holds the configuration files and database files for the Analytics database.
    • Go to this page and select the link that relates to your current version of Sitecore DMS
    • If you are looking to upgrade to DMS you will find necessary updates on the above page as well.

    • On clicking the above highlighted link (say), you will be taking to the update page.
    • On this page you will be looking for  the DMS update package for your version (SQL/Oracle) of Sitecore.
    • The link should be in the section as highlighted below:

Once you have downloaded the package, you need to copy the relevant config files to the website\App_config\include folders. You can find the installation instructions here.

Installation Instructions

  1. Extract the contents of the zip-archive to a folder on your disk.
  2. Attach the Sitecore.Analytics.mdf database.
    Note: You can create a Database Account with the minimum required permissions for the Analytics database as described in section 4.4.1 and section 4.4.2 of the CMS Installation Guide.
  3. Add the following string in the ConnectionStrings.config file:
    <add name=”analytics” connectionString=”user id=_username_;password=_password_;Data Source=_server_;Database=Sitecore_Analytics” />
  4. Copy the Sitecore.Analytics.config file and the Sitecore.Analytics.ExlcudeRobots.config file to the /App_Config/Include/ folder.
  5. Update relevant web.config and Sitecore.Analytics.config settings, such as MailServer and Analytics.EMailFromAddress (for sending out reports as emails).
  6. If you wish to use DMS with Sitecore MVC, rename the /App_Config/Include/Sitecore.MvcAnalytics.config.disabled to Sitecore.MvcAnalytics.config.
  7. Modify existing layouts and put in a <sc:VisitorIdentification runat=”server”/> Web control in the <head> block to enable automatic robot detection.
    To make this work, you should also add the appropriate namespace definition in each layout and sublayout. This can be done in web.config, for instance:
    <system.web>
    <!– Continue to run Sitecore without script validations –>
    <pages validateRequest=”false”>
    <controls>
    <add tagPrefix=”sc” namespace=”Sitecore.Web.UI.WebControls” assembly=”Sitecore.Analytics”/>
    See Engagement Analytics Configuration Reference for more details about relevant settings and the <sc:VisitorIdentification /> web control.
  8. Restart IIS.

Troubleshooting

  • John west has written a great blog around how to troubleshoot DMS after installation . So if you get “Sitecore Analytics is Disabled” go read his post here.

DMS Documentation

  • You can find some useful DMS related documentation here.
Advertisements

Custom Language URL code in Sitecore

On occasions we  might come across a specific need where we want custom language identifiers in the URLs of our Sitecore website rather than the Out of the Box language identifiers such as, fr-FR or ru-RU, etc.

The Custom URL Identifier For Languages module provides an easy way to implement custom language codes in the website URLs.

This module provides the following features :

  1. Setup custom language identifiers very easily for all installed languages.
  2. Automatically manages the internal links in Sitecore content to reflect the new language identifier values.
  3. Retains the functionality of the Out of the box language identifier as well.

So if you have a new language installed, French (say). The urls of your website  in French language will be something like :

http:// yourdomanhere/fr-FR/aboutus.aspx

While after installing the Custom URL Identifier For Languages module, you will be able to access the URL as :

http:// yourdomanhere/fr/aboutus.aspx.

The “fr” custom code can very easily be changed to any relevant custom code, from the Sitecore client interface. This module effective also takes care of all the Sitecore Links, that the content managers/editors add in the Sitecore RTE fields and converts them into the new URLs with the custom language code.

Custom warnings in the Sitecore content editor window.

During a routine project discussion, the client expressed a need for an easier way to add versions to items on the website. While the editor works on an item (although he gets to “Lock and Edit” it), what if he wants to preserve the older version for some reason and move on to create a new one. We know that Sitecore provides the “Add Version” option under the “Version” tab in the menu, but in this case the client wanted something more accessible.

Knowing that Sitecore allows managing custom warnings in the content editor window; I proposed a solution that we add a warning message along with the button to allow the user to “Version Up, Lock and Edit” the item.

Now, it is important to understand how Sitecore normally works around item edits.

  • When an item is created, it gets stored as Version 1.
  • While in the workflow cycle, an item can be updated moving from one workflow state to another.
  • During this move, the version number remains the same (in this case, 1).
  • Once the entire workflow cycle is complete and the item is published, the item version gets incremented.
  • This increment happens when the content editor selects to “Lock and Edit” an item which is in the publish workflow state.

What we were attempting to do different here was to allow the user to add a new version of an item via a new button in the content editor warning. This was required so that he had easy access to creating a new version of the item and preserving the old one in the previous version.

Going about it was simple enough as Sitecore managed the Content Editor warnings as a separate pipeline in the web.config file, so all I had to do was to add a custom pipeline processor in the getContentEditorWarnings pipeline.

I started with creating a custom pipeline processor, the task was to create a custom message that suggested that the user can also create a new version of the item and edit it in the content editor, while allowing the user to click a button that lets them “Version Up, Lock and Edit” the item.

I created a custom class “ContentEditorVersionUpAndEditWarning”. Here, initially the logic exits the method in case of the user being an administrator or if the user does not have edit permissions to the item.


	Item obj = args.Item;
	if (obj == null)
		return;
	if (Context.IsAdministrator)
	{
		if (!obj.Locking.IsLocked() || string.Compare(obj.Locking.GetOwner(),
		Context.User.Name, StringComparison.InvariantCultureIgnoreCase) == 0)
			return;
	}
	else if (obj.Locking.IsLocked())
	{
		if (obj.Locking.HasLock())
			return;
	}

As all validations are met, the logic creates a new content editor warning object and initializes its “Title” and “AddOption” fields with the message text value and the command reference respectively.


	GetContentEditorWarningsArgs.ContentEditorWarning
	contentEditorWarning = args.Add();
	contentEditorWarning.Title = Translate.Text("You can version up,
	lock and edit this item as well.");
	contentEditorWarning.AddOption(Translate.Text("Version Up, Lock and Edit"),
	"customitemedit:versionup");

The final code for the class looks like this,


namespace Sitecore.Customizations
{
    public class ContentEditorVersionUpAndEditWarning
    {        

        public void Process(GetContentEditorWarningsArgs args)
        {
            Item obj = args.Item;
            if (obj == null)
                return;
            if (Context.IsAdministrator)
            {
                if (!obj.Locking.IsLocked() || string.Compare(obj.Locking.GetOwner(),
				Context.User.Name, StringComparison.InvariantCultureIgnoreCase) == 0)
                    return;
            }
            else if (obj.Locking.IsLocked())
            {
                if (obj.Locking.HasLock())
                    return;
            }
            else
            {
                if (!Sitecore.Configuration.Settings.RequireLockBeforeEditing
				|| !Sitecore.Data.Managers.TemplateManager.IsFieldPartOfTemplate(FieldIDs.Lock, obj))
                    return;
                GetContentEditorWarningsArgs.ContentEditorWarning
				contentEditorWarning = args.Add();
                contentEditorWarning.Title = Translate.Text("You can version up,
				lock and edit this item as well.");
                contentEditorWarning.AddOption(Translate.Text("Version Up, Lock and Edit"),
				"customitemedit:versionup");
            }
        }

    }
}

Now, we move on to create the command that will be called when the warning message button is clicked.

The need here is that when the button in the warning message is clicked, the item should be “checked out” and made available in the edit mode to the user.

Simply create a new custom command class, “VersionUpLockEdit”. Create this class by inheriting the “ContentEditor.Edit” class, this will allow you to create an edit command in the content editor interface.

Add an “Execute” method, this is the method that will be called when the command is invoked.


public override void Execute(Sitecore.Shell.Framework.Commands.CommandContext context)
{
	...
	...
}

The execute method validates if the command is invoked by a valid item, if not then the program execution exits the method. The method also validates if the item is already in a “checked out” state, if it is then the logic checks in the item.



	if (context.Items.Length != 1)
		return;
	if (Sitecore.Shell.Framework.Commands.ContentEditor.Edit.CanCheckIn(context.Items[0]))
		Context.ClientPage.SendMessage((object)this, "item:checkin");
	....

If the item is valid for version up lock and edit process then the logic creates a name-value collection of arguments and invokes the “Run” method.


	Item obj = context.Items[0];
	NameValueCollection parameters = new NameValueCollection();
	parameters["id"] = obj.ID.ToString();
	parameters["language"] = obj.Language.ToString();
	parameters["version"] = obj.Version.ToString();
	Context.ClientPage.Start((object)this, "Run", parameters);

The final “Execute” method looks like this,


public override void Execute(Sitecore.Shell.Framework.Commands.CommandContext context)
{

	Assert.ArgumentNotNull((object)context, "context");
	if (context.Items.Length != 1)
		return;
	if (Sitecore.Shell.Framework.Commands.ContentEditor.Edit.CanCheckIn(context.Items[0]))
		Context.ClientPage.SendMessage((object)this, "item:checkin");
	else
	{
		// If Unlocking the item then

		Item obj = context.Items[0];
		NameValueCollection parameters = new NameValueCollection();
		parameters["id"] = obj.ID.ToString();
		parameters["language"] = obj.Language.ToString();
		parameters["version"] = obj.Version.ToString();
		Context.ClientPage.Start((object)this, "Run", parameters);

	}
}

In the Run method, the item is validated for its state, whether it is already in lock state or if the user has permissions to edit the item. Among other things the logic also validates if the item actually exists.



	Assert.ArgumentNotNull((object)args, "args");
	Item obj1 = Context.ContentDatabase.Items[args.Parameters["id"],
	Language.Parse(args.Parameters["language"]),
		Sitecore.Data.Version.Parse(args.Parameters["version"])];
	if (!Context.IsAdministrator && (!obj1.Access.CanWrite() ||
	!obj1.Locking.CanLock() && !obj1.Locking.HasLock()))
		return;
	if (obj1 == null)
	{
		SheerResponse.Alert("Item not found.", new string[0]);
	}

Once validated, the logic first creates a new version of the item and once a new version has been added, it calls the “item:versionadded” command.


	// Add item version
	Sitecore.Data.Version[] versionNumbers = obj1.Versions.GetVersionNumbers(false);
	Log.Audit((object)this, "Add version: {0}", new string[1] { AuditFormatter.FormatItem(obj1) });
	Item obj2 = obj1.Versions.AddVersion();

	if (versionNumbers == null || versionNumbers.Length <= 0)
		return;
	Context.ClientPage.SendMessage((object)this,
	"item:versionadded(id=" + obj2.ID.ToString() + ",version=" + obj2.Version.ToString() +
		",language=" + obj2.Language.ToString() + ")");

Once the new version process is complete, the logic validates if the user is an administrator, if not it proceeds to unlock the item for editing by calling the “item:startediting” command. The command passes the item id and version details as arguments.


	// Check out the item
	if (Context.User.IsAdministrator)
		obj2.Locking.Lock();
	else
		obj2 = Context.Workflow.StartEditing(obj2);
	Context.ClientPage.SendMessage((object)this,
	"item:startediting(id=" + obj2.ID.ToString() + ",version=" + obj2.Version.ToString() +
		",language=" + obj2.Language.ToString() + ")");

The finished “Run” method looks something like this,


protected void Run(ClientPipelineArgs args)
{
	Assert.ArgumentNotNull((object)args, "args");
	Item obj1 = Context.ContentDatabase.Items[args.Parameters["id"],
	Language.Parse(args.Parameters["language"]),
		Sitecore.Data.Version.Parse(args.Parameters["version"])];
	if (!Context.IsAdministrator && (!obj1.Access.CanWrite() ||
	!obj1.Locking.CanLock() && !obj1.Locking.HasLock()))
		return;
	if (obj1 == null)
	{
		SheerResponse.Alert("Item not found.", new string[0]);
	}
	else
	{
		if (!SheerResponse.CheckModified())
			return;

		// Add item version
		Sitecore.Data.Version[] versionNumbers = obj1.Versions.GetVersionNumbers(false);
		Log.Audit((object)this, "Add version: {0}", new string[1] { AuditFormatter.FormatItem(obj1) });
		Item obj2 = obj1.Versions.AddVersion();
		if (versionNumbers == null || versionNumbers.Length <= 0)
			return;
		Context.ClientPage.SendMessage((object)this,
		"item:versionadded(id=" + obj2.ID.ToString() + ",version=" + obj2.Version.ToString() +
			",language=" + obj2.Language.ToString() + ")");

		// Check out the item
		if (Context.User.IsAdministrator)
			obj2.Locking.Lock();
		else
			obj2 = Context.Workflow.StartEditing(obj2);
		Context.ClientPage.SendMessage((object)this,
		"item:startediting(id=" + obj2.ID.ToString() + ",version=" + obj2.Version.ToString() +
			",language=" + obj2.Language.ToString() + ")");
	}
}

Lastly, we need to create a patch config file, where we will include the custom pipeline processor and the custom command. For this, we save the file, “ContentEditorVersionUpAndEditWarning.config”, in the \App Config\Include folder. This config file patch is automatically included in the configuration settings at runtime.


<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:x="http://www.sitecore.net/xmlconfig/">
 <sitecore>
 <pipelines>
 <getContentEditorWarnings>
 <processor patch:after="*[type='Sitecore.Pipelines.GetContentEditorWarnings.IsLocked, Sitecore.Kernel']" mode="on"
 type="Sitecore.Customizations.ContentEditorVersionUpAndEditWarning, Sitecore.Customizations" />
 <processor />
 </getContentEditorWarnings>
 </pipelines>
 <commands>
 <command name="customitemedit:versionup" type="Sitecore.Customizations.VersionUpLockEdit, Sitecore.Customizations" />      
 </commands>
 </sitecore>
</configuration>

There you have it folks! Your custom warning message/command is ready to use.

Using this example, you can further create custom warnings in the content editor window. The utility can range from latest updates on the item being edited or workflow status details on an item, or perhaps inform the user which fields were last updated; the usage is endless. So, I will leave that to you and your best needs.

Set Owner bug in Sitecore 7

In one of my recent projects I was working on a typical client requirement around workflow related access permissions for certain users.

The requirement was that if a user creates an item in Sitecore then that item should only be visible to that particular user in a given workflow state, while other users of the same role should be able to see the workflow state and their relevant items, but they should not be able to see/work on the items of other users.

The other feature they wanted was that a Sitecore administrator has the right to assign that item to another user in event of the owner going on leave.

Now every Sitecore item has an owner field, where Sitecore saves the creator-owner of the item by default. This field was also supported by another feature that Sitecore offers, “Change Ownership”, which the Admin or any user with valid access rights to this menu item, can change the owner of the item. This feature is available in the Security menu of the Sitecore item.

With above two out of the box features it was looking pretty simple to incorporate this requirement in no time. The task was simple; use the Owner field to identify the creator-owner of the item and associate the correct access rights on the item so that it is only visible to the owner of the item. Also, using the “Change Ownership” menu option the Admin would be able to set another owner of the item and all of this made possible by Sitecore’s out of the box features without much customization.

I was elated knowing that the only customization I had to work on was for the Workflow Access rights (which was a challenge in itself), but when I got past that stage and came around to test the out of the box feature of “Change Ownership” I was surprised to see that it wasn’t working as expected. Let me show you what I found out…

When you click the “Change Ownership” button, Sitecore opens a dialogue box, where it shows the “Creator-Owner” of the item and suggests the user id of the current logged in user in the “Change Owner To” text field.

If you want to set another user as the owner of the item, you can click the “Browse” button and select from a list of users.

When you go around selecting a user and click “Ok”, the users list closes and the control returns back to the first change ownership dialogue box.

Here, to my surprise the “Change Owner To” field still read “Sitecore/nsingh”, the current user that I was logged in with. I thought to myself, “hmm, maybe it’s just me… I must have not selected a user from the list the first time around”. So I tried it again, kept awake through the whole process of selecting a different user, (even took a snapshot to prove to myself that I wasn’t seeing things) when I returned to the first dialogue box I still saw my current user in the “Change Owner To” field.

This is where I realized that Sitecore 6.6 and for that matter even Sitecore 7 has this same bug where the value is not returned correctly.

I made sure I logged this issue on Sitecore support, which would help the Sitecore team resolve this bug in the future releases (Ticket #389149).

Curious to resolve this issue, I traced the set owner control to this location \Website\sitecore\shell\Applications\Security\SetOwner\SetOwner.xml

Using ILSpy, I decompiled the Sitecore.Shell.Applications.Security.SetOwner.SetOwnerPage class and saw that on the “Browse_Click()” function , the value was being returned to a defined control in the code.

SheerResponse.SetAttribute(“ctl00_ctl00_ctl00_ctl00_ChangeOwner”, “value”, text);

It was surprising to find such sloppy coding where hardcoded control values have been assigned in the code, which in itself creates a chance for a bug , even though the “ChangeOwner “ TextBox control was available in the code as a variable.

I used fiddler to validate the control id of the “Change Owner To” field in the HTML of the page, only to find that the control Id there was “ctl00_ctl00_ctl00_ctl00_104_ChangeOwner”.

Needless to say my suspicions were correct and perhaps someone had forgotten to correct the control id in the code.

To mitigate this issue, I created my own class “MySetOwnerPage”, inheriting the “SetOwnerPage” class. I created an updated “Browse_Click” function and corrected the code by getting the control id of the control from the base class’s ChangeOwner text box control as,

SheerResponse.SetAttribute(base.ChangeOwner.ClientID, “value”, text);

The new custom class code looked something like this,


namespace Sitecore.Customizations
{
    ///
    /// The set owner page.
    ///
    ///
    public class MySetOwnerPage : SetOwnerPage
    {

        ///
        /// Browses this instance.
        ///
        ///
        protected void Browse_Click()
        {
            ClientPipelineArgs clientPipelineArgs = ContinuationManager.Current.CurrentArgs as ClientPipelineArgs;
            Assert.IsNotNull((object)clientPipelineArgs, "args");
            if (clientPipelineArgs == null)
                return;
            if (clientPipelineArgs.IsPostBack)
            {
                if (!clientPipelineArgs.HasResult)
                    return;
                string text = clientPipelineArgs.Result;
                int length = text.IndexOf('^');
                if (length >= 0)
                    text = StringUtil.Left(text, length);

                // Line of code Replaced...
				// SheerResponse.SetAttribute("ctl00_ctl00_ctl00_ctl00_ctl04_ChangeOwner", "value", text);

                SheerResponse.SetAttribute(base.ChangeOwner.ClientID, "value", text);
            }
            else
            {
                SheerResponse.ShowModalDialog(new SelectAccountOptions()
                {
                    Multiple = false,
                    ExcludeRoles = true
                }.ToUrlString().ToString(), true);
                clientPipelineArgs.WaitForPostBack();
            }
        }

        protected override void OnLoad(EventArgs e)
        {
            Assert.CanRunApplication("Content Editor/Ribbons/Chunks/Ownership");
            Assert.ArgumentNotNull((object)e, "e");

            base.OnLoad(e);

        }
    }
}

All I had to do then was to update the SetOwnerPage.xml file to call my class and I was good to go for testing. This resolved the SetOwner dialogue “bug” in Sitecore and it started returning the correct value to the “ChangeOwnerTo” field.

Sitecore CMS Best practices

A Content Management System (CMS) is important to your website (and business) for a variety of reasons starting from content authoring and management features. Today, I am talking about Sitecore CMS. Having evolved a whole lot in the last couple of years, it now offers advanced features like, analytics, personalization, campaign management and more.

How can you get the best out of Sitecore? Begin with building a strong foundation starting with the architecture of the website.


Templates

Templates are the starting point for Sitecore architects who analyze the site’s requirements, break it down to the basic tangible entities and then work to further understand the content structure of the final site.

Templates are ‘normalized groupings’ of fields that constitute an entity structure in Sitecore. These entities can be single fields or a group of related fields.

To create a good template structure, you have to be cognizant about the following details:

  • Identify all related fields that are common to an entity and can be further repurposed in other templates
  • While creating a template, fields should be grouped in logical sections, which are based on relevant categorization of data
  • In scenarios where a predefined structure of items is to be created, branch templates need to be defined after sub-items have been created
  • Field names should be chosen carefully so that business users can identify and understand them with ease
  • Define separate fields for display name, breadcrumb title as well as navigation title fields for a page item
  • Minimize the use of RTE fields within the template and keep the templates small
  • Have default values in the form of standard values
  • Make basic template components which can be inherited as foundation templates


Content Structure

Once the templates have been created, you need to begin work on creating the content tree or the content architecture of the site. This structure will help you manage the content of your site.

Here, your checklist while creating the content architecture:

  • Content structure hierarchy in Sitecore can impact the performance of your site. So be extra careful. As standard practice, we would not recommend having more than 100 items under any item node. Maintain the hierarchy in a way that it reduces the number of children per node
  • For better and faster content editor experience, indexes should be configured correctly for automatic updates
  • Identify access restrictions for items in order to provide access based on minimal requirements
  • Plan content structure based on site map. Place all the items which are accessed using a URL as descendants of the web site item
  • Use Clones instead of Proxy item as they allow to proxy an item/item sub-structure and allow for overriding the values
  • Identify the security related requirements in multi-site in one instance environment and design your site structure and media structure that meets the separation requirements
  • Maintain only a few versions of each item in the implementation


Presentation

After the content structure is ready, get started on creating the presentation of the Sitecore architecture. Presentation of the site is the layer which the users interface with on the site

  • To start with, make sure that you store the layouts inside/Sitecore/Layout/Layouts/, sub-layouts inside /Sitecore/Layout/Sublayout. You can create sub-directories within these as needed with added access restrictions
  • Do not have more than two or three layouts per device and handle the different structures using placeholders and sub-layouts respectively
  • Assign the layout details in the standard values not on items or template definition items. When applied at item level, any changes to the layouts require a lot of effort to make changes to all items. When applied at standard values, such modifications become easier.
  • If different items based on a template require different layouts, then create a new template that inherits from the existing template
  • Use Field Renderer object to render the fields on presentation. This would provide the native CMS inline editing features for the pages. Use DefaultFieldEditor module from shared source for allowing inline editing of fields that are not displayed
  • Caching options should be configured whenever the controls are used, based on the control definitions
  • Make use of Sublayouts vs xslt while creating complex presentation items. It makes managing the code easily and also allows effective debugging of the code
  • Site should be compliant to content authoring using page editor. It should not require access login to the desktop interface for content editors. This can be achieved by coding using Field Renderer object or by using Sitecore tags like sc:Text, sc:Image tags — these provide for editing in page editor out of the box


Caching

Another topic that I would like to take up here is the site performance. It is important we develop any website in a way that the code is optimum and fast. Sitecore enables you to do that and besides such codes, it allows various other features and options to improve the performance of your site

Sitecore allows monitoring and management of the website cache. You can improve site performance by keeping caching tuned to the best functioning of the website. Following these critical guidelines:

  • Combined size of all the caches should not exceed available memory
  • Use Sitecore Debugger, Firebug, /Sitecore/admin/stats.aspx, and /Sitecore/admin/cache.aspx to measure the performance of the sites
  • During cache monitoring, if the Delta value keeps changing consistently with the size of the cache being more than 80% of the max size limit for that cache, consider increasing the max size for that cache by 50%.
  • Tweak Pre-fetch cache appropriately by caching key items deemed to be required for the site.
  • Tweak the different layers of caches for your application and perform the respective measurements to see the optimal settings for your site

Implementing these effective practices will definitely help you in creating a robust architecture for your Sitecore website. I will be talking about more areas of improvements in my upcoming blogs.