Redesigned the audit trail filtering UI.

This commit is contained in:
Daniel Stolt
2014-07-06 14:46:31 +02:00
parent c276433aff
commit d3481053d9
22 changed files with 98 additions and 405 deletions

View File

@@ -34,7 +34,7 @@ namespace Orchard.AuditTrail.Controllers {
var filters = Filters.From(Request.QueryString);
var pageOfData = _auditTrailManager.GetRecords(pager.Page, pager.PageSize, filters, orderBy ?? AuditTrailOrderBy.DateDescending);
var pagerShape = New.Pager(pager).TotalItemCount(pageOfData.TotalItemCount);
var filterLayout = _auditTrailManager.BuildFilterDisplays(filters);
var filterDisplay = _auditTrailManager.BuildFilterDisplay(filters);
var eventDescriptorsQuery =
from c in _auditTrailManager.DescribeCategories()
from e in c.Events
@@ -55,7 +55,7 @@ namespace Orchard.AuditTrail.Controllers {
Records = recordViewModelsQuery.ToArray(),
Pager = pagerShape,
OrderBy = orderBy ?? AuditTrailOrderBy.DateDescending,
FilterLayout = filterLayout
FilterDisplay = filterDisplay
};
return View(viewModel);

View File

@@ -85,7 +85,6 @@
<Content Include="Styles\audittrail-disabledcontent.css" />
<Content Include="Styles\audittrail-part.css" />
<Content Include="Styles\audittrail-settings.css" />
<Content Include="Styles\audittrail-grid.css" />
<Content Include="Styles\menu.audit-trail-admin.css" />
<Content Include="Web.config" />
<Content Include="Scripts\Web.config" />
@@ -135,13 +134,15 @@
<Content Include="Views\AuditTrailEvent-User-LoginFailed..cshtml" />
<Content Include="Views\AuditTrailFilter-Common-User.cshtml" />
<Content Include="Views\AuditTrailFilter-ContentType.cshtml" />
<Content Include="Views\AuditTrailFilter-Common-Date.cshtml" />
<Content Include="Views\AuditTrailFilter-Common-Date-From.cshtml" />
<Content Include="Views\AuditTrailFilter-ContentItem.cshtml" />
<Content Include="Views\AuditTrailFilter-Common-Category.cshtml" />
<Content Include="Views\Content\Detail.cshtml" />
<Content Include="Views\AuditTrailEvent-Content.cshtml">
<SubType>Code</SubType>
</Content>
<Content Include="Views\AuditTrailFilter.cshtml" />
<Content Include="Views\AuditTrailFilter-Common-Date-To.cshtml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">

View File

@@ -46,10 +46,12 @@ namespace Orchard.AuditTrail.Providers.Content {
private void DisplayFilter(DisplayFilterContext context) {
var contentItemId = context.Filters.Get("content").ToInt32();
var contentItem = contentItemId != null ? _contentManager.Get(contentItemId.Value, VersionOptions.Latest) : default(ContentItem);
var filterDisplay = context.ShapeFactory.AuditTrailFilter__ContentItem(ContentItem: contentItem);
if (contentItemId != null) {
var contentItem = contentItemId != null ? _contentManager.Get(contentItemId.Value, VersionOptions.Latest) : default(ContentItem);
var filterDisplay = context.ShapeFactory.AuditTrailFilter__ContentItem(ContentItem: contentItem);
context.FilterLayout.TripleSecond.Add(filterDisplay);
context.FilterDisplay.Add(filterDisplay);
}
}
}
}

View File

@@ -49,7 +49,7 @@ namespace Orchard.AuditTrail.Providers.ContentDefinition {
ContentType: context.Filters.Get("contenttype"),
ContentTypes: _contentDefinitionManager.ListTypeDefinitions().OrderBy(x => x.DisplayName).ToArray());
context.FilterLayout.TripleFirst.Add(filterDisplay);
context.FilterDisplay.Add(filterDisplay);
}
}
}

View File

