diff --git a/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs index 919b39c22d..0782579497 100644 --- a/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs +++ b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs @@ -24,6 +24,14 @@ namespace Umbraco.Cms.Core.Cache INotificationHandler, INotificationHandler, INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, INotificationHandler, INotificationHandler { @@ -56,18 +64,6 @@ namespace Umbraco.Cms.Core.Cache _logger.LogInformation("Initializing Umbraco internal event handlers for cache refreshing."); - // bind to user and user group events - Bind(() => UserService.SavedUserGroup += UserService_SavedUserGroup, - () => UserService.SavedUserGroup -= UserService_SavedUserGroup); - Bind(() => UserService.DeletedUserGroup += UserService_DeletedUserGroup, - () => UserService.DeletedUserGroup -= UserService_DeletedUserGroup); - Bind(() => UserService.SavedUser += UserService_SavedUser, - () => UserService.SavedUser -= UserService_SavedUser); - Bind(() => UserService.DeletedUser += UserService_DeletedUser, - () => UserService.DeletedUser -= UserService_DeletedUser); - Bind(() => UserService.UserGroupPermissionsAssigned += UserService_UserGroupPermissionsAssigned, - () => UserService.UserGroupPermissionsAssigned -= UserService_UserGroupPermissionsAssigned); - // bind to data type events Bind(() => DataTypeService.Deleted += DataTypeService_Deleted, () => DataTypeService.Deleted -= DataTypeService_Deleted); @@ -106,12 +102,6 @@ namespace Umbraco.Cms.Core.Cache Bind(() => MacroService.Deleted += MacroService_Deleted, () => MacroService.Deleted -= MacroService_Deleted); - // bind to member events - Bind(() => MemberService.Saved += MemberService_Saved, - () => MemberService.Saved -= MemberService_Saved); - Bind(() => MemberService.Deleted += MemberService_Deleted, - () => MemberService.Deleted -= MemberService_Deleted); - // bind to media events - handles all media changes Bind(() => MediaService.TreeChanged += MediaService_TreeChanged, () => MediaService.TreeChanged -= MediaService_TreeChanged); @@ -126,12 +116,6 @@ namespace Umbraco.Cms.Core.Cache //Bind(() => ContentService.DeletedBlueprint += ContentService_DeletedBlueprint, // () => ContentService.DeletedBlueprint -= ContentService_DeletedBlueprint); - // bind to public access events - Bind(() => PublicAccessService.Saved += PublicAccessService_Saved, - () => PublicAccessService.Saved -= PublicAccessService_Saved); - Bind(() => PublicAccessService.Deleted += PublicAccessService_Deleted, - () => PublicAccessService.Deleted -= PublicAccessService_Deleted); - // bind to relation type events Bind(() => RelationService.SavedRelationType += RelationService_SavedRelationType, () => RelationService.SavedRelationType -= RelationService_SavedRelationType); @@ -141,15 +125,15 @@ namespace Umbraco.Cms.Core.Cache #region PublicAccessService - private void PublicAccessService_Saved(IPublicAccessService sender, SaveEventArgs e) + public void Handle(PublicAccessEntrySavedNotification notification) { - _distributedCache.RefreshPublicAccess(); } - private void PublicAccessService_Deleted(IPublicAccessService sender, DeleteEventArgs e) + public void Handle(PublicAccessEntryDeletedNotification notification) { _distributedCache.RefreshPublicAccess(); + } #endregion @@ -294,39 +278,36 @@ namespace Umbraco.Cms.Core.Cache #region UserService - private void UserService_UserGroupPermissionsAssigned(IUserService sender, SaveEventArgs e) + public void Handle(UserSavedNotification notification) { - // TODO: Not sure if we need this yet depends if we start caching permissions - //var groupIds = e.SavedEntities.Select(x => x.UserGroupId).Distinct(); - //foreach (var groupId in groupIds) - //{ - // DistributedCache.Instance.RefreshUserGroupPermissionsCache(groupId); - //} - } - - private void UserService_SavedUser(IUserService sender, SaveEventArgs e) - { - foreach (var entity in e.SavedEntities) + foreach (IUser entity in notification.SavedEntities) + { _distributedCache.RefreshUserCache(entity.Id); + } } - private void UserService_DeletedUser(IUserService sender, DeleteEventArgs e) + public void Handle(UserDeletedNotification notification) { - foreach (var entity in e.DeletedEntities) + foreach (IUser entity in notification.DeletedEntities) + { _distributedCache.RemoveUserCache(entity.Id); + } } - private void UserService_SavedUserGroup(IUserService sender, SaveEventArgs e) + public void Handle(UserGroupWithUsersSavedNotification notification) { - foreach (var entity in e.SavedEntities) + foreach (UserGroupWithUsers entity in notification.SavedEntities) + { _distributedCache.RefreshUserGroupCache(entity.UserGroup.Id); + } } - private void UserService_DeletedUserGroup(IUserService sender, DeleteEventArgs e) + public void Handle(UserGroupDeletedNotification notification) { - - foreach (var entity in e.DeletedEntities) + foreach (IUserGroup entity in notification.DeletedEntities) + { _distributedCache.RemoveUserGroupCache(entity.Id); + } } #endregion @@ -388,14 +369,14 @@ namespace Umbraco.Cms.Core.Cache #region MemberService - private void MemberService_Deleted(IMemberService sender, DeleteEventArgs e) + public void Handle(MemberDeletedNotification notification) { - _distributedCache.RemoveMemberCache(e.DeletedEntities.ToArray()); + _distributedCache.RemoveMemberCache(notification.DeletedEntities.ToArray()); } - private void MemberService_Saved(IMemberService sender, SaveEventArgs e) + public void Handle(MemberSavedNotification notification) { - _distributedCache.RefreshMemberCache(e.SavedEntities.ToArray()); + _distributedCache.RefreshMemberCache(notification.SavedEntities.ToArray()); } #endregion diff --git a/src/Umbraco.Infrastructure/Compose/AuditEventsComposer.cs b/src/Umbraco.Infrastructure/Compose/AuditEventsComposer.cs deleted file mode 100644 index b6d5f60765..0000000000 --- a/src/Umbraco.Infrastructure/Compose/AuditEventsComposer.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Umbraco.Cms.Core.Composing; - -namespace Umbraco.Cms.Core.Compose -{ - public sealed class AuditEventsComposer : ComponentComposer, ICoreComposer - { } -} diff --git a/src/Umbraco.Infrastructure/Compose/AuditEventsComponent.cs b/src/Umbraco.Infrastructure/Compose/AuditNotificationsHandler.cs similarity index 61% rename from src/Umbraco.Infrastructure/Compose/AuditEventsComponent.cs rename to src/Umbraco.Infrastructure/Compose/AuditNotificationsHandler.cs index 7c526d330c..e2da324b56 100644 --- a/src/Umbraco.Infrastructure/Compose/AuditEventsComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/AuditNotificationsHandler.cs @@ -2,7 +2,6 @@ using System; using System.Linq; using System.Text; using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; @@ -10,12 +9,21 @@ using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Net; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Implement; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Compose { - public sealed class AuditEventsComponent : IComponent + public sealed class AuditNotificationsHandler : + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler { private readonly IAuditService _auditService; private readonly IUserService _userService; @@ -23,55 +31,26 @@ namespace Umbraco.Cms.Core.Compose private readonly IIpResolver _ipResolver; private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; private readonly GlobalSettings _globalSettings; + private readonly IMemberService _memberService; - public AuditEventsComponent( + public AuditNotificationsHandler( IAuditService auditService, IUserService userService, IEntityService entityService, IIpResolver ipResolver, IOptions globalSettings, - IBackOfficeSecurityAccessor backOfficeSecurityAccessor) + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + IMemberService memberService) { _auditService = auditService; _userService = userService; _entityService = entityService; _ipResolver = ipResolver; _backOfficeSecurityAccessor = backOfficeSecurityAccessor; + _memberService = memberService; _globalSettings = globalSettings.Value; } - public void Initialize() - { - UserService.SavedUserGroup += OnSavedUserGroupWithUsers; - - UserService.SavedUser += OnSavedUser; - UserService.DeletedUser += OnDeletedUser; - UserService.UserGroupPermissionsAssigned += UserGroupPermissionAssigned; - - MemberService.Saved += OnSavedMember; - MemberService.Deleted += OnDeletedMember; - MemberService.AssignedRoles += OnAssignedRoles; - MemberService.RemovedRoles += OnRemovedRoles; - MemberService.Exported += OnMemberExported; - } - - public void Terminate() - { - UserService.SavedUserGroup -= OnSavedUserGroupWithUsers; - - UserService.SavedUser -= OnSavedUser; - UserService.DeletedUser -= OnDeletedUser; - UserService.UserGroupPermissionsAssigned -= UserGroupPermissionAssigned; - - MemberService.Saved -= OnSavedMember; - MemberService.Deleted -= OnDeletedMember; - MemberService.AssignedRoles -= OnAssignedRoles; - MemberService.RemovedRoles -= OnRemovedRoles; - MemberService.Exported -= OnMemberExported; - } - - public static IUser UnknownUser(GlobalSettings globalSettings) => new User(globalSettings) { Id = Cms.Core.Constants.Security.UnknownUserId, Name = Cms.Core.Constants.Security.UnknownUserName, Email = "" }; - private IUser CurrentPerformingUser { get @@ -82,45 +61,48 @@ namespace Umbraco.Cms.Core.Compose } } - private IUser GetPerformingUser(int userId) - { - var found = userId >= 0 ? _userService.GetUserById(userId) : null; - return found ?? UnknownUser(_globalSettings); - } + public static IUser UnknownUser(GlobalSettings globalSettings) => new User(globalSettings) { Id = Cms.Core.Constants.Security.UnknownUserId, Name = Cms.Core.Constants.Security.UnknownUserName, Email = "" }; private string PerformingIp => _ipResolver.GetCurrentRequestIpAddress(); - private string FormatEmail(IMember member) - { - return member == null ? string.Empty : member.Email.IsNullOrWhiteSpace() ? "" : $"<{member.Email}>"; - } + private string FormatEmail(IMember member) => member == null ? string.Empty : member.Email.IsNullOrWhiteSpace() ? "" : $"<{member.Email}>"; - private string FormatEmail(IUser user) - { - return user == null ? string.Empty : user.Email.IsNullOrWhiteSpace() ? "" : $"<{user.Email}>"; - } + private string FormatEmail(IUser user) => user == null ? string.Empty : user.Email.IsNullOrWhiteSpace() ? "" : $"<{user.Email}>"; - private void OnRemovedRoles(IMemberService sender, RolesEventArgs args) + public void Handle(MemberSavedNotification notification) { var performingUser = CurrentPerformingUser; - var roles = string.Join(", ", args.Roles); - var members = sender.GetAllMembers(args.MemberIds).ToDictionary(x => x.Id, x => x); - foreach (var id in args.MemberIds) + var members = notification.SavedEntities; + foreach (var member in members) { - members.TryGetValue(id, out var member); + var dp = string.Join(", ", ((Member)member).GetWereDirtyProperties()); + _auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp, DateTime.UtcNow, - -1, $"Member {id} \"{member?.Name ?? "(unknown)"}\" {FormatEmail(member)}", - "umbraco/member/roles/removed", $"roles modified, removed {roles}"); + -1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}", + "umbraco/member/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}"); } } - private void OnAssignedRoles(IMemberService sender, RolesEventArgs args) + public void Handle(MemberDeletedNotification notification) { var performingUser = CurrentPerformingUser; - var roles = string.Join(", ", args.Roles); - var members = sender.GetAllMembers(args.MemberIds).ToDictionary(x => x.Id, x => x); - foreach (var id in args.MemberIds) + var members = notification.DeletedEntities; + foreach (var member in members) + { + _auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp, + DateTime.UtcNow, + -1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}", + "umbraco/member/delete", $"delete member id:{member.Id} \"{member.Name}\" {FormatEmail(member)}"); + } + } + + public void Handle(AssignedMemberRolesNotification notification) + { + var performingUser = CurrentPerformingUser; + var roles = string.Join(", ", notification.Roles); + var members = _memberService.GetAllMembers(notification.MemberIds).ToDictionary(x => x.Id, x => x); + foreach (var id in notification.MemberIds) { members.TryGetValue(id, out var member); _auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp, @@ -130,10 +112,25 @@ namespace Umbraco.Cms.Core.Compose } } - private void OnMemberExported(IMemberService sender, ExportedMemberEventArgs exportedMemberEventArgs) + public void Handle(RemovedMemberRolesNotification notification) { var performingUser = CurrentPerformingUser; - var member = exportedMemberEventArgs.Member; + var roles = string.Join(", ", notification.Roles); + var members = _memberService.GetAllMembers(notification.MemberIds).ToDictionary(x => x.Id, x => x); + foreach (var id in notification.MemberIds) + { + members.TryGetValue(id, out var member); + _auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp, + DateTime.UtcNow, + -1, $"Member {id} \"{member?.Name ?? "(unknown)"}\" {FormatEmail(member)}", + "umbraco/member/roles/removed", $"roles modified, removed {roles}"); + } + } + + public void Handle(ExportedMemberNotification notification) + { + var performingUser = CurrentPerformingUser; + var member = notification.Member; _auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp, DateTime.UtcNow, @@ -141,10 +138,40 @@ namespace Umbraco.Cms.Core.Compose "umbraco/member/exported", "exported member data"); } - private void OnSavedUserGroupWithUsers(IUserService sender, SaveEventArgs saveEventArgs) + public void Handle(UserSavedNotification notification) { var performingUser = CurrentPerformingUser; - foreach (var groupWithUser in saveEventArgs.SavedEntities) + var affectedUsers = notification.SavedEntities; + foreach (var affectedUser in affectedUsers) + { + var groups = affectedUser.WasPropertyDirty("Groups") + ? string.Join(", ", affectedUser.Groups.Select(x => x.Alias)) + : null; + + var dp = string.Join(", ", ((User)affectedUser).GetWereDirtyProperties()); + + _auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp, + DateTime.UtcNow, + affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}", + "umbraco/user/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}{(groups == null ? "" : "; groups assigned: " + groups)}"); + } + } + + public void Handle(UserDeletedNotification notification) + { + var performingUser = CurrentPerformingUser; + var affectedUsers = notification.DeletedEntities; + foreach (var affectedUser in affectedUsers) + _auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp, + DateTime.UtcNow, + affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}", + "umbraco/user/delete", "delete user"); + } + + public void Handle(UserGroupWithUsersSavedNotification notification) + { + var performingUser = CurrentPerformingUser; + foreach (var groupWithUser in notification.SavedEntities) { var group = groupWithUser.UserGroup; @@ -192,13 +219,13 @@ namespace Umbraco.Cms.Core.Compose } } - private void UserGroupPermissionAssigned(IUserService sender, SaveEventArgs saveEventArgs) + public void Handle(AssignedUserGroupPermissionsNotification notification) { var performingUser = CurrentPerformingUser; - var perms = saveEventArgs.SavedEntities; + var perms = notification.EntityPermissions; foreach (var perm in perms) { - var group = sender.GetUserGroupById(perm.UserGroupId); + var group = _userService.GetUserGroupById(perm.UserGroupId); var assigned = string.Join(", ", perm.AssignedPermissions); var entity = _entityService.Get(perm.EntityId); @@ -208,100 +235,5 @@ namespace Umbraco.Cms.Core.Compose "umbraco/user-group/permissions-change", $"assigning {(string.IsNullOrWhiteSpace(assigned) ? "(nothing)" : assigned)} on id:{perm.EntityId} \"{entity.Name}\""); } } - - private void OnSavedMember(IMemberService sender, SaveEventArgs saveEventArgs) - { - var performingUser = CurrentPerformingUser; - var members = saveEventArgs.SavedEntities; - foreach (var member in members) - { - var dp = string.Join(", ", ((Member) member).GetWereDirtyProperties()); - - _auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp, - DateTime.UtcNow, - -1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}", - "umbraco/member/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}"); - } - } - - private void OnDeletedMember(IMemberService sender, DeleteEventArgs deleteEventArgs) - { - var performingUser = CurrentPerformingUser; - var members = deleteEventArgs.DeletedEntities; - foreach (var member in members) - { - _auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp, - DateTime.UtcNow, - -1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}", - "umbraco/member/delete", $"delete member id:{member.Id} \"{member.Name}\" {FormatEmail(member)}"); - } - } - - private void OnSavedUser(IUserService sender, SaveEventArgs saveEventArgs) - { - var performingUser = CurrentPerformingUser; - var affectedUsers = saveEventArgs.SavedEntities; - foreach (var affectedUser in affectedUsers) - { - var groups = affectedUser.WasPropertyDirty("Groups") - ? string.Join(", ", affectedUser.Groups.Select(x => x.Alias)) - : null; - - var dp = string.Join(", ", ((User)affectedUser).GetWereDirtyProperties()); - - _auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp, - DateTime.UtcNow, - affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}", - "umbraco/user/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}{(groups == null ? "" : "; groups assigned: " + groups)}"); - } - } - - private void OnDeletedUser(IUserService sender, DeleteEventArgs deleteEventArgs) - { - var performingUser = CurrentPerformingUser; - var affectedUsers = deleteEventArgs.DeletedEntities; - foreach (var affectedUser in affectedUsers) - _auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp, - DateTime.UtcNow, - affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}", - "umbraco/user/delete", "delete user"); - } - - private void WriteAudit(int performingId, int affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null) - { - var performingUser = _userService.GetUserById(performingId); - - var performingDetails = performingUser == null - ? $"User UNKNOWN:{performingId}" - : $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}"; - - WriteAudit(performingId, performingDetails, affectedId, ipAddress, eventType, eventDetails, affectedDetails); - } - - private void WriteAudit(IUser performingUser, int affectedId, string ipAddress, string eventType, string eventDetails) - { - var performingDetails = performingUser == null - ? $"User UNKNOWN" - : $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}"; - - WriteAudit(performingUser?.Id ?? 0, performingDetails, affectedId, ipAddress, eventType, eventDetails); - } - - private void WriteAudit(int performingId, string performingDetails, int affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null) - { - if (affectedDetails == null) - { - var affectedUser = _userService.GetUserById(affectedId); - affectedDetails = affectedUser == null - ? $"User UNKNOWN:{affectedId}" - : $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}"; - } - - _auditService.Write(performingId, performingDetails, - ipAddress, - DateTime.UtcNow, - affectedId, affectedDetails, - eventType, eventDetails); - } } } diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs deleted file mode 100644 index 687fdbf294..0000000000 --- a/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Actions; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.Membership; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Implement; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Core.Compose -{ - /// - /// TODO: this component must be removed entirely - there is some code duplication in in anticipation of this component being deleted - /// - public sealed class NotificationsComponent : IComponent - { - private readonly Notifier _notifier; - private readonly ActionCollection _actions; - private readonly IContentService _contentService; - - public NotificationsComponent(Notifier notifier, ActionCollection actions, IContentService contentService) - { - _notifier = notifier; - _actions = actions; - _contentService = contentService; - } - - public void Initialize() - { - //Send notifications for the public access changed action - PublicAccessService.Saved += PublicAccessService_Saved; - - UserService.UserGroupPermissionsAssigned += UserService_UserGroupPermissionsAssigned; - } - - public void Terminate() - { - PublicAccessService.Saved -= PublicAccessService_Saved; - UserService.UserGroupPermissionsAssigned -= UserService_UserGroupPermissionsAssigned; - } - - private void UserService_UserGroupPermissionsAssigned(IUserService sender, SaveEventArgs args) - => UserServiceUserGroupPermissionsAssigned(args, _contentService); - - private void PublicAccessService_Saved(IPublicAccessService sender, SaveEventArgs args) - => PublicAccessServiceSaved(args, _contentService); - - private void UserServiceUserGroupPermissionsAssigned(SaveEventArgs args, IContentService contentService) - { - var entities = contentService.GetByIds(args.SavedEntities.Select(e => e.EntityId)).ToArray(); - if (entities.Any() == false) - { - return; - } - _notifier.Notify(_actions.GetAction(), entities); - } - - private void PublicAccessServiceSaved(SaveEventArgs args, IContentService contentService) - { - var entities = contentService.GetByIds(args.SavedEntities.Select(e => e.ProtectedNodeId)).ToArray(); - if (entities.Any() == false) - { - return; - } - _notifier.Notify(_actions.GetAction(), entities); - } - - /// - /// This class is used to send the notifications - /// - public sealed class Notifier - { - private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; - private readonly IHostingEnvironment _hostingEnvironment; - private readonly INotificationService _notificationService; - private readonly IUserService _userService; - private readonly ILocalizedTextService _textService; - private readonly GlobalSettings _globalSettings; - private readonly ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - public Notifier( - IBackOfficeSecurityAccessor backOfficeSecurityAccessor, - IHostingEnvironment hostingEnvironment, - INotificationService notificationService, - IUserService userService, - ILocalizedTextService textService, - IOptions globalSettings, - ILogger logger) - { - _backOfficeSecurityAccessor = backOfficeSecurityAccessor; - _hostingEnvironment = hostingEnvironment; - _notificationService = notificationService; - _userService = userService; - _textService = textService; - _globalSettings = globalSettings.Value; - _logger = logger; - } - - public void Notify(IAction action, params IContent[] entities) - { - var user = _backOfficeSecurityAccessor?.BackOfficeSecurity?.CurrentUser; - - //if there is no current user, then use the admin - if (user == null) - { - _logger.LogDebug("There is no current Umbraco user logged in, the notifications will be sent from the administrator"); - user = _userService.GetUserById(Constants.Security.SuperUserId); - if (user == null) - { - _logger.LogWarning("Notifications can not be sent, no admin user with id {SuperUserId} could be resolved", Constants.Security.SuperUserId); - return; - } - } - - SendNotification(user, entities, action, _hostingEnvironment.ApplicationMainUrl); - } - - private void SendNotification(IUser sender, IEnumerable entities, IAction action, Uri siteUri) - { - if (sender == null) throw new ArgumentNullException(nameof(sender)); - if (siteUri == null) - { - _logger.LogWarning("Notifications can not be sent, no site URL is set (might be during boot process?)"); - return; - } - - //group by the content type variation since the emails will be different - foreach(var contentVariantGroup in entities.GroupBy(x => x.ContentType.Variations)) - { - _notificationService.SendNotifications( - sender, - contentVariantGroup, - action.Letter.ToString(CultureInfo.InvariantCulture), - _textService.Localize("actions", action.Alias), - siteUri, - ((IUser user, NotificationEmailSubjectParams subject) x) - => _textService.Localize( - "notifications/mailSubject", - x.user.GetUserCulture(_textService, _globalSettings), - new[] { x.subject.SiteUrl, x.subject.Action, x.subject.ItemName }), - ((IUser user, NotificationEmailBodyParams body, bool isHtml) x) - => _textService.Localize( - x.isHtml ? "notifications/mailBodyHtml" : "notifications/mailBody", - x.user.GetUserCulture(_textService, _globalSettings), - new[] - { - x.body.RecipientName, - x.body.Action, - x.body.ItemName, - x.body.EditedUser, - x.body.SiteUrl, - x.body.ItemId, - //format the summary depending on if it's variant or not - contentVariantGroup.Key == ContentVariation.Culture - ? (x.isHtml ? _textService.Localize("notifications/mailBodyVariantHtmlSummary", new[]{ x.body.Summary }) : _textService.Localize("notifications/mailBodyVariantSummary", new []{ x.body.Summary })) - : x.body.Summary, - x.body.ItemUrl - })); - } - } - - } - } - - -} diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs index 2e3403e3dd..be6539ac55 100644 --- a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs +++ b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs @@ -6,6 +6,7 @@ using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Infrastructure.Services.Notifications; @@ -13,14 +14,10 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Compose { - public sealed class NotificationsComposer : ComponentComposer, ICoreComposer + public sealed class NotificationsComposer : ICoreComposer { - public override void Compose(IUmbracoBuilder builder) + public void Compose(IUmbracoBuilder builder) { - base.Compose(builder); - - builder.Services.AddUnique(); - // add handlers for sending user notifications (i.e. emails) builder.Services.AddUnique(); builder @@ -32,7 +29,9 @@ namespace Umbraco.Cms.Core.Compose .AddNotificationHandler() .AddNotificationHandler() .AddNotificationHandler() - .AddNotificationHandler(); + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler(); // add handlers for building content relations builder @@ -52,10 +51,12 @@ namespace Umbraco.Cms.Core.Compose .AddNotificationHandler() .AddNotificationHandler() .AddNotificationHandler() + .AddNotificationHandler() .AddNotificationHandler() .AddNotificationHandler() .AddNotificationHandler() - .AddNotificationHandler(); + .AddNotificationHandler() + .AddNotificationHandler(); // add notification handlers for redirect tracking builder @@ -69,7 +70,27 @@ namespace Umbraco.Cms.Core.Compose .AddNotificationHandler() .AddNotificationHandler() .AddNotificationHandler() - .AddNotificationHandler(); + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler(); + + // add notification handlers for auditing + builder + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler(); } } } diff --git a/src/Umbraco.Infrastructure/Events/UserNotificationsHandler.cs b/src/Umbraco.Infrastructure/Events/UserNotificationsHandler.cs index 62da73c28b..3df16dda25 100644 --- a/src/Umbraco.Infrastructure/Events/UserNotificationsHandler.cs +++ b/src/Umbraco.Infrastructure/Events/UserNotificationsHandler.cs @@ -29,7 +29,9 @@ namespace Umbraco.Cms.Core.Events INotificationHandler, INotificationHandler, INotificationHandler, - INotificationHandler + INotificationHandler, + INotificationHandler, + INotificationHandler { private readonly Notifier _notifier; private readonly ActionCollection _actions; @@ -209,7 +211,26 @@ namespace Umbraco.Cms.Core.Events })); } } + } + public void Handle(AssignedUserGroupPermissionsNotification notification) + { + var entities = _contentService.GetByIds(notification.EntityPermissions.Select(e => e.EntityId)).ToArray(); + if (entities.Any() == false) + { + return; + } + _notifier.Notify(_actions.GetAction(), entities); + } + + public void Handle(PublicAccessEntrySavedNotification notification) + { + var entities = _contentService.GetByIds(notification.SavedEntities.Select(e => e.ProtectedNodeId)).ToArray(); + if (entities.Any() == false) + { + return; + } + _notifier.Notify(_actions.GetAction(), entities); } } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs index 15d56abfb3..a794d62ce2 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs @@ -27,7 +27,8 @@ namespace Umbraco.Cms.Core.PropertyEditors Icon = "icon-download-alt")] public class FileUploadPropertyEditor : DataEditor, IMediaUrlGenerator, INotificationHandler, INotificationHandler, - INotificationHandler, INotificationHandler + INotificationHandler, INotificationHandler, + INotificationHandler { private readonly IMediaFileSystem _mediaFileSystem; private readonly ContentSettings _contentSettings; @@ -95,10 +96,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// The paths to all file upload property files contained within a collection of content entities /// /// - /// - /// This method must be made private once MemberService events have been replaced by notifications - /// - internal IEnumerable ContainedFilePaths(IEnumerable entities) => entities + private IEnumerable ContainedFilePaths(IEnumerable entities) => entities .SelectMany(x => x.Properties) .Where(IsUploadField) .SelectMany(GetFilePathsFromPropertyValues) @@ -162,6 +160,8 @@ namespace Umbraco.Cms.Core.PropertyEditors public void Handle(MediaDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); + public void Handle(MemberDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); + private void DeleteContainedFiles(IEnumerable deletedEntities) { var filePathsToDelete = ContainedFilePaths(deletedEntities); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs index 52972e9f49..902cb32f08 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs @@ -34,7 +34,8 @@ namespace Umbraco.Cms.Core.PropertyEditors Icon = "icon-crop")] public class ImageCropperPropertyEditor : DataEditor, IMediaUrlGenerator, INotificationHandler, INotificationHandler, - INotificationHandler, INotificationHandler + INotificationHandler, INotificationHandler, + INotificationHandler { private readonly IMediaFileSystem _mediaFileSystem; private readonly ContentSettings _contentSettings; @@ -131,10 +132,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// The paths to all image cropper property files contained within a collection of content entities /// /// - /// - /// This method must be made private once MemberService events have been replaced by notifications - /// - internal IEnumerable ContainedFilePaths(IEnumerable entities) => entities + private IEnumerable ContainedFilePaths(IEnumerable entities) => entities .SelectMany(x => x.Properties) .Where(IsCropperField) .SelectMany(GetFilePathsFromPropertyValues) @@ -218,6 +216,8 @@ namespace Umbraco.Cms.Core.PropertyEditors public void Handle(MediaDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); + public void Handle(MemberDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); + private void DeleteContainedFiles(IEnumerable deletedEntities) { var filePathsToDelete = ContainedFilePaths(deletedEntities); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs b/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs deleted file mode 100644 index b9e9e33889..0000000000 --- a/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Implement; - -namespace Umbraco.Cms.Core.PropertyEditors -{ - // TODO: delete this component and make the "ContainedFilePaths" methods on FileUploadPropertyEditor and ImageCropperPropertyEditor private once MemberService uses notifications instead of static events - public sealed class PropertyEditorsComponent : IComponent - { - private readonly PropertyEditorCollection _propertyEditors; - private readonly List _terminate = new List(); - - public PropertyEditorsComponent(PropertyEditorCollection propertyEditors) - { - _propertyEditors = propertyEditors; - } - - public void Initialize() - { - var fileUpload = _propertyEditors.OfType().FirstOrDefault(); - if (fileUpload != null) Initialize(fileUpload); - - var imageCropper = _propertyEditors.OfType().FirstOrDefault(); - if (imageCropper != null) Initialize(imageCropper); - - // grid/examine moved to ExamineComponent - } - - public void Terminate() - { - foreach (var t in _terminate) t(); - } - - private void Initialize(FileUploadPropertyEditor fileUpload) - { - void memberServiceDeleted(IMemberService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(fileUpload.ContainedFilePaths(args.DeletedEntities.Cast())); - MemberService.Deleted += memberServiceDeleted; - _terminate.Add(() => MemberService.Deleted -= memberServiceDeleted); - } - - private void Initialize(ImageCropperPropertyEditor imageCropper) - { - void memberServiceDeleted(IMemberService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(imageCropper.ContainedFilePaths(args.DeletedEntities.Cast())); - MemberService.Deleted += memberServiceDeleted; - _terminate.Add(() => MemberService.Deleted -= memberServiceDeleted); - } - } -} diff --git a/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComposer.cs b/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComposer.cs deleted file mode 100644 index 4e876ad554..0000000000 --- a/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComposer.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Umbraco.Cms.Core.Composing; - -namespace Umbraco.Cms.Core.PropertyEditors -{ - public sealed class PropertyEditorsComposer : ComponentComposer, ICoreComposer - { } -} diff --git a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs index 38b70af19c..aba40333b0 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs @@ -9,6 +9,7 @@ using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Infrastructure.Persistence.Querying; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Services.Implement @@ -22,7 +23,6 @@ namespace Umbraco.Cms.Core.Services.Implement private readonly IMemberTypeRepository _memberTypeRepository; private readonly IMemberGroupRepository _memberGroupRepository; private readonly IAuditRepository _auditRepository; - private readonly IMemberTypeService _memberTypeService; private readonly IMemberGroupService _memberGroupService; @@ -773,10 +773,12 @@ namespace Umbraco.Cms.Core.Services.Implement member.Username = member.Username.Trim(); member.Email = member.Email.Trim(); + var evtMsgs = EventMessagesFactory.Get(); + using (IScope scope = ScopeProvider.CreateScope()) { - var saveEventArgs = new SaveEventArgs(member); - if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs)) + var savingNotification = new MemberSavingNotification(member, evtMsgs); + if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return; @@ -793,8 +795,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - saveEventArgs.CanCancel = false; - scope.Events.Dispatch(Saved, this, saveEventArgs); + scope.Notifications.Publish(new MemberSavedNotification(member, evtMsgs).WithStateFrom(savingNotification)); } Audit(AuditType.Save, 0, member.Id); @@ -808,10 +809,12 @@ namespace Umbraco.Cms.Core.Services.Implement { var membersA = members.ToArray(); + var evtMsgs = EventMessagesFactory.Get(); + using (var scope = ScopeProvider.CreateScope()) { - var saveEventArgs = new SaveEventArgs(membersA); - if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs)) + var savingNotification = new MemberSavingNotification(membersA, evtMsgs); + if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return; @@ -830,8 +833,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - saveEventArgs.CanCancel = false; - scope.Events.Dispatch(Saved, this, saveEventArgs); + scope.Notifications.Publish(new MemberSavedNotification(membersA, evtMsgs).WithStateFrom(savingNotification)); } Audit(AuditType.Save, 0, -1, "Save multiple Members"); @@ -849,32 +851,30 @@ namespace Umbraco.Cms.Core.Services.Implement /// to Delete public void Delete(IMember member) { + var evtMsgs = EventMessagesFactory.Get(); + using (var scope = ScopeProvider.CreateScope()) { - var deleteEventArgs = new DeleteEventArgs(member); - if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs)) + var deletingNotification = new MemberDeletingNotification(member, evtMsgs); + if (scope.Notifications.PublishCancelable(deletingNotification)) { scope.Complete(); return; } scope.WriteLock(Constants.Locks.MemberTree); - DeleteLocked(scope, member, deleteEventArgs); + DeleteLocked(scope, member, evtMsgs, deletingNotification.State); Audit(AuditType.Delete, 0, member.Id); scope.Complete(); } } - private void DeleteLocked(IScope scope, IMember member, DeleteEventArgs args = null) + private void DeleteLocked(IScope scope, IMember member, EventMessages evtMsgs, IDictionary notificationState = null) { // a member has no descendants _memberRepository.Delete(member); - if (args == null) - args = new DeleteEventArgs(member, false); // raise event & get flagged files - else - args.CanCancel = false; - scope.Events.Dispatch(Deleted, this, args); + scope.Notifications.Publish(new MemberDeletedNotification(member, evtMsgs).WithState(notificationState)); // media files deleted by QueuingEventDispatcher } @@ -1017,7 +1017,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Constants.Locks.MemberTree); int[] ids = _memberGroupRepository.GetMemberIds(usernames); _memberGroupRepository.AssignRoles(ids, roleNames); - scope.Events.Dispatch(AssignedRoles, this, new RolesEventArgs(ids, roleNames), nameof(AssignedRoles)); + scope.Notifications.Publish(new AssignedMemberRolesNotification(ids, roleNames)); scope.Complete(); } } @@ -1031,7 +1031,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Constants.Locks.MemberTree); int[] ids = _memberGroupRepository.GetMemberIds(usernames); _memberGroupRepository.DissociateRoles(ids, roleNames); - scope.Events.Dispatch(RemovedRoles, this, new RolesEventArgs(ids, roleNames), nameof(RemovedRoles)); + scope.Notifications.Publish(new RemovedMemberRolesNotification(ids, roleNames)); scope.Complete(); } } @@ -1044,7 +1044,7 @@ namespace Umbraco.Cms.Core.Services.Implement { scope.WriteLock(Constants.Locks.MemberTree); _memberGroupRepository.AssignRoles(memberIds, roleNames); - scope.Events.Dispatch(AssignedRoles, this, new RolesEventArgs(memberIds, roleNames), nameof(AssignedRoles)); + scope.Notifications.Publish(new AssignedMemberRolesNotification(memberIds, roleNames)); scope.Complete(); } } @@ -1057,7 +1057,7 @@ namespace Umbraco.Cms.Core.Services.Implement { scope.WriteLock(Constants.Locks.MemberTree); _memberGroupRepository.DissociateRoles(memberIds, roleNames); - scope.Events.Dispatch(RemovedRoles, this, new RolesEventArgs(memberIds, roleNames), nameof(RemovedRoles)); + scope.Notifications.Publish(new RemovedMemberRolesNotification(memberIds, roleNames)); scope.Complete(); } } @@ -1070,45 +1070,6 @@ namespace Umbraco.Cms.Core.Services.Implement #endregion - #region Event Handlers - - /// - /// Occurs before Delete - /// - public static event TypedEventHandler> Deleting; - - /// - /// Occurs after Delete - /// - public static event TypedEventHandler> Deleted; - - /// - /// Occurs before Save - /// - public static event TypedEventHandler> Saving; - - /// - /// Occurs after Save - /// - public static event TypedEventHandler> Saved; - - /// - /// Occurs after roles have been assigned. - /// - public static event TypedEventHandler AssignedRoles; - - /// - /// Occurs after roles have been removed. - /// - public static event TypedEventHandler RemovedRoles; - - /// - /// Occurs after members have been exported. - /// - public static event TypedEventHandler Exported; - - #endregion - #region Membership @@ -1145,7 +1106,7 @@ namespace Umbraco.Cms.Core.Services.Implement Properties = new List(GetPropertyExportItems(member)) }; - scope.Events.Dispatch(Exported, this, new ExportedMemberEventArgs(member, model)); + scope.Notifications.Publish(new ExportedMemberNotification(member, model)); return model; } @@ -1187,6 +1148,8 @@ namespace Umbraco.Cms.Core.Services.Implement /// Id of the MemberType public void DeleteMembersOfType(int memberTypeId) { + var evtMsgs = EventMessagesFactory.Get(); + // note: no tree to manage here using (IScope scope = ScopeProvider.CreateScope()) { @@ -1196,9 +1159,8 @@ namespace Umbraco.Cms.Core.Services.Implement IQuery query = Query().Where(x => x.ContentTypeId == memberTypeId); IMember[] members = _memberRepository.Get(query).ToArray(); - var deleteEventArgs = new DeleteEventArgs(members); - if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs)) + if (scope.Notifications.PublishCancelable(new MemberDeletingNotification(members, evtMsgs))) { scope.Complete(); return; @@ -1208,7 +1170,7 @@ namespace Umbraco.Cms.Core.Services.Implement { // delete media // triggers the deleted event (and handles the files) - DeleteLocked(scope, member); + DeleteLocked(scope, member, evtMsgs); } scope.Complete(); diff --git a/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs b/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs index 5b3e2d0a11..b6d12784c6 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; @@ -6,11 +6,12 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Services.Implement { - public class PublicAccessService : RepositoryService, IPublicAccessService + internal class PublicAccessService : RepositoryService, IPublicAccessService { private readonly IPublicAccessRepository _publicAccessRepository; @@ -114,7 +115,7 @@ namespace Umbraco.Cms.Core.Services.Implement { entry = _publicAccessRepository.GetMany().FirstOrDefault(x => x.ProtectedNodeId == content.Id); if (entry == null) - return OperationResult.Attempt.Cannot(evtMsgs); // causes rollback // causes rollback + return OperationResult.Attempt.Cannot(evtMsgs); // causes rollback var existingRule = entry.Rules.FirstOrDefault(x => x.RuleType == ruleType && x.RuleValue == ruleValue); if (existingRule == null) @@ -127,8 +128,8 @@ namespace Umbraco.Cms.Core.Services.Implement return OperationResult.Attempt.Succeed(evtMsgs, entry); } - var saveEventArgs = new SaveEventArgs(entry, evtMsgs); - if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs)) + var savingNotifiation = new PublicAccessEntrySavingNotification(entry, evtMsgs); + if (scope.Notifications.PublishCancelable(savingNotifiation)) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs, entry); @@ -138,8 +139,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.Complete(); - saveEventArgs.CanCancel = false; - scope.Events.Dispatch(Saved, this, saveEventArgs); + scope.Notifications.Publish(new PublicAccessEntrySavedNotification(entry, evtMsgs).WithStateFrom(savingNotifiation)); } return OperationResult.Attempt.Succeed(evtMsgs, entry); @@ -165,8 +165,8 @@ namespace Umbraco.Cms.Core.Services.Implement entry.RemoveRule(existingRule); - var saveEventArgs = new SaveEventArgs(entry, evtMsgs); - if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs)) + var savingNotifiation = new PublicAccessEntrySavingNotification(entry, evtMsgs); + if (scope.Notifications.PublishCancelable(savingNotifiation)) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -175,8 +175,7 @@ namespace Umbraco.Cms.Core.Services.Implement _publicAccessRepository.Save(entry); scope.Complete(); - saveEventArgs.CanCancel = false; - scope.Events.Dispatch(Saved, this, saveEventArgs); + scope.Notifications.Publish(new PublicAccessEntrySavedNotification(entry, evtMsgs).WithStateFrom(savingNotifiation)); } return OperationResult.Attempt.Succeed(evtMsgs); @@ -192,8 +191,8 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var saveEventArgs = new SaveEventArgs(entry, evtMsgs); - if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs)) + var savingNotifiation = new PublicAccessEntrySavingNotification(entry, evtMsgs); + if (scope.Notifications.PublishCancelable(savingNotifiation)) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -202,8 +201,7 @@ namespace Umbraco.Cms.Core.Services.Implement _publicAccessRepository.Save(entry); scope.Complete(); - saveEventArgs.CanCancel = false; - scope.Events.Dispatch(Saved, this, saveEventArgs); + scope.Notifications.Publish(new PublicAccessEntrySavedNotification(entry, evtMsgs).WithStateFrom(savingNotifiation)); } return OperationResult.Attempt.Succeed(evtMsgs); @@ -219,8 +217,8 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var deleteEventArgs = new DeleteEventArgs(entry, evtMsgs); - if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs)) + var deletingNotification = new PublicAccessEntryDeletingNotification(entry, evtMsgs); + if (scope.Notifications.PublishCancelable(deletingNotification)) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -229,33 +227,10 @@ namespace Umbraco.Cms.Core.Services.Implement _publicAccessRepository.Delete(entry); scope.Complete(); - deleteEventArgs.CanCancel = false; - scope.Events.Dispatch(Deleted, this, deleteEventArgs); + scope.Notifications.Publish(new PublicAccessEntryDeletedNotification(entry, evtMsgs).WithStateFrom(deletingNotification)); } return OperationResult.Attempt.Succeed(evtMsgs); } - - /// - /// Occurs before Save - /// - public static event TypedEventHandler> Saving; - - /// - /// Occurs after Save - /// - public static event TypedEventHandler> Saved; - - /// - /// Occurs before Delete - /// - public static event TypedEventHandler> Deleting; - - /// - /// Occurs after Delete - /// - public static event TypedEventHandler> Deleted; - - } } diff --git a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs index d35bcbfa50..b7b52ae616 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs @@ -14,6 +14,7 @@ using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Infrastructure.Persistence.Querying; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Services.Implement @@ -21,7 +22,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// Represents the UserService, which is an easy access to operations involving , and eventually Backoffice Users. /// - public class UserService : RepositoryService, IUserService + internal class UserService : RepositoryService, IUserService { private readonly IUserRepository _userRepository; private readonly IUserGroupRepository _userGroupRepository; @@ -109,6 +110,8 @@ namespace Umbraco.Cms.Core.Services.Implement if (username == null) throw new ArgumentNullException(nameof(username)); if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(username)); + var evtMsgs = EventMessagesFactory.Get(); + // TODO: PUT lock here!! User user; @@ -129,8 +132,8 @@ namespace Umbraco.Cms.Core.Services.Implement IsApproved = isApproved }; - var saveEventArgs = new SaveEventArgs(user); - if (scope.Events.DispatchCancelable(SavingUser, this, saveEventArgs)) + var savingNotification = new UserSavingNotification(user, evtMsgs); + if (scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return user; @@ -138,8 +141,7 @@ namespace Umbraco.Cms.Core.Services.Implement _userRepository.Save(user); - saveEventArgs.CanCancel = false; - scope.Events.Dispatch(SavedUser, this, saveEventArgs); + scope.Notifications.Publish(new UserSavedNotification(user, evtMsgs).WithStateFrom(savingNotification)); scope.Complete(); } @@ -239,10 +241,12 @@ namespace Umbraco.Cms.Core.Services.Implement } else { + var evtMsgs = EventMessagesFactory.Get(); + using (var scope = ScopeProvider.CreateScope()) { - var deleteEventArgs = new DeleteEventArgs(user); - if (scope.Events.DispatchCancelable(DeletingUser, this, deleteEventArgs)) + var deletingNotification = new UserDeletingNotification(user, evtMsgs); + if (scope.Notifications.PublishCancelable(deletingNotification)) { scope.Complete(); return; @@ -250,8 +254,7 @@ namespace Umbraco.Cms.Core.Services.Implement _userRepository.Delete(user); - deleteEventArgs.CanCancel = false; - scope.Events.Dispatch(DeletedUser, this, deleteEventArgs); + scope.Notifications.Publish(new UserDeletedNotification(user, evtMsgs).WithStateFrom(deletingNotification)); scope.Complete(); } } @@ -272,10 +275,12 @@ namespace Umbraco.Cms.Core.Services.Implement /// Default is True otherwise set to False to not raise events public void Save(IUser entity, bool raiseEvents = true) { + var evtMsgs = EventMessagesFactory.Get(); + using (var scope = ScopeProvider.CreateScope()) { - var saveEventArgs = new SaveEventArgs(entity); - if (raiseEvents && scope.Events.DispatchCancelable(SavingUser, this, saveEventArgs)) + var savingNotification = new UserSavingNotification(entity, evtMsgs); + if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return; @@ -292,8 +297,7 @@ namespace Umbraco.Cms.Core.Services.Implement _userRepository.Save(entity); if (raiseEvents) { - saveEventArgs.CanCancel = false; - scope.Events.Dispatch(SavedUser, this, saveEventArgs); + scope.Notifications.Publish(new UserSavedNotification(entity, evtMsgs).WithStateFrom(savingNotification)); } scope.Complete(); @@ -319,12 +323,14 @@ namespace Umbraco.Cms.Core.Services.Implement /// Default is True otherwise set to False to not raise events public void Save(IEnumerable entities, bool raiseEvents = true) { + var evtMsgs = EventMessagesFactory.Get(); + var entitiesA = entities.ToArray(); using (var scope = ScopeProvider.CreateScope()) { - var saveEventArgs = new SaveEventArgs(entitiesA); - if (raiseEvents && scope.Events.DispatchCancelable(SavingUser, this, saveEventArgs)) + var savingNotification = new UserSavingNotification(entitiesA, evtMsgs); + if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return; @@ -344,8 +350,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - saveEventArgs.CanCancel = false; - scope.Events.Dispatch(SavedUser, this, saveEventArgs); + scope.Notifications.Publish(new UserSavedNotification(entitiesA, evtMsgs).WithStateFrom(savingNotification)); } //commit the whole lot in one go @@ -709,14 +714,16 @@ namespace Umbraco.Cms.Core.Services.Implement if (entityIds.Length == 0) return; + var evtMsgs = EventMessagesFactory.Get(); + using (var scope = ScopeProvider.CreateScope()) { _userGroupRepository.ReplaceGroupPermissions(groupId, permissions, entityIds); scope.Complete(); var assigned = permissions.Select(p => p.ToString(CultureInfo.InvariantCulture)).ToArray(); - scope.Events.Dispatch(UserGroupPermissionsAssigned, this, - new SaveEventArgs(entityIds.Select(x => new EntityPermission(groupId, x, assigned)).ToArray(), false)); + var entityPermissions = entityIds.Select(x => new EntityPermission(groupId, x, assigned)).ToArray(); + scope.Notifications.Publish(new AssignedUserGroupPermissionsNotification(entityPermissions, evtMsgs)); } } @@ -731,14 +738,16 @@ namespace Umbraco.Cms.Core.Services.Implement if (entityIds.Length == 0) return; + var evtMsgs = EventMessagesFactory.Get(); + using (var scope = ScopeProvider.CreateScope()) { _userGroupRepository.AssignGroupPermission(groupId, permission, entityIds); scope.Complete(); var assigned = new[] { permission.ToString(CultureInfo.InvariantCulture) }; - scope.Events.Dispatch(UserGroupPermissionsAssigned, this, - new SaveEventArgs(entityIds.Select(x => new EntityPermission(groupId, x, assigned)).ToArray(), false)); + var entityPermissions = entityIds.Select(x => new EntityPermission(groupId, x, assigned)).ToArray(); + scope.Notifications.Publish(new AssignedUserGroupPermissionsNotification(entityPermissions, evtMsgs)); } } @@ -809,6 +818,8 @@ namespace Umbraco.Cms.Core.Services.Implement /// Default is True otherwise set to False to not raise events public void Save(IUserGroup userGroup, int[] userIds = null, bool raiseEvents = true) { + var evtMsgs = EventMessagesFactory.Get(); + using (var scope = ScopeProvider.CreateScope()) { // we need to figure out which users have been added / removed, for audit purposes @@ -826,9 +837,19 @@ namespace Umbraco.Cms.Core.Services.Implement removedUsers = groupIds.Except(userIds).Select(x => xGroupUsers[x]).Where(x => x.Id != 0).ToArray(); } - var saveEventArgs = new SaveEventArgs(new UserGroupWithUsers(userGroup, addedUsers, removedUsers)); + var userGroupWithUsers = new UserGroupWithUsers(userGroup, addedUsers, removedUsers); - if (raiseEvents && scope.Events.DispatchCancelable(SavingUserGroup, this, saveEventArgs)) + // this is the default/expected notification for the IUserGroup entity being saved + var savingNotification = new UserGroupSavingNotification(userGroup, evtMsgs); + if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) + { + scope.Complete(); + return; + } + + // this is an additional notification for special auditing + var savingUserGroupWithUsersNotification = new UserGroupWithUsersSavingNotification(userGroupWithUsers, evtMsgs); + if (raiseEvents && scope.Notifications.PublishCancelable(savingUserGroupWithUsersNotification)) { scope.Complete(); return; @@ -838,8 +859,8 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - saveEventArgs.CanCancel = false; - scope.Events.Dispatch(SavedUserGroup, this, saveEventArgs); + scope.Notifications.Publish(new UserGroupSavedNotification(userGroup, evtMsgs).WithStateFrom(savingNotification)); + scope.Notifications.Publish(new UserGroupWithUsersSavedNotification(userGroupWithUsers, evtMsgs).WithStateFrom(savingUserGroupWithUsersNotification)); } scope.Complete(); @@ -852,10 +873,12 @@ namespace Umbraco.Cms.Core.Services.Implement /// UserGroup to delete public void DeleteUserGroup(IUserGroup userGroup) { + var evtMsgs = EventMessagesFactory.Get(); + using (var scope = ScopeProvider.CreateScope()) { - var deleteEventArgs = new DeleteEventArgs(userGroup); - if (scope.Events.DispatchCancelable(DeletingUserGroup, this, deleteEventArgs)) + var deletingNotification = new UserGroupDeletingNotification(userGroup, evtMsgs); + if (scope.Notifications.PublishCancelable(deletingNotification)) { scope.Complete(); return; @@ -863,8 +886,7 @@ namespace Umbraco.Cms.Core.Services.Implement _userGroupRepository.Delete(userGroup); - deleteEventArgs.CanCancel = false; - scope.Events.Dispatch(DeletedUserGroup, this, deleteEventArgs); + scope.Notifications.Publish(new UserGroupDeletedNotification(userGroup, evtMsgs).WithStateFrom(deletingNotification)); scope.Complete(); } @@ -1144,49 +1166,5 @@ namespace Umbraco.Cms.Core.Services.Implement } #endregion - - /// - /// Occurs before Save - /// - public static event TypedEventHandler> SavingUser; - - /// - /// Occurs after Save - /// - public static event TypedEventHandler> SavedUser; - - /// - /// Occurs before Delete - /// - public static event TypedEventHandler> DeletingUser; - - /// - /// Occurs after Delete - /// - public static event TypedEventHandler> DeletedUser; - - /// - /// Occurs before Save - /// - public static event TypedEventHandler> SavingUserGroup; - - /// - /// Occurs after Save - /// - public static event TypedEventHandler> SavedUserGroup; - - /// - /// Occurs before Delete - /// - public static event TypedEventHandler> DeletingUserGroup; - - /// - /// Occurs after Delete - /// - public static event TypedEventHandler> DeletedUserGroup; - - // TODO: still don't know if we need this yet unless we start caching permissions, but that also means we'll need another - // event on the ContentService since there's a method there to modify node permissions too, or we can proxy events if needed. - public static event TypedEventHandler> UserGroupPermissionsAssigned; } } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/AssignedMemberRolesNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/AssignedMemberRolesNotification.cs new file mode 100644 index 0000000000..9d5d707f64 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/AssignedMemberRolesNotification.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class AssignedMemberRolesNotification : MemberRolesNotification + { + public AssignedMemberRolesNotification(int[] memberIds, string[] roles) : base(memberIds, roles) + { + + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/AssignedUserGroupPermissionsNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/AssignedUserGroupPermissionsNotification.cs new file mode 100644 index 0000000000..e0838f8c33 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/AssignedUserGroupPermissionsNotification.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models.Membership; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class AssignedUserGroupPermissionsNotification : EnumerableObjectNotification + { + public AssignedUserGroupPermissionsNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable EntityPermissions => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ExportedMemberNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ExportedMemberNotification.cs new file mode 100644 index 0000000000..449244242f --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ExportedMemberNotification.cs @@ -0,0 +1,19 @@ +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.Membership; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class ExportedMemberNotification : INotification + { + public ExportedMemberNotification(IMember member, MemberExportModel exported) + { + Member = member; + Exported = exported; + } + + public IMember Member { get; } + + public MemberExportModel Exported { get; } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MemberDeletedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MemberDeletedNotification.cs new file mode 100644 index 0000000000..41edd5371e --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MemberDeletedNotification.cs @@ -0,0 +1,15 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class MemberDeletedNotification : DeletedNotification + { + public MemberDeletedNotification(IMember target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MemberDeletingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MemberDeletingNotification.cs new file mode 100644 index 0000000000..4a4cd585c5 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MemberDeletingNotification.cs @@ -0,0 +1,20 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class MemberDeletingNotification : DeletingNotification + { + public MemberDeletingNotification(IMember target, EventMessages messages) : base(target, messages) + { + } + + public MemberDeletingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MemberRolesNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MemberRolesNotification.cs new file mode 100644 index 0000000000..2b7c9bd828 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MemberRolesNotification.cs @@ -0,0 +1,17 @@ +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public abstract class MemberRolesNotification : INotification + { + protected MemberRolesNotification(int[] memberIds, string[] roles) + { + MemberIds = memberIds; + Roles = roles; + } + + public int[] MemberIds { get; } + + public string[] Roles { get; } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MemberSavedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MemberSavedNotification.cs new file mode 100644 index 0000000000..6ea4e38870 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MemberSavedNotification.cs @@ -0,0 +1,20 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class MemberSavedNotification : SavedNotification + { + public MemberSavedNotification(IMember target, EventMessages messages) : base(target, messages) + { + } + + public MemberSavedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MemberSavingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MemberSavingNotification.cs new file mode 100644 index 0000000000..8496731304 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MemberSavingNotification.cs @@ -0,0 +1,20 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class MemberSavingNotification : SavingNotification + { + public MemberSavingNotification(IMember target, EventMessages messages) : base(target, messages) + { + } + + public MemberSavingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/PublicAccessEntryDeletedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/PublicAccessEntryDeletedNotification.cs new file mode 100644 index 0000000000..03f48dff5c --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/PublicAccessEntryDeletedNotification.cs @@ -0,0 +1,15 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class PublicAccessEntryDeletedNotification : DeletedNotification + { + public PublicAccessEntryDeletedNotification(PublicAccessEntry target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/PublicAccessEntryDeletingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/PublicAccessEntryDeletingNotification.cs new file mode 100644 index 0000000000..521a86caff --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/PublicAccessEntryDeletingNotification.cs @@ -0,0 +1,20 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class PublicAccessEntryDeletingNotification : DeletingNotification + { + public PublicAccessEntryDeletingNotification(PublicAccessEntry target, EventMessages messages) : base(target, messages) + { + } + + public PublicAccessEntryDeletingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/PublicAccessEntrySavedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/PublicAccessEntrySavedNotification.cs new file mode 100644 index 0000000000..ec1781a3d4 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/PublicAccessEntrySavedNotification.cs @@ -0,0 +1,20 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class PublicAccessEntrySavedNotification : SavedNotification + { + public PublicAccessEntrySavedNotification(PublicAccessEntry target, EventMessages messages) : base(target, messages) + { + } + + public PublicAccessEntrySavedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/PublicAccessEntrySavingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/PublicAccessEntrySavingNotification.cs new file mode 100644 index 0000000000..63f4a490a3 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/PublicAccessEntrySavingNotification.cs @@ -0,0 +1,20 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class PublicAccessEntrySavingNotification : SavingNotification + { + public PublicAccessEntrySavingNotification(PublicAccessEntry target, EventMessages messages) : base(target, messages) + { + } + + public PublicAccessEntrySavingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/RemovedMemberRolesNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/RemovedMemberRolesNotification.cs new file mode 100644 index 0000000000..bd902cb078 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/RemovedMemberRolesNotification.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class RemovedMemberRolesNotification : MemberRolesNotification + { + public RemovedMemberRolesNotification(int[] memberIds, string[] roles) : base(memberIds, roles) + { + + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UserDeletedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UserDeletedNotification.cs new file mode 100644 index 0000000000..3173647652 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/UserDeletedNotification.cs @@ -0,0 +1,15 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models.Membership; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class UserDeletedNotification : DeletedNotification + { + public UserDeletedNotification(IUser target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UserDeletingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UserDeletingNotification.cs new file mode 100644 index 0000000000..1f57755a5a --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/UserDeletingNotification.cs @@ -0,0 +1,20 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models.Membership; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class UserDeletingNotification : DeletingNotification + { + public UserDeletingNotification(IUser target, EventMessages messages) : base(target, messages) + { + } + + public UserDeletingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UserGroupDeletedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UserGroupDeletedNotification.cs new file mode 100644 index 0000000000..a9c51dc72d --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/UserGroupDeletedNotification.cs @@ -0,0 +1,15 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models.Membership; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class UserGroupDeletedNotification : DeletedNotification + { + public UserGroupDeletedNotification(IUserGroup target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UserGroupDeletingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UserGroupDeletingNotification.cs new file mode 100644 index 0000000000..c176e55456 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/UserGroupDeletingNotification.cs @@ -0,0 +1,20 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models.Membership; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class UserGroupDeletingNotification : DeletingNotification + { + public UserGroupDeletingNotification(IUserGroup target, EventMessages messages) : base(target, messages) + { + } + + public UserGroupDeletingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UserGroupSavedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UserGroupSavedNotification.cs new file mode 100644 index 0000000000..b542b35b26 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/UserGroupSavedNotification.cs @@ -0,0 +1,20 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models.Membership; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class UserGroupSavedNotification : SavedNotification + { + public UserGroupSavedNotification(IUserGroup target, EventMessages messages) : base(target, messages) + { + } + + public UserGroupSavedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UserGroupSavingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UserGroupSavingNotification.cs new file mode 100644 index 0000000000..136b2da6a5 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/UserGroupSavingNotification.cs @@ -0,0 +1,20 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models.Membership; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class UserGroupSavingNotification : SavingNotification + { + public UserGroupSavingNotification(IUserGroup target, EventMessages messages) : base(target, messages) + { + } + + public UserGroupSavingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UserGroupWithUsersSavedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UserGroupWithUsersSavedNotification.cs new file mode 100644 index 0000000000..22e0d67d06 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/UserGroupWithUsersSavedNotification.cs @@ -0,0 +1,19 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class UserGroupWithUsersSavedNotification : SavedNotification + { + public UserGroupWithUsersSavedNotification(UserGroupWithUsers target, EventMessages messages) : base(target, messages) + { + } + + public UserGroupWithUsersSavedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UserGroupWithUsersSavingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UserGroupWithUsersSavingNotification.cs new file mode 100644 index 0000000000..c61087038a --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/UserGroupWithUsersSavingNotification.cs @@ -0,0 +1,19 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class UserGroupWithUsersSavingNotification : SavingNotification + { + public UserGroupWithUsersSavingNotification(UserGroupWithUsers target, EventMessages messages) : base(target, messages) + { + } + + public UserGroupWithUsersSavingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UserSavedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UserSavedNotification.cs new file mode 100644 index 0000000000..850085ae90 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/UserSavedNotification.cs @@ -0,0 +1,20 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models.Membership; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class UserSavedNotification : SavedNotification + { + public UserSavedNotification(IUser target, EventMessages messages) : base(target, messages) + { + } + + public UserSavedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UserSavingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UserSavingNotification.cs new file mode 100644 index 0000000000..0b062c33f1 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/UserSavingNotification.cs @@ -0,0 +1,20 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models.Membership; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class UserSavingNotification : SavingNotification + { + public UserSavingNotification(IUser target, EventMessages messages) : base(target, messages) + { + } + + public UserSavingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.PublishedCache.NuCache/Compose/NotificationsComposer.cs b/src/Umbraco.PublishedCache.NuCache/Compose/NotificationsComposer.cs index df84759793..5ea0cc0678 100644 --- a/src/Umbraco.PublishedCache.NuCache/Compose/NotificationsComposer.cs +++ b/src/Umbraco.PublishedCache.NuCache/Compose/NotificationsComposer.cs @@ -1,17 +1,12 @@ -using Umbraco.Cms.Core.Compose; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Infrastructure.Services.Notifications; namespace Umbraco.Cms.Infrastructure.PublishedCache.Compose { - public sealed class NotificationsComposer : ComponentComposer, ICoreComposer + public sealed class NotificationsComposer : ICoreComposer { - public override void Compose(IUmbracoBuilder builder) - { - base.Compose(builder); - + public void Compose(IUmbracoBuilder builder) => builder.AddNotificationHandler(); - } } } diff --git a/src/Umbraco.Tests.Integration/Cache/DistributedCacheBinderTests.cs b/src/Umbraco.Tests.Integration/Cache/DistributedCacheBinderTests.cs index 1da6261bdc..2d9bcf2c9d 100644 --- a/src/Umbraco.Tests.Integration/Cache/DistributedCacheBinderTests.cs +++ b/src/Umbraco.Tests.Integration/Cache/DistributedCacheBinderTests.cs @@ -42,11 +42,6 @@ namespace Umbraco.Cms.Tests.Integration.Cache var definitions = new IEventDefinition[] { - new EventDefinition>(null, UserService, new SaveEventArgs(Enumerable.Empty())), - new EventDefinition>(null, UserService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, UserService, new SaveEventArgs(Enumerable.Empty())), - new EventDefinition>(null, UserService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, DataTypeService, new SaveEventArgs(Enumerable.Empty())), new EventDefinition>(null, DataTypeService, new DeleteEventArgs(Enumerable.Empty())), @@ -70,16 +65,10 @@ namespace Umbraco.Cms.Tests.Integration.Cache new EventDefinition>(null, MacroService, new SaveEventArgs(Enumerable.Empty())), new EventDefinition>(null, MacroService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, MemberService, new SaveEventArgs(Enumerable.Empty())), - new EventDefinition>(null, MemberService, new DeleteEventArgs(Enumerable.Empty())), - // not managed //new EventDefinition>(null, ContentService, new SaveEventArgs(Enumerable.Empty()), "SavedBlueprint"), //new EventDefinition>(null, ContentService, new DeleteEventArgs(Enumerable.Empty()), "DeletedBlueprint"), - new EventDefinition>(null, PublicAccessService, new SaveEventArgs(Enumerable.Empty())), - new EventDefinition>(null, PublicAccessService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, RelationService, new SaveEventArgs(Enumerable.Empty())), new EventDefinition>(null, RelationService, new DeleteEventArgs(Enumerable.Empty())), diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopedRepositoryTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopedRepositoryTests.cs index 43a3e91b35..3f7fc54493 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopedRepositoryTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopedRepositoryTests.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; -using Microsoft.Extensions.Logging; using NUnit.Framework; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.DependencyInjection; @@ -14,7 +13,6 @@ using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; using Umbraco.Cms.Core.Sync; -using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.PublishedCache; using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Cms.Infrastructure.Sync; @@ -56,6 +54,8 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping .AddNotificationHandler() .AddNotificationHandler() .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() .AddNotificationHandler() .AddNotificationHandler(); builder.AddNotificationHandler(); @@ -78,18 +78,18 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping var user = (IUser)new User(GlobalSettings, "name", "email", "username", "rawPassword"); service.Save(user); - // global cache contains the entity + // User has been saved so the cache has been cleared of it var globalCached = (IUser)globalCache.Get(GetCacheIdKey(user.Id), () => null); + Assert.IsNull(globalCached); + // Get user again to load it into the cache again, this also ensure we don't modify the one that's in the cache. + user = service.GetUserById(user.Id); + + // global cache contains the entity + globalCached = (IUser)globalCache.Get(GetCacheIdKey(user.Id), () => null); Assert.IsNotNull(globalCached); Assert.AreEqual(user.Id, globalCached.Id); Assert.AreEqual("name", globalCached.Name); - // get user again - else we'd modify the one that's in the cache - user = service.GetUserById(user.Id); - - _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(ServerMessenger, CacheRefresherCollection), GetRequiredService(), GetRequiredService>()); - _distributedCacheBinder.BindEvents(true); - Assert.IsNull(scopeProvider.AmbientScope); using (IScope scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped)) {