Views: 6100
Number of votes: 5
Average rating:

Modifying the EPiServer UI views

Last Friday I saw a request on the forums if it was possible to change the order of the media and the shared blocks components so that the media component would be the one shown by default. I responded that I did not know a god way but since there seems to be several people wanting this I did a quick investigation if there was a decent ways to do this. I had three ideas to investigate:

  1. Hook up to the initialization and change the sort order of the file manager component definition.
  2. Replace the media component by registering a replacement component in the IOC container.
  3. Create an IViewTransformer that would resort the file manager component.

How a view is defined and set up

When a view is created for the EPiServer UI, there are several steps and extension points. Basically the view itself defines the main structure of the view and the possible plug-in areas. If we look at the home view of CMS it looks something like this:

  • Root
    • Navigation Pane [pluggable]
      • Default Navigation Group [pluggable]
    • Main Area
    • Assets Pane [pluggable]
      • Default Assets Group [pluggable]

And after the view system has been created and components have been plugged in a default installation will look something like this:

  • Root
    • Navigation Pane [pluggable]
      • Default Navigation Group [pluggable]
        • Pages
        • Sites
        • Tasks
    • Main Area
    • Assets Pane [pluggable]
      • Default Assets Group [pluggable]
        • Shared Blocks
        • File Manager

Components and Component Definitions

For each view that is created there will be a recursive look through to see if there are components that should be plugged into the view. When components are created there are two entities that you should be aware of:

  1. Component Definition – Responsible for defining where components should be plugged in, managing access rights and creating the actual component. This will normally use the singleton pattern.
  2. Component – The actual component that is created each time a view is loaded.

As a partner developer, you would usually only care about the ComponentDefinition and not the actual components created since this is mostly a bearer of data for the current view.

If you want to know more how the view is set up I suggest reading the following SDK article: http://sdkbeta.episerver.com/SDK-html-Container/?path=/SdkDocuments/EPiServerFramework/7/Knowledge%20Base/Developer%20Guide/User%20Interface/View%20Composition/View%20Composition.htm&vppRoot=/SdkDocuments//EPiServerFramework/7/Knowledge%20Base/Developer%20Guide/

Changing the sort order for the File Manager component definition

The first idea I had was to get hold of the singleton instance of the File Manager Component Definition in an initialization module and just change the sort order of the instance. The code I tried looks like this:

public void Initialize(Framework.Initialization.InitializationEngine context)
        {
            var componentManager = ServiceLocator.Current.GetInstance<IComponentManager>();
            var mediaComponentDefinition = componentManager.GetComponentDefinition("EPiServer.Cms.Shell.UI.Components.MediaComponent") as ComponentDefinitionBase;
 
            if(fileManagerComponentDefinition != null)
            {
                fileManagerComponentDefinition.SortOrder = 90;
            }
        }

Note: I’m using "EPiServer.Cms.Shell.UI.Components.MediaComponent" instead of typeof(MediaComponent).FullName to not have to take a dependency on the CMS UI add-on. This is not needed in EPiServer 7.6 and above since it's been moved to the bin folder.

Unfortunately the code above does not compile since SortOrder has a protected setter Ledsen so we have to move to the next potential solution.

Replace the file manager component