@@ -98,25 +98,21 @@ namespace Orchard.AuditTrail.Services {
return _auditTrailRepository.Get(id);
}
public dynamic BuildFilterDisplays(Filters filters) {
var layout = (dynamic)_shapeFactory.Create("AuditTrailFilters", Arguments.From(new {
TripleFirst = _shapeFactory.Create("AuditTrailFilters_TripleFirst"),
TripleSecond = _shapeFactory.Create("AuditTrailFilters_TripleSecond"),
TripleThird = _shapeFactory.Create("AuditTrailFilters_TripleThird")
}));
var displayContext = new DisplayFilterContext(_shapeFactory, filters, layout);
public dynamic BuildFilterDisplay(Filters filters) {
var filterDisplay = (dynamic)_shapeFactory.Create("AuditTrailFilter");
var filterDisplayContext = new DisplayFilterContext(_shapeFactory, filters, filterDisplay);
// Invoke event handlers.
_auditTrailEventHandlers.DisplayFilter(displayContext);
_auditTrailEventHandlers.DisplayFilter(filterDisplayContext);
// Give each provider a chance to provide a filter display.
var providersContext = DescribeProviders();
foreach (var action in providersContext.FilterDisplays) {
action(displayContext);
action(filterDisplayContext);
}
return layout;
return filterDisplay;
}
public AuditTrailEventRecordResult CreateRecord<T>(string eventName, IUser user, IDictionary<string, object> properties = null, IDictionary<string, object> eventData = null, string eventFilterKey = null, string eventFilterData = null) where T:IAuditTrailEventProvider {

View File

@@ -42,16 +42,16 @@ namespace Orchard.AuditTrail.Services {
var toDate = context.Filters.Get("to.Date");
var category = context.Filters.Get("category");
var userNameFilterDisplay = context.ShapeFactory.AuditTrailFilter__Common__User(UserName: userName);
var dateFilterDisplay = context.ShapeFactory.AuditTrailFilter__Common__Date(
From: new DateTimeEditor {Date = fromDate, ShowDate = true},
To: new DateTimeEditor {Date = toDate, ShowDate = true});
var dateFromFilterDisplay = context.ShapeFactory.AuditTrailFilter__Common__Date__From(Editor: new DateTimeEditor {Date = fromDate, ShowDate = true});
var dateToFilterDisplay = context.ShapeFactory.AuditTrailFilter__Common__Date__To(Editor: new DateTimeEditor { Date = toDate, ShowDate = true });
var categoryFilterDisplay = context.ShapeFactory.AuditTrailFilter__Common__Category(
Categories: _auditTrailManager.Value.DescribeCategories().ToArray(),
Category: category);
context.FilterLayout.TripleFirst.Add(dateFilterDisplay);
context.FilterLayout.TripleFirst.Add(categoryFilterDisplay);
context.FilterLayout.TripleSecond.Add(userNameFilterDisplay);
context.FilterDisplay.Add(dateFromFilterDisplay);
context.FilterDisplay.Add(dateToFilterDisplay);
context.FilterDisplay.Add(categoryFilterDisplay);
context.FilterDisplay.Add(userNameFilterDisplay);
}
}
}

View File

