Tuesday, November 5, 2013

EPiServer 6: Sharing plugins between applications

In this example I will demonstrate the possibility to share EPiServer plugins between applications. The idea is to make this as seamless as possible without configuration or having to add front-end files into every project.

What we will do is to create a class library project, build a release DLL and put this DLL into the bin folder of another project. And all of this will produce a new admin tool as depicted below.

Create the plugin

We create the plugin and what is worth noting here is the Url on line 4 (this is used with the route helper, see below) and the inheritance of the IPageTypeUsage interface on line 5.

   1:  [GuiPlugIn(DisplayName = "Page Type Usage",
   2:              Description = "See the usage of a selected page type.",
   3:              Area = PlugInArea.AdminMenu,
   4:              Url = "/PageTypeUsage.aspx")]
   5:  public class PageTypeUsage : Page, IPageTypeUsage

We will not use an ASPX file at all but instead add all the controls needed to the ControlCollection of the plugin page. And this must be added in the OnInit method in order to be able to use them properly.

   1:  protected override void OnInit(EventArgs e)
   2:  {
   3:      AddControls();
   4:      InitPageTypesDropDown();
   5:      base.OnInit(e);
   6:  }

A little example of the AddControls method. Once added they will render properly without us having to doing anything in particular.

   1:  private void AddControls()
   2:  {
   3:      HtmlGenericControl html = new HtmlGenericControl("html");
   4:      HtmlHead head = new HtmlHead();
   5:      HtmlTitle title = new HtmlTitle { Text = "Page Type Usage" };
   6:      HtmlGenericControl javascript = ControlHelper.GetJavaScriptLinkControl("/util/javascript/system.js");
   7:      HtmlGenericControl css1 = ControlHelper.GetCssLinkControl("/epi/Shell/ClientResources/ShellCore.css");
   8:      Controls.Add(html);
   9:      html.Controls.Add(head);
  10:      head.Controls.Add(title);
  11:      head.Controls.Add(javascript);
  12:      head.Controls.Add(css1);

Route handler

In order for this to work without using an ASPX file and having a virtual path we will use the power of Routes.

   1:      public class RouteHandler : PlugInAttribute, IRouteHandler
   2:      {
   3:          #region IRouteHandler Members
   4:          public IHttpHandler GetHttpHandler(RequestContext requestContext)
   5:          {
   6:              var interfaceType = typeof(IPageTypeUsage);
   7:              var classType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes()).First(p => interfaceType.IsAssignableFrom(p) && p.IsClass);
   8:              return Activator.CreateInstance(classType) as IHttpHandler;
   9:          }
  10:          #endregion
  12:          public static void RegisterRoutes()
  13:          {
  14:              RouteTable.Routes.Add(new Route("PageTypeUsage.aspx", new RouteHandler()));
  15:          }
  17:          public static void Start()
  18:          {
  19:              RegisterRoutes();
  20:          }
  21:      }

By inheriting PlugInAttribute on line 1 we can access the Start method (on line 17) and register the routes. And we register the route "PageTypeUsage.aspx" - which is the same as we specified on the plugin. Once this route is hit or accessed the GetHttpHandler method on line 4 is used. Basically this utilize a reflection search of the interface IPageTypeUsage and instantiates a PageTypeUsage object (on line 8).

And voila! We have a plugin that we can share between different projects!