using Microsoft.Extensions.Logging; using NPoco; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Cms.Infrastructure.Persistence.Querying; using Umbraco.Cms.Infrastructure.Scoping; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; internal class AuditRepository : EntityRepositoryBase, IAuditRepository { public AuditRepository(IScopeAccessor scopeAccessor, ILogger logger) : base(scopeAccessor, AppCaches.NoCache, logger) { } public IEnumerable Get(AuditType type, IQuery query) { Sql? sqlClause = GetBaseQuery(false) .Where("(logHeader=@0)", type.ToString()); var translator = new SqlTranslator(sqlClause, query); Sql sql = translator.Translate(); List? dtos = Database.Fetch(sql); return dtos.Select(x => new AuditItem(x.NodeId, Enum.Parse(x.Header), x.UserId ?? Constants.Security.UnknownUserId, x.EntityType, x.Comment, x.Parameters, x.Datestamp)).ToList(); } public void CleanLogs(int maximumAgeOfLogsInMinutes) { DateTime oldestPermittedLogEntry = DateTime.Now.Subtract(new TimeSpan(0, maximumAgeOfLogsInMinutes, 0)); Database.Execute( "delete from umbracoLog where datestamp < @oldestPermittedLogEntry and logHeader in ('open','system')", new { oldestPermittedLogEntry }); } /// /// Return the audit items as paged result /// /// /// The query coming from the service /// /// /// /// /// /// /// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query /// or the custom filter /// so we need to do that here /// /// /// A user supplied custom filter /// /// public IEnumerable GetPagedResultsByQuery( IQuery query, long pageIndex, int pageSize, out long totalRecords, Direction orderDirection, AuditType[]? auditTypeFilter, IQuery? customFilter) { if (auditTypeFilter == null) { auditTypeFilter = Array.Empty(); } Sql sql = GetBaseQuery(false); var translator = new SqlTranslator(sql, query); sql = translator.Translate(); if (customFilter != null) { foreach (Tuple filterClause in customFilter.GetWhereClauses()) { sql.Where(filterClause.Item1, filterClause.Item2); } } if (auditTypeFilter.Length > 0) { foreach (AuditType type in auditTypeFilter) { sql.Where("(logHeader=@0)", type.ToString()); } } sql = orderDirection == Direction.Ascending ? sql.OrderBy("Datestamp") : sql.OrderByDescending("Datestamp"); // get page Page? page = Database.Page(pageIndex + 1, pageSize, sql); totalRecords = page.TotalItems; var items = page.Items.Select( dto => new AuditItem(dto.NodeId, Enum.ParseOrNull(dto.Header) ?? AuditType.Custom, dto.UserId ?? Constants.Security.UnknownUserId, dto.EntityType, dto.Comment, dto.Parameters, dto.Datestamp)).ToList(); // map the DateStamp for (var i = 0; i < items.Count; i++) { items[i].CreateDate = page.Items[i].Datestamp; } return items; } protected override void PersistNewItem(IAuditItem entity) => Database.Insert(new LogDto { Comment = entity.Comment, Datestamp = DateTime.Now, Header = entity.AuditType.ToString(), NodeId = entity.Id, UserId = entity.UserId, EntityType = entity.EntityType, Parameters = entity.Parameters, }); protected override void PersistUpdatedItem(IAuditItem entity) => // inserting when updating because we never update a log entry, perhaps this should throw? Database.Insert(new LogDto { Comment = entity.Comment, Datestamp = DateTime.Now, Header = entity.AuditType.ToString(), NodeId = entity.Id, UserId = entity.UserId, EntityType = entity.EntityType, Parameters = entity.Parameters, }); protected override IAuditItem? PerformGet(int id) { Sql sql = GetBaseQuery(false); sql.Where(GetBaseWhereClause(), new { id = id }); LogDto? dto = Database.First(sql); return dto == null ? null : new AuditItem(dto.NodeId, Enum.Parse(dto.Header), dto.UserId ?? Constants.Security.UnknownUserId, dto.EntityType, dto.Comment, dto.Parameters, dto.Datestamp); } protected override IEnumerable PerformGetAll(params int[]? ids) => throw new NotImplementedException(); protected override IEnumerable PerformGetByQuery(IQuery query) { Sql sqlClause = GetBaseQuery(false); var translator = new SqlTranslator(sqlClause, query); Sql sql = translator.Translate(); List? dtos = Database.Fetch(sql); return dtos.Select(x => new AuditItem(x.NodeId, Enum.Parse(x.Header), x.UserId ?? Constants.Security.UnknownUserId, x.EntityType, x.Comment, x.Parameters, x.Datestamp)).ToList(); } protected override Sql GetBaseQuery(bool isCount) { Sql sql = SqlContext.Sql(); sql = isCount ? sql.SelectCount() : sql.Select(); sql .From(); if (!isCount) { sql.LeftJoin().On((left, right) => left.UserId == right.Id); } return sql; } protected override string GetBaseWhereClause() => "umbracoLog.id = @id"; protected override IEnumerable GetDeleteClauses() => throw new NotImplementedException(); }