Use audit service instead of repository directly in services (#19357)

* Introduce new AuditEntryService

- Moved logic related to the IAuditEntryRepository from the AuditService to the new service
- Introduced new Async methods
  - Using ids (for easier transition from the previous Write method)
  - Using keys
- Moved and updated integration tests related to the audit entries to a new test class `AuditEntryServiceTests`
- Added unit tests class `AuditEntryServiceTests` and added a few unit tests
- Added migration to add columns for `performingUserKey` and `affectedUserKey` and convert existing user ids
- Adjusted usages of the old AuditService.Write method to use the new one (mostly notification handlers)

* Audit service rework

- Added new async and paged methods
- Marked (now) redundant methods as obsolete
- Updated all of the usages to use the non-obsolete methods
- Added unit tests class `AuditServiceTests` and some unit tests
- Updated existing integration test

* Use the audit service instead of the repository directly in services

* Apply suggestions from code review

* Small improvement

* Update src/Umbraco.Core/Services/AuditService.cs

* Some minor adjustments following the merge

* Delete unnecessary file

* Small cleanup on the tests

* Remove changing user id to 0 (on audit) if user id is admin in media bulk save

* Remove reference to unused IUserIdKeyResolver in TemplateService

* Remove references to unused IShortStringHelper and GlobalSettings in FileService
This commit is contained in:
Laura Neto
2025-07-24 14:52:17 +02:00
committed by GitHub
parent a50ad893a8
commit c1ac80653b
26 changed files with 942 additions and 213 deletions

View File

@@ -16,9 +16,9 @@ namespace Umbraco.Cms.Core.Services.Implement;
/// </summary>
public sealed class AuditService : RepositoryService, IAuditService
{
private readonly IUserIdKeyResolver _userIdKeyResolver;
private readonly IAuditRepository _auditRepository;
private readonly IEntityService _entityService;
private readonly IUserService _userService;
/// <summary>
/// Initializes a new instance of the <see cref="AuditService" /> class.
@@ -28,37 +28,15 @@ public sealed class AuditService : RepositoryService, IAuditService
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IAuditRepository auditRepository,
IUserService userService,
IUserIdKeyResolver userIdKeyResolver,
IEntityService entityService)
: base(provider, loggerFactory, eventMessagesFactory)
{
_auditRepository = auditRepository;
_userService = userService;
_userIdKeyResolver = userIdKeyResolver;
_entityService = entityService;
}
/// <summary>
/// Initializes a new instance of the <see cref="AuditService" /> class.
/// </summary>
[Obsolete("Use the non-obsolete constructor. Scheduled for removal in Umbraco 19.")]
public AuditService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IAuditRepository auditRepository,
IAuditEntryRepository auditEntryRepository,
IUserService userService,
IEntityService entityService)
: this(
provider,
loggerFactory,
eventMessagesFactory,
auditRepository,
userService,
entityService)
{
}
/// <inheritdoc />
public async Task<Attempt<AuditLogOperationStatus>> AddAsync(
AuditType type,
@@ -68,7 +46,9 @@ public sealed class AuditService : RepositoryService, IAuditService
string? comment = null,
string? parameters = null)
{
var userId = (await _userService.GetAsync(userKey))?.Id;
int? userId = await _userIdKeyResolver.TryGetAsync(userKey) is { Success: true } userIdAttempt
? userIdAttempt.Result
: null;
if (userId is null)
{
return Attempt.Fail(AuditLogOperationStatus.UserNotFound);
@@ -245,8 +225,7 @@ public sealed class AuditService : RepositoryService, IAuditService
ArgumentOutOfRangeException.ThrowIfNegative(skip);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(take);
IUser? user = await _userService.GetAsync(userKey);
if (user is null)
if (await _userIdKeyResolver.TryGetAsync(userKey) is not { Success: true } userIdAttempt)
{
return new PagedModel<IAuditItem>();
}
@@ -256,7 +235,7 @@ public sealed class AuditService : RepositoryService, IAuditService
sinceDate.HasValue ? Query<IAuditItem>().Where(x => x.CreateDate >= sinceDate) : null;
PagedModel<IAuditItem> result = GetItemsByUserInner(
user.Id,
userIdAttempt.Result,
pageIndex,
pageSize,
orderDirection,
@@ -418,7 +397,7 @@ public sealed class AuditService : RepositoryService, IAuditService
AuditType[]? auditTypeFilter = null,
IQuery<IAuditItem>? customFilter = null)
{
if (userId is < Constants.Security.SuperUserId)
if (userId < Constants.Security.SuperUserId)
{
return new PagedModel<IAuditItem> { Items = [], Total = 0 };
}
@@ -434,3 +413,4 @@ public sealed class AuditService : RepositoryService, IAuditService
scope.Complete();
}
}

View File

@@ -13,10 +13,10 @@ internal sealed class ContentBlueprintContainerService : EntityTypeContainerServ
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IDocumentBlueprintContainerRepository entityContainerRepository,
IAuditRepository auditRepository,
IAuditService auditService,
IEntityRepository entityRepository,
IUserIdKeyResolver userIdKeyResolver)
: base(provider, loggerFactory, eventMessagesFactory, entityContainerRepository, auditRepository, entityRepository, userIdKeyResolver)
: base(provider, loggerFactory, eventMessagesFactory, entityContainerRepository, auditService, entityRepository, userIdKeyResolver)
{
}

View File

@@ -27,7 +27,7 @@ namespace Umbraco.Cms.Core.Services;
/// </summary>
public class ContentService : RepositoryService, IContentService
{
private readonly IAuditRepository _auditRepository;
private readonly IAuditService _auditService;
private readonly IContentTypeRepository _contentTypeRepository;
private readonly IDocumentBlueprintRepository _documentBlueprintRepository;
private readonly IDocumentRepository _documentRepository;
@@ -52,7 +52,7 @@ public class ContentService : RepositoryService, IContentService
IEventMessagesFactory eventMessagesFactory,
IDocumentRepository documentRepository,
IEntityRepository entityRepository,
IAuditRepository auditRepository,
IAuditService auditService,
IContentTypeRepository contentTypeRepository,
IDocumentBlueprintRepository documentBlueprintRepository,
ILanguageRepository languageRepository,
@@ -68,7 +68,7 @@ public class ContentService : RepositoryService, IContentService
{
_documentRepository = documentRepository;
_entityRepository = entityRepository;
_auditRepository = auditRepository;
_auditService = auditService;
_contentTypeRepository = contentTypeRepository;
_documentBlueprintRepository = documentBlueprintRepository;
_languageRepository = languageRepository;
@@ -87,8 +87,7 @@ public class ContentService : RepositoryService, IContentService
_logger = loggerFactory.CreateLogger<ContentService>();
}
[Obsolete("Use non-obsolete constructor. Scheduled for removal in V17.")]
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public ContentService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
@@ -104,14 +103,16 @@ public class ContentService : RepositoryService, IContentService
ICultureImpactFactory cultureImpactFactory,
IUserIdKeyResolver userIdKeyResolver,
PropertyEditorCollection propertyEditorCollection,
IIdKeyMap idKeyMap)
IIdKeyMap idKeyMap,
IOptionsMonitor<ContentSettings> optionsMonitor,
IRelationService relationService)
: this(
provider,
loggerFactory,
eventMessagesFactory,
documentRepository,
entityRepository,
auditRepository,
StaticServiceProvider.Instance.GetRequiredService<IAuditService>(),
contentTypeRepository,
documentBlueprintRepository,
languageRepository,
@@ -121,11 +122,12 @@ public class ContentService : RepositoryService, IContentService
userIdKeyResolver,
propertyEditorCollection,
idKeyMap,
StaticServiceProvider.Instance.GetRequiredService<IOptionsMonitor<ContentSettings>>(),
StaticServiceProvider.Instance.GetRequiredService<IRelationService>())
optionsMonitor,
relationService)
{
}
[Obsolete("Use non-obsolete constructor. Scheduled for removal in V17.")]
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public ContentService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
@@ -133,6 +135,7 @@ public class ContentService : RepositoryService, IContentService
IDocumentRepository documentRepository,
IEntityRepository entityRepository,
IAuditRepository auditRepository,
IAuditService auditService,
IContentTypeRepository contentTypeRepository,
IDocumentBlueprintRepository documentBlueprintRepository,
ILanguageRepository languageRepository,
@@ -140,14 +143,17 @@ public class ContentService : RepositoryService, IContentService
IShortStringHelper shortStringHelper,
ICultureImpactFactory cultureImpactFactory,
IUserIdKeyResolver userIdKeyResolver,
PropertyEditorCollection propertyEditorCollection)
PropertyEditorCollection propertyEditorCollection,
IIdKeyMap idKeyMap,
IOptionsMonitor<ContentSettings> optionsMonitor,
IRelationService relationService)
: this(
provider,
loggerFactory,
eventMessagesFactory,
documentRepository,
entityRepository,
auditRepository,
auditService,
contentTypeRepository,
documentBlueprintRepository,
languageRepository,
@@ -156,7 +162,9 @@ public class ContentService : RepositoryService, IContentService
cultureImpactFactory,
userIdKeyResolver,
propertyEditorCollection,
StaticServiceProvider.Instance.GetRequiredService<IIdKeyMap>())
idKeyMap,
optionsMonitor,
relationService)
{
}
@@ -3084,7 +3092,20 @@ public class ContentService : RepositoryService, IContentService
#region Private Methods
private void Audit(AuditType type, int userId, int objectId, string? message = null, string? parameters = null) =>
_auditRepository.Save(new AuditItem(objectId, type, userId, UmbracoObjectTypes.Document.GetName(), message, parameters));
AuditAsync(type, userId, objectId, message, parameters).GetAwaiter().GetResult();
private async Task AuditAsync(AuditType type, int userId, int objectId, string? message = null, string? parameters = null)
{
Guid userKey = await _userIdKeyResolver.GetAsync(userId);
await _auditService.AddAsync(
type,
userKey,
objectId,
UmbracoObjectTypes.Document.GetName(),
message,
parameters);
}
private string GetLanguageDetailsForAuditEntry(IEnumerable<string> affectedCultures)
=> GetLanguageDetailsForAuditEntry(_languageRepository.GetMany(), affectedCultures);

