Evander Vanderfeesten
Apr 29, 2013
(1 votes)

EditPanel Manager

In order to keep the Edit Panel as clean as can be, I’ve written a small extension that enhances the ability to disable and hide buttons in the EditPanel for specific PageTypes. This post explains the setup for the EditPanelManager extension, which options it currently supports and how the implementation has been done. The main objective of this post is to give insight in a possible solution to the problem and should not by default be read as the best solution. While all code is usable and tested it could well be optimized at places.


The challenge

The Edit Panel, as you probably know, is one of the areas for which you can use a GuiPlugIn with Area set to PlugInArea.EditPanel to add custom tabs. With these tabs you can add specific functionality to pages or PageTypes for instance which can improve the work for your editors in case you need to provide some functionality that otherwise would mean manual selection or at least non trivial work.

While this is a great way to extend your specific pages or PageTypes, adding a multitude of these custom GuiPlugIns can make the Edit Panel a bit overwhelming to your editors in the number of tabs they see. Most of the times however your editors won’t be using all of these tabs frequently. Some tabs probably won’t be used at all throughout the site, and perhaps some tabs are only used for a specific PageType.


The basics

This extension uses an xml configuration file to manage the tabs in the EditPanel for our PageTypes. I’ll start by explaining this configuration. Further down the post I’ll show how we can hook into EPiServer to influence the actual tabs being displayed in the EditPanel.

Let’s start with the structure of the xml configuration file. This will explain all of the options that are available in this extension and will provide the basis insights on what to do with those options.

   1: <episervereditpanelmanager>
   2:   <pagetypes>
   3:     <type name="MyPageTypeName" active="MyDefaultEditPanelTab">
   4:       <properties>
   5:         <property name="MyFirstEditPanelTabName" enable="true" />
   6:         <property name="MySecondEditPanelTabName" enable="false" ignoreroles="MyUserRole" />
   7:       </properties>
   8:     </type>
   9:   </pagetypes>
  10: </episervereditpanelmanager>


The type element

Within the ‘pagetypes’ node you set up a ‘type’ element. Each type element reflects a single PageType in your EPiServer site structure. The are two attributes on the type element. The first is the ‘name’ attribute, which is mandatory. The name attribute contains the name of the PageType that you want to manage the EditPanel tabs for. The second attribute is the ‘active’ attribute, which is optional. This attribute contains the name of the EditPanel tab that you want to set as the default tab which is displayed when an editor selects a page from the PageTree (Note: if this is the same for all your PageTypes or there is no need to customize this for each PageType you should probably use the ‘uiDefaultPanelTab’ in your EPiServer site configuration).


The property element

Within the type element there is a single ‘properties’ element that contains a number of ‘property’ elements. Each property element reflects a tab that exists in the EditPanel; like the Preview tab, the Edit tab, the Workflow tab, the Version List tab etc. The are three attributes on the property element. The first is the ‘name’ attribute, which is mandatory. The name attribute contains the name of the EditPanel tab that you want to manage. The second attribute is the ‘enable’ attribute, also mandatory. The enable attribute is a Boolean and holds the values ‘true’ or ‘false’. If set to true, the EditPanel tab should be displayed. If set to false the EditPanel tab should not be displayed. The third attribute is the ‘ignoreroles’ attribute, which is optional. This attribute can hold a comma separated list of user roles. This works as an override to the enable attribute. Users from these user roles are not taken into account on the enable attribute. In the above example this means that the ‘MySecondEditPanelTabName’ is not visible to anyone, except users from the ‘MyUserRole’ user role.


Hopefully you’re still reading ;) The summary above highlights the functional use of the EditPanelManager extension. The next part will explain some more on how this can be achieved with some code examples as well. Before we get into that just another small example xml configuration.


Pop quiz

Try and see if you understand how this would affect the EditPanel of your site editors (Note: spoiler below the example!)

   1: <episervereditpanelmanager>
   2:   <pagetypes>
   3:     <type name="TeaserPageType">
   4:       <properties>
   5:         <property name="View" enable="false" />
   6:       </properties>
   7:     </type>
   8:     <type name="NewsPageType" active="Edit">
   9:       <properties>
  10:         <property name="View" enable="true" />
  11:         <property name="Edit" enable="true" />
  12:         <property name="Version List" enable="false" />
  13:         <property name="Workflow" enable="false" />
  14:         <property name="Statistics" enable="false" />
  15:       </properties>
  16:     </type>
  17:     <type name="EventPageType">
  18:       <properties>
  19:         <property name="View" enable="true" />
  20:         <property name="Edit" enable="false" ignoreroles="WebAdmins, WebEditors" />
  21:         <property name="Version List" enable="false" ignoreroles="WebAdmins, WebEditors" />
  22:         <property name="Workflow" enable="false" ignoreroles="WebAdmins, WebEditors" />
  23:         <property name="Statistics" enable="false" ignoreroles="WebAdmins, WebEditors" />
  24:       </properties>
  25:     </type>
  26:   </pagetypes>
  27: </episervereditpanelmanager>


