Adding Episerver Content Type Icons in 2 minutes!

By: Chris Magee

Two devs sitting in front of a computer with the Nansen logo

episerver

optimizely

Have tons of Episerver content? Can't easily distinguish content types from eachother in the page tree? Here's how to leverage some episerver-provided icons that look great!

Where do the icons come from?

Giving credit where credit is due, Eric (https://ericceric.com/2017/06/07/use-episervers-content-icons-as-site-tree-icons/) has a great blog post on the built-in icons in Episerver's UI. These icons give us a large amount of well-designed icons at our disposal, without having to load in custom CSS or images.

Eric's blog post also has an example of how you could implement the content icons using an interface, but I wanted to take this one step further. Maybe I'm weird, but I want to be able to leverage every single one of those icons without having to make an interface that implements the icon class, so here's what I did:

The code

icon code files

I will likely release this functionality via a Nuget package in the near future, so I created a class library that contains all of the code required. It's just three files:

  1. An enum of all the different icon names
  2. An attribute that I can apply to any content type I want
  3. An initialization module that does the magic

ContentIcon.cs

As mentioned, this is an enum of all of the different icon names from Eric's blog post (link above) except I modified the enum names to be exactly the icon's class without the "epi-icon" prefix.

public enum ContentIcon
{
    Clock,
    Checkmark,
    Stop,
    Versions,
    Revert,
    Undo,
    Redo,
    Pen,
    PenDisabled,
    DuplicatePage,
    Primary,
    Trash,
    Users,
    Search,
    Plus,
    Minus,
    Star,
    Settings,
    Lock,
    Pin,
    Page,
    Folder,
    SharedBlock,
    Up,
    Down,
    Reload,
    Share,
    Download,
    DnD,
    ContextMenu,
    Rename,
    Left,
    Right,
    User,
    Hidden,
    NewWindow,
    Catalog,
    Category,
    Product,
    SKU,
    Package,
    Bundle,
    Boxes,
    References,
    Bubble,
    Location,
    Mail,
    Telephone,
    Website,
    Link,
    Upload,
    Error,
    Warning,
    Info,
    Pricing,
    Cut,
    Copy,
    Paste,
    Detach,
    List,
    Thumbnails,
    Tiles,
    Project,
    Published,
    PreviouslyPublished,
    Truck,
    Cart,
    Sort,
    Campaign,
    PublishProject,
    Promotion,
    Layout,
    SortAscending,
    SortDescending,
    Edited,
    Bell,
    Help,
    Segments,
    Guides,
    ObjectRoot,
    ObjectTrash,
    ObjectStart,
    ObjectPage,
    ObjectContainer,
    ObjectInternalLink,
    ObjectExternalLink,
    ObjectNoLink,
    ObjectFetchContent,
    ObjectContainerFetchContent,
    ObjectSharedBlock,
    ObjectFolder,
    ObjectFolderOpen,
    ObjectCatalog,
    ObjectCategory,
    ObjectProduct,
    ObjectVariation,
    ObjectPackage,
    ObjectBundle,
    ObjectUnknown,
    ObjectImage,
    ObjectFolderAllSites,
    ObjectFolderThisSite,
    ObjectPageContextual,
    ObjectSharedBlockContextual,
    ObjectOptimizingBlockContextual,
    ObjectProductContextual,
    ObjectContainerContextual,
    ObjectVideo,
    ObjectExternalFolder,
    ObjectExternalFolderOpen,
    ObjectItem,
    ObjectCart,
    ObjectTruck,
    ObjectCampaign,
    ObjectPromotion,
    ObjectProject,
    Success,
    Danger
}

Sweet, so now we have the ability to use ContentIcon.. What's next?

ContentIconAttribute.cs

We'll make a custom attribute with a constructor that takes a ContentIcon enum value to put on top of any content type we want.

public class ContentIconAttribute : Attribute
{
    public ContentIcon Icon { get; set; }

    public ContentIconAttribute(ContentIcon icon)
    {
        Icon = icon;
    }
}

Sweet, so now we can decorate classes with "[ContentIcon(ContentIcon.)]". What's next?

Initializer.cs

On startup, we need to modify the UIDescriptors for classes that have the [ContentIcon] attribute on them. Now, I'm going to drop the entire code file here, but I'll break it down right below.

[InitializableModule]
[ModuleDependency(typeof(InitializableModule))]
public class Initializer : IInitializableModule
{
    public void Initialize(InitializationEngine context)
    {
        var registry = context.Locate.Advanced.GetInstance<UIDescriptorRegistry>();
        var classes = GetDescriptorClasses();

        foreach (var descriptor in registry.UIDescriptors)
        {
            if (classes.ContainsKey(descriptor.ForType))
            {
                descriptor.IconClass += " " + classes[descriptor.ForType];
            }
        }
    }

    public void Uninitialize(InitializationEngine context)
    {
    }

    private Dictionary<Type, string> GetDescriptorClasses()
    {
        var typesWithAttribute = GetTypesWithAttribute();

        var descriptors = new Dictionary<Type, string>();

        foreach (var type in typesWithAttribute)
        {
            var iconAttribute = (ContentIconAttribute)Attribute.GetCustomAttribute(type, typeof(ContentIconAttribute));

            descriptors.Add(type, $"epi-icon{iconAttribute.Icon.ToString()}");
        }

        return descriptors;
    }

    private List<Type> GetTypesWithAttribute()
    {
        var validAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic);
        var typesWithAttribute = new List<Type>();

        foreach (var assembly in validAssemblies)
        {
            typesWithAttribute.AddRange(assembly.GetTypes().Where(type => type.IsDefined(typeof(ContentIconAttribute), false)));
        }

        return typesWithAttribute;
    }
}  

