Tag : validators

Link Description Length Validator

Today’s adventure is about custom validators.

A feature I’m working on has a requirement that a link’s description must be no longer than 60 characters. I decided to look at the field validators that Sitecore comes with. The closest one I could find was the “Max Length 40” validator located at:

/sitecore/system/Settings/Validation Rules/Field Rules/Sample/Max Length 40

I setup a template and slapped that validator onto the general link field and, as I expected, it didn’t work right:

Inserting the link
Insert External Links

Displays a nice red validation error
Red Bar

To determine why less than 40 characters were throwing a validation errors, we need to look at the raw values. Behind the scenes, Sitecore is storing this in the field:

<link text="Some Link Text" linktype="external" url="http://google.com" anchor="" target="" />;

The 40 character validator is looking at the entire <link> tag and determining that it’s over 40 characters. This clearly won’t do.

Let’s take a peek under the hood. The “Max Length 40” validator indicates that its type is “Sitecore.Data.Validators.FieldValidators.MaxLengthFieldValidator,Sitecore.Kernel”

Max Length Type

Using ReSharper/dotPeek, I reflected into the MaxLengthFieldValidator and see that the evaluation logic is pretty simple

protected override ValidatorResult Evaluate()
{
    int num = MainUtil.GetInt(this.Parameters["maxlength"], 0);
    if (num <= 0)
        return ValidatorResult.Valid;
    string controlValidationValue = this.ControlValidationValue;
    if (string.IsNullOrEmpty(controlValidationValue) || controlValidationValue.Length <= num)
        return ValidatorResult.Valid;
    this.Text = this.GetText("The maximum length of the field \"{0}\" is {1} characters.", this.GetFieldDisplayName(), num.ToString());
    return this.GetFailedResult(ValidatorResult.Error);
}

It doesn’t care about what the field type is. It’s just looking at the raw value. I decided to extend this class and create my own custom validator.

The first thing I needed to do was determine whether or not the field being passed to Evaluate was a link field. Validators have a method called GetField() which will return the Field the validation is being called on. Using that, I’m able to add the following to the top of my Evaluate method:

var field = GetField();
if (field.TypeKey != "general link") return base.Evaluate();

Right now, I only care about general link fields. In the future, this can be extended/rewritten to incorporate other types of fields, but right now general link is the focus. Right now, to get the value of this field, I have to call the property ControlValidationValue. This returns the raw string value of the field. To save you the scroll, this is what that looks like:

<link text="Some Link Text" linktype="external" url="http://google.com" anchor="" target="" />

From here, I considered using some form of regex in order to parse out the text field. That could get messy. I thought to myself, “well, Sitecore’s rendering engine has to be able to translate this into an <a> tag, so it must have a way of parsing it!”. This lead me to finding the LinkField class. Using the LinkField class, I’m able to wrap the Field returned from GetField() and access all of the link properties:

var link = new LinkField(field);

There’s only one problem: that constructor will grab the link from the database. At the time this validator is called, the value has not been saved to the field. This means the link I get back doesn’t have the text populated. Thankfully, there’s another constructor that allows you to pass in the runtime value.

var link = new LinkField(field, ControlValidationValue);

From here, I’m able to read the parsed text from the link and run the validation on that. Unfortunately, I don’t have a way of passing this text to the base MaxLengthFieldValidator, so I resorted to a copy/paste of the base evaluator.

Putting it all together, we end up with:

using System;
using System.Runtime.Serialization;
using Sitecore;
using Sitecore.Data.Fields;
using Sitecore.Data.Validators;
using Sitecore.Data.Validators.FieldValidators;

namespace Website.Validators
{
    [Serializable]
    public class TextOrLinkMaxLengthFieldValidator : MaxLengthFieldValidator
    {
        public TextOrLinkMaxLengthFieldValidator()
        {
        }

        public TextOrLinkMaxLengthFieldValidator(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }

        protected override ValidatorResult Evaluate()
        {
            var field = GetField();
            if (field.TypeKey != "general link") return base.Evaluate();

            var link = new LinkField(field, ControlValidationValue);
            int num = MainUtil.GetInt(Parameters["maxlength"], 0);
            if (num <= 0) return ValidatorResult.Valid;

            string linkText = link.Text;
            if (string.IsNullOrEmpty(linkText) || linkText.Length <= num)
            {
                return ValidatorResult.Valid;
            }

            Text = GetText("The maximum length of the description text for field \"{0}\" is {1} characters.",
                GetFieldDisplayName(), num.ToString());

                
            return GetFailedResult(ValidatorResult.Error);
        }

        public override string Name
        {
            get {return "Max Length Text or Link"; }
        }
    }
}