View File

@@ -14,10 +14,10 @@ internal sealed class ContentTypeContainerService : EntityTypeContainerService<I
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IDocumentTypeContainerRepository entityContainerRepository,
IAuditRepository auditRepository,
IAuditService auditService,
IEntityRepository entityRepository,
IUserIdKeyResolver userIdKeyResolver)
: base(provider, loggerFactory, eventMessagesFactory, entityContainerRepository, auditRepository, entityRepository, userIdKeyResolver)
: base(provider, loggerFactory, eventMessagesFactory, entityContainerRepository, auditService, entityRepository, userIdKeyResolver)
{
}

View File

@@ -1,4 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
@@ -22,7 +24,7 @@ public class ContentTypeService : ContentTypeServiceBase<IContentTypeRepository,
IEventMessagesFactory eventMessagesFactory,
IContentService contentService,
IContentTypeRepository repository,
IAuditRepository auditRepository,
IAuditService auditService,
IDocumentTypeContainerRepository entityContainerRepository,
IEntityRepository entityRepository,
IEventAggregator eventAggregator,
@@ -33,7 +35,7 @@ public class ContentTypeService : ContentTypeServiceBase<IContentTypeRepository,
loggerFactory,
eventMessagesFactory,
repository,
auditRepository,
auditService,
entityContainerRepository,
entityRepository,
eventAggregator,
@@ -41,6 +43,63 @@ public class ContentTypeService : ContentTypeServiceBase<IContentTypeRepository,
contentTypeFilters) =>
ContentService = contentService;
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public ContentTypeService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IContentService contentService,
IContentTypeRepository repository,
IAuditRepository auditRepository,
IDocumentTypeContainerRepository entityContainerRepository,
IEntityRepository entityRepository,
IEventAggregator eventAggregator,
IUserIdKeyResolver userIdKeyResolver,
ContentTypeFilterCollection contentTypeFilters)
: this(
provider,
loggerFactory,
eventMessagesFactory,
contentService,
repository,
StaticServiceProvider.Instance.GetRequiredService<IAuditService>(),
entityContainerRepository,
entityRepository,
eventAggregator,
userIdKeyResolver,
contentTypeFilters)
{
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public ContentTypeService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IContentService contentService,
IContentTypeRepository repository,
IAuditRepository auditRepository,
IAuditService auditService,
IDocumentTypeContainerRepository entityContainerRepository,
IEntityRepository entityRepository,
IEventAggregator eventAggregator,
IUserIdKeyResolver userIdKeyResolver,
ContentTypeFilterCollection contentTypeFilters)
: this(
provider,
loggerFactory,
eventMessagesFactory,
contentService,
repository,
auditService,
entityContainerRepository,
entityRepository,
eventAggregator,
userIdKeyResolver,
contentTypeFilters)
{
}
protected override int[] ReadLockIds => ContentTypeLocks.ReadLockIds;
protected override int[] WriteLockIds => ContentTypeLocks.WriteLockIds;

View File

@@ -1,6 +1,8 @@
using System.Globalization;
using System.Runtime.InteropServices;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Exceptions;
using Umbraco.Cms.Core.Models;
@@ -20,7 +22,7 @@ public abstract class ContentTypeServiceBase<TRepository, TItem> : ContentTypeSe
where TRepository : IContentTypeRepositoryBase<TItem>
where TItem : class, IContentTypeComposition
{
private readonly IAuditRepository _auditRepository;
private readonly IAuditService _auditService;
private readonly IEntityContainerRepository _containerRepository;
private readonly IEntityRepository _entityRepository;
private readonly IEventAggregator _eventAggregator;
@@ -32,7 +34,7 @@ public abstract class ContentTypeServiceBase<TRepository, TItem> : ContentTypeSe
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
TRepository repository,
IAuditRepository auditRepository,
IAuditService auditService,
IEntityContainerRepository containerRepository,
IEntityRepository entityRepository,
IEventAggregator eventAggregator,
@@ -41,7 +43,7 @@ public abstract class ContentTypeServiceBase<TRepository, TItem> : ContentTypeSe
: base(provider, loggerFactory, eventMessagesFactory)
{
Repository = repository;
_auditRepository = auditRepository;
_auditService = auditService;
_containerRepository = containerRepository;
_entityRepository = entityRepository;
_eventAggregator = eventAggregator;
@@ -49,6 +51,32 @@ public abstract class ContentTypeServiceBase<TRepository, TItem> : ContentTypeSe
_contentTypeFilters = contentTypeFilters;
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
protected ContentTypeServiceBase(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
TRepository repository,
IAuditRepository auditRepository,
IEntityContainerRepository containerRepository,
IEntityRepository entityRepository,
IEventAggregator eventAggregator,
IUserIdKeyResolver userIdKeyResolver,
ContentTypeFilterCollection contentTypeFilters)
: this(
provider,
loggerFactory,
eventMessagesFactory,
repository,
StaticServiceProvider.Instance.GetRequiredService<IAuditService>(),
containerRepository,
entityRepository,
eventAggregator,
userIdKeyResolver,
contentTypeFilters)
{
}
protected TRepository Repository { get; }
protected abstract int[] WriteLockIds { get; }
protected abstract int[] ReadLockIds { get; }
@@ -1398,9 +1426,18 @@ public abstract class ContentTypeServiceBase<TRepository, TItem> : ContentTypeSe
#region Audit
private void Audit(AuditType type, int userId, int objectId)
private void Audit(AuditType type, int userId, int objectId) =>
AuditAsync(type, userId, objectId).GetAwaiter().GetResult();
private async Task AuditAsync(AuditType type, int userId, int objectId)
{
_auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetUmbracoObjectType(ContainedObjectType).GetName()));
Guid userKey = await _userIdKeyResolver.GetAsync(userId);
await _auditService.AddAsync(
type,
userKey,
objectId,
ObjectTypes.GetUmbracoObjectType(ContainedObjectType).GetName());
}
#endregion

View File

@@ -15,7 +15,7 @@ namespace Umbraco.Cms.Core.Services;
internal sealed class ContentVersionService : IContentVersionService
{
private readonly IAuditRepository _auditRepository;
private readonly IAuditService _auditService;
private readonly IContentVersionCleanupPolicy _contentVersionCleanupPolicy;
private readonly IDocumentVersionRepository _documentVersionRepository;
private readonly IEventMessagesFactory _eventMessagesFactory;
@@ -32,7 +32,7 @@ internal sealed class ContentVersionService : IContentVersionService
IContentVersionCleanupPolicy contentVersionCleanupPolicy,
ICoreScopeProvider scopeProvider,
IEventMessagesFactory eventMessagesFactory,
IAuditRepository auditRepository,
IAuditService auditService,
ILanguageRepository languageRepository,
IEntityService entityService,
IContentService contentService,
@@ -43,7 +43,7 @@ internal sealed class ContentVersionService : IContentVersionService
_contentVersionCleanupPolicy = contentVersionCleanupPolicy;
_scopeProvider = scopeProvider;
_eventMessagesFactory = eventMessagesFactory;
_auditRepository = auditRepository;
_auditService = auditService;
_languageRepository = languageRepository;
_entityService = entityService;
_contentService = contentService;
@@ -299,16 +299,19 @@ internal sealed class ContentVersionService : IContentVersionService
return versionsToDelete;
}
private void Audit(AuditType type, int userId, int objectId, string? message = null, string? parameters = null)
private void Audit(AuditType type, int userId, int objectId, string? message = null, string? parameters = null) =>
AuditAsync(type, userId, objectId, message, parameters).GetAwaiter().GetResult();
private async Task AuditAsync(AuditType type, int userId, int objectId, string? message = null, string? parameters = null)
{
var entry = new AuditItem(
objectId,
Guid userKey = await _userIdKeyResolver.GetAsync(userId);
await _auditService.AddAsync(
type,
userId,
userKey,
objectId,
UmbracoObjectTypes.Document.GetName(),
message,
parameters);
_auditRepository.Save(entry);
}
}