As promised, here's what the initializer is doing in general:

  1. On Startup, scanning all assemblies for types that have the [ContentIcon] attribute
  2. Finding the UI Descriptors that apply to those types from Episerver's UIDescriptorRegistry
  3. Setting the IconClass on the UI Descriptor, using the pattern I mentioned above, "epi-icon" + .

Alright, now assuming you've either implemented the above or pulled the nuget package for this, how do you use it?


[ContentIcon(ContentIcon.Settings)]
[ContentType(GUID = "DC417FE5-A405-43C3-8F9C-868A841E3DE1", DisplayName = "Global Settings Page")]
public class GlobalSettingsPage : PageData
{
    ... Your properties ...
}  

Pretty easy, right? Now you can make beautiful and self-explanatory page tree icons in just a couple minutes for all your content types.

episerver page tree screenshot example

If you found this helpful, had trouble with any of the code provided, or found a bug, please let me know via the contact information available in the right bar or in the comments.

Edit #1: Coloring your Icons

Thank you again to Eric, who shared a link to his post on coloring content icons (https://ericceric.com/2017/07/09/colorize-your-site-icons/). Adding colors is a great idea, it can add tons of new icons just by switching up the colors. In the same vein, I extended the [ContentIcon] attribute to support colors. Here's how!

ContentIconColor.cs

public enum ContentIconColor
{
    Default,
    Inverted,
    Active,
    Success,
    Danger,
    Warning,
    Info
}

ContentIconAttribute.cs

public class ContentIconAttribute : Attribute
{
    public ContentIcon Icon { get; set; }
    public ContentIconColor Color { get; set; }

    public ContentIconAttribute(ContentIcon icon, ContentIconColor color = ContentIconColor.Default)
    {
        Icon = icon;
        Color = color;
    }
}

Initializer.cs

Modify the method to optionally apply the style if it's not the default icon.

private Dictionary<Type, string> GetDescriptorClasses()
{
    var typesWithAttribute = GetTypesWithAttribute();

    var descriptors = new Dictionary<Type, string>();

    foreach (var type in typesWithAttribute)
    {
        var iconAttribute = (ContentIconAttribute)Attribute.GetCustomAttribute(type, typeof(ContentIconAttribute));

        var iconClass = $"epi-icon{iconAttribute.Icon.ToString()}";

        if(iconAttribute.Color != ContentIconColor.Default)
        {
            iconClass += $" epi-icon--{iconAttribute.Color.ToString().ToLower()}";
        }

        descriptors.Add(type, iconClass);
    }

    return descriptors;
}

And there you go! You can apply new colors with an optional variable in the [ContentIcon] attribute.

[ContentIcon(ContentIcon.User, ContentIconColor.Active)]
[ContentType(GUID = "DC417FE5-A405-43C3-8F9A-868A841E3DE1", DisplayName = "Job Page")]
public class JobPage : PageData
{
    ... Your properties ...
}

here's what it looks like: content icon colors