Restricting the number of words in a rich-text editor in Episerver

This post was inspired by a recent question in the Episerver forums, where the goal was to restrict the number of words an editor can enter in a rich-text editor.

My desire was to do the minimum amount of customization possible, and not get into the weeds in Dojo (if possible). The solution turned out to be straightforward, and although I'm 100% sure it could be improved, I ran it through a few complex(ish) scenarios, and it seemed to work well.

All you need is a single validation attribute, which makes use of Html Agility Pack (available via NuGet) to parse the HTML so the words can be split out and counted:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class MaxWordsAttribute : ValidationAttribute
{
    private readonly int _maxWords;

    public MaxWordsAttribute(int maxWords)
    {
        _maxWords = maxWords;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        // Allow null
        if (!(value is XhtmlString xhtmlString))
        {
            return ValidationResult.Success;
        }

        var doc = new HtmlDocument();
        doc.LoadHtml(xhtmlString.ToHtmlString());

        var words = new List<string>();

        foreach (var node in doc.DocumentNode.SelectNodes("//text()"))
        {
            if (string.IsNullOrEmpty(node.InnerText))
            {
                continue;
            }

            var text = HttpUtility.HtmlDecode(node.InnerText);

            words.AddRange(text.Split(new[] {' ', '\r', '\n'}, StringSplitOptions.RemoveEmptyEntries)
                .Where(x => !string.IsNullOrWhiteSpace(x)));
        }

        return words.Count <= _maxWords
            ? ValidationResult.Success
            : new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
    }

    public override string FormatErrorMessage(string name)
    {
        if (string.IsNullOrEmpty(ErrorMessage))
        {
            ErrorMessage = "{0} must have {1} words or fewer.";
        }

        return string.Format(CultureInfo.InvariantCulture, ErrorMessageString, name, _maxWords);
    }
}

Not much to explain really, it's as simple as that!

Usage is just as easy:

[MaxWords(10)]
public virtual XhtmlString Description { get; set; }

If you have any improvements, suggestions or whatnot, let me know via a comment.

Comments

There are zero comments 😢