View File

@@ -13,10 +13,10 @@ internal sealed class DataTypeContainerService : EntityTypeContainerService<IDat
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IDataTypeContainerRepository entityContainerRepository,
IAuditRepository auditRepository,
IAuditService auditService,
IEntityRepository entityRepository,
IUserIdKeyResolver userIdKeyResolver)
: base(provider, loggerFactory, eventMessagesFactory, entityContainerRepository, auditRepository, entityRepository, userIdKeyResolver)
: base(provider, loggerFactory, eventMessagesFactory, entityContainerRepository, auditService, entityRepository, userIdKeyResolver)
{
}

View File

@@ -27,38 +27,42 @@ namespace Umbraco.Cms.Core.Services.Implement
private readonly IContentTypeRepository _contentTypeRepository;
private readonly IMediaTypeRepository _mediaTypeRepository;
private readonly IMemberTypeRepository _memberTypeRepository;
private readonly IAuditRepository _auditRepository;
private readonly IAuditService _auditService;
private readonly IIOHelper _ioHelper;
private readonly IDataTypeContainerService _dataTypeContainerService;
private readonly IUserIdKeyResolver _userIdKeyResolver;
private readonly Lazy<IIdKeyMap> _idKeyMap;
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")]
public DataTypeService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IDataTypeRepository dataTypeRepository,
IDataValueEditorFactory dataValueEditorFactory,
IAuditRepository auditRepository,
IAuditService auditService,
IContentTypeRepository contentTypeRepository,
IMediaTypeRepository mediaTypeRepository,
IMemberTypeRepository memberTypeRepository,
IIOHelper ioHelper,
Lazy<IIdKeyMap> idKeyMap)
: this(
provider,
loggerFactory,
eventMessagesFactory,
dataTypeRepository,
dataValueEditorFactory,
auditRepository,
contentTypeRepository,
StaticServiceProvider.Instance.GetRequiredService<IMediaTypeRepository>(),
StaticServiceProvider.Instance.GetRequiredService<IMemberTypeRepository>(),
ioHelper,
idKeyMap)
: base(provider, loggerFactory, eventMessagesFactory)
{
_dataValueEditorFactory = dataValueEditorFactory;
_dataTypeRepository = dataTypeRepository;
_auditService = auditService;
_contentTypeRepository = contentTypeRepository;
_mediaTypeRepository = mediaTypeRepository;
_memberTypeRepository = memberTypeRepository;
_ioHelper = ioHelper;
_idKeyMap = idKeyMap;
// resolve dependencies for obsolete methods through the static service provider, so they don't pollute the constructor signature
_dataTypeContainerService = StaticServiceProvider.Instance.GetRequiredService<IDataTypeContainerService>();
_dataTypeContainerRepository = StaticServiceProvider.Instance.GetRequiredService<IDataTypeContainerRepository>();
_userIdKeyResolver = StaticServiceProvider.Instance.GetRequiredService<IUserIdKeyResolver>();
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public DataTypeService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
@@ -71,21 +75,48 @@ namespace Umbraco.Cms.Core.Services.Implement
IMemberTypeRepository memberTypeRepository,
IIOHelper ioHelper,
Lazy<IIdKeyMap> idKeyMap)
: base(provider, loggerFactory, eventMessagesFactory)
: this(
provider,
loggerFactory,
eventMessagesFactory,
dataTypeRepository,
dataValueEditorFactory,
StaticServiceProvider.Instance.GetRequiredService<IAuditService>(),
contentTypeRepository,
mediaTypeRepository,
memberTypeRepository,
ioHelper,
idKeyMap)
{
_dataValueEditorFactory = dataValueEditorFactory;
_dataTypeRepository = dataTypeRepository;
_auditRepository = auditRepository;
_contentTypeRepository = contentTypeRepository;
_mediaTypeRepository = mediaTypeRepository;
_memberTypeRepository = memberTypeRepository;
_ioHelper = ioHelper;
_idKeyMap = idKeyMap;
}
// resolve dependencies for obsolete methods through the static service provider, so they don't pollute the constructor signature
_dataTypeContainerService = StaticServiceProvider.Instance.GetRequiredService<IDataTypeContainerService>();
_dataTypeContainerRepository = StaticServiceProvider.Instance.GetRequiredService<IDataTypeContainerRepository>();
_userIdKeyResolver = StaticServiceProvider.Instance.GetRequiredService<IUserIdKeyResolver>();
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public DataTypeService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IDataTypeRepository dataTypeRepository,
IDataValueEditorFactory dataValueEditorFactory,
IAuditRepository auditRepository,
IAuditService auditService,
IContentTypeRepository contentTypeRepository,
IMediaTypeRepository mediaTypeRepository,
IMemberTypeRepository memberTypeRepository,
IIOHelper ioHelper,
Lazy<IIdKeyMap> idKeyMap)
: this(
provider,
loggerFactory,
eventMessagesFactory,
dataTypeRepository,
dataValueEditorFactory,
auditService,
contentTypeRepository,
mediaTypeRepository,
memberTypeRepository,
ioHelper,
idKeyMap)
{
}
#region Containers
@@ -474,8 +505,7 @@ namespace Umbraco.Cms.Core.Services.Implement
scope.Notifications.Publish(new DataTypeMovedNotification(moveEventInfo, eventMessages).WithStateFrom(movingDataTypeNotification));
var currentUserId = await _userIdKeyResolver.GetAsync(userKey);
Audit(AuditType.Move, currentUserId, toMove.Id);
await AuditAsync(AuditType.Move, userKey, toMove.Id);
scope.Complete();
}
@@ -597,7 +627,7 @@ namespace Umbraco.Cms.Core.Services.Implement
: DataTypeOperationStatus.Success;
},
userKey,
AuditType.New);
AuditType.Save);
/// <summary>
/// Saves a collection of <see cref="IDataType"/>
@@ -705,8 +735,7 @@ namespace Umbraco.Cms.Core.Services.Implement
scope.Notifications.Publish(new DataTypeDeletedNotification(dataType, eventMessages).WithStateFrom(deletingDataTypeNotification));
var currentUserId = await _userIdKeyResolver.GetAsync(userKey);
Audit(AuditType.Delete, currentUserId, dataType.Id);
await AuditAsync(AuditType.Delete, userKey, dataType.Id);
scope.Complete();
@@ -902,7 +931,7 @@ namespace Umbraco.Cms.Core.Services.Implement
scope.Notifications.Publish(new DataTypeSavedNotification(dataType, eventMessages).WithStateFrom(savingDataTypeNotification));
Audit(auditType, currentUserId, dataType.Id);
await AuditAsync(auditType, userKey, dataType.Id);
scope.Complete();
return Attempt.SucceedWithStatus(DataTypeOperationStatus.Success, dataType);
@@ -915,7 +944,20 @@ namespace Umbraco.Cms.Core.Services.Implement
{ Result: var intId } => _dataTypeRepository.Get(intId),
};
private void Audit(AuditType type, int userId, int objectId)
=> _auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.DataType)));
private void Audit(AuditType type, int userId, int objectId) =>
AuditAsync(type, userId, objectId).GetAwaiter().GetResult();
private async Task AuditAsync(AuditType type, int userId, int objectId)
{
Guid userKey = await _userIdKeyResolver.GetAsync(userId);
await AuditAsync(type, userKey, objectId);
}
private async Task AuditAsync(AuditType type, Guid userKey, int objectId) =>
await _auditService.AddAsync(
type,
userKey,
objectId,
UmbracoObjectTypes.DataType.GetName());
}
}

View File

