Welcome to the navigation

Nisi sint laboris dolor deserunt sed aliqua, cupidatat ex esse enim in consequat, voluptate id anim velit labore laborum, tempor dolor et consectetur minim magna. Nisi minim pariatur, magna proident, labore cupidatat deserunt elit, sed consequat, veniam, laboris duis voluptate consectetur reprehenderit dolore dolor culpa dolore quis esse in tempor

Yeah, this will be replaced... But please enjoy the search!

Validate Optimizely XhtmlString property length

Since the built in attributes [StringLength], [MinLength] and [MaxLength] doesn't work when using a XhtmlString property we need to implement a custom ValidationAttribute.

The goal

The goal is to have an attribute that only will count the characters entered excluding any HTML. We must be able to set the min length and the max length, also it would be nice to have the possibility to enter a custom error message

The list of demands are

  • Must validate written text excluding any HTML-markup
  • Must have a MinLength
  • Must have a MaxLength
  • Should have an option to set a custom error message
[Display(Name = "MainContent")]
[Required]
[XhtmlStringLengthValidation(50, 500)]
public virtual XhtmlString MainContent{ get; set; }

Custom error message example

[XhtmlStringLengthValidation(50, 500, CustomErrorMessage = "The Main content field must contain more than {0} characters but less than {1}.")]

XhtmlStringLengthValidationAttribute

A ValidationAttribute example

///
using EPiServer.Core;
using EPiServer.Core.Html;
using System.ComponentModel.DataAnnotations;
using System.Web;
///

[AttributeUsage(AttributeTargets.Property)]
public class XhtmlStringLengthValidationAttribute : ValidationAttribute
{
    public string? CustomErrorMessage { get; set; }

    private readonly int _minLength;
    private readonly int _maxLength;

    public XhtmlStringLengthValidationAttribute(int minLength, int maxLength)
    {
        _minLength = minLength;
        _maxLength = maxLength;
    }

    public override bool IsValid(object value)
    {
        if (_minLength == 0 && value is null)
        {
            return true;
        }

        var propertyDecodedText = GetDecodedText(value);

        return !(propertyDecodedText.Length < _minLength || propertyDecodedText.Length > _maxLength);
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var result = base.IsValid(value, validationContext);

        if (!string.IsNullOrWhiteSpace(result?.ErrorMessage))
        {
            var propertyDecodedText = GetDecodedText(value);

            result.ErrorMessage = string.IsNullOrEmpty(CustomErrorMessage)
                ? $"{result.ErrorMessage} The text must be between {_minLength} and {_maxLength} long ({propertyDecodedText.Length})."
                : string.Format(CustomErrorMessage, _minLength, _maxLength);
        }

        return result;
    }


    private string GetDecodedText(object value)
    {
        if (value is XhtmlString property)
        {
            var propertyHtml = property.ToString();
            var propertyPlainText = TextIndexer.StripHtml(propertyHtml, maxTextLengthToReturn: propertyHtml.Length);
            var propertyDecodedText = HttpUtility.HtmlDecode(propertyPlainText);

            return propertyDecodedText;
        }

        return string.Empty;
    }
}

 

A similar result can be achieved using IValidate, I do hoever prefer using attributes since they are more targeted and implementation specific.