Ok, so how does the above configuration affect our editors?

Once logged in, I won’t see the View tab in the EditPanel for the TeaserPageType. Further I won’t see the VersionList, Workflow and Statistics tab on the NewsPageType, and the Edit tab is the default tab when selecting a page of type NewsPageType in the PageTree. If the logged in user has the ‘WebAdmins’ user role he’ll see all five tabs on the EventPageType. If the logged in user has the ‘WebSpecialAdmins’ user role, he’ll only see the ‘View’ tab in the EditPanel.


Technical Implementation

So far for the examples. As promised I’ll dive into the actual implementation.

We start by creating a GuiPlugIn. Let’s call this ‘EditPanelManager’.

   1: [GuiPlugIn(Area = PlugInArea.EditPanel)]
   2: public class EditPanelManager : ICustomPlugInLoader
   3: {
   4:     private static readonly ILog Logger = LogManager.GetLogger(typeof(EditPanelManager));
   6:     //property that holds the current page type config element
   7:     public EPiServerEditPanelManagerPageTypeConfigElement CurrentPageTypeConfigElement
   8:     {
   9:         get; set;
  10:     }
  12:     public PlugInDescriptor[] List()
  13:     {
  14:         //hook LoadComplete-event on EditPanel page
  15:         EPiServer.UI.Edit.EditPanel editPanel = HttpContext.Current.Handler as EPiServer.UI.Edit.EditPanel;
  17:         if (null != editPanel)
  18:         {
  19:             //ADD SOME LOGIC HERE
  20:         }
  22:         //Never return a plugin - we don't want to add tabs.
  23:         return new PlugInDescriptor[0] { };
  24:     }
  26:     protected void EditPanelLoadComplete(object sender, EventArgs e)
  27:     {
  28:         //ADD SOME LOGIC HERE
  29:     }
  30: }


The next step is to implement the ‘PlugInDescriptor[] List()’ method from the ICustomPlugInLoader interface. In short we add an EventHandler to LoadComplete of the EditPanel if the PageType of the current page exists in our xml configuration file.

   1: public PlugInDescriptor[] List()
   2: {
   3:     // hook LoadComplete-event on EditPanel page
   4:     EPiServer.UI.Edit.EditPanel editPanel = HttpContext.Current.Handler as EPiServer.UI.Edit.EditPanel;
   6:     if (null != editPanel)
   7:     {
   8:         //get the list of all registered pagetypes from the config file
   9:         List<EPiServerEditPanelManagerPageTypeConfigElement> pageTypeList = EditPanelManagerHelper.GetPageTypeNamesFromConfig();
  11:         //check if the pagetype of the current page exists in the list of pagetypes in the config file
  12:         try
  13:         {
  14:             CurrentPageTypeConfigElement = pageTypeList.First(x => x.Name.ToLower().Trim() == editPanel.CurrentPage.PageTypeName.ToLower().Trim());
  15:         }
  16:         catch (Exception ex)
  17:         {
  18:             Logger.Debug("error occured while getting the CurrentPageTypeConfigElement", ex);
  19:         }
  21:         //match found, add the event handler to the LoadComplete event of the editpanel
  22:         if (CurrentPageTypeConfigElement != null)
  23:         {
  24:             editPanel.LoadComplete += EditPanelLoadComplete;
  25:         }
  26:     }
  28:     //Never return a plugin - we don't want to add tabs.
  29:     return new PlugInDescriptor[0] { };
  30: }

The EventHandler is added to manipulate the way the tabs in the EditPanel are rendered (Thanks to the article ‘Neat Trick: Modifying Edit Mode Tabs’ by Allan Thræn).

