Skip to content

Audit Details in the XYZ form as Master Detail relation

Victor Tomaili edited this page May 3, 2021 · 1 revision

Audit Details IN XyzDialog

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

Example Screenshot

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>
Clone this wiki locally