@@ -29,7 +29,7 @@ namespace Orchard.AuditTrail.Services {
/// </summary>
/// <param name="filters">Input for each filter builder.</param>
/// <returns>A tree of shapes.</returns>
dynamic BuildFilterDisplays(Filters filters);
dynamic BuildFilterDisplay(Filters filters);
/// <summary>
/// Records an audit trail event.

View File

@@ -2,14 +2,14 @@ using Orchard.DisplayManagement;
namespace Orchard.AuditTrail.Services.Models {
public class DisplayFilterContext {
public DisplayFilterContext(IShapeFactory shapeFactory, Filters filters, dynamic filterLayout) {
public DisplayFilterContext(IShapeFactory shapeFactory, Filters filters, dynamic filterDisplay) {
ShapeFactory = shapeFactory;
Filters = filters;
FilterLayout = filterLayout;
FilterDisplay = filterDisplay;
}
public dynamic ShapeFactory { get; set; }
public Filters Filters { get; set; }
public dynamic FilterLayout { get; set; }
public dynamic FilterDisplay { get; set; }
}
}

View File

@@ -4,22 +4,7 @@ using Orchard.DisplayManagement;
namespace Orchard.AuditTrail.Shapes {
public class AuditTrailShapes : IDependency {
[Shape]
public void AuditTrailFilters(dynamic Shape, dynamic Display, TextWriter Output) {
DispayChildren(Shape, Display, Output);
}
[Shape]
public void AuditTrailFilters_TripleFirst(dynamic Shape, dynamic Display, TextWriter Output) {
DispayChildren(Shape, Display, Output);
}
[Shape]
public void AuditTrailFilters_TripleSecond(dynamic Shape, dynamic Display, TextWriter Output) {
DispayChildren(Shape, Display, Output);
}
[Shape]
public void AuditTrailFilters_TripleThird(dynamic Shape, dynamic Display, TextWriter Output) {
public void AuditTrailFilterDisplay(dynamic Shape, dynamic Display, TextWriter Output) {
DispayChildren(Shape, Display, Output);
}

View File

@@ -1,20 +1,35 @@
.audittrail-filter {

.audittrail-filter-section {
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
justify-content: flex-start;
align-items: flex-end;
align-content: flex-end;
margin-bottom: 1em;
border: 1px solid #eaeaea;
background: #f5f5f5;
padding: 4px 10px 3px;
}
.audittrail-filter fieldset label {
display: inline-block;
width: 10em;
}
.filter-control-group {
margin-right: 10px;
margin-bottom: 6px;
}
.audittrail-filter fieldset label.inline {
display: inline;
}
.filter-control-group label {
padding-bottom: 0;
}
.filter-control-group select {
padding-top: 2px !important;
padding-bottom: 2px !important;
}
.audittrail-event-metadata-section {
margin-bottom: 1em;
}
.audittrail-event-metadata-section strong {
font-weight: bold;
/*font-weight: bold;*/
}

View File

@@ -1,285 +0,0 @@
.table, .row, .cell {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.table > .row {
width: 100%;
}
.table.fixed > .row {
margin-left: auto;
margin-right: auto;
}
.row {
display: block;
margin: 0 0 20px 0;
}
.row:after {
content: "";
display: table;
clear: both;
}
.cell {
display: block;
float: left;
padding-right: 10px;
padding-left: 10px;
}
.row > .cell:last-of-type {
padding-right: 0;
}
.row > .cell:first-of-type {
padding-left: 0;
}
/* Opt-in outside padding */
.row-pad {
padding: 20px 0 20px 20px;
}
.row-pad .cell:last-of-type {
padding-right: 20px;
}
.span-1-1 {
width: 100%;
}
.span-1-2 {
width: 50%;
}
.span-1-3 {
width: 33.33%;
}
.span-1-4 {
width: 25%;
}
.span-1-5 {
width: 20%;
}
.span-1-6 {
width: 16.66%;
}
.span-1-7 {
width: 14.28%;
}
.span-1-8 {
width: 12.5%;
}
.span-1-9 {
width: 11.11%;
}
.span-1-10 {
width: 10%;
}
.span-1-11 {
width: 9.09%;
}
.span-1-12 {
width: 8.33%;
}
.span-2-3 {
width: 66.66%;
}
.span-2-4 {
width: 50%;
}
.span-2-5 {
width: 40%;
}
.span-2-6 {
width: 33.33%;
}
.span-2-8 {
width: 25%;
}
.span-2-10 {
width: 20%;
}
.span-2-12 {
width: 16.66%;
}
.span-3-4 {
width: 75%;
}
.span-3-5 {
width: 60%;
}
.span-3-6 {
width: 50%;
}
.span-3-8 {
width: 37.5%;
}
.span-3-10 {
width: 33.33%;
}
.span-3-12 {
width: 25%;
}
.span-4-5 {
width: 80%;
}
.span-4-6 {
width: 66.66%;
}
.span-4-8 {
width: 50%;
}
.span-4-10 {
width: 40%;
}
.span-4-12 {
width: 33.33%;
}
.span-5-6 {
width: 83.33%;
}
.span-5-8 {
width: 75%;
}
.span-5-10 {
width: 50%;
}
.span-5-12 {
width: 41.66%;
}
.span-6-8 {
width: 75%;
}
.span-6-10 {
width: 60%;
}
.span-6-12 {
width: 50%;
}
.span-7-8 {
width: 87.5%;
}
.span-7-10 {
width: 70%;
}
.span-7-12 {
width: 58.33%;
}
.span-8-10 {
width: 80%;
}
.span-8-12 {
width: 66.66%;
}
.span-9-10 {
width: 90%;
}
.span-9-12 {
width: 75%;
}
.span-10-12 {
width: 83.33%;
}
.span-11-12 {
width: 91.66%;
}
/* Bootstrap compatible spans */
.span-1 { width: 8.33%; }
.span-2 { width: 16.66%; }
.span-3 { width: 25%; }
.span-4 { width: 33.33%; }
.span-5 { width: 41.66%; }
.span-6 { width: 50%; }
.span-7 { width: 58.33%; }
.span-8 { width: 66.66%; }
.span-9 { width: 75%; }
.span-10 { width: 83.33%; }
.span-11 { width: 91.66%; }
.span-12 { width: 100%; }
/* RESPONSIVENESS */
/* Large desktop */
@media (min-width: 1200px) {
.table.fixed > .row {
width: 1170px;
}
}
/* Default */
@media (min-width: 980px) and (max-width: 1199px) {
.table.fixed > .row {
width: 960px;
}
}
/* Portrait tablet to landscape and desktop */
@media (min-width: 768px) and (max-width: 979px) {
.table.fixed > .row {
width: 724px;
}
}
/* Landscape phone to portrait tablet */
@media (max-width: 767px) {
.table.fixed > .row {
width: 100%;
}
}
/* Landscape phones and down */
@media (max-width: 480px) {
.table.fixed > .row {
width: 100%;
}
}

View File

@@ -3,7 +3,7 @@ using Orchard.AuditTrail.Services.Models;
namespace Orchard.AuditTrail.ViewModels {
public class AuditTrailViewModel {
public dynamic FilterLayout { get; set; }
public dynamic FilterDisplay { get; set; }
public AuditTrailOrderBy OrderBy { get; set; }
public IEnumerable<AuditTrailEventSummaryViewModel> Records { get; set; }
public dynamic List { get; set; }

View File

@@ -2,48 +2,29 @@
@model Orchard.AuditTrail.ViewModels.AuditTrailViewModel
@{
Style.Include("audittrail-display.css");
Style.Include("audittrail-grid.css");
var orderBy = Model.OrderBy;
var orderByItems = new List<SelectListItem> {
new SelectListItem {Text = T("Date (newest first)").Text, Value = AuditTrailOrderBy.DateDescending.ToString(), Selected = Model.OrderBy == AuditTrailOrderBy.DateDescending},
new SelectListItem {Text = T("Category (alphabetical)").Text, Value = AuditTrailOrderBy.CategoryAscending.ToString(), Selected = Model.OrderBy == AuditTrailOrderBy.CategoryAscending},
new SelectListItem {Text = T("Event name (alphabetical)").Text, Value = AuditTrailOrderBy.EventAscending.ToString(), Selected = Model.OrderBy == AuditTrailOrderBy.EventAscending},
};
Layout.Title = T("Audit Trail");
}
<section class="audittrail-filter">
@using (Html.BeginForm("Index", "Admin", new { area = "Orchard.AuditTrail" }, FormMethod.Get)) {
<div class="table">
<div class="row">
<div class="cell span-12">
@Display(Model.FilterLayout)
</div>
</div>
<div class="row">
<div class="cell span-4">
@Display(Model.FilterLayout.TripleFirst)
<fieldset>
@Html.LabelFor(m => orderBy, T("Sort by:"))
@Html.DropDownListFor(m => orderBy, orderByItems)
</fieldset>
</div>
<div class="cell span-4">
@Display(Model.FilterLayout.TripleSecond)
</div>
<div class="cell span-4">
@Display(Model.FilterLayout.TripleThird)
</div>
</div>
<div class="row">
<div class="cell span-12">
<button type="submit" value="yes">@T("Apply")</button>
</div>
</div>
@using (Html.BeginForm("Index", "Admin", new { area = "Orchard.AuditTrail" }, FormMethod.Get)) {
<section class="audittrail-filter-section">
@Display(Model.FilterDisplay)
<div class="filter-control-group">
@Html.LabelFor(m => orderBy, T("Sort by:"))
@Html.DropDownListFor(m => orderBy, orderByItems)
</div>
<div class="filter-control-group">
<button type="submit" class="filter-apply-button" value="yes">@T("Apply")</button>
</div>
}
</section>
<section class="audittrail-list">
}
<section class="audittrail-list-section">
@if (!Model.Records.Any()) {
<p class="info">@T("There are no records to display.")</p>
}

View File

@@ -4,7 +4,5 @@
var currentCategory = (string)Model.Category;
var listItems = categories.Select(x => new SelectListItem { Text = x.Name.Text, Value = x.Category, Selected = x.Category == currentCategory });
}
<fieldset>
@Html.Label("category", T("Category:").Text)
@Html.DropDownList("category", listItems, "")
</fieldset>
@Html.Label("category", T("Category:").Text)
@Html.DropDownList("category", listItems, "")

View File

@@ -0,0 +1,6 @@
@using Orchard.Core.Common.ViewModels
@{
var editor = (DateTimeEditor)Model.Editor;
}
@Html.Label("from.Date", T("From:").Text)
@Html.EditorFor(m => editor)

View File

@@ -0,0 +1,6 @@
@using Orchard.Core.Common.ViewModels
@{
var editor = (DateTimeEditor)Model.Editor;
}
@Html.Label("to.Date", T("To:").Text)
@Html.EditorFor(m => editor)

View File

@@ -1,11 +0,0 @@
@using Orchard.Core.Common.ViewModels
@{
var from = (DateTimeEditor)Model.From;
var to = (DateTimeEditor)Model.To;
}
<fieldset>
@Html.Label("from.Date", T("From:").Text)
@Html.EditorFor(m => from)
@Html.Label("to.Date", T("To:").Text, new { @class = "inline" })
@Html.EditorFor(m => to)
</fieldset>

View File

@@ -1,4 +1,2 @@
<fieldset>
@Html.Label("username", T("User:").Text)
@Html.TextBox("username", (string)Model.UserName, new { @class = "text" })
</fieldset>
@Html.Label("username", T("User:").Text)
@Html.TextBox("username", (string)Model.UserName, new { @class = "text" })

View File

@@ -2,10 +2,6 @@
@{
var contentItem = (ContentItem)Model.ContentItem;
}
@if (contentItem != null) {
<fieldset>
@Html.Label("contentitem", T("Content Item:").Text)
@Html.ItemEditLink(contentItem)
@Html.Hidden("content", contentItem.Id)
</fieldset>
}
@Html.Label("contentitem", T("Content Item:").Text)
@Html.TextBox("contentitemname", Html.ItemDisplayText(contentItem), new { @class = "text", disabled = "disabled"})
@Html.Hidden("content", contentItem.Id)

View File

@@ -4,7 +4,5 @@
var currentContentType = (string)Model.ContentType;
var listItems = contentTypes.Select(x => new SelectListItem {Text = x.DisplayName, Value = x.Name, Selected = x.Name == currentContentType});
}
<fieldset>
@Html.Label("contenttype", T("Content type:").Text)
@Html.DropDownList("contenttype", listItems, "")
</fieldset>
@Html.Label("contenttype", T("Content type:").Text)
@Html.DropDownList("contenttype", listItems, "")

View File

@@ -0,0 +1,7 @@
@using (Html.BeginForm("Index", "Admin", new { area = "Orchard.AuditTrail" }, FormMethod.Get)) {
foreach (var item in Model) {
<div class="filter-control-group">
@Display(item)
</div>
}
}

View File

@@ -526,14 +526,14 @@ span.message {
color:#fff;
}
.info {
background: url(images/info.gif) no-repeat 5px 5px #fff;
background: url(images/info.gif) no-repeat 6px 6px #fff;
border:1px solid #e4e8ee; /* blue */
padding-left:26px;
padding:4px 4px 4px 26px;
}
.error {
background: url(images/error.gif) no-repeat 5px 5px #fff;
background: url(images/error.gif) no-repeat 6px 6px #fff;
border:1px solid #e5cece; /* red */
padding-left:26px;
padding-left:4px 4px 4px 26px;
}
.message-Information, .notifications {
background:#e6f1c9; /* green */