Category Archives: Rules

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 ๐Ÿ™‚

Advertisements

Compare dates in your Sitecore personalization rule

experienceeditorcompa

Hello good people,

First of all I would like to thank Sitecore for awarding me the Sitecore’s Technology MVP 2017. I’m truly honoured.

Today I’m going to share with you how easy it is to create/make a date comparer rule.

In this scenario we will do a pretty dumb rule ๐Ÿ™‚
There will be two values(date picker) which will be compared by using the operator condition.

Jeff Darchuk has a great post on how to create/add rules in Sitecore – Lets use that rules engine!
So why not follow his instructions ๐Ÿ™‚

1. Create a new tag to identify our new group of rules, we will call it TestRules.
/sitecore/system/Settings/Rules/Definitions/Tags
createtag

2. Go to path: /sitecore/system/Settings/Rules/Definitions/Elements. Here you will see a bunch of rules, lets create a new Element Group and call it TestRules:
createelementfolder

3. Associate the new tag TestRules with the rule element folder(TestRules) by setting the default tag on the rule element.
tagdefault

4. We want the rule to be accessible when the editors are working in the Experience Editor(Presentation Details), let’s add the new tag(TestRules) to the conditional rendering rules:
rulecontext

Now let us create the rule and select the condition alternative:
createrule

So now we have a an empty condition rule called – DateComparerRule. Next will be to add some logic to it, we will add the two date fields and a condition comparer.
createrule

Here is the “text” code:
when [DateFieldOne,DateTime,,first date] [operatorid,Operator,,compares to] [DateFieldTwo,DateTime,,second date]

Let me give you a quick explanation.
DateFieldOne and DateFieldTwo – These are the properties in our custom rule
DateTime – This is the datepicker
“first date” and “second date” – The texts you want to show the editor, when he/she is using the rule.

operatorid – Will hold what “operator condition” you have selected(greater than, less than etc)
Operator – The operator picker
“compares to” – The text you want to show to the editor, when he/she is using the rule.

Ok so lets see how the rule look and feels for the editors.
experienceeditor
experienceeditordateexperienceeditorcompa

Yes it seems to work ok, next will be to add some code for the rule.

Here is the actual code for our custom rule,DateComparerRule. We need to inherit from OperatorCondition in order to compare the two date field properties: DateFieldOne and DateFieldTwo.

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

	public string DateFieldOne { get; set; }

	public string DateFieldTwo { get; set; }

	protected override bool Execute(T ruleContext)
	{
		Assert.ArgumentNotNull((object)ruleContext, "ruleContext");

		if (!IsDate(DateFieldOne) || !IsDate(DateFieldTwo))
			return false;

		ConditionOperator conditionOperator = base.GetOperator();

		return DateComparer(DateUtil.ParseDateTime(DateFieldOne, DateTime.MinValue), DateUtil.ParseDateTime(DateFieldTwo, DateTime.MinValue), conditionOperator);
	}

	private bool IsDate(string date)
	{
		DateTime tempDate;
		return DateTime.TryParse(date, out tempDate);
	}

	private bool DateComparer(DateTime date1, DateTime date2, ConditionOperator conditionOperator)
	{
			
		switch (conditionOperator)
		{
			case ConditionOperator.Equal:
				return date1.Equals(date2);
			case ConditionOperator.LessThan:
				return date1 < date2;
			case ConditionOperator.LessThanOrEqual:
				return date1 <= date2;
			case ConditionOperator.GreaterThan:
				return date1 > date2;
			case ConditionOperator.GreaterThanOrEqual:
				return date1 >= date2;
			case ConditionOperator.NotEqual:
				return !date1.Equals(date2);
			default:
				return false;
		}

	}
	
}

In order to get what ConditionOperator the editor has selected(greater than, less than etc.) we use the base method base.GetOperator(). From method DateComparer we will return the outcome of the comparison between the two properties: DateFieldOne and DateFieldTwo.

What we have left is to update the new rule, DateComparer, in Sitecore. We need to tell it where to find the code we have created. We do that by updating field Type(in section Script) with the following:
Sandbox.Sitecore.Rules.Conditions.DateComparer;Sandbox.Sitecore

This is not a big thing but I want you guys to see how easy it is to make your own custom rules.

Keep on personalization out there.

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