We also need to implement the LoadComplete event of the EditPanel for which we’ve added the new EventHandler in the ‘PlugInDescriptor[] List()’ method. There are three single line calls in the LoadComplete event. First we get the TabStrip object by looking for the ‘actionTab’ control. Secondly we retrieve all properties (the tabs) that are configured for the current page type in our xml configuration file. Finally, we call our Tab Manager that handles the processing of the individual tabs.

   1: protected void EditPanelLoadComplete(object sender, EventArgs e)
   2: {
   3:     // find the TabStrip with id = "actionTab"
   4:     TabStrip actionTabStrip = ControlHelper.FindControl<TabStrip>(sender as Control, "actionTab");
   6:     //get all properties for this pagetype from the config file
   7:     List<EPiServerEditPanelManagerPropertyConfigElement> elements =
   8:         EditPanelManagerHelper.GetPropertyElementsFromPageTypeConfigElement(CurrentPageTypeConfigElement);
  10:     //call our tab manager
  11:     TabStripHelper.SetTabs(actionTabStrip, elements, CurrentPageTypeConfigElement.Active);
  12: }

Within both the ‘PlugInDescriptor[] List()’ method and the LoadComplete event method there are calls to certain Helper classes. Of course you can set this up any way you like. For this demo I’ve chosen to just simply set up two classes; the EditPanelManagerHelper and the TabStripHelper.



The EditPanelManagerHelper is used to retrieve the correct PageTypes and properties from our configuration file. It has two methods. The first method is ‘GetPageTypeNamesFromConfig()’ and reads the xml configuration file for all PageTypes defined using the ConfigurationManager. The second method is ‘GetPropertyElementsFromPageTypeConfigElement()’ that reads all properties for a given PageType present in the xml configuration file.

   1: public static class EditPanelManagerHelper
   2: {
   3:     public static List<EPiServerEditPanelManagerPageTypeConfigElement> GetPageTypeNamesFromConfig()
   4:     {
   5:         List<EPiServerEditPanelManagerPageTypeConfigElement> list = new List<EPiServerEditPanelManagerPageTypeConfigElement>();
   6:         EPiServerEditPanelManagerConfigSection section = ConfigurationManager.GetSection("episervereditpanelmanager") as EPiServerEditPanelManagerConfigSection;
   7:         if (section != null)
   8:         {
   9:             list.AddRange(section.PageTypes.Cast<EPiServerEditPanelManagerPageTypeConfigElement>());
  10:         }
  11:         return list;
  12:     }
  14:     public static List<EPiServerEditPanelManagerPropertyConfigElement> GetPropertyElementsFromPageTypeConfigElement(EPiServerEditPanelManagerPageTypeConfigElement ePiServerEditPanelManagerPageTypeConfigElement)
  15:     {
  16:         //get element information from pagetype config node
  17:         return ePiServerEditPanelManagerPageTypeConfigElement.Elements.Cast<EPiServerEditPanelManagerPropertyConfigElement>().ToList();
  18:     }
  19: }



The second helper class is the TabStripHelper. This contains the actual logic that controls which tabs are displayed and which are hidden.

   1: private static readonly ILog Logger = LogManager.GetLogger(typeof(TabStripHelper));
   3: public static void SetTabs(TabStrip tabStrip, List<EPiServerEditPanelManagerPropertyConfigElement> elements, string activeTab)
   4: {
   5:     if (tabStrip == null)
   6:     {
   7:         return;
   8:     }
  10:     int firstVisibleTab = -1;
  11:     int activeTabIndex = -1;
  12:     for (int i = 0; i < tabStrip.Controls.Count; i++)
  13:     {
  14:         Tab tab = (Tab)tabStrip.Controls[i];
  16:         string tabName = tab.Text.ToLower().Trim();
  18:         //get element by tab name
  19:         EPiServerEditPanelManagerPropertyConfigElement element = null;
  20:         if (elements != null)
  21:         {
  22:             try
  23:             {
  24:                 IEnumerable<EPiServerEditPanelManagerPropertyConfigElement> list = elements.ToList().Where(x => x.Name.ToLower().Trim() == tabName);
  25:                 if (list.Any())
  26:                 {
  27:                     element = list.Single();
  28:                 }
  29:             }
  30:             catch (Exception ex)
  31:             {
  32:                 Logger.Debug(string.Format("error occured while getting the element for tab '{0}'", tabName), ex);
  33:             }
  34:         }
  35:         if (element != null)
  36:         {
  37:             if (string.IsNullOrEmpty(element.IgnoreRoles))
  38:             {
  39:                 //set visibility
  40:                 tab.Visible = element.Enable;
  41:             }
  42:             else
  43:             {
  44:                 //check current user roles
  45:                 tab.Visible = (UserHasRole(element.IgnoreRoles) ? !element.Enable : element.Enable);
  46:             }
  47:         }
  49:         //store first visible tab index
  50:         if (tab.Visible && firstVisibleTab == -1)
  51:         {
  52:             firstVisibleTab = i;
  53:         }
  55:         //store given active tab index
  56:         if (!string.IsNullOrEmpty(activeTab) && tab.Visible && tabName == activeTab.ToLower())
  57:         {
  58:             activeTabIndex = i;
  59:         }
  60:     }
  62:     if (tabStrip.SelectedTab == 0)
  63:     {
  64:         int set = (activeTabIndex > -1 ? activeTabIndex : firstVisibleTab);
  65:         tabStrip.SetSelectedTab(set);
  66:     }
  67: }
  69: public static bool UserHasRole(string roles)
  70: {
  71:     if(string.IsNullOrEmpty(roles))
  72:     {
  73:         return true;
  74:     }
  76:     //get current user roles
  77:     List<string> currentUserRoleList;
  78:     try
  79:     {
  80:         List<string> list = EPiServer.Security.PrincipalInfo.Current.RoleList.ToList();
  81:         currentUserRoleList = list.ConvertAll(x => x.ToLower());
  82:     }
  83:     catch (Exception)
  84:     {
  85:         Logger.Error("error occured while handling the current user role list");
  86:         return true;
  87:     }
  89:     List<string> roleList = roles.Split(',').ToList();
  90:     return roleList.Any(role => currentUserRoleList.Contains(role.ToLower().Trim()));
  91: }
  92:     }

