<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1639164799743833&amp;ev=PageView&amp;noscript=1">
Diagram Views

Improve Episerver Content Editing with Validation

Brad McDavid
#CMS, #Episerver, #Code
Published on July 15, 2016
warren-wong-323107-unsplash-1

We look at the benefits of validation in Episerver and how developers can use it to save themselves and content editors a lot of headaches.

As we discuss regularly on the Diagram blog, the Episerver Content Management System (CMS) is an incredibly powerful and flexible tool that provides developers and site editors with plenty of capabilities for creating and managing content. Whether we’re looking at content personalization, creating responsive content, or any number of other topics, we’re always excited to talk about how we’re able to use this great platform to help our clients build a successful digital strategy.

In my last blog, I looked at how we can arrange tabs on the “all properties” edit view for a content editor in Episerver. I also discussed adding permissions to access those tabs, which keeps the editing experience simpler by only showing editors what they are able to access. In today’s blog, I will explore another nice feature in the editing process: validation.

Defining Content Models

Developers can define content in Episerver by creating a content model that includes all of the properties for that type of content. Episerver supports many of ASP.NET’s built-in attributes when creating a content model’s properties. Here are just a few of these attributes:

  • RequiredAttribute – the property must contain a value.
  • RegularExpressionAttribute – the property must match a regular expression pattern.
  • RangeAttribute – the property must fall within given value range, typically used for numbers.

These attributes can go a long way toward simplifying a content editor’s tasks, as well as a developer’s tasks writing code using these models. Many times without validation, a developer with have to do manual checks before using content models, which can become tedious. These manual checks can also cause additional processing overhead, and they will most likely result in many undesirable coding statements being included in the model’s view files.

Validation to the rescue

When done properly, validation can save both content editors and developers many potential headaches by ensuring the content is created correctly before publishing. The built-in attributes are a great starting point, but more complex situations often arise, such as limiting ContentArea properties to a fixed number of items or handling conditional validation. 

Episerver can handle both these situations with relative ease by creating validation classes that either derive from the ValidationAttribute abstract class or from the IValidate<ModelType> interface, where the ModelType is a piece of editable content. Below is an example of custom validation limiting ContentArea or LinkItemCollection properties to a max number using the ValidationAttribute:

using EPiServer.Core;
using EPiServer.SpecializedProperties;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;

namespace WSOL.Web.UI.Validation
{
    /// 
    /// Limits number of items for a linkitemcollection or contentarea
    /// 
    [AttributeUsage(AttributeTargets.Property)]
    public class MaxItemsAttribute : ValidationAttribute
    {
        private int _max;

        /// 
        /// Constructor with given max
        /// 
        ///
        public MaxItemsAttribute(int max)
        {
            _max = max;
        }

        /// 
        /// Determine if valid
        /// 
        ///
        /// 
        public override bool IsValid(object value)
        {
            string error = $"restricted to {_max} items!";
            var linkCollection = value as LinkItemCollection;

            if (linkCollection?.Count > _max)
            {
                ErrorMessage = error;

                return false;
            }

            var contentArea = value as ContentArea;

            if (contentArea?.FilteredItems?.Count() > _max)
            {
                ErrorMessage = error;

                return false;
            }

            return true;
        }

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

            if (!string.IsNullOrWhiteSpace(result?.ErrorMessage))
            {
                result.ErrorMessage = $"{validationContext.DisplayName} {ErrorMessage}";
            }

            return result;
        }
    }
}

Conditional Validation

Often times, as web sites grow in complexity, so does the usage of their content models, leading to issues that can be solved using validation. We had one such issue on our very own website, wearediagram.com. In several areas throughout the site, we use a featured blog post content block which dynamically gets the latest blog entry for a given category. The requirements for these content blocks changed; in some some spots, we wanted to be able to manually select featured blogs. Instead of creating a new content type to accommodate this, we just added a few new properties to the model type, a checkbox to toggle manual mode, and a content reference to store the manual blog reference.

This created a situation in which there were now two potential fields from which to get a featured blog post. What if the editor enabled the manual checkbox, but didn’t set a value for either the manual blog post field or the parent reference to pull a dynamic a blog post? We were able to solve this issue by implementing the IValidate<FeaturedBlogPost> interface to handle those checks for us, ensuring that the content is always created correctly. Below is the code that handles the validation for us:

namespace WSOL.Web.Business.Validation
{
    using EPiServer.Core;
    using EPiServer.Validation;
    using System.Collections.Generic;
    using System.Linq;
    using WSOL.Web.Models.Blocks;

    public class FeaturedBlogPostRequired : IValidate
    {
        public IEnumerable Validate(FeaturedBlogPostData instance)
        {
            if (instance.UseManual && ContentReference.IsNullOrEmpty(instance.ManualFeaturedPost))
            {
                return new[]
                {
                    new ValidationError()
                    {
                       ErrorMessage = "Manual Blog post cannot be empty!",
                       PropertyName = instance.GetPropertyName(x => x.ManualFeaturedPost),
                       Severity = ValidationErrorSeverity.Error,
                       ValidationType = ValidationErrorType.AttributeMatched
                    }
                 };

            }
            else if (!instance.UseManual && ContentReference.IsNullOrEmpty(instance.AutomaticFeaturedPostParent))
            {
                return new[]
                 {
                    new ValidationError()
                    {
                       ErrorMessage = "Automatic Blog Posts Parent cannot be empty!",
                       PropertyName = instance.GetPropertyName(x => x.AutomaticFeaturedPostParent),
                       Severity = ValidationErrorSeverity.Error,
                       ValidationType = ValidationErrorType.AttributeMatched
                    }
                 };
            }

            return Enumerable.Empty();
        }
    }
}

If the content editor tries to publish changes that do not meet the validation, they will be notified in the UI with the following message:

Validation_Error_Message.jpg

For developers, keeping the view clean for the FeaturedBlogPost model is as simple as adding a calculated property to the model, which can then be referenced by the view:

private BlogPostData _FeaturedPost;

        [Ignore]
        public virtual BlogPostData FeaturedPost
        {
            get
            {
                if (_FeaturedPost == null)
                {
                    _FeaturedPost = UseManual ? ManualFeaturedPost.GetContent() : AutomaticFeaturedPost.GetContent();
                }

                return _FeaturedPost;
            }
        }

IValidate can also be useful for ensuring that metadata fields aren’t duplicated. We’ve had many cases where certain fields are required for SEO, but clients wanted an additional area to add more fields to a page, such as a page summary. In order to avoid multiple fields that might duplicate metadata information, we utilize the IValidate interface to ensure editors don’t create metadata in a free form area that is already accounted for in other fields.

Episerver’s support of custom validation is an essential part of the content editing process. Validation, when done correctly, ensures content is created the right way every time, which can help both content editors and developers avoid many headaches. Developers will also write much less code when handling or displaying data, and they can feel comfortable in knowing the content was created within the desired limitations.

Do you have any questions about how to use validation to create the best editing experience for your site? Do you want to know more about how Diagram can help you make the most of your Episerver website? Please contact us, and we’ll work with you to build not just a great website, but a successful digital strategy.