1. Create new “Class Library” project.
  2. Add these NuGet packages to your project: (Please consider you need to have Feed Url of EPiserver NuGet server -> Link):EPiServer.CMS.UI.Core
  3. Entry  point: We need to define a “Normal MVC Controller” and decorate that with “GuiPlugIn” attribute:
            Area = EPiServer.PlugIn.PlugInArea.AdminMenu,
            DisplayName = "Robots.txt Handler",
            Description = "Robots.txt Handler",
            Url = Const.ModuleUrlBase + Const.Separator + Const.ModuleAdminController + Const.Separator + Const.ModuleAdminIndexAction,
            RequiredAccess = AccessLevel.Administer)]
        public class AdminController : Controller

    As you can see within attribute properties you can define the name and access level and base Url to use the module to route the URL correctly  into controller. Another important bit is “Area” property! This tells Episerver WHERE to show the  link of the plugin.  You can see full list and description here

  4. When a user clicks on plugin link in “AdminMenu” we want to show a list of available “Robot.txt” handler for each site to a user. To achieve this let’s add “Index”:
           public ActionResult Index()
                var robotTxts = robotsTxtRepository.All().ToList();
                var model = new AdminViewModel
                    Sites = GetSitesList(robotTxts.Select(a => a.SiteId)),
                    AvailableRobotTxts = robotTxts.Select(a => new AvailableRobotTxt { Id = a.Id.ExternalId, Name = siteList.Single(s => s.Id.ToString() == a.SiteId).Name })
                return PluginPartialView(Const.ModuleAdminIndexAction, model);

    And more than obvious we need to define ViewModel and repository class. You can see them on GitHub

  5. We need to add “Index.cshtml” to the project. Let’s create folder “\View\Admin” and create “Index.cshtml” under “Admin”:
    @model AdminViewModel
    @{  Layout = "Layout.cshtml"; }
    @if (Model.AvailableRobotTxts != null && Model.AvailableRobotTxts.Any())
            @foreach (var robotTxt in Model.AvailableRobotTxts)
                    <span><a href="@Url.Content(string.Format("{0}/{1}/{2}/{3}", Const.ModuleUrlBase,Const.ModuleAdminController,Const.ModuleAdminEditAction, robotTxt.Id))">Edit</a></span>
                    <span><a href="@Url.Content(string.Format("{0}/{1}/{2}/{3}", Const.ModuleUrlBase, Const.ModuleAdminController, Const.ModuleAdminDeleteAction, robotTxt.Id))">Delete</a></span>
    @if (Model.Sites != null && Model.Sites.Any())
        using (Html.BeginForm())
            @Html.DropDownList("SelectedSite", Model.Sites)
            @Html.TextAreaFor(a => a.RobotText, new { @rows = 20 })
            <input type="submit" value="Submit" />


  6. Create “module.config”. This file instructs Episerver how route segment should work and register the plugin DLL:
    <?xml version="1.0" encoding="utf-8"?>
    <module loadFromBin="false" productName="Zanganeh RobotsTxtHandler" >
        <add assembly="Zanganeh.RobotsTxtHandler" />
        <route url="{moduleArea}/{controller}/{action}/{id}">
            <add key="moduleArea" value="Zanganeh.RobotsTxtHandler" />
            <add key="controller" value="" />
            <add key="action" value="" />
            <add key="id" value="" />
  7. Create web.config  under “View” folder, so Razor template works fine:
    <?xml version="1.0"?>
        <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
          <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
          <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
        <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <pages pageBaseType="System.Web.Mvc.WebViewPage">
            <add namespace="System.Web.Mvc" />
            <add namespace="System.Web.Mvc.Ajax" />
            <add namespace="System.Web.Mvc.Html" />
            <add namespace="System.Web.Routing" />
            <add namespace="EPiServer.Framework.Web.Mvc.Html" />
            <add namespace="Zanganeh.RobotsTxtHandler" />
            <add namespace="Zanganeh.RobotsTxtHandler.ViewModel" />
            <add assembly="Zanganeh.RobotsTxtHandler" />
  8. Create new “nuspec” file to instruct NuGet Package Manager to copy “View” properly into proper area:
    <?xml version="1.0" encoding="utf-8"?>
    <package xmlns="">
    	<metadata xmlns="">
    		<tags>Robots EPiServer</tags>
        <file src="module.config" target="content\modules\Zanganeh.RobotsTxtHandler\module.config" />
        <file src="Views\web.config" target="content\modules\Zanganeh.RobotsTxtHandler\Views\web.config" />
        <file src="Views\Admin\Index.cshtml" target="content\modules\Zanganeh.RobotsTxtHandler\Views\Admin\Index.cshtml" />


  9. I’m using “MyGet” (can take a look using link) to build my project and generate NuGet file and hosting purpose.

You can fork GitHub and use push it to your own MyGet to see how it works. Valdis Iljuconoks mentioned good point why not extending Link. I just started working on this project for learning purpose and one of our client was asking for this feature and because it is simple I just picked it up to try the Episerver Plugins.

Leave a Reply

Your email address will not be published. Required fields are marked *