-
Notifications
You must be signed in to change notification settings - Fork 802
Audit Details in the XYZ form as Master Detail relation
I used the example for auditing that can be found in the WIKI to create an auditing system for my project - I find that it is functional and works for my purposes.
While I could do a straight listing of the AuditLogRow I felt it lacked some user friendly non-technical functionality that might be useful to some of the system administrators checking on who is changing what.
I was originally wanting a way to show the auditing for a particular table and to do it dynamically or at least Generically rather than having a lot of classes to accomplish this. Accomplishing that task seemed to be a bit of a challenge that requires more of my time than I have right now, so I have implemented this method that I outline here. I know that my original plan can be done and in the future if I find the time I will try to accomplish something on the order of AuditLog<T>
.
So one of the ways I found was to utilize the master-detail approach so that for any given record I could easily see the changes on that specific record. Again this method is not the ideal way I had originally desired but it works for right now as long as I have an idea of the 'record' that I am interested in seeing those changes.
An example screenshot is below
Note: the Column TableName has been renamed to Tablename so as to not create conflicts with the KeyWord.
//=====================================================
// MyTblRow.cs
//=====================================================
public sealed class MyTblRow : Row, IIdRow, INameRow, IIsActiveDeletedRow, IIsActiveRow, IAuditLog
{
#region MASTER-DETAIL
[DisplayName("Audit Log Details"), IgnoreAuditLog]
[MasterDetailRelation(foreignKey: "RowId", MasterKeyField = "Id",
FilterField = "Module", FilterValue = "MyTblRow"), NotMapped, Updatable(false),Insertable(false)]
public List<AuditLogRow> AuditList
{
get { return Fields.AuditList[this]; }
set { Fields.AuditList[this] = value; }
}
#endregion
public class RowFields : RowFieldsBase
{
public RowListField<AuditLogRow> AuditList;
}
}
//=====================================================
// MyTblForm.ts
//=====================================================
public class MyTblForm
{
[Tab("Auditing"), AllowHide(true),
InsertPermission(PermissionKeys.Administration),
ModifyPermission(PermissionKeys.Administration),
ReadPermission(PermissionKeys.Administration)]
[Category("Audit")]
[AuditLogDetailEditor]
public List<Entities.AuditLogRow> AuditList { get; set; }
}
//=====================================================
// AuditLogDetailEditor.ts
//=====================================================
/// <reference path="../../Common/Helpers/GridEditorBase.ts" />
namespace MyProject.MyModuleDB {
@Serenity.Decorators.registerClass()
export class AuditLogDetailEditor extends Common.GridEditorBase<AuditLogRow> {
protected getColumnsKey() { return "MyModuleDB.AuditLogDetail"; }
// protected getFormKey() { return AuditLogForm.formKey; }
protected getDialogType() { return AuditLogDetailDialog; }
protected getLocalTextPrefix() { return AuditLogRow.localTextPrefix; }
constructor(container: JQuery) {
super(container);
this.removeComponents();
}
protected removeComponents() {
this.element.find(".tool-buttons").remove();
this.element.find(".s-Toolbar").remove();
}
}
}
//=====================================================
// AuditLogDetailDialog.ts
//=====================================================
namespace MyProject.MyModuleDB {
@Serenity.Decorators.registerClass()
export class AuditLogDetailDialog extends Serenity.EntityDialog<AuditLogRow, any> {
protected getFormKey() { return AuditLogDetailForm.formKey; }
protected getIdProperty() { return AuditLogRow.idProperty; }
protected getLocalTextPrefix() { return AuditLogRow.localTextPrefix; }
protected getNameProperty() { return AuditLogRow.nameProperty; }
protected getService() { return AuditLogService.baseUrl; }
protected form = new AuditLogDetailForm(this.idPrefix);
constructor(container: JQuery) {
super(container);
}
}
}
//=====================================================
// AuditLogDetailColumns.cs
//=====================================================
namespace MyProject.MyModuleDB.Columns
{
using Serenity;
using Serenity.ComponentModel;
using Serenity.Data;
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.IO;
[ColumnsScript("MyModuleDB.AuditLogDetail")]
[BasedOnRow(typeof(Entities.AuditLogRow), CheckNames = true)]
public class AuditLogDetailColumns
{
[EditLink, DisplayName("Db.Shared.RecordId"), AlignRight, Hidden]
public Int64 Id { get; set; }
[Hidden]
public Int32 UserId { get; set; }
[DisplayName("Record Id"),Width(90)]
public Int32 RowId { get; set; }
[DisplayName("Action"), Width(90)]
public String Action { get; set; }
[DisplayName("Changes"), Width(330)]
public String Changes { get; set; }
[DisplayName("Changed On"), Width(90)]
public DateTime ChangedOn { get; set; }
[DisplayName("Changed By"), Width(90)]
public String UserName { get; set; }
[DisplayName("Service URL"), Width(280)]
public String Page { get; set; }
}
}
//=====================================================
// AuditLogDetailReport.cs
//=====================================================
namespace MyProject.MyModuleDB
{
using Entities;
using Serenity.ComponentModel;
using Serenity.Data;
using Serenity.Reporting;
using System;
using System.Collections.Generic;
[Report("MyModuleDB.AuditLogDetail")]
[ReportDesign(MVC.Views.MyModuleDB.AuditLogDetails.AuditLogDetailReport)]
[RequiredPermission(PermissionKeys.General)]
public class AuditLogDetailReport : IReport, ICustomizeHtmlToPdf
{
public object GetData()
{
// return base.GetData();
using (var connection = SqlConnections.NewFor<MyAudit>())
{
var fld = MyAudit.Fields;
var OrderByFlds = new IField[] { fld.Tablename, fld.UserName };
return connection.List<MyAudit>(q => q
.Select(fld.UserName, fld.Action, fld.Tablename, fld.Module,
fld.RowId, fld.Changes, fld.ChangedOn, fld.Page)
.GroupBy(fld.Tablename)
.OrderBy(OrderByFlds));
}
}
public void Customize(IHtmlToPdfOptions options)
{
// you may customize HTML to PDF converter (WKHTML) parameters here, e.g.
// options.MarginsAll = "2cm";
}
}
public class AuditLogDetailReportData
{
public AuditLogRow AffectedRow { get; set; }
public List<AuditLogRow> AuditDetails { get; set; }
public List<MyTblRow> MyTblDetails { get; set; }
}
}
//=====================================================
// AuditLogDetailReport.cshtml JUST QUICK MARK UP.
//=====================================================
@model MyProject.MyModuleDB.AuditLogDetailReportData
@if ((bool?)ViewData["Printing"] == true)
{
Layout = MVC.Views.Shared._LayoutNoNavigation;
}
<section class="invoice">
<div class="row">
<div class="col-xs-12">
<h2 class="page-header">
Audit Log Detail
<small class="pull-right">Date: @DateTime.Now.ToShortDateString()</small>
</h2>
</div>
</div>
<!-- info row -->
<div class="row invoice-info">
@*<div class="col-sm-4 invoice-col">
From
<address>
<strong>MyCompany, LLC.</strong><br>
Info Industrial Center<br>
WuHa, AK 85301<br>
Phone: (555) 867-5309<br>
Email: [email protected]
</address>
</div>*@
<!-- /.col -->
@*<div class="col-sm-4 invoice-col">
To
<address>
<strong>@Model.Customer.CompanyName</strong><br>
@Model.Customer.Address
Phone: @Model.Customer.Phone<br>
Fax: @Model.Customer.Fax
</address>
</div>*@
<!-- /.col -->
@*<div class="col-sm-4 invoice-col">
<b>Invoice #IX@(Model.Order.OrderID)</b><br>
<br>
<b>Order ID:</b> @Model.Order.OrderID<br>
<b>Payment Due:</b> @Model.Order.OrderDate.Value.ToShortDateString()<br>
</div>*@
<!-- /.col -->
</div>
<!-- /.row -->
<!-- Table row -->
<div class="row">
<div class="col-xs-12 table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>UserName</th>
<th>Action</th>
<th>Tablename</th>
<th>Module</th>
<th>RowId</th>
<th>Changes</th>
<th>Page</th>
<th>ChangedOn</th>
</tr>
</thead>
<tbody>
@foreach (var d in Model.Details)
{
<tr>
<td>@(d.UserName)</td>
<td>@(d.Action)</td>
<td>@d.Tablename</td>
<td>@(d.Module)</td>
<td>@(d.RowId)</td>
<td>@(d.Changes)</td>
<td>@(d.Page)</td>
<td>@(d.ChangedOn)</td>
</tr>
}
</tbody>
</table>
</div>
<!-- /.col -->
</div>
</section>
Copyright © Serenity Platform 2017-present. All rights reserved.
Documentation | Serene Template | Live Demo | Premium Support | Issues | Discussions