@@ -12,7 +12,7 @@ namespace Umbraco.Cms.Core.Services;
internal sealed class DictionaryItemService : RepositoryService, IDictionaryItemService
{
private readonly IDictionaryRepository _dictionaryRepository;
private readonly IAuditRepository _auditRepository;
private readonly IAuditService _auditService;
private readonly ILanguageService _languageService;
private readonly IUserIdKeyResolver _userIdKeyResolver;
@@ -21,13 +21,13 @@ internal sealed class DictionaryItemService : RepositoryService, IDictionaryItem
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IDictionaryRepository dictionaryRepository,
IAuditRepository auditRepository,
IAuditService auditService,
ILanguageService languageService,
IUserIdKeyResolver userIdKeyResolver)
: base(provider, loggerFactory, eventMessagesFactory)
{
_dictionaryRepository = dictionaryRepository;
_auditRepository = auditRepository;
_auditService = auditService;
_languageService = languageService;
_userIdKeyResolver = userIdKeyResolver;
}
@@ -199,8 +199,7 @@ internal sealed class DictionaryItemService : RepositoryService, IDictionaryItem
new DictionaryItemDeletedNotification(dictionaryItem, eventMessages)
.WithStateFrom(deletingNotification));
var currentUserId = await _userIdKeyResolver.GetAsync(userKey);
Audit(AuditType.Delete, "Delete DictionaryItem", currentUserId, dictionaryItem.Id, nameof(DictionaryItem));
await AuditAsync(AuditType.Delete, "Delete DictionaryItem", userKey, dictionaryItem.Id, nameof(DictionaryItem));
scope.Complete();
return Attempt.SucceedWithStatus<IDictionaryItem?, DictionaryItemOperationStatus>(DictionaryItemOperationStatus.Success, dictionaryItem);
@@ -261,8 +260,7 @@ internal sealed class DictionaryItemService : RepositoryService, IDictionaryItem
scope.Notifications.Publish(
new DictionaryItemMovedNotification(moveEventInfo, eventMessages).WithStateFrom(movingNotification));
var currentUserId = await _userIdKeyResolver.GetAsync(userKey);
Audit(AuditType.Move, "Move DictionaryItem", currentUserId, dictionaryItem.Id, nameof(DictionaryItem));
await AuditAsync(AuditType.Move, "Move DictionaryItem", userKey, dictionaryItem.Id, nameof(DictionaryItem));
scope.Complete();
return Attempt.SucceedWithStatus(DictionaryItemOperationStatus.Success, dictionaryItem);
@@ -322,8 +320,7 @@ internal sealed class DictionaryItemService : RepositoryService, IDictionaryItem
scope.Notifications.Publish(
new DictionaryItemSavedNotification(dictionaryItem, eventMessages).WithStateFrom(savingNotification));
var currentUserId = await _userIdKeyResolver.GetAsync(userKey);
Audit(auditType, auditMessage, currentUserId, dictionaryItem.Id, nameof(DictionaryItem));
await AuditAsync(auditType, auditMessage, userKey, dictionaryItem.Id, nameof(DictionaryItem));
scope.Complete();
return Attempt.SucceedWithStatus(DictionaryItemOperationStatus.Success, dictionaryItem);
@@ -339,8 +336,13 @@ internal sealed class DictionaryItemService : RepositoryService, IDictionaryItem
}
}
private void Audit(AuditType type, string message, int userId, int objectId, string? entityType) =>
_auditRepository.Save(new AuditItem(objectId, type, userId, entityType, message));
private async Task AuditAsync(AuditType type, string message, Guid userKey, int objectId, string? entityType) =>
await _auditService.AddAsync(
type,
userKey,
objectId,
entityType,
message);
private bool HasValidParent(IDictionaryItem dictionaryItem)
=> dictionaryItem.ParentId.HasValue == false || _dictionaryRepository.Get(dictionaryItem.ParentId.Value) != null;

View File

