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
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.
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:
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
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.)
Keep on personalization out there.
Thatโs all for now folks ๐