Wednesday, 23 March 2016

Create Custom Date Range Slider Bar With Graph for the Office 365 / SharePoint Search Refinement Panel

When you're refining search results on an Office 365 / SharePoint search page by a date, the Date Range Slider Bar with Graph is a nice graphical way to restrict results to date ranges.

Out of the box this control only works with dates in the past. But what if you wanted to refine based on future dates? Well, like most stuff in Office 365 / SharePoint, it's configurable. The only caveat is, you have to swim in "undocumented waters" (it's a little fiddly)!

Out of the box Date Slider
(dates in the past)
Customised Date Slider
(daters in the future)

At this point I need to give a quick shout out to IThinkSharePoint, for a blog post that helped point me in the right direction for creating a custom date refiner. That blog shows you how to change the properties of the refinement webpart to include different date ranges. The example they give shows ranges that go back several years.

This blog post focuses on dates in the future, and also on customising the date range control (so that you see the dates displayed in a graphical way).

The process to create a custom date range filter is;
  1. Decide on the date ranges
  2. Copy the OOTB date range filter Display Template
  3. Modify the new Display Template
  4. Export the search refinement webpart and update the properties (can be done with PowerShell alternatively)
  5. Import the search refinement webpart back on the search page

1. Deciding on the date range


The date range that you decide on will be set in the refinement webparts properties, as well as in the date range filter template (Filter_SliderBarGraph). 

Because the values are directly interconnected, decide up front what the intervals will be. For this example, I've used 7 days, 30 days, and 90 days. This will allow me to filter items (in this case, events) that are:
  • In the next 7 days
  • Between 7 - 30 days time
  • Between 30 - 90 days time
  • And any combination in between. E.g:
    • Events later than 90 days from now
    • Events that occur within 7 - 30 days

2. Copy the OOTB date range filter Display Template


A display template called Filter_SliderBarGraph.html is used to render the date range filter, and it lives in the "/_catalogs/masterpage/display templates/Filters" directory.

Make a copy of the template, and then open the copy. In this example, I've saved mine as Filter_SliderBarGraph_LookAhead.html



3. Modify the new Display Template


Open the copy of the Display Template you just renamed.

The template needs to be slightly modified to cater for the different date range that will be sent to it. It's important to note that this template (that you will modify) will be directly tied to the date range values that you set in the refinement webpart (decided on in step 1).

There's no documentation on how to customise the date range filter control. I used Chrome and the developer tools to experiment with the JavaScript until I worked out what needed to be customised.



From the image above, you can see we need to customise the BucketedFilterData object that gets populated when the template is rendered.

Though the date ranges are correct, we need to update the labels used for each date range. The number of BoundaryValues (and the date ranges they include) directly relate to the number of date intervals set in the refinement webparts properties (see the next step).

Once you've experimented and got this right, add code to the display template (html) file that will override the labels.

Open the display template, and insert the code just after line 66 (ctx.BucketedFilterData = AjaxControlToolkit.SliderRefinementControl.GetDefaultBuckets(ctx);)

ctx.BucketedFilterData.BoundaryValues[0].Label = "Oldest"
ctx.BucketedFilterData.BoundaryValues[0].NextIntervalLabel = "Older"
ctx.BucketedFilterData.BoundaryValues[1].Label = "Today"
ctx.BucketedFilterData.BoundaryValues[1].NextIntervalLabel = "Next 7 days"
ctx.BucketedFilterData.BoundaryValues[2].Label = "7 days"
ctx.BucketedFilterData.BoundaryValues[2].NextIntervalLabel = "Within 7 - 30 days"
ctx.BucketedFilterData.BoundaryValues[3].Label = "30 days"
ctx.BucketedFilterData.BoundaryValues[3].NextIntervalLabel = "Within 30 - 90 days"
ctx.BucketedFilterData.BoundaryValues[4].Label = "90 days"
ctx.BucketedFilterData.BoundaryValues[4].NextIntervalLabel = "90+ days"
ctx.BucketedFilterData.BoundaryValues[5].Label = "90 days onwards"
ctx.BucketedFilterData.Labels.min = "Now - {0}" //"min" //{0}"
ctx.BucketedFilterData.Labels.range = "Between {0} - {1}"
ctx.BucketedFilterData.Labels.value = "{0}"
ctx.BucketedFilterData.Labels.max = "Between {0} - Furthest Event" //"max" //"{1}"

Your display template should look similar to this:



You'll also need to do a little massaging of the ctx object.

I found when I did this on O365 (as opposed to SharePoint on prem), I needed to use existing managed properties for the date refinement (e.g. RefinableDate00) (I didn't have the option of creating new Managed Search properties that use the DateTime type).

When using the RefinableDate managed property, the date range filter control doesn't render. I used the Chrome developer tools to inspect the JavaScript to see what was happening. The call to GetDefaultBuckets wasn't returning any Boundary objects.

Hmm. I un-minified the GetDefaultBuckets function (courtesy of unminify.com) and stepped through the function. It turns out that ctx has a property that gets parsed by this function. That property needs to be modified to ensure the function returns the BoundaryValues.

I won't lengthen this post (anymore than it already is) with going through why this happens. All you need to know, is to add a line of JavaScript to correctly format the string value right before the call to GetDefaultBuckets (line 79 below). That line of code is:

ctx.RefinementControl.spec = "(" + ctx.RefinementControl.spec.substring(ctx.RefinementControl.spec.search(/discretize=manual/i));

Your display template file should now look like this:


Next, update the name of the filter. Go to line 2, and change the name of the template. I changed mine to Slider with bar graph (Look Ahead).


Finally, save the display template (and if you're not editing the file via webdav, upload the file to the Filters folder).

4. Export the search refinement webpart and update the properties


Once you've saved a modified version of the Filter_SliderBarGraph display template you're nearly there! All you need to do now is update the webpart properties of the refinement webpart.

The first thing to do is ensure the date property you want to filter on has been included in the refinement filters.

Edit the refinement webpart properties, and select Refinements.

In the Refinement configuration dialog, select the date managed property that you intend to include (I mapped my crawled property to RefinableDate00).

For the Display template setting, select the new display template.



Save the changes to the webpart.

Next, export the refinement webpart. Once exported, open the webpart file in a text editor.

Find the SelectedRefinementControlsJson property. The property contains a serialised JavaScript object (some JSON). You need to edit two of the properties of that JSON string.

The first property is the "useDefaultDateIntervals". Ensure the value of this property is "false".

The next property to edit is the "intervals". Remembering back to step 1, where we decided on the date interval we were going to use. This property needs to reflect that. It's an array of numbers representing the date range intervals. I've used "-1, 7, 30, 90", to give me items that are for today (-1), next 7 days (7), next 30 days (30), and the next 90 days (90).


Save the changes.

5. Import the search refinement webpart back onto the search page


The final task is to remove the original refinement webpart from the search page, and replace it with the refinement webpart you just edited. I'm sure most people reading this will know how to do that, but for completeness, I'll outline the task in bullet points.
  1. Edit the search page
  2. Remove (delete) the refinement webpart
  3. Using the same zone you removed the old refinement webpart from, click "Add webpart"
  4. On the ribbon, click "Upload webpart"
  5. Browse to the location you saved the refinement webpart to (step 4), and select the webpart
  6. Click OK to upload the webpart
  7. In the same webpart zone (again), click "Add webpart" (again).
  8. Select the "Refinement" webpart from the ribbon, and click OK
  9. Check-in and save the page.
Wollah! If you done everything correctly (assuming I've documented the process thoroughly), you should have a new look ahead date refiner!