Create your own TimePicker(macro) personalization rule in Sitecore

timepickermacro

Once more unto the breach, dear friends, once more ๐Ÿ™‚ This time I want to share with you how to make your own TimePicker macro, which will be used by a custom rule.

I needed a rule where I could present content at a specific time during the day. That means I’m NOT interested in the date, ONLY the time.
There are a bunch of nice rules but not a “Time rule”. So what to do?
Well let’s do a rule then, unfortunately I could not find a TimePicker macro.
Something like this would be nice:
[TimeField,Time,,pick a time]

So what is a macro? A macro is used by the rules. A typical macro could be the datetimepicker or a numeric input.
Here is how the datetimepicker macro is used in a rule:
[DateField,DateTime,,the date]
The macros are listed under Rules at: /sitecore/system/Settings/Rules/Definitions/Macros
macros

What we need to do is to create a new macro. Let’s start by adding/creating a new macro template in /sitecore/system/Settings/Rules/Definitions/Macros. We will call it – Time.
timemacroempty
We will come back to the macro item, when we have some code to point to ๐Ÿ™‚

We want the Timepicker macro to be similar to the Datetimepicker macro but without the date. Why not use dotPeek to find out how Sitecore did. After a lot of hair pulling I finally figured it out.

Let me give you a quick explanation on how it works:
The macro will do a SheerResponse call(ShowModalDialog), here it will give an url to an XML file (see it as a page/dialog for the datetimepicker).
The url points to the default.aspx page in Sitecore/shell, which will “generate” a dialog containing a datetimepicker. The xml file will also need a “code beside” in order to present the datetime picker.

So for the datetimepicker we have the following:
DateTimeMacro.cs (in Sitecore.Kernel)
DateTimeSelector.xml (in folder Sitecore\shell\Applications\Dialogs\DateTimeSelectors)
DateTimeSelector.cs – The “code beside” for the xml file (in Sitecore.Client)

Now lets do the same for our new Timepicker, first we create the macro:

public class TimeMacro : IRuleMacro
{
	public void Execute(XElement element, string name, UrlString parameters, string value)
	{


		Assert.ArgumentNotNull((object) element, "element");
		Assert.ArgumentNotNull((object) name, "name");
		Assert.ArgumentNotNull((object) parameters, "parameters");
		Assert.ArgumentNotNull((object) value, "value");

		SheerResponse.ShowModalDialog(new UrlString(UIUtil.GetUri("control:Sitecore.Shell.Applications.Dialogs.TimeSelector")).ToString(), "580px","475px", string.Empty, true);

	}

}

This little puppy:
UIUtil.GetUri("control:Sitecore.Shell.Applications.Dialogs.TimeSelector")
Generates the following url:
/sitecore/shell/default.aspx?xmlcontrol=Sitecore.Shell.Applications.Dialogs.TimeSelector

Time to get confused ๐Ÿ™‚ Sitecore.Shell.Applications.Dialogs.TimeSelector is not an xml file, it’s an element in an xml file. Here is our new xml file:

<?xml version="1.0" encoding="utf-8" ?>
<control xmlns:def="Definition" xmlns="http://schemas.sitecore.net/Visual-Studio-Intellisense">
  <Sitecore.Shell.Applications.Dialogs.TimeSelector>
    <FormDialog Header="Time" Text="Select a time." Icon="People/32x32/clock.png">
      <CodeBeside Type="Sandbox.Sitecore.Dialogs.CustomTimeSelector, Sandbox.Sitecore" />
      
      <div style="text-align: center;">
        <TimePicker ID="Time"/>
      </div>

    </FormDialog>
  </Sitecore.Shell.Applications.Dialogs.TimeSelector>
</control>

This is how the popup/dialog will look like.
I put the xml file together with the DatetimeSelector file in folder:
Sitecore\shell\Applications\Dialogs\DateTimeSelectors

Let’s do the code beside class(defined in the xml file), here you find the TimePicker control with the property name Time.

