The Amazing World of Razl – Part 1

As a Hedgehog employee I have the pleasure of playing around with all of their tools. I find it strange that many Sitecore developers haven`t realized the potential of Razl. Most of us consider it a DB compare tool which we can only use once or twice in our day-to-day job. While Razl is a DB compare tool, Razl has much more usages than most people think it does, so I decided to put a blog post around a couple of things that can be done with Razl.

Automated Content Synchronization between Production and Staging/QA Environments

Many Sitecore developers face the problem that the Staging/QA environments don’t have fresh content. A quality assurance team working with fake/old data, is not ideal and might cause some issues. For example, imagine a client disapproving a working module because it is hard to show how  it will work once it gets to production. This is a situation in which Razl shines. Razl’s APIs allow us to write which can synchronize the Staging/QA environments with fresh content on a daily/hourly basis. The scripts are fairly simple to write and they are even simpler to execute. The scripts can be run with your favorite scheduling mechanism. For more information about Razl Scripting you can check the following articles:

Introducing Razl Scripting (Follow @me_mikeshaw)
Official Razl Documentation About Scripting 
Razl Script Mode (Follow @mikeedwards83)

Setting Up New Development Environments

When adding new people to the team, setting up their Development Environments might be a long and painful process, especially with complex solutions. Razl makes it easy for the new developer to grab all the content and/or some other necessary pieces (custom modules, things from the core database, etc.) and start working.

On Demand or Automated Synchronization between Production and Development Environments

There are some situations in which bugs are caused by the live content and are hard to reproduce on development workboxes. Razl makes it easy to get the pieces that caused issues, bring it to their local environment and now the developers to properly debug them.

Keeping Track on Content Changes

Razl comes with History View that uses the Sitecore History Engine. The history view provides an amazing option to track what has been changed and who has changed it, so when something goes wrong it is easier to check what the cause is and who caused it. For more information about Razl History View, you can check the following articles:

Official Razl Documentation About History View
Razl History View (Follow @mikeedwards83)

Simple Way to Visually Compare CMS/Master Database and CDS/Web Database

Sometimes there might be problems with content publishing from CMS to CDS Servers. Razl makes it easy to track and resolve these kinds of problems. If anything goes wrong with the publishing process, and republish is not an option, Razl can be used to compare the Master and the Web Database. It allows us to check what was left unpublished and helps us identify the problem.

Simple Way to Migrate Content from QA/Staging Content to Production

Sometimes, especially in tight deadline situations, the content is entered on some of the Pre-Production Environments. Here Razl comes in handy to easily move the content between environments.

In conclusion these are just some of the core functionalities that Razl offers in our day-to-day work. It can also be used in other automation scenarios like automatic content synchronization on deploy, moving content across environments via workflow and etc.

Make sure to check out this places for more invaluable Razl Information:

The official Razl Documentation
Hedgehog Development Razl Posts
Mike Edwards Razl Posts

Happy Razling ! 🙂

The Amazing World of RAZL Part 2 – Script Like A Boss

 

Glass Mapper and Custom Time Field

Every Sitecore developer struggled with the problem of implementing a time field and many developers found this article which provides good solution to the problem. The field works well, uses Sitecore built-in timepicker and is easy to implement. The huge problem comes when trying to use the custom time field with one of the most used Sitecore ORMs – Glass Mapper. This arises from the fact that the time picker uses TimeSpan to store its value and Glass doesn’t support TimeSpan values out-of-the-box. Fortunately there are 2 possible solutions to the problem – creating Glass Abstract Data Mapper (in order to support TimeSpan values) or modifying the Custom Time Field (in order for it to work with DateTime which is supported by the default Glass Date Time Handler).

Creating an Abstract Data Mapper Approach

Creating and registering an AbstractSitecoreFieldMapper for Glass is pretty straightforward. The only implementations required are GetFieldValue, SetFieldValue. The other important part is to call the base constructor with Type (or Type array) which is used by Glass to know which types are handled by the custom mapper.

You can find the implementation of the AbstractSitecoreFieldMapper bellow.


namespace Sandbox.GlassExtensions
{
    using Glass.Mapper.Sc;
    using Glass.Mapper.Sc.DataMappers;
    using Sitecore;
    using System;

    public class SitecoreFieldTimeHandler : AbstractSitecoreFieldMapper
    {
        public SitecoreFieldTimeHandler() :
            base(typeof (TimeSpan))
        {

        }

        public override object GetFieldValue(string fieldValue,
            Glass.Mapper.Sc.Configuration.SitecoreFieldConfiguration config, SitecoreDataMappingContext context)
        {
            return DateUtil.ParseTimeSpan(fieldValue, TimeSpan.Zero);
        }

        public override string SetFieldValue(object value,
            Glass.Mapper.Sc.Configuration.SitecoreFieldConfiguration config, SitecoreDataMappingContext context)
        {
            if (value is TimeSpan)
            {
                TimeSpan time = (TimeSpan)value;
                return time.ToString();
            }

            throw new NotSupportedException("The value is not of type System.TimeSpan");
        }
    }
}

After the handler is implemented it needs to be registered in App_Start/GlassMapperScCustom.cs. Keep in mind this code is only valid if you are using Glass with Castle Windsor (via Glass.Mapper.SC.CastleWindsor). If you are using a custom implementation with different dependency injection framework – adjust the registration.


public static void CastleConfig(IWindsorContainer container)
{
	var config = new Config();

	 container.Register(
		        Component.For<AbstractDataMapper>()
		            .ImplementedBy<SitecoreFieldTimeHandler>()
		            .LifestyleCustom<NoTrackLifestyleManager>());

	container.Install(new SitecoreInstaller(config));
}

And that is it !

If you are not happy with TimeSpans for storing time – feel free to implement you custom data type to store the value.

NOTE: If you are using TDS Code Generation with GlassV3 Templates you will need to update GlassV3Item.tt GetGlassFieldType method. Example of what should be added is shown bellow.

case "[TIMEFIELDNAME]":
   return "TimeSpan";

Modifying the Custom Time Field Approach

This approach uses the default Glass Date Time Handler (The code can be found here) and introduces small modifications to the Custom Time Field.

The modified code can be found bellow.


namespace Sandbox.TimeField
{
    using Sitecore;
    using Sitecore.Data.Items;
    using Sitecore.Diagnostics;
    using Sitecore.Shell.Applications.ContentEditor;
    using Sitecore.Web.UI.HtmlControls;
    using Sitecore.Web.UI.Sheer;
    using System;

    public sealed class GlassTimeField : Input, IContentField
    {
        private TimePicker _picker;

        public string ItemID
        {
            get { return GetViewStateString("ItemID"); }
            set
            {
                Assert.ArgumentNotNull(value, "value");
                SetViewStateString("ItemID", value);
            }
        }

        public string RealValue
        {
            get { return GetViewStateString("RealValue"); }
            set
            {
                Assert.ArgumentNotNull(value, "value");
                SetViewStateString("RealValue", value);
            }
        }

        public GlassTimeField()
        {
            Class = "scContentControl";
            Change = "#";
            Activation = true;
        }

        public override void HandleMessage(Message message)
        {
            Assert.ArgumentNotNull(message, "message");
            base.HandleMessage(message);
            string name;
            if (message["id"] != ID || (name = message.Name) == null)
                return;
            if (name == "contentdate:today")
            {
                Now();
            }
            else
            {
                if (name != "contentdate:clear")
                    return;
                ClearField();
            }
        }

        public string GetValue()
        {
            return
                (System.DateTime.MinValue +
                 DateUtil.ParseTimeSpan(_picker == null ? RealValue : _picker.Value, TimeSpan.Zero))
                    .ToString("yyyyMMddTHHmmss");
        }

        public void SetValue(string value)
        {
            Assert.ArgumentNotNull(value, "value");
            RealValue = value;
            if (_picker == null)
                return;
            _picker.Value = value.Length == 15 ? DateUtil.IsoDateToDateTime(value).ToString("t") : value;
        }

        protected override Item GetItem()
        {
            return Client.ContentDatabase.GetItem(ItemID);
        }

        protected override bool LoadPostData(string value)
        {
            if (!base.LoadPostData(value))
                return false;
            _picker.Value = value ?? string.Empty;
            return true;
        }

        protected override void OnInit(EventArgs e)
        {
            SetViewStateBool("Showtime", true);
            _picker = new TimePicker();
            _picker.ID = ID + "_picker";
            Controls.Add(_picker);
            if (!string.IsNullOrEmpty(RealValue))
                _picker.Value = RealValue.Length == 15
                    ? DateUtil.IsoDateToDateTime(RealValue).ToString("t")
                    : RealValue;
            _picker.OnChanged += (EventHandler) ((param0, param1) => SetModified());
            _picker.Disabled = Disabled;
            base.OnInit(e);
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            ServerProperties["Value"] = ServerProperties["Value"];
            ServerProperties["RealValue"] = ServerProperties["RealValue"];
        }

        protected override void SetModified()
        {
            base.SetModified();
            if (!TrackModified)
                return;

            Sitecore.Context.ClientPage.Modified = true;
        }

        private void ClearField()
        {
            SetRealValue(string.Empty);
        }

        private void SetRealValue(string realvalue)
        {
            if (realvalue != RealValue)
                SetModified();
            RealValue = realvalue;
            _picker.Value = realvalue;
        }

        private void Now()
        {
            SetRealValue(DateUtil.IsoNowTime);
        }
    }
}

The modified methods are GetValue, SetValue and OnInit. The idea is to force the Time Field to work with Sitecore`s IsoDateTimeFormat. A slightly dirty approach – but it does the job.

Happy Glass Mapping ! 🙂