The next idea I had was to replace the media component by registering a custom component in the IOC container (http://sdkbeta.episerver.com/SDK-html-Container/?path=/SdkDocuments/EPiServerFramework/7/Knowledge%20Base/Developer%20Guide/User%20Interface/View%20Composition/How%20To/Replace%20a%20component%20globally.htm&vppRoot=/SdkDocuments//EPiServerFramework/7/Knowledge%20Base/Developer%20Guide/).

The idea is that whenever a component should be created, it’s possible to let the IOC container create an instance of custom type registered in the container. The code to do this would look something like this:

[InitializableModule]
    [ModuleDependency(typeof(Web.InitializationModule))]
    public class ClassFactoryInitialization : IInitializableModule
    {
        public void Initialize(Framework.Initialization.InitializationEngine context)
        {
            context.Container.Configure(ce =>
            {
                ce.For<IComponent>().Add<MovedFileManagementComponent>().Named("EPiServer.Cms.Shell.UI.Components.MediaComponent");
            });
        }
    }
 
    public class MovedMediaComponent : ComponentBase
    {
        public MovedMediaComponent()
            : base("epi-cms.component.FileManagement")
        {
            //Define all settings to mock the "real" media component.
        }
    }

 

Though doable I noticed that I have to create my custom component without any reference to the original component definition. This would simply cause too much copy and paste of settings to make a copy of the standard media component and with the risc of breaking when upgrading EPiServer. So I decided to move on to the third option…

Registering an IViewTransformer to move the Media Component

Last idea I had was to register an IViewTransformer. An IViewTransformer can be used to modify the view and there are actually quite a few used in the system:

  • Configuration view transformer – Makes it possible to modify some parts of the view through configuration.
  • Personalization view transformer – Enables user customizable parts of the view (add/remove/sort gadgets)
  • Sort component transformer – Sorts the actual components according to their given sort order. This makes it possible to plug-in a component at a specific place in the UI.

The idea here is to modify the sort order of the file manager component before the sort component transformer does the actual sorting. The code looks as follows:

using System;
using EPiServer.Shell.ViewComposition;
 
namespace Samples
{
    /// <summary>
    /// Moves the media component before the blocks component when creating a view.
    /// </summary>
    [ViewTransformer]
    public class SortComponentTransformer : IViewTransformer
    {
        /// <summary>
        /// Used to select the order of execution when there are several <see cref="IViewTransformer"/>s.
        /// </summary>
        public int SortOrder { get { return 7900; } }//Note: Default sort transformer has 8000
 
        /// <summary>
        /// Transforms the view according to the rules for the transformer.
        /// </summary>
        /// <param name="view">The view.</param>
        /// <param name="principal">The principal.</param>
        public void TransformView(ICompositeView view, System.Security.Principal.IPrincipal principal)
        {
            if (!String.Equals(view.Name, "/episerver/cms/home", StringComparison.OrdinalIgnoreCase))
            {
                //Only apply sorting for the cms home view.
                return;
            }
            SortContainerRecursively(view.RootContainer);
        }
 
        private void SortContainerRecursively(IContainer parentContainer)
        {
            foreach (IComponent component in parentContainer.Components)
            {
                IContainer childContainer = component as IContainer;
 
                if (childContainer != null)
                {
                    SortContainerRecursively(childContainer);
                }
                else if (String.Equals(component.DefinitionName, "EPiServer.Cms.Shell.UI.Components.MediaComponent", StringComparison.OrdinalIgnoreCase))
                {
                    //Change the sort order for the media component so that it's placed before the
                    //shared blocks component. Since this is done before sorting the components it will affect sorting.
                    component.SortOrder = 90;
                    return;
                }
            }
        }
    }
}

Actually, on this third attempt we have a working solution.

ResortedFileManager

There is one small caveat though that you should know about:

If the user has already personalized the Assets Pane, the sorting will be overridden by the personalization since this is applied after the sorting. This shouldn’t be a real problem as long as you implement the sorting before the users start using the system. If you add this on a system in use you might want to clear your views as described here: http://world.episerver.com/Blogs/Linus-Ekstrom/Dates/2013/1/Resetting-your-EPiServer-views/

Feb 18, 2013

Alexander Wagner
( By Alexander Wagner, 2/18/2013 4:53:48 PM)

Brilliant! Thank you very much!

Arild Henrichsen
( By Arild Henrichsen, 2/18/2013 9:20:37 PM)

Thanks for the quick response Linus! Excellent walkthrough of the alternatives.

Henning Sjørbotten
( By Henning Sjørbotten, 9/9/2014 10:35:40 AM)

Just what I needed, works great!

Yagnik Jadav
( By Yagnik Jadav, 10/18/2017 8:08:33 AM)

Hi Linus,

I read your couple of blog about custom component and content provider.

I am using latest version of CMS and trying to plug component with content provider at top of asset pane next to media and blocks tab but even I set DefaultAssetGroup, my component adding at bottom of asset pane.

Any idea?

( 10/19/2017 12:24:55 PM)

Hi Yagnik!

I am actually just about to resurect a project that I worked on over two years ago that has some parts implementing custom content and views. I will blog about this shortly, but for now: please have a look at the code in the Github repo to see if it helps you: https://github.com/LinusEkstrom/EpiserverExtensions.Settings

Please login to comment.