@@ -14,7 +14,7 @@ internal abstract class EntityTypeContainerService<TTreeEntity, TEntityContainer
where TEntityContainerRepository : IEntityContainerRepository
{
private readonly TEntityContainerRepository _entityContainerRepository;
private readonly IAuditRepository _auditRepository;
private readonly IAuditService _auditService;
private readonly IEntityRepository _entityRepository;
private readonly IUserIdKeyResolver _userIdKeyResolver;
@@ -31,13 +31,13 @@ internal abstract class EntityTypeContainerService<TTreeEntity, TEntityContainer
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
TEntityContainerRepository entityContainerRepository,
IAuditRepository auditRepository,
IAuditService auditService,
IEntityRepository entityRepository,
IUserIdKeyResolver userIdKeyResolver)
: base(provider, loggerFactory, eventMessagesFactory)
{
_entityContainerRepository = entityContainerRepository;
_auditRepository = auditRepository;
_auditService = auditService;
_entityRepository = entityRepository;
_userIdKeyResolver = userIdKeyResolver;
}
@@ -183,8 +183,7 @@ internal abstract class EntityTypeContainerService<TTreeEntity, TEntityContainer
_entityContainerRepository.Delete(container);
var currentUserId = await _userIdKeyResolver.GetAsync(userKey);
Audit(AuditType.Delete, currentUserId, container.Id);
await AuditAsync(AuditType.Delete, userKey, container.Id);
scope.Complete();
scope.Notifications.Publish(new EntityContainerDeletedNotification(container, eventMessages).WithStateFrom(deletingEntityContainerNotification));
@@ -218,8 +217,7 @@ internal abstract class EntityTypeContainerService<TTreeEntity, TEntityContainer
_entityContainerRepository.Save(container);
var currentUserId = await _userIdKeyResolver.GetAsync(userKey);
Audit(auditType, currentUserId, container.Id);
await AuditAsync(auditType, userKey, container.Id);
scope.Complete();
scope.Notifications.Publish(new EntityContainerSavedNotification(container, eventMessages).WithStateFrom(savingEntityContainerNotification));
@@ -239,8 +237,12 @@ internal abstract class EntityTypeContainerService<TTreeEntity, TEntityContainer
return _entityContainerRepository.Get(treeEntity.ParentId);
}
private void Audit(AuditType type, int userId, int objectId)
=> _auditRepository.Save(new AuditItem(objectId, type, userId, ContainerObjectType.GetName()));
private async Task AuditAsync(AuditType type, Guid userKey, int objectId) =>
await _auditService.AddAsync(
type,
userKey,
objectId,
ContainerObjectType.GetName());
private void ReadLock(ICoreScope scope)
{

View File

@@ -1,7 +1,9 @@
using System.Text.RegularExpressions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Models;
@@ -22,7 +24,7 @@ namespace Umbraco.Cms.Core.Services;
public class FileService : RepositoryService, IFileService
{
private const string PartialViewHeader = "@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage";
private readonly IAuditRepository _auditRepository;
private readonly IAuditService _auditService;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IPartialViewRepository _partialViewRepository;
private readonly IScriptRepository _scriptRepository;
@@ -31,6 +33,31 @@ public class FileService : RepositoryService, IFileService
private readonly ITemplateRepository _templateRepository;
private readonly IUserIdKeyResolver _userIdKeyResolver;
public FileService(
ICoreScopeProvider uowProvider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IStylesheetRepository stylesheetRepository,
IScriptRepository scriptRepository,
IPartialViewRepository partialViewRepository,
IAuditService auditService,
IHostingEnvironment hostingEnvironment,
ITemplateService templateService,
ITemplateRepository templateRepository,
IUserIdKeyResolver userIdKeyResolver)
: base(uowProvider, loggerFactory, eventMessagesFactory)
{
_stylesheetRepository = stylesheetRepository;
_scriptRepository = scriptRepository;
_partialViewRepository = partialViewRepository;
_auditService = auditService;
_hostingEnvironment = hostingEnvironment;
_templateService = templateService;
_templateRepository = templateRepository;
_userIdKeyResolver = userIdKeyResolver;
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public FileService(
ICoreScopeProvider uowProvider,
ILoggerFactory loggerFactory,
@@ -45,16 +72,50 @@ public class FileService : RepositoryService, IFileService
IUserIdKeyResolver userIdKeyResolver,
IShortStringHelper shortStringHelper,
IOptions<GlobalSettings> globalSettings)
: base(uowProvider, loggerFactory, eventMessagesFactory)
: this(
uowProvider,
loggerFactory,
eventMessagesFactory,
stylesheetRepository,
scriptRepository,
partialViewRepository,
StaticServiceProvider.Instance.GetRequiredService<IAuditService>(),
hostingEnvironment,
templateService,
templateRepository,
userIdKeyResolver)
{
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public FileService(
ICoreScopeProvider uowProvider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IStylesheetRepository stylesheetRepository,
IScriptRepository scriptRepository,
IPartialViewRepository partialViewRepository,
IAuditService auditService,
IAuditRepository auditRepository,
IHostingEnvironment hostingEnvironment,
ITemplateService templateService,
ITemplateRepository templateRepository,
IUserIdKeyResolver userIdKeyResolver,
IShortStringHelper shortStringHelper,
IOptions<GlobalSettings> globalSettings)
: this(
uowProvider,
loggerFactory,
eventMessagesFactory,
stylesheetRepository,
scriptRepository,
partialViewRepository,
auditService,
hostingEnvironment,
templateService,
templateRepository,
userIdKeyResolver)
{
_stylesheetRepository = stylesheetRepository;
_scriptRepository = scriptRepository;
_partialViewRepository = partialViewRepository;
_auditRepository = auditRepository;
_hostingEnvironment = hostingEnvironment;
_templateService = templateService;
_templateRepository = templateRepository;
_userIdKeyResolver = userIdKeyResolver;
}
#region Stylesheets
@@ -70,7 +131,18 @@ public class FileService : RepositoryService, IFileService
}
private void Audit(AuditType type, int userId, int objectId, string? entityType) =>
_auditRepository.Save(new AuditItem(objectId, type, userId, entityType));
AuditAsync(type, userId, objectId, entityType).GetAwaiter().GetResult();
private async Task AuditAsync(AuditType type, int userId, int objectId, string? entityType)
{
Guid userKey = await _userIdKeyResolver.GetAsync(userId);
await _auditService.AddAsync(
type,
userKey,
objectId,
entityType);
}
/// <inheritdoc />
[Obsolete("Please use IStylesheetService for stylesheet operations - will be removed in Umbraco 15")]

View File

@@ -1,4 +1,6 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
@@ -16,14 +18,41 @@ public abstract class FileServiceOperationBase<TRepository, TEntity, TOperationS
{
private readonly ILogger<StylesheetService> _logger;
private readonly IUserIdKeyResolver _userIdKeyResolver;
private readonly IAuditRepository _auditRepository;
private readonly IAuditService _auditService;
protected FileServiceOperationBase(ICoreScopeProvider provider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, TRepository repository, ILogger<StylesheetService> logger, IUserIdKeyResolver userIdKeyResolver, IAuditRepository auditRepository)
protected FileServiceOperationBase(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
TRepository repository,
ILogger<StylesheetService> logger,
IUserIdKeyResolver userIdKeyResolver,
IAuditService auditService)
: base(provider, loggerFactory, eventMessagesFactory, repository)
{
_logger = logger;
_userIdKeyResolver = userIdKeyResolver;
_auditRepository = auditRepository;
_auditService = auditService;
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
protected FileServiceOperationBase(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
TRepository repository,
ILogger<StylesheetService> logger,
IUserIdKeyResolver userIdKeyResolver,
IAuditRepository auditRepository)
: this(
provider,
loggerFactory,
eventMessagesFactory,
repository,
logger,
userIdKeyResolver,
StaticServiceProvider.Instance.GetRequiredService<IAuditService>())
{
}
protected abstract TOperationStatus Success { get; }
@@ -249,10 +278,7 @@ public abstract class FileServiceOperationBase<TRepository, TEntity, TOperationS
}
private async Task AuditAsync(AuditType type, Guid userKey)
{
var userId = await _userIdKeyResolver.GetAsync(userKey);
_auditRepository.Save(new AuditItem(-1, type, userId, EntityType));
}
=> await _auditService.AddAsync(type, userKey, -1, EntityType);
private string GetFilePath(string? parentPath, string fileName)
=> Path.Join(parentPath, fileName);

View File

@@ -13,7 +13,7 @@ namespace Umbraco.Cms.Core.Services;
internal sealed class LanguageService : RepositoryService, ILanguageService
{
private readonly ILanguageRepository _languageRepository;
private readonly IAuditRepository _auditRepository;
private readonly IAuditService _auditService;
private readonly IUserIdKeyResolver _userIdKeyResolver;
private readonly IIsoCodeValidator _isoCodeValidator;
@@ -22,13 +22,13 @@ internal sealed class LanguageService : RepositoryService, ILanguageService
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
ILanguageRepository languageRepository,
IAuditRepository auditRepository,
IAuditService auditService,
IUserIdKeyResolver userIdKeyResolver,
IIsoCodeValidator isoCodeValidator)
: base(provider, loggerFactory, eventMessagesFactory)
{
_languageRepository = languageRepository;
_auditRepository = auditRepository;
_auditService = auditService;
_userIdKeyResolver = userIdKeyResolver;
_isoCodeValidator = isoCodeValidator;
}
@@ -160,8 +160,7 @@ internal sealed class LanguageService : RepositoryService, ILanguageService
scope.Notifications.Publish(
new LanguageDeletedNotification(language, eventMessages).WithStateFrom(deletingLanguageNotification));
var currentUserId = await _userIdKeyResolver.GetAsync(userKey);
Audit(AuditType.Delete, "Delete Language", currentUserId, language.Id, UmbracoObjectTypes.Language.GetName());
await AuditAsync(AuditType.Delete, "Delete Language", userKey, language.Id, UmbracoObjectTypes.Language.GetName());
scope.Complete();
return Attempt.SucceedWithStatus<ILanguage?, LanguageOperationStatus>(LanguageOperationStatus.Success, language);
}
@@ -213,16 +212,20 @@ internal sealed class LanguageService : RepositoryService, ILanguageService
scope.Notifications.Publish(
new LanguageSavedNotification(language, eventMessages).WithStateFrom(savingNotification));
var currentUserId = await _userIdKeyResolver.GetAsync(userKey);
Audit(auditType, auditMessage, currentUserId, language.Id, UmbracoObjectTypes.Language.GetName());
await AuditAsync(auditType, auditMessage, userKey, language.Id, UmbracoObjectTypes.Language.GetName());
scope.Complete();
return Attempt.SucceedWithStatus(LanguageOperationStatus.Success, language);
}
}
private void Audit(AuditType type, string message, int userId, int objectId, string? entityType) =>
_auditRepository.Save(new AuditItem(objectId, type, userId, entityType, message));
private async Task AuditAsync(AuditType type, string message, Guid userKey, int objectId, string? entityType) =>
await _auditService.AddAsync(
type,
userKey,
objectId,
entityType,
message);
private bool HasInvalidFallbackLanguage(ILanguage language)
{

View File

@@ -17,7 +17,6 @@ namespace Umbraco.Cms.Core.Services;
[Obsolete("Please use ILanguageService and IDictionaryItemService for localization. Will be removed in V15.")]
internal class LocalizationService : RepositoryService, ILocalizationService
{
private readonly IAuditRepository _auditRepository;
private readonly IDictionaryRepository _dictionaryRepository;
private readonly ILanguageRepository _languageRepository;
private readonly ILanguageService _languageService;
@@ -30,14 +29,12 @@ internal class LocalizationService : RepositoryService, ILocalizationService
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IDictionaryRepository dictionaryRepository,
IAuditRepository auditRepository,
ILanguageRepository languageRepository)
: this(
provider,
loggerFactory,
eventMessagesFactory,
dictionaryRepository,
auditRepository,
languageRepository,
StaticServiceProvider.Instance.GetRequiredService<ILanguageService>(),
StaticServiceProvider.Instance.GetRequiredService<IDictionaryItemService>(),
@@ -51,7 +48,6 @@ internal class LocalizationService : RepositoryService, ILocalizationService
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IDictionaryRepository dictionaryRepository,
IAuditRepository auditRepository,
ILanguageRepository languageRepository,
ILanguageService languageService,
IDictionaryItemService dictionaryItemService,
@@ -59,7 +55,6 @@ internal class LocalizationService : RepositoryService, ILocalizationService
: base(provider, loggerFactory, eventMessagesFactory)
{
_dictionaryRepository = dictionaryRepository;
_auditRepository = auditRepository;
_languageRepository = languageRepository;
_languageService = languageService;
_dictionaryItemService = dictionaryItemService;

View File

@@ -1,5 +1,7 @@
using System.Globalization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models;
@@ -22,7 +24,7 @@ namespace Umbraco.Cms.Core.Services
{
private readonly IMediaRepository _mediaRepository;
private readonly IMediaTypeRepository _mediaTypeRepository;
private readonly IAuditRepository _auditRepository;
private readonly IAuditService _auditService;
private readonly IEntityRepository _entityRepository;
private readonly IShortStringHelper _shortStringHelper;
private readonly IUserIdKeyResolver _userIdKeyResolver;
@@ -37,7 +39,7 @@ namespace Umbraco.Cms.Core.Services
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IMediaRepository mediaRepository,
IAuditRepository auditRepository,
IAuditService auditService,
IMediaTypeRepository mediaTypeRepository,
IEntityRepository entityRepository,
IShortStringHelper shortStringHelper,
@@ -46,13 +48,66 @@ namespace Umbraco.Cms.Core.Services
{
_mediaFileManager = mediaFileManager;
_mediaRepository = mediaRepository;
_auditRepository = auditRepository;
_auditService = auditService;
_mediaTypeRepository = mediaTypeRepository;
_entityRepository = entityRepository;
_shortStringHelper = shortStringHelper;
_userIdKeyResolver = userIdKeyResolver;
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public MediaService(
ICoreScopeProvider provider,
MediaFileManager mediaFileManager,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IMediaRepository mediaRepository,
IAuditRepository auditRepository,
IMediaTypeRepository mediaTypeRepository,
IEntityRepository entityRepository,
IShortStringHelper shortStringHelper,
IUserIdKeyResolver userIdKeyResolver)
: this(
provider,
mediaFileManager,
loggerFactory,
eventMessagesFactory,
mediaRepository,
StaticServiceProvider.Instance.GetRequiredService<IAuditService>(),
mediaTypeRepository,
entityRepository,
shortStringHelper,
userIdKeyResolver)
{
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public MediaService(
ICoreScopeProvider provider,
MediaFileManager mediaFileManager,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IMediaRepository mediaRepository,
IAuditService auditService,
IAuditRepository auditRepository,
IMediaTypeRepository mediaTypeRepository,
IEntityRepository entityRepository,
IShortStringHelper shortStringHelper,
IUserIdKeyResolver userIdKeyResolver)
: this(
provider,
mediaFileManager,
loggerFactory,
eventMessagesFactory,
mediaRepository,
auditService,
mediaTypeRepository,
entityRepository,
shortStringHelper,
userIdKeyResolver)
{
}
#endregion
#region Count
@@ -788,7 +843,7 @@ namespace Umbraco.Cms.Core.Services
scope.Notifications.Publish(new MediaSavedNotification(mediasA, messages).WithStateFrom(savingNotification));
// TODO: See note about suppressing events in content service
scope.Notifications.Publish(new MediaTreeChangeNotification(treeChanges, messages));
Audit(AuditType.Save, userId == -1 ? 0 : userId, Constants.System.Root, "Bulk save media");
Audit(AuditType.Save, userId, Constants.System.Root, "Bulk save media");
scope.Complete();
}
@@ -1235,9 +1290,20 @@ namespace Umbraco.Cms.Core.Services
#region Private Methods
private void Audit(AuditType type, int userId, int objectId, string? message = null)
private void Audit(AuditType type, int userId, int objectId, string? message = null) =>
AuditAsync(type, userId, objectId, message).GetAwaiter().GetResult();
private async Task AuditAsync(AuditType type, int userId, int objectId, string? message = null, string? parameters = null)
{
_auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.Media), message));
Guid userKey = await _userIdKeyResolver.GetAsync(userId);
await _auditService.AddAsync(
type,
userKey,
objectId,
UmbracoObjectTypes.Media.GetName(),
message,
parameters);
}
#endregion

View File

@@ -14,10 +14,10 @@ internal sealed class MediaTypeContainerService : EntityTypeContainerService<IMe
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IMediaTypeContainerRepository entityContainerRepository,
IAuditRepository auditRepository,
IAuditService auditService,
IEntityRepository entityRepository,
IUserIdKeyResolver userIdKeyResolver)
: base(provider, loggerFactory, eventMessagesFactory, entityContainerRepository, auditRepository, entityRepository, userIdKeyResolver)
: base(provider, loggerFactory, eventMessagesFactory, entityContainerRepository, auditService, entityRepository, userIdKeyResolver)
{
}

View File

@@ -1,4 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
@@ -19,7 +21,7 @@ public class MediaTypeService : ContentTypeServiceBase<IMediaTypeRepository, IMe
IEventMessagesFactory eventMessagesFactory,
IMediaService mediaService,
IMediaTypeRepository mediaTypeRepository,
IAuditRepository auditRepository,
IAuditService auditService,
IMediaTypeContainerRepository entityContainerRepository,
IEntityRepository entityRepository,
IEventAggregator eventAggregator,
@@ -30,13 +32,72 @@ public class MediaTypeService : ContentTypeServiceBase<IMediaTypeRepository, IMe
loggerFactory,
eventMessagesFactory,
mediaTypeRepository,
auditRepository,
auditService,
entityContainerRepository,
entityRepository,
eventAggregator,
userIdKeyResolver,
contentTypeFilters) => MediaService = mediaService;
contentTypeFilters)
{
MediaService = mediaService;
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public MediaTypeService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IMediaService mediaService,
IMediaTypeRepository mediaTypeRepository,
IAuditRepository auditRepository,
IMediaTypeContainerRepository entityContainerRepository,
IEntityRepository entityRepository,
IEventAggregator eventAggregator,
IUserIdKeyResolver userIdKeyResolver,
ContentTypeFilterCollection contentTypeFilters)
: this(
provider,
loggerFactory,
eventMessagesFactory,
mediaService,
mediaTypeRepository,
StaticServiceProvider.Instance.GetRequiredService<IAuditService>(),
entityContainerRepository,
entityRepository,
eventAggregator,
userIdKeyResolver,
contentTypeFilters)
{
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public MediaTypeService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IMediaService mediaService,
IMediaTypeRepository mediaTypeRepository,
IAuditService auditService,
IAuditRepository auditRepository,
IMediaTypeContainerRepository entityContainerRepository,
IEntityRepository entityRepository,
IEventAggregator eventAggregator,
IUserIdKeyResolver userIdKeyResolver,
ContentTypeFilterCollection contentTypeFilters)
: this(
provider,
loggerFactory,
eventMessagesFactory,
mediaService,
mediaTypeRepository,
auditService,
entityContainerRepository,
entityRepository,
eventAggregator,
userIdKeyResolver,
contentTypeFilters)
{
}
protected override int[] ReadLockIds => MediaTypeLocks.ReadLockIds;

