Creating an EPiServer Plugin

Posted on October 8, 2009 by Frederik Vig in EPiServer

In EPiServer you can use plugins to extend the UI with new functionality for administrators and editors. You have 10 different types to choose from.

  • ActionWindow – Item in listing on action window in edit mode
  • EditPanel – A custom tab item on the edit panel tab strip
  • Edit Tree – A custom tab item on the edit tree tab strip
  • SystemSettings – A custom tab item on the system settings tab strip
  • AdminMenu – A custom item on the administration menu
  • AdminConfigMenu – A custom item on the administration configuration menu
  • SidSettingsArea – A custom tab item on the sid settings tab strip
  • WorkRoom – A custom item on the workroom tab strip
  • DynamicContent – A custom GUI plugin for creating/editing dynamic content objects
  • ReportMenu – A custom item on the report center menu

Source: PlugInArea Enumeration

Example

To get started we choose Add New Item in Visual Studio.

Adding a new EPiServer plugin through the New Item dialog box and EPiServer plugin template

After typing in a name and clicking Add we get a new dialog with the 10 different plugin types that we can choose from.

Choosing the AdminMenu plugin type

In this example where going for the AdminMenu choice, which will create a new web forms page for us.

using System;
using System.Collections.Generic;
using System.Web.Security;
using System.Web.UI.WebControls;
using EPiServer.Personalization;
using EPiServer.PlugIn;
using EPiServer.Security;
using EPiServer.Util.PlugIns;
using System.Web.UI;
 
namespace FV.Templates.FV.Plugins
{
    [GuiPlugIn(DisplayName = "Custom admin plugin", Description = "Decription of custom admin plugin", Area = PlugInArea.AdminMenu, Url = "~/Templates/FV/Plugins/CustomAdminPlugin.aspx")]
    public partial class CustomAdminPlugin : System.Web.UI.Page
    {
 
        // TODO: Add your Plugin Control Code here.
 
    }
}

If you build and open up admin mode, you’ll see our Custom admin plugin in the menu on the left with an empty page.

Lets add some content! Switch to the web forms (.aspx) page and add some HTML markup and a few textbox controls and a button.

<%@ Page Language="c#" Codebehind="CustomAdminPlugin.aspx.cs" AutoEventWireup="False" Inherits="FV.Templates.FV.Plugins.CustomAdminPlugin" Title="Custom admin plugin" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Custom admin plugin</title>
    <link rel="stylesheet" href="/App_Themes/Default/styles/system.css" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <h1>Custom admin plugin</h1>
    <fieldset>
            <p>
                <asp:Label runat="server" AssociatedControlID="txtApiKey">API key</asp:Label> <asp:TextBox runat="server" ID="txtApiKey" />
            </p>
            <p>
                <asp:Label runat="server" AssociatedControlID="txtSecurityKey">Security key</asp:Label> <asp:TextBox runat="server" ID="txtSecurityKey" />
            </p>
            <p>
                <asp:Button runat="server" ID="btnSave" CssClass="submit" Text="Save" OnClick="btnSave_Click" /> 
            </p>
    </fieldset>
    </form>
</body>
</html>

At the top I added a link to the system.css stylesheet that EPiServer UI uses. This will make it easier for us to follow the same style rules that the UI uses.

You should now see something like this.

The next step is finding a way to store our API and Security keys. There are a couple of approaches for this.

From the EPiServer SDK:

Some plug-ins need to store some kind of internal settings or state, there are different approaches to this. If you only need to store simple system settings, for example the mail server name in your brand new mail plug-in you probably want to use web.config. If you have large amout of data the obvious choice is to store it in a database. When your need fits in between these two, you need to store small sets of relational data and don’t want to use your own database for this simple purpose you have PlugInSettings. PlugInSettings is used to store plug-in settings and information in a DataSet, these will be persisted as xml together will the plug-in definition in the EpiServer database.

If we switch back to our code-behind file we can start implementing the btnSave_Click method.

using System;
using System.Data;
using System.Web;
using EPiServer;
using EPiServer.PlugIn;
using EPiServer.Security;
 
namespace FV.Templates.FV.Plugins
{
    [GuiPlugIn(DisplayName = "Custom admin plugin", Description = "Decription of custom admin plugin", Area = PlugInArea.AdminMenu, Url = "~/Templates/FV/Plugins/CustomAdminPlugin.aspx")]
    public partial class CustomAdminPlugin : SimplePage
    {
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
 
            if (!PrincipalInfo.HasAdminAccess)
            {
                AccessDenied();
            }
 
            if (!IsPostBack)
            {
                this.PopulateForm();
            }
        }
 
        protected void btnSave_Click(object sender, EventArgs e)
        {
            var data = new DataSet();
            data.Tables.Add(new DataTable());
            data.Tables[0].Columns.Add(new DataColumn("API key", typeof(string)));
            data.Tables[0].Columns.Add(new DataColumn("Security key", typeof(string)));
            var configuration = data.Tables[0].NewRow();
            configuration["API key"] = HttpUtility.HtmlEncode(txtApiKey.Text);
            configuration["Security key"] = HttpUtility.HtmlEncode(txtSecurityKey.Text);
 
            data.Tables[0].Rows.Add(configuration);
 
            PlugInSettings.Save(typeof(CustomAdminPlugin), data);
        }
 
