From 22fad0444b5ab6a0b2f24a93eb7ea5aed9c23981 Mon Sep 17 00:00:00 2001 From: neozhu Date: Thu, 23 Sep 2021 10:31:35 +0800 Subject: [PATCH 1/3] Update Program.cs --- src/SmartAdmin.WebUI/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SmartAdmin.WebUI/Program.cs b/src/SmartAdmin.WebUI/Program.cs index 89698ca6..37e351a4 100644 --- a/src/SmartAdmin.WebUI/Program.cs +++ b/src/SmartAdmin.WebUI/Program.cs @@ -15,7 +15,7 @@ namespace SmartAdmin.WebUI { public class Program { - public async static Task Main(string[] args) + public static async Task Main(string[] args) { var filePath = Path.Combine(Directory.GetCurrentDirectory(), "Files"); if (!Directory.Exists(filePath)) From 634462da234eb16fa83a672f9ce4b10a23c9e121 Mon Sep 17 00:00:00 2001 From: neozhu Date: Thu, 23 Sep 2021 17:32:47 +0800 Subject: [PATCH 2/3] WIP: add audit trail --- .../Interfaces/IApplicationDbContext.cs | 2 + .../AuditTrails/DTOs/AuditTrailDto.cs | 24 +++ .../AuditTrailsWithPaginationQuery.cs | 62 ++++++ src/Domain/Common/IAuditTrial.cs | 15 ++ src/Domain/Domain.csproj | 4 + src/Domain/Entities/Audit/AuditTrail.cs | 30 +++ src/Domain/Entities/Customer.cs | 2 +- src/Domain/Enums/AuditType.cs | 19 ++ .../Constants/Permission/Permissions.cs | 6 + .../Persistence/ApplicationDbContext.cs | 99 +++++++++- .../Configurations/AuditTrailConfiguration.cs | 48 +++++ .../Pages/AuditTrails/Index.cshtml | 182 ++++++++++++++++++ .../Pages/AuditTrails/Index.cshtml.cs | 50 +++++ 13 files changed, 540 insertions(+), 3 deletions(-) create mode 100644 src/Application/Features/AuditTrails/DTOs/AuditTrailDto.cs create mode 100644 src/Application/Features/AuditTrails/Queries/PaginationQuery/AuditTrailsWithPaginationQuery.cs create mode 100644 src/Domain/Common/IAuditTrial.cs create mode 100644 src/Domain/Entities/Audit/AuditTrail.cs create mode 100644 src/Domain/Enums/AuditType.cs create mode 100644 src/Infrastructure/Persistence/Configurations/AuditTrailConfiguration.cs create mode 100644 src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml create mode 100644 src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml.cs diff --git a/src/Application/Common/Interfaces/IApplicationDbContext.cs b/src/Application/Common/Interfaces/IApplicationDbContext.cs index 1e38c63f..17605ae9 100644 --- a/src/Application/Common/Interfaces/IApplicationDbContext.cs +++ b/src/Application/Common/Interfaces/IApplicationDbContext.cs @@ -1,6 +1,7 @@ using System.Threading; using System.Threading.Tasks; using CleanArchitecture.Razor.Domain.Entities; +using CleanArchitecture.Razor.Domain.Entities.Audit; using CleanArchitecture.Razor.Domain.Entities.Worflow; using Microsoft.EntityFrameworkCore; @@ -8,6 +9,7 @@ namespace CleanArchitecture.Razor.Application.Common.Interfaces { public interface IApplicationDbContext { + DbSet AuditTrails { get; set; } DbSet Customers { get; set; } DbSet DocumentTypes { get; set; } DbSet Documents { get; set; } diff --git a/src/Application/Features/AuditTrails/DTOs/AuditTrailDto.cs b/src/Application/Features/AuditTrails/DTOs/AuditTrailDto.cs new file mode 100644 index 00000000..ef845ace --- /dev/null +++ b/src/Application/Features/AuditTrails/DTOs/AuditTrailDto.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CleanArchitecture.Razor.Application.Features.AuditTrails.DTOs +{ + public class AuditTrailDto + { + public int Id { get; set; } + public string UserId { get; set; } + public string AuditType { get; set; } + public string TableName { get; set; } + public DateTime DateTime { get; set; } + public string OldValues { get; set; } + public string NewValues { get; set; } + public string AffectedColumns { get; set; } + public string PrimaryKey { get; set; } + } +} diff --git a/src/Application/Features/AuditTrails/Queries/PaginationQuery/AuditTrailsWithPaginationQuery.cs b/src/Application/Features/AuditTrails/Queries/PaginationQuery/AuditTrailsWithPaginationQuery.cs new file mode 100644 index 00000000..daba2915 --- /dev/null +++ b/src/Application/Features/AuditTrails/Queries/PaginationQuery/AuditTrailsWithPaginationQuery.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using AutoMapper; +using CleanArchitecture.Razor.Application.Common.Extensions; +using CleanArchitecture.Razor.Application.Common.Interfaces; +using CleanArchitecture.Razor.Application.Common.Models; +using CleanArchitecture.Razor.Application.Models; +using CleanArchitecture.Razor.Domain.Entities; +using System.Linq.Dynamic.Core; +using MediatR; +using CleanArchitecture.Razor.Application.Common.Mappings; +using AutoMapper.QueryableExtensions; +using CleanArchitecture.Razor.Application.Common.Specification; +using CleanArchitecture.Razor.Application.Features.AuditTrails.DTOs; +using CleanArchitecture.Razor.Domain.Entities.Audit; + +namespace CleanArchitecture.Razor.Application.AuditTrails.Queries.PaginationQuery +{ + public class AuditTrailsWithPaginationQuery : PaginationRequest, IRequest> + { + + + } + public class AuditTrailsQueryHandler : IRequestHandler> + { + private readonly ICurrentUserService _currentUserService; + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + + public AuditTrailsQueryHandler( + ICurrentUserService currentUserService, + IApplicationDbContext context, + IMapper mapper + ) + { + _currentUserService = currentUserService; + _context = context; + _mapper = mapper; + } + public async Task> Handle(AuditTrailsWithPaginationQuery request, CancellationToken cancellationToken) + { + var filters = PredicateBuilder.FromFilter(request.FilterRules); + + var data = await _context.AuditTrails + .Where(filters) + .OrderBy($"{request.Sort} {request.Order}") + .ProjectTo(_mapper.ConfigurationProvider) + .PaginatedDataAsync(request.Page, request.Rows); + + return data; + } + + + } +} diff --git a/src/Domain/Common/IAuditTrial.cs b/src/Domain/Common/IAuditTrial.cs new file mode 100644 index 00000000..c3109178 --- /dev/null +++ b/src/Domain/Common/IAuditTrial.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CleanArchitecture.Razor.Domain.Common +{ + public interface IAuditTrial + { + } +} diff --git a/src/Domain/Domain.csproj b/src/Domain/Domain.csproj index 23098dbf..82714c00 100644 --- a/src/Domain/Domain.csproj +++ b/src/Domain/Domain.csproj @@ -6,4 +6,8 @@ CleanArchitecture.Razor.Domain + + + + diff --git a/src/Domain/Entities/Audit/AuditTrail.cs b/src/Domain/Entities/Audit/AuditTrail.cs new file mode 100644 index 00000000..7c269ee2 --- /dev/null +++ b/src/Domain/Entities/Audit/AuditTrail.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CleanArchitecture.Razor.Domain.Common; +using CleanArchitecture.Razor.Domain.Enums; +using Microsoft.EntityFrameworkCore.ChangeTracking; + +namespace CleanArchitecture.Razor.Domain.Entities.Audit +{ + public class AuditTrail: IEntity + { + public int Id { get; set; } + public string UserId { get; set; } + public AuditType AuditType { get; set; } + public string TableName { get; set; } + public DateTime DateTime { get; set; } + public Dictionary OldValues { get; set; } = new(); + public Dictionary NewValues { get; set; } = new(); + public ICollection AffectedColumns { get; set; } + public Dictionary PrimaryKey { get; set; } = new(); + + public List TemporaryProperties { get; } = new(); + public bool HasTemporaryProperties => TemporaryProperties.Any(); + } +} diff --git a/src/Domain/Entities/Customer.cs b/src/Domain/Entities/Customer.cs index 94e094ba..14041e13 100644 --- a/src/Domain/Entities/Customer.cs +++ b/src/Domain/Entities/Customer.cs @@ -12,7 +12,7 @@ namespace CleanArchitecture.Razor.Domain.Entities { - public partial class Customer : AuditableEntity, IHasDomainEvent + public partial class Customer : AuditableEntity, IHasDomainEvent,IAuditTrial { public int Id { get; set; } public string Name { get; set; } diff --git a/src/Domain/Enums/AuditType.cs b/src/Domain/Enums/AuditType.cs new file mode 100644 index 00000000..fc565262 --- /dev/null +++ b/src/Domain/Enums/AuditType.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CleanArchitecture.Razor.Domain.Enums +{ + public enum AuditType + { + None, + Create, + Update, + Delete + } +} diff --git a/src/Infrastructure/Constants/Permission/Permissions.cs b/src/Infrastructure/Constants/Permission/Permissions.cs index 61538999..93b09524 100644 --- a/src/Infrastructure/Constants/Permission/Permissions.cs +++ b/src/Infrastructure/Constants/Permission/Permissions.cs @@ -7,6 +7,12 @@ namespace CleanArchitecture.Razor.Infrastructure.Constants.Permission { public static class Permissions { + [DisplayName("AuditTrails")] + [Description("AuditTrails Permissions")] + public static class AuditTrails + { + public const string View = "Permissions.AuditTrails.View"; + } [DisplayName("Workflow")] [Description("Approval Permissions")] public static class Approval diff --git a/src/Infrastructure/Persistence/ApplicationDbContext.cs b/src/Infrastructure/Persistence/ApplicationDbContext.cs index ac901ff0..505c70d7 100644 --- a/src/Infrastructure/Persistence/ApplicationDbContext.cs +++ b/src/Infrastructure/Persistence/ApplicationDbContext.cs @@ -1,11 +1,14 @@ using CleanArchitecture.Razor.Application.Common.Interfaces; using CleanArchitecture.Razor.Domain.Common; using CleanArchitecture.Razor.Domain.Entities; +using CleanArchitecture.Razor.Domain.Entities.Audit; using CleanArchitecture.Razor.Domain.Entities.Worflow; +using CleanArchitecture.Razor.Domain.Enums; using CleanArchitecture.Razor.Infrastructure.Identity; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading; @@ -32,7 +35,7 @@ public ApplicationDbContext( _domainEventService = domainEventService; _dateTime = dateTime; } - + public DbSet AuditTrails { get; set; } public DbSet Customers { get; set; } public DbSet DocumentTypes { get; set; } public DbSet Documents { get; set; } @@ -42,6 +45,8 @@ public ApplicationDbContext( public override async Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) { + var auditEntries = OnBeforeSaveChanges(_currentUserService.UserId); + foreach (var entry in ChangeTracker.Entries()) { switch (entry.State) @@ -69,7 +74,7 @@ public ApplicationDbContext( var result = await base.SaveChangesAsync(cancellationToken); await DispatchEvents(); - + await OnAfterSaveChanges(auditEntries, cancellationToken); return result; } @@ -95,5 +100,95 @@ private async Task DispatchEvents() await _domainEventService.Publish(domainEventEntity); } } + + + private List OnBeforeSaveChanges(string userId) + { + ChangeTracker.DetectChanges(); + var auditEntries = new List(); + foreach (var entry in ChangeTracker.Entries()) + { + if (entry.Entity is AuditTrail || + entry.State == EntityState.Detached || + entry.State == EntityState.Unchanged) + continue; + + var auditEntry = new AuditTrail() + { + DateTime = _dateTime.Now, + TableName = entry.Entity.GetType().Name, + UserId = userId, + AffectedColumns = new List() + }; + auditEntries.Add(auditEntry); + foreach (var property in entry.Properties) + { + + if (property.IsTemporary) + { + auditEntry.TemporaryProperties.Add(property); + continue; + } + string propertyName = property.Metadata.Name; + if (property.Metadata.IsPrimaryKey()) + { + auditEntry.PrimaryKey[propertyName] = property.CurrentValue; + continue; + } + + switch (entry.State) + { + case EntityState.Added: + auditEntry.AuditType = AuditType.Create; + auditEntry.NewValues[propertyName] = property.CurrentValue; + break; + + case EntityState.Deleted: + auditEntry.AuditType = AuditType.Delete; + auditEntry.OldValues[propertyName] = property.OriginalValue; + break; + + case EntityState.Modified: + if (property.IsModified && property.OriginalValue?.Equals(property.CurrentValue) == false) + { + auditEntry.AffectedColumns.Add(propertyName); + auditEntry.AuditType = AuditType.Update; + auditEntry.OldValues[propertyName] = property.OriginalValue; + auditEntry.NewValues[propertyName] = property.CurrentValue; + } + break; + } + } + } + + foreach (var auditEntry in auditEntries.Where(_ => !_.HasTemporaryProperties)) + { + AuditTrails.Add(auditEntry); + } + return auditEntries.Where(_ => _.HasTemporaryProperties).ToList(); + } + + private Task OnAfterSaveChanges(List auditEntries, CancellationToken cancellationToken = new()) + { + if (auditEntries == null || auditEntries.Count == 0) + return Task.CompletedTask; + + foreach (var auditEntry in auditEntries) + { + foreach (var prop in auditEntry.TemporaryProperties) + { + if (prop.Metadata.IsPrimaryKey()) + { + auditEntry.PrimaryKey[prop.Metadata.Name] = prop.CurrentValue; + } + else + { + auditEntry.NewValues[prop.Metadata.Name] = prop.CurrentValue; + } + } + AuditTrails.Add(auditEntry); + } + return SaveChangesAsync(cancellationToken); + } } } diff --git a/src/Infrastructure/Persistence/Configurations/AuditTrailConfiguration.cs b/src/Infrastructure/Persistence/Configurations/AuditTrailConfiguration.cs new file mode 100644 index 00000000..a4850d1c --- /dev/null +++ b/src/Infrastructure/Persistence/Configurations/AuditTrailConfiguration.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using CleanArchitecture.Razor.Domain.Entities; +using CleanArchitecture.Razor.Domain.Entities.Audit; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace CleanArchitecture.Infrastructure.Persistence.Configurations +{ + public class AuditTrailConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.Property(t => t.AuditType) + .HasConversion(); + builder.Property(e => e.AffectedColumns) + .HasConversion( + v => JsonSerializer.Serialize(v, null), + v => JsonSerializer.Deserialize>(v, null), + new ValueComparer>( + (c1, c2) => c1.SequenceEqual(c2), + c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), + c => (ICollection)c.ToList())); + + builder.Property(u => u.OldValues) + .HasConversion( + d => JsonSerializer.Serialize(d, null), + s => JsonSerializer.Deserialize>(s, null) + ); + builder.Property(u => u.NewValues) + .HasConversion( + d => JsonSerializer.Serialize(d, null), + s => JsonSerializer.Deserialize>(s, null) + ); + builder.Property(u => u.PrimaryKey) + .HasConversion( + d => JsonSerializer.Serialize(d, null), + s => JsonSerializer.Deserialize>(s, null) + ); + + builder.Ignore(x => x.TemporaryProperties); + builder.Ignore(x => x.HasTemporaryProperties); + } + } +} diff --git a/src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml b/src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml new file mode 100644 index 00000000..6e7b4455 --- /dev/null +++ b/src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml @@ -0,0 +1,182 @@ +@page +@using CleanArchitecture.Razor.Infrastructure.Constants.Permission +@model SmartAdmin.WebUI.Pages.Approval.Histories +@inject Microsoft.Extensions.Localization.IStringLocalizer _localizer +@inject Microsoft.AspNetCore.Authorization.IAuthorizationService _authorizationService +@{ + ViewData["Title"] = _localizer["Audit Trails"].Value; + ViewData["PageName"] = "approval_histories"; + ViewData["Category1"] = _localizer["Workflow"].Value; + ViewData["Heading"] = _localizer["Audit Trails"].Value; + ViewData["PageDescription"] = _localizer["See all available options"].Value; + ViewData["PreemptiveClass"] = "Default"; + var _canSearch = await _authorizationService.AuthorizeAsync(User, null, Permissions.Approval.Search); + var _canExport = await _authorizationService.AuthorizeAsync(User, null, Permissions.Approval.Export); + +} +@section HeadBlock { + + + + + +} +
+
+

+ @_localizer["Approval"] + @_localizer["See all available options"] +

+
+ @if (_canSearch.Succeeded) + { + + } + @if (_canExport.Succeeded) + { + + } + +
+
+
+
+
+ +
+
+
+
+
+ + +@section ScriptsBlock { + + + + + + + +} diff --git a/src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml.cs b/src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml.cs new file mode 100644 index 00000000..5b673f71 --- /dev/null +++ b/src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml.cs @@ -0,0 +1,50 @@ +using System.Threading.Tasks; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Localization; +using Microsoft.AspNetCore.Authorization; +using CleanArchitecture.Razor.Application.Features.ApprovalDatas.Queries.Pagination; +using CleanArchitecture.Razor.Infrastructure.Constants.Permission; +using CleanArchitecture.Razor.Application.Features.ApprovalDatas.Queries.Export; +using CleanArchitecture.Razor.Application.AuditTrails.Queries.PaginationQuery; + +namespace SmartAdmin.WebUI.Pages.AuditTrails +{ + [Authorize(policy: Permissions.AuditTrails.View)] + public class IndexModel : PageModel + { + private readonly ISender _mediator; + private readonly IStringLocalizer _localizer; + [BindProperty] + public string WorkflowId { get; set; } + [BindProperty] + public string Comments { get; set; } + [BindProperty] + public string Outcome { get; set; } + [BindProperty] + public string Approver { get; set; } + public IndexModel( + ISender mediator, + IStringLocalizer localizer + ) + { + _mediator = mediator; + _localizer = localizer; + } + public async Task OnGetAsync() + { + + } + public async Task OnGetDataAsync([FromQuery] AuditTrailsWithPaginationQuery command) + { + var result = await _mediator.Send(command); + return new JsonResult(result); + } + + + + + + } +} From 5e282855625ccca5ae105e210afb86703ad330ee Mon Sep 17 00:00:00 2001 From: neozhu Date: Thu, 23 Sep 2021 19:03:16 +0800 Subject: [PATCH 3/3] add audit trail features --- src/Application/Application.csproj | 9 + .../AuditTrails/DTOs/AuditTrailDto.cs | 17 +- .../Queries/Export/ExportAuditTrailsQuery.cs | 73 ++++++++ .../Export/ExportAuditTrailsQuery.Designer.cs | 118 +++++++++++++ .../Export/ExportAuditTrailsQuery.de-DE.resx | 138 +++++++++++++++ .../Export/ExportAuditTrailsQuery.en.resx | 138 +++++++++++++++ .../Export/ExportAuditTrailsQuery.ja-JP.resx | 138 +++++++++++++++ .../Export/ExportAuditTrailsQuery.resx | 138 +++++++++++++++ .../Export/ExportAuditTrailsQuery.ru.resx | 138 +++++++++++++++ .../Export/ExportAuditTrailsQuery.zh-CN.resx | 138 +++++++++++++++ .../Constants/Permission/Permissions.cs | 2 + .../Pages/Approval/Histories.cshtml | 17 -- .../Pages/AuditTrails/Index.cshtml | 76 ++++----- .../Pages/AuditTrails/Index.cshtml.cs | 7 +- .../Pages/AuditTrails/IndexModel.de-DE.resx | 159 ++++++++++++++++++ .../Pages/AuditTrails/IndexModel.en.resx | 159 ++++++++++++++++++ .../Pages/AuditTrails/IndexModel.ja-JP.resx | 159 ++++++++++++++++++ .../Pages/AuditTrails/IndexModel.resx | 159 ++++++++++++++++++ .../Pages/AuditTrails/IndexModel.ru.resx | 159 ++++++++++++++++++ .../Pages/AuditTrails/IndexModel.zh-CN.resx | 159 ++++++++++++++++++ src/SmartAdmin.WebUI/nav.json | 5 + 21 files changed, 2042 insertions(+), 64 deletions(-) create mode 100644 src/Application/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.cs create mode 100644 src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.Designer.cs create mode 100644 src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.de-DE.resx create mode 100644 src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.en.resx create mode 100644 src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.ja-JP.resx create mode 100644 src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.resx create mode 100644 src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.ru.resx create mode 100644 src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.zh-CN.resx create mode 100644 src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.de-DE.resx create mode 100644 src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.en.resx create mode 100644 src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.ja-JP.resx create mode 100644 src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.resx create mode 100644 src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.ru.resx create mode 100644 src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.zh-CN.resx diff --git a/src/Application/Application.csproj b/src/Application/Application.csproj index 4dbbb2e6..06f20aa7 100644 --- a/src/Application/Application.csproj +++ b/src/Application/Application.csproj @@ -74,6 +74,11 @@ True ExportApprovalDatasQueryHandler.resx + + True + True + ExportAuditTrailsQuery.resx + True True @@ -111,6 +116,10 @@ ResXFileCodeGenerator ExportApprovalDatasQueryHandler.Designer.cs + + ResXFileCodeGenerator + ExportAuditTrailsQuery.Designer.cs + ResXFileCodeGenerator ImportKeyValuesCommandHandler.Designer.cs diff --git a/src/Application/Features/AuditTrails/DTOs/AuditTrailDto.cs b/src/Application/Features/AuditTrails/DTOs/AuditTrailDto.cs index ef845ace..4f981e84 100644 --- a/src/Application/Features/AuditTrails/DTOs/AuditTrailDto.cs +++ b/src/Application/Features/AuditTrails/DTOs/AuditTrailDto.cs @@ -5,12 +5,27 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.Json; using System.Threading.Tasks; +using AutoMapper; +using CleanArchitecture.Razor.Application.Common.Mappings; +using CleanArchitecture.Razor.Domain.Entities.Audit; namespace CleanArchitecture.Razor.Application.Features.AuditTrails.DTOs { - public class AuditTrailDto + public class AuditTrailDto : IMapFrom { + public void Mapping(Profile profile) + { + profile.CreateMap() + .ForMember(x => x.AuditType, s => s.MapFrom(y => y.AuditType.ToString())) + .ForMember(x => x.OldValues, s => s.MapFrom(y => JsonSerializer.Serialize(y.OldValues, null))) + .ForMember(x => x.NewValues, s => s.MapFrom(y => JsonSerializer.Serialize(y.NewValues, null))) + .ForMember(x => x.PrimaryKey, s => s.MapFrom(y => JsonSerializer.Serialize(y.PrimaryKey, null))) + .ForMember(x => x.AffectedColumns, s => s.MapFrom(y => JsonSerializer.Serialize(y.AffectedColumns, null))) + ; + + } public int Id { get; set; } public string UserId { get; set; } public string AuditType { get; set; } diff --git a/src/Application/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.cs b/src/Application/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.cs new file mode 100644 index 00000000..9d212c4f --- /dev/null +++ b/src/Application/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using AutoMapper; +using CleanArchitecture.Razor.Application.Common.Extensions; +using CleanArchitecture.Razor.Application.Common.Interfaces; +using CleanArchitecture.Razor.Domain.Entities; +using System.Linq.Dynamic.Core; +using MediatR; +using Microsoft.EntityFrameworkCore; +using AutoMapper.QueryableExtensions; +using Microsoft.Extensions.Localization; +using CleanArchitecture.Razor.Application.Features.AuditTrails.DTOs; +using CleanArchitecture.Razor.Domain.Entities.Worflow; +using CleanArchitecture.Razor.Domain.Entities.Audit; + +namespace CleanArchitecture.Razor.Application.Features.AuditTrails.Queries.Export +{ + public class ExportAuditTrailsQuery : IRequest + { + public string filterRules { get; set; } + public string sort { get; set; } = "Id"; + public string order { get; set; } = "desc"; + } + + public class ExportAuditTrailsQueryHandler : + IRequestHandler + { + private readonly IApplicationDbContext _context; + private readonly IMapper _mapper; + private readonly IExcelService _excelService; + private readonly IStringLocalizer _localizer; + + public ExportAuditTrailsQueryHandler( + IApplicationDbContext context, + IMapper mapper, + IExcelService excelService, + IStringLocalizer localizer + ) + { + _context = context; + _mapper = mapper; + _excelService = excelService; + _localizer = localizer; + } + + public async Task Handle(ExportAuditTrailsQuery request, CancellationToken cancellationToken) + { + var filters = PredicateBuilder.FromFilter(request.filterRules); + var data = await _context.AuditTrails + .Where(filters) + .OrderBy($"{request.sort} {request.order}") + .ProjectTo(_mapper.ConfigurationProvider) + .ToListAsync(cancellationToken); + var result = await _excelService.ExportAsync(data, + new Dictionary>() + { + //{ _localizer["Id"], item => item.Id }, + { _localizer["Date Time"], item => item.DateTime.ToString("yyyy-MM-dd HH:mm:ss") }, + { _localizer["Table Name"], item => item.TableName }, + { _localizer["Audit Type"], item => item.AuditType }, + { _localizer["Old Values"], item => item.OldValues }, + { _localizer["New Values"], item => item.NewValues }, + { _localizer["Primary Key"], item => item.PrimaryKey }, + }, _localizer["AuditTrails"] + ); + return result; + } + } +} + diff --git a/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.Designer.cs b/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.Designer.cs new file mode 100644 index 00000000..b1ca1cfa --- /dev/null +++ b/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.Designer.cs @@ -0,0 +1,118 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace CleanArchitecture.Razor.Application.Resources.Features.AuditTrails.Queries.Export { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ExportAuditTrailsQuery { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ExportAuditTrailsQuery() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CleanArchitecture.Razor.Application.Resources.Features.AuditTrails.Queries.Export" + + ".ExportAuditTrailsQuery", typeof(ExportAuditTrailsQuery).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to AuditTrails. + /// + internal static string AuditTrails { + get { + return ResourceManager.GetString("AuditTrails", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Date Time. + /// + internal static string Date_Time { + get { + return ResourceManager.GetString("Date Time", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Values. + /// + internal static string New_Values { + get { + return ResourceManager.GetString("New Values", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Old Values. + /// + internal static string Old_Values { + get { + return ResourceManager.GetString("Old Values", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Primary Key. + /// + internal static string Primary_Key { + get { + return ResourceManager.GetString("Primary Key", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Table Name. + /// + internal static string Table_Name { + get { + return ResourceManager.GetString("Table Name", resourceCulture); + } + } + } +} diff --git a/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.de-DE.resx b/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.de-DE.resx new file mode 100644 index 00000000..2b12c9d4 --- /dev/null +++ b/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.de-DE.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Buchungsprotokolle + + + Terminzeit + + + Neue Werte + + + Alte Werte + + + Primärschlüssel + + + Tabellenname + + \ No newline at end of file diff --git a/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.en.resx b/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.en.resx new file mode 100644 index 00000000..d5f7260c --- /dev/null +++ b/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.en.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + AuditTrails + + + Date Time + + + New Values + + + Old Values + + + Primary Key + + + Table Name + + \ No newline at end of file diff --git a/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.ja-JP.resx b/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.ja-JP.resx new file mode 100644 index 00000000..9965bed5 --- /dev/null +++ b/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.ja-JP.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 監査証跡 + + + 日付時刻 + + + 新しい値 + + + 古い値 + + + 主キー + + + テーブル名 + + \ No newline at end of file diff --git a/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.resx b/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.resx new file mode 100644 index 00000000..d5f7260c --- /dev/null +++ b/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + AuditTrails + + + Date Time + + + New Values + + + Old Values + + + Primary Key + + + Table Name + + \ No newline at end of file diff --git a/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.ru.resx b/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.ru.resx new file mode 100644 index 00000000..3d18b495 --- /dev/null +++ b/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.ru.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + AuditTrails + + + Дата Время + + + Новые ценности + + + Старые ценности + + + Основной ключ + + + Имя таблицы + + \ No newline at end of file diff --git a/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.zh-CN.resx b/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.zh-CN.resx new file mode 100644 index 00000000..14ce030f --- /dev/null +++ b/src/Application/Resources/Features/AuditTrails/Queries/Export/ExportAuditTrailsQuery.zh-CN.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 审计跟踪 + + + 记录时间 + + + 新值 + + + 旧值 + + + 主键 + + + 表名 + + \ No newline at end of file diff --git a/src/Infrastructure/Constants/Permission/Permissions.cs b/src/Infrastructure/Constants/Permission/Permissions.cs index 93b09524..4f72e626 100644 --- a/src/Infrastructure/Constants/Permission/Permissions.cs +++ b/src/Infrastructure/Constants/Permission/Permissions.cs @@ -12,6 +12,8 @@ public static class Permissions public static class AuditTrails { public const string View = "Permissions.AuditTrails.View"; + public const string Search = "Permissions.AuditTrails.Search"; + public const string Export = "Permissions.AuditTrails.Export"; } [DisplayName("Workflow")] [Description("Approval Permissions")] diff --git a/src/SmartAdmin.WebUI/Pages/Approval/Histories.cshtml b/src/SmartAdmin.WebUI/Pages/Approval/Histories.cshtml index 59fdfa75..6a52b82b 100644 --- a/src/SmartAdmin.WebUI/Pages/Approval/Histories.cshtml +++ b/src/SmartAdmin.WebUI/Pages/Approval/Histories.cshtml @@ -90,23 +90,6 @@ sortOrder: 'desc', pageSize: 15, pageList: [10, 15, 30, 50, 100, 1000], - onBeforeLoad: function () { - $('#approvalbutton').prop('disabled', true); - }, - onCheckAll: function (rows) { - const checked = $(this).datagrid('getChecked').length > 0; - $('#approvalbutton').prop('disabled', !checked); - }, - onUncheckAll: function () { - $('#approvalbutton').prop('disabled', true); - }, - onCheck: function () { - $('#approvalbutton').prop('disabled', false); - }, - onUncheck: function () { - const checked = $(this).datagrid('getChecked').length > 0; - $('#approvalbutton').prop('disabled', !checked); - }, columns: [[ { field: 'ck', checkbox: true }, { field: 'WorkflowName', title: '@_localizer["Workflow Name"]', sortable: true, width: 200 }, diff --git a/src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml b/src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml index 6e7b4455..3234e6cc 100644 --- a/src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml +++ b/src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml @@ -1,17 +1,17 @@ @page @using CleanArchitecture.Razor.Infrastructure.Constants.Permission -@model SmartAdmin.WebUI.Pages.Approval.Histories -@inject Microsoft.Extensions.Localization.IStringLocalizer _localizer +@model SmartAdmin.WebUI.Pages.AuditTrails.IndexModel +@inject Microsoft.Extensions.Localization.IStringLocalizer _localizer @inject Microsoft.AspNetCore.Authorization.IAuthorizationService _authorizationService @{ - ViewData["Title"] = _localizer["Audit Trails"].Value; - ViewData["PageName"] = "approval_histories"; - ViewData["Category1"] = _localizer["Workflow"].Value; - ViewData["Heading"] = _localizer["Audit Trails"].Value; - ViewData["PageDescription"] = _localizer["See all available options"].Value; - ViewData["PreemptiveClass"] = "Default"; - var _canSearch = await _authorizationService.AuthorizeAsync(User, null, Permissions.Approval.Search); - var _canExport = await _authorizationService.AuthorizeAsync(User, null, Permissions.Approval.Export); + ViewData["Title"] = _localizer["Audit Trails"].Value; + ViewData["PageName"] = "audittrails_index"; + ViewData["Category1"] = _localizer["Authorization"].Value; + ViewData["Heading"] = _localizer["Audit Trails"].Value; + ViewData["PageDescription"] = _localizer["See all available options"].Value; + ViewData["PreemptiveClass"] = "Default"; + var _canSearch = await _authorizationService.AuthorizeAsync(User, null, Permissions.AuditTrails.Search); + var _canExport = await _authorizationService.AuthorizeAsync(User, null, Permissions.AuditTrails.Export); } @section HeadBlock { @@ -28,7 +28,7 @@

- @_localizer["Approval"] + @_localizer["Audit Trails"] @_localizer["See all available options"]

@@ -52,7 +52,7 @@
- +
@@ -76,7 +76,7 @@ }); var $dg={}; var initdatagrid = () => { - $dg = $('#approval_dg').datagrid({ + $dg = $('#audit_dg').datagrid({ height: 700, method: 'GET', rownumbers: false, @@ -86,47 +86,26 @@ pagination: true, clientPaging: false, remoteFilter: true, - sortName: 'RequestDateTime', + sortName: 'DateTime', sortOrder: 'desc', pageSize: 15, pageList: [10, 15, 30, 50, 100, 1000], - onBeforeLoad: function () { - $('#approvalbutton').prop('disabled', true); - }, - onCheckAll: function (rows) { - const checked = $(this).datagrid('getChecked').length > 0; - $('#approvalbutton').prop('disabled', !checked); - }, - onUncheckAll: function () { - $('#approvalbutton').prop('disabled', true); - }, - onCheck: function () { - $('#approvalbutton').prop('disabled', false); - }, - onUncheck: function () { - const checked = $(this).datagrid('getChecked').length > 0; - $('#approvalbutton').prop('disabled', !checked); - }, columns: [[ - { field: 'ck', checkbox: true }, - { field: 'WorkflowName', title: '@_localizer["Workflow Name"]', sortable: true, width: 200 }, - { field: 'Status', title: '@_localizer["Status"]', sortable: true, width: 130 }, - { field: 'DocumentName', title: '@_localizer["Document Name"]', sortable: true, width: 180 }, - { field: 'Applicant', title: '@_localizer["Applicant"]', sortable: true, width: 130 }, - { field: 'Approver', title: '@_localizer["Approver"]', sortable: true, width: 130 }, - { field: 'Outcome', title: '@_localizer["Outcome"]', sortable: true, width: 130 }, - { field: 'Comments', title: '@_localizer["Comments"]', sortable: true, width: 230 }, - { field: 'RequestDateTime', title: '@_localizer["Request DateTime"]', formatter: datetimeformatter, sortable: true, width: 160 }, - { field: 'ApprovedDateTime', title: '@_localizer["Approved DateTime"]', formatter: datetimeformatter, sortable: true, width: 160 }, + { field: 'DateTime', title: '@_localizer["Date Time"]', sortable: true, width: 165, formatter: datetimeformatter }, + { field: 'TableName', title: '@_localizer["Table Name"]', sortable: true, width: 150 }, + { field: 'AuditType', title: '@_localizer["Audit Type"]', sortable: true, width: 120 }, + { field: 'PrimaryKey', title: '@_localizer["Primary Key"]', sortable: true, width: 160 }, + { field: 'OldValues', title: '@_localizer["Old Values"]', sortable: true, width: 280 }, + { field: 'NewValues', title: '@_localizer["New Values"]', sortable: true, width: 280 }, ]] }) .datagrid('enableFilter', {}) - .datagrid('load', '@Url.Page("/Approval/Histories")?handler=Data'); + .datagrid('load', '@Url.Page("/AuditTrails/Index")?handler=Data'); } var reload = () => { - $dg.datagrid('load', '@Url.Page("/Approval/Histories")?handler=Data'); + $dg.datagrid('load', '@Url.Page("/AuditTrails/Index")?handler=Data'); } $(() => { @@ -143,7 +122,7 @@ var headers = { "RequestVerificationToken": $('input[name="__RequestVerificationToken"]').val() } - axios.post('@Url.Page("/Approval/Histories")?handler=Export', + axios.post('@Url.Page("/AuditTrails/Index")?handler=Export', data, { headers: headers, @@ -159,7 +138,14 @@ link.click(); toastr["info"](`@_localizer["Export Success"]`); }).catch(error => { - console.log(error) + if (error.response.data.Errors) { + const errors = error.response.data.Errors; + errors.forEach(item => { + toastr["error"](item); + }); + } else { + toastr["error"](`@_localizer["Export fail"],${error.response.data}`); + } }) diff --git a/src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml.cs b/src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml.cs index 5b673f71..384517df 100644 --- a/src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml.cs +++ b/src/SmartAdmin.WebUI/Pages/AuditTrails/Index.cshtml.cs @@ -8,6 +8,7 @@ using CleanArchitecture.Razor.Infrastructure.Constants.Permission; using CleanArchitecture.Razor.Application.Features.ApprovalDatas.Queries.Export; using CleanArchitecture.Razor.Application.AuditTrails.Queries.PaginationQuery; +using CleanArchitecture.Razor.Application.Features.AuditTrails.Queries.Export; namespace SmartAdmin.WebUI.Pages.AuditTrails { @@ -41,7 +42,11 @@ public async Task OnGetDataAsync([FromQuery] AuditTrailsWithPagin var result = await _mediator.Send(command); return new JsonResult(result); } - + public async Task OnPostExportAsync([FromBody] ExportAuditTrailsQuery command) + { + var result = await _mediator.Send(command); + return File(result, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", _localizer["ApprovalHistories"] + ".xlsx"); + } diff --git a/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.de-DE.resx b/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.de-DE.resx new file mode 100644 index 00000000..8fa717f3 --- /dev/null +++ b/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.de-DE.resx @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Buchungsprotokolle + + + Audit-Typ + + + Genehmigung + + + Terminzeit + + + Excel exportieren + + + Exportieren fehlgeschlagen + + + Exporterfolg + + + Neue Werte + + + Alte Werte + + + Primärschlüssel + + + Suche + + + Alle verfügbaren Optionen anzeigen + + + Tabellenname + + \ No newline at end of file diff --git a/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.en.resx b/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.en.resx new file mode 100644 index 00000000..f239779b --- /dev/null +++ b/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.en.resx @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Audit Trails + + + Audit Type + + + Authorization + + + Date Time + + + Export Excel + + + Export fail + + + Export Success + + + New Values + + + Old Values + + + Primary Key + + + Search + + + See all available options + + + Table Name + + \ No newline at end of file diff --git a/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.ja-JP.resx b/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.ja-JP.resx new file mode 100644 index 00000000..b7159ec9 --- /dev/null +++ b/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.ja-JP.resx @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 監査証跡 + + + 監査タイプ + + + 承認 + + + 日付時刻 + + + Excelをエクスポートする + + + エクスポートに失敗する + + + エクスポートの成功 + + + 新しい値 + + + 古い値 + + + 主キー + + + 検索 + + + 利用可能なすべてのオプションを表示 + + + テーブル名 + + \ No newline at end of file diff --git a/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.resx b/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.resx new file mode 100644 index 00000000..f239779b --- /dev/null +++ b/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.resx @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Audit Trails + + + Audit Type + + + Authorization + + + Date Time + + + Export Excel + + + Export fail + + + Export Success + + + New Values + + + Old Values + + + Primary Key + + + Search + + + See all available options + + + Table Name + + \ No newline at end of file diff --git a/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.ru.resx b/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.ru.resx new file mode 100644 index 00000000..fcf190aa --- /dev/null +++ b/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.ru.resx @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Журналы аудита + + + Тип аудита + + + Авторизация + + + Дата Время + + + Экспорт в Excel + + + Ошибка экспорта + + + Успех экспорта + + + Новые ценности + + + Старые ценности + + + Основной ключ + + + Поиск + + + Посмотреть все доступные варианты + + + Имя таблицы + + \ No newline at end of file diff --git a/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.zh-CN.resx b/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.zh-CN.resx new file mode 100644 index 00000000..db5ed90b --- /dev/null +++ b/src/SmartAdmin.WebUI/Resources/Pages/AuditTrails/IndexModel.zh-CN.resx @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 审计跟踪 + + + 审核类型 + + + 授权 + + + 记录时间 + + + 导出 Excel + + + 导出失败 + + + 出口成功 + + + 新值 + + + 旧值 + + + 主键 + + + 搜索 + + + 查看所有可用选项 + + + 表名 + + \ No newline at end of file diff --git a/src/SmartAdmin.WebUI/nav.json b/src/SmartAdmin.WebUI/nav.json index de780228..8ce18d13 100644 --- a/src/SmartAdmin.WebUI/nav.json +++ b/src/SmartAdmin.WebUI/nav.json @@ -81,6 +81,11 @@ "title": "Roles", "href": "authorization_roles.html", "roles": [] + }, + { + "title": "Audit Trails", + "href": "audittrails_index.html", + "roles": [] } ] },