View File

@@ -1,4 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Membership;
@@ -19,9 +21,10 @@ namespace Umbraco.Cms.Core.Services
private readonly IMemberRepository _memberRepository;
private readonly IMemberTypeRepository _memberTypeRepository;
private readonly IMemberGroupRepository _memberGroupRepository;
private readonly IAuditRepository _auditRepository;
private readonly IAuditService _auditService;
private readonly IMemberGroupService _memberGroupService;
private readonly Lazy<IIdKeyMap> _idKeyMap;
private readonly IUserIdKeyResolver _userIdKeyResolver;
#region Constructor
@@ -33,18 +36,72 @@ namespace Umbraco.Cms.Core.Services
IMemberRepository memberRepository,
IMemberTypeRepository memberTypeRepository,
IMemberGroupRepository memberGroupRepository,
IAuditRepository auditRepository,
Lazy<IIdKeyMap> idKeyMap)
IAuditService auditService,
Lazy<IIdKeyMap> idKeyMap,
IUserIdKeyResolver userIdKeyResolver)
: base(provider, loggerFactory, eventMessagesFactory)
{
_memberRepository = memberRepository;
_memberTypeRepository = memberTypeRepository;
_memberGroupRepository = memberGroupRepository;
_auditRepository = auditRepository;
_auditService = auditService;
_idKeyMap = idKeyMap;
_userIdKeyResolver = userIdKeyResolver;
_memberGroupService = memberGroupService ?? throw new ArgumentNullException(nameof(memberGroupService));
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public MemberService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IMemberGroupService memberGroupService,
IMemberRepository memberRepository,
IMemberTypeRepository memberTypeRepository,
IMemberGroupRepository memberGroupRepository,
IAuditRepository auditRepository,
Lazy<IIdKeyMap> idKeyMap)
: this(
provider,
loggerFactory,
eventMessagesFactory,
memberGroupService,
memberRepository,
memberTypeRepository,
memberGroupRepository,
StaticServiceProvider.Instance.GetRequiredService<IAuditService>(),
idKeyMap,
StaticServiceProvider.Instance.GetRequiredService<IUserIdKeyResolver>())
{
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public MemberService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IMemberGroupService memberGroupService,
IMemberRepository memberRepository,
IMemberTypeRepository memberTypeRepository,
IMemberGroupRepository memberGroupRepository,
IAuditService auditService,
IAuditRepository auditRepository,
Lazy<IIdKeyMap> idKeyMap,
IUserIdKeyResolver userIdKeyResolver)
: this(
provider,
loggerFactory,
eventMessagesFactory,
memberGroupService,
memberRepository,
memberTypeRepository,
memberGroupRepository,
auditService,
idKeyMap,
userIdKeyResolver)
{
}
#endregion
#region Count
@@ -1139,7 +1196,21 @@ namespace Umbraco.Cms.Core.Services
#region Private Methods
private void Audit(AuditType type, int userId, int objectId, string? message = null) => _auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.Member), message));
private void Audit(AuditType type, int userId, int objectId, string? message = null) =>
AuditAsync(type, userId, objectId, message).GetAwaiter().GetResult();
private async Task AuditAsync(AuditType type, int userId, int objectId, string? message = null, string? parameters = null)
{
Guid userKey = await _userIdKeyResolver.GetAsync(userId);
await _auditService.AddAsync(
type,
userKey,
objectId,
UmbracoObjectTypes.Member.GetName(),
message,
parameters);
}
private IMember? GetMemberFromRepository(Guid id)
=> _idKeyMap.Value.GetIdForKey(id, UmbracoObjectTypes.Member) switch

View File