public class CustomTimeSelector : DialogForm
{
	/// <summary>Gets or sets the time.</summary>
	/// <value>The time.</value>
	protected TimePicker Time { get; set; }

	/// <summary>Raises the load event.</summary>
	/// <param name="e">The <see cref="T:System.EventArgs" /> instance containing the event data.</param>
	protected override void OnLoad(EventArgs e)
	{
		base.OnLoad(e);
		if (Context.ClientPage.IsEvent)
			return;

		Time.Value = DateTime.Now.TimeOfDay.ToString();
	}

	/// <summary>Handles a click on the OK button.</summary>
	/// <param name="sender">The sender.</param>
	/// <param name="args">The arguments.</param>
	protected override void OnOK(object sender, EventArgs args)
	{
		SheerResponse.SetDialogValue(Time.Value);
		base.OnOK(sender, args);
	}
}

The TimePicker, that was indeed confusing for me. I started out by creating a new TimePicker field, adding it to the core database etc. I thought it would be a normal field in the content editor. But that was so wrong… Here we use the Sitecore.Web.UI.HtmlControls. The good thing is that I did not have to create a TimePicker control, there is already one(used by the DatetimePicker control).

When ok button is hit, we will set the time value in the macro(in a rule).

Ok, so now we have done the macro. We will now update the new macro item, Time, in Sitecore. We need to point to the code for the macro class:
macroset

If you want to test it, you can easily browse to the following url:
your site/sitecore/shell/default.aspx?xmlcontrol=Sitecore.Shell.Applications.Dialogs.TimeSelector
controlinaction

To make it complete, we need a rule. We can call it – Time of day. Check out my previous post how to make a custom rule, Compare dates in your Sitecore personalization rule.
The text for the rule:
when current time is [operatorid,Operator,,compares to] [TimeField,Time,,pick a time]
Notice our new macro: [TimeField,Time,,pick a time] ๐Ÿ™‚

Here is the code for the rule. The TimeField property will hold the time that was selected from the Timepicker macro.

public class TimeOfDayCondition<T> : OperatorCondition<T> where T : RuleContext
{

	public string TimeField { get; set; }

	protected override bool Execute(T ruleContext)
	{
		Assert.ArgumentNotNull((object)ruleContext, "ruleContext");
		
		ConditionOperator conditionOperator = base.GetOperator();

		TimeSpan? timeToCompare = ConvertTime(TimeField);

		if (!timeToCompare.HasValue)
			return false;

		return TimeSpanComparer(DateTime.Now.TimeOfDay, timeToCompare.Value, conditionOperator);
		
	}


	private TimeSpan? ConvertTime(string timeToConvert)
	{
		DateTime time;
		if (!DateTime.TryParse(timeToConvert, out time))
			return null;

		return time.TimeOfDay;

	}


	private bool TimeSpanComparer(TimeSpan timeSpan1, TimeSpan timeSpan2, ConditionOperator conditionOperator)
	{
			
		switch (conditionOperator)
		{
			case ConditionOperator.Equal:
				return timeSpan1.Equals(timeSpan2);
			case ConditionOperator.LessThan:
				return timeSpan1 < timeSpan2;
			case ConditionOperator.LessThanOrEqual:
				return timeSpan1 <= timeSpan2;
			case ConditionOperator.GreaterThan:
				return timeSpan1 > timeSpan2;
			case ConditionOperator.GreaterThanOrEqual:
				return timeSpan1 >= timeSpan2;
			case ConditionOperator.NotEqual:
				return !timeSpan1.Equals(timeSpan2);
			default:
				return false;
		}

	}

}

In method TimeSpanComparer we will compare current time with the time in the TimeField property, for that we will use the Operator condition(LessThan, GreaterThan etc.)

Here is the rule in action:
timepickermacro

Keep on personalization out there.

Thatโ€™s all for now folks ๐Ÿ™‚


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.