The Need for Custom Field Validators in Sitecore
In a recent project, one of the components we were building was utilizing a Single-line Text
field to capture time in the format h:mm
and another dropdown to select am
vs pm
. We could’ve used the time zone feature, but because of Daylight Savings confusions in the past, this was a better approach to ensure the date and time shown was always accurate to their needs.
The Problem
There are two primary issues that can come about with storing time within a string field. The first being that OOTB there’s no validation to ensure that people are entering in time in the correct format and because of the extra computed fields that occur, it needs to be correct. The second, is that if it was incorrect and not fixed, and then the user published the item it would cause publishing to fail, again, due to the nature of a couple pipelines at play. And if the user is publishing many, many items it would be near impossible for them to determine where the problem is. And silently logging the issue does not help anyone. Great for debugging of sorts, but
Creating a Custom Field Validator in Sitecore
It’s actually a two part solution. The first being building a custom field validator. The second revolves some smart Exception handling.
The Validator
Creating a custom validator is remarkably easy and to be honest, should really be utilized to a greater degree in my opinion. Here’s the primary code to get this to work:
using Sitecore.Data.Validators;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Serialization;
using Sitecore.Data.Fields;
namespace ABCCompany.Foundation.Extensions.Validators
{
public class CustomTimeValidator : StandardValidator
{
public CustomTimeValidator() : base()
{
}
public CustomTimeValidator(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
protected override ValidatorResult Evaluate()
{
// Get the field value from the Parameters property
Field field = this.GetField();
string fieldValue = field?.Value;
if (String.IsNullOrEmpty(fieldValue)) return GetFailedResult(ValidatorResult.CriticalError);
// Perform the validation logic
if (!string.IsNullOrEmpty(fieldValue))
{
// Implement your validation logic here
// You can use regular expressions or other parsing methods to check the format
// Example: Regular expression pattern to match the h:mm format
string pattern = @"^\d{1,2}:\d{2}$";
if (!System.Text.RegularExpressions.Regex.IsMatch(fieldValue, pattern))
{
// Set the error message if the validation fails
this.Text = "The time format should be in h:mm.";
return GetFailedResult(ValidatorResult.CriticalError);
}
}
// Return successful result if the validation passes
return ValidatorResult.Valid;
}
public override string Name
{
get { return "Custom Time Validator"; }
}
protected override ValidatorResult GetMaxValidatorResult()
{
// Return the maximum validator result
return ValidatorResult.CriticalError;
}
}
}
Essentially everything within the Evaluate()
function is where you will determine if the field value meets the necessary criteria or not. This can be as complicated or as simple as you wish.
Next we have the configuration file patch.
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:localenv="http://www.sitecore.net/xmlconfig/localenv">
<sitecore role:require="Standalone or ContentDelivery or ContentManagement">
<pipelines>
<saveUI>
<processor type="ABCCompany.Foundation.Extensions.Validators.CustomTimeValidator, ABCCompany.Foundation.Extensions" />
</saveUI>
</pipelines>
</sitecore>
</configuration>
And finally, we have creating the corresponding Sitecore item that we can then assign to the field as part of validation.
This is located in /sitecore/system/settings/Validation Rules/Field Rules
.
Exception Handling
Normally if we have a pipeline and an invalid time was entered and then subsequently published you would likely see an error in the Publishing modal talking about some kind of DateTime
parse issue but in no way indicate on which item or which field the error occurred. So to show you how this works I’ve now entered an invalid time.
And then we published it. On purpose.
So what we’ve done here is we’ve wrapped the DateTime.ParseExact
function in a try
/ catch
. And then within the catch statement, we’ve thrown a new Exception
with a custom message detailing the cause of the error, the item ID it happened on, and even the path to the item. Using some simple /n
within the string we’ve added some simple formatting to ensure it’s more readable.
Simple things like this just ensure your authors' experiences are less stressful.