London Dev Meetup Rescheduled! Due to unavoidable reasons, the event has been moved to 21st May. Speakers remain the same—any changes will be communicated. Seats are limited—register here to secure your spot!

Navigation [hide] [expand]
Area: Optimizely CMS
ARCHIVED This content is retired and no longer maintained. See the latest version here.

Example: Creating custom report

(Thanks to Henrik Fransas at NetRelations for this example.)

In Episerver you can create custom reports as well as using the built-in reports in the admin view. This topic gives an example on how to create a custom report of existing pages on a website and how to export the report to Excel using EPPlus.

  1. Start by creating a Controller that looks like this:
    namespace AlloyExample.Controllers
    {
       [EPiServer.PlugIn.GuiPlugIn(
            Area = EPiServer.PlugIn.PlugInArea.ReportMenu, 
            Url = "~/existingpagesreport",
            Category = "Existing Pages",
            DisplayName = "Pages By PageType")]
        [Authorize(Roles = "Administrators, WebAdmins")]
        public class ExistingPagesReportController : Controller
        {
            public ActionResult Index()
            {
                return View();
            }
        }
       
    }

    This controller is a GuiPlugin which will be shown in the report menu and use the url /existingpagesreport. The controller is protected so you have to be part of the administrator group to use it.
  2. For the url to work, add a route to it in global.asax.cs:
    protected override void RegisterRoutes(RouteCollection routes)
            {
                base.RegisterRoutes(routes);
                routes.MapRoute(
                    "ExistingPagesReport",
                    "existingpagesreport/{action}",
                    new { controller = "ExistingPagesReport", action = "Index" });
            }

  3. Create a simple view and make sure it shows up in the report center.
  4. Add a ViewModel with the necessary properties:
    public class ExistingPagesReportViewModel
        {
            public IEnumerable<PageType> PageTypes { get; set; }
            public PageDataCollection Pages { get; set; }
            public string SelectedPageType { get; set; }
        }

  5. Create a helper class to make a request against Episerver like this:
    public static class ExistingPagesHelper
        {
            public static IEnumerable<PageType> GetAllPageTypes()
            {
                var contentTypeRepository = ServiceLocator.Current.GetInstance<IContentTypeRepository>();
                return contentTypeRepository.List().OfType<PageType>();
            }
     
            public static void SetPagesForPageTypeName(ExistingPagesReportViewModel model)
            {
                var criterias = new PropertyCriteriaCollection();
     
                var criteria = new PropertyCriteria();
                criteria.Condition = CompareCondition.Equal;
                criteria.Name = "PageTypeID";
                criteria.Type = PropertyDataType.PageType;
                criteria.Value = model.SelectedPageType;
                criteria.Required = true;
     
                criterias.Add(criteria);
     
                var pages = DataFactory.Instance.FindPagesWithCriteria(ContentReference.RootPage, criterias);
     
                model.Pages = pages;
            }
        }

  6. Update your index action to create an instance of the View model and assign all page types to it:
    public ActionResult Index()
            {
               var model = new ExistingPagesReportViewModel { PageTypes = ExistingPagesHelper.GetAllPageTypes() };
     
                return View(model);
            }

  7. Create a view and add Episerver's CSS and JavaScript to it to have the same look-and-feel as other built-in reports in Episerver. The complete view looks like this:
    @model ExistingPagesReportViewModel
     
    @using EPiServer.DataAbstraction
    @using EPiServer.Framework.Web.Resources
     
    @{
        Layout = null;
    }
     
    <!DOCTYPE html>
     
    <html>
    <head>
        <title>@ViewBag.Title</title>
        <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
        <!-- Shell -->
        @Html.Raw(ClientResources.RenderResources("ShellCore"))
        <!-- LightTheme -->
        @Html.Raw(ClientResources.RenderResources("ShellCoreLightTheme"))
     
        <link href="/EPiServer/CMS/App_Themes/Default/Styles/system.css" type="text/css" rel="stylesheet">
        <link href="/EPiServer/CMS/App_Themes/Default/Styles/ToolButton.css" type="text/css" rel="stylesheet">
    </head>
    <body>
        @Html.Raw(Html.ShellInitializationScript())
     
        <div class="epi-contentContainer epi-padding">
            <div class="epi-contentArea">
                <div class="EP-systemImage" style="background-image: url('/App_Themes/Default/Images/ReportCenter/PublishedPages.gif');">
                    <h1 class="EP-prefix">
                        Existing Pages
                    </h1>
                    <p class="EP-systemInfo">
                        This report displays pages that exists on the site.
                    </p>
                </div>
                <div id="FullRegion_ValidationSummary" class="EP-validationSummary" style="color: Black; display: none;">
     
                </div>
            </div>
            @using (Html.BeginForm("ListPages", "ExistingPagesReport", FormMethod.Post))
            {
                <script src="/Util/javascript/episerverscriptmanager.js" type="text/javascript"></script>
                <script src="/EPiServer/CMS/javascript/system.js" type="text/javascript"></script>
                <script src="/EPiServer/CMS/javascript/dialog.js" type="text/javascript"></script>
                <script src="/EPiServer/CMS/javascript/system.aspx" type="text/javascript"></script>
     
                <input type="hidden" id="doExport" name="doExport" value="False">
                <div class="epi-formArea">
                    <fieldset>
                        <legend>
                            Report Criteria
                        </legend>
                        <div class="epi-size10">
                            <label for="pageTypes">Select PageType</label>
                            <select name="pageType" id="pageType">
                                @foreach (var type in Model.PageTypes.Where(w => w.ID != 1).OrderBy(o => o.Name))
                                {
                                    <option value="@type.ID" @(type.ID.ToString() == Model.SelectedPageType ? "selected=selected" : "") >@type.Name</option>
                                }
                            </select>
                        </div>
                    </fieldset>
     
                    <div class="epitoolbuttonrow">
                        <span class="epi-cmsButton"><input class="epi-cmsButton-text epi-cmsButton-tools epi-cmsButton-Report" type="submit" name="showReport" id="showReport" value="Show Report" onmouseover="EPi.ToolButton.MouseDownHandler(this)" onmouseout="EPi.ToolButton.ResetMouseDownHandler(this)" /></span>
                        <span class="epi-cmsButton"><input class="epi-cmsButton-text epi-cmsButton-tools epi-cmsButton-Report" type="submit" name="exportReport" id="exportReport" value="Export Report" onmouseover="EPi.ToolButton.MouseDownHandler(this)" onmouseout="EPi.ToolButton.ResetMouseDownHandler(this)" /></span>
                    </div>
     
                </div>
            }
     
            @if (Model.Pages != null && Model.Pages.Count > 0)
            {
                <div class="epi-floatLeft epi-marginVertical-small">Number of Hits: @Model.Pages.Count</div>
                <div class="epi-contentArea epi-clear">
                    <div>
                        <table class="epi-default epi-default-legacy" cellspacing="0" id="FullRegion_MainRegion_ReportView" style="border-style: None; width: 100%; border-collapse: collapse;">
                            <tr>
                                <th scope="col">Page Id</th>
                                <th scope="col">Page Name</th>
                                <th scope="col">Page Url</th>
                                <th scope="col">Published Date</th>
                            </tr>
                            @foreach (var page in Model.Pages)
                            {
                                <tr>
                                    <td style="width: 27%;">@page.ContentLink.ID</td>
                                    <td>@page.PageName</td>
                                    <td>@Url.ContentUrl(page.ContentLink)</td>
                                    <td>@(page.StartPublish.HasValue ? page.StartPublish.Value.ToString("yyyy-MM-dd HH:mm") : "Not published")</td>
                                </tr>
                            }
                        </table>
                    </div>
                </div>
     
            }
     
        </div>
        <script type="text/javascript">
            document.getElementById("exportReport").onclick = function () {
                document.getElementById("doExport").value = "True";
            };
            document.getElementById("showReport").onclick = function () {
                document.getElementById("doExport").value = "False";
            };
        </script>
    </body>
    </html>

    This code has two submit buttons but only one form action; the JavaScript function connected to the click event on the export and search submit buttons updates a hidden value telling the controller to do an export of the data or not.

    The action in the controller that handles this looks like this:

    [HttpPost]
            public ActionResult ListPages(FormCollection form)
            {
                var model = new ExistingPagesReportViewModel
                {
                    PageTypes = ExistingPagesHelper.GetAllPageTypes(),
                    SelectedPageType = form["pageType"]
                };
     
                ExistingPagesHelper.SetPagesForPageTypeName(model);
     
     
                var doExport = false;
     
                if (bool.TryParse(form["doExport"], out doExport) && doExport && model.Pages != null && model.Pages.Count > 0)
                {
                    Export(model.Pages, System.Web.HttpContext.Current.Response);
                }
     
                return View("Index", model);
            }
    It returns the view if the export values are not true.
  8. To export the report to Excel, use EPPlus and create a function in the same class like this:
    public void Export(PageDataCollection pagesToExport, HttpResponse response)
            {
                using (var package = new ExcelPackage())
                {
                    ExcelWorksheet ws = package.Workbook.Worksheets.Add("pages");
     
                    ws.Cells[1, 1].Value = "PageId";
                    ws.Cells[1, 2].Value = "PageName";
                    ws.Cells[1, 3].Value = "PageUrl";
                    ws.Cells[1, 4].Value = "Published Date";
     
                    ws.Row(1).Style.Font.Bold = true;
                    ws.Row(1).Style.Locked = true;
     
                    int row = 2;
     
                    foreach (var page in pagesToExport)
                    {
                        ws.Cells[row, 1].Value = page.ContentLink.ID;
                        ws.Cells[row, 2].Value = page.PageName;
                        ws.Cells[row, 3].Value = Url.ContentUrl(page.ContentLink);
                        ws.Cells[row, 4].Value = page.StartPublish.HasValue ? page.StartPublish.Value.ToString("yyyy-MM-dd HH:mm") : "Not published";
     
                        ++row;
                    }
     
                    response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                    response.AddHeader("content-disposition", string.Format("attachment; filename=pages{0}.xlsx", DateTime.Now.ToString("yyyyMMdd")));
                    response.BinaryWrite(package.GetAsByteArray());
                    response.Flush();
                    response.End();
                }
            }

Last updated: Jan 10, 2017