Making a configurable color picker for Episerver: Part 2

Edit: So I went ahead and packaged this up as a NuGet package and posted it to the Episerver NuGet Feed, should you want to install it and not look into the code you can find it here.

The last blog post dealt with the easy part of making a configurable color picker for Episerver, basically creating a Dojo widget (by extending the Dijit _PaletteMixin) which persists a color ID and not the Hex code.

This blog post is going to be a little quicker paced essentially dealing with everything else required, which includes:

  1. Creating a color palette manager, to read the color palette configurations (from a config file) and updating the editor descriptor
  2. Creating a custom property to make retrieving a color value simply
  3. and finally, wrapping up everything

Sounds like a lot, but honestly, I'm going to run through these as quickly as possible just highlighting stuff that I think is at least a little interesting (that's a relative term!). You can, however, find the entire code in its own reasonably-well-documented and commented GitHub repository if you want to dive into things.

The color palette manager

Ok, the first step is creating a color palette manager which will load in the colors from a config file. There isn't much Episerver to this, it's really just loading all the defined palettes from a config.

However, because I wanted color palettes to be definable based on the specific site in a multi-site, we allow the user to define a host in the config:

<colorPalette host="alloy.local">
  <colors>
    <color id="1">
      <value>#ff00ff</value>
      <name>Magenta</name>
    </color>
  </colors>
</colorPalette>

This means when retrieving the color palette our method has to get the site URL for the current SiteDefinition, which is really pretty trivial (the following is slightly simplified):

public IColorPalette GetPalette()
{
    var palettes = GetPalettes();
    
    var siteUrl = SiteDefinition.Current.SiteUrl;

    var palette = palettes.SingleOrDefault(x => string.Equals(x.Host, siteUrl.Host, StringComparison.OrdinalIgnoreCase));

    if (palette != null)
    {
        return palette;
    }

    Logger.Error($"No palette matches host {siteUrl.Host}.");

    return null;
}

Finally, I also applied some caching when loading all the palettes from the config (to ensure we're not doing so continuously), this was as simple as using the the Episerver IObjectInstanceCache.

The EditorDescriptor created in the first part also had to be wired up to actually use this manager and pass the color values to the Dojo widget.

As mentioned, the full code is in a public repository and the README also has more information about the config structure.

Introducing a custom property

The first part of this post left us with the color value being accessible as a nullable integer, but that isn't really that convenient when working with it. Wouldn't it be nice if we could instead get the full color object? Something like:

[UIHint(ColorPickerUIHint.ColorPicker)]
[BackingType(typeof(PropertyPaletteColor))]
public virtual IColor Color { get; set; }

Where IColor is something like this:

public interface IColor
{
    // The ID.
    int Id { get; }

    // The name.
    string Name { get; }

    // The value (Hex code, RGB code etc.)
    string Value { get; }
}

Well, the good news is this is really simple in Episerver — all we need to do is create a custom property! This is still persisted in the database as a nullable integer but we create a custom getter which retrieves the actual color (via the manager) when getting the property, similarly when setting we only need to keep the ID. Overall it's only a few lines:

[PropertyDefinitionTypePlugIn]
public class PropertyPaletteColor : PropertyNumber
{
    public override object Value
    {
        get => !Number.HasValue ? null : ServiceLocator.Current.GetInstance<IColorPaletteManager>().GetColor(Number.Value);
        set => base.Value = (value as IColor)?.Id ?? value;
    }

    public override Type PropertyValueType => typeof(Color);

    public override object SaveData(PropertyDataCollection properties) { return Number; }
}

With than in place, we're nearly finished...

Wrapping up

Really that's the meat-and-potatoes of this color picker, there was inevitably some fiddling and faffing around trying to get everything to work together nicely. This meant some more tweaks to the JavaScript created in part one (you can see the current version here), although the essential functionality remains the same.

Hopefully there was some value in documenting the process and I'm open to suggestions and improvements, right now I've tested the color picker against Episerver 10 and 11 and it works well in both.

 

Comments

There are zero comments 😢