This helper class contains two methods. The ‘UserHasRole’ method has been added here for convenience, but you probably have another place for this in your codebase. It simply checks the current users role to the provided userrole list from the ‘ignoreroles’ attribute for the current PageType.

The first method ‘SetTabs()’ holds three input parameters. The current TabStrip (which holds all tabs of the EditPanel), the list of tabs that you configured in your xml configuration file for the given PageType and the name of the activeTab if present in the xml configuration file. It simply iterates over all controls in the TabStrip and compares these to the xml configuration setup.



And there you have it, that’s all there is to it. As you can see this example shows how to influence the way tabs are rendered within the EditPanel and can possibly simplify the way your site editors experience the Edit Mode of EPiServer. Feel free to comment!

Apr 29, 2013


smithsson68@gmail.com Jun 13, 2013 12:23 PM

Hi, have you tested this on EPiServer 6 R2. I am trying to use parts of it to locate the "XForms Data" tab so I can hide it for certain users but the TabStrip only ever seems to have once child control which is the "preview" tab.

Here is my code:
private static void EditPanel_LoadedPage(EPiServer.UI.Edit.EditPanel sender,
EPiServer.UI.Edit.LoadedPageEventArgs e)
// find the TabStrip with id = "actionTab"
TabStrip actionTabStrip = ControlHelper.FindControl(sender as Control, "actionTab");

//call our tab manager

public static void SetTabs(TabStrip tabStrip)
if (tabStrip == null)

for (int i = 0; i < tabStrip.Controls.Count; i++)
Tab tab = (Tab) tabStrip.Controls[i];
string tabName = tab.Text.ToLower().Trim();

// For loop only ever executes once and the tab is always "preview"


Please login to comment.
Latest blogs
Enhance Your Writing with Suggestions

Are you tired of staring at a blank screen, struggling to find the right words? The Epicweb.Optimizely.AIAssistant Addon is here to revolutionize...

Luc Gosso (MVP) | May 31, 2023 | Syndicated blog

Content Graph - Letting GraphQL do all the hard work for you

Background As we have seen before, setting up Content Graph on the CMS side is pretty easy. However, when it comes to the “head” part of the setup,...

Kunal Shetye | May 26, 2023 | Syndicated blog

Improved headless functionality in Customized Commerce

Did you know that with the release of Content Delivery Commerce API 3.7 we have massively improved the out of the box headless capabilities of...

Marcus Hoffmann | May 25, 2023

Boost Your Productivity with the AI Assistant Addon for Optimizely Content Cloud

In today's fast-paced digital world, efficiency and convenience are paramount. That's why we're excited to introduce the Optimizely AI-Assistant...

Luc Gosso (MVP) | May 25, 2023 | Syndicated blog

Swapcode.Optimizely.AuditLog updated to v1.4.1

If you are using my audit log add-on Swapcode.Optimizely.AuditLog then I suggest that you update it in your solution. I've been waiting now for few...

Antti Alasvuo | May 20, 2023

The Web Project Podcast: Episode 19: Implement the Design (w/ Ethan Marcotte)

Corey and Deane talk about how front-end development has evolved past the early days. Then, Ethan Marcotte, author of Responsive Web Design and...

Corey Vilhauer | May 16, 2023 | Syndicated blog