@@ -1,4 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
@@ -20,7 +22,7 @@ public class MemberTypeService : ContentTypeServiceBase<IMemberTypeRepository, I
IEventMessagesFactory eventMessagesFactory,
IMemberService memberService,
IMemberTypeRepository memberTypeRepository,
IAuditRepository auditRepository,
IAuditService auditService,
IMemberTypeContainerRepository entityContainerRepository,
IEntityRepository entityRepository,
IEventAggregator eventAggregator,
@@ -31,7 +33,7 @@ public class MemberTypeService : ContentTypeServiceBase<IMemberTypeRepository, I
loggerFactory,
eventMessagesFactory,
memberTypeRepository,
auditRepository,
auditService,
entityContainerRepository,
entityRepository,
eventAggregator,
@@ -42,6 +44,63 @@ public class MemberTypeService : ContentTypeServiceBase<IMemberTypeRepository, I
_memberTypeRepository = memberTypeRepository;
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public MemberTypeService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IMemberService memberService,
IMemberTypeRepository memberTypeRepository,
IAuditRepository auditRepository,
IMemberTypeContainerRepository entityContainerRepository,
IEntityRepository entityRepository,
IEventAggregator eventAggregator,
IUserIdKeyResolver userIdKeyResolver,
ContentTypeFilterCollection contentTypeFilters)
: this(
provider,
loggerFactory,
eventMessagesFactory,
memberService,
memberTypeRepository,
StaticServiceProvider.Instance.GetRequiredService<IAuditService>(),
entityContainerRepository,
entityRepository,
eventAggregator,
userIdKeyResolver,
contentTypeFilters)
{
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public MemberTypeService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IMemberService memberService,
IMemberTypeRepository memberTypeRepository,
IAuditService auditService,
IAuditRepository auditRepository,
IMemberTypeContainerRepository entityContainerRepository,
IEntityRepository entityRepository,
IEventAggregator eventAggregator,
IUserIdKeyResolver userIdKeyResolver,
ContentTypeFilterCollection contentTypeFilters)
: this(
provider,
loggerFactory,
eventMessagesFactory,
memberService,
memberTypeRepository,
auditService,
entityContainerRepository,
entityRepository,
eventAggregator,
userIdKeyResolver,
contentTypeFilters)
{
}
// beware! order is important to avoid deadlocks
protected override int[] ReadLockIds { get; } = { Constants.Locks.MemberTypes };

View File

@@ -1,4 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
@@ -14,6 +16,19 @@ public class PartialViewService : FileServiceOperationBase<IPartialViewRepositor
{
private readonly PartialViewSnippetCollection _snippetCollection;
public PartialViewService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IPartialViewRepository repository,
ILogger<StylesheetService> logger,
IUserIdKeyResolver userIdKeyResolver,
IAuditService auditService,
PartialViewSnippetCollection snippetCollection)
: base(provider, loggerFactory, eventMessagesFactory, repository, logger, userIdKeyResolver, auditService)
=> _snippetCollection = snippetCollection;
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public PartialViewService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
@@ -23,8 +38,40 @@ public class PartialViewService : FileServiceOperationBase<IPartialViewRepositor
IUserIdKeyResolver userIdKeyResolver,
IAuditRepository auditRepository,
PartialViewSnippetCollection snippetCollection)
: base(provider, loggerFactory, eventMessagesFactory, repository, logger, userIdKeyResolver, auditRepository)
=> _snippetCollection = snippetCollection;
: this(
provider,
loggerFactory,
eventMessagesFactory,
repository,
logger,
userIdKeyResolver,
StaticServiceProvider.Instance.GetRequiredService<IAuditService>(),
snippetCollection)
{
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public PartialViewService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IPartialViewRepository repository,
ILogger<StylesheetService> logger,
IUserIdKeyResolver userIdKeyResolver,
IAuditService auditService,
IAuditRepository auditRepository,
PartialViewSnippetCollection snippetCollection)
: this(
provider,
loggerFactory,
eventMessagesFactory,
repository,
logger,
userIdKeyResolver,
auditService,
snippetCollection)
{
}
protected override string[] AllowedFileExtensions { get; } = { ".cshtml" };

View File

@@ -1,4 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
@@ -13,7 +15,7 @@ namespace Umbraco.Cms.Core.Services;
public class RelationService : RepositoryService, IRelationService
{
private readonly IAuditRepository _auditRepository;
private readonly IAuditService _auditService;
private readonly IUserIdKeyResolver _userIdKeyResolver;
private readonly IEntityService _entityService;
private readonly IRelationRepository _relationRepository;
@@ -26,17 +28,62 @@ public class RelationService : RepositoryService, IRelationService
IEntityService entityService,
IRelationRepository relationRepository,
IRelationTypeRepository relationTypeRepository,
IAuditRepository auditRepository,
IAuditService auditService,
IUserIdKeyResolver userIdKeyResolver)
: base(uowProvider, loggerFactory, eventMessagesFactory)
{
_relationRepository = relationRepository;
_relationTypeRepository = relationTypeRepository;
_auditRepository = auditRepository;
_auditService = auditService;
_userIdKeyResolver = userIdKeyResolver;
_entityService = entityService ?? throw new ArgumentNullException(nameof(entityService));
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public RelationService(
ICoreScopeProvider uowProvider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IEntityService entityService,
IRelationRepository relationRepository,
IRelationTypeRepository relationTypeRepository,
IAuditRepository auditRepository,
IUserIdKeyResolver userIdKeyResolver)
: this(
uowProvider,
loggerFactory,
eventMessagesFactory,
entityService,
relationRepository,
relationTypeRepository,
StaticServiceProvider.Instance.GetRequiredService<IAuditService>(),
userIdKeyResolver)
{
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public RelationService(
ICoreScopeProvider uowProvider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IEntityService entityService,
IRelationRepository relationRepository,
IRelationTypeRepository relationTypeRepository,
IAuditService auditService,
IAuditRepository auditRepository,
IUserIdKeyResolver userIdKeyResolver)
: this(
uowProvider,
loggerFactory,
eventMessagesFactory,
entityService,
relationRepository,
relationTypeRepository,
auditService,
userIdKeyResolver)
{
}
/// <inheritdoc />
public IRelation? GetById(int id)
{
@@ -601,8 +648,7 @@ public class RelationService : RepositoryService, IRelationService
}
_relationTypeRepository.Save(relationType);
var currentUser = await _userIdKeyResolver.GetAsync(userKey);
Audit(auditType, currentUser, relationType.Id, auditMessage);
await AuditAsync(auditType, userKey, relationType.Id, auditMessage);
scope.Complete();
scope.Notifications.Publish(
new RelationTypeSavedNotification(relationType, eventMessages).WithStateFrom(savingNotification));
@@ -666,8 +712,7 @@ public class RelationService : RepositoryService, IRelationService
}
_relationTypeRepository.Delete(relationType);
var currentUser = await _userIdKeyResolver.GetAsync(userKey);
Audit(AuditType.Delete, currentUser, relationType.Id, "Deleted relation type");
await AuditAsync(AuditType.Delete, userKey, relationType.Id, "Deleted relation type");
scope.Notifications.Publish(new RelationTypeDeletedNotification(relationType, eventMessages).WithStateFrom(deletingNotification));
scope.Complete();
return Attempt.SucceedWithStatus<IRelationType?, RelationTypeOperationStatus>(RelationTypeOperationStatus.Success, relationType);
@@ -744,7 +789,23 @@ public class RelationService : RepositoryService, IRelationService
}
private void Audit(AuditType type, int userId, int objectId, string? message = null) =>
_auditRepository.Save(new AuditItem(objectId, type, userId, UmbracoObjectTypes.RelationType.GetName(), message));
AuditAsync(type, userId, objectId, message).GetAwaiter().GetResult();
private async Task AuditAsync(AuditType type, int userId, int objectId, string? message = null, string? parameters = null)
{
Guid userKey = await _userIdKeyResolver.GetAsync(userId);
await AuditAsync(type, userKey, objectId, message, parameters);
}
private async Task AuditAsync(AuditType type, Guid userKey, int objectId, string? message = null, string? parameters = null) =>
await _auditService.AddAsync(
type,
userKey,
objectId,
UmbracoObjectTypes.RelationType.GetName(),
message,
parameters);
#endregion
}

View File

@@ -1,4 +1,6 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
@@ -10,6 +12,19 @@ namespace Umbraco.Cms.Core.Services;
public class ScriptService : FileServiceOperationBase<IScriptRepository, IScript, ScriptOperationStatus>, IScriptService
{
public ScriptService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IScriptRepository repository,
ILogger<StylesheetService> logger,
IUserIdKeyResolver userIdKeyResolver,
IAuditService auditService)
: base(provider, loggerFactory, eventMessagesFactory, repository, logger, userIdKeyResolver, auditService)
{
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public ScriptService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
@@ -18,7 +33,35 @@ public class ScriptService : FileServiceOperationBase<IScriptRepository, IScript
ILogger<StylesheetService> logger,
IUserIdKeyResolver userIdKeyResolver,
IAuditRepository auditRepository)
: base(provider, loggerFactory, eventMessagesFactory, repository, logger, userIdKeyResolver, auditRepository)
: this(
provider,
loggerFactory,
eventMessagesFactory,
repository,
logger,
userIdKeyResolver,
StaticServiceProvider.Instance.GetRequiredService<IAuditService>())
{
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public ScriptService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IScriptRepository repository,
ILogger<StylesheetService> logger,
IUserIdKeyResolver userIdKeyResolver,
IAuditService auditService,
IAuditRepository auditRepository)
: this(
provider,
loggerFactory,
eventMessagesFactory,
repository,
logger,
userIdKeyResolver,
auditService)
{
}