        private void PopulateForm()
        {
 
        }    
    }
}

In our OnLoad method I make sure the current user has administrative privileges (access to admin mode). If she doesn’t, we call the AccessDenied method, which will take the user to the login page.

In btnSend_Click I create a new DataSet with a DataTable which I use to store the values for the API and security key.

To retrieve the existing values and add it to the textboxes we use PlugInSettings class’ Populate method.

private void PopulateForm()
{
	var data = new DataSet();
	data.Tables.Add(new DataTable());
	data.Tables[0].Columns.Add(new DataColumn("API key", typeof(string)));
	data.Tables[0].Columns.Add(new DataColumn("Security key", typeof(string)));
	PlugInSettings.Populate(typeof(CustomAdminPlugin), data);
 
	if (data.Tables[0] == null || data.Tables[0].Rows.Count == 0)
	{
		return;
	}
 
	this.txtApiKey.Text = data.Tables[0].Rows[0]["API key"].ToString();
	this.txtSecurityKey.Text = data.Tables[0].Rows[0]["Security key"].ToString();
}

If we where to use these settings in some other plugin, we could just use the PlugInSettings.Populate method again.

Refactoring

If you take a closer look at the code you’ll see a lot of code smells. We repeat the same code in the btnSend_Click and Populate methods. Also if we need to access some of the settings from another place, it would be nice to not need to write the same code again.

Lets start refactoring by creating a new class to hold the code for setting and retrieving the API and security key.

using System.Data;
using System.Web;
using EPiServer.PlugIn;
 
namespace FV.Templates.FV.Classes
{
    public class CustomAdminPluginSettings
    {
        private DataSet configurationDataSet;
        private const string ApiKeyColumnName = "API Key";
        private const string SecurityKeyColumnName = "Security key";
 
        public string ApiKey
        {
            get;
            set;
        }
 
        public string SecurityKey
        {
            get;
            set;
        }
 
        private void InitializeConfigurationDataSet()
        {
            this.configurationDataSet = new DataSet();
            this.configurationDataSet.Tables.Add(new DataTable());
            this.configurationDataSet.Tables[0].Columns.Add(new DataColumn(ApiKeyColumnName, typeof(string)));
            this.configurationDataSet.Tables[0].Columns.Add(new DataColumn(SecurityKeyColumnName, typeof(string)));
        }
 
        public void SaveConfigurationSettings()
        {
            this.InitializeConfigurationDataSet();
 
            var configuration = this.configurationDataSet.Tables[0].NewRow();
 
            if (this.ApiKey != null)
            {
                configuration[ApiKeyColumnName] = HttpUtility.HtmlEncode(this.ApiKey);
            }
 
            if (this.SecurityKey != null)
            {
                configuration[SecurityKeyColumnName] = HttpUtility.HtmlEncode(this.SecurityKey);
            }
 
            this.configurationDataSet.Tables[0].Rows.Add(configuration);
 
            PlugInSettings.Save(typeof(CustomAdminPluginSettings), this.configurationDataSet);
        }
 
        public void LoadConfigurationSettings()
        {
            this.InitializeConfigurationDataSet();
            PlugInSettings.Populate(typeof(CustomAdminPluginSettings), this.configurationDataSet);
 
            if (this.configurationDataSet.Tables[0] == null || this.configurationDataSet.Tables[0].Rows.Count == 0)
            {
                return;
            }
 
            this.ApiKey = this.configurationDataSet.Tables[0].Rows[0][ApiKeyColumnName].ToString();
            this.SecurityKey = this.configurationDataSet.Tables[0].Rows[0][SecurityKeyColumnName].ToString();
        }
    }
}

Now we only need to update our plugin code.

using System;
using EPiServer;
using EPiServer.PlugIn;
using EPiServer.Security;
using FV.Templates.FV.Classes;
 
namespace FV.Templates.FV.Plugins
{
    [GuiPlugIn(DisplayName = "Custom admin plugin", Description = "Decription of custom admin plugin", Area = PlugInArea.AdminMenu, Url = "~/Templates/FV/Plugins/CustomAdminPlugin.aspx")]
    public partial class CustomAdminPlugin : SimplePage
    {
        private CustomAdminPluginSettings settings;
 
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
 
            if (!PrincipalInfo.HasAdminAccess)
            {
                AccessDenied();
            }
 
            if (!IsPostBack)
            {
                this.PopulateForm();
            }
        }
 
        protected void btnSave_Click(object sender, EventArgs e)
        {
            this.settings = new CustomAdminPluginSettings
                                {
                                    ApiKey = this.txtApiKey.Text,
                                    SecurityKey = this.txtSecurityKey.Text
                                };
 
 
            this.settings.Save();
        }
 
        private void PopulateForm()
        {
            this.settings = new CustomAdminPluginSettings();
            this.settings.Load();
 
            this.txtApiKey.Text = this.settings.ApiKey;
            this.txtSecurityKey.Text = this.settings.SecurityKey;
        }    
    }
}

And we’re done. Much cleaner!

Download the code

Related Posts: