Sitecore Validators: Validating image width, height and aspect ratio

In my previous post I explained how to use Sitecore Field Validators, and some time ago I explained the basics behind validators. Now it’s time to deliver a real validator: The image validator.

Sitecore delivers a ImageSizeValidator, but this validator only validates that the image does not exceed a certain amount of megabytes in size.

This validator validates that the selected image conforms within certain width/height aspects, or conforms to a certain aspect ratio.

Usually users should not worry about image sizes as it is your responsibility as a developer to rescale any user-selected image to the size needed on the web. However, under certain circumstances it can be very helpful to limit the users choices (especially a minimum width or height can be usefull to avoid having images being up-scaled).

The code is simple. Grab the image from the field and check that the width/height or aspect ratio is within the selected ranges. Here is the code:

using System;
using System.Runtime.Serialization;
using Sitecore;
using Sitecore.Configuration;
using Sitecore.Data;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Data.Validators;
using Sitecore.Diagnostics;
using Sitecore.Shell.Applications.ContentEditor;

namespace Your.Namespace.FieldValidators
{
  /// <summary>
  /// Validator that validates than an image conforms with specific width/height requirements
  /// </summary>
  /// <remarks>
  /// Validator accepts the following parameters:
  /// MinWidth = minimum image width
  /// MaxWidth = maximum image width
  /// MinHeight = minimum image height
  /// MaxHeight = maximum image height
  /// AspectRatio = The required aspect ratio (width/height width 2 decimals)
  /// Set minwidth/height and maxwidth/maxheight to the same values to validate an exact image size
  /// </remarks>
  [Serializable]
  public class ImageWidthHeightValidator : StandardValidator
  {
    /// <summary>
    /// Initializes a new instance of the <see cref="ImageWidthHeightValidator"/> class.
    /// </summary>
    public ImageWidthHeightValidator()
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="ImageWidthHeightValidator"/> class.
    /// </summary>
    /// <param name="info">The Serialization info.</param>
    /// <param name="context">The context.</param>
    public ImageWidthHeightValidator(SerializationInfo info, StreamingContext context) : base(info, context)
    {
    }

    /// <summary>
    /// Gets the name.
    /// </summary>
    /// <value>The validator name.</value>
    public override string Name
    {
      get { return "Image Width/Height"; }
    }

    /// <summary>
    /// Gets the max validator result.
    /// </summary>
    /// <returns>The max validator result.</returns>
    /// <remarks>
    /// This is used when saving and the validator uses a thread. If the Max Validator Result
    /// is Error or below, the validator does not have to be evaluated before saving.
    /// If the Max Validator Result is CriticalError or FatalError, the validator must have
    /// been evaluated before saving.
    /// </remarks>
    protected override ValidatorResult GetMaxValidatorResult()
    {
      return GetFailedResult(ValidatorResult.CriticalError);
    }

    /// <summary>
    /// When overridden in a derived class, this method contains the code to determine whether the value in the input control is valid.
    /// </summary>
    /// <returns>The result of the evaluation.</returns>
    protected override ValidatorResult Evaluate()
    {
      int minWidth = MainUtil.GetInt(Parameters["MinWidth"], 0);
      int minHeight = MainUtil.GetInt(Parameters["MinHeight"], 0);
      int maxWidth = MainUtil.GetInt(Parameters["MaxWidth"], int.MaxValue);
      int maxHeight = MainUtil.GetInt(Parameters["MaxHeight"], int.MaxValue);
      float aspectRatio = MainUtil.GetFloat(Parameters["AspectRatio"], 0.00f);

      if (ItemUri != null)
      {
        if (MediaItem == null)
          return ValidatorResult.Valid;

        int width = MainUtil.GetInt(MediaItem.InnerItem["Width"], 0);
        int height = MainUtil.GetInt(MediaItem.InnerItem["Height"], 0);

        if (minWidth == maxWidth && minHeight == maxHeight && (width != minWidth || height != minHeight))
          return GetResult("The image referenced in the Image field \"{0}\" does not match the size requirements. Image needs to be exactly {1}x{2} but is {3}x{4}",
                           GetField().DisplayName, minWidth.ToString(), minHeight.ToString(), width.ToString(),
                           height.ToString());

        if (aspectRatio != 0.00f && height != 0 && (Math.Round((double) width/height, 2) != Math.Round(aspectRatio, 2)))
          return GetResult("The image referenced in the Image field \"{0}\" has an invalid aspect ratio. The aspect ratio needs to be {1} but is {2}.",
                           GetField().DisplayName, Math.Round(aspectRatio, 2).ToString(),
                           (Math.Round((double) width/height, 2).ToString()));

        if (width < minWidth)
          return GetResult("The image referenced in the Image field \"{0}\" is too small. The width needs to be at least {1} pixels but is {2}.",
                           GetField().DisplayName, minWidth.ToString(), width.ToString());

        if (height < minHeight)
          return GetResult("The image referenced in the Image field \"{0}\" is too small. The height needs to be at least {1} pixels but is {2}.",
                           GetField().DisplayName, minHeight.ToString(), height.ToString());

        if (width > maxWidth)
          return GetResult("The image referenced in the Image field \"{0}\" is too big. The width needs to at most {1} pixels but is {2}.",
                           GetField().DisplayName, maxWidth.ToString(), width.ToString());

        if (height > maxHeight)
          return GetResult("The image referenced in the Image field \"{0}\" is too big. The height needs to be at most {1} pixels but is {2}.",
                           GetField().DisplayName, maxHeight.ToString(), height.ToString());
      }
      return ValidatorResult.Valid;
    }

    /// <summary>
    /// Gets the media item.
    /// </summary>
    /// <value>The media item.</value>
    private MediaItem MediaItem
    {
      get
      {
        ItemUri itemUri = ItemUri;

        Field field = GetField();
        if (field == null)
          return null;

        if (string.IsNullOrEmpty(field.Value))
          return null;

        if (field.Value == "<image />")
          return null;

        string attribute = new XmlValue(field.Value, "image").GetAttribute("mediaid");
        if (string.IsNullOrEmpty(attribute))
          return null;

        Database database = Factory.GetDatabase(itemUri.DatabaseName);
        Assert.IsNotNull(database, itemUri.DatabaseName);
        return database.GetItem(attribute);
      }
    }

    /// <summary>
    /// Gets the result.
    /// </summary>
    /// <param name="text">The text.</param>
    /// <param name="arguments">The arguments.</param>
    /// <returns></returns>
    private ValidatorResult GetResult(string text, params string[] arguments)
    {
      Text = GetText(text, arguments);
      return GetFailedResult(ValidatorResult.Warning);
    }

  }
}

The validator relies heavily on parameters, which is set in the validator’s Parameters’s field. See Using Sitecore Field Validators on how to do it. Here is a couple of parameter examples:

  • MinWidth=110&MaxWidth=110&MinHeight=95&MaxHeight=95
    Image must be exactly 110×95 pixels.
  • MinWidth=430&MaxWidth=430&MinHeight=1&MaxHeight=9999
    Image must be exactly 430 pixels wide, but the height is optional
  • MinWidth=100
    Image must be at least 100 pixels wide.
  • AspectRatio=1.5
    Image width must be at least 1.5 times the height.
    For example 100×150 or 200×300 is accepted.

Remember that you can use the Result parameter to determine the validation result.

About briancaos

Developer at Pentia A/S since 2003. Have developed Web Applications using Sitecore Since Sitecore 4.1.
This entry was posted in c#, Sitecore 6 and tagged , , . Bookmark the permalink.

3 Responses to Sitecore Validators: Validating image width, height and aspect ratio

  1. Pingback: Tools: Remove whitespaces at the end of lines in Visual Studio - Rikki Mongoose

  2. Pingback: Sitecore: Создаём Validation Rule - Rikki Mongoose

  3. Ben Sewards says:

    Hey Brian,

    Thanks for this extended validator! I made some quick adjustments based on the fact that I needed to return a Fatal Result on INITIAL save. The problem that I encountered was that the MediaItem property was trying to access the “mediaid” property that wasn’t available until Sitecore processed this attribute upon save. To get around this, I simple replaced that code with:

    MediaItem media = database.GetItem(“/sitecore/media library” + field.InheritedValue);
    return media;

    One other thing to note is that in Evaluate(), you are making 2 more get calls to the MediaItem property after the property is compared against null. We can simply save resources by first setting this property to a local (I set it as an instance variable of the class) MediaItem:

    _mediaItem = MediaItem;
    if (_mediaItem == null)
    return ValidatorResult.Valid;

    int width = MainUtil.GetInt(_mediaItem.InnerItem[“Width”], 0);
    int height = MainUtil.GetInt(_mediaItem.InnerItem[“Height”], 0);

    With these adjustments, the field based rule will correctly validate on the initial save. And if the MediaItem does not pass one of the Evaluate() conditions, the save process will be blocked IF the parameter of the rule has appended &Result=FatalError OR the GetResult() method is changed to the following:

    return GetFailedResult(ValidatorResult.FatalError);

    Thanks again!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s