Reports
Describes the report extensibility that is WebForm-based.
@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="do
(Thanks to Henrik Fransas at NetRelations for this example.)
In Optimizely, you can create custom reports and use the built-in reports in the admin view. The following example shows how to create a custom report of existing pages on a website and export the report to Excel using EPPlus.
-
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 is shown in the report menu and uses the URL
/existingpagesreport
. The controller is protected, so you have to be part of the administrator group to use it. -
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" }); }
Create a simple view and ensure it displays in the report center.
-
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; } }
-
Create a helper class to make a request against Optimizely 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; } }
-
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); }
-
Create a view and add Optimizely's CSS and JavaScript to it to have the same appearance as other built-in reports in Optimizely. 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 exist 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.
-
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(); } }
Updated 6 months ago