View File

@@ -1,4 +1,6 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
@@ -10,6 +12,19 @@ namespace Umbraco.Cms.Core.Services;
public class StylesheetService : FileServiceOperationBase<IStylesheetRepository, IStylesheet, StylesheetOperationStatus>, IStylesheetService
{
public StylesheetService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IStylesheetRepository repository,
ILogger<StylesheetService> logger,
IUserIdKeyResolver userIdKeyResolver,
IAuditService auditService)
: base(provider, loggerFactory, eventMessagesFactory, repository, logger, userIdKeyResolver, auditService)
{
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public StylesheetService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
@@ -18,7 +33,35 @@ public class StylesheetService : FileServiceOperationBase<IStylesheetRepository,
ILogger<StylesheetService> logger,
IUserIdKeyResolver userIdKeyResolver,
IAuditRepository auditRepository)
: base(provider, loggerFactory, eventMessagesFactory, repository, logger, userIdKeyResolver, auditRepository)
: this(
provider,
loggerFactory,
eventMessagesFactory,
repository,
logger,
userIdKeyResolver,
StaticServiceProvider.Instance.GetRequiredService<IAuditService>())
{
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public StylesheetService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IStylesheetRepository repository,
ILogger<StylesheetService> logger,
IUserIdKeyResolver userIdKeyResolver,
IAuditService auditService,
IAuditRepository auditRepository)
: this(
provider,
loggerFactory,
eventMessagesFactory,
repository,
logger,
userIdKeyResolver,
auditService)
{
}

View File

@@ -1,4 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models;
@@ -16,11 +18,26 @@ public class TemplateService : RepositoryService, ITemplateService
{
private readonly IShortStringHelper _shortStringHelper;
private readonly ITemplateRepository _templateRepository;
private readonly IAuditRepository _auditRepository;
private readonly IAuditService _auditService;
private readonly ITemplateContentParserService _templateContentParserService;
private readonly IUserIdKeyResolver _userIdKeyResolver;
private readonly IDefaultViewContentProvider _defaultViewContentProvider;
public TemplateService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IShortStringHelper shortStringHelper,
ITemplateRepository templateRepository,
IAuditService auditService,
ITemplateContentParserService templateContentParserService)
: base(provider, loggerFactory, eventMessagesFactory)
{
_shortStringHelper = shortStringHelper;
_templateRepository = templateRepository;
_auditService = auditService;
_templateContentParserService = templateContentParserService;
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public TemplateService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
@@ -31,14 +48,38 @@ public class TemplateService : RepositoryService, ITemplateService
ITemplateContentParserService templateContentParserService,
IUserIdKeyResolver userIdKeyResolver,
IDefaultViewContentProvider defaultViewContentProvider)
: base(provider, loggerFactory, eventMessagesFactory)
: this(
provider,
loggerFactory,
eventMessagesFactory,
shortStringHelper,
templateRepository,
StaticServiceProvider.Instance.GetRequiredService<IAuditService>(),
templateContentParserService)
{
}
[Obsolete("Use the non-obsolete constructor instead. Scheduled removal in v19.")]
public TemplateService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IShortStringHelper shortStringHelper,
ITemplateRepository templateRepository,
IAuditService auditService,
IAuditRepository auditRepository,
ITemplateContentParserService templateContentParserService,
IUserIdKeyResolver userIdKeyResolver,
IDefaultViewContentProvider defaultViewContentProvider)
: this(
provider,
loggerFactory,
eventMessagesFactory,
shortStringHelper,
templateRepository,
auditService,
templateContentParserService)
{
_shortStringHelper = shortStringHelper;
_templateRepository = templateRepository;
_auditRepository = auditRepository;
_templateContentParserService = templateContentParserService;
_userIdKeyResolver = userIdKeyResolver;
_defaultViewContentProvider = defaultViewContentProvider;
}
/// <inheritdoc />
@@ -81,8 +122,7 @@ public class TemplateService : RepositoryService, ITemplateService
scope.Notifications.Publish(
new TemplateSavedNotification(template, eventMessages).WithStateFrom(savingEvent));
var currentUserId = await _userIdKeyResolver.GetAsync(userKey);
Audit(AuditType.New, currentUserId, template.Id, UmbracoObjectTypes.Template.GetName());
await Audit(AuditType.New, userKey, template.Id, UmbracoObjectTypes.Template.GetName());
scope.Complete();
}
@@ -270,8 +310,7 @@ public class TemplateService : RepositoryService, ITemplateService
scope.Notifications.Publish(
new TemplateSavedNotification(template, eventMessages).WithStateFrom(savingNotification));
var currentUserId = await _userIdKeyResolver.GetAsync(userKey);
Audit(auditType, currentUserId, template.Id, UmbracoObjectTypes.Template.GetName());
await Audit(auditType, userKey, template.Id, UmbracoObjectTypes.Template.GetName());
scope.Complete();
return Attempt.SucceedWithStatus(TemplateOperationStatus.Success, template);
}
@@ -391,8 +430,8 @@ public class TemplateService : RepositoryService, ITemplateService
}
}
private void Audit(AuditType type, int userId, int objectId, string? entityType) =>
_auditRepository.Save(new AuditItem(objectId, type, userId, entityType));
private Task Audit(AuditType type, Guid userKey, int objectId, string? entityType) =>
_auditService.AddAsync(type, userKey, objectId, entityType);
private async Task<Attempt<ITemplate?, TemplateOperationStatus>> DeleteAsync(Func<Task<ITemplate?>> getTemplate, Guid userKey)
{
@@ -424,8 +463,7 @@ public class TemplateService : RepositoryService, ITemplateService
scope.Notifications.Publish(
new TemplateDeletedNotification(template, eventMessages).WithStateFrom(deletingNotification));
var currentUserId = await _userIdKeyResolver.GetAsync(userKey);
Audit(AuditType.Delete, currentUserId, template.Id, UmbracoObjectTypes.Template.GetName());
await Audit(AuditType.Delete, userKey, template.Id, UmbracoObjectTypes.Template.GetName());
scope.Complete();
return Attempt.SucceedWithStatus<ITemplate?, TemplateOperationStatus>(TemplateOperationStatus.Success, template);
}

View File

@@ -23,7 +23,7 @@ public class AuditServiceTests
private Mock<ICoreScopeProvider> _scopeProviderMock;
private Mock<IAuditRepository> _auditRepositoryMock;
private Mock<IEntityService> _entityServiceMock;
private Mock<IUserService> _userServiceMock;
private Mock<IUserIdKeyResolver> _userIdKeyResolverMock;
[SetUp]
public void Setup()
@@ -31,14 +31,14 @@ public class AuditServiceTests
_scopeProviderMock = new Mock<ICoreScopeProvider>(MockBehavior.Strict);
_auditRepositoryMock = new Mock<IAuditRepository>(MockBehavior.Strict);
_entityServiceMock = new Mock<IEntityService>(MockBehavior.Strict);
_userServiceMock = new Mock<IUserService>(MockBehavior.Strict);
_userIdKeyResolverMock = new Mock<IUserIdKeyResolver>(MockBehavior.Strict);
_auditService = new AuditService(
_scopeProviderMock.Object,
Mock.Of<ILoggerFactory>(MockBehavior.Strict),
Mock.Of<IEventMessagesFactory>(MockBehavior.Strict),
_auditRepositoryMock.Object,
_userServiceMock.Object,
_userIdKeyResolverMock.Object,
_entityServiceMock.Object);
}
@@ -59,10 +59,8 @@ public class AuditServiceTests
Assert.AreEqual(parameters, item.Parameters);
});
Mock<IUser> mockUser = new Mock<IUser>();
mockUser.Setup(x => x.Id).Returns(Constants.Security.SuperUserId);
_userServiceMock.Setup(x => x.GetAsync(Constants.Security.SuperUserKey)).ReturnsAsync(mockUser.Object);
_userIdKeyResolverMock.Setup(x => x.TryGetAsync(Constants.Security.SuperUserKey))
.ReturnsAsync(Attempt.Succeed(Constants.Security.SuperUserId));
var result = await _auditService.AddAsync(
type,
@@ -79,7 +77,7 @@ public class AuditServiceTests
[Test]
public async Task AddAsync_Does_Not_Succeed_When_Non_Existing_User_Is_Provided()
{
_userServiceMock.Setup(x => x.GetAsync(It.IsAny<Guid>())).ReturnsAsync((IUser?)null);
_userIdKeyResolverMock.Setup(x => x.TryGetAsync(It.IsAny<Guid>())).ReturnsAsync(Attempt.Fail<int>());
var result = await _auditService.AddAsync(
AuditType.Publish,