From f5f6824629a2622a5e27b02399caf0d87c079b0e Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 18 Feb 2021 14:07:07 +0100 Subject: [PATCH 01/42] Allow serving App_Plugins files to Smidge for minification --- .../UmbracoTestServerTestBase.cs | 5 +-- .../Testing/UmbracoIntegrationTest.cs | 2 +- .../UmbracoBuilderExtensions.cs | 5 +-- .../UmbracoBuilderExtensions.cs | 32 +++++++++++++++++-- src/Umbraco.Web.UI.NetCore/Startup.cs | 2 +- 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs index 2434b2b8fb..1695c359bb 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -139,9 +139,10 @@ namespace Umbraco.Tests.Integration.TestServerTest public override void ConfigureServices(IServiceCollection services) { services.AddTransient(); + IWebHostEnvironment webHostEnvironment = TestHelper.GetWebHostEnvironment(); TypeLoader typeLoader = services.AddTypeLoader( GetType().Assembly, - TestHelper.GetWebHostEnvironment(), + webHostEnvironment, TestHelper.GetHostingEnvironment(), TestHelper.ConsoleLoggerFactory, AppCaches.NoCache, @@ -154,7 +155,7 @@ namespace Umbraco.Tests.Integration.TestServerTest .AddConfiguration() .AddUmbracoCore() .AddWebComponents() - .AddRuntimeMinifier() + .AddRuntimeMinifier(webHostEnvironment) .AddBackOfficeCore() .AddBackOfficeAuthentication() .AddBackOfficeIdentity() diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index 2a1e9a9298..cc56b250ac 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -215,7 +215,7 @@ namespace Umbraco.Tests.Integration.Testing builder.AddConfiguration() .AddUmbracoCore() .AddWebComponents() - .AddRuntimeMinifier() + .AddRuntimeMinifier(webHostEnvironment) .AddBackOfficeAuthentication() .AddBackOfficeIdentity() .AddTestServices(TestHelper, GetAppCaches()); diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs index 6587f3c6e4..9cbcdf7054 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs @@ -20,6 +20,7 @@ using Umbraco.Web.BackOffice.Trees; using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.DependencyInjection; using Umbraco.Web.WebAssets; +using IWebHostEnvironment = Microsoft.AspNetCore.Hosting.IWebHostEnvironment; namespace Umbraco.Web.BackOffice.DependencyInjection { @@ -31,11 +32,11 @@ namespace Umbraco.Web.BackOffice.DependencyInjection /// /// Adds all required components to run the Umbraco back office /// - public static IUmbracoBuilder AddBackOffice(this IUmbracoBuilder builder) => builder + public static IUmbracoBuilder AddBackOffice(this IUmbracoBuilder builder, IWebHostEnvironment webHostEnvironment) => builder .AddConfiguration() .AddUmbracoCore() .AddWebComponents() - .AddRuntimeMinifier() + .AddRuntimeMinifier(webHostEnvironment) .AddBackOfficeCore() .AddBackOfficeAuthentication() .AddBackOfficeIdentity() diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index e8097335d6..7c64d4adbd 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -12,7 +12,9 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; using Serilog; using Smidge; using Smidge.Nuglify; @@ -207,9 +209,11 @@ namespace Umbraco.Web.Common.DependencyInjection /// /// Add runtime minifier support for Umbraco /// - public static IUmbracoBuilder AddRuntimeMinifier(this IUmbracoBuilder builder) + public static IUmbracoBuilder AddRuntimeMinifier(this IUmbracoBuilder builder, IWebHostEnvironment webHostEnvironment) { - builder.Services.AddSmidge(builder.Config.GetSection(Core.Constants.Configuration.ConfigRuntimeMinification)); + var smidgePhysicalFileProvider = new SmidgePhysicalFileProvider(webHostEnvironment.ContentRootFileProvider, webHostEnvironment.WebRootFileProvider); + + builder.Services.AddSmidge(builder.Config.GetSection(Core.Constants.Configuration.ConfigRuntimeMinification), smidgePhysicalFileProvider); builder.Services.AddSmidgeNuglify(); return builder; @@ -394,5 +398,29 @@ namespace Umbraco.Web.Common.DependencyInjection return new AspNetCoreHostingEnvironment(wrappedHostingSettings,wrappedWebRoutingSettings, webHostEnvironment); } + + /// + /// This file provider lets us serve physical files to Smidge for minification from both wwwroot and App_Plugins (which is outside wwwroot). + /// This file provider is NOT intended for use anywhere else, as it exposes files from the content root. + /// + private class SmidgePhysicalFileProvider : IFileProvider + { + private readonly IFileProvider _contentRootFileProvider; + private readonly IFileProvider _webRooFileProvider; + + public SmidgePhysicalFileProvider(IFileProvider contentRootFileProvider, IFileProvider webRooFileProvider) + { + _contentRootFileProvider = contentRootFileProvider; + _webRooFileProvider = webRooFileProvider; + } + + public IFileInfo GetFileInfo(string subpath) => subpath.InvariantStartsWith(Core.Constants.SystemDirectories.AppPlugins) + ? _contentRootFileProvider.GetFileInfo(subpath) + : _webRooFileProvider.GetFileInfo(subpath); + + public IDirectoryContents GetDirectoryContents(string subpath) => throw new NotSupportedException(); + + public IChangeToken Watch(string filter) => throw new NotSupportedException(); + } } } diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs index c3d3d18451..cbb8bec2da 100644 --- a/src/Umbraco.Web.UI.NetCore/Startup.cs +++ b/src/Umbraco.Web.UI.NetCore/Startup.cs @@ -46,7 +46,7 @@ namespace Umbraco.Web.UI.NetCore { #pragma warning disable IDE0022 // Use expression body for methods services.AddUmbraco(_env, _config) - .AddBackOffice() + .AddBackOffice(_env) .AddWebsite() .AddComposers() .Build(); From 8dddf0c987d47d093c4216b3a5892d56a122bfe3 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Fri, 19 Feb 2021 07:19:16 +0100 Subject: [PATCH 02/42] Fix build error after merge --- .../DependencyInjection/UmbracoBuilderExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index c3b9bb89cc..b51e03b6ac 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -415,7 +415,7 @@ namespace Umbraco.Extensions _webRooFileProvider = webRooFileProvider; } - public IFileInfo GetFileInfo(string subpath) => subpath.InvariantStartsWith(Core.Constants.SystemDirectories.AppPlugins) + public IFileInfo GetFileInfo(string subpath) => subpath.InvariantStartsWith(Constants.SystemDirectories.AppPlugins) ? _contentRootFileProvider.GetFileInfo(subpath) : _webRooFileProvider.GetFileInfo(subpath); From 90031b83b065a5857c3e3a1d6bc87fd9f53f98b0 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Fri, 26 Feb 2021 16:37:34 +0100 Subject: [PATCH 03/42] Replaced static events in BackOfficeUserManager with IEventAggregator notifications and removed various unused or obsolete events (i.e. user invitation events) --- .../Security/IBackOfficeUserManager.cs | 6 + .../Security/IUmbracoUserManager.cs | 14 -- .../Security/SignOutAuditEventArgs.cs | 19 -- .../Security/SignOutSuccessResult.cs | 7 + .../Security/UserInviteEventArgs.cs | 36 ---- .../MemberIdentityUserManagerTests.cs | 1 - .../Controllers/AuthenticationController.cs | 6 +- .../Controllers/UsersController.cs | 37 +--- .../ServiceCollectionExtensions.cs | 1 - .../UmbracoBuilderExtensions.cs | 6 + .../BackOfficeApplicationBuilderExtensions.cs | 9 - .../Security/BackOfficeSignInManager.cs | 8 +- .../Security/BackOfficeUserManagerAuditer.cs | 95 +++------- .../Security/BackOfficeUserManager.cs | 165 +++++++----------- .../Security/MemberManager.cs | 35 ---- .../UserForgotPasswordChangedNotification.cs | 9 + ...UserForgotPasswordRequestedNotification.cs | 9 + .../Security/UserLockedNotification.cs | 9 + .../Security/UserLoginFailedNotification.cs | 9 + ...erLoginRequiresVerificationNotification.cs | 9 + .../Security/UserLoginSuccessNotification.cs | 9 + .../Security/UserLogoutSuccessNotification.cs | 11 ++ .../Security/UserNotification.cs | 36 ++++ .../UserPasswordChangedNotification.cs | 9 + .../UserResetAccessFailedCountNotification.cs | 9 + .../Security/UserUnlockedNotification.cs | 9 + 26 files changed, 248 insertions(+), 325 deletions(-) delete mode 100644 src/Umbraco.Infrastructure/Security/SignOutAuditEventArgs.cs create mode 100644 src/Umbraco.Infrastructure/Security/SignOutSuccessResult.cs delete mode 100644 src/Umbraco.Infrastructure/Security/UserInviteEventArgs.cs create mode 100644 src/Umbraco.Web.Common/Security/UserForgotPasswordChangedNotification.cs create mode 100644 src/Umbraco.Web.Common/Security/UserForgotPasswordRequestedNotification.cs create mode 100644 src/Umbraco.Web.Common/Security/UserLockedNotification.cs create mode 100644 src/Umbraco.Web.Common/Security/UserLoginFailedNotification.cs create mode 100644 src/Umbraco.Web.Common/Security/UserLoginRequiresVerificationNotification.cs create mode 100644 src/Umbraco.Web.Common/Security/UserLoginSuccessNotification.cs create mode 100644 src/Umbraco.Web.Common/Security/UserLogoutSuccessNotification.cs create mode 100644 src/Umbraco.Web.Common/Security/UserNotification.cs create mode 100644 src/Umbraco.Web.Common/Security/UserPasswordChangedNotification.cs create mode 100644 src/Umbraco.Web.Common/Security/UserResetAccessFailedCountNotification.cs create mode 100644 src/Umbraco.Web.Common/Security/UserUnlockedNotification.cs diff --git a/src/Umbraco.Infrastructure/Security/IBackOfficeUserManager.cs b/src/Umbraco.Infrastructure/Security/IBackOfficeUserManager.cs index d47a471426..2a357430ed 100644 --- a/src/Umbraco.Infrastructure/Security/IBackOfficeUserManager.cs +++ b/src/Umbraco.Infrastructure/Security/IBackOfficeUserManager.cs @@ -1,3 +1,6 @@ +using System.Security.Principal; +using Umbraco.Cms.Infrastructure.Security; + namespace Umbraco.Cms.Core.Security { /// @@ -5,5 +8,8 @@ namespace Umbraco.Cms.Core.Security /// public interface IBackOfficeUserManager : IUmbracoUserManager { + void NotifyForgotPasswordRequested(IPrincipal currentUser, string userId); + void NotifyForgotPasswordChanged(IPrincipal currentUser, string userId); + SignOutSuccessResult NotifyLogoutSuccess(IPrincipal currentUser, string userId); } } diff --git a/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs b/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs index afe828de3e..33c828b6bd 100644 --- a/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs +++ b/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs @@ -1,12 +1,9 @@ using System; using System.Collections.Generic; using System.Security.Claims; -using System.Security.Principal; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; -using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Models.Identity; -using Umbraco.Cms.Core.Models.Membership; namespace Umbraco.Cms.Core.Security { @@ -386,16 +383,5 @@ namespace Umbraco.Cms.Core.Security /// A user can only support a phone number if the BackOfficeUserStore is replaced with another that implements IUserPhoneNumberStore /// Task GetPhoneNumberAsync(TUser user); - - // TODO: These are raised from outside the signinmanager and usermanager in the auth and user controllers, - // let's see if there's a way to avoid that and only have these called within signinmanager and usermanager - // which means we can remove these from the interface (things like invite seems like they cannot be moved) - // TODO: When we change to not having the crappy static events this will need to be revisited - void RaiseForgotPasswordRequestedEvent(IPrincipal currentUser, string userId); - void RaiseForgotPasswordChangedSuccessEvent(IPrincipal currentUser, string userId); - SignOutAuditEventArgs RaiseLogoutSuccessEvent(IPrincipal currentUser, string userId); - UserInviteEventArgs RaiseSendingUserInvite(IPrincipal currentUser, UserInvite invite, IUser createdUser); - bool HasSendingUserInviteEventHandler { get; } - } } diff --git a/src/Umbraco.Infrastructure/Security/SignOutAuditEventArgs.cs b/src/Umbraco.Infrastructure/Security/SignOutAuditEventArgs.cs deleted file mode 100644 index 5e86d48d5d..0000000000 --- a/src/Umbraco.Infrastructure/Security/SignOutAuditEventArgs.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Umbraco.Cms.Core.Security -{ - /// - /// Event args used when signing out - /// - public class SignOutAuditEventArgs : IdentityAuditEventArgs - { - public SignOutAuditEventArgs(AuditEvent action, string ipAddress, string comment = null, string performingUser = Cms.Core.Constants.Security.SuperUserIdAsString, string affectedUser = Cms.Core.Constants.Security.SuperUserIdAsString) - : base(action, ipAddress, performingUser, comment, affectedUser, null) - { - } - - /// - /// Allows event handlers to set a GET absolute URL to be redirected to after successful logout out of the back office. This - /// can be used for external login providers. - /// - public string SignOutRedirectUrl { get; set; } - } -} diff --git a/src/Umbraco.Infrastructure/Security/SignOutSuccessResult.cs b/src/Umbraco.Infrastructure/Security/SignOutSuccessResult.cs new file mode 100644 index 0000000000..f1b4688505 --- /dev/null +++ b/src/Umbraco.Infrastructure/Security/SignOutSuccessResult.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Cms.Infrastructure.Security +{ + public class SignOutSuccessResult + { + public string SignOutRedirectUrl { get; set; } + } +} diff --git a/src/Umbraco.Infrastructure/Security/UserInviteEventArgs.cs b/src/Umbraco.Infrastructure/Security/UserInviteEventArgs.cs deleted file mode 100644 index 2d21b03187..0000000000 --- a/src/Umbraco.Infrastructure/Security/UserInviteEventArgs.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Umbraco.Cms.Core.Models.ContentEditing; -using Umbraco.Cms.Core.Models.Membership; - -namespace Umbraco.Cms.Core.Security -{ - public class UserInviteEventArgs : IdentityAuditEventArgs - { - public UserInviteEventArgs(string ipAddress, string performingUser, UserInvite invitedUser, IUser localUser, string comment = null) - : base(AuditEvent.SendingUserInvite, ipAddress, performingUser, comment, string.Intern(localUser.Id.ToString()), localUser.Name) - { - InvitedUser = invitedUser ?? throw new System.ArgumentNullException(nameof(invitedUser)); - User = localUser; - } - - /// - /// The model used to invite the user - /// - public UserInvite InvitedUser { get; } - - /// - /// If event handler sets this to true it indicates that Umbraco will no try to send the invite itself - /// - public bool InviteHandled { get; set; } - - /// - /// The local user that has been created that is pending the invite - /// - public IUser User { get; } - - /// - /// if set to true will show the edit user button in the UI, else it will not be shown - /// - public bool ShowUserResult { get; set; } - - } -} diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberIdentityUserManagerTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberIdentityUserManagerTests.cs index b88189a08b..cedb8ba684 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberIdentityUserManagerTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberIdentityUserManagerTests.cs @@ -70,7 +70,6 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security pwdValidators, new BackOfficeIdentityErrorDescriber(), _mockServiceProviders.Object, - new Mock().Object, new Mock>>().Object, _mockPasswordConfiguration.Object); diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs index 9bf77abba2..1e5f094471 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs @@ -390,7 +390,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers await _emailSender.SendAsync(mailMessage); - _userManager.RaiseForgotPasswordRequestedEvent(User, user.Id.ToString()); + _userManager.NotifyForgotPasswordRequested(User, user.Id.ToString()); } } @@ -554,7 +554,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } } - _userManager.RaiseForgotPasswordChangedSuccessEvent(User, model.UserId.ToString()); + _userManager.NotifyForgotPasswordChanged(User, model.UserId.ToString()); return Ok(); } @@ -578,7 +578,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers _logger.LogInformation("User {UserName} from IP address {RemoteIpAddress} has logged out", User.Identity == null ? "UNKNOWN" : User.Identity.Name, HttpContext.Connection.RemoteIpAddress); var userId = result.Principal.Identity.GetUserId(); - var args = _userManager.RaiseLogoutSuccessEvent(User, userId); + var args = _userManager.NotifyLogoutSuccess(User, userId); if (!args.SignOutRedirectUrl.IsNullOrWhiteSpace()) { return new ObjectResult(new diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs index 06c04211d3..1e91376281 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs @@ -443,7 +443,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return new ValidationErrorResult(new SimpleValidationModel(ModelState.ToErrorDictionary())); } - if (!_emailSender.CanSendRequiredEmail() && !_userManager.HasSendingUserInviteEventHandler) + if (!_emailSender.CanSendRequiredEmail()) { return new ValidationErrorResult("No Email server is configured"); } @@ -482,39 +482,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers _userService.Save(user); var display = _umbracoMapper.Map(user); - UserInviteEventArgs inviteArgs; - - try - { - inviteArgs = _userManager.RaiseSendingUserInvite(User, userSave, user); - } - catch (Exception ex) - { - _logger.LogError(ex, "An error occurred in a custom event handler while inviting the user"); - return ValidationErrorResult.CreateNotificationValidationErrorResult($"An error occurred inviting the user (check logs for more info): {ex.Message}"); - } - - // If the event is handled then no need to send the email - if (inviteArgs.InviteHandled) - { - // if no user result was created then map the minimum args manually for the UI - if (!inviteArgs.ShowUserResult) - { - display = new UserDisplay - { - Name = userSave.Name, - Email = userSave.Email, - Username = userSave.Username - }; - } - } - else - { - //send the email - - await SendUserInviteEmailAsync(display, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Name, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Email, user, userSave.Message); - - } + //send the email + await SendUserInviteEmailAsync(display, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Name, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Email, user, userSave.Message); display.AddSuccessNotification(_localizedTextService.Localize("speechBubbles/resendInviteHeader"), _localizedTextService.Localize("speechBubbles/resendInviteSuccess", new[] { user.Name })); return display; diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/ServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/ServiceCollectionExtensions.cs index 5ebada2c98..19d6643824 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/ServiceCollectionExtensions.cs @@ -61,7 +61,6 @@ namespace Umbraco.Extensions services.TryAddScoped(); services.TryAddSingleton(); services.TryAddSingleton(); - services.TryAddSingleton(); /* * IdentityBuilderExtensions.AddUserManager adds UserManager to service collection diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs index e28c8e4196..4fc0584f53 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs @@ -20,6 +20,7 @@ using Umbraco.Cms.Web.BackOffice.Security; using Umbraco.Cms.Web.BackOffice.Services; using Umbraco.Cms.Web.BackOffice.Trees; using Umbraco.Cms.Web.Common.Authorization; +using Umbraco.Cms.Web.Common.Security; namespace Umbraco.Extensions { @@ -83,6 +84,11 @@ namespace Umbraco.Extensions builder.Services.AddUnique(); builder.Services.AddUnique(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + return builder; } diff --git a/src/Umbraco.Web.BackOffice/Extensions/BackOfficeApplicationBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/BackOfficeApplicationBuilderExtensions.cs index d63deda88a..bfa56653a1 100644 --- a/src/Umbraco.Web.BackOffice/Extensions/BackOfficeApplicationBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/BackOfficeApplicationBuilderExtensions.cs @@ -20,8 +20,6 @@ namespace Umbraco.Extensions throw new ArgumentNullException(nameof(app)); } - app.UseBackOfficeUserManagerAuditing(); - if (!app.UmbracoCanBoot()) { return app; @@ -55,12 +53,5 @@ namespace Umbraco.Extensions return app; } - - private static IApplicationBuilder UseBackOfficeUserManagerAuditing(this IApplicationBuilder app) - { - var auditer = app.ApplicationServices.GetRequiredService(); - auditer.Start(); - return app; - } } } diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs index d9ebdfaae6..03ebb1aa45 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeSignInManager.cs @@ -24,7 +24,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security // borrowed from https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs private const string UmbracoSignInMgrXsrfKey = "XsrfId"; - private BackOfficeUserManager _userManager; + private readonly BackOfficeUserManager _userManager; private readonly IBackOfficeExternalLoginProviders _externalLogins; private readonly GlobalSettings _globalSettings; @@ -429,17 +429,17 @@ namespace Umbraco.Cms.Web.BackOffice.Security Logger.LogInformation("User: {UserName} logged in from IP address {IpAddress}", username, Context.Connection.RemoteIpAddress); if (user != null) { - _userManager.RaiseLoginSuccessEvent(Context.User, user.Id); + _userManager.NotifyLoginSuccess(Context.User, user.Id); } } else if (result.IsLockedOut) { - _userManager.RaiseAccountLockedEvent(Context.User, user.Id); + _userManager.NotifyAccountLocked(Context.User, user.Id); Logger.LogInformation("Login attempt failed for username {UserName} from IP address {IpAddress}, the user is locked", username, Context.Connection.RemoteIpAddress); } else if (result.RequiresTwoFactor) { - _userManager.RaiseLoginRequiresVerificationEvent(Context.User, user.Id); + _userManager.NotifyLoginRequiresVerification(Context.User, user.Id); Logger.LogInformation("Login attempt requires verification for username {UserName} from IP address {IpAddress}", username, Context.Connection.RemoteIpAddress); } else if (!result.Succeeded || result.IsNotAllowed) diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs index 6e67ee84ba..40b9cf81a7 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs @@ -1,9 +1,8 @@ using System; -using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Compose; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Web.Common.Security; using Umbraco.Extensions; @@ -11,38 +10,47 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Web.BackOffice.Security { /// - /// Binds to events to write audit logs for the + /// Binds to notifications to write audit logs for the /// - internal class BackOfficeUserManagerAuditer : IDisposable + internal class BackOfficeUserManagerAuditer : + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler { private readonly IAuditService _auditService; private readonly IUserService _userService; private readonly GlobalSettings _globalSettings; - private bool _disposedValue; - public BackOfficeUserManagerAuditer(IAuditService auditService, IUserService userService, IOptions globalSettings) + public BackOfficeUserManagerAuditer(IAuditService auditService, IUserService userService, GlobalSettings globalSettings) { _auditService = auditService; _userService = userService; - _globalSettings = globalSettings.Value; + _globalSettings = globalSettings; } - /// - /// Binds to events to start auditing - /// - public void Start() + public void Handle(UserLoginSuccessNotification notification) { - // NOTE: This was migrated as-is from v8 including these missing entries - // TODO: See note about static events in BackOfficeUserManager - BackOfficeUserManager.ForgotPasswordRequested += OnForgotPasswordRequest; - BackOfficeUserManager.ForgotPasswordChangedSuccess += OnForgotPasswordChange; - BackOfficeUserManager.LoginFailed += OnLoginFailed; - BackOfficeUserManager.LoginSuccess += OnLoginSuccess; - BackOfficeUserManager.LogoutSuccess += OnLogoutSuccess; - BackOfficeUserManager.PasswordChanged += OnPasswordChanged; - BackOfficeUserManager.PasswordReset += OnPasswordReset; + var performingUser = GetPerformingUser(notification.PerformingUserId); + WriteAudit(performingUser, notification.AffectedUserId, notification.IpAddress, "umbraco/user/sign-in/login", "login success"); } + public void Handle(UserLogoutSuccessNotification notification) + { + var performingUser = GetPerformingUser(notification.PerformingUserId); + WriteAudit(performingUser, notification.AffectedUserId, notification.IpAddress, "umbraco/user/sign-in/logout", "logout success"); + } + + public void Handle(UserLoginFailedNotification notification) => + WriteAudit(notification.PerformingUserId, "0", notification.IpAddress, "umbraco/user/sign-in/failed", "login failed", ""); + + public void Handle(UserForgotPasswordRequestedNotification notification) => + WriteAudit(notification.PerformingUserId, notification.AffectedUserId, notification.IpAddress, "umbraco/user/password/forgot/request", "password forgot/request"); + + public void Handle(UserForgotPasswordChangedNotification notification) => + WriteAudit(notification.PerformingUserId, notification.AffectedUserId, notification.IpAddress, "umbraco/user/password/forgot/change", "password forgot/change"); + private IUser GetPerformingUser(string userId) { if (!int.TryParse(userId, out int asInt)) @@ -56,28 +64,6 @@ namespace Umbraco.Cms.Web.BackOffice.Security private static string FormatEmail(IMembershipUser user) => user == null ? string.Empty : user.Email.IsNullOrWhiteSpace() ? "" : $"<{user.Email}>"; - private void OnLoginSuccess(object sender, IdentityAuditEventArgs args) - { - var performingUser = GetPerformingUser(args.PerformingUser); - WriteAudit(performingUser, args.AffectedUser, args.IpAddress, "umbraco/user/sign-in/login", "login success"); - } - - private void OnLogoutSuccess(object sender, IdentityAuditEventArgs args) - { - IUser performingUser = GetPerformingUser(args.PerformingUser); - WriteAudit(performingUser, args.AffectedUser, args.IpAddress, "umbraco/user/sign-in/logout", "logout success"); - } - - private void OnPasswordReset(object sender, IdentityAuditEventArgs args) => WriteAudit(args.PerformingUser, args.AffectedUser, args.IpAddress, "umbraco/user/password/reset", "password reset"); - - private void OnPasswordChanged(object sender, IdentityAuditEventArgs args) => WriteAudit(args.PerformingUser, args.AffectedUser, args.IpAddress, "umbraco/user/password/change", "password change"); - - private void OnLoginFailed(object sender, IdentityAuditEventArgs args) => WriteAudit(args.PerformingUser, "0", args.IpAddress, "umbraco/user/sign-in/failed", "login failed", affectedDetails: ""); - - private void OnForgotPasswordChange(object sender, IdentityAuditEventArgs args) => WriteAudit(args.PerformingUser, args.AffectedUser, args.IpAddress, "umbraco/user/password/forgot/change", "password forgot/change"); - - private void OnForgotPasswordRequest(object sender, IdentityAuditEventArgs args) => WriteAudit(args.PerformingUser, args.AffectedUser, args.IpAddress, "umbraco/user/password/forgot/request", "password forgot/request"); - private void WriteAudit(string performingId, string affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null) { IUser performingUser = null; @@ -137,30 +123,5 @@ namespace Umbraco.Cms.Web.BackOffice.Security eventType, eventDetails); } - - protected virtual void Dispose(bool disposing) - { - if (!_disposedValue) - { - if (disposing) - { - BackOfficeUserManager.ForgotPasswordRequested -= OnForgotPasswordRequest; - BackOfficeUserManager.ForgotPasswordChangedSuccess -= OnForgotPasswordChange; - BackOfficeUserManager.LoginFailed -= OnLoginFailed; - BackOfficeUserManager.LoginSuccess -= OnLoginSuccess; - BackOfficeUserManager.LogoutSuccess -= OnLogoutSuccess; - BackOfficeUserManager.PasswordChanged -= OnPasswordChanged; - BackOfficeUserManager.PasswordReset -= OnPasswordReset; - } - _disposedValue = true; - } - } - - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } } } diff --git a/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs b/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs index ca7acb7a4e..4fed85121a 100644 --- a/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs +++ b/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs @@ -8,10 +8,10 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Models.ContentEditing; -using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Net; using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Infrastructure.Security; using Umbraco.Extensions; namespace Umbraco.Cms.Web.Common.Security @@ -19,6 +19,7 @@ namespace Umbraco.Cms.Web.Common.Security public class BackOfficeUserManager : UmbracoUserManager, IBackOfficeUserManager { private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IEventAggregator _eventAggregator; public BackOfficeUserManager( IIpResolver ipResolver, @@ -31,10 +32,12 @@ namespace Umbraco.Cms.Web.Common.Security IServiceProvider services, IHttpContextAccessor httpContextAccessor, ILogger> logger, - IOptions passwordConfiguration) + IOptions passwordConfiguration, + IEventAggregator eventAggregator) : base(ipResolver, store, optionsAccessor, passwordHasher, userValidators, passwordValidators, errors, services, logger, passwordConfiguration) { _httpContextAccessor = httpContextAccessor; + _eventAggregator = eventAggregator; } /// @@ -109,7 +112,7 @@ namespace Umbraco.Cms.Web.Common.Security // Slightly confusing: this will return a Success if we successfully update the AccessFailed count if (result.Succeeded) { - RaiseLoginFailedEvent(_httpContextAccessor.HttpContext?.User, user.Id); + NotifyLoginFailed(_httpContextAccessor.HttpContext?.User, user.Id); } return result; @@ -120,7 +123,7 @@ namespace Umbraco.Cms.Web.Common.Security IdentityResult result = await base.ChangePasswordWithResetAsync(userId, token, newPassword); if (result.Succeeded) { - RaisePasswordChangedEvent(_httpContextAccessor.HttpContext?.User, userId); + NotifyPasswordChanged(_httpContextAccessor.HttpContext?.User, userId); } return result; @@ -131,7 +134,7 @@ namespace Umbraco.Cms.Web.Common.Security IdentityResult result = await base.ChangePasswordAsync(user, currentPassword, newPassword); if (result.Succeeded) { - RaisePasswordChangedEvent(_httpContextAccessor.HttpContext?.User, user.Id); + NotifyPasswordChanged(_httpContextAccessor.HttpContext?.User, user.Id); } return result; @@ -150,11 +153,11 @@ namespace Umbraco.Cms.Web.Common.Security // The way we unlock is by setting the lockoutEnd date to the current datetime if (result.Succeeded && lockoutEnd >= DateTimeOffset.UtcNow) { - RaiseAccountLockedEvent(_httpContextAccessor.HttpContext?.User, user.Id); + NotifyAccountLocked(_httpContextAccessor.HttpContext?.User, user.Id); } else { - RaiseAccountUnlockedEvent(_httpContextAccessor.HttpContext?.User, user.Id); + NotifyAccountUnlocked(_httpContextAccessor.HttpContext?.User, user.Id); // Resets the login attempt fails back to 0 when unlock is clicked await ResetAccessFailedCountAsync(user); @@ -168,8 +171,8 @@ namespace Umbraco.Cms.Web.Common.Security { IdentityResult result = await base.ResetAccessFailedCountAsync(user); - // raise the event now that it's reset - RaiseResetAccessFailedCountEvent(_httpContextAccessor.HttpContext?.User, user.Id); + // notify now that it's reset + NotifyResetAccessFailedCount(_httpContextAccessor.HttpContext?.User, user.Id); return result; } @@ -181,101 +184,59 @@ namespace Umbraco.Cms.Web.Common.Security return currentUserId; } - private IdentityAuditEventArgs CreateArgs(AuditEvent auditEvent, IPrincipal currentUser, string affectedUserId, string affectedUsername) + public void NotifyAccountLocked(IPrincipal currentUser, string userId) => Notify(currentUser, + (currentUserId, ip) => new UserLockedNotification(ip, userId, currentUserId) + ); + + public void NotifyAccountUnlocked(IPrincipal currentUser, string userId) => Notify(currentUser, + (currentUserId, ip) => new UserUnlockedNotification(ip, userId, currentUserId) + ); + + public void NotifyForgotPasswordRequested(IPrincipal currentUser, string userId) => Notify(currentUser, + (currentUserId, ip) => new UserForgotPasswordRequestedNotification(ip, userId, currentUserId) + ); + + public void NotifyForgotPasswordChanged(IPrincipal currentUser, string userId) => Notify(currentUser, + (currentUserId, ip) => new UserForgotPasswordChangedNotification(ip, userId, currentUserId) + ); + + public void NotifyLoginFailed(IPrincipal currentUser, string userId) => Notify(currentUser, + (currentUserId, ip) => new UserLoginFailedNotification(ip, userId, currentUserId) + ); + + public void NotifyLoginRequiresVerification(IPrincipal currentUser, string userId) => Notify(currentUser, + (currentUserId, ip) => new UserLoginRequiresVerificationNotification(ip, userId, currentUserId) + ); + + public void NotifyLoginSuccess(IPrincipal currentUser, string userId) => Notify(currentUser, + (currentUserId, ip) => new UserLoginSuccessNotification(ip, userId, currentUserId) + ); + + public SignOutSuccessResult NotifyLogoutSuccess(IPrincipal currentUser, string userId) + { + var notification = Notify(currentUser, + (currentUserId, ip) => new UserLogoutSuccessNotification(ip, userId, currentUserId) + ); + + return new SignOutSuccessResult {SignOutRedirectUrl = notification.SignOutRedirectUrl}; + } + + public void NotifyPasswordChanged(IPrincipal currentUser, string userId) => Notify(currentUser, + (currentUserId, ip) => new UserPasswordChangedNotification(ip, userId, currentUserId) + ); + + public void NotifyResetAccessFailedCount(IPrincipal currentUser, string userId) => Notify(currentUser, + (currentUserId, ip) => new UserResetAccessFailedCountNotification(ip, userId, currentUserId) + ); + + private T Notify(IPrincipal currentUser, Func createNotification) where T : INotification { var currentUserId = GetCurrentUserId(currentUser); var ip = IpResolver.GetCurrentRequestIpAddress(); - return new IdentityAuditEventArgs(auditEvent, ip, currentUserId, string.Empty, affectedUserId, affectedUsername); + + var notification = createNotification(currentUserId, ip); + _eventAggregator.Publish(notification); + return notification; } - - private IdentityAuditEventArgs CreateArgs(AuditEvent auditEvent, BackOfficeIdentityUser currentUser, string affectedUserId, string affectedUsername) - { - var currentUserId = currentUser.Id; - var ip = IpResolver.GetCurrentRequestIpAddress(); - return new IdentityAuditEventArgs(auditEvent, ip, currentUserId, string.Empty, affectedUserId, affectedUsername); - } - - // TODO: Review where these are raised and see if they can be simplified and either done in the this usermanager or the signin manager, - // lastly we'll resort to the authentication controller but we should try to remove all instances of that occuring - public void RaiseAccountLockedEvent(IPrincipal currentUser, string userId) => OnAccountLocked(CreateArgs(AuditEvent.AccountLocked, currentUser, userId, string.Empty)); - - public void RaiseAccountUnlockedEvent(IPrincipal currentUser, string userId) => OnAccountUnlocked(CreateArgs(AuditEvent.AccountUnlocked, currentUser, userId, string.Empty)); - - public void RaiseForgotPasswordRequestedEvent(IPrincipal currentUser, string userId) => OnForgotPasswordRequested(CreateArgs(AuditEvent.ForgotPasswordRequested, currentUser, userId, string.Empty)); - - public void RaiseForgotPasswordChangedSuccessEvent(IPrincipal currentUser, string userId) => OnForgotPasswordChangedSuccess(CreateArgs(AuditEvent.ForgotPasswordChangedSuccess, currentUser, userId, string.Empty)); - - public void RaiseLoginFailedEvent(IPrincipal currentUser, string userId) => OnLoginFailed(CreateArgs(AuditEvent.LoginFailed, currentUser, userId, string.Empty)); - - public void RaiseLoginRequiresVerificationEvent(IPrincipal currentUser, string userId) => OnLoginRequiresVerification(CreateArgs(AuditEvent.LoginRequiresVerification, currentUser, userId, string.Empty)); - - public void RaiseLoginSuccessEvent(IPrincipal currentUser, string userId) => OnLoginSuccess(CreateArgs(AuditEvent.LoginSucces, currentUser, userId, string.Empty)); - - public SignOutAuditEventArgs RaiseLogoutSuccessEvent(IPrincipal currentUser, string userId) - { - var currentUserId = GetCurrentUserId(currentUser); - var args = new SignOutAuditEventArgs(AuditEvent.LogoutSuccess, IpResolver.GetCurrentRequestIpAddress(), performingUser: currentUserId, affectedUser: userId); - OnLogoutSuccess(args); - return args; - } - - public void RaisePasswordChangedEvent(IPrincipal currentUser, string userId) => OnPasswordChanged(CreateArgs(AuditEvent.LogoutSuccess, currentUser, userId, string.Empty)); - - public void RaiseResetAccessFailedCountEvent(IPrincipal currentUser, string userId) => OnResetAccessFailedCount(CreateArgs(AuditEvent.ResetAccessFailedCount, currentUser, userId, string.Empty)); - - public UserInviteEventArgs RaiseSendingUserInvite(IPrincipal currentUser, UserInvite invite, IUser createdUser) - { - var currentUserId = GetCurrentUserId(currentUser); - var ip = IpResolver.GetCurrentRequestIpAddress(); - var args = new UserInviteEventArgs(ip, currentUserId, invite, createdUser); - OnSendingUserInvite(args); - return args; - } - - public bool HasSendingUserInviteEventHandler => SendingUserInvite != null; - - // TODO: These static events are problematic. Moving forward we don't want static events at all but we cannot - // have non-static events here because the user manager is a Scoped instance not a singleton - // so we'll have to deal with this a diff way i.e. refactoring how events are done entirely - public static event EventHandler AccountLocked; - public static event EventHandler AccountUnlocked; - public static event EventHandler ForgotPasswordRequested; - public static event EventHandler ForgotPasswordChangedSuccess; - public static event EventHandler LoginFailed; - public static event EventHandler LoginRequiresVerification; - public static event EventHandler LoginSuccess; - public static event EventHandler LogoutSuccess; - public static event EventHandler PasswordChanged; - public static event EventHandler PasswordReset; - public static event EventHandler ResetAccessFailedCount; - - /// - /// Raised when a user is invited - /// - public static event EventHandler SendingUserInvite; // this event really has nothing to do with the user manager but was the most convenient place to put it - - protected virtual void OnAccountLocked(IdentityAuditEventArgs e) => AccountLocked?.Invoke(this, e); - - protected virtual void OnSendingUserInvite(UserInviteEventArgs e) => SendingUserInvite?.Invoke(this, e); - - protected virtual void OnAccountUnlocked(IdentityAuditEventArgs e) => AccountUnlocked?.Invoke(this, e); - - protected virtual void OnForgotPasswordRequested(IdentityAuditEventArgs e) => ForgotPasswordRequested?.Invoke(this, e); - - protected virtual void OnForgotPasswordChangedSuccess(IdentityAuditEventArgs e) => ForgotPasswordChangedSuccess?.Invoke(this, e); - - protected virtual void OnLoginFailed(IdentityAuditEventArgs e) => LoginFailed?.Invoke(this, e); - - protected virtual void OnLoginRequiresVerification(IdentityAuditEventArgs e) => LoginRequiresVerification?.Invoke(this, e); - - protected virtual void OnLoginSuccess(IdentityAuditEventArgs e) => LoginSuccess?.Invoke(this, e); - - protected virtual void OnLogoutSuccess(SignOutAuditEventArgs e) => LogoutSuccess?.Invoke(this, e); - - protected virtual void OnPasswordChanged(IdentityAuditEventArgs e) => PasswordChanged?.Invoke(this, e); - - protected virtual void OnPasswordReset(IdentityAuditEventArgs e) => PasswordReset?.Invoke(this, e); - - protected virtual void OnResetAccessFailedCount(IdentityAuditEventArgs e) => ResetAccessFailedCount?.Invoke(this, e); } } diff --git a/src/Umbraco.Web.Common/Security/MemberManager.cs b/src/Umbraco.Web.Common/Security/MemberManager.cs index c36ae0c0fc..a6165bac79 100644 --- a/src/Umbraco.Web.Common/Security/MemberManager.cs +++ b/src/Umbraco.Web.Common/Security/MemberManager.cs @@ -1,24 +1,17 @@ using System; using System.Collections.Generic; -using System.Security.Claims; -using System.Security.Principal; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Models.ContentEditing; -using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Net; using Umbraco.Cms.Core.Security; -using Umbraco.Extensions; namespace Umbraco.Cms.Web.Common.Security { public class MemberManager : UmbracoUserManager, IMemberManager { - private readonly IHttpContextAccessor _httpContextAccessor; public MemberManager( IIpResolver ipResolver, @@ -29,38 +22,10 @@ namespace Umbraco.Cms.Web.Common.Security IEnumerable> passwordValidators, BackOfficeIdentityErrorDescriber errors, IServiceProvider services, - IHttpContextAccessor httpContextAccessor, ILogger> logger, IOptions passwordConfiguration) : base(ipResolver, store, optionsAccessor, passwordHasher, userValidators, passwordValidators, errors, services, logger, passwordConfiguration) { - _httpContextAccessor = httpContextAccessor; } - - private string GetCurrentUserId(IPrincipal currentUser) - { - ClaimsIdentity umbIdentity = currentUser?.GetUmbracoIdentity(); - var currentUserId = umbIdentity?.GetUserId() ?? Cms.Core.Constants.Security.SuperUserIdAsString; - return currentUserId; - } - - private IdentityAuditEventArgs CreateArgs(AuditEvent auditEvent, IPrincipal currentUser, string affectedUserId, string affectedUsername) - { - var currentUserId = GetCurrentUserId(currentUser); - var ip = IpResolver.GetCurrentRequestIpAddress(); - return new IdentityAuditEventArgs(auditEvent, ip, currentUserId, string.Empty, affectedUserId, affectedUsername); - } - - //TODO: have removed all other member audit events - can revisit if we need member auditing on a user level in future - - public void RaiseForgotPasswordRequestedEvent(IPrincipal currentUser, string userId) => throw new NotImplementedException(); - - public void RaiseForgotPasswordChangedSuccessEvent(IPrincipal currentUser, string userId) => throw new NotImplementedException(); - - public SignOutAuditEventArgs RaiseLogoutSuccessEvent(IPrincipal currentUser, string userId) => throw new NotImplementedException(); - - public UserInviteEventArgs RaiseSendingUserInvite(IPrincipal currentUser, UserInvite invite, IUser createdUser) => throw new NotImplementedException(); - - public bool HasSendingUserInviteEventHandler { get; } } } diff --git a/src/Umbraco.Web.Common/Security/UserForgotPasswordChangedNotification.cs b/src/Umbraco.Web.Common/Security/UserForgotPasswordChangedNotification.cs new file mode 100644 index 0000000000..04fb74a1ca --- /dev/null +++ b/src/Umbraco.Web.Common/Security/UserForgotPasswordChangedNotification.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Web.Common.Security +{ + public class UserForgotPasswordChangedNotification : UserNotification + { + public UserForgotPasswordChangedNotification(string ipAddress, string affectedUserId, string performingUserId) : base(ipAddress, affectedUserId, performingUserId) + { + } + } +} diff --git a/src/Umbraco.Web.Common/Security/UserForgotPasswordRequestedNotification.cs b/src/Umbraco.Web.Common/Security/UserForgotPasswordRequestedNotification.cs new file mode 100644 index 0000000000..766e1c61ca --- /dev/null +++ b/src/Umbraco.Web.Common/Security/UserForgotPasswordRequestedNotification.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Web.Common.Security +{ + public class UserForgotPasswordRequestedNotification : UserNotification + { + public UserForgotPasswordRequestedNotification(string ipAddress, string affectedUserId, string performingUserId) : base(ipAddress, affectedUserId, performingUserId) + { + } + } +} diff --git a/src/Umbraco.Web.Common/Security/UserLockedNotification.cs b/src/Umbraco.Web.Common/Security/UserLockedNotification.cs new file mode 100644 index 0000000000..6c4737fb49 --- /dev/null +++ b/src/Umbraco.Web.Common/Security/UserLockedNotification.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Web.Common.Security +{ + public class UserLockedNotification : UserNotification + { + public UserLockedNotification(string ipAddress, string affectedUserId, string performingUserId) : base(ipAddress, affectedUserId, performingUserId) + { + } + } +} diff --git a/src/Umbraco.Web.Common/Security/UserLoginFailedNotification.cs b/src/Umbraco.Web.Common/Security/UserLoginFailedNotification.cs new file mode 100644 index 0000000000..de8578015e --- /dev/null +++ b/src/Umbraco.Web.Common/Security/UserLoginFailedNotification.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Web.Common.Security +{ + public class UserLoginFailedNotification : UserNotification + { + public UserLoginFailedNotification(string ipAddress, string affectedUserId, string performingUserId) : base(ipAddress, affectedUserId, performingUserId) + { + } + } +} diff --git a/src/Umbraco.Web.Common/Security/UserLoginRequiresVerificationNotification.cs b/src/Umbraco.Web.Common/Security/UserLoginRequiresVerificationNotification.cs new file mode 100644 index 0000000000..cff2d9f45e --- /dev/null +++ b/src/Umbraco.Web.Common/Security/UserLoginRequiresVerificationNotification.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Web.Common.Security +{ + public class UserLoginRequiresVerificationNotification : UserNotification + { + public UserLoginRequiresVerificationNotification(string ipAddress, string affectedUserId, string performingUserId) : base(ipAddress, affectedUserId, performingUserId) + { + } + } +} diff --git a/src/Umbraco.Web.Common/Security/UserLoginSuccessNotification.cs b/src/Umbraco.Web.Common/Security/UserLoginSuccessNotification.cs new file mode 100644 index 0000000000..bb9f3ccecb --- /dev/null +++ b/src/Umbraco.Web.Common/Security/UserLoginSuccessNotification.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Web.Common.Security +{ + public class UserLoginSuccessNotification : UserNotification + { + public UserLoginSuccessNotification(string ipAddress, string affectedUserId, string performingUserId) : base(ipAddress, affectedUserId, performingUserId) + { + } + } +} diff --git a/src/Umbraco.Web.Common/Security/UserLogoutSuccessNotification.cs b/src/Umbraco.Web.Common/Security/UserLogoutSuccessNotification.cs new file mode 100644 index 0000000000..9ef9d5d0e7 --- /dev/null +++ b/src/Umbraco.Web.Common/Security/UserLogoutSuccessNotification.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Cms.Web.Common.Security +{ + public class UserLogoutSuccessNotification : UserNotification + { + public UserLogoutSuccessNotification(string ipAddress, string affectedUserId, string performingUserId) : base(ipAddress, affectedUserId, performingUserId) + { + } + + public string SignOutRedirectUrl { get; set; } + } +} diff --git a/src/Umbraco.Web.Common/Security/UserNotification.cs b/src/Umbraco.Web.Common/Security/UserNotification.cs new file mode 100644 index 0000000000..40d37ec82a --- /dev/null +++ b/src/Umbraco.Web.Common/Security/UserNotification.cs @@ -0,0 +1,36 @@ +using System; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Web.Common.Security +{ + public abstract class UserNotification : INotification + { + protected UserNotification(string ipAddress, string affectedUserId, string performingUserId) + { + DateTimeUtc = DateTime.UtcNow; + IpAddress = ipAddress; + AffectedUserId = affectedUserId; + PerformingUserId = performingUserId; + } + + /// + /// Current date/time in UTC format + /// + public DateTime DateTimeUtc { get; } + + /// + /// The source IP address of the user performing the action + /// + public string IpAddress { get; } + + /// + /// The user affected by the event raised + /// + public string AffectedUserId { get; } + + /// + /// If a user is performing an action on a different user, then this will be set. Otherwise it will be -1 + /// + public string PerformingUserId { get; } + } +} diff --git a/src/Umbraco.Web.Common/Security/UserPasswordChangedNotification.cs b/src/Umbraco.Web.Common/Security/UserPasswordChangedNotification.cs new file mode 100644 index 0000000000..68115d411c --- /dev/null +++ b/src/Umbraco.Web.Common/Security/UserPasswordChangedNotification.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Web.Common.Security +{ + public class UserPasswordChangedNotification : UserNotification + { + public UserPasswordChangedNotification(string ipAddress, string affectedUserId, string performingUserId) : base(ipAddress, affectedUserId, performingUserId) + { + } + } +} diff --git a/src/Umbraco.Web.Common/Security/UserResetAccessFailedCountNotification.cs b/src/Umbraco.Web.Common/Security/UserResetAccessFailedCountNotification.cs new file mode 100644 index 0000000000..9335bd76a0 --- /dev/null +++ b/src/Umbraco.Web.Common/Security/UserResetAccessFailedCountNotification.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Web.Common.Security +{ + public class UserResetAccessFailedCountNotification : UserNotification + { + public UserResetAccessFailedCountNotification(string ipAddress, string affectedUserId, string performingUserId) : base(ipAddress, affectedUserId, performingUserId) + { + } + } +} diff --git a/src/Umbraco.Web.Common/Security/UserUnlockedNotification.cs b/src/Umbraco.Web.Common/Security/UserUnlockedNotification.cs new file mode 100644 index 0000000000..0ecba4d597 --- /dev/null +++ b/src/Umbraco.Web.Common/Security/UserUnlockedNotification.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Web.Common.Security +{ + public class UserUnlockedNotification : UserNotification + { + public UserUnlockedNotification(string ipAddress, string affectedUserId, string performingUserId) : base(ipAddress, affectedUserId, performingUserId) + { + } + } +} From 03cc342c109c351baeeb23ccaecd3bdcf7aa7a61 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 1 Mar 2021 13:10:37 +0100 Subject: [PATCH 04/42] WIP on content service events --- .../Events/ContentSavingEventArgs.cs | 77 -------- .../Cache/DistributedCacheBinder_Handlers.cs | 17 +- .../Compose/NotificationsComponent.cs | 166 +++++++++++++++++- ...omplexPropertyEditorContentEventHandler.cs | 49 +++++- .../Services/CancelableNotification.cs | 124 +++++++++++++ .../Services/Implement/ContentService.cs | 144 +++++++++------ 6 files changed, 419 insertions(+), 158 deletions(-) delete mode 100644 src/Umbraco.Core/Events/ContentSavingEventArgs.cs create mode 100644 src/Umbraco.Infrastructure/Services/CancelableNotification.cs diff --git a/src/Umbraco.Core/Events/ContentSavingEventArgs.cs b/src/Umbraco.Core/Events/ContentSavingEventArgs.cs deleted file mode 100644 index b1cded2eb4..0000000000 --- a/src/Umbraco.Core/Events/ContentSavingEventArgs.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Collections.Generic; -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.Core.Events -{ - /// - /// Represent event data for the Saving event. - /// - public class ContentSavingEventArgs : SaveEventArgs - { - #region Factory Methods - - /// - /// Converts to while preserving all args state - /// - /// - public ContentSavedEventArgs ToContentSavedEventArgs() - { - return new ContentSavedEventArgs(EventObject, Messages, AdditionalData) - { - EventState = EventState - }; - } - - /// - /// Converts to while preserving all args state - /// - /// - public ContentPublishedEventArgs ToContentPublishedEventArgs() - { - return new ContentPublishedEventArgs(EventObject, false, Messages) - { - EventState = EventState, - AdditionalData = AdditionalData - }; - } - - /// - /// Converts to while preserving all args state - /// - /// - public ContentPublishingEventArgs ToContentPublishingEventArgs() - { - return new ContentPublishingEventArgs(EventObject, Messages) - { - EventState = EventState, - AdditionalData = AdditionalData - }; - } - - #endregion - - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - public ContentSavingEventArgs(IEnumerable eventObject, EventMessages eventMessages) - : base(eventObject, eventMessages) - { } - - /// - /// Initializes a new instance of the class. - /// - public ContentSavingEventArgs(IContent eventObject, EventMessages eventMessages) - : base(eventObject, eventMessages) - { } - - #endregion - - /// - /// Determines whether a culture is being saved, during a Saving event. - /// - public bool IsSavingCulture(IContent content, string culture) - => content.CultureInfos.TryGetValue(culture, out var cultureInfo) && cultureInfo.IsDirty(); - } -} diff --git a/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs index 99c1d2b0ee..43dc1d0fcd 100644 --- a/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs +++ b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -126,8 +126,6 @@ namespace Umbraco.Cms.Core.Cache () => MediaService.TreeChanged -= MediaService_TreeChanged); // bind to content events - Bind(() => ContentService.Saved += ContentService_Saved, // needed for permissions - () => ContentService.Saved -= ContentService_Saved); Bind(() => ContentService.Copied += ContentService_Copied, // needed for permissions () => ContentService.Copied -= ContentService_Copied); Bind(() => ContentService.TreeChanged += ContentService_TreeChanged,// handles all content changes @@ -182,19 +180,6 @@ namespace Umbraco.Cms.Core.Cache { } - /// - /// Handles cache refreshing for when content is saved (not published) - /// - /// - /// - /// - /// When an entity is saved we need to notify other servers about the change in order for the Examine indexes to - /// stay up-to-date for unpublished content. - /// - private void ContentService_Saved(IContentService sender, SaveEventArgs e) - { - } - private void ContentService_TreeChanged(IContentService sender, TreeChange.EventArgs args) { _distributedCache.RefreshContentCache(args.Changes.ToArray()); diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs index 068a8bceea..92e7d6c4d7 100644 --- a/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -18,10 +18,162 @@ using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; +using Umbraco.Cms.Infrastructure.Services; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Compose { + public sealed class NotificationsHandler : + INotificationHandler>, + INotificationHandler> + { + private readonly Notifier _notifier; + private readonly ActionCollection _actions; + private readonly IContentService _contentService; + + public void Handle(SavedNotification notification) + { + var newEntities = new List(); + var updatedEntities = new List(); + + //need to determine if this is updating or if it is new + foreach (var entity in notification.SavedEntities) + { + var dirty = (IRememberBeingDirty)entity; + if (dirty.WasPropertyDirty("Id")) + { + //it's new + newEntities.Add(entity); + } + else + { + //it's updating + updatedEntities.Add(entity); + } + } + _notifier.Notify(_actions.GetAction(), newEntities.ToArray()); + _notifier.Notify(_actions.GetAction(), updatedEntities.ToArray()); + } + + public void Handle(SortedNotification notification) + { + var parentId = notification.SortedEntities.Select(x => x.ParentId).Distinct().ToList(); + if (parentId.Count != 1) + return; // this shouldn't happen, for sorting all entities will have the same parent id + + // in this case there's nothing to report since if the root is sorted we can't report on a fake entity. + // this is how it was in v7, we can't report on root changes because you can't subscribe to root changes. + if (parentId[0] <= 0) + return; + + var parent = _contentService.GetById(parentId[0]); + if (parent == null) + return; // this shouldn't happen + + _notifier.Notify(_actions.GetAction(), new[] { parent }); + } + + /// + /// 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 + })); + } + } + + } + } + public sealed class NotificationsComponent : IComponent { private readonly Notifier _notifier; @@ -41,10 +193,10 @@ namespace Umbraco.Cms.Core.Compose ContentService.SentToPublish += ContentService_SentToPublish; //Send notifications for the published action ContentService.Published += ContentService_Published; - //Send notifications for the saved action - ContentService.Sorted += ContentService_Sorted; - //Send notifications for the update and created actions - ContentService.Saved += ContentService_Saved; + ////Send notifications for the saved action + //ContentService.Sorted += ContentService_Sorted; + ////Send notifications for the update and created actions + //ContentService.Saved += ContentService_Saved; //Send notifications for the unpublish action ContentService.Unpublished += ContentService_Unpublished; //Send notifications for the move/move to recycle bin and restore actions @@ -65,8 +217,8 @@ namespace Umbraco.Cms.Core.Compose { ContentService.SentToPublish -= ContentService_SentToPublish; ContentService.Published -= ContentService_Published; - ContentService.Sorted -= ContentService_Sorted; - ContentService.Saved -= ContentService_Saved; + //ContentService.Sorted -= ContentService_Sorted; + //ContentService.Saved -= ContentService_Saved; ContentService.Unpublished -= ContentService_Unpublished; ContentService.Moved -= ContentService_Moved; ContentService.Trashed -= ContentService_Trashed; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentEventHandler.cs b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentEventHandler.cs index 8098a5f8d4..02b7d53108 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentEventHandler.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentEventHandler.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -7,10 +7,53 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; +using Umbraco.Cms.Infrastructure.Services; using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors { + // TODO: add copying handling + public abstract class ComplexPropertyEditorContentNotificationHandler + : INotificationHandler> + { + private readonly string _editorAlias; + private readonly Func _formatPropertyValue; + + protected ComplexPropertyEditorContentNotificationHandler(string editorAlias, Func formatPropertyValue) + { + _editorAlias = editorAlias; + _formatPropertyValue = formatPropertyValue; + } + + public void Handle(SavingNotification notification) + { + foreach (var entity in notification.SavedEntities) + { + var props = entity.GetPropertiesByEditor(_editorAlias); + UpdatePropertyValues(props, true); + } + } + + private void UpdatePropertyValues(IEnumerable props, bool onlyMissingKeys) + { + foreach (var prop in props) + { + // A Property may have one or more values due to cultures + var propVals = prop.Values; + foreach (var cultureVal in propVals) + { + // Remove keys from published value & any nested properties + var updatedPublishedVal = _formatPropertyValue(cultureVal.PublishedValue?.ToString(), onlyMissingKeys); + cultureVal.PublishedValue = updatedPublishedVal; + + // Remove keys from edited/draft value & any nested properties + var updatedEditedVal = _formatPropertyValue(cultureVal.EditedValue?.ToString(), onlyMissingKeys); + cultureVal.EditedValue = updatedEditedVal; + } + } + } + } + /// /// Utility class for dealing with Copying/Saving events for complex editors /// @@ -26,7 +69,7 @@ namespace Umbraco.Cms.Core.PropertyEditors _editorAlias = editorAlias; _formatPropertyValue = formatPropertyValue; ContentService.Copying += ContentService_Copying; - ContentService.Saving += ContentService_Saving; + //ContentService.Saving += ContentService_Saving; } /// @@ -84,7 +127,7 @@ namespace Umbraco.Cms.Core.PropertyEditors if (disposing) { ContentService.Copying -= ContentService_Copying; - ContentService.Saving -= ContentService_Saving; + //ContentService.Saving -= ContentService_Saving; } _disposedValue = true; } diff --git a/src/Umbraco.Infrastructure/Services/CancelableNotification.cs b/src/Umbraco.Infrastructure/Services/CancelableNotification.cs new file mode 100644 index 0000000000..178ce3df9b --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/CancelableNotification.cs @@ -0,0 +1,124 @@ +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services +{ + // TODO split this file into several small classes and move to another namespace + + public interface ICancelableNotification + { + bool Cancel { get; set; } + } + + public abstract class ObjectNotification : INotification where T : class + { + protected ObjectNotification(T target, EventMessages messages) + { + Messages = messages; + Target = target; + } + + public EventMessages Messages { get; } + + protected T Target { get; } + } + + public abstract class EnumerableObjectNotification : ObjectNotification> + { + protected EnumerableObjectNotification(T target, EventMessages messages) : base(new [] {target}, messages) + { + } + + protected EnumerableObjectNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } + + public abstract class CancelableObjectNotification : ObjectNotification where T : class + { + protected CancelableObjectNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public bool Cancel { get; set; } + + public void CancelOperation(EventMessage cancelationMessage) + { + Cancel = true; + cancelationMessage.IsDefaultEventMessage = true; + Messages.Add(cancelationMessage); + } + } + + public abstract class CancelableEnumerableObjectNotification : CancelableObjectNotification> + { + protected CancelableEnumerableObjectNotification(T target, EventMessages messages) : base(new [] {target}, messages) + { + } + protected CancelableEnumerableObjectNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } + + public class DeletingNotification : CancelableEnumerableObjectNotification + { + public DeletingNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable DeletedEntities => Target; + } + + public class DeletedNotification : EnumerableObjectNotification + { + public DeletedNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable DeletedEntities => Target; + } + + public class SortingNotification : CancelableEnumerableObjectNotification + { + public SortingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable SortedEntities => Target; + } + + public class SortedNotification : EnumerableObjectNotification + { + public SortedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable SortedEntities => Target; + } + + public class SavingNotification : CancelableEnumerableObjectNotification + { + public SavingNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public SavingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable SavedEntities => Target; + } + + public class SavedNotification : EnumerableObjectNotification + { + public SavedNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public SavedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable SavedEntities => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index 017540ae3f..379ff9a0a2 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -13,6 +13,7 @@ using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence.Querying; +using Umbraco.Cms.Infrastructure.Services; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Services.Implement @@ -32,6 +33,7 @@ namespace Umbraco.Cms.Core.Services.Implement private readonly IShortStringHelper _shortStringHelper; private readonly ILogger _logger; private IQuery _queryNotTrashed; + private readonly IEventAggregator _eventAggregator; #region Constructors @@ -39,7 +41,7 @@ namespace Umbraco.Cms.Core.Services.Implement IEventMessagesFactory eventMessagesFactory, IDocumentRepository documentRepository, IEntityRepository entityRepository, IAuditRepository auditRepository, IContentTypeRepository contentTypeRepository, IDocumentBlueprintRepository documentBlueprintRepository, ILanguageRepository languageRepository, - Lazy propertyValidationService, IShortStringHelper shortStringHelper) + Lazy propertyValidationService, IShortStringHelper shortStringHelper, IEventAggregator eventAggregator) : base(provider, loggerFactory, eventMessagesFactory) { _documentRepository = documentRepository; @@ -50,6 +52,7 @@ namespace Umbraco.Cms.Core.Services.Implement _languageRepository = languageRepository; _propertyValidationService = propertyValidationService; _shortStringHelper = shortStringHelper; + _eventAggregator = eventAggregator; _logger = loggerFactory.CreateLogger(); } @@ -747,11 +750,15 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs); - if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving))) + if (raiseEvents) { - scope.Complete(); - return OperationResult.Cancel(evtMsgs); + var notification = new SavingNotification(content, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) + { + scope.Complete(); + return OperationResult.Cancel(evtMsgs); + } } scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); @@ -773,7 +780,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - scope.Events.Dispatch(Saved, this, saveEventArgs.ToContentSavedEventArgs(), nameof(Saved)); + _eventAggregator.Publish(new SavedNotification(content, evtMsgs)); } var changeType = TreeChangeTypes.RefreshNode; scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); @@ -802,11 +809,15 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var saveEventArgs = new ContentSavingEventArgs(contentsA, evtMsgs); - if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving))) + if (raiseEvents) { - scope.Complete(); - return OperationResult.Cancel(evtMsgs); + var notification = new SavingNotification(contentsA, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) + { + scope.Complete(); + return OperationResult.Cancel(evtMsgs); + } } var treeChanges = contentsA.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)); @@ -823,7 +834,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - scope.Events.Dispatch(Saved, this, saveEventArgs.ToContentSavedEventArgs(), nameof(Saved)); + _eventAggregator.Publish(new SavedNotification(contentsA, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, treeChanges.ToEventArgs()); Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Saved multiple content"); @@ -867,9 +878,12 @@ namespace Umbraco.Cms.Core.Services.Implement var allLangs = _languageRepository.GetMany().ToList(); - var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs); - if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving))) + var notification = new SavingNotification(content, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) + { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); + } // if culture is specific, first publish the invariant values, then publish the culture itself. // if culture is '*', then publish them all (including variants) @@ -881,7 +895,7 @@ namespace Umbraco.Cms.Core.Services.Implement // we don't care about the response here, this response will be rechecked below but we need to set the culture info values now. content.PublishCulture(impact); - var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId, raiseEvents); + var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, userId, raiseEvents); scope.Complete(); return result; } @@ -905,9 +919,16 @@ namespace Umbraco.Cms.Core.Services.Implement var allLangs = _languageRepository.GetMany().ToList(); var evtMsgs = EventMessagesFactory.Get(); - var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs); - if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving))) - return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); + + if (raiseEvents) + { + var notification = new SavingNotification(content, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) + { + return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); + } + } var varies = content.ContentType.VariesByCulture(); @@ -927,7 +948,7 @@ namespace Umbraco.Cms.Core.Services.Implement foreach (var impact in impacts) content.PublishCulture(impact); - var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId, raiseEvents); + var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, userId, raiseEvents); scope.Complete(); return result; } @@ -969,9 +990,12 @@ namespace Umbraco.Cms.Core.Services.Implement var allLangs = _languageRepository.GetMany().ToList(); - var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs); - if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving))) + var notification = new SavingNotification(content, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) + { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); + } // all cultures = unpublish whole if (culture == "*" || (!content.ContentType.VariesByCulture() && culture == null)) @@ -982,7 +1006,7 @@ namespace Umbraco.Cms.Core.Services.Implement // to be non-routable so that when it's re-published all variants were as they were. content.PublishedState = PublishedState.Unpublishing; - var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId); + var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, userId); scope.Complete(); return result; } @@ -996,7 +1020,7 @@ namespace Umbraco.Cms.Core.Services.Implement var removed = content.UnpublishCulture(culture); //save and publish any changes - var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId); + var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, userId); scope.Complete(); @@ -1039,13 +1063,16 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); - var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs); - if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving))) + var notification = new SavingNotification(content, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) + { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); + } var allLangs = _languageRepository.GetMany().ToList(); - var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId, raiseEvents); + var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, userId, raiseEvents); scope.Complete(); return result; } @@ -1069,15 +1096,13 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// private PublishResult CommitDocumentChangesInternal(IScope scope, IContent content, - ContentSavingEventArgs saveEventArgs, IReadOnlyCollection allLangs, + EventMessages evtMsgs, IReadOnlyCollection allLangs, int userId = Cms.Core.Constants.Security.SuperUserId, bool raiseEvents = true, bool branchOne = false, bool branchRoot = false) { if (scope == null) throw new ArgumentNullException(nameof(scope)); if (content == null) throw new ArgumentNullException(nameof(content)); - if (saveEventArgs == null) throw new ArgumentNullException(nameof(saveEventArgs)); - - var evtMsgs = saveEventArgs.Messages; + if (evtMsgs == null) throw new ArgumentNullException(nameof(evtMsgs)); PublishResult publishResult = null; PublishResult unpublishResult = null; @@ -1210,7 +1235,7 @@ namespace Umbraco.Cms.Core.Services.Implement // raise the Saved event, always if (raiseEvents) { - scope.Events.Dispatch(Saved, this, saveEventArgs.ToContentSavedEventArgs(), nameof(Saved)); + _eventAggregator.Publish(new SavedNotification(content, evtMsgs)); } if (unpublishing) // we have tried to unpublish - won't happen in a branch @@ -1375,8 +1400,9 @@ namespace Umbraco.Cms.Core.Services.Implement if (pendingCultures.Count == 0) continue; //shouldn't happen but no point in processing this document if there's nothing there - var saveEventArgs = new ContentSavingEventArgs(d, evtMsgs); - if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving))) + var notification = new SavingNotification(d, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { results.Add(new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, d)); continue; @@ -1390,7 +1416,7 @@ namespace Umbraco.Cms.Core.Services.Implement d.UnpublishCulture(c); } - var result = CommitDocumentChangesInternal(scope, d, saveEventArgs, allLangs.Value, d.WriterId); + var result = CommitDocumentChangesInternal(scope, d, evtMsgs, allLangs.Value, d.WriterId); if (result.Success == false) _logger.LogError(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result); results.Add(result); @@ -1436,11 +1462,12 @@ namespace Umbraco.Cms.Core.Services.Implement if (pendingCultures.Count == 0) continue; //shouldn't happen but no point in processing this document if there's nothing there - var saveEventArgs = new ContentSavingEventArgs(d, evtMsgs); - if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving))) + var notification = new SavingNotification(d, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { results.Add(new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, d)); - continue; // this document is canceled move next + continue; } var publishing = true; @@ -1470,7 +1497,7 @@ namespace Umbraco.Cms.Core.Services.Implement else if (!publishing) result = new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, d); else - result = CommitDocumentChangesInternal(scope, d, saveEventArgs, allLangs.Value, d.WriterId); + result = CommitDocumentChangesInternal(scope, d, evtMsgs, allLangs.Value, d.WriterId); if (result.Success == false) _logger.LogError(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result); @@ -1718,9 +1745,12 @@ namespace Umbraco.Cms.Core.Services.Implement if (culturesToPublish.Count == 0) // empty = already published return new PublishResult(PublishResultType.SuccessPublishAlready, evtMsgs, document); - var saveEventArgs = new ContentSavingEventArgs(document, evtMsgs); - if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving))) + var notification = new SavingNotification(document, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) + { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, document); + } // publish & check if values are valid if (!publishCultures(document, culturesToPublish, allLangs)) @@ -1729,7 +1759,7 @@ namespace Umbraco.Cms.Core.Services.Implement return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, document); } - var result = CommitDocumentChangesInternal(scope, document, saveEventArgs, allLangs, userId, branchOne: true, branchRoot: isRoot); + var result = CommitDocumentChangesInternal(scope, document, evtMsgs, allLangs, userId, branchOne: true, branchRoot: isRoot); if (result.Success) publishedDocuments.Add(document); return result; @@ -1746,6 +1776,9 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { + var deleteNotification = new DeletingNotification(content, evtMsgs); + _eventAggregator.Publish(deleteNotification); + var deleteEventArgs = new DeleteEventArgs(content, evtMsgs); if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs, nameof(Deleting))) { @@ -2322,16 +2355,23 @@ namespace Umbraco.Cms.Core.Services.Implement private OperationResult Sort(IScope scope, IContent[] itemsA, int userId, EventMessages evtMsgs, bool raiseEvents) { - var saveEventArgs = new ContentSavingEventArgs(itemsA, evtMsgs); if (raiseEvents) { - //raise cancelable sorting event - if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Sorting))) + // raise cancelable sorting event + var sortingNotification = new SortingNotification(itemsA, evtMsgs); + _eventAggregator.Publish(sortingNotification); + if (sortingNotification.Cancel) + { return OperationResult.Cancel(evtMsgs); + } - //raise saving event (this one cannot be canceled) - saveEventArgs.CanCancel = false; - scope.Events.Dispatch(Saving, this, saveEventArgs, nameof(Saving)); + // raise cancelable saving event + var savingNotification = new SavingNotification(itemsA, evtMsgs); + _eventAggregator.Publish(savingNotification); + if (savingNotification.Cancel) + { + return OperationResult.Cancel(evtMsgs); + } } var published = new List(); @@ -2364,10 +2404,9 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - var savedEventsArgs = saveEventArgs.ToContentSavedEventArgs(); //first saved, then sorted - scope.Events.Dispatch(Saved, this, savedEventsArgs, nameof(Saved)); - scope.Events.Dispatch(Sorted, this, savedEventsArgs, nameof(Sorted)); + _eventAggregator.Publish(new SavedNotification(itemsA, evtMsgs)); + _eventAggregator.Publish(new SortedNotification(itemsA, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)).ToEventArgs()); @@ -2483,11 +2522,6 @@ namespace Umbraco.Cms.Core.Services.Implement /// public static event TypedEventHandler> Sorted; - /// - /// Occurs before Save - /// - public static event TypedEventHandler Saving; - /// /// Occurs after Save /// From ab2b35904f68c8d8764cc6751851d43d8e07ed89 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 1 Mar 2021 17:05:59 +0100 Subject: [PATCH 05/42] Remove a bunch more events from ContentService --- .../Cache/DistributedCacheBinder_Handlers.cs | 2 - .../Compose/BlockEditorComposer.cs | 10 - .../Compose/NestedContentPropertyComposer.cs | 10 - .../Compose/NotificationsComponent.cs | 263 +-------------- .../Compose/NotificationsHandler.cs | 217 ++++++++++++ .../Compose/RelateOnCopyComponent.cs | 59 ---- .../Compose/RelateOnCopyComposer.cs | 7 - .../Compose/RelateOnTrashComponent.cs | 98 +++--- .../BlockEditorPropertyHandler.cs} | 26 +- ...omplexPropertyEditorContentEventHandler.cs | 145 -------- ...ropertyEditorContentNotificationHandler.cs | 55 +++ .../FileUploadPropertyEditor.cs | 76 +++-- .../ImageCropperPropertyEditor.cs | 33 +- .../NestedContentPropertyHandler.cs} | 25 +- .../PropertyEditorsComponent.cs | 14 +- .../Routing/RedirectTrackingComposer.cs | 15 - ...omponent.cs => RedirectTrackingHandler.cs} | 106 +++--- .../Services/CancelableNotification.cs | 269 ++++++++++++++- .../Services/Implement/ContentService.cs | 314 +++++++----------- 19 files changed, 849 insertions(+), 895 deletions(-) delete mode 100644 src/Umbraco.Infrastructure/Compose/BlockEditorComposer.cs delete mode 100644 src/Umbraco.Infrastructure/Compose/NestedContentPropertyComposer.cs create mode 100644 src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs delete mode 100644 src/Umbraco.Infrastructure/Compose/RelateOnCopyComponent.cs delete mode 100644 src/Umbraco.Infrastructure/Compose/RelateOnCopyComposer.cs rename src/Umbraco.Infrastructure/{Compose/BlockEditorComponent.cs => PropertyEditors/BlockEditorPropertyHandler.cs} (92%) delete mode 100644 src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentEventHandler.cs create mode 100644 src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs rename src/Umbraco.Infrastructure/{Compose/NestedContentPropertyComponent.cs => PropertyEditors/NestedContentPropertyHandler.cs} (78%) delete mode 100644 src/Umbraco.Infrastructure/Routing/RedirectTrackingComposer.cs rename src/Umbraco.Infrastructure/Routing/{RedirectTrackingComponent.cs => RedirectTrackingHandler.cs} (58%) diff --git a/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs index 43dc1d0fcd..eaa3c0df86 100644 --- a/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs +++ b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs @@ -126,8 +126,6 @@ namespace Umbraco.Cms.Core.Cache () => MediaService.TreeChanged -= MediaService_TreeChanged); // bind to content events - Bind(() => ContentService.Copied += ContentService_Copied, // needed for permissions - () => ContentService.Copied -= ContentService_Copied); Bind(() => ContentService.TreeChanged += ContentService_TreeChanged,// handles all content changes () => ContentService.TreeChanged -= ContentService_TreeChanged); diff --git a/src/Umbraco.Infrastructure/Compose/BlockEditorComposer.cs b/src/Umbraco.Infrastructure/Compose/BlockEditorComposer.cs deleted file mode 100644 index bcc70e1748..0000000000 --- a/src/Umbraco.Infrastructure/Compose/BlockEditorComposer.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Umbraco.Cms.Core.Composing; - -namespace Umbraco.Cms.Core.Compose -{ - /// - /// A composer for Block editors to run a component - /// - public class BlockEditorComposer : ComponentComposer, ICoreComposer - { } -} diff --git a/src/Umbraco.Infrastructure/Compose/NestedContentPropertyComposer.cs b/src/Umbraco.Infrastructure/Compose/NestedContentPropertyComposer.cs deleted file mode 100644 index c8cddd6d08..0000000000 --- a/src/Umbraco.Infrastructure/Compose/NestedContentPropertyComposer.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Umbraco.Cms.Core.Composing; - -namespace Umbraco.Cms.Core.Compose -{ - /// - /// A composer for nested content to run a component - /// - public class NestedContentPropertyComposer : ComponentComposer, ICoreComposer - { } -} diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs index 92e7d6c4d7..af65a654b7 100644 --- a/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs @@ -13,167 +13,15 @@ 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.Entities; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; -using Umbraco.Cms.Infrastructure.Services; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Compose { - public sealed class NotificationsHandler : - INotificationHandler>, - INotificationHandler> - { - private readonly Notifier _notifier; - private readonly ActionCollection _actions; - private readonly IContentService _contentService; - - public void Handle(SavedNotification notification) - { - var newEntities = new List(); - var updatedEntities = new List(); - - //need to determine if this is updating or if it is new - foreach (var entity in notification.SavedEntities) - { - var dirty = (IRememberBeingDirty)entity; - if (dirty.WasPropertyDirty("Id")) - { - //it's new - newEntities.Add(entity); - } - else - { - //it's updating - updatedEntities.Add(entity); - } - } - _notifier.Notify(_actions.GetAction(), newEntities.ToArray()); - _notifier.Notify(_actions.GetAction(), updatedEntities.ToArray()); - } - - public void Handle(SortedNotification notification) - { - var parentId = notification.SortedEntities.Select(x => x.ParentId).Distinct().ToList(); - if (parentId.Count != 1) - return; // this shouldn't happen, for sorting all entities will have the same parent id - - // in this case there's nothing to report since if the root is sorted we can't report on a fake entity. - // this is how it was in v7, we can't report on root changes because you can't subscribe to root changes. - if (parentId[0] <= 0) - return; - - var parent = _contentService.GetById(parentId[0]); - if (parent == null) - return; // this shouldn't happen - - _notifier.Notify(_actions.GetAction(), new[] { parent }); - } - - /// - /// 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 - })); - } - } - - } - } - + // TODO: this component must be removed entirely - there is some code duplication in NotificationsHandler in anticipation of this component being deleted public sealed class NotificationsComponent : IComponent { private readonly Notifier _notifier; @@ -189,24 +37,6 @@ namespace Umbraco.Cms.Core.Compose public void Initialize() { - //Send notifications for the send to publish action - ContentService.SentToPublish += ContentService_SentToPublish; - //Send notifications for the published action - ContentService.Published += ContentService_Published; - ////Send notifications for the saved action - //ContentService.Sorted += ContentService_Sorted; - ////Send notifications for the update and created actions - //ContentService.Saved += ContentService_Saved; - //Send notifications for the unpublish action - ContentService.Unpublished += ContentService_Unpublished; - //Send notifications for the move/move to recycle bin and restore actions - ContentService.Moved += ContentService_Moved; - //Send notifications for the delete action when content is moved to the recycle bin - ContentService.Trashed += ContentService_Trashed; - //Send notifications for the copy action - ContentService.Copied += ContentService_Copied; - //Send notifications for the rollback action - ContentService.RolledBack += ContentService_RolledBack; //Send notifications for the public access changed action PublicAccessService.Saved += PublicAccessService_Saved; @@ -215,15 +45,6 @@ namespace Umbraco.Cms.Core.Compose public void Terminate() { - ContentService.SentToPublish -= ContentService_SentToPublish; - ContentService.Published -= ContentService_Published; - //ContentService.Sorted -= ContentService_Sorted; - //ContentService.Saved -= ContentService_Saved; - ContentService.Unpublished -= ContentService_Unpublished; - ContentService.Moved -= ContentService_Moved; - ContentService.Trashed -= ContentService_Trashed; - ContentService.Copied -= ContentService_Copied; - ContentService.RolledBack -= ContentService_RolledBack; PublicAccessService.Saved -= PublicAccessService_Saved; UserService.UserGroupPermissionsAssigned -= UserService_UserGroupPermissionsAssigned; } @@ -234,72 +55,6 @@ namespace Umbraco.Cms.Core.Compose private void PublicAccessService_Saved(IPublicAccessService sender, SaveEventArgs args) => PublicAccessServiceSaved(args, _contentService); - private void ContentService_RolledBack(IContentService sender, RollbackEventArgs args) - => _notifier.Notify(_actions.GetAction(), args.Entity); - - private void ContentService_Copied(IContentService sender, CopyEventArgs args) - => _notifier.Notify(_actions.GetAction(), args.Original); - - private void ContentService_Trashed(IContentService sender, MoveEventArgs args) - => _notifier.Notify(_actions.GetAction(), args.MoveInfoCollection.Select(m => m.Entity).ToArray()); - - private void ContentService_Moved(IContentService sender, MoveEventArgs args) - => ContentServiceMoved(args); - - private void ContentService_Unpublished(IContentService sender, PublishEventArgs args) - => _notifier.Notify(_actions.GetAction(), args.PublishedEntities.ToArray()); - - private void ContentService_Saved(IContentService sender, ContentSavedEventArgs args) - => ContentServiceSaved(args); - - private void ContentService_Sorted(IContentService sender, SaveEventArgs args) - => ContentServiceSorted(sender, args); - - private void ContentService_Published(IContentService sender, ContentPublishedEventArgs args) - => _notifier.Notify(_actions.GetAction(), args.PublishedEntities.ToArray()); - - private void ContentService_SentToPublish(IContentService sender, SendToPublishEventArgs args) - => _notifier.Notify(_actions.GetAction(), args.Entity); - - private void ContentServiceSorted(IContentService sender, SaveEventArgs args) - { - var parentId = args.SavedEntities.Select(x => x.ParentId).Distinct().ToList(); - if (parentId.Count != 1) return; // this shouldn't happen, for sorting all entities will have the same parent id - - // in this case there's nothing to report since if the root is sorted we can't report on a fake entity. - // this is how it was in v7, we can't report on root changes because you can't subscribe to root changes. - if (parentId[0] <= 0) return; - - var parent = sender.GetById(parentId[0]); - if (parent == null) return; // this shouldn't happen - - _notifier.Notify(_actions.GetAction(), new[] { parent }); - } - - private void ContentServiceSaved(SaveEventArgs args) - { - var newEntities = new List(); - var updatedEntities = new List(); - - //need to determine if this is updating or if it is new - foreach (var entity in args.SavedEntities) - { - var dirty = (IRememberBeingDirty)entity; - if (dirty.WasPropertyDirty("Id")) - { - //it's new - newEntities.Add(entity); - } - else - { - //it's updating - updatedEntities.Add(entity); - } - } - _notifier.Notify(_actions.GetAction(), newEntities.ToArray()); - _notifier.Notify(_actions.GetAction(), updatedEntities.ToArray()); - } - private void UserServiceUserGroupPermissionsAssigned(SaveEventArgs args, IContentService contentService) { var entities = contentService.GetByIds(args.SavedEntities.Select(e => e.EntityId)).ToArray(); @@ -310,22 +65,6 @@ namespace Umbraco.Cms.Core.Compose _notifier.Notify(_actions.GetAction(), entities); } - private void ContentServiceMoved(MoveEventArgs args) - { - // notify about the move for all moved items - _notifier.Notify(_actions.GetAction(), args.MoveInfoCollection.Select(m => m.Entity).ToArray()); - - // for any items being moved from the recycle bin (restored), explicitly notify about that too - var restoredEntities = args.MoveInfoCollection - .Where(m => m.OriginalPath.Contains(Constants.System.RecycleBinContentString)) - .Select(m => m.Entity) - .ToArray(); - if (restoredEntities.Any()) - { - _notifier.Notify(_actions.GetAction(), restoredEntities); - } - } - private void PublicAccessServiceSaved(SaveEventArgs args, IContentService contentService) { var entities = contentService.GetByIds(args.SavedEntities.Select(e => e.ProtectedNodeId)).ToArray(); diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs b/src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs new file mode 100644 index 0000000000..34c855286f --- /dev/null +++ b/src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs @@ -0,0 +1,217 @@ +// 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.Configuration.Models; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.Entities; +using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.Services; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.Compose +{ + // TODO: insert these notification handlers in core composition + public sealed class NotificationsHandler : + INotificationHandler>, + INotificationHandler>, + INotificationHandler>, + INotificationHandler>, + INotificationHandler>, + INotificationHandler>, + INotificationHandler>, + INotificationHandler>, + INotificationHandler> + { + private readonly Notifier _notifier; + private readonly ActionCollection _actions; + private readonly IContentService _contentService; + + public NotificationsHandler(Notifier notifier, ActionCollection actions, IContentService contentService) + { + _notifier = notifier; + _actions = actions; + _contentService = contentService; + } + + public void Handle(SavedNotification notification) + { + var newEntities = new List(); + var updatedEntities = new List(); + + //need to determine if this is updating or if it is new + foreach (var entity in notification.SavedEntities) + { + var dirty = (IRememberBeingDirty)entity; + if (dirty.WasPropertyDirty("Id")) + { + //it's new + newEntities.Add(entity); + } + else + { + //it's updating + updatedEntities.Add(entity); + } + } + _notifier.Notify(_actions.GetAction(), newEntities.ToArray()); + _notifier.Notify(_actions.GetAction(), updatedEntities.ToArray()); + } + + public void Handle(SortedNotification notification) + { + var parentId = notification.SortedEntities.Select(x => x.ParentId).Distinct().ToList(); + if (parentId.Count != 1) + return; // this shouldn't happen, for sorting all entities will have the same parent id + + // in this case there's nothing to report since if the root is sorted we can't report on a fake entity. + // this is how it was in v7, we can't report on root changes because you can't subscribe to root changes. + if (parentId[0] <= 0) + return; + + var parent = _contentService.GetById(parentId[0]); + if (parent == null) + return; // this shouldn't happen + + _notifier.Notify(_actions.GetAction(), new[] { parent }); + } + + public void Handle(PublishedNotification notification) => _notifier.Notify(_actions.GetAction(), notification.PublishedEntities.ToArray()); + + public void Handle(MovedNotification notification) + { + // notify about the move for all moved items + _notifier.Notify(_actions.GetAction(), notification.MoveInfoCollection.Select(m => m.Entity).ToArray()); + + // for any items being moved from the recycle bin (restored), explicitly notify about that too + var restoredEntities = notification.MoveInfoCollection + .Where(m => m.OriginalPath.Contains(Constants.System.RecycleBinContentString)) + .Select(m => m.Entity) + .ToArray(); + if (restoredEntities.Any()) + { + _notifier.Notify(_actions.GetAction(), restoredEntities); + } + } + + public void Handle(TrashedNotification notification) => _notifier.Notify(_actions.GetAction(), notification.MoveInfoCollection.Select(m => m.Entity).ToArray()); + + public void Handle(CopiedNotification notification) => _notifier.Notify(_actions.GetAction(), notification.Original); + + public void Handle(RolledBackNotification notification) => _notifier.Notify(_actions.GetAction(), notification.Entity); + + public void Handle(SentToPublishNotification notification) => _notifier.Notify(_actions.GetAction(), notification.Entity); + + public void Handle(UnpublishedNotification notification) => _notifier.Notify(_actions.GetAction(), notification.UnpublishedEntities.ToArray()); + + /// + /// 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/RelateOnCopyComponent.cs b/src/Umbraco.Infrastructure/Compose/RelateOnCopyComponent.cs deleted file mode 100644 index c24e7614e3..0000000000 --- a/src/Umbraco.Infrastructure/Compose/RelateOnCopyComponent.cs +++ /dev/null @@ -1,59 +0,0 @@ -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.Compose -{ - // TODO: This should just exist in the content service/repo! - public sealed class RelateOnCopyComponent : IComponent - { - private readonly IRelationService _relationService; - private readonly IAuditService _auditService; - - public RelateOnCopyComponent(IRelationService relationService, IAuditService auditService) - { - _relationService = relationService; - _auditService = auditService; - } - - public void Initialize() - { - ContentService.Copied += ContentServiceCopied; - } - - public void Terminate() - { - ContentService.Copied -= ContentServiceCopied; - } - - private void ContentServiceCopied(IContentService sender, CopyEventArgs e) - { - if (e.RelateToOriginal == false) return; - - - var relationType = _relationService.GetRelationTypeByAlias(Cms.Core.Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias); - - if (relationType == null) - { - relationType = new RelationType(Cms.Core.Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, - Cms.Core.Constants.Conventions.RelationTypes.RelateDocumentOnCopyName, - true, - Cms.Core.Constants.ObjectTypes.Document, - Cms.Core.Constants.ObjectTypes.Document); - - _relationService.Save(relationType); - } - - var relation = new Relation(e.Original.Id, e.Copy.Id, relationType); - _relationService.Save(relation); - - _auditService.Add( - AuditType.Copy, - e.Copy.WriterId, - e.Copy.Id, ObjectTypes.GetName(UmbracoObjectTypes.Document), - $"Copied content with Id: '{e.Copy.Id}' related to original content with Id: '{e.Original.Id}'"); - } - } -} diff --git a/src/Umbraco.Infrastructure/Compose/RelateOnCopyComposer.cs b/src/Umbraco.Infrastructure/Compose/RelateOnCopyComposer.cs deleted file mode 100644 index ad2a3db78d..0000000000 --- a/src/Umbraco.Infrastructure/Compose/RelateOnCopyComposer.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Umbraco.Cms.Core.Composing; - -namespace Umbraco.Cms.Core.Compose -{ - public sealed class RelateOnCopyComposer : ComponentComposer, ICoreComposer - { } -} diff --git a/src/Umbraco.Infrastructure/Compose/RelateOnTrashComponent.cs b/src/Umbraco.Infrastructure/Compose/RelateOnTrashComponent.cs index de00961357..cdba2d49bc 100644 --- a/src/Umbraco.Infrastructure/Compose/RelateOnTrashComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/RelateOnTrashComponent.cs @@ -1,15 +1,20 @@ -using System.Linq; +using System.Linq; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; +using Umbraco.Cms.Infrastructure.Services; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Compose { - public sealed class RelateOnTrashComponent : IComponent + // TODO: insert these notification handlers in core composition + // TODO: lots of duplicate code in this one, refactor + public sealed class RelateOnTrashHandler : + INotificationHandler>, + INotificationHandler> { private readonly IRelationService _relationService; private readonly IEntityService _entityService; @@ -17,7 +22,7 @@ namespace Umbraco.Cms.Core.Compose private readonly IAuditService _auditService; private readonly IScopeProvider _scopeProvider; - public RelateOnTrashComponent( + public RelateOnTrashHandler( IRelationService relationService, IEntityService entityService, ILocalizedTextService textService, @@ -31,27 +36,10 @@ namespace Umbraco.Cms.Core.Compose _scopeProvider = scopeProvider; } - public void Initialize() + public void Handle(MovedNotification notification) { - ContentService.Moved += ContentService_Moved; - ContentService.Trashed += ContentService_Trashed; - MediaService.Moved += MediaService_Moved; - MediaService.Trashed += MediaService_Trashed; - } - - public void Terminate() - { - ContentService.Moved -= ContentService_Moved; - ContentService.Trashed -= ContentService_Trashed; - MediaService.Moved -= MediaService_Moved; - MediaService.Trashed -= MediaService_Trashed; - } - - private void ContentService_Moved(IContentService sender, MoveEventArgs e) - { - foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Cms.Core.Constants.System.RecycleBinContentString))) + foreach (var item in notification.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Cms.Core.Constants.System.RecycleBinContentString))) { - const string relationTypeAlias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias; var relations = _relationService.GetByChildId(item.Entity.Id); @@ -62,20 +50,7 @@ namespace Umbraco.Cms.Core.Compose } } - private void MediaService_Moved(IMediaService sender, MoveEventArgs e) - { - foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Cms.Core.Constants.System.RecycleBinMediaString))) - { - const string relationTypeAlias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias; - var relations = _relationService.GetByChildId(item.Entity.Id); - foreach (var relation in relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias))) - { - _relationService.Delete(relation); - } - } - } - - private void ContentService_Trashed(IContentService sender, MoveEventArgs e) + public void Handle(TrashedNotification notification) { using (var scope = _scopeProvider.CreateScope()) { @@ -94,7 +69,7 @@ namespace Umbraco.Cms.Core.Compose _relationService.Save(relationType); } - foreach (var item in e.MoveInfoCollection) + foreach (var item in notification.MoveInfoCollection) { var originalPath = item.OriginalPath.ToDelimitedList(); var originalParentId = originalPath.Count > 2 @@ -124,6 +99,55 @@ namespace Umbraco.Cms.Core.Compose scope.Complete(); } } + } + + + public sealed class RelateOnTrashComponent : IComponent + { + private readonly IRelationService _relationService; + private readonly IEntityService _entityService; + private readonly ILocalizedTextService _textService; + private readonly IAuditService _auditService; + private readonly IScopeProvider _scopeProvider; + + public RelateOnTrashComponent( + IRelationService relationService, + IEntityService entityService, + ILocalizedTextService textService, + IAuditService auditService, + IScopeProvider scopeProvider) + { + _relationService = relationService; + _entityService = entityService; + _textService = textService; + _auditService = auditService; + _scopeProvider = scopeProvider; + } + + public void Initialize() + { + MediaService.Moved += MediaService_Moved; + MediaService.Trashed += MediaService_Trashed; + } + + public void Terminate() + { + MediaService.Moved -= MediaService_Moved; + MediaService.Trashed -= MediaService_Trashed; + } + + private void MediaService_Moved(IMediaService sender, MoveEventArgs e) + { + foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Cms.Core.Constants.System.RecycleBinMediaString))) + { + const string relationTypeAlias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias; + var relations = _relationService.GetByChildId(item.Entity.Id); + foreach (var relation in relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias))) + { + _relationService.Delete(relation); + } + } + } public void MediaService_Trashed(IMediaService sender, MoveEventArgs e) { diff --git a/src/Umbraco.Infrastructure/Compose/BlockEditorComponent.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyHandler.cs similarity index 92% rename from src/Umbraco.Infrastructure/Compose/BlockEditorComponent.cs rename to src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyHandler.cs index f54b52db51..5ce37d7b75 100644 --- a/src/Umbraco.Infrastructure/Compose/BlockEditorComponent.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyHandler.cs @@ -1,36 +1,28 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Models.Blocks; -using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Extensions; -namespace Umbraco.Cms.Core.Compose +namespace Umbraco.Cms.Core.PropertyEditors { /// - /// A component for Block editors used to bind to events + /// A handler for Block editors used to bind to notifications /// - public class BlockEditorComponent : IComponent + // TODO: insert these notification handlers in core composition + public class BlockEditorPropertyHandler : ComplexPropertyEditorContentNotificationHandler { - private ComplexPropertyEditorContentEventHandler _handler; private readonly BlockListEditorDataConverter _converter = new BlockListEditorDataConverter(); - public void Initialize() - { - _handler = new ComplexPropertyEditorContentEventHandler( - Constants.PropertyEditors.Aliases.BlockList, - ReplaceBlockListUdis); - } + protected override string EditorAlias => Constants.PropertyEditors.Aliases.BlockList; - public void Terminate() => _handler?.Dispose(); - - private string ReplaceBlockListUdis(string rawJson, bool onlyMissingUdis) + protected override string FormatPropertyValue(string rawJson, bool onlyMissingKeys) { // the block editor doesn't ever have missing UDIs so when this is true there's nothing to process - if (onlyMissingUdis) return rawJson; + if (onlyMissingKeys) + return rawJson; return ReplaceBlockListUdis(rawJson, null); } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentEventHandler.cs b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentEventHandler.cs deleted file mode 100644 index 02b7d53108..0000000000 --- a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentEventHandler.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System; -using System.Collections.Generic; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Implement; -using Umbraco.Cms.Infrastructure.Services; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Core.PropertyEditors -{ - // TODO: add copying handling - public abstract class ComplexPropertyEditorContentNotificationHandler - : INotificationHandler> - { - private readonly string _editorAlias; - private readonly Func _formatPropertyValue; - - protected ComplexPropertyEditorContentNotificationHandler(string editorAlias, Func formatPropertyValue) - { - _editorAlias = editorAlias; - _formatPropertyValue = formatPropertyValue; - } - - public void Handle(SavingNotification notification) - { - foreach (var entity in notification.SavedEntities) - { - var props = entity.GetPropertiesByEditor(_editorAlias); - UpdatePropertyValues(props, true); - } - } - - private void UpdatePropertyValues(IEnumerable props, bool onlyMissingKeys) - { - foreach (var prop in props) - { - // A Property may have one or more values due to cultures - var propVals = prop.Values; - foreach (var cultureVal in propVals) - { - // Remove keys from published value & any nested properties - var updatedPublishedVal = _formatPropertyValue(cultureVal.PublishedValue?.ToString(), onlyMissingKeys); - cultureVal.PublishedValue = updatedPublishedVal; - - // Remove keys from edited/draft value & any nested properties - var updatedEditedVal = _formatPropertyValue(cultureVal.EditedValue?.ToString(), onlyMissingKeys); - cultureVal.EditedValue = updatedEditedVal; - } - } - } - } - - /// - /// Utility class for dealing with Copying/Saving events for complex editors - /// - public class ComplexPropertyEditorContentEventHandler : IDisposable - { - private readonly string _editorAlias; - private readonly Func _formatPropertyValue; - private bool _disposedValue; - - public ComplexPropertyEditorContentEventHandler(string editorAlias, - Func formatPropertyValue) - { - _editorAlias = editorAlias; - _formatPropertyValue = formatPropertyValue; - ContentService.Copying += ContentService_Copying; - //ContentService.Saving += ContentService_Saving; - } - - /// - /// Copying event handler - /// - /// - /// - private void ContentService_Copying(IContentService sender, CopyEventArgs e) - { - var props = e.Copy.GetPropertiesByEditor(_editorAlias); - UpdatePropertyValues(props, false); - } - - /// - /// Saving event handler - /// - /// - /// - private void ContentService_Saving(IContentService sender, ContentSavingEventArgs e) - { - foreach (var entity in e.SavedEntities) - { - var props = entity.GetPropertiesByEditor(_editorAlias); - UpdatePropertyValues(props, true); - } - } - - private void UpdatePropertyValues(IEnumerable props, bool onlyMissingKeys) - { - foreach (var prop in props) - { - // A Property may have one or more values due to cultures - var propVals = prop.Values; - foreach (var cultureVal in propVals) - { - // Remove keys from published value & any nested properties - var updatedPublishedVal = _formatPropertyValue(cultureVal.PublishedValue?.ToString(), onlyMissingKeys); - cultureVal.PublishedValue = updatedPublishedVal; - - // Remove keys from edited/draft value & any nested properties - var updatedEditedVal = _formatPropertyValue(cultureVal.EditedValue?.ToString(), onlyMissingKeys); - cultureVal.EditedValue = updatedEditedVal; - } - } - } - - /// - /// Unbinds from events - /// - /// - protected virtual void Dispose(bool disposing) - { - if (!_disposedValue) - { - if (disposing) - { - ContentService.Copying -= ContentService_Copying; - //ContentService.Saving -= ContentService_Saving; - } - _disposedValue = true; - } - } - - /// - /// Unbinds from events - /// - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } -} diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs new file mode 100644 index 0000000000..083dce1a96 --- /dev/null +++ b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs @@ -0,0 +1,55 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Infrastructure.Services; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.PropertyEditors +{ + // TODO: insert these notification handlers in core composition + public abstract class ComplexPropertyEditorContentNotificationHandler : + INotificationHandler>, + INotificationHandler> + { + protected abstract string EditorAlias { get; } + + protected abstract string FormatPropertyValue(string rawJson, bool onlyMissingKeys); + + public void Handle(SavingNotification notification) + { + foreach (var entity in notification.SavedEntities) + { + var props = entity.GetPropertiesByEditor(EditorAlias); + UpdatePropertyValues(props, true); + } + } + + public void Handle(CopyingNotification notification) + { + var props = notification.Copy.GetPropertiesByEditor(EditorAlias); + UpdatePropertyValues(props, false); + } + + private void UpdatePropertyValues(IEnumerable props, bool onlyMissingKeys) + { + foreach (var prop in props) + { + // A Property may have one or more values due to cultures + var propVals = prop.Values; + foreach (var cultureVal in propVals) + { + // Remove keys from published value & any nested properties + var updatedPublishedVal = FormatPropertyValue(cultureVal.PublishedValue?.ToString(), onlyMissingKeys); + cultureVal.PublishedValue = updatedPublishedVal; + + // Remove keys from edited/draft value & any nested properties + var updatedEditedVal = FormatPropertyValue(cultureVal.EditedValue?.ToString(), onlyMissingKeys); + cultureVal.EditedValue = updatedEditedVal; + } + } + } + } +} diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs index 28d35e60d2..aa9e8089f2 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -14,6 +14,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; +using Umbraco.Cms.Infrastructure.Services; using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors @@ -24,7 +25,8 @@ namespace Umbraco.Cms.Core.PropertyEditors "fileupload", Group = Constants.PropertyEditors.Groups.Media, Icon = "icon-download-alt")] - public class FileUploadPropertyEditor : DataEditor, IMediaUrlGenerator + // TODO: insert these notification handlers in core composition + public class FileUploadPropertyEditor : DataEditor, IMediaUrlGenerator, INotificationHandler>, INotificationHandler> { private readonly IMediaFileSystem _mediaFileSystem; private readonly ContentSettings _contentSettings; @@ -32,6 +34,7 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly IDataTypeService _dataTypeService; private readonly ILocalizationService _localizationService; private readonly ILocalizedTextService _localizedTextService; + private readonly IContentService _contentService; public FileUploadPropertyEditor( ILoggerFactory loggerFactory, @@ -42,7 +45,8 @@ namespace Umbraco.Cms.Core.PropertyEditors ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, UploadAutoFillProperties uploadAutoFillProperties, - IJsonSerializer jsonSerializer) + IJsonSerializer jsonSerializer, + IContentService contentService) : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) { _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); @@ -51,6 +55,7 @@ namespace Umbraco.Cms.Core.PropertyEditors _localizationService = localizationService; _localizedTextService = localizedTextService; _uploadAutoFillProperties = uploadAutoFillProperties; + _contentService = contentService; } /// @@ -119,37 +124,6 @@ namespace Umbraco.Cms.Core.PropertyEditors } } - /// - /// After a content has been copied, also copy uploaded files. - /// - /// The event sender. - /// The event arguments. - internal void ContentServiceCopied(IContentService sender, CopyEventArgs args) - { - // get the upload field properties with a value - var properties = args.Original.Properties.Where(IsUploadField); - - // copy files - var isUpdated = false; - foreach (var property in properties) - { - //copy each of the property values (variants, segments) to the destination - foreach (var propertyValue in property.Values) - { - var propVal = property.GetValue(propertyValue.Culture, propertyValue.Segment); - if (propVal == null || !(propVal is string str) || str.IsNullOrWhiteSpace()) continue; - var sourcePath = _mediaFileSystem.GetRelativePath(str); - var copyPath = _mediaFileSystem.CopyFile(args.Copy, property.PropertyType, sourcePath); - args.Copy.SetValue(property.Alias, _mediaFileSystem.GetUrl(copyPath), propertyValue.Culture, propertyValue.Segment); - isUpdated = true; - } - } - - // if updated, re-save the copy with the updated value - if (isUpdated) - sender.Save(args.Copy); - } - /// /// After a media has been created, auto-fill the properties. /// @@ -182,6 +156,40 @@ namespace Umbraco.Cms.Core.PropertyEditors AutoFillProperties(entity); } + public void Handle(CopiedNotification notification) + { + // get the upload field properties with a value + var properties = notification.Original.Properties.Where(IsUploadField); + + // copy files + var isUpdated = false; + foreach (var property in properties) + { + //copy each of the property values (variants, segments) to the destination + foreach (var propertyValue in property.Values) + { + var propVal = property.GetValue(propertyValue.Culture, propertyValue.Segment); + if (propVal == null || !(propVal is string str) || str.IsNullOrWhiteSpace()) + { + continue; + } + + var sourcePath = _mediaFileSystem.GetRelativePath(str); + var copyPath = _mediaFileSystem.CopyFile(notification.Copy, property.PropertyType, sourcePath); + notification.Copy.SetValue(property.Alias, _mediaFileSystem.GetUrl(copyPath), propertyValue.Culture, propertyValue.Segment); + isUpdated = true; + } + } + + // if updated, re-save the copy with the updated value + if (isUpdated) + { + _contentService.Save(notification.Copy); + } + } + + public void Handle(DeletedNotification notification) => notification.MediaFilesToDelete.AddRange(ServiceDeleted(notification.DeletedEntities.OfType())); + /// /// Auto-fill properties (or clear). /// diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs index 74bd7823e3..26b78cade5 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -16,6 +16,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; +using Umbraco.Cms.Infrastructure.Services; using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors @@ -31,7 +32,8 @@ namespace Umbraco.Cms.Core.PropertyEditors HideLabel = false, Group = Constants.PropertyEditors.Groups.Media, Icon = "icon-crop")] - public class ImageCropperPropertyEditor : DataEditor, IMediaUrlGenerator + // TODO: insert these notification handlers in core composition + public class ImageCropperPropertyEditor : DataEditor, IMediaUrlGenerator, INotificationHandler>, INotificationHandler> { private readonly IMediaFileSystem _mediaFileSystem; private readonly ContentSettings _contentSettings; @@ -39,6 +41,7 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly IIOHelper _ioHelper; private readonly UploadAutoFillProperties _autoFillProperties; private readonly ILogger _logger; + private readonly IContentService _contentService; /// /// Initializes a new instance of the class. @@ -53,7 +56,8 @@ namespace Umbraco.Cms.Core.PropertyEditors IShortStringHelper shortStringHelper, ILocalizedTextService localizedTextService, UploadAutoFillProperties uploadAutoFillProperties, - IJsonSerializer jsonSerializer) + IJsonSerializer jsonSerializer, + IContentService contentService) : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) { _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); @@ -61,6 +65,7 @@ namespace Umbraco.Cms.Core.PropertyEditors _dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService)); _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); _autoFillProperties = uploadAutoFillProperties ?? throw new ArgumentNullException(nameof(uploadAutoFillProperties)); + _contentService = contentService; _logger = loggerFactory.CreateLogger(); } @@ -175,12 +180,10 @@ namespace Umbraco.Cms.Core.PropertyEditors /// /// After a content has been copied, also copy uploaded files. /// - /// The event sender. - /// The event arguments. - public void ContentServiceCopied(IContentService sender, CopyEventArgs args) + public void Handle(CopiedNotification notification) { // get the image cropper field properties - var properties = args.Original.Properties.Where(IsCropperField); + var properties = notification.Original.Properties.Where(IsCropperField); // copy files var isUpdated = false; @@ -191,19 +194,27 @@ namespace Umbraco.Cms.Core.PropertyEditors { var propVal = property.GetValue(propertyValue.Culture, propertyValue.Segment); var src = GetFileSrcFromPropertyValue(propVal, out var jo); - if (src == null) continue; + if (src == null) + { + continue; + } var sourcePath = _mediaFileSystem.GetRelativePath(src); - var copyPath = _mediaFileSystem.CopyFile(args.Copy, property.PropertyType, sourcePath); + var copyPath = _mediaFileSystem.CopyFile(notification.Copy, property.PropertyType, sourcePath); jo["src"] = _mediaFileSystem.GetUrl(copyPath); - args.Copy.SetValue(property.Alias, jo.ToString(), propertyValue.Culture, propertyValue.Segment); + notification.Copy.SetValue(property.Alias, jo.ToString(), propertyValue.Culture, propertyValue.Segment); isUpdated = true; } } // if updated, re-save the copy with the updated value if (isUpdated) - sender.Save(args.Copy); + { + _contentService.Save(notification.Copy); + } + } + public void Handle(DeletedNotification notification) => notification.MediaFilesToDelete.AddRange(ServiceDeleted(notification.DeletedEntities.OfType())); + /// /// After a media has been created, auto-fill the properties. /// diff --git a/src/Umbraco.Infrastructure/Compose/NestedContentPropertyComponent.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyHandler.cs similarity index 78% rename from src/Umbraco.Infrastructure/Compose/NestedContentPropertyComponent.cs rename to src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyHandler.cs index 332c2464ad..887c547778 100644 --- a/src/Umbraco.Infrastructure/Compose/NestedContentPropertyComponent.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyHandler.cs @@ -1,29 +1,19 @@ -using System; +using System; using System.Linq; using Newtonsoft.Json.Linq; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Extensions; -namespace Umbraco.Cms.Core.Compose +namespace Umbraco.Cms.Core.PropertyEditors { /// - /// A component for NestedContent used to bind to events + /// A handler for NestedContent used to bind to notifications /// - public class NestedContentPropertyComponent : IComponent + // TODO: insert these notification handlers in core composition + public class NestedContentPropertyHandler : ComplexPropertyEditorContentNotificationHandler { - private ComplexPropertyEditorContentEventHandler _handler; + protected override string EditorAlias => Constants.PropertyEditors.Aliases.NestedContent; - public void Initialize() - { - _handler = new ComplexPropertyEditorContentEventHandler( - Constants.PropertyEditors.Aliases.NestedContent, - CreateNestedContentKeys); - } - - public void Terminate() => _handler?.Dispose(); - - private string CreateNestedContentKeys(string rawJson, bool onlyMissingKeys) => CreateNestedContentKeys(rawJson, onlyMissingKeys, null); + protected override string FormatPropertyValue(string rawJson, bool onlyMissingKeys) => CreateNestedContentKeys(rawJson, onlyMissingKeys, null); // internal for tests internal string CreateNestedContentKeys(string rawJson, bool onlyMissingKeys, Func createGuid = null) @@ -78,6 +68,5 @@ namespace Umbraco.Cms.Core.Compose } } } - } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs b/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs index f9cb83118d..c870d85139 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -42,17 +42,11 @@ namespace Umbraco.Cms.Core.PropertyEditors { MediaService.Saving += fileUpload.MediaServiceSaving; _terminate.Add(() => MediaService.Saving -= fileUpload.MediaServiceSaving); - ContentService.Copied += fileUpload.ContentServiceCopied; - _terminate.Add(() => ContentService.Copied -= fileUpload.ContentServiceCopied); void mediaServiceDeleted(IMediaService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(fileUpload.ServiceDeleted(args.DeletedEntities.Cast())); MediaService.Deleted += mediaServiceDeleted; _terminate.Add(() => MediaService.Deleted -= mediaServiceDeleted); - void contentServiceDeleted(IContentService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(fileUpload.ServiceDeleted(args.DeletedEntities.Cast())); - ContentService.Deleted += contentServiceDeleted; - _terminate.Add(() => ContentService.Deleted -= contentServiceDeleted); - void memberServiceDeleted(IMemberService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(fileUpload.ServiceDeleted(args.DeletedEntities.Cast())); MemberService.Deleted += memberServiceDeleted; _terminate.Add(() => MemberService.Deleted -= memberServiceDeleted); @@ -62,17 +56,11 @@ namespace Umbraco.Cms.Core.PropertyEditors { MediaService.Saving += imageCropper.MediaServiceSaving; _terminate.Add(() => MediaService.Saving -= imageCropper.MediaServiceSaving); - ContentService.Copied += imageCropper.ContentServiceCopied; - _terminate.Add(() => ContentService.Copied -= imageCropper.ContentServiceCopied); void mediaServiceDeleted(IMediaService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(imageCropper.ServiceDeleted(args.DeletedEntities.Cast())); MediaService.Deleted += mediaServiceDeleted; _terminate.Add(() => MediaService.Deleted -= mediaServiceDeleted); - void contentServiceDeleted(IContentService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(imageCropper.ServiceDeleted(args.DeletedEntities.Cast())); - ContentService.Deleted += contentServiceDeleted; - _terminate.Add(() => ContentService.Deleted -= contentServiceDeleted); - void memberServiceDeleted(IMemberService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(imageCropper.ServiceDeleted(args.DeletedEntities.Cast())); MemberService.Deleted += memberServiceDeleted; _terminate.Add(() => MemberService.Deleted -= memberServiceDeleted); diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTrackingComposer.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingComposer.cs deleted file mode 100644 index e56fbda4d7..0000000000 --- a/src/Umbraco.Infrastructure/Routing/RedirectTrackingComposer.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Umbraco.Cms.Core.Composing; - -namespace Umbraco.Cms.Core.Routing -{ - /// - /// Implements an Application Event Handler for managing redirect URLs tracking. - /// - /// - /// when content is renamed or moved, we want to create a permanent 301 redirect from it's old URL - /// not managing domains because we don't know how to do it - changing domains => must create a higher level strategy using rewriting rules probably - /// recycle bin = moving to and from does nothing: to = the node is gone, where would we redirect? from = same - /// - public class RedirectTrackingComposer : ComponentComposer, ICoreComposer - { } -} diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs similarity index 58% rename from src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs rename to src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs index f6d48fa057..c1456af8b3 100644 --- a/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs @@ -1,36 +1,38 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; 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; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Implement; +using Umbraco.Cms.Infrastructure.Services; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Routing { - /// Implements an Application Event Handler for managing redirect URLs tracking. + /// Implements a notification handler for managing redirect URLs tracking. /// when content is renamed or moved, we want to create a permanent 301 redirect from it's old URL /// /// not managing domains because we don't know how to do it - changing domains => must create a higher level /// strategy using rewriting rules probably /// /// recycle bin = moving to and from does nothing: to = the node is gone, where would we redirect? from = same - public sealed class RedirectTrackingComponent : IComponent + // TODO: insert these notification handlers in core composition + public sealed class RedirectTrackingHandler : + INotificationHandler>, + INotificationHandler>, + INotificationHandler>, + INotificationHandler> { - private const string _eventStateKey = "Umbraco.Web.Redirects.RedirectTrackingEventHandler"; - private readonly IOptionsMonitor _webRoutingSettings; private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly IRedirectUrlService _redirectUrlService; private readonly IVariationContextAccessor _variationContextAccessor; - public RedirectTrackingComponent(IOptionsMonitor webRoutingSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService, IVariationContextAccessor variationContextAccessor) + public RedirectTrackingHandler(IOptionsMonitor webRoutingSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService, IVariationContextAccessor variationContextAccessor) { _webRoutingSettings = webRoutingSettings; _publishedSnapshotAccessor = publishedSnapshotAccessor; @@ -38,86 +40,62 @@ namespace Umbraco.Cms.Core.Routing _variationContextAccessor = variationContextAccessor; } - public void Initialize() + public void Handle(PublishingNotification notification) { - ContentService.Publishing += ContentService_Publishing; - ContentService.Published += ContentService_Published; - ContentService.Moving += ContentService_Moving; - ContentService.Moved += ContentService_Moved; + // don't let the notification handlers kick in if Redirect Tracking is turned off in the config + if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) + return; - // kill all redirects once a content is deleted - //ContentService.Deleted += ContentService_Deleted; - // BUT, doing it here would prevent content deletion due to FK - // so the rows are actually deleted by the ContentRepository (see GetDeleteClauses) - - // rolled back items have to be published, so publishing will take care of that - } - - public void Terminate() - { - ContentService.Publishing -= ContentService_Publishing; - ContentService.Published -= ContentService_Published; - ContentService.Moving -= ContentService_Moving; - ContentService.Moved -= ContentService_Moved; - } - - private void ContentService_Publishing(IContentService sender, PublishEventArgs args) - { - // don't let the event handlers kick in if Redirect Tracking is turned off in the config - if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) return; - - var oldRoutes = GetOldRoutes(args.EventState); - foreach (var entity in args.PublishedEntities) + var oldRoutes = GetOldRoutes(); + foreach (var entity in notification.PublishedEntities) { StoreOldRoute(entity, oldRoutes); } } - private void ContentService_Published(IContentService sender, ContentPublishedEventArgs args) + public void Handle(PublishedNotification notification) { - // don't let the event handlers kick in if Redirect Tracking is turned off in the config - if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) return; + // don't let the notification handlers kick in if Redirect Tracking is turned off in the config + if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) + return; - var oldRoutes = GetOldRoutes(args.EventState); + var oldRoutes = GetOldRoutes(); CreateRedirects(oldRoutes); } - private void ContentService_Moving(IContentService sender, MoveEventArgs args) + public void Handle(MovingNotification notification) { - // don't let the event handlers kick in if Redirect Tracking is turned off in the config - if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) return; + // don't let the notification handlers kick in if Redirect Tracking is turned off in the config + if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) + return; - var oldRoutes = GetOldRoutes(args.EventState); - foreach (var info in args.MoveInfoCollection) + var oldRoutes = GetOldRoutes(); + foreach (var info in notification.MoveInfoCollection) { StoreOldRoute(info.Entity, oldRoutes); } } - private void ContentService_Moved(IContentService sender, MoveEventArgs args) + // TODO refactor (this is duplicate code, see published notification handling above) + public void Handle(MovedNotification notification) { - // don't let the event handlers kick in if Redirect Tracking is turned off in the config - if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) return; + // don't let the notification handlers kick in if Redirect Tracking is turned off in the config + if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) + return; - var oldRoutes = GetOldRoutes(args.EventState); + var oldRoutes = GetOldRoutes(); CreateRedirects(oldRoutes); } - private OldRoutesDictionary GetOldRoutes(IDictionary eventState) - { - if (! eventState.ContainsKey(_eventStateKey)) - { - eventState[_eventStateKey] = new OldRoutesDictionary(); - } - - return eventState[_eventStateKey] as OldRoutesDictionary; - } + // TODO: figure out how to do notification state / temp state + private OldRoutesDictionary GetOldRoutes() => new OldRoutesDictionary(); private void StoreOldRoute(IContent entity, OldRoutesDictionary oldRoutes) { var contentCache = _publishedSnapshotAccessor.PublishedSnapshot.Content; var entityContent = contentCache.GetById(entity.Id); - if (entityContent == null) return; + if (entityContent == null) + return; // get the default affected cultures by going up the tree until we find the first culture variant entity (default to no cultures) var defaultCultures = entityContent.AncestorsOrSelf()?.FirstOrDefault(a => a.Cultures.Any())?.Cultures.Keys.ToArray() @@ -130,7 +108,8 @@ namespace Umbraco.Cms.Core.Routing foreach (var culture in cultures) { var route = contentCache.GetRouteById(x.Id, culture); - if (IsNotRoute(route)) continue; + if (IsNotRoute(route)) + continue; oldRoutes[new ContentIdAndCulture(x.Id, culture)] = new ContentKeyAndOldRoute(x.Key, route); } } @@ -143,7 +122,8 @@ namespace Umbraco.Cms.Core.Routing foreach (var oldRoute in oldRoutes) { var newRoute = contentCache.GetRouteById(oldRoute.Key.ContentId, oldRoute.Key.Culture); - if (IsNotRoute(newRoute) || oldRoute.Value.OldRoute == newRoute) continue; + if (IsNotRoute(newRoute) || oldRoute.Value.OldRoute == newRoute) + continue; _redirectUrlService.Register(oldRoute.Value.OldRoute, oldRoute.Value.ContentKey, oldRoute.Key.Culture); } } @@ -176,6 +156,8 @@ namespace Umbraco.Cms.Core.Routing } private class OldRoutesDictionary : Dictionary - { } + { + + } } } diff --git a/src/Umbraco.Infrastructure/Services/CancelableNotification.cs b/src/Umbraco.Infrastructure/Services/CancelableNotification.cs index 178ce3df9b..bc4de2ce49 100644 --- a/src/Umbraco.Infrastructure/Services/CancelableNotification.cs +++ b/src/Umbraco.Infrastructure/Services/CancelableNotification.cs @@ -1,11 +1,16 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; using System.Collections.Generic; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services { // TODO split this file into several small classes and move to another namespace - public interface ICancelableNotification + public interface ICancelableNotification : INotification { bool Cancel { get; set; } } @@ -34,7 +39,7 @@ namespace Umbraco.Cms.Infrastructure.Services } } - public abstract class CancelableObjectNotification : ObjectNotification where T : class + public abstract class CancelableObjectNotification : ObjectNotification, ICancelableNotification where T : class { protected CancelableObjectNotification(T target, EventMessages messages) : base(target, messages) { @@ -66,16 +71,33 @@ namespace Umbraco.Cms.Infrastructure.Services { } + public DeletingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + public IEnumerable DeletedEntities => Target; } public class DeletedNotification : EnumerableObjectNotification { - public DeletedNotification(T target, EventMessages messages) : base(target, messages) + public DeletedNotification(T target, EventMessages messages) : base(target, messages) => MediaFilesToDelete = new List(); + + public IEnumerable DeletedEntities => Target; + + public List MediaFilesToDelete { get; } + } + + public class DeletedBlueprintNotification : EnumerableObjectNotification + { + public DeletedBlueprintNotification(T target, EventMessages messages) : base(target, messages) { } - public IEnumerable DeletedEntities => Target; + public DeletedBlueprintNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable DeletedBlueprints => Target; } public class SortingNotification : CancelableEnumerableObjectNotification @@ -121,4 +143,243 @@ namespace Umbraco.Cms.Infrastructure.Services public IEnumerable SavedEntities => Target; } + + public class SavedBlueprintNotification : ObjectNotification where T : class + { + public SavedBlueprintNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public T SavedBlueprint => Target; + } + + public class PublishingNotification : CancelableEnumerableObjectNotification + { + public PublishingNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public PublishingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable PublishedEntities => Target; + } + + public class PublishedNotification : EnumerableObjectNotification + { + public PublishedNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public PublishedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable PublishedEntities => Target; + } + + public class MovingNotification : CancelableObjectNotification>> + { + public MovingNotification(MoveEventInfo target, EventMessages messages) : base(new[] {target}, messages) + { + } + + public MovingNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable> MoveInfoCollection => Target; + } + + public class MovedNotification : ObjectNotification>> + { + public MovedNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) + { + } + + public MovedNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable> MoveInfoCollection => Target; + } + + public class TrashingNotification : CancelableObjectNotification>> + { + public TrashingNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) + { + } + + public TrashingNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable> MoveInfoCollection => Target; + } + + public class TrashedNotification : ObjectNotification>> + { + public TrashedNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) + { + } + + public TrashedNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable> MoveInfoCollection => Target; + } + + public class CopyingNotification : CancelableObjectNotification where T : class + { + public CopyingNotification(T original, T copy, int parentId, EventMessages messages) : base(original, messages) + { + Copy = copy; + ParentId = parentId; + } + + public T Original => Target; + + public T Copy { get; } + + public int ParentId { get; } + } + + public class CopiedNotification : ObjectNotification where T : class + { + public CopiedNotification(T original, T copy, int parentId, EventMessages messages) : base(original, messages) + { + Copy = copy; + ParentId = parentId; + } + + public T Original => Target; + + public T Copy { get; } + + public int ParentId { get; } + } + + public class RollingBackNotification : CancelableObjectNotification where T : class + { + public RollingBackNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public T Entity => Target; + } + + public class RolledBackNotification : ObjectNotification where T : class + { + public RolledBackNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public T Entity => Target; + } + + public class SendingToPublishNotification : CancelableObjectNotification where T : class + { + public SendingToPublishNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public T Entity => Target; + } + + public class SentToPublishNotification : ObjectNotification where T : class + { + public SentToPublishNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public T Entity => Target; + } + + + public class UnpublishingNotification : CancelableEnumerableObjectNotification + { + public UnpublishingNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public UnpublishingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable UnpublishedEntities => Target; + } + + public class UnpublishedNotification : EnumerableObjectNotification + { + public UnpublishedNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public UnpublishedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable UnpublishedEntities => Target; + } + + public class EmptiedRecycleBinNotification : INotification + { + public EmptiedRecycleBinNotification(Guid nodeObjectType, EventMessages messages) + { + NodeObjectType = nodeObjectType; + Messages = messages; + } + + public Guid NodeObjectType { get; } + + public EventMessages Messages { get; } + + public bool IsContentRecycleBin => NodeObjectType == Constants.ObjectTypes.Document; + + public bool IsMediaRecycleBin => NodeObjectType == Constants.ObjectTypes.Media; + } + + public class EmptyingRecycleBinNotification : EmptiedRecycleBinNotification, ICancelableNotification + { + public EmptyingRecycleBinNotification(Guid nodeObjectType, EventMessages messages) + : base(nodeObjectType, messages) + { + } + + public bool Cancel { get; set; } + } + + public class DeletedVersionsNotification : INotification + { + public DeletedVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) + { + Id = id; + Messages = messages; + SpecificVersion = specificVersion; + DeletePriorVersions = deletePriorVersions; + DateToRetain = dateToRetain; + } + + public int Id { get; } + + public EventMessages Messages { get; } + + public int SpecificVersion { get; } + + public bool DeletePriorVersions { get; } + + public DateTime DateToRetain { get; } + } + + public class DeletingVersionsNotification : DeletedVersionsNotification, ICancelableNotification + { + public DeletingVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) + : base(id, messages, specificVersion, deletePriorVersions, dateToRetain) + { + } + + public bool Cancel { get; set; } + } } diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index 379ff9a0a2..64ff7260c9 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -34,6 +34,7 @@ namespace Umbraco.Cms.Core.Services.Implement private readonly ILogger _logger; private IQuery _queryNotTrashed; private readonly IEventAggregator _eventAggregator; + private readonly IRelationService _relationService; #region Constructors @@ -41,7 +42,7 @@ namespace Umbraco.Cms.Core.Services.Implement IEventMessagesFactory eventMessagesFactory, IDocumentRepository documentRepository, IEntityRepository entityRepository, IAuditRepository auditRepository, IContentTypeRepository contentTypeRepository, IDocumentBlueprintRepository documentBlueprintRepository, ILanguageRepository languageRepository, - Lazy propertyValidationService, IShortStringHelper shortStringHelper, IEventAggregator eventAggregator) + Lazy propertyValidationService, IShortStringHelper shortStringHelper, IEventAggregator eventAggregator, IRelationService relationService) : base(provider, loggerFactory, eventMessagesFactory) { _documentRepository = documentRepository; @@ -53,6 +54,7 @@ namespace Umbraco.Cms.Core.Services.Implement _propertyValidationService = propertyValidationService; _shortStringHelper = shortStringHelper; _eventAggregator = eventAggregator; + _relationService = relationService; _logger = loggerFactory.CreateLogger(); } @@ -1150,7 +1152,7 @@ namespace Umbraco.Cms.Core.Services.Implement : null; // ensure that the document can be published, and publish handling events, business rules, etc - publishResult = StrategyCanPublish(scope, content, /*checkPath:*/ (!branchOne || branchRoot), culturesPublishing, culturesUnpublishing, evtMsgs, saveEventArgs, allLangs); + publishResult = StrategyCanPublish(scope, content, /*checkPath:*/ (!branchOne || branchRoot), culturesPublishing, culturesUnpublishing, evtMsgs, allLangs); if (publishResult.Success) { // note: StrategyPublish flips the PublishedState to Publishing! @@ -1243,7 +1245,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (unpublishResult.Success) // and succeeded, trigger events { // events and audit - scope.Events.Dispatch(Unpublished, this, new PublishEventArgs(content, false, false), "Unpublished"); + _eventAggregator.Publish(new UnpublishedNotification(content, evtMsgs)); scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, TreeChangeTypes.RefreshBranch).ToEventArgs()); if (culturesUnpublishing != null) @@ -1298,7 +1300,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!branchOne) // for branches, handled by SaveAndPublishBranch { scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); - scope.Events.Dispatch(Published, this, saveEventArgs.ToContentPublishedEventArgs(), nameof(Published)); + _eventAggregator.Publish(new PublishingNotification(content, evtMsgs)); } // it was not published and now is... descendants that were 'published' (but @@ -1307,7 +1309,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!branchOne && isNew == false && previouslyPublished == false && HasChildren(content.Id)) { var descendants = GetPublishedDescendantsLocked(content).ToArray(); - scope.Events.Dispatch(Published, this, new ContentPublishedEventArgs(descendants, false, evtMsgs), "Published"); + _eventAggregator.Publish(new PublishedNotification(descendants, evtMsgs)); } switch (publishResult.Result) @@ -1721,7 +1723,7 @@ namespace Umbraco.Cms.Core.Services.Implement // trigger events for the entire branch // (SaveAndPublishBranchOne does *not* do it) scope.Events.Dispatch(TreeChanged, this, new TreeChange(document, TreeChangeTypes.RefreshBranch).ToEventArgs()); - scope.Events.Dispatch(Published, this, new ContentPublishedEventArgs(publishedDocuments, false, evtMsgs), nameof(Published)); + _eventAggregator.Publish(new PublishedNotification(publishedDocuments, evtMsgs)); scope.Complete(); } @@ -1776,11 +1778,9 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var deleteNotification = new DeletingNotification(content, evtMsgs); - _eventAggregator.Publish(deleteNotification); - - var deleteEventArgs = new DeleteEventArgs(content, evtMsgs); - if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs, nameof(Deleting))) + var notification = new DeletingNotification(content, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -1792,7 +1792,9 @@ namespace Umbraco.Cms.Core.Services.Implement // but... Unpublishing event makes no sense (not going to cancel?) and no need to save // just raise the event if (content.Trashed == false && content.Published) - scope.Events.Dispatch(Unpublished, this, new PublishEventArgs(content, false, false), nameof(Unpublished)); + { + _eventAggregator.Publish(new UnpublishedNotification(content, evtMsgs)); + } DeleteLocked(scope, content); @@ -1807,11 +1809,12 @@ namespace Umbraco.Cms.Core.Services.Implement private void DeleteLocked(IScope scope, IContent content) { + var evtMsgs = EventMessagesFactory.Get(); + void DoDelete(IContent c) { _documentRepository.Delete(c); - var args = new DeleteEventArgs(c, false); // raise event & get flagged files - scope.Events.Dispatch(Deleted, this, args, nameof(Deleted)); + _eventAggregator.Publish(new DeletedNotification(c, evtMsgs)); // media files deleted by QueuingEventDispatcher } @@ -1842,10 +1845,13 @@ namespace Umbraco.Cms.Core.Services.Implement /// Optional Id of the User deleting versions of a Content object public void DeleteVersions(int id, DateTime versionDate, int userId = Cms.Core.Constants.Security.SuperUserId) { + var evtMsgs = EventMessagesFactory.Get(); + using (var scope = ScopeProvider.CreateScope()) { - var deleteRevisionsEventArgs = new DeleteRevisionsEventArgs(id, dateToRetain: versionDate); - if (scope.Events.DispatchCancelable(DeletingVersions, this, deleteRevisionsEventArgs)) + var notification = new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return; @@ -1854,8 +1860,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); _documentRepository.DeleteVersions(id, versionDate); - deleteRevisionsEventArgs.CanCancel = false; - scope.Events.Dispatch(DeletedVersions, this, deleteRevisionsEventArgs); + _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version date)"); scope.Complete(); @@ -1872,9 +1877,13 @@ namespace Umbraco.Cms.Core.Services.Implement /// Optional Id of the User deleting versions of a Content object public void DeleteVersion(int id, int versionId, bool deletePriorVersions, int userId = Cms.Core.Constants.Security.SuperUserId) { + var evtMsgs = EventMessagesFactory.Get(); + using (var scope = ScopeProvider.CreateScope()) { - if (scope.Events.DispatchCancelable(DeletingVersions, this, new DeleteRevisionsEventArgs(id, /*specificVersion:*/ versionId))) + var notification = new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return; @@ -1891,7 +1900,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (c.VersionId != versionId && c.PublishedVersionId != versionId) // don't delete the current or published version _documentRepository.DeleteVersion(versionId); - scope.Events.Dispatch(DeletedVersions, this, new DeleteRevisionsEventArgs(id, false,/* specificVersion:*/ versionId)); + _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version)"); scope.Complete(); @@ -1914,8 +1923,10 @@ namespace Umbraco.Cms.Core.Services.Implement var originalPath = content.Path; var moveEventInfo = new MoveEventInfo(content, originalPath, Cms.Core.Constants.System.RecycleBinContent); - var moveEventArgs = new MoveEventArgs(evtMsgs, moveEventInfo); - if (scope.Events.DispatchCancelable(Trashing, this, moveEventArgs, nameof(Trashing))) + + var notification = new TrashingNotification(moveEventInfo, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return OperationResult.Cancel(evtMsgs); // causes rollback @@ -1934,9 +1945,7 @@ namespace Umbraco.Cms.Core.Services.Implement .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - moveEventArgs.CanCancel = false; - moveEventArgs.MoveInfoCollection = moveInfo; - scope.Events.Dispatch(Trashed, this, moveEventArgs, nameof(Trashed)); + _eventAggregator.Publish(new TrashedNotification(moveInfo, evtMsgs)); Audit(AuditType.Move, userId, content.Id, "Moved to recycle bin"); scope.Complete(); @@ -1965,6 +1974,8 @@ namespace Umbraco.Cms.Core.Services.Implement return; } + var evtMsgs = EventMessagesFactory.Get(); + var moves = new List<(IContent, string)>(); using (var scope = ScopeProvider.CreateScope()) @@ -1976,8 +1987,9 @@ namespace Umbraco.Cms.Core.Services.Implement throw new InvalidOperationException("Parent does not exist or is trashed."); // causes rollback var moveEventInfo = new MoveEventInfo(content, content.Path, parentId); - var moveEventArgs = new MoveEventArgs(moveEventInfo); - if (scope.Events.DispatchCancelable(Moving, this, moveEventArgs, nameof(Moving))) + var notification = new MovingNotification(moveEventInfo, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return; // causes rollback @@ -2006,9 +2018,8 @@ namespace Umbraco.Cms.Core.Services.Implement .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - moveEventArgs.MoveInfoCollection = moveInfo; - moveEventArgs.CanCancel = false; - scope.Events.Dispatch(Moved, this, moveEventArgs, nameof(Moved)); + _eventAggregator.Publish(new MovedNotification(moveInfo, evtMsgs)); + Audit(AuditType.Move, userId, content.Id); scope.Complete(); @@ -2093,8 +2104,9 @@ namespace Umbraco.Cms.Core.Services.Implement // are managed by Delete, and not here. // no idea what those events are for, keep a simplified version - var recycleBinEventArgs = new RecycleBinEventArgs(nodeObjectType, evtMsgs); - if (scope.Events.DispatchCancelable(EmptyingRecycleBin, this, recycleBinEventArgs)) + var notification = new EmptyingRecycleBinNotification(nodeObjectType, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -2109,9 +2121,7 @@ namespace Umbraco.Cms.Core.Services.Implement deleted.Add(content); } - recycleBinEventArgs.CanCancel = false; - recycleBinEventArgs.RecycleBinEmptiedSuccessfully = true; // oh my?! - scope.Events.Dispatch(EmptiedRecycleBin, this, recycleBinEventArgs); + _eventAggregator.Publish(new EmptiedRecycleBinNotification(nodeObjectType, evtMsgs)); scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange(x, TreeChangeTypes.Remove)).ToEventArgs()); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.RecycleBinContent, "Recycle bin emptied"); @@ -2151,13 +2161,16 @@ namespace Umbraco.Cms.Core.Services.Implement /// The newly created object public IContent Copy(IContent content, int parentId, bool relateToOriginal, bool recursive, int userId = Cms.Core.Constants.Security.SuperUserId) { + var evtMsgs = EventMessagesFactory.Get(); + var copy = content.DeepCloneWithResetIdentities(); copy.ParentId = parentId; using (var scope = ScopeProvider.CreateScope()) { - var copyEventArgs = new CopyEventArgs(content, copy, true, parentId, relateToOriginal); - if (scope.Events.DispatchCancelable(Copying, this, copyEventArgs)) + var notification = new CopyingNotification(content, copy, parentId, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return null; @@ -2212,8 +2225,12 @@ namespace Umbraco.Cms.Core.Services.Implement var descendantCopy = descendant.DeepCloneWithResetIdentities(); descendantCopy.ParentId = parentId; - if (scope.Events.DispatchCancelable(Copying, this, new CopyEventArgs(descendant, descendantCopy, parentId))) + notification = new CopyingNotification(descendant, descendantCopy, parentId, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) + { continue; + } // a copy is not published (but not really unpublishing either) // update the create author and last edit author @@ -2237,7 +2254,14 @@ namespace Umbraco.Cms.Core.Services.Implement scope.Events.Dispatch(TreeChanged, this, new TreeChange(copy, TreeChangeTypes.RefreshBranch).ToEventArgs()); foreach (var x in copies) - scope.Events.Dispatch(Copied, this, new CopyEventArgs(x.Item1, x.Item2, false, x.Item2.ParentId, relateToOriginal)); + { + if (relateToOriginal) + { + RelateOnCopy(x.Item1, x.Item2); + } + + _eventAggregator.Publish(new CopiedNotification(x.Item1, x.Item2, parentId, evtMsgs)); + } Audit(AuditType.Copy, userId, content.Id); scope.Complete(); @@ -2246,6 +2270,26 @@ namespace Umbraco.Cms.Core.Services.Implement return copy; } + private void RelateOnCopy(IContent original, IContent copy) + { + var relationType = _relationService.GetRelationTypeByAlias(Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias); + if (relationType == null) + { + relationType = new RelationType(Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, + Constants.Conventions.RelationTypes.RelateDocumentOnCopyName, + true, + Constants.ObjectTypes.Document, + Constants.ObjectTypes.Document); + + _relationService.Save(relationType); + } + + var relation = new Relation(original.Id, copy.Id, relationType); + _relationService.Save(relation); + + Audit(AuditType.Copy, copy.WriterId, copy.Id, $"Copied content with Id: '{copy.Id}' related to original content with Id: '{original.Id}'"); + } + /// /// Sends an to Publication, which executes handlers and events for the 'Send to Publication' action. /// @@ -2254,10 +2298,13 @@ namespace Umbraco.Cms.Core.Services.Implement /// True if sending publication was successful otherwise false public bool SendToPublication(IContent content, int userId = Cms.Core.Constants.Security.SuperUserId) { + var evtMsgs = EventMessagesFactory.Get(); + using (var scope = ScopeProvider.CreateScope()) { - var sendToPublishEventArgs = new SendToPublishEventArgs(content); - if (scope.Events.DispatchCancelable(SendingToPublish, this, sendToPublishEventArgs)) + var notification = new SendingToPublishNotification(content, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return false; @@ -2282,8 +2329,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!saveResult.Success) return saveResult.Success; - sendToPublishEventArgs.CanCancel = false; - scope.Events.Dispatch(SentToPublish, this, sendToPublishEventArgs); + _eventAggregator.Publish(new SentToPublishNotification(content, evtMsgs)); if (culturesChanging != null) Audit(AuditType.SendToPublishVariant, userId, content.Id, $"Send To Publish for cultures: {culturesChanging}", culturesChanging); @@ -2412,7 +2458,9 @@ namespace Umbraco.Cms.Core.Services.Implement scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)).ToEventArgs()); if (raiseEvents && published.Any()) - scope.Events.Dispatch(Published, this, new ContentPublishedEventArgs(published, false, evtMsgs), "Published"); + { + _eventAggregator.Publish(new PublishedNotification(published, evtMsgs)); + } Audit(AuditType.Sort, userId, 0, "Sorting content performed by user"); return OperationResult.Succeed(evtMsgs); @@ -2492,136 +2540,11 @@ namespace Umbraco.Cms.Core.Services.Implement #region Event Handlers - /// - /// Occurs before Delete - /// - public static event TypedEventHandler> Deleting; - - /// - /// Occurs after Delete - /// - public static event TypedEventHandler> Deleted; - - /// - /// Occurs before Delete Versions - /// - public static event TypedEventHandler DeletingVersions; - - /// - /// Occurs after Delete Versions - /// - public static event TypedEventHandler DeletedVersions; - - /// - /// Occurs before Sorting - /// - public static event TypedEventHandler> Sorting; - - /// - /// Occurs after Sorting - /// - public static event TypedEventHandler> Sorted; - - /// - /// Occurs after Save - /// - public static event TypedEventHandler Saved; - - /// - /// Occurs before Copy - /// - public static event TypedEventHandler> Copying; - - /// - /// Occurs after Copy - /// - public static event TypedEventHandler> Copied; - - /// - /// Occurs before Content is moved to Recycle Bin - /// - public static event TypedEventHandler> Trashing; - - /// - /// Occurs after Content is moved to Recycle Bin - /// - public static event TypedEventHandler> Trashed; - - /// - /// Occurs before Move - /// - public static event TypedEventHandler> Moving; - - /// - /// Occurs after Move - /// - public static event TypedEventHandler> Moved; - - /// - /// Occurs before Rollback - /// - public static event TypedEventHandler> RollingBack; - - /// - /// Occurs after Rollback - /// - public static event TypedEventHandler> RolledBack; - - /// - /// Occurs before Send to Publish - /// - public static event TypedEventHandler> SendingToPublish; - - /// - /// Occurs after Send to Publish - /// - public static event TypedEventHandler> SentToPublish; - - /// - /// Occurs before the Recycle Bin is emptied - /// - public static event TypedEventHandler EmptyingRecycleBin; - - /// - /// Occurs after the Recycle Bin has been Emptied - /// - public static event TypedEventHandler EmptiedRecycleBin; - - /// - /// Occurs before publish - /// - public static event TypedEventHandler Publishing; - - /// - /// Occurs after publish - /// - public static event TypedEventHandler Published; - - /// - /// Occurs before unpublish - /// - public static event TypedEventHandler> Unpublishing; - - /// - /// Occurs after unpublish - /// - public static event TypedEventHandler> Unpublished; - /// /// Occurs after change. /// public static event TypedEventHandler.EventArgs> TreeChanged; - /// - /// Occurs after a blueprint has been saved. - /// - public static event TypedEventHandler> SavedBlueprint; - - /// - /// Occurs after a blueprint has been deleted. - /// - public static event TypedEventHandler> DeletedBlueprint; - #endregion #region Publishing Strategies @@ -2635,14 +2558,15 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// /// - /// + /// /// private PublishResult StrategyCanPublish(IScope scope, IContent content, bool checkPath, IReadOnlyList culturesPublishing, - IReadOnlyCollection culturesUnpublishing, EventMessages evtMsgs, ContentSavingEventArgs savingEventArgs, - IReadOnlyCollection allLangs) + IReadOnlyCollection culturesUnpublishing, EventMessages evtMsgs, IReadOnlyCollection allLangs) { - // raise Publishing event - if (scope.Events.DispatchCancelable(Publishing, this, savingEventArgs.ToContentPublishingEventArgs())) + // raise Publishing notification + var notification = new PublishingNotification(content, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { _logger.LogInformation("Document {ContentName} (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "publishing was cancelled"); return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); @@ -2801,8 +2725,10 @@ namespace Umbraco.Cms.Core.Services.Implement /// private PublishResult StrategyCanUnpublish(IScope scope, IContent content, EventMessages evtMsgs) { - // raise Unpublishing event - if (scope.Events.DispatchCancelable(Unpublishing, this, new PublishEventArgs(content, evtMsgs))) + // raise Unpublishing notification + var notification = new UnpublishingNotification(content, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { _logger.LogInformation("Document {ContentName} (id={ContentId}) cannot be unpublished: unpublishing was cancelled.", content.Name, content.Id); return new PublishResult(PublishResultType.FailedUnpublishCancelledByEvent, evtMsgs, content); @@ -2871,6 +2797,7 @@ namespace Umbraco.Cms.Core.Services.Implement var changes = new List>(); var moves = new List<(IContent, string)>(); var contentTypeIdsA = contentTypeIds.ToArray(); + var evtMsgs = EventMessagesFactory.Get(); // using an immediate uow here because we keep making changes with // PerformMoveLocked and DeleteLocked that must be applied immediately, @@ -2883,7 +2810,9 @@ namespace Umbraco.Cms.Core.Services.Implement var query = Query().WhereIn(x => x.ContentTypeId, contentTypeIdsA); var contents = _documentRepository.Get(query).ToArray(); - if (scope.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs(contents), nameof(Deleting))) + var notification = new DeletingNotification(contents, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return; @@ -2897,7 +2826,9 @@ namespace Umbraco.Cms.Core.Services.Implement // but... Unpublishing event makes no sense (not going to cancel?) and no need to save // just raise the event if (content.Trashed == false && content.Published) - scope.Events.Dispatch(Unpublished, this, new PublishEventArgs(content, false, false), nameof(Unpublished)); + { + _eventAggregator.Publish(new UnpublishedNotification(content, evtMsgs)); + } // if current content has children, move them to trash var c = content; @@ -2920,7 +2851,9 @@ namespace Umbraco.Cms.Core.Services.Implement .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); if (moveInfos.Length > 0) - scope.Events.Dispatch(Trashed, this, new MoveEventArgs(false, moveInfos), nameof(Trashed)); + { + _eventAggregator.Publish(new TrashedNotification(moveInfos, evtMsgs)); + } scope.Events.Dispatch(TreeChanged, this, changes.ToEventArgs()); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, $"Delete content of type {string.Join(",", contentTypeIdsA)}"); @@ -2997,6 +2930,8 @@ namespace Umbraco.Cms.Core.Services.Implement public void SaveBlueprint(IContent content, int userId = Cms.Core.Constants.Security.SuperUserId) { + var evtMsgs = EventMessagesFactory.Get(); + //always ensure the blueprint is at the root if (content.ParentId != -1) content.ParentId = -1; @@ -3017,7 +2952,7 @@ namespace Umbraco.Cms.Core.Services.Implement Audit(AuditType.Save, Cms.Core.Constants.Security.SuperUserId, content.Id, $"Saved content template: {content.Name}"); - scope.Events.Dispatch(SavedBlueprint, this, new SaveEventArgs(content), "SavedBlueprint"); + _eventAggregator.Publish(new SavedBlueprintNotification(content, evtMsgs)); scope.Complete(); } @@ -3025,11 +2960,13 @@ namespace Umbraco.Cms.Core.Services.Implement public void DeleteBlueprint(IContent content, int userId = Cms.Core.Constants.Security.SuperUserId) { + var evtMsgs = EventMessagesFactory.Get(); + using (var scope = ScopeProvider.CreateScope()) { scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); _documentBlueprintRepository.Delete(content); - scope.Events.Dispatch(DeletedBlueprint, this, new DeleteEventArgs(content), nameof(DeletedBlueprint)); + _eventAggregator.Publish(new DeletedBlueprintNotification(content, evtMsgs)); scope.Complete(); } } @@ -3099,6 +3036,8 @@ namespace Umbraco.Cms.Core.Services.Implement public void DeleteBlueprintsOfTypes(IEnumerable contentTypeIds, int userId = Cms.Core.Constants.Security.SuperUserId) { + var evtMsgs = EventMessagesFactory.Get(); + using (var scope = ScopeProvider.CreateScope()) { scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); @@ -3119,7 +3058,7 @@ namespace Umbraco.Cms.Core.Services.Implement _documentBlueprintRepository.Delete(blueprint); } - scope.Events.Dispatch(DeletedBlueprint, this, new DeleteEventArgs(blueprints), nameof(DeletedBlueprint)); + _eventAggregator.Publish(new DeletedBlueprintNotification(blueprints, evtMsgs)); scope.Complete(); } } @@ -3154,10 +3093,9 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var rollbackEventArgs = new RollbackEventArgs(content); - - //Emit RollingBack event aka before - if (scope.Events.DispatchCancelable(RollingBack, this, rollbackEventArgs)) + var notification = new RollingBackNotification(content, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -3177,9 +3115,7 @@ namespace Umbraco.Cms.Core.Services.Implement } else { - //Emit RolledBack event aka after - rollbackEventArgs.CanCancel = false; - scope.Events.Dispatch(RolledBack, this, rollbackEventArgs); + _eventAggregator.Publish(new RolledBackNotification(content, evtMsgs)); //Logging & Audit message _logger.LogInformation("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId); From c3048193c129b2ada3f293c6e9d98df11bc2d821 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 1 Mar 2021 20:12:36 +0100 Subject: [PATCH 06/42] Removed most of the static events in MediaService --- .../Events/DeleteRevisionsEventArgs.cs | 69 ------ .../Cache/DistributedCacheBinder_Handlers.cs | 15 -- .../Compose/NotificationsHandler.cs | 4 +- .../Compose/RelateOnTrashComposer.cs | 7 - ...shComponent.cs => RelateOnTrashHandler.cs} | 102 +++----- .../FileUploadPropertyEditor.cs | 45 ++-- .../ImageCropperPropertyEditor.cs | 36 +-- .../PropertyEditorsComponent.cs | 14 -- .../Services/CancelableNotification.cs | 33 ++- .../Services/Implement/ContentService.cs | 29 ++- .../Services/Implement/MediaService.cs | 219 +++++++----------- 11 files changed, 164 insertions(+), 409 deletions(-) delete mode 100644 src/Umbraco.Core/Events/DeleteRevisionsEventArgs.cs delete mode 100644 src/Umbraco.Infrastructure/Compose/RelateOnTrashComposer.cs rename src/Umbraco.Infrastructure/Compose/{RelateOnTrashComponent.cs => RelateOnTrashHandler.cs} (59%) diff --git a/src/Umbraco.Core/Events/DeleteRevisionsEventArgs.cs b/src/Umbraco.Core/Events/DeleteRevisionsEventArgs.cs deleted file mode 100644 index a7eb48107e..0000000000 --- a/src/Umbraco.Core/Events/DeleteRevisionsEventArgs.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; - -namespace Umbraco.Cms.Core.Events -{ - public class DeleteRevisionsEventArgs : DeleteEventArgs, IEquatable - { - public DeleteRevisionsEventArgs(int id, bool canCancel, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) - : base(id, canCancel) - { - DeletePriorVersions = deletePriorVersions; - SpecificVersion = specificVersion; - DateToRetain = dateToRetain; - } - - public DeleteRevisionsEventArgs(int id, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) - : base(id) - { - DeletePriorVersions = deletePriorVersions; - SpecificVersion = specificVersion; - DateToRetain = dateToRetain; - } - - public bool DeletePriorVersions { get; } - public int SpecificVersion { get; } - public DateTime DateToRetain { get; } - - /// - /// Returns true if we are deleting a specific revision - /// - public bool IsDeletingSpecificRevision => SpecificVersion != default; - - public bool Equals(DeleteRevisionsEventArgs other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return base.Equals(other) && DateToRetain.Equals(other.DateToRetain) && DeletePriorVersions == other.DeletePriorVersions && SpecificVersion.Equals(other.SpecificVersion); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((DeleteRevisionsEventArgs) obj); - } - - public override int GetHashCode() - { - unchecked - { - int hashCode = base.GetHashCode(); - hashCode = (hashCode * 397) ^ DateToRetain.GetHashCode(); - hashCode = (hashCode * 397) ^ DeletePriorVersions.GetHashCode(); - hashCode = (hashCode * 397) ^ SpecificVersion.GetHashCode(); - return hashCode; - } - } - - public static bool operator ==(DeleteRevisionsEventArgs left, DeleteRevisionsEventArgs right) - { - return Equals(left, right); - } - - public static bool operator !=(DeleteRevisionsEventArgs left, DeleteRevisionsEventArgs right) - { - return !Equals(left, right); - } - } -} diff --git a/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs index eaa3c0df86..1aa4906029 100644 --- a/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs +++ b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs @@ -183,14 +183,6 @@ namespace Umbraco.Cms.Core.Cache _distributedCache.RefreshContentCache(args.Changes.ToArray()); } - // TODO: our weird events handling wants this for now - private void ContentService_Deleted(IContentService sender, DeleteEventArgs e) { } - private void ContentService_Moved(IContentService sender, MoveEventArgs e) { } - private void ContentService_Trashed(IContentService sender, MoveEventArgs e) { } - private void ContentService_EmptiedRecycleBin(IContentService sender, RecycleBinEventArgs e) { } - private void ContentService_Published(IContentService sender, PublishEventArgs e) { } - private void ContentService_Unpublished(IContentService sender, PublishEventArgs e) { } - //private void ContentService_SavedBlueprint(IContentService sender, SaveEventArgs e) //{ // _distributedCache.RefreshUnpublishedPageCache(e.SavedEntities.ToArray()); @@ -396,13 +388,6 @@ namespace Umbraco.Cms.Core.Cache _distributedCache.RefreshMediaCache(args.Changes.ToArray()); } - // TODO: our weird events handling wants this for now - private void MediaService_Saved(IMediaService sender, SaveEventArgs e) { } - private void MediaService_Deleted(IMediaService sender, DeleteEventArgs e) { } - private void MediaService_Moved(IMediaService sender, MoveEventArgs e) { } - private void MediaService_Trashed(IMediaService sender, MoveEventArgs e) { } - private void MediaService_EmptiedRecycleBin(IMediaService sender, RecycleBinEventArgs e) { } - #endregion #region MemberService diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs b/src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs index 34c855286f..cfd7121191 100644 --- a/src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs +++ b/src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs @@ -27,7 +27,7 @@ namespace Umbraco.Cms.Core.Compose INotificationHandler>, INotificationHandler>, INotificationHandler>, - INotificationHandler>, + INotificationHandler>, INotificationHandler>, INotificationHandler>, INotificationHandler>, @@ -104,7 +104,7 @@ namespace Umbraco.Cms.Core.Compose } } - public void Handle(TrashedNotification notification) => _notifier.Notify(_actions.GetAction(), notification.MoveInfoCollection.Select(m => m.Entity).ToArray()); + public void Handle(MovedToRecycleBinNotification notification) => _notifier.Notify(_actions.GetAction(), notification.MoveInfoCollection.Select(m => m.Entity).ToArray()); public void Handle(CopiedNotification notification) => _notifier.Notify(_actions.GetAction(), notification.Original); diff --git a/src/Umbraco.Infrastructure/Compose/RelateOnTrashComposer.cs b/src/Umbraco.Infrastructure/Compose/RelateOnTrashComposer.cs deleted file mode 100644 index 8394dfc993..0000000000 --- a/src/Umbraco.Infrastructure/Compose/RelateOnTrashComposer.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Umbraco.Cms.Core.Composing; - -namespace Umbraco.Cms.Core.Compose -{ - public sealed class RelateOnTrashComposer : ComponentComposer, ICoreComposer - { } -} diff --git a/src/Umbraco.Infrastructure/Compose/RelateOnTrashComponent.cs b/src/Umbraco.Infrastructure/Compose/RelateOnTrashHandler.cs similarity index 59% rename from src/Umbraco.Infrastructure/Compose/RelateOnTrashComponent.cs rename to src/Umbraco.Infrastructure/Compose/RelateOnTrashHandler.cs index cdba2d49bc..216b051c86 100644 --- a/src/Umbraco.Infrastructure/Compose/RelateOnTrashComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/RelateOnTrashHandler.cs @@ -1,10 +1,8 @@ using System.Linq; -using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Implement; using Umbraco.Cms.Infrastructure.Services; using Umbraco.Extensions; @@ -14,7 +12,9 @@ namespace Umbraco.Cms.Core.Compose // TODO: lots of duplicate code in this one, refactor public sealed class RelateOnTrashHandler : INotificationHandler>, - INotificationHandler> + INotificationHandler>, + INotificationHandler>, + INotificationHandler> { private readonly IRelationService _relationService; private readonly IEntityService _entityService; @@ -38,9 +38,9 @@ namespace Umbraco.Cms.Core.Compose public void Handle(MovedNotification notification) { - foreach (var item in notification.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Cms.Core.Constants.System.RecycleBinContentString))) + foreach (var item in notification.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinContentString))) { - const string relationTypeAlias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias; + const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias; var relations = _relationService.GetByChildId(item.Entity.Id); foreach (var relation in relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias))) @@ -50,22 +50,20 @@ namespace Umbraco.Cms.Core.Compose } } - public void Handle(TrashedNotification notification) + public void Handle(MovedToRecycleBinNotification notification) { using (var scope = _scopeProvider.CreateScope()) { - const string relationTypeAlias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias; + const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias; var relationType = _relationService.GetRelationTypeByAlias(relationTypeAlias); // check that the relation-type exists, if not, then recreate it if (relationType == null) { - var documentObjectType = Cms.Core.Constants.ObjectTypes.Document; - const string relationTypeName = - Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName; + var documentObjectType = Constants.ObjectTypes.Document; + const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName; - relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, - documentObjectType); + relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType); _relationService.Save(relationType); } @@ -74,7 +72,7 @@ namespace Umbraco.Cms.Core.Compose var originalPath = item.OriginalPath.ToDelimitedList(); var originalParentId = originalPath.Count > 2 ? int.Parse(originalPath[originalPath.Count - 2]) - : Cms.Core.Constants.System.Root; + : Constants.System.Root; //before we can create this relation, we need to ensure that the original parent still exists which //may not be the case if the encompassing transaction also deleted it when this item was moved to the bin @@ -82,118 +80,76 @@ namespace Umbraco.Cms.Core.Compose if (_entityService.Exists(originalParentId)) { // Add a relation for the item being deleted, so that we can know the original parent for if we need to restore later - var relation = _relationService.GetByParentAndChildId(originalParentId, item.Entity.Id, relationType) ?? - new Relation(originalParentId, item.Entity.Id, relationType); + var relation = _relationService.GetByParentAndChildId(originalParentId, item.Entity.Id, relationType) ?? new Relation(originalParentId, item.Entity.Id, relationType); _relationService.Save(relation); _auditService.Add(AuditType.Delete, item.Entity.WriterId, item.Entity.Id, ObjectTypes.GetName(UmbracoObjectTypes.Document), - string.Format(_textService.Localize( - "recycleBin/contentTrashed"), - item.Entity.Id, originalParentId)); + string.Format(_textService.Localize("recycleBin/contentTrashed"), item.Entity.Id, originalParentId) + ); } } scope.Complete(); } } - } - - public sealed class RelateOnTrashComponent : IComponent - { - private readonly IRelationService _relationService; - private readonly IEntityService _entityService; - private readonly ILocalizedTextService _textService; - private readonly IAuditService _auditService; - private readonly IScopeProvider _scopeProvider; - - public RelateOnTrashComponent( - IRelationService relationService, - IEntityService entityService, - ILocalizedTextService textService, - IAuditService auditService, - IScopeProvider scopeProvider) + public void Handle(MovedNotification notification) { - _relationService = relationService; - _entityService = entityService; - _textService = textService; - _auditService = auditService; - _scopeProvider = scopeProvider; - } - - public void Initialize() - { - MediaService.Moved += MediaService_Moved; - MediaService.Trashed += MediaService_Trashed; - } - - public void Terminate() - { - MediaService.Moved -= MediaService_Moved; - MediaService.Trashed -= MediaService_Trashed; - } - - private void MediaService_Moved(IMediaService sender, MoveEventArgs e) - { - foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Cms.Core.Constants.System.RecycleBinMediaString))) + foreach (var item in notification.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinMediaString))) { - const string relationTypeAlias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias; + const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias; var relations = _relationService.GetByChildId(item.Entity.Id); foreach (var relation in relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias))) { _relationService.Delete(relation); } } + } - public void MediaService_Trashed(IMediaService sender, MoveEventArgs e) + public void Handle(MovedToRecycleBinNotification notification) { using (var scope = _scopeProvider.CreateScope()) { - const string relationTypeAlias = - Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias; + const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias; var relationType = _relationService.GetRelationTypeByAlias(relationTypeAlias); // check that the relation-type exists, if not, then recreate it if (relationType == null) { - var documentObjectType = Cms.Core.Constants.ObjectTypes.Document; - const string relationTypeName = - Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName; - relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, - documentObjectType); + var documentObjectType = Constants.ObjectTypes.Document; + const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName; + relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType); _relationService.Save(relationType); } - foreach (var item in e.MoveInfoCollection) + foreach (var item in notification.MoveInfoCollection) { var originalPath = item.OriginalPath.ToDelimitedList(); var originalParentId = originalPath.Count > 2 ? int.Parse(originalPath[originalPath.Count - 2]) - : Cms.Core.Constants.System.Root; + : Constants.System.Root; //before we can create this relation, we need to ensure that the original parent still exists which //may not be the case if the encompassing transaction also deleted it when this item was moved to the bin if (_entityService.Exists(originalParentId)) { // Add a relation for the item being deleted, so that we can know the original parent for if we need to restore later - var relation = - _relationService.GetByParentAndChildId(originalParentId, item.Entity.Id, relationType) ?? - new Relation(originalParentId, item.Entity.Id, relationType); + var relation = _relationService.GetByParentAndChildId(originalParentId, item.Entity.Id, relationType) ?? new Relation(originalParentId, item.Entity.Id, relationType); _relationService.Save(relation); _auditService.Add(AuditType.Delete, item.Entity.CreatorId, item.Entity.Id, ObjectTypes.GetName(UmbracoObjectTypes.Media), - string.Format(_textService.Localize( - "recycleBin/mediaTrashed"), - item.Entity.Id, originalParentId)); + string.Format(_textService.Localize("recycleBin/mediaTrashed"), item.Entity.Id, originalParentId) + ); } } scope.Complete(); } + } } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs index aa9e8089f2..6ef2be84ce 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs @@ -26,7 +26,9 @@ namespace Umbraco.Cms.Core.PropertyEditors Group = Constants.PropertyEditors.Groups.Media, Icon = "icon-download-alt")] // TODO: insert these notification handlers in core composition - public class FileUploadPropertyEditor : DataEditor, IMediaUrlGenerator, INotificationHandler>, INotificationHandler> + public class FileUploadPropertyEditor : DataEditor, IMediaUrlGenerator, + INotificationHandler>, INotificationHandler>, + INotificationHandler>, INotificationHandler> { private readonly IMediaFileSystem _mediaFileSystem; private readonly ContentSettings _contentSettings; @@ -124,37 +126,6 @@ namespace Umbraco.Cms.Core.PropertyEditors } } - /// - /// After a media has been created, auto-fill the properties. - /// - /// The event sender. - /// The event arguments. - internal void MediaServiceCreated(IMediaService sender, NewEventArgs args) - { - AutoFillProperties(args.Entity); - } - - /// - /// After a media has been saved, auto-fill the properties. - /// - /// The event sender. - /// The event arguments. - public void MediaServiceSaving(IMediaService sender, SaveEventArgs args) - { - foreach (var entity in args.SavedEntities) - AutoFillProperties(entity); - } - - /// - /// After a content item has been saved, auto-fill the properties. - /// - /// The event sender. - /// The event arguments. - public void ContentServiceSaving(IContentService sender, SaveEventArgs args) - { - foreach (var entity in args.SavedEntities) - AutoFillProperties(entity); - } public void Handle(CopiedNotification notification) { @@ -190,6 +161,16 @@ namespace Umbraco.Cms.Core.PropertyEditors public void Handle(DeletedNotification notification) => notification.MediaFilesToDelete.AddRange(ServiceDeleted(notification.DeletedEntities.OfType())); + public void Handle(DeletedNotification notification) => notification.MediaFilesToDelete.AddRange(ServiceDeleted(notification.DeletedEntities.OfType())); + + public void Handle(SavingNotification notification) + { + foreach (var entity in notification.SavedEntities) + { + AutoFillProperties(entity); + } + } + /// /// Auto-fill properties (or clear). /// diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs index 26b78cade5..ab1c4d9e67 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs @@ -33,7 +33,9 @@ namespace Umbraco.Cms.Core.PropertyEditors Group = Constants.PropertyEditors.Groups.Media, Icon = "icon-crop")] // TODO: insert these notification handlers in core composition - public class ImageCropperPropertyEditor : DataEditor, IMediaUrlGenerator, INotificationHandler>, INotificationHandler> + public class ImageCropperPropertyEditor : DataEditor, IMediaUrlGenerator, + INotificationHandler>, INotificationHandler>, + INotificationHandler>, INotificationHandler> { private readonly IMediaFileSystem _mediaFileSystem; private readonly ContentSettings _contentSettings; @@ -215,36 +217,14 @@ namespace Umbraco.Cms.Core.PropertyEditors public void Handle(DeletedNotification notification) => notification.MediaFilesToDelete.AddRange(ServiceDeleted(notification.DeletedEntities.OfType())); - /// - /// After a media has been created, auto-fill the properties. - /// - /// The event sender. - /// The event arguments. - public void MediaServiceCreated(IMediaService sender, NewEventArgs args) - { - AutoFillProperties(args.Entity); - } + public void Handle(DeletedNotification notification) => notification.MediaFilesToDelete.AddRange(ServiceDeleted(notification.DeletedEntities.OfType())); - /// - /// After a media has been saved, auto-fill the properties. - /// - /// The event sender. - /// The event arguments. - public void MediaServiceSaving(IMediaService sender, SaveEventArgs args) + public void Handle(SavingNotification notification) { - foreach (var entity in args.SavedEntities) - AutoFillProperties(entity); - } - - /// - /// After a content item has been saved, auto-fill the properties. - /// - /// The event sender. - /// The event arguments. - public void ContentServiceSaving(IContentService sender, SaveEventArgs args) - { - foreach (var entity in args.SavedEntities) + foreach (var entity in notification.SavedEntities) + { AutoFillProperties(entity); + } } /// diff --git a/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs b/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs index c870d85139..37407bd856 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs @@ -40,13 +40,6 @@ namespace Umbraco.Cms.Core.PropertyEditors private void Initialize(FileUploadPropertyEditor fileUpload) { - MediaService.Saving += fileUpload.MediaServiceSaving; - _terminate.Add(() => MediaService.Saving -= fileUpload.MediaServiceSaving); - - void mediaServiceDeleted(IMediaService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(fileUpload.ServiceDeleted(args.DeletedEntities.Cast())); - MediaService.Deleted += mediaServiceDeleted; - _terminate.Add(() => MediaService.Deleted -= mediaServiceDeleted); - void memberServiceDeleted(IMemberService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(fileUpload.ServiceDeleted(args.DeletedEntities.Cast())); MemberService.Deleted += memberServiceDeleted; _terminate.Add(() => MemberService.Deleted -= memberServiceDeleted); @@ -54,13 +47,6 @@ namespace Umbraco.Cms.Core.PropertyEditors private void Initialize(ImageCropperPropertyEditor imageCropper) { - MediaService.Saving += imageCropper.MediaServiceSaving; - _terminate.Add(() => MediaService.Saving -= imageCropper.MediaServiceSaving); - - void mediaServiceDeleted(IMediaService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(imageCropper.ServiceDeleted(args.DeletedEntities.Cast())); - MediaService.Deleted += mediaServiceDeleted; - _terminate.Add(() => MediaService.Deleted -= mediaServiceDeleted); - void memberServiceDeleted(IMemberService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(imageCropper.ServiceDeleted(args.DeletedEntities.Cast())); MemberService.Deleted += memberServiceDeleted; _terminate.Add(() => MemberService.Deleted -= memberServiceDeleted); diff --git a/src/Umbraco.Infrastructure/Services/CancelableNotification.cs b/src/Umbraco.Infrastructure/Services/CancelableNotification.cs index bc4de2ce49..e564d87c5e 100644 --- a/src/Umbraco.Infrastructure/Services/CancelableNotification.cs +++ b/src/Umbraco.Infrastructure/Services/CancelableNotification.cs @@ -205,26 +205,26 @@ namespace Umbraco.Cms.Infrastructure.Services public IEnumerable> MoveInfoCollection => Target; } - public class TrashingNotification : CancelableObjectNotification>> + public class MovingToRecycleBinNotification : CancelableObjectNotification>> { - public TrashingNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) + public MovingToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) { } - public TrashingNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + public MovingToRecycleBinNotification(IEnumerable> target, EventMessages messages) : base(target, messages) { } public IEnumerable> MoveInfoCollection => Target; } - public class TrashedNotification : ObjectNotification>> + public class MovedToRecycleBinNotification : ObjectNotification>> { - public TrashedNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) + public MovedToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) { } - public TrashedNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + public MovedToRecycleBinNotification(IEnumerable> target, EventMessages messages) : base(target, messages) { } @@ -324,34 +324,27 @@ namespace Umbraco.Cms.Infrastructure.Services public IEnumerable UnpublishedEntities => Target; } - public class EmptiedRecycleBinNotification : INotification + public class EmptiedRecycleBinNotification : INotification where T : class { - public EmptiedRecycleBinNotification(Guid nodeObjectType, EventMessages messages) + public EmptiedRecycleBinNotification(EventMessages messages) { - NodeObjectType = nodeObjectType; Messages = messages; } - public Guid NodeObjectType { get; } - public EventMessages Messages { get; } - - public bool IsContentRecycleBin => NodeObjectType == Constants.ObjectTypes.Document; - - public bool IsMediaRecycleBin => NodeObjectType == Constants.ObjectTypes.Media; } - public class EmptyingRecycleBinNotification : EmptiedRecycleBinNotification, ICancelableNotification + public class EmptyingRecycleBinNotification : EmptiedRecycleBinNotification, ICancelableNotification where T : class { - public EmptyingRecycleBinNotification(Guid nodeObjectType, EventMessages messages) - : base(nodeObjectType, messages) + public EmptyingRecycleBinNotification(EventMessages messages) + : base(messages) { } public bool Cancel { get; set; } } - public class DeletedVersionsNotification : INotification + public class DeletedVersionsNotification : INotification where T : class { public DeletedVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) { @@ -373,7 +366,7 @@ namespace Umbraco.Cms.Infrastructure.Services public DateTime DateToRetain { get; } } - public class DeletingVersionsNotification : DeletedVersionsNotification, ICancelableNotification + public class DeletingVersionsNotification : DeletedVersionsNotification, ICancelableNotification where T : class { public DeletingVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) : base(id, messages, specificVersion, deletePriorVersions, dateToRetain) diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index 64ff7260c9..e8013ac5d9 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -1796,7 +1796,7 @@ namespace Umbraco.Cms.Core.Services.Implement _eventAggregator.Publish(new UnpublishedNotification(content, evtMsgs)); } - DeleteLocked(scope, content); + DeleteLocked(scope, content, evtMsgs); scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, TreeChangeTypes.Remove).ToEventArgs()); Audit(AuditType.Delete, userId, content.Id); @@ -1807,10 +1807,8 @@ namespace Umbraco.Cms.Core.Services.Implement return OperationResult.Succeed(evtMsgs); } - private void DeleteLocked(IScope scope, IContent content) + private void DeleteLocked(IScope scope, IContent content, EventMessages evtMsgs) { - var evtMsgs = EventMessagesFactory.Get(); - void DoDelete(IContent c) { _documentRepository.Delete(c); @@ -1849,7 +1847,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate); + var notification = new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate); _eventAggregator.Publish(notification); if (notification.Cancel) { @@ -1860,7 +1858,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); _documentRepository.DeleteVersions(id, versionDate); - _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate)); + _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version date)"); scope.Complete(); @@ -1881,7 +1879,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId); + var notification = new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId); _eventAggregator.Publish(notification); if (notification.Cancel) { @@ -1900,7 +1898,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (c.VersionId != versionId && c.PublishedVersionId != versionId) // don't delete the current or published version _documentRepository.DeleteVersion(versionId); - _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId)); + _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version)"); scope.Complete(); @@ -1924,7 +1922,7 @@ namespace Umbraco.Cms.Core.Services.Implement var originalPath = content.Path; var moveEventInfo = new MoveEventInfo(content, originalPath, Cms.Core.Constants.System.RecycleBinContent); - var notification = new TrashingNotification(moveEventInfo, evtMsgs); + var notification = new MovingToRecycleBinNotification(moveEventInfo, evtMsgs); _eventAggregator.Publish(notification); if (notification.Cancel) { @@ -1945,7 +1943,7 @@ namespace Umbraco.Cms.Core.Services.Implement .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - _eventAggregator.Publish(new TrashedNotification(moveInfo, evtMsgs)); + _eventAggregator.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs)); Audit(AuditType.Move, userId, content.Id, "Moved to recycle bin"); scope.Complete(); @@ -2091,7 +2089,6 @@ namespace Umbraco.Cms.Core.Services.Implement /// public OperationResult EmptyRecycleBin(int userId = Cms.Core.Constants.Security.SuperUserId) { - var nodeObjectType = Cms.Core.Constants.ObjectTypes.Document; var deleted = new List(); var evtMsgs = EventMessagesFactory.Get(); @@ -2104,7 +2101,7 @@ namespace Umbraco.Cms.Core.Services.Implement // are managed by Delete, and not here. // no idea what those events are for, keep a simplified version - var notification = new EmptyingRecycleBinNotification(nodeObjectType, evtMsgs); + var notification = new EmptyingRecycleBinNotification(evtMsgs); _eventAggregator.Publish(notification); if (notification.Cancel) { @@ -2117,11 +2114,11 @@ namespace Umbraco.Cms.Core.Services.Implement var contents = _documentRepository.Get(query).ToArray(); foreach (var content in contents) { - DeleteLocked(scope, content); + DeleteLocked(scope, content, evtMsgs); deleted.Add(content); } - _eventAggregator.Publish(new EmptiedRecycleBinNotification(nodeObjectType, evtMsgs)); + _eventAggregator.Publish(new EmptiedRecycleBinNotification(evtMsgs)); scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange(x, TreeChangeTypes.Remove)).ToEventArgs()); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.RecycleBinContent, "Recycle bin emptied"); @@ -2843,7 +2840,7 @@ namespace Umbraco.Cms.Core.Services.Implement // delete content // triggers the deleted event (and handles the files) - DeleteLocked(scope, content); + DeleteLocked(scope, content, evtMsgs); changes.Add(new TreeChange(content, TreeChangeTypes.Remove)); } @@ -2852,7 +2849,7 @@ namespace Umbraco.Cms.Core.Services.Implement .ToArray(); if (moveInfos.Length > 0) { - _eventAggregator.Publish(new TrashedNotification(moveInfos, evtMsgs)); + _eventAggregator.Publish(new MovedToRecycleBinNotification(moveInfos, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, changes.ToEventArgs()); diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index 60061ed9bf..b7fe23dd7f 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -13,6 +13,7 @@ using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence.Querying; +using Umbraco.Cms.Infrastructure.Services; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Services.Implement @@ -30,11 +31,13 @@ namespace Umbraco.Cms.Core.Services.Implement private readonly IMediaFileSystem _mediaFileSystem; + private readonly IEventAggregator _eventAggregator; + #region Constructors public MediaService(IScopeProvider provider, IMediaFileSystem mediaFileSystem, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, IMediaRepository mediaRepository, IAuditRepository auditRepository, IMediaTypeRepository mediaTypeRepository, - IEntityRepository entityRepository, IShortStringHelper shortStringHelper) + IEntityRepository entityRepository, IShortStringHelper shortStringHelper, IEventAggregator eventAggregator) : base(provider, loggerFactory, eventMessagesFactory) { _mediaFileSystem = mediaFileSystem; @@ -43,6 +46,7 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaTypeRepository = mediaTypeRepository; _entityRepository = entityRepository; _shortStringHelper = shortStringHelper; + _eventAggregator = eventAggregator; } #endregion @@ -289,19 +293,22 @@ namespace Umbraco.Cms.Core.Services.Implement private void CreateMedia(IScope scope, Core.Models.Media media, IMedia parent, int userId, bool withIdentity) { + var evtMsgs = EventMessagesFactory.Get(); + media.CreatorId = userId; if (withIdentity) { - // if saving is cancelled, media remains without an identity - var saveEventArgs = new SaveEventArgs(media); - if (Saving.IsRaisedEventCancelled(saveEventArgs, this)) + var notification = new SavingNotification(media, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) + { return; + } _mediaRepository.Save(media); - saveEventArgs.CanCancel = false; - scope.Events.Dispatch(Saved, this, saveEventArgs); + _eventAggregator.Publish(new SavedNotification(media, evtMsgs)); scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, TreeChangeTypes.RefreshNode).ToEventArgs()); } @@ -659,11 +666,15 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var saveEventArgs = new SaveEventArgs(media, evtMsgs); - if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs)) + if (raiseEvents) { - scope.Complete(); - return OperationResult.Attempt.Cancel(evtMsgs); + var notification = new SavingNotification(media, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) + { + scope.Complete(); + return OperationResult.Attempt.Cancel(evtMsgs); + } } // poor man's validation? @@ -682,8 +693,7 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaRepository.Save(media); if (raiseEvents) { - saveEventArgs.CanCancel = false; - scope.Events.Dispatch(Saved, this, saveEventArgs); + _eventAggregator.Publish(new SavedNotification(media, evtMsgs)); } var changeType = TreeChangeTypes.RefreshNode; scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, changeType).ToEventArgs()); @@ -708,11 +718,15 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var saveEventArgs = new SaveEventArgs(mediasA, evtMsgs); - if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, new SaveEventArgs(mediasA, evtMsgs))) + if (raiseEvents) { - scope.Complete(); - return OperationResult.Attempt.Cancel(evtMsgs); + var notification = new SavingNotification(mediasA, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) + { + scope.Complete(); + return OperationResult.Attempt.Cancel(evtMsgs); + } } var treeChanges = mediasA.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)); @@ -727,8 +741,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - saveEventArgs.CanCancel = false; - scope.Events.Dispatch(Saved, this, saveEventArgs); + _eventAggregator.Publish(new SavedNotification(mediasA, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, treeChanges.ToEventArgs()); Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Bulk save media"); @@ -754,7 +767,9 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (scope.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs(media, evtMsgs))) + var notification = new DeletingNotification(media, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -762,7 +777,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.MediaTree); - DeleteLocked(scope, media); + DeleteLocked(scope, media, evtMsgs); scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, TreeChangeTypes.Remove).ToEventArgs()); Audit(AuditType.Delete, userId, media.Id); @@ -773,13 +788,12 @@ namespace Umbraco.Cms.Core.Services.Implement return OperationResult.Attempt.Succeed(evtMsgs); } - private void DeleteLocked(IScope scope, IMedia media) + private void DeleteLocked(IScope scope, IMedia media, EventMessages evtMsgs) { void DoDelete(IMedia c) { _mediaRepository.Delete(c); - var args = new DeleteEventArgs(c, false); // raise event & get flagged files - scope.Events.Dispatch(Deleted, this, args); + _eventAggregator.Publish(new DeletedNotification(c, evtMsgs)); // media files deleted by QueuingEventDispatcher } @@ -815,36 +829,25 @@ namespace Umbraco.Cms.Core.Services.Implement { DeleteVersions(scope, true, id, versionDate, userId); scope.Complete(); - - //if (uow.Events.DispatchCancelable(DeletingVersions, this, new DeleteRevisionsEventArgs(id, dateToRetain: versionDate))) - //{ - // uow.Complete(); - // return; - //} - - //uow.WriteLock(Constants.Locks.MediaTree); - //var repository = uow.CreateRepository(); - //repository.DeleteVersions(id, versionDate); - - //uow.Events.Dispatch(DeletedVersions, this, new DeleteRevisionsEventArgs(id, false, dateToRetain: versionDate)); - //Audit(uow, AuditType.Delete, "Delete Media by version date, userId, Constants.System.Root); - - //uow.Complete(); } } private void DeleteVersions(IScope scope, bool wlock, int id, DateTime versionDate, int userId = Cms.Core.Constants.Security.SuperUserId) { - var args = new DeleteRevisionsEventArgs(id, dateToRetain: versionDate); - if (scope.Events.DispatchCancelable(DeletingVersions, this, args)) + var evtMsgs = EventMessagesFactory.Get(); + + var notification = new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate); + _eventAggregator.Publish(notification); + if (notification.Cancel) + { return; + } if (wlock) scope.WriteLock(Cms.Core.Constants.Locks.MediaTree); _mediaRepository.DeleteVersions(id, versionDate); - args.CanCancel = false; - scope.Events.Dispatch(DeletedVersions, this, args); + _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete Media by version date"); } @@ -858,10 +861,13 @@ namespace Umbraco.Cms.Core.Services.Implement /// Optional Id of the User deleting versions of a Media object public void DeleteVersion(int id, int versionId, bool deletePriorVersions, int userId = Cms.Core.Constants.Security.SuperUserId) { + var evtMsgs = EventMessagesFactory.Get(); + using (var scope = ScopeProvider.CreateScope()) { - var args = new DeleteRevisionsEventArgs(id, /*specificVersion:*/ versionId); - if (scope.Events.DispatchCancelable(DeletingVersions, this, args)) + var notification = new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return; @@ -879,8 +885,7 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaRepository.DeleteVersion(versionId); - args.CanCancel = false; - scope.Events.Dispatch(DeletedVersions, this, args); + _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete Media by version"); scope.Complete(); @@ -911,8 +916,10 @@ namespace Umbraco.Cms.Core.Services.Implement var originalPath = media.Path; var moveEventInfo = new MoveEventInfo(media, originalPath, Cms.Core.Constants.System.RecycleBinMedia); - var moveEventArgs = new MoveEventArgs(true, evtMsgs, moveEventInfo); - if (scope.Events.DispatchCancelable(Trashing, this, moveEventArgs, nameof(Trashing))) + + var notification = new MovingToRecycleBinNotification(moveEventInfo, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -923,9 +930,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, TreeChangeTypes.RefreshBranch).ToEventArgs()); var moveInfo = moves.Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - moveEventArgs.MoveInfoCollection = moveInfo; - moveEventArgs.CanCancel = false; - scope.Events.Dispatch(Trashed, this, moveEventArgs, nameof(Trashed)); + _eventAggregator.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs)); Audit(AuditType.Move, userId, media.Id, "Move Media to recycle bin"); scope.Complete(); @@ -962,8 +967,9 @@ namespace Umbraco.Cms.Core.Services.Implement throw new InvalidOperationException("Parent does not exist or is trashed."); // causes rollback var moveEventInfo = new MoveEventInfo(media, media.Path, parentId); - var moveEventArgs = new MoveEventArgs(true, evtMsgs, moveEventInfo); - if (scope.Events.DispatchCancelable(Moving, this, moveEventArgs, nameof(Moving))) + var notification = new MovingNotification(moveEventInfo, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -979,9 +985,7 @@ namespace Umbraco.Cms.Core.Services.Implement var moveInfo = moves //changes .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - moveEventArgs.MoveInfoCollection = moveInfo; - moveEventArgs.CanCancel = false; - scope.Events.Dispatch(Moved, this, moveEventArgs, nameof(Moved)); + _eventAggregator.Publish(new MovedNotification(moveInfo, evtMsgs)); Audit(AuditType.Move, userId, media.Id); scope.Complete(); } @@ -1050,7 +1054,6 @@ namespace Umbraco.Cms.Core.Services.Implement /// Optional Id of the User emptying the Recycle Bin public OperationResult EmptyRecycleBin(int userId = Cms.Core.Constants.Security.SuperUserId) { - var nodeObjectType = Cms.Core.Constants.ObjectTypes.Media; var deleted = new List(); var evtMsgs = EventMessagesFactory.Get(); // TODO: and then? @@ -1063,23 +1066,23 @@ namespace Umbraco.Cms.Core.Services.Implement // v7 EmptyingRecycleBin and EmptiedRecycleBin events are greatly simplified since // each deleted items will have its own deleting/deleted events. so, files and such // are managed by Delete, and not here. - var args = new RecycleBinEventArgs(nodeObjectType, evtMsgs); - - if (scope.Events.DispatchCancelable(EmptyingRecycleBin, this, args)) + var notification = new EmptyingRecycleBinNotification(evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return OperationResult.Cancel(evtMsgs); } + // emptying the recycle bin means deleting whatever is in there - do it properly! var query = Query().Where(x => x.ParentId == Cms.Core.Constants.System.RecycleBinMedia); var medias = _mediaRepository.Get(query).ToArray(); foreach (var media in medias) { - DeleteLocked(scope, media); + DeleteLocked(scope, media, evtMsgs); deleted.Add(media); } - args.CanCancel = false; - scope.Events.Dispatch(EmptiedRecycleBin, this, args); + _eventAggregator.Publish(new EmptiedRecycleBinNotification(new EventMessages())); scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange(x, TreeChangeTypes.Remove)).ToEventArgs()); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.RecycleBinMedia, "Empty Media recycle bin"); scope.Complete(); @@ -1105,13 +1108,19 @@ namespace Umbraco.Cms.Core.Services.Implement var itemsA = items.ToArray(); if (itemsA.Length == 0) return true; + var evtMsgs = EventMessagesFactory.Get(); + using (var scope = ScopeProvider.CreateScope()) { - var args = new SaveEventArgs(itemsA); - if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, args)) + if (raiseEvents) { - scope.Complete(); - return false; + var notification = new SavingNotification(itemsA, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) + { + scope.Complete(); + return false; + } } var saved = new List(); @@ -1137,8 +1146,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - args.CanCancel = false; - scope.Events.Dispatch(Saved, this, args); + _eventAggregator.Publish(new SavedNotification(itemsA, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)).ToEventArgs()); Audit(AuditType.Sort, userId, 0); @@ -1216,66 +1224,6 @@ namespace Umbraco.Cms.Core.Services.Implement #region Event Handlers - /// - /// Occurs before Delete - /// - public static event TypedEventHandler> Deleting; - - /// - /// Occurs after Delete - /// - public static event TypedEventHandler> Deleted; - - /// - /// Occurs before Delete Versions - /// - public static event TypedEventHandler DeletingVersions; - - /// - /// Occurs after Delete Versions - /// - public static event TypedEventHandler DeletedVersions; - - /// - /// Occurs before Save - /// - public static event TypedEventHandler> Saving; - - /// - /// Occurs after Save - /// - public static event TypedEventHandler> Saved; - - /// - /// Occurs before Media is moved to Recycle Bin - /// - public static event TypedEventHandler> Trashing; - - /// - /// Occurs after Media is moved to Recycle Bin - /// - public static event TypedEventHandler> Trashed; - - /// - /// Occurs before Move - /// - public static event TypedEventHandler> Moving; - - /// - /// Occurs after Move - /// - public static event TypedEventHandler> Moved; - - /// - /// Occurs before the Recycle Bin is emptied - /// - public static event TypedEventHandler EmptyingRecycleBin; - - /// - /// Occurs after the Recycle Bin has been Emptied - /// - public static event TypedEventHandler EmptiedRecycleBin; - /// /// Occurs after change. /// @@ -1307,6 +1255,7 @@ namespace Umbraco.Cms.Core.Services.Implement var changes = new List>(); var moves = new List<(IMedia, string)>(); var mediaTypeIdsA = mediaTypeIds.ToArray(); + var evtMsgs = EventMessagesFactory.Get(); using (var scope = ScopeProvider.CreateScope()) { @@ -1315,7 +1264,9 @@ namespace Umbraco.Cms.Core.Services.Implement var query = Query().WhereIn(x => x.ContentTypeId, mediaTypeIdsA); var medias = _mediaRepository.Get(query).ToArray(); - if (scope.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs(medias))) + var notification = new DeletingNotification(medias, evtMsgs); + _eventAggregator.Publish(notification); + if (notification.Cancel) { scope.Complete(); return; @@ -1338,14 +1289,16 @@ namespace Umbraco.Cms.Core.Services.Implement // delete media // triggers the deleted event (and handles the files) - DeleteLocked(scope, media); + DeleteLocked(scope, media, evtMsgs); changes.Add(new TreeChange(media, TreeChangeTypes.Remove)); } var moveInfos = moves.Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); if (moveInfos.Length > 0) - scope.Events.Dispatch(Trashed, this, new MoveEventArgs(false, moveInfos), nameof(Trashed)); + { + _eventAggregator.Publish(new MovedToRecycleBinNotification(moveInfos, evtMsgs)); + } scope.Events.Dispatch(TreeChanged, this, changes.ToEventArgs()); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, $"Delete Media of types {string.Join(",", mediaTypeIdsA)}"); From 335a62164c6bcae89bfd97d1907d35f71625e044 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 1 Mar 2021 20:31:04 +0100 Subject: [PATCH 07/42] Audit the last few backoffice user events --- .../DependencyInjection/UmbracoBuilderExtensions.cs | 3 +++ .../Security/BackOfficeUserManagerAuditer.cs | 10 +++++++++- .../Security/BackOfficeUserManager.cs | 6 +++++- .../Security/UserPasswordResetNotification.cs | 9 +++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Web.Common/Security/UserPasswordResetNotification.cs diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs index 4fc0584f53..d678fa2279 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs @@ -85,9 +85,12 @@ namespace Umbraco.Extensions builder.Services.AddUnique(); builder.AddNotificationHandler(); + builder.AddNotificationHandler(); builder.AddNotificationHandler(); builder.AddNotificationHandler(); builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); return builder; } diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs index 40b9cf81a7..00b93a7c37 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs @@ -17,7 +17,9 @@ namespace Umbraco.Cms.Web.BackOffice.Security INotificationHandler, INotificationHandler, INotificationHandler, - INotificationHandler + INotificationHandler, + INotificationHandler, + INotificationHandler { private readonly IAuditService _auditService; private readonly IUserService _userService; @@ -51,6 +53,12 @@ namespace Umbraco.Cms.Web.BackOffice.Security public void Handle(UserForgotPasswordChangedNotification notification) => WriteAudit(notification.PerformingUserId, notification.AffectedUserId, notification.IpAddress, "umbraco/user/password/forgot/change", "password forgot/change"); + public void Handle(UserPasswordChangedNotification notification) => + WriteAudit(notification.PerformingUserId, notification.AffectedUserId, notification.IpAddress, "umbraco/user/password/change", "password change"); + + public void Handle(UserPasswordResetNotification notification) => + WriteAudit(notification.PerformingUserId, notification.AffectedUserId, notification.IpAddress, "umbraco/user/password/reset", "password reset"); + private IUser GetPerformingUser(string userId) { if (!int.TryParse(userId, out int asInt)) diff --git a/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs b/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs index 4fed85121a..459ed57138 100644 --- a/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs +++ b/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs @@ -123,7 +123,7 @@ namespace Umbraco.Cms.Web.Common.Security IdentityResult result = await base.ChangePasswordWithResetAsync(userId, token, newPassword); if (result.Succeeded) { - NotifyPasswordChanged(_httpContextAccessor.HttpContext?.User, userId); + NotifyPasswordReset(_httpContextAccessor.HttpContext?.User, userId); } return result; @@ -225,6 +225,10 @@ namespace Umbraco.Cms.Web.Common.Security (currentUserId, ip) => new UserPasswordChangedNotification(ip, userId, currentUserId) ); + public void NotifyPasswordReset(IPrincipal currentUser, string userId) => Notify(currentUser, + (currentUserId, ip) => new UserPasswordResetNotification(ip, userId, currentUserId) + ); + public void NotifyResetAccessFailedCount(IPrincipal currentUser, string userId) => Notify(currentUser, (currentUserId, ip) => new UserResetAccessFailedCountNotification(ip, userId, currentUserId) ); diff --git a/src/Umbraco.Web.Common/Security/UserPasswordResetNotification.cs b/src/Umbraco.Web.Common/Security/UserPasswordResetNotification.cs new file mode 100644 index 0000000000..618fa04f4c --- /dev/null +++ b/src/Umbraco.Web.Common/Security/UserPasswordResetNotification.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Web.Common.Security +{ + public class UserPasswordResetNotification : UserNotification + { + public UserPasswordResetNotification(string ipAddress, string affectedUserId, string performingUserId) : base(ipAddress, affectedUserId, performingUserId) + { + } + } +} From 1fd909e435331cf16d93a030ebb556e0dad1a789 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 2 Mar 2021 08:14:37 +0100 Subject: [PATCH 08/42] Simplify auditer (make sure it can survive boot with no DB) --- .../Security/BackOfficeUserManagerAuditer.cs | 41 ++----------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs index 00b93a7c37..bb6d9563ec 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs @@ -1,6 +1,4 @@ using System; -using Umbraco.Cms.Core.Compose; -using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Services; @@ -23,26 +21,18 @@ namespace Umbraco.Cms.Web.BackOffice.Security { private readonly IAuditService _auditService; private readonly IUserService _userService; - private readonly GlobalSettings _globalSettings; - public BackOfficeUserManagerAuditer(IAuditService auditService, IUserService userService, GlobalSettings globalSettings) + public BackOfficeUserManagerAuditer(IAuditService auditService, IUserService userService/*, GlobalSettings globalSettings*/) { _auditService = auditService; _userService = userService; - _globalSettings = globalSettings; } public void Handle(UserLoginSuccessNotification notification) - { - var performingUser = GetPerformingUser(notification.PerformingUserId); - WriteAudit(performingUser, notification.AffectedUserId, notification.IpAddress, "umbraco/user/sign-in/login", "login success"); - } + => WriteAudit(notification.PerformingUserId, notification.AffectedUserId, notification.IpAddress, "umbraco/user/sign-in/login", "login success"); public void Handle(UserLogoutSuccessNotification notification) - { - var performingUser = GetPerformingUser(notification.PerformingUserId); - WriteAudit(performingUser, notification.AffectedUserId, notification.IpAddress, "umbraco/user/sign-in/logout", "logout success"); - } + => WriteAudit(notification.PerformingUserId, notification.AffectedUserId, notification.IpAddress, "umbraco/user/sign-in/logout", "logout success"); public void Handle(UserLoginFailedNotification notification) => WriteAudit(notification.PerformingUserId, "0", notification.IpAddress, "umbraco/user/sign-in/failed", "login failed", ""); @@ -59,17 +49,6 @@ namespace Umbraco.Cms.Web.BackOffice.Security public void Handle(UserPasswordResetNotification notification) => WriteAudit(notification.PerformingUserId, notification.AffectedUserId, notification.IpAddress, "umbraco/user/password/reset", "password reset"); - private IUser GetPerformingUser(string userId) - { - if (!int.TryParse(userId, out int asInt)) - { - return AuditEventsComponent.UnknownUser(_globalSettings); - } - - IUser found = asInt >= 0 ? _userService.GetUserById(asInt) : null; - return found ?? AuditEventsComponent.UnknownUser(_globalSettings); - } - private static string FormatEmail(IMembershipUser user) => user == null ? string.Empty : user.Email.IsNullOrWhiteSpace() ? "" : $"<{user.Email}>"; private void WriteAudit(string performingId, string affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null) @@ -97,20 +76,6 @@ namespace Umbraco.Cms.Web.BackOffice.Security WriteAudit(performingIdAsInt, performingDetails, affectedIdAsInt, ipAddress, eventType, eventDetails, affectedDetails); } - private void WriteAudit(IUser performingUser, string affectedId, string ipAddress, string eventType, string eventDetails) - { - var performingDetails = performingUser == null - ? $"User UNKNOWN" - : $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}"; - - if (!int.TryParse(affectedId, out int affectedIdInt)) - { - affectedIdInt = 0; - } - - WriteAudit(performingUser?.Id ?? 0, performingDetails, affectedIdInt, ipAddress, eventType, eventDetails); - } - private void WriteAudit(int performingId, string performingDetails, int affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null) { if (affectedDetails == null) From 6ae822588cb12c8104c58eaf24a97df9c787ad2c Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 2 Mar 2021 14:15:26 +0100 Subject: [PATCH 09/42] Fix tests and clean up some --- .../Events/ContentPublishedEventArgs.cs | 30 --- .../Events/ContentPublishingEventArgs.cs | 30 --- .../Events/ContentSavedEventArgs.cs | 24 -- .../Services/CancelableNotification.cs | 42 ++- ....cs => ContentServiceNotificationTests.cs} | 251 +++++++++++------- .../Services/ContentServiceTests.cs | 91 +++++-- .../Services/ContentTypeServiceTests.cs | 25 +- .../Services/MediaTypeServiceTests.cs | 23 +- .../BlockEditorComponentTests.cs | 12 +- .../NestedContentPropertyComponentTests.cs | 18 +- src/Umbraco.Tests/Models/MediaXmlTest.cs | 4 +- .../Routing/MediaUrlProviderTests.cs | 6 +- .../Scoping/ScopedNuCacheTests.cs | 24 +- src/Umbraco.Tests/Scoping/ScopedXmlTests.cs | 25 +- src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 1 + 15 files changed, 349 insertions(+), 257 deletions(-) delete mode 100644 src/Umbraco.Core/Events/ContentPublishedEventArgs.cs delete mode 100644 src/Umbraco.Core/Events/ContentPublishingEventArgs.cs delete mode 100644 src/Umbraco.Core/Events/ContentSavedEventArgs.cs rename src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/{ContentServiceEventTests.cs => ContentServiceNotificationTests.cs} (54%) diff --git a/src/Umbraco.Core/Events/ContentPublishedEventArgs.cs b/src/Umbraco.Core/Events/ContentPublishedEventArgs.cs deleted file mode 100644 index ceec857c64..0000000000 --- a/src/Umbraco.Core/Events/ContentPublishedEventArgs.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.Core.Events -{ - /// - /// Represents event data for the Published event. - /// - public class ContentPublishedEventArgs : PublishEventArgs - { - /// - /// Initializes a new instance of the class. - /// - public ContentPublishedEventArgs(IEnumerable eventObject, bool canCancel, EventMessages eventMessages) - : base(eventObject, canCancel, eventMessages) - { } - - /// - /// Determines whether a culture has been published, during a Published event. - /// - public bool HasPublishedCulture(IContent content, string culture) - => content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.ChangedCulture + culture); - - /// - /// Determines whether a culture has been unpublished, during a Published event. - /// - public bool HasUnpublishedCulture(IContent content, string culture) - => content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.UnpublishedCulture + culture); - } -} diff --git a/src/Umbraco.Core/Events/ContentPublishingEventArgs.cs b/src/Umbraco.Core/Events/ContentPublishingEventArgs.cs deleted file mode 100644 index e7893ea195..0000000000 --- a/src/Umbraco.Core/Events/ContentPublishingEventArgs.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.Core.Events -{ - /// - /// Represents event data for the Publishing event. - /// - public class ContentPublishingEventArgs : PublishEventArgs - { - /// - /// Initializes a new instance of the class. - /// - public ContentPublishingEventArgs(IEnumerable eventObject, EventMessages eventMessages) - : base(eventObject, eventMessages) - { } - - /// - /// Determines whether a culture is being published, during a Publishing event. - /// - public bool IsPublishingCulture(IContent content, string culture) - => content.PublishCultureInfos.TryGetValue(culture, out var cultureInfo) && cultureInfo.IsDirty(); - - /// - /// Determines whether a culture is being unpublished, during a Publishing event. - /// - public bool IsUnpublishingCulture(IContent content, string culture) - => content.IsPropertyDirty(ContentBase.ChangeTrackingPrefix.UnpublishedCulture + culture); //bit of a hack since we know that the content implementation tracks changes this way - } -} diff --git a/src/Umbraco.Core/Events/ContentSavedEventArgs.cs b/src/Umbraco.Core/Events/ContentSavedEventArgs.cs deleted file mode 100644 index 554330563a..0000000000 --- a/src/Umbraco.Core/Events/ContentSavedEventArgs.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.Core.Events -{ - /// - /// Represents event data for the Saved event. - /// - public class ContentSavedEventArgs : SaveEventArgs - { - /// - /// Initializes a new instance of the class. - /// - public ContentSavedEventArgs(IEnumerable eventObject, EventMessages messages, IDictionary additionalData) - : base(eventObject, false, messages, additionalData) - { } - - /// - /// Determines whether a culture has been saved, during a Saved event. - /// - public bool HasSavedCulture(IContent content, string culture) - => content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.UpdatedCulture + culture); - } -} diff --git a/src/Umbraco.Infrastructure/Services/CancelableNotification.cs b/src/Umbraco.Infrastructure/Services/CancelableNotification.cs index e564d87c5e..4e9ef538b6 100644 --- a/src/Umbraco.Infrastructure/Services/CancelableNotification.cs +++ b/src/Umbraco.Infrastructure/Services/CancelableNotification.cs @@ -3,8 +3,8 @@ using System; using System.Collections.Generic; -using Umbraco.Cms.Core; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Infrastructure.Services { @@ -375,4 +375,44 @@ namespace Umbraco.Cms.Infrastructure.Services public bool Cancel { get; set; } } + + public static class ContentNotificationExtensions + { + /// + /// Determines whether a culture is being saved, during a Saving notification + /// + public static bool IsSavingCulture(this SavingNotification notification, T content, string culture) where T : IContentBase + => content.CultureInfos.TryGetValue(culture, out ContentCultureInfos cultureInfo) && cultureInfo.IsDirty(); + + /// + /// Determines whether a culture has been saved, during a Saved notification + /// + public static bool HasSavedCulture(this SavedNotification notification, T content, string culture) where T : IContentBase + => content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.UpdatedCulture + culture); + + /// + /// Determines whether a culture is being published, during a Publishing notification + /// + public static bool IsPublishingCulture(this PublishingNotification notification, IContent content, string culture) + => content.PublishCultureInfos.TryGetValue(culture, out ContentCultureInfos cultureInfo) && cultureInfo.IsDirty(); + + /// + /// Determines whether a culture is being unpublished, during a Publishing notification + /// + public static bool IsUnpublishingCulture(this UnpublishingNotification notification, IContent content, string culture) + => content.IsPropertyDirty(ContentBase.ChangeTrackingPrefix.UnpublishedCulture + culture); //bit of a hack since we know that the content implementation tracks changes this way + + /// + /// Determines whether a culture has been published, during a Published notification + /// + public static bool HasPublishedCulture(this PublishedNotification notification, IContent content, string culture) + => content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.ChangedCulture + culture); + + /// + /// Determines whether a culture has been unpublished, during a Published notification + /// + public static bool HasUnpublishedCulture(this UnpublishedNotification notification, IContent content, string culture) + => content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.UnpublishedCulture + culture); + + } } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceEventTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs similarity index 54% rename from src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceEventTests.cs rename to src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs index 24bb2b3a84..ee82e86671 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceEventTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs @@ -1,14 +1,17 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using System; using System.Linq; using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; using Umbraco.Cms.Core.Services.Implement; +using Umbraco.Cms.Infrastructure.Services; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; @@ -22,7 +25,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services PublishedRepositoryEvents = true, WithApplication = true, Logger = UmbracoTestOptions.Logger.Console)] - public class ContentServiceEventTests : UmbracoIntegrationTest + public class ContentServiceNotificationTests : UmbracoIntegrationTest { private IContentTypeService ContentTypeService => GetRequiredService(); @@ -46,6 +49,14 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services CreateTestData(); } + protected override void CustomTestSetup(IUmbracoBuilder builder) => builder + .AddNotificationHandler, ContentNotificationHandler>() + .AddNotificationHandler, ContentNotificationHandler>() + .AddNotificationHandler, ContentNotificationHandler>() + .AddNotificationHandler, ContentNotificationHandler>() + .AddNotificationHandler, ContentNotificationHandler>() + .AddNotificationHandler, ContentNotificationHandler>(); + private void CreateTestData() { Template template = TemplateBuilder.CreateTextPageTemplate(); @@ -82,36 +93,43 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services // properties: title, bodyText, keywords, description document.SetValue("title", "title-en", "en-US"); - void OnSaving(IContentService sender, ContentSavingEventArgs e) + var savingWasCalled = false; + var savedWasCalled = false; + + ContentNotificationHandler.SavingContent = notification => { - IContent saved = e.SavedEntities.First(); + IContent saved = notification.SavedEntities.First(); Assert.AreSame(document, saved); - Assert.IsTrue(e.IsSavingCulture(saved, "en-US")); - Assert.IsFalse(e.IsSavingCulture(saved, "fr-FR")); - } + Assert.IsTrue(notification.IsSavingCulture(saved, "en-US")); + Assert.IsFalse(notification.IsSavingCulture(saved, "fr-FR")); - void OnSaved(IContentService sender, ContentSavedEventArgs e) + savingWasCalled = true; + }; + + ContentNotificationHandler.SavedContent = notification => { - IContent saved = e.SavedEntities.First(); + IContent saved = notification.SavedEntities.First(); Assert.AreSame(document, saved); - Assert.IsTrue(e.HasSavedCulture(saved, "en-US")); - Assert.IsFalse(e.HasSavedCulture(saved, "fr-FR")); - } + Assert.IsTrue(notification.HasSavedCulture(saved, "en-US")); + Assert.IsFalse(notification.HasSavedCulture(saved, "fr-FR")); + + savedWasCalled = true; + }; - ContentService.Saving += OnSaving; - ContentService.Saved += OnSaved; try { ContentService.Save(document); + Assert.IsTrue(savingWasCalled); + Assert.IsTrue(savedWasCalled); } finally { - ContentService.Saving -= OnSaving; - ContentService.Saved -= OnSaved; + ContentNotificationHandler.SavingContent = null; + ContentNotificationHandler.SavedContent = null; } } @@ -120,18 +138,23 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services { IContent document = new Content("content", -1, _contentType); - void OnSaving(IContentService sender, ContentSavingEventArgs e) + var savingWasCalled = false; + var savedWasCalled = false; + + ContentNotificationHandler.SavingContent = notification => { - IContent saved = e.SavedEntities.First(); + IContent saved = notification.SavedEntities.First(); Assert.IsTrue(document.GetValue("title").IsNullOrWhiteSpace()); saved.SetValue("title", "title"); - } - void OnSaved(IContentService sender, ContentSavedEventArgs e) + savingWasCalled = true; + }; + + ContentNotificationHandler.SavedContent = notification => { - IContent saved = e.SavedEntities.First(); + IContent saved = notification.SavedEntities.First(); Assert.AreSame("title", document.GetValue("title")); @@ -140,18 +163,20 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services Assert.AreEqual("title", propValue.EditedValue); Assert.IsNull(propValue.PublishedValue); - } - ContentService.Saving += OnSaving; - ContentService.Saved += OnSaved; + savedWasCalled = true; + }; + try { ContentService.Save(document); + Assert.IsTrue(savingWasCalled); + Assert.IsTrue(savedWasCalled); } finally { - ContentService.Saving -= OnSaving; - ContentService.Saved -= OnSaved; + ContentNotificationHandler.SavingContent = null; + ContentNotificationHandler.SavedContent = null; } } @@ -179,36 +204,43 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services // re-get - dirty properties need resetting document = ContentService.GetById(document.Id); - void OnPublishing(IContentService sender, ContentPublishingEventArgs e) + var publishingWasCalled = false; + var publishedWasCalled = false; + + ContentNotificationHandler.PublishingContent += notification => { - IContent publishing = e.PublishedEntities.First(); + IContent publishing = notification.PublishedEntities.First(); Assert.AreSame(document, publishing); - Assert.IsFalse(e.IsPublishingCulture(publishing, "en-US")); - Assert.IsTrue(e.IsPublishingCulture(publishing, "fr-FR")); - } + Assert.IsFalse(notification.IsPublishingCulture(publishing, "en-US")); + Assert.IsTrue(notification.IsPublishingCulture(publishing, "fr-FR")); - void OnPublished(IContentService sender, ContentPublishedEventArgs e) + publishingWasCalled = true; + }; + + ContentNotificationHandler.PublishedContent += notification => { - IContent published = e.PublishedEntities.First(); + IContent published = notification.PublishedEntities.First(); Assert.AreSame(document, published); - Assert.IsFalse(e.HasPublishedCulture(published, "en-US")); - Assert.IsTrue(e.HasPublishedCulture(published, "fr-FR")); - } + Assert.IsFalse(notification.HasPublishedCulture(published, "en-US")); + Assert.IsTrue(notification.HasPublishedCulture(published, "fr-FR")); + + publishedWasCalled = true; + }; - ContentService.Publishing += OnPublishing; - ContentService.Published += OnPublished; try { ContentService.SaveAndPublish(document, "fr-FR"); + Assert.IsTrue(publishingWasCalled); + Assert.IsTrue(publishedWasCalled); } finally { - ContentService.Publishing -= OnPublishing; - ContentService.Published -= OnPublished; + ContentNotificationHandler.PublishingContent = null; + ContentNotificationHandler.PublishedContent = null; } document = ContentService.GetById(document.Id); @@ -223,18 +255,23 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services { IContent document = new Content("content", -1, _contentType); - void OnSaving(IContentService sender, ContentSavingEventArgs e) + var savingWasCalled = false; + var savedWasCalled = false; + + ContentNotificationHandler.SavingContent = notification => { - IContent saved = e.SavedEntities.First(); + IContent saved = notification.SavedEntities.First(); Assert.IsTrue(document.GetValue("title").IsNullOrWhiteSpace()); saved.SetValue("title", "title"); - } - void OnSaved(IContentService sender, ContentSavedEventArgs e) + savingWasCalled = true; + }; + + ContentNotificationHandler.SavedContent = notification => { - IContent saved = e.SavedEntities.First(); + IContent saved = notification.SavedEntities.First(); Assert.AreSame("title", document.GetValue("title")); @@ -243,21 +280,20 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services Assert.AreEqual("title", propValue.EditedValue); Assert.AreEqual("title", propValue.PublishedValue); - } - // We are binding to Saving (not Publishing), because the Publishing event is really just used for cancelling, it should not be - // used for setting values and it won't actually work! This is because the Publishing event is raised AFTER the values on the model - // are published, but Saving is raised BEFORE. - ContentService.Saving += OnSaving; - ContentService.Saved += OnSaved; + savedWasCalled = true; + }; + try { ContentService.SaveAndPublish(document); + Assert.IsTrue(savingWasCalled); + Assert.IsTrue(savedWasCalled); } finally { - ContentService.Saving -= OnSaving; - ContentService.Saved -= OnSaved; + ContentNotificationHandler.SavingContent = null; + ContentNotificationHandler.SavedContent = null; } } @@ -278,27 +314,28 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services // re-create it document = new Content("content", -1, _contentType); - void OnSaving(IContentService sender, ContentSavingEventArgs e) + var savingWasCalled = false; + + ContentNotificationHandler.SavingContent = notification => { - IContent saved = e.SavedEntities.First(); + IContent saved = notification.SavedEntities.First(); Assert.IsTrue(document.GetValue("title").IsNullOrWhiteSpace()); saved.SetValue("title", "title"); - } - // We are binding to Saving (not Publishing), because the Publishing event is really just used for cancelling, it should not be - // used for setting values and it won't actually work! This is because the Publishing event is raised AFTER the values on the model - // are published, but Saving is raised BEFORE. - ContentService.Saving += OnSaving; + savingWasCalled = true; + }; + try { result = ContentService.SaveAndPublish(document); Assert.IsTrue(result.Success); // will succeed now because we were able to specify the required value in the Saving event + Assert.IsTrue(savingWasCalled); } finally { - ContentService.Saving -= OnSaving; + ContentNotificationHandler.SavingContent = null; } } @@ -328,44 +365,45 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services // re-get - dirty properties need resetting document = contentService.GetById(document.Id); - void OnPublishing(IContentService sender, ContentPublishingEventArgs e) - { - IContent publishing = e.PublishedEntities.First(); - - Assert.AreSame(document, publishing); - - Assert.IsFalse(e.IsPublishingCulture(publishing, "en-US")); - Assert.IsFalse(e.IsPublishingCulture(publishing, "fr-FR")); - - Assert.IsFalse(e.IsUnpublishingCulture(publishing, "en-US")); - Assert.IsTrue(e.IsUnpublishingCulture(publishing, "fr-FR")); - } - - void OnPublished(IContentService sender, ContentPublishedEventArgs e) - { - IContent published = e.PublishedEntities.First(); - - Assert.AreSame(document, published); - - Assert.IsFalse(e.HasPublishedCulture(published, "en-US")); - Assert.IsFalse(e.HasPublishedCulture(published, "fr-FR")); - - Assert.IsFalse(e.HasUnpublishedCulture(published, "en-US")); - Assert.IsTrue(e.HasUnpublishedCulture(published, "fr-FR")); - } - document.UnpublishCulture("fr-FR"); - ContentService.Publishing += OnPublishing; - ContentService.Published += OnPublished; + var unpublishingWasCalled = false; + var unpublishedWasCalled = false; + + ContentNotificationHandler.UnpublishingContent += notification => + { + IContent unpublished = notification.UnpublishedEntities.First(); + + Assert.AreSame(document, unpublished); + + Assert.IsFalse(notification.IsUnpublishingCulture(unpublished, "en-US")); + Assert.IsTrue(notification.IsUnpublishingCulture(unpublished, "fr-FR")); + + unpublishingWasCalled = true; + }; + + ContentNotificationHandler.UnpublishedContent += notification => + { + IContent unpublished = notification.UnpublishedEntities.First(); + + Assert.AreSame(document, unpublished); + + Assert.IsFalse(notification.HasUnpublishedCulture(unpublished, "en-US")); + Assert.IsTrue(notification.HasUnpublishedCulture(unpublished, "fr-FR")); + + unpublishedWasCalled = true; + }; + try { contentService.CommitDocumentChanges(document); + Assert.IsTrue(unpublishingWasCalled); + Assert.IsTrue(unpublishedWasCalled); } finally { - ContentService.Publishing -= OnPublishing; - ContentService.Published -= OnPublished; + ContentNotificationHandler.UnpublishingContent = null; + ContentNotificationHandler.UnpublishedContent = null; } document = contentService.GetById(document.Id); @@ -373,5 +411,38 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services Assert.IsFalse(document.IsCulturePublished("fr-FR")); Assert.IsTrue(document.IsCulturePublished("en-US")); } + + public class ContentNotificationHandler : + INotificationHandler>, + INotificationHandler>, + INotificationHandler>, + INotificationHandler>, + INotificationHandler>, + INotificationHandler> + { + public void Handle(SavingNotification notification) => SavingContent?.Invoke(notification); + + public void Handle(SavedNotification notification) => SavedContent?.Invoke(notification); + + public void Handle(PublishingNotification notification) => PublishingContent?.Invoke(notification); + + public void Handle(PublishedNotification notification) => PublishedContent?.Invoke(notification); + + public void Handle(UnpublishingNotification notification) => UnpublishingContent?.Invoke(notification); + + public void Handle(UnpublishedNotification notification) => UnpublishedContent?.Invoke(notification); + + public static Action> SavingContent { get; set; } + + public static Action> SavedContent { get; set; } + + public static Action> PublishingContent { get; set; } + + public static Action> PublishedContent { get; set; } + + public static Action> UnpublishingContent { get; set; } + + public static Action> UnpublishedContent { get; set; } + } } } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs index 5421add491..2321613bdc 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -9,6 +9,7 @@ using System.Threading; using Moq; using NUnit.Framework; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; @@ -20,6 +21,7 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; +using Umbraco.Cms.Infrastructure.Services; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Builders.Extensions; using Umbraco.Cms.Tests.Common.Extensions; @@ -73,6 +75,11 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services [SetUp] public void Setup() => ContentRepositoryBase.ThrowOnWarning = true; + protected override void CustomTestSetup(IUmbracoBuilder builder) => builder + .AddNotificationHandler, ContentNotificationHandler>() + .AddNotificationHandler, ContentNotificationHandler>() + .AddNotificationHandler, ContentNotificationHandler>(); + [TearDown] public void Teardown() => ContentRepositoryBase.ThrowOnWarning = false; @@ -1076,7 +1083,19 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services [Test] public void Can_Publish_Content_WithEvents() { - ContentService.Publishing += ContentServiceOnPublishing; + bool publishingWasCalled = false; + + ContentNotificationHandler.PublishingContent = notification => + { + Assert.AreEqual(1, notification.PublishedEntities.Count()); + IContent entity = notification.PublishedEntities.First(); + Assert.AreEqual("foo", entity.Name); + + IContent e = ContentService.GetById(entity.Id); + Assert.AreEqual("Home", e.Name); + + publishingWasCalled = true; + }; // tests that during 'publishing' event, what we get from the repo is the 'old' content, // because 'publishing' fires before the 'saved' event ie before the content is actually @@ -1094,23 +1113,15 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services IContent e = ContentService.GetById(content.Id); Assert.AreEqual("foo", e.Name); + + Assert.IsTrue(publishingWasCalled); } finally { - ContentService.Publishing -= ContentServiceOnPublishing; + ContentNotificationHandler.PublishingContent = null; } } - private void ContentServiceOnPublishing(IContentService sender, PublishEventArgs args) - { - Assert.AreEqual(1, args.PublishedEntities.Count()); - IContent entity = args.PublishedEntities.First(); - Assert.AreEqual("foo", entity.Name); - - IContent e = ContentService.GetById(entity.Id); - Assert.AreEqual("Home", e.Name); - } - [Test] public void Can_Not_Publish_Invalid_Cultures() { @@ -1899,26 +1910,31 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services public void Can_Copy_And_Modify_Content_With_Events() { // see https://github.com/umbraco/Umbraco-CMS/issues/5513 - static void Copying(IContentService sender, CopyEventArgs args) - { - args.Copy.SetValue("title", "1"); - args.Original.SetValue("title", "2"); - } - static void Copied(IContentService sender, CopyEventArgs args) + bool copyingWasCalled = false; + bool copiedWasCalled = false; + + ContentNotificationHandler.CopyingContent = notification => { - string copyVal = args.Copy.GetValue("title"); - string origVal = args.Original.GetValue("title"); + notification.Copy.SetValue("title", "1"); + notification.Original.SetValue("title", "2"); + + copyingWasCalled = true; + }; + + ContentNotificationHandler.CopiedContent = notification => + { + string copyVal = notification.Copy.GetValue("title"); + string origVal = notification.Original.GetValue("title"); Assert.AreEqual("1", copyVal); Assert.AreEqual("2", origVal); - } + + copiedWasCalled = true; + }; try { - ContentService.Copying += Copying; - ContentService.Copied += Copied; - Template template = TemplateBuilder.CreateTextPageTemplate(); FileService.SaveTemplate(template); @@ -1930,11 +1946,14 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services IContent copy = ContentService.Copy(content, content.ParentId, false, Constants.Security.SuperUserId); Assert.AreEqual("1", copy.GetValue("title")); + + Assert.IsTrue(copyingWasCalled); + Assert.IsTrue(copiedWasCalled); } finally { - ContentService.Copying -= Copying; - ContentService.Copied -= Copied; + ContentNotificationHandler.CopyingContent = null; + ContentNotificationHandler.CopiedContent = null; } } @@ -3275,5 +3294,23 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services return content; } + + public class ContentNotificationHandler : + INotificationHandler>, + INotificationHandler>, + INotificationHandler> + { + public void Handle(PublishingNotification notification) => PublishingContent?.Invoke(notification); + + public void Handle(CopyingNotification notification) => CopyingContent?.Invoke(notification); + + public void Handle(CopiedNotification notification) => CopiedContent?.Invoke(notification); + + public static Action> PublishingContent { get; set; } + + public static Action> CopyingContent { get; set; } + + public static Action> CopiedContent { get; set; } + } } } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs index 556a909255..c8f3bff84a 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs @@ -6,11 +6,13 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Exceptions; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; +using Umbraco.Cms.Infrastructure.Services; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; @@ -30,6 +32,9 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services private ContentTypeService ContentTypeService => (ContentTypeService)GetRequiredService(); + protected override void CustomTestSetup(IUmbracoBuilder builder) => builder + .AddNotificationHandler, ContentNotificationHandler>(); + [Test] public void CanSaveAndGetIsElement() { @@ -112,7 +117,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services [Test] public void Deleting_Content_Types_With_Hierarchy_Of_Content_Items_Doesnt_Raise_Trashed_Event_For_Deleted_Items_1() { - ContentService.Trashed += ContentServiceOnTrashed; + ContentNotificationHandler.MovedContentToRecycleBin = MovedContentToRecycleBin; try { @@ -151,14 +156,14 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services } finally { - ContentService.Trashed -= ContentServiceOnTrashed; + ContentNotificationHandler.MovedContentToRecycleBin = null; } } [Test] public void Deleting_Content_Types_With_Hierarchy_Of_Content_Items_Doesnt_Raise_Trashed_Event_For_Deleted_Items_2() { - ContentService.Trashed += ContentServiceOnTrashed; + ContentNotificationHandler.MovedContentToRecycleBin = MovedContentToRecycleBin; try { @@ -194,13 +199,13 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services } finally { - ContentService.Trashed -= ContentServiceOnTrashed; + ContentNotificationHandler.MovedContentToRecycleBin = null; } } - private void ContentServiceOnTrashed(IContentService sender, MoveEventArgs e) + private void MovedContentToRecycleBin(MovedToRecycleBinNotification notification) { - foreach (MoveEventInfo item in e.MoveInfoCollection) + foreach (MoveEventInfo item in notification.MoveInfoCollection) { // if this item doesn't exist then Fail! IContent exists = ContentService.GetById(item.Entity.Id); @@ -1729,5 +1734,13 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services return list.ToArray(); } + + public class ContentNotificationHandler : + INotificationHandler> + { + public void Handle(MovedToRecycleBinNotification notification) => MovedContentToRecycleBin?.Invoke(notification); + + public static Action> MovedContentToRecycleBin { get; set; } + } } } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs index ad1d353039..0d9ce8e5b8 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -6,10 +6,12 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using NUnit.Framework; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; +using Umbraco.Cms.Infrastructure.Services; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; @@ -25,6 +27,9 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services private IMediaTypeService MediaTypeService => GetRequiredService(); + protected override void CustomTestSetup(IUmbracoBuilder builder) => builder + .AddNotificationHandler, ContentNotificationHandler>(); + [Test] public void Get_With_Missing_Guid() { @@ -94,7 +99,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services [Test] public void Deleting_Media_Types_With_Hierarchy_Of_Media_Items_Doesnt_Raise_Trashed_Event_For_Deleted_Items() { - MediaService.Trashed += MediaServiceOnTrashed; + ContentNotificationHandler.MovedMediaToRecycleBin = MovedMediaToRecycleBin; try { @@ -130,13 +135,13 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services } finally { - MediaService.Trashed -= MediaServiceOnTrashed; + ContentNotificationHandler.MovedMediaToRecycleBin = null; } } - private void MediaServiceOnTrashed(IMediaService sender, MoveEventArgs e) + private void MovedMediaToRecycleBin(MovedToRecycleBinNotification notification) { - foreach (MoveEventInfo item in e.MoveInfoCollection) + foreach (MoveEventInfo item in notification.MoveInfoCollection) { // if this item doesn't exist then Fail! IMedia exists = MediaService.GetById(item.Entity.Id); @@ -213,5 +218,13 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services Assert.AreNotEqual(clonedMediaType.PropertyTypes.First(x => x.Alias.StartsWith("umbracoFile")).Id, originalMediaType.PropertyTypes.First(x => x.Alias.StartsWith("umbracoFile")).Id); Assert.AreNotEqual(clonedMediaType.PropertyGroups.First(x => x.Name.StartsWith("Media")).Id, originalMediaType.PropertyGroups.First(x => x.Name.StartsWith("Media")).Id); } + + public class ContentNotificationHandler : + INotificationHandler> + { + public void Handle(MovedToRecycleBinNotification notification) => MovedMediaToRecycleBin?.Invoke(notification); + + public static Action> MovedMediaToRecycleBin { get; set; } + } } } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/BlockEditorComponentTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/BlockEditorComponentTests.cs index e1b9ec9264..590ff58222 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/BlockEditorComponentTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/BlockEditorComponentTests.cs @@ -7,7 +7,7 @@ using System.Linq; using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Compose; +using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Extensions; using Constants = Umbraco.Cms.Core.Constants; @@ -32,7 +32,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors [Test] public void Cannot_Have_Null_Udi() { - var component = new BlockEditorComponent(); + var component = new BlockEditorPropertyHandler(); var json = GetBlockListJson(null, string.Empty); Assert.Throws(() => component.ReplaceBlockListUdis(json)); } @@ -48,7 +48,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors var expected = ReplaceGuids(json, guids, ContentGuid1, ContentGuid2, SettingsGuid1); - var component = new BlockEditorComponent(); + var component = new BlockEditorPropertyHandler(); var result = component.ReplaceBlockListUdis(json, GuidFactory); var expectedJson = JsonConvert.SerializeObject(JsonConvert.DeserializeObject(expected, _serializerSettings), _serializerSettings); @@ -75,7 +75,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors // get the json with the subFeatures as escaped var json = GetBlockListJson(innerJsonEscaped); - var component = new BlockEditorComponent(); + var component = new BlockEditorPropertyHandler(); var result = component.ReplaceBlockListUdis(json, GuidFactory); // the expected result is that the subFeatures data is no longer escaped @@ -119,7 +119,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors SubContentGuid2, SubSettingsGuid1); - var component = new BlockEditorComponent(); + var component = new BlockEditorPropertyHandler(); var result = component.ReplaceBlockListUdis(json, GuidFactory); var expectedJson = JsonConvert.SerializeObject(JsonConvert.DeserializeObject(expected, _serializerSettings), _serializerSettings); @@ -147,7 +147,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors var json = GetBlockListJson(complexEditorJsonEscaped); - var component = new BlockEditorComponent(); + var component = new BlockEditorPropertyHandler(); var result = component.ReplaceBlockListUdis(json, GuidFactory); // the expected result is that the subFeatures data is no longer escaped diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/NestedContentPropertyComponentTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/NestedContentPropertyComponentTests.cs index d132f346b4..0ada6a20dd 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/NestedContentPropertyComponentTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/NestedContentPropertyComponentTests.cs @@ -4,7 +4,7 @@ using System; using Newtonsoft.Json; using NUnit.Framework; -using Umbraco.Cms.Core.Compose; +using Umbraco.Cms.Core.PropertyEditors; namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors { @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors [Test] public void Invalid_Json() { - var component = new NestedContentPropertyComponent(); + var component = new NestedContentPropertyHandler(); Assert.DoesNotThrow(() => component.CreateNestedContentKeys("this is not json", true)); } @@ -34,7 +34,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors .Replace("04a6dba8-813c-4144-8aca-86a3f24ebf08", guids[0].ToString()) .Replace("d8e214d8-c5a5-4b45-9b51-4050dd47f5fa", guids[1].ToString()); - var component = new NestedContentPropertyComponent(); + var component = new NestedContentPropertyHandler(); var result = component.CreateNestedContentKeys(json, false, GuidFactory); Assert.AreEqual(JsonConvert.DeserializeObject(expected).ToString(), JsonConvert.DeserializeObject(result).ToString()); @@ -78,7 +78,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors .Replace("dccf550c-3a05-469e-95e1-a8f560f788c2", guids[2].ToString()) .Replace("fbde4288-8382-4e13-8933-ed9c160de050", guids[3].ToString()); - var component = new NestedContentPropertyComponent(); + var component = new NestedContentPropertyHandler(); var result = component.CreateNestedContentKeys(json, false, GuidFactory); Assert.AreEqual(JsonConvert.DeserializeObject(expected).ToString(), JsonConvert.DeserializeObject(result).ToString()); @@ -126,7 +126,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors .Replace("dccf550c-3a05-469e-95e1-a8f560f788c2", guids[2].ToString()) .Replace("fbde4288-8382-4e13-8933-ed9c160de050", guids[3].ToString()); - var component = new NestedContentPropertyComponent(); + var component = new NestedContentPropertyHandler(); var result = component.CreateNestedContentKeys(json, false, GuidFactory); Assert.AreEqual(JsonConvert.DeserializeObject(expected).ToString(), JsonConvert.DeserializeObject(result).ToString()); @@ -230,7 +230,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors .Replace("dccf550c-3a05-469e-95e1-a8f560f788c2", guids[2].ToString()) .Replace("fbde4288-8382-4e13-8933-ed9c160de050", guids[3].ToString()); - var component = new NestedContentPropertyComponent(); + var component = new NestedContentPropertyHandler(); var result = component.CreateNestedContentKeys(json, false, GuidFactory); Assert.AreEqual(JsonConvert.DeserializeObject(expected).ToString(), JsonConvert.DeserializeObject(result).ToString()); @@ -248,7 +248,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors {""name"":""Item 2 was copied and has no key prop"",""ncContentTypeAlias"":""nested"",""text"":""zoot""} ]"; - var component = new NestedContentPropertyComponent(); + var component = new NestedContentPropertyHandler(); var result = component.CreateNestedContentKeys(json, true, GuidFactory); // Ensure the new GUID is put in a key into the JSON @@ -291,7 +291,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors } ]"; - var component = new NestedContentPropertyComponent(); + var component = new NestedContentPropertyHandler(); var result = component.CreateNestedContentKeys(json, true, GuidFactory); // Ensure the new GUID is put in a key into the JSON for each item @@ -390,7 +390,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors } ]"; - var component = new NestedContentPropertyComponent(); + var component = new NestedContentPropertyHandler(); var result = component.CreateNestedContentKeys(json, true, GuidFactory); // Ensure the new GUID is put in a key into the JSON for each item diff --git a/src/Umbraco.Tests/Models/MediaXmlTest.cs b/src/Umbraco.Tests/Models/MediaXmlTest.cs index 6b6be30dab..bb26b2e70e 100644 --- a/src/Umbraco.Tests/Models/MediaXmlTest.cs +++ b/src/Umbraco.Tests/Models/MediaXmlTest.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Xml.Linq; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -36,7 +36,7 @@ namespace Umbraco.Tests.Models var contentSettings = new ContentSettings(); var mediaFileSystem = new MediaFileSystem(Mock.Of(), scheme, loggerFactory.CreateLogger(), ShortStringHelper); - var ignored = new FileUploadPropertyEditor(loggerFactory, mediaFileSystem, Microsoft.Extensions.Options.Options.Create(contentSettings), DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, UploadAutoFillProperties, JsonNetSerializer); + var ignored = new FileUploadPropertyEditor(loggerFactory, mediaFileSystem, Microsoft.Extensions.Options.Options.Create(contentSettings), DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, UploadAutoFillProperties, JsonNetSerializer, ContentService); var media = MockedMedia.CreateMediaImage(mediaType, -1); media.WriterId = -1; // else it's zero and that's not a user and it breaks the tests diff --git a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs index a3a54a5fcf..7149938b39 100644 --- a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -38,8 +38,8 @@ namespace Umbraco.Tests.Routing var dataTypeService = Mock.Of(); var propertyEditors = new MediaUrlGeneratorCollection(new IMediaUrlGenerator[] { - new FileUploadPropertyEditor(loggerFactory, mediaFileSystemMock, Microsoft.Extensions.Options.Options.Create(contentSettings), dataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, UploadAutoFillProperties, JsonNetSerializer), - new ImageCropperPropertyEditor(loggerFactory, mediaFileSystemMock, Microsoft.Extensions.Options.Options.Create(contentSettings), dataTypeService, LocalizationService, IOHelper, ShortStringHelper, LocalizedTextService, UploadAutoFillProperties, JsonNetSerializer), + new FileUploadPropertyEditor(loggerFactory, mediaFileSystemMock, Microsoft.Extensions.Options.Options.Create(contentSettings), dataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, UploadAutoFillProperties, JsonNetSerializer, ContentService), + new ImageCropperPropertyEditor(loggerFactory, mediaFileSystemMock, Microsoft.Extensions.Options.Options.Create(contentSettings), dataTypeService, LocalizationService, IOHelper, ShortStringHelper, LocalizedTextService, UploadAutoFillProperties, JsonNetSerializer, ContentService), }); _mediaUrlProvider = new DefaultMediaUrlProvider(propertyEditors, UriUtility); } diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index d8d86c3162..e35959648d 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; 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,6 +23,7 @@ using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.PublishedCache; using Umbraco.Cms.Infrastructure.PublishedCache.Persistence; +using Umbraco.Cms.Infrastructure.Services; using Umbraco.Cms.Tests.Common; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Extensions; @@ -49,6 +51,14 @@ namespace Umbraco.Tests.Scoping Builder.Services.AddUnique(f => Mock.Of()); Builder.WithCollectionBuilder() .Add(() => Builder.TypeLoader.GetCacheRefreshers()); + Builder.AddNotificationHandler, NotificationHandler>(); + } + + public class NotificationHandler : INotificationHandler> + { + public void Handle(PublishedNotification notification) => PublishedContent?.Invoke(notification); + + public static Action> PublishedContent { get; set; } } public override void TearDown() @@ -58,17 +68,9 @@ namespace Umbraco.Tests.Scoping _distributedCacheBinder?.UnbindEvents(); _distributedCacheBinder = null; - _onPublishedAssertAction = null; - ContentService.Published -= OnPublishedAssert; + NotificationHandler.PublishedContent = null; } - private void OnPublishedAssert(IContentService sender, PublishEventArgs args) - { - _onPublishedAssertAction?.Invoke(); - } - - private Action _onPublishedAssertAction; - protected override IPublishedSnapshotService CreatePublishedSnapshotService(GlobalSettings globalSettings = null) { var options = new PublishedSnapshotServiceOptions { IgnoreLocalDb = true }; @@ -149,7 +151,7 @@ namespace Umbraco.Tests.Scoping // event handler var evented = 0; - _onPublishedAssertAction = () => + NotificationHandler.PublishedContent = notification => { evented++; @@ -171,8 +173,6 @@ namespace Umbraco.Tests.Scoping Assert.IsNotNull(x); Assert.AreEqual("name", x.Name(VariationContextAccessor)); - ContentService.Published += OnPublishedAssert; - using (var scope = ScopeProvider.CreateScope()) { item.Name = "changed"; diff --git a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs index 4a47f3689e..50f4828efd 100644 --- a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs @@ -7,6 +7,7 @@ using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PublishedCache; @@ -14,6 +15,7 @@ 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.Services; using Umbraco.Cms.Infrastructure.Sync; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Extensions; @@ -41,6 +43,7 @@ namespace Umbraco.Tests.Scoping Builder.Services.AddUnique(f => Mock.Of()); Builder.WithCollectionBuilder() .Add(() => Builder.TypeLoader.GetCacheRefreshers()); + Builder.AddNotificationHandler, NotificationHandler>(); } protected override void ComposeSettings() @@ -54,24 +57,24 @@ namespace Umbraco.Tests.Scoping Builder.Services.AddTransient(x => Microsoft.Extensions.Options.Options.Create(userPasswordConfigurationSettings)); } + + public class NotificationHandler : INotificationHandler> + { + public void Handle(PublishedNotification notification) => PublishedContent?.Invoke(notification); + + public static Action> PublishedContent { get; set; } + } + [TearDown] public void Teardown() { _distributedCacheBinder?.UnbindEvents(); _distributedCacheBinder = null; - _onPublishedAssertAction = null; - ContentService.Published -= OnPublishedAssert; + NotificationHandler.PublishedContent = null; SafeXmlReaderWriter.Cloning = null; } - private void OnPublishedAssert(IContentService sender, PublishEventArgs args) - { - _onPublishedAssertAction?.Invoke(); - } - - private Action _onPublishedAssertAction; - // in 7.6, content.Instance // .XmlContent - comes from .XmlContentInternal and is cached in context items for current request // .XmlContentInternal - the actual main xml document @@ -111,7 +114,7 @@ namespace Umbraco.Tests.Scoping // event handler var evented = 0; - _onPublishedAssertAction = () => + NotificationHandler.PublishedContent = notification => { evented++; @@ -128,8 +131,6 @@ namespace Umbraco.Tests.Scoping Assert.AreEqual(beforeOuterXml, xml.OuterXml); }; - ContentService.Published += OnPublishedAssert; - using (var scope = ScopeProvider.CreateScope()) { ServiceContext.ContentService.SaveAndPublish(item); // should create an xml clone diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 47ba5f78ce..079f848abb 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -160,6 +160,7 @@ namespace Umbraco.Tests.Testing protected UmbracoMapper Mapper => Factory.GetRequiredService(); protected IHttpContextAccessor HttpContextAccessor => Factory.GetRequiredService(); + protected IContentService ContentService => Factory.GetRequiredService(); protected IRuntimeState RuntimeState => MockRuntimeState(RuntimeLevel.Run); private ILoggerFactory _loggerFactory; From 1462e6c3afadc4c785217a3b616184bbd5fc82d7 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 2 Mar 2021 14:30:57 +0100 Subject: [PATCH 10/42] Split notifications into separate classes + move namespace --- .../Events/ICancelableNotification.cs | 10 + .../Compose/NotificationsHandler.cs | 2 +- .../Compose/RelateOnTrashHandler.cs | 2 +- ...ropertyEditorContentNotificationHandler.cs | 2 +- .../FileUploadPropertyEditor.cs | 3 +- .../ImageCropperPropertyEditor.cs | 2 +- .../Routing/RedirectTrackingHandler.cs | 2 +- .../Services/CancelableNotification.cs | 418 ------------------ .../Services/Implement/ContentService.cs | 2 +- .../Services/Implement/MediaService.cs | 2 +- .../CancelableEnumerableObjectNotification.cs | 18 + .../CancelableObjectNotification.cs | 23 + .../ContentNotificationExtensions.cs | 47 ++ .../Notifications/CopiedNotification.cs | 22 + .../Notifications/CopyingNotification.cs | 22 + .../DeletedBlueprintNotification.cs | 21 + .../Notifications/DeletedNotification.cs | 17 + .../DeletedVersionsNotification.cs | 30 ++ .../Notifications/DeletingNotification.cs | 21 + .../DeletingVersionsNotification.cs | 18 + .../EmptiedRecycleBinNotification.cs | 17 + .../EmptyingRecycleBinNotification.cs | 17 + .../EnumerableObjectNotification.cs | 19 + .../Notifications/MovedNotification.cs | 21 + .../MovedToRecycleBinNotification.cs | 21 + .../Notifications/MovingNotification.cs | 21 + .../MovingToRecycleBinNotification.cs | 21 + .../Notifications/ObjectNotification.cs | 20 + .../Notifications/PublishedNotification.cs | 21 + .../Notifications/PublishingNotification.cs | 21 + .../Notifications/RolledBackNotification.cs | 16 + .../Notifications/RollingBackNotification.cs | 16 + .../SavedBlueprintNotification.cs | 16 + .../Notifications/SavedNotification.cs | 21 + .../Notifications/SavingNotification.cs | 21 + .../SendingToPublishNotification.cs | 16 + .../SentToPublishNotification.cs | 16 + .../Notifications/SortedNotification.cs | 17 + .../Notifications/SortingNotification.cs | 17 + .../Notifications/UnpublishedNotification.cs | 21 + .../Notifications/UnpublishingNotification.cs | 21 + .../ContentServiceNotificationTests.cs | 2 +- .../Services/ContentServiceTests.cs | 2 +- .../Services/ContentTypeServiceTests.cs | 2 +- .../Services/MediaTypeServiceTests.cs | 2 +- .../Scoping/ScopedNuCacheTests.cs | 3 +- src/Umbraco.Tests/Scoping/ScopedXmlTests.cs | 4 +- 47 files changed, 660 insertions(+), 436 deletions(-) create mode 100644 src/Umbraco.Core/Events/ICancelableNotification.cs delete mode 100644 src/Umbraco.Infrastructure/Services/CancelableNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/CancelableEnumerableObjectNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/CancelableObjectNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentNotificationExtensions.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/CopyingNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/DeletedBlueprintNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/DeletedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/DeletingNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/DeletingVersionsNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/EnumerableObjectNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/MovedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/MovedToRecycleBinNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/MovingNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/MovingToRecycleBinNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ObjectNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/PublishedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/PublishingNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/RolledBackNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/RollingBackNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/SavedBlueprintNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/SavedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/SavingNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/SendingToPublishNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/SentToPublishNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/SortedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/SortingNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/UnpublishedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/UnpublishingNotification.cs diff --git a/src/Umbraco.Core/Events/ICancelableNotification.cs b/src/Umbraco.Core/Events/ICancelableNotification.cs new file mode 100644 index 0000000000..df1abc672e --- /dev/null +++ b/src/Umbraco.Core/Events/ICancelableNotification.cs @@ -0,0 +1,10 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +namespace Umbraco.Cms.Core.Events +{ + public interface ICancelableNotification : INotification + { + bool Cancel { get; set; } + } +} diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs b/src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs index cfd7121191..205aece4d1 100644 --- a/src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs +++ b/src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs @@ -16,7 +16,7 @@ using Umbraco.Cms.Core.Models.Entities; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Infrastructure.Services; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Compose diff --git a/src/Umbraco.Infrastructure/Compose/RelateOnTrashHandler.cs b/src/Umbraco.Infrastructure/Compose/RelateOnTrashHandler.cs index 216b051c86..e1fcdb3bea 100644 --- a/src/Umbraco.Infrastructure/Compose/RelateOnTrashHandler.cs +++ b/src/Umbraco.Infrastructure/Compose/RelateOnTrashHandler.cs @@ -3,7 +3,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Infrastructure.Services; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Compose diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs index 083dce1a96..29d23c1c33 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Infrastructure.Services; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs index 6ef2be84ce..08308af246 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs @@ -14,7 +14,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Infrastructure.Services; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors @@ -126,7 +126,6 @@ namespace Umbraco.Cms.Core.PropertyEditors } } - public void Handle(CopiedNotification notification) { // get the upload field properties with a value diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs index ab1c4d9e67..8c618c73e4 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs @@ -16,7 +16,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Infrastructure.Services; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs index c1456af8b3..2ff847d32f 100644 --- a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs @@ -8,7 +8,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Infrastructure.Services; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Routing diff --git a/src/Umbraco.Infrastructure/Services/CancelableNotification.cs b/src/Umbraco.Infrastructure/Services/CancelableNotification.cs deleted file mode 100644 index 4e9ef538b6..0000000000 --- a/src/Umbraco.Infrastructure/Services/CancelableNotification.cs +++ /dev/null @@ -1,418 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System; -using System.Collections.Generic; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.Infrastructure.Services -{ - // TODO split this file into several small classes and move to another namespace - - public interface ICancelableNotification : INotification - { - bool Cancel { get; set; } - } - - public abstract class ObjectNotification : INotification where T : class - { - protected ObjectNotification(T target, EventMessages messages) - { - Messages = messages; - Target = target; - } - - public EventMessages Messages { get; } - - protected T Target { get; } - } - - public abstract class EnumerableObjectNotification : ObjectNotification> - { - protected EnumerableObjectNotification(T target, EventMessages messages) : base(new [] {target}, messages) - { - } - - protected EnumerableObjectNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - } - - public abstract class CancelableObjectNotification : ObjectNotification, ICancelableNotification where T : class - { - protected CancelableObjectNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public bool Cancel { get; set; } - - public void CancelOperation(EventMessage cancelationMessage) - { - Cancel = true; - cancelationMessage.IsDefaultEventMessage = true; - Messages.Add(cancelationMessage); - } - } - - public abstract class CancelableEnumerableObjectNotification : CancelableObjectNotification> - { - protected CancelableEnumerableObjectNotification(T target, EventMessages messages) : base(new [] {target}, messages) - { - } - protected CancelableEnumerableObjectNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - } - - public class DeletingNotification : CancelableEnumerableObjectNotification - { - public DeletingNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public DeletingNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable DeletedEntities => Target; - } - - public class DeletedNotification : EnumerableObjectNotification - { - public DeletedNotification(T target, EventMessages messages) : base(target, messages) => MediaFilesToDelete = new List(); - - public IEnumerable DeletedEntities => Target; - - public List MediaFilesToDelete { get; } - } - - public class DeletedBlueprintNotification : EnumerableObjectNotification - { - public DeletedBlueprintNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public DeletedBlueprintNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable DeletedBlueprints => Target; - } - - public class SortingNotification : CancelableEnumerableObjectNotification - { - public SortingNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable SortedEntities => Target; - } - - public class SortedNotification : EnumerableObjectNotification - { - public SortedNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable SortedEntities => Target; - } - - public class SavingNotification : CancelableEnumerableObjectNotification - { - public SavingNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public SavingNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable SavedEntities => Target; - } - - public class SavedNotification : EnumerableObjectNotification - { - public SavedNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public SavedNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable SavedEntities => Target; - } - - public class SavedBlueprintNotification : ObjectNotification where T : class - { - public SavedBlueprintNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public T SavedBlueprint => Target; - } - - public class PublishingNotification : CancelableEnumerableObjectNotification - { - public PublishingNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public PublishingNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable PublishedEntities => Target; - } - - public class PublishedNotification : EnumerableObjectNotification - { - public PublishedNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public PublishedNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable PublishedEntities => Target; - } - - public class MovingNotification : CancelableObjectNotification>> - { - public MovingNotification(MoveEventInfo target, EventMessages messages) : base(new[] {target}, messages) - { - } - - public MovingNotification(IEnumerable> target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable> MoveInfoCollection => Target; - } - - public class MovedNotification : ObjectNotification>> - { - public MovedNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) - { - } - - public MovedNotification(IEnumerable> target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable> MoveInfoCollection => Target; - } - - public class MovingToRecycleBinNotification : CancelableObjectNotification>> - { - public MovingToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) - { - } - - public MovingToRecycleBinNotification(IEnumerable> target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable> MoveInfoCollection => Target; - } - - public class MovedToRecycleBinNotification : ObjectNotification>> - { - public MovedToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) - { - } - - public MovedToRecycleBinNotification(IEnumerable> target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable> MoveInfoCollection => Target; - } - - public class CopyingNotification : CancelableObjectNotification where T : class - { - public CopyingNotification(T original, T copy, int parentId, EventMessages messages) : base(original, messages) - { - Copy = copy; - ParentId = parentId; - } - - public T Original => Target; - - public T Copy { get; } - - public int ParentId { get; } - } - - public class CopiedNotification : ObjectNotification where T : class - { - public CopiedNotification(T original, T copy, int parentId, EventMessages messages) : base(original, messages) - { - Copy = copy; - ParentId = parentId; - } - - public T Original => Target; - - public T Copy { get; } - - public int ParentId { get; } - } - - public class RollingBackNotification : CancelableObjectNotification where T : class - { - public RollingBackNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public T Entity => Target; - } - - public class RolledBackNotification : ObjectNotification where T : class - { - public RolledBackNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public T Entity => Target; - } - - public class SendingToPublishNotification : CancelableObjectNotification where T : class - { - public SendingToPublishNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public T Entity => Target; - } - - public class SentToPublishNotification : ObjectNotification where T : class - { - public SentToPublishNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public T Entity => Target; - } - - - public class UnpublishingNotification : CancelableEnumerableObjectNotification - { - public UnpublishingNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public UnpublishingNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable UnpublishedEntities => Target; - } - - public class UnpublishedNotification : EnumerableObjectNotification - { - public UnpublishedNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public UnpublishedNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable UnpublishedEntities => Target; - } - - public class EmptiedRecycleBinNotification : INotification where T : class - { - public EmptiedRecycleBinNotification(EventMessages messages) - { - Messages = messages; - } - - public EventMessages Messages { get; } - } - - public class EmptyingRecycleBinNotification : EmptiedRecycleBinNotification, ICancelableNotification where T : class - { - public EmptyingRecycleBinNotification(EventMessages messages) - : base(messages) - { - } - - public bool Cancel { get; set; } - } - - public class DeletedVersionsNotification : INotification where T : class - { - public DeletedVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) - { - Id = id; - Messages = messages; - SpecificVersion = specificVersion; - DeletePriorVersions = deletePriorVersions; - DateToRetain = dateToRetain; - } - - public int Id { get; } - - public EventMessages Messages { get; } - - public int SpecificVersion { get; } - - public bool DeletePriorVersions { get; } - - public DateTime DateToRetain { get; } - } - - public class DeletingVersionsNotification : DeletedVersionsNotification, ICancelableNotification where T : class - { - public DeletingVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) - : base(id, messages, specificVersion, deletePriorVersions, dateToRetain) - { - } - - public bool Cancel { get; set; } - } - - public static class ContentNotificationExtensions - { - /// - /// Determines whether a culture is being saved, during a Saving notification - /// - public static bool IsSavingCulture(this SavingNotification notification, T content, string culture) where T : IContentBase - => content.CultureInfos.TryGetValue(culture, out ContentCultureInfos cultureInfo) && cultureInfo.IsDirty(); - - /// - /// Determines whether a culture has been saved, during a Saved notification - /// - public static bool HasSavedCulture(this SavedNotification notification, T content, string culture) where T : IContentBase - => content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.UpdatedCulture + culture); - - /// - /// Determines whether a culture is being published, during a Publishing notification - /// - public static bool IsPublishingCulture(this PublishingNotification notification, IContent content, string culture) - => content.PublishCultureInfos.TryGetValue(culture, out ContentCultureInfos cultureInfo) && cultureInfo.IsDirty(); - - /// - /// Determines whether a culture is being unpublished, during a Publishing notification - /// - public static bool IsUnpublishingCulture(this UnpublishingNotification notification, IContent content, string culture) - => content.IsPropertyDirty(ContentBase.ChangeTrackingPrefix.UnpublishedCulture + culture); //bit of a hack since we know that the content implementation tracks changes this way - - /// - /// Determines whether a culture has been published, during a Published notification - /// - public static bool HasPublishedCulture(this PublishedNotification notification, IContent content, string culture) - => content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.ChangedCulture + culture); - - /// - /// Determines whether a culture has been unpublished, during a Published notification - /// - public static bool HasUnpublishedCulture(this UnpublishedNotification notification, IContent content, string culture) - => content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.UnpublishedCulture + culture); - - } -} diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index e8013ac5d9..8928b4972d 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -13,7 +13,7 @@ using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence.Querying; -using Umbraco.Cms.Infrastructure.Services; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Services.Implement diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index b7fe23dd7f..4ccf48a690 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -13,7 +13,7 @@ using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence.Querying; -using Umbraco.Cms.Infrastructure.Services; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Services.Implement diff --git a/src/Umbraco.Infrastructure/Services/Notifications/CancelableEnumerableObjectNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/CancelableEnumerableObjectNotification.cs new file mode 100644 index 0000000000..3875967530 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/CancelableEnumerableObjectNotification.cs @@ -0,0 +1,18 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public abstract class CancelableEnumerableObjectNotification : CancelableObjectNotification> + { + protected CancelableEnumerableObjectNotification(T target, EventMessages messages) : base(new [] {target}, messages) + { + } + protected CancelableEnumerableObjectNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/CancelableObjectNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/CancelableObjectNotification.cs new file mode 100644 index 0000000000..603ee9efbb --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/CancelableObjectNotification.cs @@ -0,0 +1,23 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public abstract class CancelableObjectNotification : ObjectNotification, ICancelableNotification where T : class + { + protected CancelableObjectNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public bool Cancel { get; set; } + + public void CancelOperation(EventMessage cancelationMessage) + { + Cancel = true; + cancelationMessage.IsDefaultEventMessage = true; + Messages.Add(cancelationMessage); + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentNotificationExtensions.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentNotificationExtensions.cs new file mode 100644 index 0000000000..1a05c2f79d --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentNotificationExtensions.cs @@ -0,0 +1,47 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public static class ContentNotificationExtensions + { + /// + /// Determines whether a culture is being saved, during a Saving notification + /// + public static bool IsSavingCulture(this SavingNotification notification, T content, string culture) where T : IContentBase + => content.CultureInfos.TryGetValue(culture, out ContentCultureInfos cultureInfo) && cultureInfo.IsDirty(); + + /// + /// Determines whether a culture has been saved, during a Saved notification + /// + public static bool HasSavedCulture(this SavedNotification notification, T content, string culture) where T : IContentBase + => content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.UpdatedCulture + culture); + + /// + /// Determines whether a culture is being published, during a Publishing notification + /// + public static bool IsPublishingCulture(this PublishingNotification notification, IContent content, string culture) + => content.PublishCultureInfos.TryGetValue(culture, out ContentCultureInfos cultureInfo) && cultureInfo.IsDirty(); + + /// + /// Determines whether a culture is being unpublished, during a Publishing notification + /// + public static bool IsUnpublishingCulture(this UnpublishingNotification notification, IContent content, string culture) + => content.IsPropertyDirty(ContentBase.ChangeTrackingPrefix.UnpublishedCulture + culture); //bit of a hack since we know that the content implementation tracks changes this way + + /// + /// Determines whether a culture has been published, during a Published notification + /// + public static bool HasPublishedCulture(this PublishedNotification notification, IContent content, string culture) + => content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.ChangedCulture + culture); + + /// + /// Determines whether a culture has been unpublished, during a Published notification + /// + public static bool HasUnpublishedCulture(this UnpublishedNotification notification, IContent content, string culture) + => content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.UnpublishedCulture + culture); + + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs new file mode 100644 index 0000000000..f89487cbc0 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs @@ -0,0 +1,22 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class CopiedNotification : ObjectNotification where T : class + { + public CopiedNotification(T original, T copy, int parentId, EventMessages messages) : base(original, messages) + { + Copy = copy; + ParentId = parentId; + } + + public T Original => Target; + + public T Copy { get; } + + public int ParentId { get; } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/CopyingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/CopyingNotification.cs new file mode 100644 index 0000000000..a62b3ba869 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/CopyingNotification.cs @@ -0,0 +1,22 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class CopyingNotification : CancelableObjectNotification where T : class + { + public CopyingNotification(T original, T copy, int parentId, EventMessages messages) : base(original, messages) + { + Copy = copy; + ParentId = parentId; + } + + public T Original => Target; + + public T Copy { get; } + + public int ParentId { get; } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletedBlueprintNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletedBlueprintNotification.cs new file mode 100644 index 0000000000..4a4ad777bf --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletedBlueprintNotification.cs @@ -0,0 +1,21 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class DeletedBlueprintNotification : EnumerableObjectNotification + { + public DeletedBlueprintNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public DeletedBlueprintNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable DeletedBlueprints => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletedNotification.cs new file mode 100644 index 0000000000..f6e28f74ed --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletedNotification.cs @@ -0,0 +1,17 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class DeletedNotification : EnumerableObjectNotification + { + public DeletedNotification(T target, EventMessages messages) : base(target, messages) => MediaFilesToDelete = new List(); + + public IEnumerable DeletedEntities => Target; + + public List MediaFilesToDelete { get; } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotification.cs new file mode 100644 index 0000000000..3f55abf4b4 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotification.cs @@ -0,0 +1,30 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class DeletedVersionsNotification : INotification where T : class + { + public DeletedVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) + { + Id = id; + Messages = messages; + SpecificVersion = specificVersion; + DeletePriorVersions = deletePriorVersions; + DateToRetain = dateToRetain; + } + + public int Id { get; } + + public EventMessages Messages { get; } + + public int SpecificVersion { get; } + + public bool DeletePriorVersions { get; } + + public DateTime DateToRetain { get; } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletingNotification.cs new file mode 100644 index 0000000000..b821ac2d30 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletingNotification.cs @@ -0,0 +1,21 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class DeletingNotification : CancelableEnumerableObjectNotification + { + public DeletingNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public DeletingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable DeletedEntities => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletingVersionsNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletingVersionsNotification.cs new file mode 100644 index 0000000000..6a02ad402e --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletingVersionsNotification.cs @@ -0,0 +1,18 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class DeletingVersionsNotification : DeletedVersionsNotification, ICancelableNotification where T : class + { + public DeletingVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) + : base(id, messages, specificVersion, deletePriorVersions, dateToRetain) + { + } + + public bool Cancel { get; set; } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs new file mode 100644 index 0000000000..6f8b36cd12 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs @@ -0,0 +1,17 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class EmptiedRecycleBinNotification : INotification where T : class + { + public EmptiedRecycleBinNotification(EventMessages messages) + { + Messages = messages; + } + + public EventMessages Messages { get; } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs new file mode 100644 index 0000000000..e6b55fc79d --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs @@ -0,0 +1,17 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class EmptyingRecycleBinNotification : EmptiedRecycleBinNotification, ICancelableNotification where T : class + { + public EmptyingRecycleBinNotification(EventMessages messages) + : base(messages) + { + } + + public bool Cancel { get; set; } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/EnumerableObjectNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/EnumerableObjectNotification.cs new file mode 100644 index 0000000000..0201040ecf --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/EnumerableObjectNotification.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 abstract class EnumerableObjectNotification : ObjectNotification> + { + protected EnumerableObjectNotification(T target, EventMessages messages) : base(new [] {target}, messages) + { + } + + protected EnumerableObjectNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MovedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MovedNotification.cs new file mode 100644 index 0000000000..d0b934a1b5 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MovedNotification.cs @@ -0,0 +1,21 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class MovedNotification : ObjectNotification>> + { + public MovedNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) + { + } + + public MovedNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable> MoveInfoCollection => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MovedToRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MovedToRecycleBinNotification.cs new file mode 100644 index 0000000000..785484152a --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MovedToRecycleBinNotification.cs @@ -0,0 +1,21 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class MovedToRecycleBinNotification : ObjectNotification>> + { + public MovedToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) + { + } + + public MovedToRecycleBinNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable> MoveInfoCollection => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MovingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MovingNotification.cs new file mode 100644 index 0000000000..e3f567346e --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MovingNotification.cs @@ -0,0 +1,21 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class MovingNotification : CancelableObjectNotification>> + { + public MovingNotification(MoveEventInfo target, EventMessages messages) : base(new[] {target}, messages) + { + } + + public MovingNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable> MoveInfoCollection => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MovingToRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MovingToRecycleBinNotification.cs new file mode 100644 index 0000000000..9984136d74 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MovingToRecycleBinNotification.cs @@ -0,0 +1,21 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class MovingToRecycleBinNotification : CancelableObjectNotification>> + { + public MovingToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) + { + } + + public MovingToRecycleBinNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable> MoveInfoCollection => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ObjectNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ObjectNotification.cs new file mode 100644 index 0000000000..a6b84e875d --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ObjectNotification.cs @@ -0,0 +1,20 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public abstract class ObjectNotification : INotification where T : class + { + protected ObjectNotification(T target, EventMessages messages) + { + Messages = messages; + Target = target; + } + + public EventMessages Messages { get; } + + protected T Target { get; } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/PublishedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/PublishedNotification.cs new file mode 100644 index 0000000000..fcb4d5de34 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/PublishedNotification.cs @@ -0,0 +1,21 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class PublishedNotification : EnumerableObjectNotification + { + public PublishedNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public PublishedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable PublishedEntities => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/PublishingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/PublishingNotification.cs new file mode 100644 index 0000000000..60972e441d --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/PublishingNotification.cs @@ -0,0 +1,21 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class PublishingNotification : CancelableEnumerableObjectNotification + { + public PublishingNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public PublishingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable PublishedEntities => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/RolledBackNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/RolledBackNotification.cs new file mode 100644 index 0000000000..6960133e0c --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/RolledBackNotification.cs @@ -0,0 +1,16 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class RolledBackNotification : ObjectNotification where T : class + { + public RolledBackNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public T Entity => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/RollingBackNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/RollingBackNotification.cs new file mode 100644 index 0000000000..ec2542d415 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/RollingBackNotification.cs @@ -0,0 +1,16 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class RollingBackNotification : CancelableObjectNotification where T : class + { + public RollingBackNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public T Entity => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SavedBlueprintNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SavedBlueprintNotification.cs new file mode 100644 index 0000000000..5fde3145bc --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/SavedBlueprintNotification.cs @@ -0,0 +1,16 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class SavedBlueprintNotification : ObjectNotification where T : class + { + public SavedBlueprintNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public T SavedBlueprint => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SavedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SavedNotification.cs new file mode 100644 index 0000000000..6c1265c878 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/SavedNotification.cs @@ -0,0 +1,21 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class SavedNotification : EnumerableObjectNotification + { + public SavedNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public SavedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable SavedEntities => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SavingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SavingNotification.cs new file mode 100644 index 0000000000..3f1869a955 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/SavingNotification.cs @@ -0,0 +1,21 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class SavingNotification : CancelableEnumerableObjectNotification + { + public SavingNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public SavingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable SavedEntities => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SendingToPublishNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SendingToPublishNotification.cs new file mode 100644 index 0000000000..4d0b1d8782 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/SendingToPublishNotification.cs @@ -0,0 +1,16 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class SendingToPublishNotification : CancelableObjectNotification where T : class + { + public SendingToPublishNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public T Entity => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SentToPublishNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SentToPublishNotification.cs new file mode 100644 index 0000000000..d870bbb5db --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/SentToPublishNotification.cs @@ -0,0 +1,16 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class SentToPublishNotification : ObjectNotification where T : class + { + public SentToPublishNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public T Entity => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SortedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SortedNotification.cs new file mode 100644 index 0000000000..10e3c08e03 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/SortedNotification.cs @@ -0,0 +1,17 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class SortedNotification : EnumerableObjectNotification + { + public SortedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable SortedEntities => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SortingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SortingNotification.cs new file mode 100644 index 0000000000..18430b353b --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/SortingNotification.cs @@ -0,0 +1,17 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class SortingNotification : CancelableEnumerableObjectNotification + { + public SortingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable SortedEntities => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UnpublishedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UnpublishedNotification.cs new file mode 100644 index 0000000000..201d37ddfc --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/UnpublishedNotification.cs @@ -0,0 +1,21 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class UnpublishedNotification : EnumerableObjectNotification + { + public UnpublishedNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public UnpublishedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable UnpublishedEntities => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UnpublishingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UnpublishingNotification.cs new file mode 100644 index 0000000000..ac997fad4a --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/UnpublishingNotification.cs @@ -0,0 +1,21 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public class UnpublishingNotification : CancelableEnumerableObjectNotification + { + public UnpublishingNotification(T target, EventMessages messages) : base(target, messages) + { + } + + public UnpublishingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable UnpublishedEntities => Target; + } +} diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs index ee82e86671..8a093a0427 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs @@ -11,7 +11,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; using Umbraco.Cms.Core.Services.Implement; -using Umbraco.Cms.Infrastructure.Services; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs index 2321613bdc..612dbb87ae 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs @@ -21,7 +21,7 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; -using Umbraco.Cms.Infrastructure.Services; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Builders.Extensions; using Umbraco.Cms.Tests.Common.Extensions; diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs index c8f3bff84a..66d3c3488a 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs @@ -12,7 +12,7 @@ using Umbraco.Cms.Core.Exceptions; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; -using Umbraco.Cms.Infrastructure.Services; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs index 0d9ce8e5b8..45732beb83 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs @@ -11,7 +11,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; -using Umbraco.Cms.Infrastructure.Services; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index e35959648d..be6d1f6d86 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -17,13 +17,12 @@ using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Implement; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.PublishedCache; using Umbraco.Cms.Infrastructure.PublishedCache.Persistence; -using Umbraco.Cms.Infrastructure.Services; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Cms.Tests.Common; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Extensions; diff --git a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs index 50f4828efd..5731c71930 100644 --- a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs @@ -11,11 +11,9 @@ using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PublishedCache; -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.Services; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Cms.Infrastructure.Sync; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Extensions; From 22680a741cae9af4395e7e6c1634a8ffb615c11e Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 2 Mar 2021 19:11:39 +0100 Subject: [PATCH 11/42] Make integration tests run again + revert to weird logic in test --- .../Compose/RelateOnCopyHandler.cs | 51 +++++++++++++++++++ .../Services/Implement/ContentService.cs | 33 ++---------- .../ContentNotificationExtensions.cs | 30 +++++++++-- .../Notifications/CopiedNotification.cs | 4 +- .../ContentServiceNotificationTests.cs | 44 +++++++++------- 5 files changed, 108 insertions(+), 54 deletions(-) create mode 100644 src/Umbraco.Infrastructure/Compose/RelateOnCopyHandler.cs diff --git a/src/Umbraco.Infrastructure/Compose/RelateOnCopyHandler.cs b/src/Umbraco.Infrastructure/Compose/RelateOnCopyHandler.cs new file mode 100644 index 0000000000..a14b2e70b3 --- /dev/null +++ b/src/Umbraco.Infrastructure/Compose/RelateOnCopyHandler.cs @@ -0,0 +1,51 @@ +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.Services.Notifications; + +namespace Umbraco.Cms.Infrastructure.Compose +{ + // TODO: insert these notification handlers in core composition + public class RelateOnCopyHandler : INotificationHandler> + { + private readonly IRelationService _relationService; + private readonly IAuditService _auditService; + + public RelateOnCopyHandler(IRelationService relationService, IAuditService auditService) + { + _relationService = relationService; + _auditService = auditService; + } + + public void Handle(CopiedNotification notification) + { + if (notification.RelateToOriginal == false) + { + return; + } + + var relationType = _relationService.GetRelationTypeByAlias(Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias); + + if (relationType == null) + { + relationType = new RelationType(Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, + Constants.Conventions.RelationTypes.RelateDocumentOnCopyName, + true, + Constants.ObjectTypes.Document, + Constants.ObjectTypes.Document); + + _relationService.Save(relationType); + } + + var relation = new Relation(notification.Original.Id, notification.Copy.Id, relationType); + _relationService.Save(relation); + + _auditService.Add( + AuditType.Copy, + notification.Copy.WriterId, + notification.Copy.Id, ObjectTypes.GetName(UmbracoObjectTypes.Document), + $"Copied content with Id: '{notification.Copy.Id}' related to original content with Id: '{notification.Original.Id}'"); + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index 8928b4972d..da24ac4427 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -34,7 +34,6 @@ namespace Umbraco.Cms.Core.Services.Implement private readonly ILogger _logger; private IQuery _queryNotTrashed; private readonly IEventAggregator _eventAggregator; - private readonly IRelationService _relationService; #region Constructors @@ -42,7 +41,7 @@ namespace Umbraco.Cms.Core.Services.Implement IEventMessagesFactory eventMessagesFactory, IDocumentRepository documentRepository, IEntityRepository entityRepository, IAuditRepository auditRepository, IContentTypeRepository contentTypeRepository, IDocumentBlueprintRepository documentBlueprintRepository, ILanguageRepository languageRepository, - Lazy propertyValidationService, IShortStringHelper shortStringHelper, IEventAggregator eventAggregator, IRelationService relationService) + Lazy propertyValidationService, IShortStringHelper shortStringHelper, IEventAggregator eventAggregator) : base(provider, loggerFactory, eventMessagesFactory) { _documentRepository = documentRepository; @@ -54,7 +53,6 @@ namespace Umbraco.Cms.Core.Services.Implement _propertyValidationService = propertyValidationService; _shortStringHelper = shortStringHelper; _eventAggregator = eventAggregator; - _relationService = relationService; _logger = loggerFactory.CreateLogger(); } @@ -1300,7 +1298,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!branchOne) // for branches, handled by SaveAndPublishBranch { scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); - _eventAggregator.Publish(new PublishingNotification(content, evtMsgs)); + _eventAggregator.Publish(new PublishedNotification(content, evtMsgs)); } // it was not published and now is... descendants that were 'published' (but @@ -2252,12 +2250,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.Events.Dispatch(TreeChanged, this, new TreeChange(copy, TreeChangeTypes.RefreshBranch).ToEventArgs()); foreach (var x in copies) { - if (relateToOriginal) - { - RelateOnCopy(x.Item1, x.Item2); - } - - _eventAggregator.Publish(new CopiedNotification(x.Item1, x.Item2, parentId, evtMsgs)); + _eventAggregator.Publish(new CopiedNotification(x.Item1, x.Item2, parentId, relateToOriginal, evtMsgs)); } Audit(AuditType.Copy, userId, content.Id); @@ -2267,26 +2260,6 @@ namespace Umbraco.Cms.Core.Services.Implement return copy; } - private void RelateOnCopy(IContent original, IContent copy) - { - var relationType = _relationService.GetRelationTypeByAlias(Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias); - if (relationType == null) - { - relationType = new RelationType(Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, - Constants.Conventions.RelationTypes.RelateDocumentOnCopyName, - true, - Constants.ObjectTypes.Document, - Constants.ObjectTypes.Document); - - _relationService.Save(relationType); - } - - var relation = new Relation(original.Id, copy.Id, relationType); - _relationService.Save(relation); - - Audit(AuditType.Copy, copy.WriterId, copy.Id, $"Copied content with Id: '{copy.Id}' related to original content with Id: '{original.Id}'"); - } - /// /// Sends an to Publication, which executes handlers and events for the 'Send to Publication' action. /// diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentNotificationExtensions.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentNotificationExtensions.cs index 1a05c2f79d..046aa5b07b 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/ContentNotificationExtensions.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentNotificationExtensions.cs @@ -23,13 +23,19 @@ namespace Umbraco.Cms.Infrastructure.Services.Notifications /// Determines whether a culture is being published, during a Publishing notification /// public static bool IsPublishingCulture(this PublishingNotification notification, IContent content, string culture) - => content.PublishCultureInfos.TryGetValue(culture, out ContentCultureInfos cultureInfo) && cultureInfo.IsDirty(); + => IsPublishingCulture(content, culture); /// - /// Determines whether a culture is being unpublished, during a Publishing notification + /// Determines whether a culture is being unpublished, during an Publishing notification + /// + public static bool IsUnpublishingCulture(this PublishingNotification notification, IContent content, string culture) + => IsUnpublishingCulture(content, culture); + + /// + /// Determines whether a culture is being unpublished, during a Unpublishing notification /// public static bool IsUnpublishingCulture(this UnpublishingNotification notification, IContent content, string culture) - => content.IsPropertyDirty(ContentBase.ChangeTrackingPrefix.UnpublishedCulture + culture); //bit of a hack since we know that the content implementation tracks changes this way + => IsUnpublishingCulture(content, culture); /// /// Determines whether a culture has been published, during a Published notification @@ -40,8 +46,22 @@ namespace Umbraco.Cms.Infrastructure.Services.Notifications /// /// Determines whether a culture has been unpublished, during a Published notification /// - public static bool HasUnpublishedCulture(this UnpublishedNotification notification, IContent content, string culture) - => content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.UnpublishedCulture + culture); + public static bool HasUnpublishedCulture(this PublishedNotification notification, IContent content, string culture) + => HasUnpublishedCulture(content, culture); + /// + /// Determines whether a culture has been unpublished, during an Unpublished notification + /// + public static bool HasUnpublishedCulture(this UnpublishedNotification notification, IContent content, string culture) + => HasUnpublishedCulture(content, culture); + + private static bool IsUnpublishingCulture(IContent content, string culture) + => content.IsPropertyDirty(ContentBase.ChangeTrackingPrefix.UnpublishedCulture + culture); + + public static bool IsPublishingCulture(IContent content, string culture) + => content.PublishCultureInfos.TryGetValue(culture, out ContentCultureInfos cultureInfo) && cultureInfo.IsDirty(); + + public static bool HasUnpublishedCulture(IContent content, string culture) + => content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.UnpublishedCulture + culture); } } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs index f89487cbc0..f70780e1fa 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs @@ -7,10 +7,11 @@ namespace Umbraco.Cms.Infrastructure.Services.Notifications { public class CopiedNotification : ObjectNotification where T : class { - public CopiedNotification(T original, T copy, int parentId, EventMessages messages) : base(original, messages) + public CopiedNotification(T original, T copy, int parentId, bool relateToOriginal, EventMessages messages) : base(original, messages) { Copy = copy; ParentId = parentId; + RelateToOriginal = relateToOriginal; } public T Original => Target; @@ -18,5 +19,6 @@ namespace Umbraco.Cms.Infrastructure.Services.Notifications public T Copy { get; } public int ParentId { get; } + public bool RelateToOriginal { get; } } } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs index 8a093a0427..e872640ebe 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs @@ -367,43 +367,51 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services document.UnpublishCulture("fr-FR"); - var unpublishingWasCalled = false; - var unpublishedWasCalled = false; + var publishingWasCalled = false; + var publishedWasCalled = false; - ContentNotificationHandler.UnpublishingContent += notification => + // TODO: revisit this - it was migrated when removing static events, but the expected result seems illogic - why does this test bind to Published and not Unpublished? + + ContentNotificationHandler.PublishingContent += notification => { - IContent unpublished = notification.UnpublishedEntities.First(); + IContent published = notification.PublishedEntities.First(); - Assert.AreSame(document, unpublished); + Assert.AreSame(document, published); - Assert.IsFalse(notification.IsUnpublishingCulture(unpublished, "en-US")); - Assert.IsTrue(notification.IsUnpublishingCulture(unpublished, "fr-FR")); + Assert.IsFalse(notification.IsPublishingCulture(published, "en-US")); + Assert.IsFalse(notification.IsPublishingCulture(published, "fr-FR")); - unpublishingWasCalled = true; + Assert.IsFalse(notification.IsUnpublishingCulture(published, "en-US")); + Assert.IsTrue(notification.IsUnpublishingCulture(published, "fr-FR")); + + publishingWasCalled = true; }; - ContentNotificationHandler.UnpublishedContent += notification => + ContentNotificationHandler.PublishedContent += notification => { - IContent unpublished = notification.UnpublishedEntities.First(); + IContent published = notification.PublishedEntities.First(); - Assert.AreSame(document, unpublished); + Assert.AreSame(document, published); - Assert.IsFalse(notification.HasUnpublishedCulture(unpublished, "en-US")); - Assert.IsTrue(notification.HasUnpublishedCulture(unpublished, "fr-FR")); + Assert.IsFalse(notification.HasPublishedCulture(published, "en-US")); + Assert.IsFalse(notification.HasPublishedCulture(published, "fr-FR")); - unpublishedWasCalled = true; + Assert.IsFalse(notification.HasUnpublishedCulture(published, "en-US")); + Assert.IsTrue(notification.HasUnpublishedCulture(published, "fr-FR")); + + publishedWasCalled = true; }; try { contentService.CommitDocumentChanges(document); - Assert.IsTrue(unpublishingWasCalled); - Assert.IsTrue(unpublishedWasCalled); + Assert.IsTrue(publishingWasCalled); + Assert.IsTrue(publishedWasCalled); } finally { - ContentNotificationHandler.UnpublishingContent = null; - ContentNotificationHandler.UnpublishedContent = null; + ContentNotificationHandler.PublishingContent = null; + ContentNotificationHandler.PublishedContent = null; } document = contentService.GetById(document.Id); From b3ab0c15e346a55c41b2ecc8ea4173e9a0f6dd17 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 2 Mar 2021 19:50:40 +0100 Subject: [PATCH 12/42] Don't look for removed events in distributed cache event test --- .../Cache/DistributedCacheBinderTests.cs | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/Umbraco.Tests.Integration/Cache/DistributedCacheBinderTests.cs b/src/Umbraco.Tests.Integration/Cache/DistributedCacheBinderTests.cs index 73b5c5beea..9b8a1e9c98 100644 --- a/src/Umbraco.Tests.Integration/Cache/DistributedCacheBinderTests.cs +++ b/src/Umbraco.Tests.Integration/Cache/DistributedCacheBinderTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading; using NUnit.Framework; @@ -82,25 +82,10 @@ namespace Umbraco.Cms.Tests.Integration.Cache new EventDefinition>(null, MemberGroupService, new SaveEventArgs(Enumerable.Empty())), new EventDefinition>(null, MemberGroupService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, MediaService, new SaveEventArgs(Enumerable.Empty())), - new EventDefinition>(null, MediaService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, MediaService, new MoveEventArgs(new MoveEventInfo(null, "", -1)), "Moved"), - new EventDefinition>(null, MediaService, new MoveEventArgs(new MoveEventInfo(null, "", -1)), "Trashed"), - new EventDefinition(null, MediaService, new RecycleBinEventArgs(Guid.NewGuid())), - - new EventDefinition>(null, ContentService, new SaveEventArgs(Enumerable.Empty()), "Saved"), - new EventDefinition>(null, ContentService, new DeleteEventArgs(Enumerable.Empty()), "Deleted"), - // not managed //new EventDefinition>(null, ContentService, new SaveEventArgs(Enumerable.Empty()), "SavedBlueprint"), //new EventDefinition>(null, ContentService, new DeleteEventArgs(Enumerable.Empty()), "DeletedBlueprint"), - new EventDefinition>(null, ContentService, new CopyEventArgs(null, null, -1)), - new EventDefinition>(null, ContentService, new MoveEventArgs(new MoveEventInfo(null, "", -1)), "Trashed"), - new EventDefinition(null, ContentService, new RecycleBinEventArgs(Guid.NewGuid())), - new EventDefinition>(null, ContentService, new PublishEventArgs(Enumerable.Empty()), "Published"), - new EventDefinition>(null, ContentService, new PublishEventArgs(Enumerable.Empty()), "Unpublished"), - new EventDefinition>(null, PublicAccessService, new SaveEventArgs(Enumerable.Empty())), new EventDefinition>(null, PublicAccessService, new DeleteEventArgs(Enumerable.Empty())), From f8db15dcc7fe8b7b2218474df5b4cffa9a54e52b Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 3 Mar 2021 09:02:29 +0100 Subject: [PATCH 13/42] Wire up notification handlers in a composer --- .../Compose/NotificationsComposer.cs | 49 ++++++++++++++++++- .../Compose/RelateOnCopyHandler.cs | 5 +- .../Compose/RelateOnTrashHandler.cs | 2 +- ...Handler.cs => UserNotificationsHandler.cs} | 6 +-- .../BlockEditorPropertyHandler.cs | 1 - ...ropertyEditorContentNotificationHandler.cs | 1 - .../FileUploadPropertyEditor.cs | 1 - .../ImageCropperPropertyEditor.cs | 1 - .../NestedContentPropertyHandler.cs | 1 - .../Routing/RedirectTrackingHandler.cs | 1 - 10 files changed, 54 insertions(+), 14 deletions(-) rename src/Umbraco.Infrastructure/Compose/{NotificationsHandler.cs => UserNotificationsHandler.cs} (97%) diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs index 21e473bb87..d5328929b2 100644 --- a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs +++ b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs @@ -1,5 +1,9 @@ -using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Compose @@ -11,6 +15,49 @@ namespace Umbraco.Cms.Core.Compose base.Compose(builder); builder.Services.AddUnique(); + + // add handlers for sending user notifications (i.e. emails) + builder.Services.AddUnique(); + builder + .AddNotificationHandler, UserNotificationsHandler>() + .AddNotificationHandler, UserNotificationsHandler>() + .AddNotificationHandler, UserNotificationsHandler>() + .AddNotificationHandler, UserNotificationsHandler>() + .AddNotificationHandler, UserNotificationsHandler>() + .AddNotificationHandler, UserNotificationsHandler>() + .AddNotificationHandler, UserNotificationsHandler>() + .AddNotificationHandler, UserNotificationsHandler>() + .AddNotificationHandler, UserNotificationsHandler>(); + + // add handlers for building content relations + builder + .AddNotificationHandler, RelateOnCopyHandler>() + .AddNotificationHandler, RelateOnTrashHandler>() + .AddNotificationHandler, RelateOnTrashHandler>() + .AddNotificationHandler, RelateOnTrashHandler>() + .AddNotificationHandler, RelateOnTrashHandler>(); + + // add notification handlers for property editors + builder + .AddNotificationHandler, BlockEditorPropertyHandler>() + .AddNotificationHandler, BlockEditorPropertyHandler>() + .AddNotificationHandler, NestedContentPropertyHandler>() + .AddNotificationHandler, NestedContentPropertyHandler>() + .AddNotificationHandler, FileUploadPropertyEditor>() + .AddNotificationHandler, FileUploadPropertyEditor>() + .AddNotificationHandler, FileUploadPropertyEditor>() + .AddNotificationHandler, FileUploadPropertyEditor>() + .AddNotificationHandler, ImageCropperPropertyEditor>() + .AddNotificationHandler, ImageCropperPropertyEditor>() + .AddNotificationHandler, ImageCropperPropertyEditor>() + .AddNotificationHandler, ImageCropperPropertyEditor>(); + + // add notification handlers for redirect tracking + builder + .AddNotificationHandler, RedirectTrackingHandler>() + .AddNotificationHandler, RedirectTrackingHandler>() + .AddNotificationHandler, RedirectTrackingHandler>() + .AddNotificationHandler, RedirectTrackingHandler>(); } } } diff --git a/src/Umbraco.Infrastructure/Compose/RelateOnCopyHandler.cs b/src/Umbraco.Infrastructure/Compose/RelateOnCopyHandler.cs index a14b2e70b3..ff5b9f732a 100644 --- a/src/Umbraco.Infrastructure/Compose/RelateOnCopyHandler.cs +++ b/src/Umbraco.Infrastructure/Compose/RelateOnCopyHandler.cs @@ -1,12 +1,11 @@ -using Umbraco.Cms.Core; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Services.Notifications; -namespace Umbraco.Cms.Infrastructure.Compose +namespace Umbraco.Cms.Core.Compose { - // TODO: insert these notification handlers in core composition + // TODO: this should probably be moved to another namespace public class RelateOnCopyHandler : INotificationHandler> { private readonly IRelationService _relationService; diff --git a/src/Umbraco.Infrastructure/Compose/RelateOnTrashHandler.cs b/src/Umbraco.Infrastructure/Compose/RelateOnTrashHandler.cs index e1fcdb3bea..3157594b34 100644 --- a/src/Umbraco.Infrastructure/Compose/RelateOnTrashHandler.cs +++ b/src/Umbraco.Infrastructure/Compose/RelateOnTrashHandler.cs @@ -8,7 +8,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Compose { - // TODO: insert these notification handlers in core composition + // TODO: this should probably be moved to another namespace // TODO: lots of duplicate code in this one, refactor public sealed class RelateOnTrashHandler : INotificationHandler>, diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs b/src/Umbraco.Infrastructure/Compose/UserNotificationsHandler.cs similarity index 97% rename from src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs rename to src/Umbraco.Infrastructure/Compose/UserNotificationsHandler.cs index 205aece4d1..5c80f94806 100644 --- a/src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs +++ b/src/Umbraco.Infrastructure/Compose/UserNotificationsHandler.cs @@ -21,8 +21,8 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Compose { - // TODO: insert these notification handlers in core composition - public sealed class NotificationsHandler : + // TODO: this should probably be moved to another namespace + public sealed class UserNotificationsHandler : INotificationHandler>, INotificationHandler>, INotificationHandler>, @@ -37,7 +37,7 @@ namespace Umbraco.Cms.Core.Compose private readonly ActionCollection _actions; private readonly IContentService _contentService; - public NotificationsHandler(Notifier notifier, ActionCollection actions, IContentService contentService) + public UserNotificationsHandler(Notifier notifier, ActionCollection actions, IContentService contentService) { _notifier = notifier; _actions = actions; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyHandler.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyHandler.cs index 5ce37d7b75..e08ad47682 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyHandler.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyHandler.cs @@ -11,7 +11,6 @@ namespace Umbraco.Cms.Core.PropertyEditors /// /// A handler for Block editors used to bind to notifications /// - // TODO: insert these notification handlers in core composition public class BlockEditorPropertyHandler : ComplexPropertyEditorContentNotificationHandler { private readonly BlockListEditorDataConverter _converter = new BlockListEditorDataConverter(); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs index 29d23c1c33..c2d9ab20be 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs @@ -9,7 +9,6 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors { - // TODO: insert these notification handlers in core composition public abstract class ComplexPropertyEditorContentNotificationHandler : INotificationHandler>, INotificationHandler> diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs index 08308af246..6ad2d4c4ad 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs @@ -25,7 +25,6 @@ namespace Umbraco.Cms.Core.PropertyEditors "fileupload", Group = Constants.PropertyEditors.Groups.Media, Icon = "icon-download-alt")] - // TODO: insert these notification handlers in core composition public class FileUploadPropertyEditor : DataEditor, IMediaUrlGenerator, INotificationHandler>, INotificationHandler>, INotificationHandler>, INotificationHandler> diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs index 8c618c73e4..8df80b6775 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs @@ -32,7 +32,6 @@ namespace Umbraco.Cms.Core.PropertyEditors HideLabel = false, Group = Constants.PropertyEditors.Groups.Media, Icon = "icon-crop")] - // TODO: insert these notification handlers in core composition public class ImageCropperPropertyEditor : DataEditor, IMediaUrlGenerator, INotificationHandler>, INotificationHandler>, INotificationHandler>, INotificationHandler> diff --git a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyHandler.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyHandler.cs index 887c547778..f9944d12b2 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyHandler.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyHandler.cs @@ -8,7 +8,6 @@ namespace Umbraco.Cms.Core.PropertyEditors /// /// A handler for NestedContent used to bind to notifications /// - // TODO: insert these notification handlers in core composition public class NestedContentPropertyHandler : ComplexPropertyEditorContentNotificationHandler { protected override string EditorAlias => Constants.PropertyEditors.Aliases.NestedContent; diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs index 2ff847d32f..6d8b9cccc7 100644 --- a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs @@ -20,7 +20,6 @@ namespace Umbraco.Cms.Core.Routing /// strategy using rewriting rules probably /// /// recycle bin = moving to and from does nothing: to = the node is gone, where would we redirect? from = same - // TODO: insert these notification handlers in core composition public sealed class RedirectTrackingHandler : INotificationHandler>, INotificationHandler>, From a6b4115675e40a6e4145abbff2ad93f7c6359759 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 3 Mar 2021 09:15:52 +0100 Subject: [PATCH 14/42] Moved notification handlers out of the Compose namespace --- src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs | 1 + .../{Compose => Events}/RelateOnCopyHandler.cs | 4 +--- .../{Compose => Events}/RelateOnTrashHandler.cs | 4 +--- .../{Compose => Events}/UserNotificationsHandler.cs | 4 +--- 4 files changed, 4 insertions(+), 9 deletions(-) rename src/Umbraco.Infrastructure/{Compose => Events}/RelateOnCopyHandler.cs (93%) rename src/Umbraco.Infrastructure/{Compose => Events}/RelateOnTrashHandler.cs (98%) rename src/Umbraco.Infrastructure/{Compose => Events}/UserNotificationsHandler.cs (98%) diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs index d5328929b2..74300936b0 100644 --- a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs +++ b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs @@ -1,5 +1,6 @@ using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Routing; diff --git a/src/Umbraco.Infrastructure/Compose/RelateOnCopyHandler.cs b/src/Umbraco.Infrastructure/Events/RelateOnCopyHandler.cs similarity index 93% rename from src/Umbraco.Infrastructure/Compose/RelateOnCopyHandler.cs rename to src/Umbraco.Infrastructure/Events/RelateOnCopyHandler.cs index ff5b9f732a..c438679813 100644 --- a/src/Umbraco.Infrastructure/Compose/RelateOnCopyHandler.cs +++ b/src/Umbraco.Infrastructure/Events/RelateOnCopyHandler.cs @@ -1,11 +1,9 @@ -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Services.Notifications; -namespace Umbraco.Cms.Core.Compose +namespace Umbraco.Cms.Core.Events { - // TODO: this should probably be moved to another namespace public class RelateOnCopyHandler : INotificationHandler> { private readonly IRelationService _relationService; diff --git a/src/Umbraco.Infrastructure/Compose/RelateOnTrashHandler.cs b/src/Umbraco.Infrastructure/Events/RelateOnTrashHandler.cs similarity index 98% rename from src/Umbraco.Infrastructure/Compose/RelateOnTrashHandler.cs rename to src/Umbraco.Infrastructure/Events/RelateOnTrashHandler.cs index 3157594b34..7c122dd09e 100644 --- a/src/Umbraco.Infrastructure/Compose/RelateOnTrashHandler.cs +++ b/src/Umbraco.Infrastructure/Events/RelateOnTrashHandler.cs @@ -1,14 +1,12 @@ using System.Linq; -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; -namespace Umbraco.Cms.Core.Compose +namespace Umbraco.Cms.Core.Events { - // TODO: this should probably be moved to another namespace // TODO: lots of duplicate code in this one, refactor public sealed class RelateOnTrashHandler : INotificationHandler>, diff --git a/src/Umbraco.Infrastructure/Compose/UserNotificationsHandler.cs b/src/Umbraco.Infrastructure/Events/UserNotificationsHandler.cs similarity index 98% rename from src/Umbraco.Infrastructure/Compose/UserNotificationsHandler.cs rename to src/Umbraco.Infrastructure/Events/UserNotificationsHandler.cs index 5c80f94806..a099a66d8e 100644 --- a/src/Umbraco.Infrastructure/Compose/UserNotificationsHandler.cs +++ b/src/Umbraco.Infrastructure/Events/UserNotificationsHandler.cs @@ -9,7 +9,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Actions; 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.Entities; @@ -19,9 +18,8 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; -namespace Umbraco.Cms.Core.Compose +namespace Umbraco.Cms.Core.Events { - // TODO: this should probably be moved to another namespace public sealed class UserNotificationsHandler : INotificationHandler>, INotificationHandler>, From 60241f5f51dd9ca357457bcbf6bf978bd224caad Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 3 Mar 2021 09:16:48 +0100 Subject: [PATCH 15/42] A little renaming for consistency --- .../Compose/NotificationsComposer.cs | 10 +++++----- ...opyHandler.cs => RelateOnCopyNotifcationHandler.cs} | 4 ++-- ...hHandler.cs => RelateOnTrashNotificationHandler.cs} | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) rename src/Umbraco.Infrastructure/Events/{RelateOnCopyHandler.cs => RelateOnCopyNotifcationHandler.cs} (88%) rename src/Umbraco.Infrastructure/Events/{RelateOnTrashHandler.cs => RelateOnTrashNotificationHandler.cs} (98%) diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs index 74300936b0..f426d9b749 100644 --- a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs +++ b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs @@ -32,11 +32,11 @@ namespace Umbraco.Cms.Core.Compose // add handlers for building content relations builder - .AddNotificationHandler, RelateOnCopyHandler>() - .AddNotificationHandler, RelateOnTrashHandler>() - .AddNotificationHandler, RelateOnTrashHandler>() - .AddNotificationHandler, RelateOnTrashHandler>() - .AddNotificationHandler, RelateOnTrashHandler>(); + .AddNotificationHandler, RelateOnCopyNotifcationHandler>() + .AddNotificationHandler, RelateOnTrashNotificationHandler>() + .AddNotificationHandler, RelateOnTrashNotificationHandler>() + .AddNotificationHandler, RelateOnTrashNotificationHandler>() + .AddNotificationHandler, RelateOnTrashNotificationHandler>(); // add notification handlers for property editors builder diff --git a/src/Umbraco.Infrastructure/Events/RelateOnCopyHandler.cs b/src/Umbraco.Infrastructure/Events/RelateOnCopyNotifcationHandler.cs similarity index 88% rename from src/Umbraco.Infrastructure/Events/RelateOnCopyHandler.cs rename to src/Umbraco.Infrastructure/Events/RelateOnCopyNotifcationHandler.cs index c438679813..0de73070f1 100644 --- a/src/Umbraco.Infrastructure/Events/RelateOnCopyHandler.cs +++ b/src/Umbraco.Infrastructure/Events/RelateOnCopyNotifcationHandler.cs @@ -4,12 +4,12 @@ using Umbraco.Cms.Infrastructure.Services.Notifications; namespace Umbraco.Cms.Core.Events { - public class RelateOnCopyHandler : INotificationHandler> + public class RelateOnCopyNotifcationHandler : INotificationHandler> { private readonly IRelationService _relationService; private readonly IAuditService _auditService; - public RelateOnCopyHandler(IRelationService relationService, IAuditService auditService) + public RelateOnCopyNotifcationHandler(IRelationService relationService, IAuditService auditService) { _relationService = relationService; _auditService = auditService; diff --git a/src/Umbraco.Infrastructure/Events/RelateOnTrashHandler.cs b/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs similarity index 98% rename from src/Umbraco.Infrastructure/Events/RelateOnTrashHandler.cs rename to src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs index 7c122dd09e..be421b9bbe 100644 --- a/src/Umbraco.Infrastructure/Events/RelateOnTrashHandler.cs +++ b/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs @@ -8,7 +8,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Events { // TODO: lots of duplicate code in this one, refactor - public sealed class RelateOnTrashHandler : + public sealed class RelateOnTrashNotificationHandler : INotificationHandler>, INotificationHandler>, INotificationHandler>, @@ -20,7 +20,7 @@ namespace Umbraco.Cms.Core.Events private readonly IAuditService _auditService; private readonly IScopeProvider _scopeProvider; - public RelateOnTrashHandler( + public RelateOnTrashNotificationHandler( IRelationService relationService, IEntityService entityService, ILocalizedTextService textService, From 6c942f26c834ff4af50114eba4086ff392cd0414 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 3 Mar 2021 10:11:00 +0100 Subject: [PATCH 16/42] Let the file upload and image cropper editors clean up files themselves --- .../FileUploadPropertyEditor.cs | 29 +++++++++++------- .../ImageCropperPropertyEditor.cs | 30 +++++++++++-------- .../PropertyEditorsComponent.cs | 5 ++-- .../Notifications/DeletedNotification.cs | 6 ++-- 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs index 6ad2d4c4ad..a43531bb83 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs @@ -92,16 +92,17 @@ namespace Umbraco.Cms.Core.PropertyEditors } /// - /// Ensures any files associated are removed + /// The paths to all file upload property files contained within a collection of content entities /// - /// - internal IEnumerable ServiceDeleted(IEnumerable deletedEntities) - { - return deletedEntities.SelectMany(x => x.Properties) - .Where(IsUploadField) - .SelectMany(GetFilePathsFromPropertyValues) - .Distinct(); - } + /// + /// + /// This method must be made private once MemberService events have been replaced by notifications + /// + internal IEnumerable ContainedFilePaths(IEnumerable entities) => entities + .SelectMany(x => x.Properties) + .Where(IsUploadField) + .SelectMany(GetFilePathsFromPropertyValues) + .Distinct(); /// /// Look through all property values stored against the property and resolve any file paths stored @@ -157,9 +158,15 @@ namespace Umbraco.Cms.Core.PropertyEditors } } - public void Handle(DeletedNotification notification) => notification.MediaFilesToDelete.AddRange(ServiceDeleted(notification.DeletedEntities.OfType())); + public void Handle(DeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); - public void Handle(DeletedNotification notification) => notification.MediaFilesToDelete.AddRange(ServiceDeleted(notification.DeletedEntities.OfType())); + public void Handle(DeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); + + private void DeleteContainedFiles(IEnumerable deletedEntities) + { + var filePathsToDelete = ContainedFilePaths(deletedEntities); + _mediaFileSystem.DeleteMediaFiles(filePathsToDelete); + } public void Handle(SavingNotification notification) { diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs index 8df80b6775..d4c0c94054 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs @@ -128,16 +128,17 @@ namespace Umbraco.Cms.Core.PropertyEditors } /// - /// Ensures any files associated are removed + /// The paths to all image cropper property files contained within a collection of content entities /// - /// - internal IEnumerable ServiceDeleted(IEnumerable deletedEntities) - { - return deletedEntities.SelectMany(x => x.Properties) - .Where(IsCropperField) - .SelectMany(GetFilePathsFromPropertyValues) - .Distinct(); - } + /// + /// + /// This method must be made private once MemberService events have been replaced by notifications + /// + internal IEnumerable ContainedFilePaths(IEnumerable entities) => entities + .SelectMany(x => x.Properties) + .Where(IsCropperField) + .SelectMany(GetFilePathsFromPropertyValues) + .Distinct(); /// /// Look through all property values stored against the property and resolve any file paths stored @@ -211,12 +212,17 @@ namespace Umbraco.Cms.Core.PropertyEditors { _contentService.Save(notification.Copy); } - } - public void Handle(DeletedNotification notification) => notification.MediaFilesToDelete.AddRange(ServiceDeleted(notification.DeletedEntities.OfType())); + public void Handle(DeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); - public void Handle(DeletedNotification notification) => notification.MediaFilesToDelete.AddRange(ServiceDeleted(notification.DeletedEntities.OfType())); + public void Handle(DeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); + + private void DeleteContainedFiles(IEnumerable deletedEntities) + { + var filePathsToDelete = ContainedFilePaths(deletedEntities); + _mediaFileSystem.DeleteMediaFiles(filePathsToDelete); + } public void Handle(SavingNotification notification) { diff --git a/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs b/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs index 37407bd856..b9e9e33889 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/PropertyEditorsComponent.cs @@ -12,6 +12,7 @@ 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; @@ -40,14 +41,14 @@ namespace Umbraco.Cms.Core.PropertyEditors private void Initialize(FileUploadPropertyEditor fileUpload) { - void memberServiceDeleted(IMemberService sender, DeleteEventArgs args) => args.MediaFilesToDelete.AddRange(fileUpload.ServiceDeleted(args.DeletedEntities.Cast())); + 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.ServiceDeleted(args.DeletedEntities.Cast())); + 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/Services/Notifications/DeletedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletedNotification.cs index f6e28f74ed..17d165109c 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/DeletedNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletedNotification.cs @@ -8,10 +8,10 @@ namespace Umbraco.Cms.Infrastructure.Services.Notifications { public class DeletedNotification : EnumerableObjectNotification { - public DeletedNotification(T target, EventMessages messages) : base(target, messages) => MediaFilesToDelete = new List(); + public DeletedNotification(T target, EventMessages messages) : base(target, messages) + { + } public IEnumerable DeletedEntities => Target; - - public List MediaFilesToDelete { get; } } } From 134a92e4d9dfd584a4fc100e86e1210a2f58d4f7 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 3 Mar 2021 12:02:29 +0100 Subject: [PATCH 17/42] Use request cache to preserve state between events for redirect tracking --- .../Routing/RedirectTrackingHandler.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs index 6d8b9cccc7..dfcbc0f364 100644 --- a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; @@ -30,13 +31,17 @@ namespace Umbraco.Cms.Core.Routing private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly IRedirectUrlService _redirectUrlService; private readonly IVariationContextAccessor _variationContextAccessor; + private readonly IRequestCache _requestCache; - public RedirectTrackingHandler(IOptionsMonitor webRoutingSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService, IVariationContextAccessor variationContextAccessor) + private const string NotificationStateKey = "Umbraco.Cms.Core.Routing.RedirectTrackingHandler"; + + public RedirectTrackingHandler(IOptionsMonitor webRoutingSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService, IVariationContextAccessor variationContextAccessor, IRequestCache requestCache) { _webRoutingSettings = webRoutingSettings; _publishedSnapshotAccessor = publishedSnapshotAccessor; _redirectUrlService = redirectUrlService; _variationContextAccessor = variationContextAccessor; + _requestCache = requestCache; } public void Handle(PublishingNotification notification) @@ -86,8 +91,7 @@ namespace Umbraco.Cms.Core.Routing CreateRedirects(oldRoutes); } - // TODO: figure out how to do notification state / temp state - private OldRoutesDictionary GetOldRoutes() => new OldRoutesDictionary(); + private OldRoutesDictionary GetOldRoutes() => (OldRoutesDictionary)_requestCache.Get(NotificationStateKey, () => new OldRoutesDictionary()); private void StoreOldRoute(IContent entity, OldRoutesDictionary oldRoutes) { From 1f9a46f6b3192f25e647448ae5de7189bab8ad7e Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 3 Mar 2021 17:08:21 +0100 Subject: [PATCH 18/42] Create scoped notification publishing - particularly for "ed" notifications that must run after the current scope has completed --- .../Events/IScopedNotificationPublisher.cs | 34 ++++ .../Events/ScopedNotificationPublisher.cs | 68 +++++++ src/Umbraco.Infrastructure/Scoping/IScope.cs | 7 +- src/Umbraco.Infrastructure/Scoping/Scope.cs | 25 ++- .../Scoping/ScopeProvider.cs | 10 +- .../Services/Implement/ContentService.cs | 172 ++++++------------ .../Services/Implement/MediaService.cs | 95 +++------- .../Umbraco.Core/Components/ComponentTests.cs | 4 +- .../Scoping/ScopeEventDispatcherTests.cs | 3 +- 9 files changed, 229 insertions(+), 189 deletions(-) create mode 100644 src/Umbraco.Core/Events/IScopedNotificationPublisher.cs create mode 100644 src/Umbraco.Core/Events/ScopedNotificationPublisher.cs diff --git a/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs new file mode 100644 index 0000000000..e6c8a06647 --- /dev/null +++ b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; + +namespace Umbraco.Cms.Core.Events +{ + public interface IScopedNotificationPublisher + { + /// + /// Publishes a cancelable notification to the notification subscribers + /// + /// + /// True if the notification was cancelled by a subscriber, false otherwise + bool PublishCancelable(ICancelableNotification notification); + + /// + /// Publishes a cancelable notification to the notification subscribers + /// + /// + /// True if the notification was cancelled by a subscriber, false otherwise + Task PublishCancelableAsync(ICancelableNotification notification); + + /// + /// Publishes a notification to the notification subscribers + /// + /// + /// The notification is published upon successful completion of the current scope, i.e. when things have been saved/published/deleted etc. + void Publish(INotification notification); + + /// + /// Invokes publishing of all pending notifications within the current scope + /// + /// + void ScopeExit(bool completed); + } +} diff --git a/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs new file mode 100644 index 0000000000..dfaca55663 --- /dev/null +++ b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Umbraco.Cms.Core.Events +{ + public class ScopedNotificationPublisher : IScopedNotificationPublisher + { + private readonly IEventAggregator _eventAggregator; + private readonly List _notificationOnScopeCompleted; + + public ScopedNotificationPublisher(IEventAggregator eventAggregator) + { + _eventAggregator = eventAggregator; + _notificationOnScopeCompleted = new List(); + } + + public bool PublishCancelable(ICancelableNotification notification) + { + if (notification == null) + { + throw new ArgumentNullException(nameof(notification)); + } + + _eventAggregator.Publish(notification); + return notification.Cancel; + } + + public async Task PublishCancelableAsync(ICancelableNotification notification) + { + if (notification == null) + { + throw new ArgumentNullException(nameof(notification)); + } + + await _eventAggregator.PublishAsync(notification); + return notification.Cancel; + } + + public void Publish(INotification notification) + { + if (notification == null) + { + throw new ArgumentNullException(nameof(notification)); + } + + _notificationOnScopeCompleted.Add(notification); + } + + public void ScopeExit(bool completed) + { + try + { + if (completed) + { + foreach (var notification in _notificationOnScopeCompleted) + { + _eventAggregator.Publish(notification); + } + } + } + finally + { + _notificationOnScopeCompleted.Clear(); + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Scoping/IScope.cs b/src/Umbraco.Infrastructure/Scoping/IScope.cs index 7a6a62a6c7..ab1d905b44 100644 --- a/src/Umbraco.Infrastructure/Scoping/IScope.cs +++ b/src/Umbraco.Infrastructure/Scoping/IScope.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Infrastructure.Persistence; @@ -30,6 +30,11 @@ namespace Umbraco.Cms.Core.Scoping /// IEventDispatcher Events { get; } + /// + /// Gets the scope notification publisher + /// + IScopedNotificationPublisher Notifications { get; } + /// /// Gets the repositories cache mode. /// diff --git a/src/Umbraco.Infrastructure/Scoping/Scope.cs b/src/Umbraco.Infrastructure/Scoping/Scope.cs index 7d50f5e55a..a374f8726b 100644 --- a/src/Umbraco.Infrastructure/Scoping/Scope.cs +++ b/src/Umbraco.Infrastructure/Scoping/Scope.cs @@ -19,6 +19,7 @@ namespace Umbraco.Cms.Core.Scoping private readonly ScopeProvider _scopeProvider; private readonly CoreDebugSettings _coreDebugSettings; private readonly IMediaFileSystem _mediaFileSystem; + private readonly IEventAggregator _eventAggregator; private readonly ILogger _logger; private readonly IsolationLevel _isolationLevel; @@ -35,12 +36,15 @@ namespace Umbraco.Cms.Core.Scoping private EventMessages _messages; private ICompletable _fscope; private IEventDispatcher _eventDispatcher; + // eventually this may need to be injectable - for now we'll create it explicitly and let future needs determine if it should be injectable + private IScopedNotificationPublisher _notificationPublisher; // initializes a new scope private Scope( ScopeProvider scopeProvider, CoreDebugSettings coreDebugSettings, IMediaFileSystem mediaFileSystem, + IEventAggregator eventAggregator, ILogger logger, FileSystems fileSystems, Scope parent, @@ -56,6 +60,7 @@ namespace Umbraco.Cms.Core.Scoping _scopeProvider = scopeProvider; _coreDebugSettings = coreDebugSettings; _mediaFileSystem = mediaFileSystem; + _eventAggregator = eventAggregator; _logger = logger; Context = scopeContext; @@ -69,6 +74,7 @@ namespace Umbraco.Cms.Core.Scoping Detachable = detachable; + #if DEBUG_SCOPES _scopeProvider.RegisterScope(this); Console.WriteLine("create " + InstanceId.ToString("N").Substring(0, 8)); @@ -124,6 +130,7 @@ namespace Umbraco.Cms.Core.Scoping ScopeProvider scopeProvider, CoreDebugSettings coreDebugSettings, IMediaFileSystem mediaFileSystem, + IEventAggregator eventAggregator, ILogger logger, FileSystems fileSystems, bool detachable, @@ -134,7 +141,7 @@ namespace Umbraco.Cms.Core.Scoping bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) - : this(scopeProvider, coreDebugSettings, mediaFileSystem, logger, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) + : this(scopeProvider, coreDebugSettings, mediaFileSystem, eventAggregator, logger, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) { } // initializes a new scope in a nested scopes chain, with its parent @@ -142,6 +149,7 @@ namespace Umbraco.Cms.Core.Scoping ScopeProvider scopeProvider, CoreDebugSettings coreDebugSettings, IMediaFileSystem mediaFileSystem, + IEventAggregator eventAggregator, ILogger logger, FileSystems fileSystems, Scope parent, @@ -151,7 +159,7 @@ namespace Umbraco.Cms.Core.Scoping bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) - : this(scopeProvider, coreDebugSettings, mediaFileSystem, logger, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) + : this(scopeProvider, coreDebugSettings, mediaFileSystem, eventAggregator, logger, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) { } public Guid InstanceId { get; } = Guid.NewGuid(); @@ -314,6 +322,16 @@ namespace Umbraco.Cms.Core.Scoping } } + public IScopedNotificationPublisher Notifications + { + get + { + EnsureNotDisposed(); + if (ParentScope != null) return ParentScope.Notifications; + return _notificationPublisher ?? (_notificationPublisher = new ScopedNotificationPublisher(_eventAggregator)); + } + } + /// public bool Complete() { @@ -453,7 +471,10 @@ namespace Umbraco.Cms.Core.Scoping { // deal with events if (onException == false) + { _eventDispatcher?.ScopeExit(completed); + _notificationPublisher?.ScopeExit(completed); + } }, () => { // if *we* created it, then get rid of it diff --git a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs index b0b1868a0d..392028166d 100644 --- a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs @@ -26,8 +26,9 @@ namespace Umbraco.Cms.Core.Scoping private readonly FileSystems _fileSystems; private readonly CoreDebugSettings _coreDebugSettings; private readonly IMediaFileSystem _mediaFileSystem; + private readonly IEventAggregator _eventAggregator; - public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, IOptions coreDebugSettings, IMediaFileSystem mediaFileSystem, ILogger logger, ILoggerFactory loggerFactory, IRequestCache requestCache) + public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, IOptions coreDebugSettings, IMediaFileSystem mediaFileSystem, ILogger logger, ILoggerFactory loggerFactory, IRequestCache requestCache, IEventAggregator eventAggregator) { DatabaseFactory = databaseFactory; _fileSystems = fileSystems; @@ -36,6 +37,7 @@ namespace Umbraco.Cms.Core.Scoping _logger = logger; _loggerFactory = loggerFactory; _requestCache = requestCache; + _eventAggregator = eventAggregator; // take control of the FileSystems _fileSystems.IsScoped = () => AmbientScope != null && AmbientScope.ScopedFileSystems; @@ -253,7 +255,7 @@ namespace Umbraco.Cms.Core.Scoping IEventDispatcher eventDispatcher = null, bool? scopeFileSystems = null) { - return new Scope(this, _coreDebugSettings, _mediaFileSystem, _loggerFactory.CreateLogger(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems); + return new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems); } /// @@ -309,13 +311,13 @@ namespace Umbraco.Cms.Core.Scoping { var ambientContext = AmbientContext; var newContext = ambientContext == null ? new ScopeContext() : null; - var scope = new Scope(this, _coreDebugSettings, _mediaFileSystem, _loggerFactory.CreateLogger(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); + var scope = new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); // assign only if scope creation did not throw! SetAmbient(scope, newContext ?? ambientContext); return scope; } - var nested = new Scope(this, _coreDebugSettings, _mediaFileSystem, _loggerFactory.CreateLogger(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); + var nested = new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); SetAmbient(nested, AmbientContext); return nested; } diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index da24ac4427..dbd6cce1c1 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -33,7 +33,6 @@ namespace Umbraco.Cms.Core.Services.Implement private readonly IShortStringHelper _shortStringHelper; private readonly ILogger _logger; private IQuery _queryNotTrashed; - private readonly IEventAggregator _eventAggregator; #region Constructors @@ -41,7 +40,7 @@ namespace Umbraco.Cms.Core.Services.Implement IEventMessagesFactory eventMessagesFactory, IDocumentRepository documentRepository, IEntityRepository entityRepository, IAuditRepository auditRepository, IContentTypeRepository contentTypeRepository, IDocumentBlueprintRepository documentBlueprintRepository, ILanguageRepository languageRepository, - Lazy propertyValidationService, IShortStringHelper shortStringHelper, IEventAggregator eventAggregator) + Lazy propertyValidationService, IShortStringHelper shortStringHelper) : base(provider, loggerFactory, eventMessagesFactory) { _documentRepository = documentRepository; @@ -52,7 +51,6 @@ namespace Umbraco.Cms.Core.Services.Implement _languageRepository = languageRepository; _propertyValidationService = propertyValidationService; _shortStringHelper = shortStringHelper; - _eventAggregator = eventAggregator; _logger = loggerFactory.CreateLogger(); } @@ -750,15 +748,10 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (raiseEvents) + if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(content, evtMsgs))) { - var notification = new SavingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) - { - scope.Complete(); - return OperationResult.Cancel(evtMsgs); - } + scope.Complete(); + return OperationResult.Cancel(evtMsgs); } scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); @@ -780,7 +773,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - _eventAggregator.Publish(new SavedNotification(content, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(content, evtMsgs)); } var changeType = TreeChangeTypes.RefreshNode; scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); @@ -809,15 +802,10 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (raiseEvents) + if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(contentsA, evtMsgs))) { - var notification = new SavingNotification(contentsA, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) - { - scope.Complete(); - return OperationResult.Cancel(evtMsgs); - } + scope.Complete(); + return OperationResult.Cancel(evtMsgs); } var treeChanges = contentsA.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)); @@ -834,7 +822,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - _eventAggregator.Publish(new SavedNotification(contentsA, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(contentsA, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, treeChanges.ToEventArgs()); Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Saved multiple content"); @@ -878,9 +866,7 @@ namespace Umbraco.Cms.Core.Services.Implement var allLangs = _languageRepository.GetMany().ToList(); - var notification = new SavingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SavingNotification(content, evtMsgs))) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); } @@ -920,14 +906,9 @@ namespace Umbraco.Cms.Core.Services.Implement var evtMsgs = EventMessagesFactory.Get(); - if (raiseEvents) + if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(content, evtMsgs))) { - var notification = new SavingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) - { - return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); - } + return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); } var varies = content.ContentType.VariesByCulture(); @@ -990,9 +971,7 @@ namespace Umbraco.Cms.Core.Services.Implement var allLangs = _languageRepository.GetMany().ToList(); - var notification = new SavingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SavingNotification(content, evtMsgs))) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); } @@ -1063,9 +1042,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); - var notification = new SavingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SavingNotification(content, evtMsgs))) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); } @@ -1235,7 +1212,7 @@ namespace Umbraco.Cms.Core.Services.Implement // raise the Saved event, always if (raiseEvents) { - _eventAggregator.Publish(new SavedNotification(content, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(content, evtMsgs)); } if (unpublishing) // we have tried to unpublish - won't happen in a branch @@ -1243,7 +1220,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (unpublishResult.Success) // and succeeded, trigger events { // events and audit - _eventAggregator.Publish(new UnpublishedNotification(content, evtMsgs)); + scope.Notifications.Publish(new UnpublishedNotification(content, evtMsgs)); scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, TreeChangeTypes.RefreshBranch).ToEventArgs()); if (culturesUnpublishing != null) @@ -1298,7 +1275,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!branchOne) // for branches, handled by SaveAndPublishBranch { scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); - _eventAggregator.Publish(new PublishedNotification(content, evtMsgs)); + scope.Notifications.Publish(new PublishedNotification(content, evtMsgs)); } // it was not published and now is... descendants that were 'published' (but @@ -1307,7 +1284,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!branchOne && isNew == false && previouslyPublished == false && HasChildren(content.Id)) { var descendants = GetPublishedDescendantsLocked(content).ToArray(); - _eventAggregator.Publish(new PublishedNotification(descendants, evtMsgs)); + scope.Notifications.Publish(new PublishedNotification(descendants, evtMsgs)); } switch (publishResult.Result) @@ -1400,9 +1377,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (pendingCultures.Count == 0) continue; //shouldn't happen but no point in processing this document if there's nothing there - var notification = new SavingNotification(d, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SavingNotification(d, evtMsgs))) { results.Add(new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, d)); continue; @@ -1462,9 +1437,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (pendingCultures.Count == 0) continue; //shouldn't happen but no point in processing this document if there's nothing there - var notification = new SavingNotification(d, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SavingNotification(d, evtMsgs))) { results.Add(new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, d)); continue; @@ -1721,7 +1694,7 @@ namespace Umbraco.Cms.Core.Services.Implement // trigger events for the entire branch // (SaveAndPublishBranchOne does *not* do it) scope.Events.Dispatch(TreeChanged, this, new TreeChange(document, TreeChangeTypes.RefreshBranch).ToEventArgs()); - _eventAggregator.Publish(new PublishedNotification(publishedDocuments, evtMsgs)); + scope.Notifications.Publish(new PublishedNotification(publishedDocuments, evtMsgs)); scope.Complete(); } @@ -1745,9 +1718,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (culturesToPublish.Count == 0) // empty = already published return new PublishResult(PublishResultType.SuccessPublishAlready, evtMsgs, document); - var notification = new SavingNotification(document, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SavingNotification(document, evtMsgs))) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, document); } @@ -1776,9 +1747,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new DeletingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingNotification(content, evtMsgs))) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -1791,7 +1760,7 @@ namespace Umbraco.Cms.Core.Services.Implement // just raise the event if (content.Trashed == false && content.Published) { - _eventAggregator.Publish(new UnpublishedNotification(content, evtMsgs)); + scope.Notifications.Publish(new UnpublishedNotification(content, evtMsgs)); } DeleteLocked(scope, content, evtMsgs); @@ -1810,7 +1779,7 @@ namespace Umbraco.Cms.Core.Services.Implement void DoDelete(IContent c) { _documentRepository.Delete(c); - _eventAggregator.Publish(new DeletedNotification(c, evtMsgs)); + scope.Notifications.Publish(new DeletedNotification(c, evtMsgs)); // media files deleted by QueuingEventDispatcher } @@ -1845,9 +1814,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate))) { scope.Complete(); return; @@ -1856,7 +1823,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); _documentRepository.DeleteVersions(id, versionDate); - _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate)); + scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version date)"); scope.Complete(); @@ -1877,9 +1844,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId))) { scope.Complete(); return; @@ -1896,7 +1861,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (c.VersionId != versionId && c.PublishedVersionId != versionId) // don't delete the current or published version _documentRepository.DeleteVersion(versionId); - _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId)); + scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version)"); scope.Complete(); @@ -1920,9 +1885,7 @@ namespace Umbraco.Cms.Core.Services.Implement var originalPath = content.Path; var moveEventInfo = new MoveEventInfo(content, originalPath, Cms.Core.Constants.System.RecycleBinContent); - var notification = new MovingToRecycleBinNotification(moveEventInfo, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new MovingToRecycleBinNotification(moveEventInfo, evtMsgs))) { scope.Complete(); return OperationResult.Cancel(evtMsgs); // causes rollback @@ -1941,7 +1904,7 @@ namespace Umbraco.Cms.Core.Services.Implement .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - _eventAggregator.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs)); + scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs)); Audit(AuditType.Move, userId, content.Id, "Moved to recycle bin"); scope.Complete(); @@ -1983,9 +1946,8 @@ namespace Umbraco.Cms.Core.Services.Implement throw new InvalidOperationException("Parent does not exist or is trashed."); // causes rollback var moveEventInfo = new MoveEventInfo(content, content.Path, parentId); - var notification = new MovingNotification(moveEventInfo, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + + if (scope.Notifications.PublishCancelable(new MovingNotification(moveEventInfo, evtMsgs))) { scope.Complete(); return; // causes rollback @@ -2014,7 +1976,7 @@ namespace Umbraco.Cms.Core.Services.Implement .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - _eventAggregator.Publish(new MovedNotification(moveInfo, evtMsgs)); + scope.Notifications.Publish(new MovedNotification(moveInfo, evtMsgs)); Audit(AuditType.Move, userId, content.Id); @@ -2099,9 +2061,7 @@ namespace Umbraco.Cms.Core.Services.Implement // are managed by Delete, and not here. // no idea what those events are for, keep a simplified version - var notification = new EmptyingRecycleBinNotification(evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new EmptyingRecycleBinNotification(evtMsgs))) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -2116,7 +2076,7 @@ namespace Umbraco.Cms.Core.Services.Implement deleted.Add(content); } - _eventAggregator.Publish(new EmptiedRecycleBinNotification(evtMsgs)); + scope.Notifications.Publish(new EmptiedRecycleBinNotification(evtMsgs)); scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange(x, TreeChangeTypes.Remove)).ToEventArgs()); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.RecycleBinContent, "Recycle bin emptied"); @@ -2163,9 +2123,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new CopyingNotification(content, copy, parentId, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new CopyingNotification(content, copy, parentId, evtMsgs))) { scope.Complete(); return null; @@ -2220,9 +2178,7 @@ namespace Umbraco.Cms.Core.Services.Implement var descendantCopy = descendant.DeepCloneWithResetIdentities(); descendantCopy.ParentId = parentId; - notification = new CopyingNotification(descendant, descendantCopy, parentId, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new CopyingNotification(descendant, descendantCopy, parentId, evtMsgs))) { continue; } @@ -2250,7 +2206,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.Events.Dispatch(TreeChanged, this, new TreeChange(copy, TreeChangeTypes.RefreshBranch).ToEventArgs()); foreach (var x in copies) { - _eventAggregator.Publish(new CopiedNotification(x.Item1, x.Item2, parentId, relateToOriginal, evtMsgs)); + scope.Notifications.Publish(new CopiedNotification(x.Item1, x.Item2, parentId, relateToOriginal, evtMsgs)); } Audit(AuditType.Copy, userId, content.Id); @@ -2272,9 +2228,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new SendingToPublishNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SendingToPublishNotification(content, evtMsgs))) { scope.Complete(); return false; @@ -2299,7 +2253,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!saveResult.Success) return saveResult.Success; - _eventAggregator.Publish(new SentToPublishNotification(content, evtMsgs)); + scope.Notifications.Publish(new SentToPublishNotification(content, evtMsgs)); if (culturesChanging != null) Audit(AuditType.SendToPublishVariant, userId, content.Id, $"Send To Publish for cultures: {culturesChanging}", culturesChanging); @@ -2374,17 +2328,13 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { // raise cancelable sorting event - var sortingNotification = new SortingNotification(itemsA, evtMsgs); - _eventAggregator.Publish(sortingNotification); - if (sortingNotification.Cancel) + if (scope.Notifications.PublishCancelable(new SortingNotification(itemsA, evtMsgs))) { return OperationResult.Cancel(evtMsgs); } - // raise cancelable saving event - var savingNotification = new SavingNotification(itemsA, evtMsgs); - _eventAggregator.Publish(savingNotification); - if (savingNotification.Cancel) + // raise cancelable saving event + if (scope.Notifications.PublishCancelable(new SavingNotification(itemsA, evtMsgs))) { return OperationResult.Cancel(evtMsgs); } @@ -2421,15 +2371,15 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { //first saved, then sorted - _eventAggregator.Publish(new SavedNotification(itemsA, evtMsgs)); - _eventAggregator.Publish(new SortedNotification(itemsA, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(itemsA, evtMsgs)); + scope.Notifications.Publish(new SortedNotification(itemsA, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)).ToEventArgs()); if (raiseEvents && published.Any()) { - _eventAggregator.Publish(new PublishedNotification(published, evtMsgs)); + scope.Notifications.Publish(new PublishedNotification(published, evtMsgs)); } Audit(AuditType.Sort, userId, 0, "Sorting content performed by user"); @@ -2534,9 +2484,7 @@ namespace Umbraco.Cms.Core.Services.Implement IReadOnlyCollection culturesUnpublishing, EventMessages evtMsgs, IReadOnlyCollection allLangs) { // raise Publishing notification - var notification = new PublishingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new PublishingNotification(content, evtMsgs))) { _logger.LogInformation("Document {ContentName} (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "publishing was cancelled"); return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); @@ -2696,9 +2644,7 @@ namespace Umbraco.Cms.Core.Services.Implement private PublishResult StrategyCanUnpublish(IScope scope, IContent content, EventMessages evtMsgs) { // raise Unpublishing notification - var notification = new UnpublishingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new UnpublishingNotification(content, evtMsgs))) { _logger.LogInformation("Document {ContentName} (id={ContentId}) cannot be unpublished: unpublishing was cancelled.", content.Name, content.Id); return new PublishResult(PublishResultType.FailedUnpublishCancelledByEvent, evtMsgs, content); @@ -2780,9 +2726,7 @@ namespace Umbraco.Cms.Core.Services.Implement var query = Query().WhereIn(x => x.ContentTypeId, contentTypeIdsA); var contents = _documentRepository.Get(query).ToArray(); - var notification = new DeletingNotification(contents, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingNotification(contents, evtMsgs))) { scope.Complete(); return; @@ -2797,7 +2741,7 @@ namespace Umbraco.Cms.Core.Services.Implement // just raise the event if (content.Trashed == false && content.Published) { - _eventAggregator.Publish(new UnpublishedNotification(content, evtMsgs)); + scope.Notifications.Publish(new UnpublishedNotification(content, evtMsgs)); } // if current content has children, move them to trash @@ -2822,7 +2766,7 @@ namespace Umbraco.Cms.Core.Services.Implement .ToArray(); if (moveInfos.Length > 0) { - _eventAggregator.Publish(new MovedToRecycleBinNotification(moveInfos, evtMsgs)); + scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfos, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, changes.ToEventArgs()); @@ -2922,7 +2866,7 @@ namespace Umbraco.Cms.Core.Services.Implement Audit(AuditType.Save, Cms.Core.Constants.Security.SuperUserId, content.Id, $"Saved content template: {content.Name}"); - _eventAggregator.Publish(new SavedBlueprintNotification(content, evtMsgs)); + scope.Notifications.Publish(new SavedBlueprintNotification(content, evtMsgs)); scope.Complete(); } @@ -2936,7 +2880,7 @@ namespace Umbraco.Cms.Core.Services.Implement { scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); _documentBlueprintRepository.Delete(content); - _eventAggregator.Publish(new DeletedBlueprintNotification(content, evtMsgs)); + scope.Notifications.Publish(new DeletedBlueprintNotification(content, evtMsgs)); scope.Complete(); } } @@ -3028,7 +2972,7 @@ namespace Umbraco.Cms.Core.Services.Implement _documentBlueprintRepository.Delete(blueprint); } - _eventAggregator.Publish(new DeletedBlueprintNotification(blueprints, evtMsgs)); + scope.Notifications.Publish(new DeletedBlueprintNotification(blueprints, evtMsgs)); scope.Complete(); } } @@ -3063,9 +3007,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new RollingBackNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new RollingBackNotification(content, evtMsgs))) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -3085,7 +3027,7 @@ namespace Umbraco.Cms.Core.Services.Implement } else { - _eventAggregator.Publish(new RolledBackNotification(content, evtMsgs)); + scope.Notifications.Publish(new RolledBackNotification(content, evtMsgs)); //Logging & Audit message _logger.LogInformation("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId); diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index 4ccf48a690..0621e8dfbf 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -31,13 +31,11 @@ namespace Umbraco.Cms.Core.Services.Implement private readonly IMediaFileSystem _mediaFileSystem; - private readonly IEventAggregator _eventAggregator; - #region Constructors public MediaService(IScopeProvider provider, IMediaFileSystem mediaFileSystem, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, IMediaRepository mediaRepository, IAuditRepository auditRepository, IMediaTypeRepository mediaTypeRepository, - IEntityRepository entityRepository, IShortStringHelper shortStringHelper, IEventAggregator eventAggregator) + IEntityRepository entityRepository, IShortStringHelper shortStringHelper) : base(provider, loggerFactory, eventMessagesFactory) { _mediaFileSystem = mediaFileSystem; @@ -46,7 +44,6 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaTypeRepository = mediaTypeRepository; _entityRepository = entityRepository; _shortStringHelper = shortStringHelper; - _eventAggregator = eventAggregator; } #endregion @@ -299,16 +296,14 @@ namespace Umbraco.Cms.Core.Services.Implement if (withIdentity) { - var notification = new SavingNotification(media, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SavingNotification(media, evtMsgs))) { return; } _mediaRepository.Save(media); - _eventAggregator.Publish(new SavedNotification(media, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(media, evtMsgs)); scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, TreeChangeTypes.RefreshNode).ToEventArgs()); } @@ -666,15 +661,10 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (raiseEvents) + if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(media, evtMsgs))) { - var notification = new SavingNotification(media, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) - { - scope.Complete(); - return OperationResult.Attempt.Cancel(evtMsgs); - } + scope.Complete(); + return OperationResult.Attempt.Cancel(evtMsgs); } // poor man's validation? @@ -693,7 +683,7 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaRepository.Save(media); if (raiseEvents) { - _eventAggregator.Publish(new SavedNotification(media, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(media, evtMsgs)); } var changeType = TreeChangeTypes.RefreshNode; scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, changeType).ToEventArgs()); @@ -718,15 +708,10 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (raiseEvents) + if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(mediasA, evtMsgs))) { - var notification = new SavingNotification(mediasA, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) - { - scope.Complete(); - return OperationResult.Attempt.Cancel(evtMsgs); - } + scope.Complete(); + return OperationResult.Attempt.Cancel(evtMsgs); } var treeChanges = mediasA.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)); @@ -741,7 +726,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - _eventAggregator.Publish(new SavedNotification(mediasA, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(mediasA, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, treeChanges.ToEventArgs()); Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Bulk save media"); @@ -767,9 +752,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new DeletingNotification(media, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingNotification(media, evtMsgs))) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -793,7 +776,7 @@ namespace Umbraco.Cms.Core.Services.Implement void DoDelete(IMedia c) { _mediaRepository.Delete(c); - _eventAggregator.Publish(new DeletedNotification(c, evtMsgs)); + scope.Notifications.Publish(new DeletedNotification(c, evtMsgs)); // media files deleted by QueuingEventDispatcher } @@ -836,9 +819,7 @@ namespace Umbraco.Cms.Core.Services.Implement { var evtMsgs = EventMessagesFactory.Get(); - var notification = new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate))) { return; } @@ -847,7 +828,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.MediaTree); _mediaRepository.DeleteVersions(id, versionDate); - _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate)); + scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete Media by version date"); } @@ -865,9 +846,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId))) { scope.Complete(); return; @@ -885,7 +864,7 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaRepository.DeleteVersion(versionId); - _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId)); + scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete Media by version"); scope.Complete(); @@ -917,9 +896,7 @@ namespace Umbraco.Cms.Core.Services.Implement var moveEventInfo = new MoveEventInfo(media, originalPath, Cms.Core.Constants.System.RecycleBinMedia); - var notification = new MovingToRecycleBinNotification(moveEventInfo, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new MovingToRecycleBinNotification(moveEventInfo, evtMsgs))) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -928,9 +905,8 @@ namespace Umbraco.Cms.Core.Services.Implement PerformMoveLocked(media, Cms.Core.Constants.System.RecycleBinMedia, null, userId, moves, true); scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, TreeChangeTypes.RefreshBranch).ToEventArgs()); - var moveInfo = moves.Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) - .ToArray(); - _eventAggregator.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs)); + var moveInfo = moves.Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)).ToArray(); + scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs)); Audit(AuditType.Move, userId, media.Id, "Move Media to recycle bin"); scope.Complete(); @@ -967,9 +943,7 @@ namespace Umbraco.Cms.Core.Services.Implement throw new InvalidOperationException("Parent does not exist or is trashed."); // causes rollback var moveEventInfo = new MoveEventInfo(media, media.Path, parentId); - var notification = new MovingNotification(moveEventInfo, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new MovingNotification(moveEventInfo, evtMsgs))) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -985,7 +959,7 @@ namespace Umbraco.Cms.Core.Services.Implement var moveInfo = moves //changes .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - _eventAggregator.Publish(new MovedNotification(moveInfo, evtMsgs)); + scope.Notifications.Publish(new MovedNotification(moveInfo, evtMsgs)); Audit(AuditType.Move, userId, media.Id); scope.Complete(); } @@ -1066,9 +1040,7 @@ namespace Umbraco.Cms.Core.Services.Implement // v7 EmptyingRecycleBin and EmptiedRecycleBin events are greatly simplified since // each deleted items will have its own deleting/deleted events. so, files and such // are managed by Delete, and not here. - var notification = new EmptyingRecycleBinNotification(evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new EmptyingRecycleBinNotification(evtMsgs))) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -1082,7 +1054,7 @@ namespace Umbraco.Cms.Core.Services.Implement DeleteLocked(scope, media, evtMsgs); deleted.Add(media); } - _eventAggregator.Publish(new EmptiedRecycleBinNotification(new EventMessages())); + scope.Notifications.Publish(new EmptiedRecycleBinNotification(new EventMessages())); scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange(x, TreeChangeTypes.Remove)).ToEventArgs()); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.RecycleBinMedia, "Empty Media recycle bin"); scope.Complete(); @@ -1112,15 +1084,10 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (raiseEvents) + if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(itemsA, evtMsgs))) { - var notification = new SavingNotification(itemsA, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) - { - scope.Complete(); - return false; - } + scope.Complete(); + return false; } var saved = new List(); @@ -1146,7 +1113,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - _eventAggregator.Publish(new SavedNotification(itemsA, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(itemsA, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)).ToEventArgs()); Audit(AuditType.Sort, userId, 0); @@ -1264,9 +1231,7 @@ namespace Umbraco.Cms.Core.Services.Implement var query = Query().WhereIn(x => x.ContentTypeId, mediaTypeIdsA); var medias = _mediaRepository.Get(query).ToArray(); - var notification = new DeletingNotification(medias, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingNotification(medias, evtMsgs))) { scope.Complete(); return; @@ -1297,7 +1262,7 @@ namespace Umbraco.Cms.Core.Services.Implement .ToArray(); if (moveInfos.Length > 0) { - _eventAggregator.Publish(new MovedToRecycleBinNotification(moveInfos, evtMsgs)); + scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfos, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, changes.ToEventArgs()); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs index b06144d555..745e8309d7 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs @@ -17,6 +17,7 @@ using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration; 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.IO; using Umbraco.Cms.Core.Logging; @@ -50,7 +51,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components var fs = new FileSystems(mock.Object, loggerFactory.CreateLogger(), loggerFactory, IOHelper, Options.Create(globalSettings), Mock.Of()); var coreDebug = new CoreDebugSettings(); IMediaFileSystem mediaFileSystem = Mock.Of(); - var p = new ScopeProvider(f, fs, Options.Create(coreDebug), mediaFileSystem, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance); + IEventAggregator eventAggregator = Mock.Of(); + var p = new ScopeProvider(f, fs, Options.Create(coreDebug), mediaFileSystem, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance, eventAggregator); mock.Setup(x => x.GetService(typeof(ILogger))).Returns(logger); mock.Setup(x => x.GetService(typeof(ILogger))).Returns(loggerFactory.CreateLogger); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs index 17bc26dc23..43d14677fa 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs @@ -87,7 +87,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Scoping Mock.Of(), Mock.Of>(), instance, - Mock.Of() + Mock.Of(), + Mock.Of() ); } From 8e745eb13c17ee8acce6126ad1b3b7e8417eddaa Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 3 Mar 2021 17:08:21 +0100 Subject: [PATCH 19/42] Create scoped notification publishing - particularly for "ed" notifications that must run after the current scope has completed --- .../Events/IScopedNotificationPublisher.cs | 34 ++++ .../Events/ScopedNotificationPublisher.cs | 68 +++++++ src/Umbraco.Infrastructure/Scoping/IScope.cs | 7 +- src/Umbraco.Infrastructure/Scoping/Scope.cs | 25 ++- .../Scoping/ScopeProvider.cs | 10 +- .../Services/Implement/ContentService.cs | 172 ++++++------------ .../Services/Implement/MediaService.cs | 95 +++------- .../Umbraco.Core/Components/ComponentTests.cs | 4 +- .../Scoping/ScopeEventDispatcherTests.cs | 3 +- src/Umbraco.Tests/TestHelpers/TestObjects.cs | 4 +- 10 files changed, 232 insertions(+), 190 deletions(-) create mode 100644 src/Umbraco.Core/Events/IScopedNotificationPublisher.cs create mode 100644 src/Umbraco.Core/Events/ScopedNotificationPublisher.cs diff --git a/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs new file mode 100644 index 0000000000..e6c8a06647 --- /dev/null +++ b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; + +namespace Umbraco.Cms.Core.Events +{ + public interface IScopedNotificationPublisher + { + /// + /// Publishes a cancelable notification to the notification subscribers + /// + /// + /// True if the notification was cancelled by a subscriber, false otherwise + bool PublishCancelable(ICancelableNotification notification); + + /// + /// Publishes a cancelable notification to the notification subscribers + /// + /// + /// True if the notification was cancelled by a subscriber, false otherwise + Task PublishCancelableAsync(ICancelableNotification notification); + + /// + /// Publishes a notification to the notification subscribers + /// + /// + /// The notification is published upon successful completion of the current scope, i.e. when things have been saved/published/deleted etc. + void Publish(INotification notification); + + /// + /// Invokes publishing of all pending notifications within the current scope + /// + /// + void ScopeExit(bool completed); + } +} diff --git a/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs new file mode 100644 index 0000000000..dfaca55663 --- /dev/null +++ b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Umbraco.Cms.Core.Events +{ + public class ScopedNotificationPublisher : IScopedNotificationPublisher + { + private readonly IEventAggregator _eventAggregator; + private readonly List _notificationOnScopeCompleted; + + public ScopedNotificationPublisher(IEventAggregator eventAggregator) + { + _eventAggregator = eventAggregator; + _notificationOnScopeCompleted = new List(); + } + + public bool PublishCancelable(ICancelableNotification notification) + { + if (notification == null) + { + throw new ArgumentNullException(nameof(notification)); + } + + _eventAggregator.Publish(notification); + return notification.Cancel; + } + + public async Task PublishCancelableAsync(ICancelableNotification notification) + { + if (notification == null) + { + throw new ArgumentNullException(nameof(notification)); + } + + await _eventAggregator.PublishAsync(notification); + return notification.Cancel; + } + + public void Publish(INotification notification) + { + if (notification == null) + { + throw new ArgumentNullException(nameof(notification)); + } + + _notificationOnScopeCompleted.Add(notification); + } + + public void ScopeExit(bool completed) + { + try + { + if (completed) + { + foreach (var notification in _notificationOnScopeCompleted) + { + _eventAggregator.Publish(notification); + } + } + } + finally + { + _notificationOnScopeCompleted.Clear(); + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Scoping/IScope.cs b/src/Umbraco.Infrastructure/Scoping/IScope.cs index 7a6a62a6c7..ab1d905b44 100644 --- a/src/Umbraco.Infrastructure/Scoping/IScope.cs +++ b/src/Umbraco.Infrastructure/Scoping/IScope.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Infrastructure.Persistence; @@ -30,6 +30,11 @@ namespace Umbraco.Cms.Core.Scoping /// IEventDispatcher Events { get; } + /// + /// Gets the scope notification publisher + /// + IScopedNotificationPublisher Notifications { get; } + /// /// Gets the repositories cache mode. /// diff --git a/src/Umbraco.Infrastructure/Scoping/Scope.cs b/src/Umbraco.Infrastructure/Scoping/Scope.cs index 7d50f5e55a..a374f8726b 100644 --- a/src/Umbraco.Infrastructure/Scoping/Scope.cs +++ b/src/Umbraco.Infrastructure/Scoping/Scope.cs @@ -19,6 +19,7 @@ namespace Umbraco.Cms.Core.Scoping private readonly ScopeProvider _scopeProvider; private readonly CoreDebugSettings _coreDebugSettings; private readonly IMediaFileSystem _mediaFileSystem; + private readonly IEventAggregator _eventAggregator; private readonly ILogger _logger; private readonly IsolationLevel _isolationLevel; @@ -35,12 +36,15 @@ namespace Umbraco.Cms.Core.Scoping private EventMessages _messages; private ICompletable _fscope; private IEventDispatcher _eventDispatcher; + // eventually this may need to be injectable - for now we'll create it explicitly and let future needs determine if it should be injectable + private IScopedNotificationPublisher _notificationPublisher; // initializes a new scope private Scope( ScopeProvider scopeProvider, CoreDebugSettings coreDebugSettings, IMediaFileSystem mediaFileSystem, + IEventAggregator eventAggregator, ILogger logger, FileSystems fileSystems, Scope parent, @@ -56,6 +60,7 @@ namespace Umbraco.Cms.Core.Scoping _scopeProvider = scopeProvider; _coreDebugSettings = coreDebugSettings; _mediaFileSystem = mediaFileSystem; + _eventAggregator = eventAggregator; _logger = logger; Context = scopeContext; @@ -69,6 +74,7 @@ namespace Umbraco.Cms.Core.Scoping Detachable = detachable; + #if DEBUG_SCOPES _scopeProvider.RegisterScope(this); Console.WriteLine("create " + InstanceId.ToString("N").Substring(0, 8)); @@ -124,6 +130,7 @@ namespace Umbraco.Cms.Core.Scoping ScopeProvider scopeProvider, CoreDebugSettings coreDebugSettings, IMediaFileSystem mediaFileSystem, + IEventAggregator eventAggregator, ILogger logger, FileSystems fileSystems, bool detachable, @@ -134,7 +141,7 @@ namespace Umbraco.Cms.Core.Scoping bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) - : this(scopeProvider, coreDebugSettings, mediaFileSystem, logger, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) + : this(scopeProvider, coreDebugSettings, mediaFileSystem, eventAggregator, logger, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) { } // initializes a new scope in a nested scopes chain, with its parent @@ -142,6 +149,7 @@ namespace Umbraco.Cms.Core.Scoping ScopeProvider scopeProvider, CoreDebugSettings coreDebugSettings, IMediaFileSystem mediaFileSystem, + IEventAggregator eventAggregator, ILogger logger, FileSystems fileSystems, Scope parent, @@ -151,7 +159,7 @@ namespace Umbraco.Cms.Core.Scoping bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) - : this(scopeProvider, coreDebugSettings, mediaFileSystem, logger, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) + : this(scopeProvider, coreDebugSettings, mediaFileSystem, eventAggregator, logger, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) { } public Guid InstanceId { get; } = Guid.NewGuid(); @@ -314,6 +322,16 @@ namespace Umbraco.Cms.Core.Scoping } } + public IScopedNotificationPublisher Notifications + { + get + { + EnsureNotDisposed(); + if (ParentScope != null) return ParentScope.Notifications; + return _notificationPublisher ?? (_notificationPublisher = new ScopedNotificationPublisher(_eventAggregator)); + } + } + /// public bool Complete() { @@ -453,7 +471,10 @@ namespace Umbraco.Cms.Core.Scoping { // deal with events if (onException == false) + { _eventDispatcher?.ScopeExit(completed); + _notificationPublisher?.ScopeExit(completed); + } }, () => { // if *we* created it, then get rid of it diff --git a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs index b0b1868a0d..392028166d 100644 --- a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs @@ -26,8 +26,9 @@ namespace Umbraco.Cms.Core.Scoping private readonly FileSystems _fileSystems; private readonly CoreDebugSettings _coreDebugSettings; private readonly IMediaFileSystem _mediaFileSystem; + private readonly IEventAggregator _eventAggregator; - public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, IOptions coreDebugSettings, IMediaFileSystem mediaFileSystem, ILogger logger, ILoggerFactory loggerFactory, IRequestCache requestCache) + public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, IOptions coreDebugSettings, IMediaFileSystem mediaFileSystem, ILogger logger, ILoggerFactory loggerFactory, IRequestCache requestCache, IEventAggregator eventAggregator) { DatabaseFactory = databaseFactory; _fileSystems = fileSystems; @@ -36,6 +37,7 @@ namespace Umbraco.Cms.Core.Scoping _logger = logger; _loggerFactory = loggerFactory; _requestCache = requestCache; + _eventAggregator = eventAggregator; // take control of the FileSystems _fileSystems.IsScoped = () => AmbientScope != null && AmbientScope.ScopedFileSystems; @@ -253,7 +255,7 @@ namespace Umbraco.Cms.Core.Scoping IEventDispatcher eventDispatcher = null, bool? scopeFileSystems = null) { - return new Scope(this, _coreDebugSettings, _mediaFileSystem, _loggerFactory.CreateLogger(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems); + return new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems); } /// @@ -309,13 +311,13 @@ namespace Umbraco.Cms.Core.Scoping { var ambientContext = AmbientContext; var newContext = ambientContext == null ? new ScopeContext() : null; - var scope = new Scope(this, _coreDebugSettings, _mediaFileSystem, _loggerFactory.CreateLogger(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); + var scope = new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); // assign only if scope creation did not throw! SetAmbient(scope, newContext ?? ambientContext); return scope; } - var nested = new Scope(this, _coreDebugSettings, _mediaFileSystem, _loggerFactory.CreateLogger(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); + var nested = new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); SetAmbient(nested, AmbientContext); return nested; } diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index da24ac4427..dbd6cce1c1 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -33,7 +33,6 @@ namespace Umbraco.Cms.Core.Services.Implement private readonly IShortStringHelper _shortStringHelper; private readonly ILogger _logger; private IQuery _queryNotTrashed; - private readonly IEventAggregator _eventAggregator; #region Constructors @@ -41,7 +40,7 @@ namespace Umbraco.Cms.Core.Services.Implement IEventMessagesFactory eventMessagesFactory, IDocumentRepository documentRepository, IEntityRepository entityRepository, IAuditRepository auditRepository, IContentTypeRepository contentTypeRepository, IDocumentBlueprintRepository documentBlueprintRepository, ILanguageRepository languageRepository, - Lazy propertyValidationService, IShortStringHelper shortStringHelper, IEventAggregator eventAggregator) + Lazy propertyValidationService, IShortStringHelper shortStringHelper) : base(provider, loggerFactory, eventMessagesFactory) { _documentRepository = documentRepository; @@ -52,7 +51,6 @@ namespace Umbraco.Cms.Core.Services.Implement _languageRepository = languageRepository; _propertyValidationService = propertyValidationService; _shortStringHelper = shortStringHelper; - _eventAggregator = eventAggregator; _logger = loggerFactory.CreateLogger(); } @@ -750,15 +748,10 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (raiseEvents) + if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(content, evtMsgs))) { - var notification = new SavingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) - { - scope.Complete(); - return OperationResult.Cancel(evtMsgs); - } + scope.Complete(); + return OperationResult.Cancel(evtMsgs); } scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); @@ -780,7 +773,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - _eventAggregator.Publish(new SavedNotification(content, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(content, evtMsgs)); } var changeType = TreeChangeTypes.RefreshNode; scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); @@ -809,15 +802,10 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (raiseEvents) + if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(contentsA, evtMsgs))) { - var notification = new SavingNotification(contentsA, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) - { - scope.Complete(); - return OperationResult.Cancel(evtMsgs); - } + scope.Complete(); + return OperationResult.Cancel(evtMsgs); } var treeChanges = contentsA.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)); @@ -834,7 +822,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - _eventAggregator.Publish(new SavedNotification(contentsA, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(contentsA, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, treeChanges.ToEventArgs()); Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Saved multiple content"); @@ -878,9 +866,7 @@ namespace Umbraco.Cms.Core.Services.Implement var allLangs = _languageRepository.GetMany().ToList(); - var notification = new SavingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SavingNotification(content, evtMsgs))) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); } @@ -920,14 +906,9 @@ namespace Umbraco.Cms.Core.Services.Implement var evtMsgs = EventMessagesFactory.Get(); - if (raiseEvents) + if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(content, evtMsgs))) { - var notification = new SavingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) - { - return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); - } + return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); } var varies = content.ContentType.VariesByCulture(); @@ -990,9 +971,7 @@ namespace Umbraco.Cms.Core.Services.Implement var allLangs = _languageRepository.GetMany().ToList(); - var notification = new SavingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SavingNotification(content, evtMsgs))) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); } @@ -1063,9 +1042,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); - var notification = new SavingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SavingNotification(content, evtMsgs))) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); } @@ -1235,7 +1212,7 @@ namespace Umbraco.Cms.Core.Services.Implement // raise the Saved event, always if (raiseEvents) { - _eventAggregator.Publish(new SavedNotification(content, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(content, evtMsgs)); } if (unpublishing) // we have tried to unpublish - won't happen in a branch @@ -1243,7 +1220,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (unpublishResult.Success) // and succeeded, trigger events { // events and audit - _eventAggregator.Publish(new UnpublishedNotification(content, evtMsgs)); + scope.Notifications.Publish(new UnpublishedNotification(content, evtMsgs)); scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, TreeChangeTypes.RefreshBranch).ToEventArgs()); if (culturesUnpublishing != null) @@ -1298,7 +1275,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!branchOne) // for branches, handled by SaveAndPublishBranch { scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); - _eventAggregator.Publish(new PublishedNotification(content, evtMsgs)); + scope.Notifications.Publish(new PublishedNotification(content, evtMsgs)); } // it was not published and now is... descendants that were 'published' (but @@ -1307,7 +1284,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!branchOne && isNew == false && previouslyPublished == false && HasChildren(content.Id)) { var descendants = GetPublishedDescendantsLocked(content).ToArray(); - _eventAggregator.Publish(new PublishedNotification(descendants, evtMsgs)); + scope.Notifications.Publish(new PublishedNotification(descendants, evtMsgs)); } switch (publishResult.Result) @@ -1400,9 +1377,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (pendingCultures.Count == 0) continue; //shouldn't happen but no point in processing this document if there's nothing there - var notification = new SavingNotification(d, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SavingNotification(d, evtMsgs))) { results.Add(new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, d)); continue; @@ -1462,9 +1437,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (pendingCultures.Count == 0) continue; //shouldn't happen but no point in processing this document if there's nothing there - var notification = new SavingNotification(d, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SavingNotification(d, evtMsgs))) { results.Add(new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, d)); continue; @@ -1721,7 +1694,7 @@ namespace Umbraco.Cms.Core.Services.Implement // trigger events for the entire branch // (SaveAndPublishBranchOne does *not* do it) scope.Events.Dispatch(TreeChanged, this, new TreeChange(document, TreeChangeTypes.RefreshBranch).ToEventArgs()); - _eventAggregator.Publish(new PublishedNotification(publishedDocuments, evtMsgs)); + scope.Notifications.Publish(new PublishedNotification(publishedDocuments, evtMsgs)); scope.Complete(); } @@ -1745,9 +1718,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (culturesToPublish.Count == 0) // empty = already published return new PublishResult(PublishResultType.SuccessPublishAlready, evtMsgs, document); - var notification = new SavingNotification(document, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SavingNotification(document, evtMsgs))) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, document); } @@ -1776,9 +1747,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new DeletingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingNotification(content, evtMsgs))) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -1791,7 +1760,7 @@ namespace Umbraco.Cms.Core.Services.Implement // just raise the event if (content.Trashed == false && content.Published) { - _eventAggregator.Publish(new UnpublishedNotification(content, evtMsgs)); + scope.Notifications.Publish(new UnpublishedNotification(content, evtMsgs)); } DeleteLocked(scope, content, evtMsgs); @@ -1810,7 +1779,7 @@ namespace Umbraco.Cms.Core.Services.Implement void DoDelete(IContent c) { _documentRepository.Delete(c); - _eventAggregator.Publish(new DeletedNotification(c, evtMsgs)); + scope.Notifications.Publish(new DeletedNotification(c, evtMsgs)); // media files deleted by QueuingEventDispatcher } @@ -1845,9 +1814,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate))) { scope.Complete(); return; @@ -1856,7 +1823,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); _documentRepository.DeleteVersions(id, versionDate); - _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate)); + scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version date)"); scope.Complete(); @@ -1877,9 +1844,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId))) { scope.Complete(); return; @@ -1896,7 +1861,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (c.VersionId != versionId && c.PublishedVersionId != versionId) // don't delete the current or published version _documentRepository.DeleteVersion(versionId); - _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId)); + scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version)"); scope.Complete(); @@ -1920,9 +1885,7 @@ namespace Umbraco.Cms.Core.Services.Implement var originalPath = content.Path; var moveEventInfo = new MoveEventInfo(content, originalPath, Cms.Core.Constants.System.RecycleBinContent); - var notification = new MovingToRecycleBinNotification(moveEventInfo, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new MovingToRecycleBinNotification(moveEventInfo, evtMsgs))) { scope.Complete(); return OperationResult.Cancel(evtMsgs); // causes rollback @@ -1941,7 +1904,7 @@ namespace Umbraco.Cms.Core.Services.Implement .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - _eventAggregator.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs)); + scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs)); Audit(AuditType.Move, userId, content.Id, "Moved to recycle bin"); scope.Complete(); @@ -1983,9 +1946,8 @@ namespace Umbraco.Cms.Core.Services.Implement throw new InvalidOperationException("Parent does not exist or is trashed."); // causes rollback var moveEventInfo = new MoveEventInfo(content, content.Path, parentId); - var notification = new MovingNotification(moveEventInfo, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + + if (scope.Notifications.PublishCancelable(new MovingNotification(moveEventInfo, evtMsgs))) { scope.Complete(); return; // causes rollback @@ -2014,7 +1976,7 @@ namespace Umbraco.Cms.Core.Services.Implement .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - _eventAggregator.Publish(new MovedNotification(moveInfo, evtMsgs)); + scope.Notifications.Publish(new MovedNotification(moveInfo, evtMsgs)); Audit(AuditType.Move, userId, content.Id); @@ -2099,9 +2061,7 @@ namespace Umbraco.Cms.Core.Services.Implement // are managed by Delete, and not here. // no idea what those events are for, keep a simplified version - var notification = new EmptyingRecycleBinNotification(evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new EmptyingRecycleBinNotification(evtMsgs))) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -2116,7 +2076,7 @@ namespace Umbraco.Cms.Core.Services.Implement deleted.Add(content); } - _eventAggregator.Publish(new EmptiedRecycleBinNotification(evtMsgs)); + scope.Notifications.Publish(new EmptiedRecycleBinNotification(evtMsgs)); scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange(x, TreeChangeTypes.Remove)).ToEventArgs()); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.RecycleBinContent, "Recycle bin emptied"); @@ -2163,9 +2123,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new CopyingNotification(content, copy, parentId, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new CopyingNotification(content, copy, parentId, evtMsgs))) { scope.Complete(); return null; @@ -2220,9 +2178,7 @@ namespace Umbraco.Cms.Core.Services.Implement var descendantCopy = descendant.DeepCloneWithResetIdentities(); descendantCopy.ParentId = parentId; - notification = new CopyingNotification(descendant, descendantCopy, parentId, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new CopyingNotification(descendant, descendantCopy, parentId, evtMsgs))) { continue; } @@ -2250,7 +2206,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.Events.Dispatch(TreeChanged, this, new TreeChange(copy, TreeChangeTypes.RefreshBranch).ToEventArgs()); foreach (var x in copies) { - _eventAggregator.Publish(new CopiedNotification(x.Item1, x.Item2, parentId, relateToOriginal, evtMsgs)); + scope.Notifications.Publish(new CopiedNotification(x.Item1, x.Item2, parentId, relateToOriginal, evtMsgs)); } Audit(AuditType.Copy, userId, content.Id); @@ -2272,9 +2228,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new SendingToPublishNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SendingToPublishNotification(content, evtMsgs))) { scope.Complete(); return false; @@ -2299,7 +2253,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!saveResult.Success) return saveResult.Success; - _eventAggregator.Publish(new SentToPublishNotification(content, evtMsgs)); + scope.Notifications.Publish(new SentToPublishNotification(content, evtMsgs)); if (culturesChanging != null) Audit(AuditType.SendToPublishVariant, userId, content.Id, $"Send To Publish for cultures: {culturesChanging}", culturesChanging); @@ -2374,17 +2328,13 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { // raise cancelable sorting event - var sortingNotification = new SortingNotification(itemsA, evtMsgs); - _eventAggregator.Publish(sortingNotification); - if (sortingNotification.Cancel) + if (scope.Notifications.PublishCancelable(new SortingNotification(itemsA, evtMsgs))) { return OperationResult.Cancel(evtMsgs); } - // raise cancelable saving event - var savingNotification = new SavingNotification(itemsA, evtMsgs); - _eventAggregator.Publish(savingNotification); - if (savingNotification.Cancel) + // raise cancelable saving event + if (scope.Notifications.PublishCancelable(new SavingNotification(itemsA, evtMsgs))) { return OperationResult.Cancel(evtMsgs); } @@ -2421,15 +2371,15 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { //first saved, then sorted - _eventAggregator.Publish(new SavedNotification(itemsA, evtMsgs)); - _eventAggregator.Publish(new SortedNotification(itemsA, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(itemsA, evtMsgs)); + scope.Notifications.Publish(new SortedNotification(itemsA, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)).ToEventArgs()); if (raiseEvents && published.Any()) { - _eventAggregator.Publish(new PublishedNotification(published, evtMsgs)); + scope.Notifications.Publish(new PublishedNotification(published, evtMsgs)); } Audit(AuditType.Sort, userId, 0, "Sorting content performed by user"); @@ -2534,9 +2484,7 @@ namespace Umbraco.Cms.Core.Services.Implement IReadOnlyCollection culturesUnpublishing, EventMessages evtMsgs, IReadOnlyCollection allLangs) { // raise Publishing notification - var notification = new PublishingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new PublishingNotification(content, evtMsgs))) { _logger.LogInformation("Document {ContentName} (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "publishing was cancelled"); return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); @@ -2696,9 +2644,7 @@ namespace Umbraco.Cms.Core.Services.Implement private PublishResult StrategyCanUnpublish(IScope scope, IContent content, EventMessages evtMsgs) { // raise Unpublishing notification - var notification = new UnpublishingNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new UnpublishingNotification(content, evtMsgs))) { _logger.LogInformation("Document {ContentName} (id={ContentId}) cannot be unpublished: unpublishing was cancelled.", content.Name, content.Id); return new PublishResult(PublishResultType.FailedUnpublishCancelledByEvent, evtMsgs, content); @@ -2780,9 +2726,7 @@ namespace Umbraco.Cms.Core.Services.Implement var query = Query().WhereIn(x => x.ContentTypeId, contentTypeIdsA); var contents = _documentRepository.Get(query).ToArray(); - var notification = new DeletingNotification(contents, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingNotification(contents, evtMsgs))) { scope.Complete(); return; @@ -2797,7 +2741,7 @@ namespace Umbraco.Cms.Core.Services.Implement // just raise the event if (content.Trashed == false && content.Published) { - _eventAggregator.Publish(new UnpublishedNotification(content, evtMsgs)); + scope.Notifications.Publish(new UnpublishedNotification(content, evtMsgs)); } // if current content has children, move them to trash @@ -2822,7 +2766,7 @@ namespace Umbraco.Cms.Core.Services.Implement .ToArray(); if (moveInfos.Length > 0) { - _eventAggregator.Publish(new MovedToRecycleBinNotification(moveInfos, evtMsgs)); + scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfos, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, changes.ToEventArgs()); @@ -2922,7 +2866,7 @@ namespace Umbraco.Cms.Core.Services.Implement Audit(AuditType.Save, Cms.Core.Constants.Security.SuperUserId, content.Id, $"Saved content template: {content.Name}"); - _eventAggregator.Publish(new SavedBlueprintNotification(content, evtMsgs)); + scope.Notifications.Publish(new SavedBlueprintNotification(content, evtMsgs)); scope.Complete(); } @@ -2936,7 +2880,7 @@ namespace Umbraco.Cms.Core.Services.Implement { scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); _documentBlueprintRepository.Delete(content); - _eventAggregator.Publish(new DeletedBlueprintNotification(content, evtMsgs)); + scope.Notifications.Publish(new DeletedBlueprintNotification(content, evtMsgs)); scope.Complete(); } } @@ -3028,7 +2972,7 @@ namespace Umbraco.Cms.Core.Services.Implement _documentBlueprintRepository.Delete(blueprint); } - _eventAggregator.Publish(new DeletedBlueprintNotification(blueprints, evtMsgs)); + scope.Notifications.Publish(new DeletedBlueprintNotification(blueprints, evtMsgs)); scope.Complete(); } } @@ -3063,9 +3007,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new RollingBackNotification(content, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new RollingBackNotification(content, evtMsgs))) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -3085,7 +3027,7 @@ namespace Umbraco.Cms.Core.Services.Implement } else { - _eventAggregator.Publish(new RolledBackNotification(content, evtMsgs)); + scope.Notifications.Publish(new RolledBackNotification(content, evtMsgs)); //Logging & Audit message _logger.LogInformation("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId); diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index 4ccf48a690..0621e8dfbf 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -31,13 +31,11 @@ namespace Umbraco.Cms.Core.Services.Implement private readonly IMediaFileSystem _mediaFileSystem; - private readonly IEventAggregator _eventAggregator; - #region Constructors public MediaService(IScopeProvider provider, IMediaFileSystem mediaFileSystem, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, IMediaRepository mediaRepository, IAuditRepository auditRepository, IMediaTypeRepository mediaTypeRepository, - IEntityRepository entityRepository, IShortStringHelper shortStringHelper, IEventAggregator eventAggregator) + IEntityRepository entityRepository, IShortStringHelper shortStringHelper) : base(provider, loggerFactory, eventMessagesFactory) { _mediaFileSystem = mediaFileSystem; @@ -46,7 +44,6 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaTypeRepository = mediaTypeRepository; _entityRepository = entityRepository; _shortStringHelper = shortStringHelper; - _eventAggregator = eventAggregator; } #endregion @@ -299,16 +296,14 @@ namespace Umbraco.Cms.Core.Services.Implement if (withIdentity) { - var notification = new SavingNotification(media, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new SavingNotification(media, evtMsgs))) { return; } _mediaRepository.Save(media); - _eventAggregator.Publish(new SavedNotification(media, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(media, evtMsgs)); scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, TreeChangeTypes.RefreshNode).ToEventArgs()); } @@ -666,15 +661,10 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (raiseEvents) + if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(media, evtMsgs))) { - var notification = new SavingNotification(media, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) - { - scope.Complete(); - return OperationResult.Attempt.Cancel(evtMsgs); - } + scope.Complete(); + return OperationResult.Attempt.Cancel(evtMsgs); } // poor man's validation? @@ -693,7 +683,7 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaRepository.Save(media); if (raiseEvents) { - _eventAggregator.Publish(new SavedNotification(media, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(media, evtMsgs)); } var changeType = TreeChangeTypes.RefreshNode; scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, changeType).ToEventArgs()); @@ -718,15 +708,10 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (raiseEvents) + if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(mediasA, evtMsgs))) { - var notification = new SavingNotification(mediasA, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) - { - scope.Complete(); - return OperationResult.Attempt.Cancel(evtMsgs); - } + scope.Complete(); + return OperationResult.Attempt.Cancel(evtMsgs); } var treeChanges = mediasA.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)); @@ -741,7 +726,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - _eventAggregator.Publish(new SavedNotification(mediasA, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(mediasA, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, treeChanges.ToEventArgs()); Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Bulk save media"); @@ -767,9 +752,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new DeletingNotification(media, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingNotification(media, evtMsgs))) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -793,7 +776,7 @@ namespace Umbraco.Cms.Core.Services.Implement void DoDelete(IMedia c) { _mediaRepository.Delete(c); - _eventAggregator.Publish(new DeletedNotification(c, evtMsgs)); + scope.Notifications.Publish(new DeletedNotification(c, evtMsgs)); // media files deleted by QueuingEventDispatcher } @@ -836,9 +819,7 @@ namespace Umbraco.Cms.Core.Services.Implement { var evtMsgs = EventMessagesFactory.Get(); - var notification = new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate))) { return; } @@ -847,7 +828,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.MediaTree); _mediaRepository.DeleteVersions(id, versionDate); - _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate)); + scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete Media by version date"); } @@ -865,9 +846,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var notification = new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId))) { scope.Complete(); return; @@ -885,7 +864,7 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaRepository.DeleteVersion(versionId); - _eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId)); + scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete Media by version"); scope.Complete(); @@ -917,9 +896,7 @@ namespace Umbraco.Cms.Core.Services.Implement var moveEventInfo = new MoveEventInfo(media, originalPath, Cms.Core.Constants.System.RecycleBinMedia); - var notification = new MovingToRecycleBinNotification(moveEventInfo, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new MovingToRecycleBinNotification(moveEventInfo, evtMsgs))) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -928,9 +905,8 @@ namespace Umbraco.Cms.Core.Services.Implement PerformMoveLocked(media, Cms.Core.Constants.System.RecycleBinMedia, null, userId, moves, true); scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, TreeChangeTypes.RefreshBranch).ToEventArgs()); - var moveInfo = moves.Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) - .ToArray(); - _eventAggregator.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs)); + var moveInfo = moves.Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)).ToArray(); + scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs)); Audit(AuditType.Move, userId, media.Id, "Move Media to recycle bin"); scope.Complete(); @@ -967,9 +943,7 @@ namespace Umbraco.Cms.Core.Services.Implement throw new InvalidOperationException("Parent does not exist or is trashed."); // causes rollback var moveEventInfo = new MoveEventInfo(media, media.Path, parentId); - var notification = new MovingNotification(moveEventInfo, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new MovingNotification(moveEventInfo, evtMsgs))) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -985,7 +959,7 @@ namespace Umbraco.Cms.Core.Services.Implement var moveInfo = moves //changes .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - _eventAggregator.Publish(new MovedNotification(moveInfo, evtMsgs)); + scope.Notifications.Publish(new MovedNotification(moveInfo, evtMsgs)); Audit(AuditType.Move, userId, media.Id); scope.Complete(); } @@ -1066,9 +1040,7 @@ namespace Umbraco.Cms.Core.Services.Implement // v7 EmptyingRecycleBin and EmptiedRecycleBin events are greatly simplified since // each deleted items will have its own deleting/deleted events. so, files and such // are managed by Delete, and not here. - var notification = new EmptyingRecycleBinNotification(evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new EmptyingRecycleBinNotification(evtMsgs))) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -1082,7 +1054,7 @@ namespace Umbraco.Cms.Core.Services.Implement DeleteLocked(scope, media, evtMsgs); deleted.Add(media); } - _eventAggregator.Publish(new EmptiedRecycleBinNotification(new EventMessages())); + scope.Notifications.Publish(new EmptiedRecycleBinNotification(new EventMessages())); scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange(x, TreeChangeTypes.Remove)).ToEventArgs()); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.RecycleBinMedia, "Empty Media recycle bin"); scope.Complete(); @@ -1112,15 +1084,10 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (raiseEvents) + if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(itemsA, evtMsgs))) { - var notification = new SavingNotification(itemsA, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) - { - scope.Complete(); - return false; - } + scope.Complete(); + return false; } var saved = new List(); @@ -1146,7 +1113,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - _eventAggregator.Publish(new SavedNotification(itemsA, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(itemsA, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)).ToEventArgs()); Audit(AuditType.Sort, userId, 0); @@ -1264,9 +1231,7 @@ namespace Umbraco.Cms.Core.Services.Implement var query = Query().WhereIn(x => x.ContentTypeId, mediaTypeIdsA); var medias = _mediaRepository.Get(query).ToArray(); - var notification = new DeletingNotification(medias, evtMsgs); - _eventAggregator.Publish(notification); - if (notification.Cancel) + if (scope.Notifications.PublishCancelable(new DeletingNotification(medias, evtMsgs))) { scope.Complete(); return; @@ -1297,7 +1262,7 @@ namespace Umbraco.Cms.Core.Services.Implement .ToArray(); if (moveInfos.Length > 0) { - _eventAggregator.Publish(new MovedToRecycleBinNotification(moveInfos, evtMsgs)); + scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfos, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, changes.ToEventArgs()); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs index b06144d555..745e8309d7 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs @@ -17,6 +17,7 @@ using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration; 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.IO; using Umbraco.Cms.Core.Logging; @@ -50,7 +51,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components var fs = new FileSystems(mock.Object, loggerFactory.CreateLogger(), loggerFactory, IOHelper, Options.Create(globalSettings), Mock.Of()); var coreDebug = new CoreDebugSettings(); IMediaFileSystem mediaFileSystem = Mock.Of(); - var p = new ScopeProvider(f, fs, Options.Create(coreDebug), mediaFileSystem, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance); + IEventAggregator eventAggregator = Mock.Of(); + var p = new ScopeProvider(f, fs, Options.Create(coreDebug), mediaFileSystem, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance, eventAggregator); mock.Setup(x => x.GetService(typeof(ILogger))).Returns(logger); mock.Setup(x => x.GetService(typeof(ILogger))).Returns(loggerFactory.CreateLogger); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs index 17bc26dc23..43d14677fa 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs @@ -87,7 +87,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Scoping Mock.Of(), Mock.Of>(), instance, - Mock.Of() + Mock.Of(), + Mock.Of() ); } diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index 3d4f58fd18..6aefad7cc8 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -8,6 +8,7 @@ using NPoco; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Infrastructure.Migrations.Install; @@ -86,7 +87,8 @@ namespace Umbraco.Tests.TestHelpers fileSystems ??= new FileSystems(Current.Factory, loggerFactory.CreateLogger(), loggerFactory, TestHelper.IOHelper, Options.Create(globalSettings), TestHelper.GetHostingEnvironment()); var coreDebug = TestHelper.CoreDebugSettings; var mediaFileSystem = Mock.Of(); - return new ScopeProvider(databaseFactory, fileSystems, Options.Create(coreDebugSettings), mediaFileSystem, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance); + var eventAggregator = Mock.Of(); + return new ScopeProvider(databaseFactory, fileSystems, Options.Create(coreDebugSettings), mediaFileSystem, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance, eventAggregator); } } From 27c5c1b46104757303e7b58ff7b31826248dc3b4 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 4 Mar 2021 07:14:54 +0100 Subject: [PATCH 20/42] Added some copyright notices and updated a few TODOs --- src/Umbraco.Core/Events/IScopedNotificationPublisher.cs | 3 +++ src/Umbraco.Core/Events/ScopedNotificationPublisher.cs | 3 +++ src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs | 4 +++- src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs | 3 +++ .../Events/RelateOnCopyNotifcationHandler.cs | 3 +++ .../Events/RelateOnTrashNotificationHandler.cs | 3 +++ .../PropertyEditors/BlockEditorPropertyHandler.cs | 3 +++ .../PropertyEditors/NestedContentPropertyHandler.cs | 3 +++ src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs | 3 +++ 9 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs index e6c8a06647..96f8e771c0 100644 --- a/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs +++ b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs @@ -1,3 +1,6 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + using System.Threading.Tasks; namespace Umbraco.Cms.Core.Events diff --git a/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs index dfaca55663..206706716c 100644 --- a/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs +++ b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs @@ -1,3 +1,6 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + using System; using System.Collections.Generic; using System.Threading.Tasks; diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs index af65a654b7..687fdbf294 100644 --- a/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs +++ b/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs @@ -21,7 +21,9 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Compose { - // TODO: this component must be removed entirely - there is some code duplication in NotificationsHandler in anticipation of this component being deleted + /// + /// 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; diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs index f426d9b749..906f10c524 100644 --- a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs +++ b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs @@ -1,3 +1,6 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; diff --git a/src/Umbraco.Infrastructure/Events/RelateOnCopyNotifcationHandler.cs b/src/Umbraco.Infrastructure/Events/RelateOnCopyNotifcationHandler.cs index 0de73070f1..f8e162dc9e 100644 --- a/src/Umbraco.Infrastructure/Events/RelateOnCopyNotifcationHandler.cs +++ b/src/Umbraco.Infrastructure/Events/RelateOnCopyNotifcationHandler.cs @@ -1,3 +1,6 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Services.Notifications; diff --git a/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs b/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs index be421b9bbe..9ec248b728 100644 --- a/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs @@ -1,3 +1,6 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + using System.Linq; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Scoping; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyHandler.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyHandler.cs index e08ad47682..24c9b37cde 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyHandler.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyHandler.cs @@ -1,3 +1,6 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + using System; using System.Collections.Generic; using System.Linq; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyHandler.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyHandler.cs index f9944d12b2..1bc5dd2f4b 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyHandler.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyHandler.cs @@ -1,3 +1,6 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + using System; using System.Linq; using Newtonsoft.Json.Linq; diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs index dfcbc0f364..bacbbc91b9 100644 --- a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs @@ -1,3 +1,6 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + using System; using System.Collections.Generic; using System.Linq; From 500ca7b31ea94f8813fbf81d659d4e6a68c2669a Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 4 Mar 2021 11:58:45 +0100 Subject: [PATCH 21/42] Refactor RedirectTrackingHandler to remove duplicate code --- .../Routing/RedirectTrackingHandler.cs | 38 ++++++------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs index bacbbc91b9..75a554867d 100644 --- a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs @@ -47,44 +47,28 @@ namespace Umbraco.Cms.Core.Routing _requestCache = requestCache; } - public void Handle(PublishingNotification notification) + public void Handle(PublishingNotification notification) => StoreOldRoutes(notification.PublishedEntities); + + public void Handle(PublishedNotification notification) => CreateRedirectsForOldRoutes(); + + public void Handle(MovingNotification notification) => StoreOldRoutes(notification.MoveInfoCollection.Select(m => m.Entity)); + + public void Handle(MovedNotification notification) => CreateRedirectsForOldRoutes(); + + private void StoreOldRoutes(IEnumerable entities) { // don't let the notification handlers kick in if Redirect Tracking is turned off in the config if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) return; var oldRoutes = GetOldRoutes(); - foreach (var entity in notification.PublishedEntities) + foreach (var entity in entities) { StoreOldRoute(entity, oldRoutes); } } - public void Handle(PublishedNotification notification) - { - // don't let the notification handlers kick in if Redirect Tracking is turned off in the config - if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) - return; - - var oldRoutes = GetOldRoutes(); - CreateRedirects(oldRoutes); - } - - public void Handle(MovingNotification notification) - { - // don't let the notification handlers kick in if Redirect Tracking is turned off in the config - if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) - return; - - var oldRoutes = GetOldRoutes(); - foreach (var info in notification.MoveInfoCollection) - { - StoreOldRoute(info.Entity, oldRoutes); - } - } - - // TODO refactor (this is duplicate code, see published notification handling above) - public void Handle(MovedNotification notification) + private void CreateRedirectsForOldRoutes() { // don't let the notification handlers kick in if Redirect Tracking is turned off in the config if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) From 893c325191615e9e68e840e0de70b1cf7a035aa4 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 4 Mar 2021 12:25:43 +0100 Subject: [PATCH 22/42] Marked the TreeChange events as internal for now (until they can be rewritten) --- .../Services/Implement/ContentService.cs | 5 ++++- .../Services/Implement/MediaService.cs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index dbd6cce1c1..0dfc864acb 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -2463,7 +2463,10 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// Occurs after change. /// - public static event TypedEventHandler.EventArgs> TreeChanged; + /// + /// This event needs to be rewritten using notifications instead + /// + internal static event TypedEventHandler.EventArgs> TreeChanged; #endregion diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index 0621e8dfbf..6a904bced9 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -1194,7 +1194,10 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// Occurs after change. /// - public static event TypedEventHandler.EventArgs> TreeChanged; + /// + /// This event needs to be rewritten using notifications instead + /// + internal static event TypedEventHandler.EventArgs> TreeChanged; #endregion From fb6ee032a4f0769274fccc5c366421dcbd6f316e Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 4 Mar 2021 13:01:35 +0100 Subject: [PATCH 23/42] Made all notifications sealed --- .../Notifications/CopiedNotification.cs | 2 +- .../Notifications/CopyingNotification.cs | 2 +- .../DeletedBlueprintNotification.cs | 2 +- .../Notifications/DeletedNotification.cs | 2 +- .../DeletedVersionsNotification.cs | 18 ++--------- .../DeletedVersionsNotificationBase.cs | 30 +++++++++++++++++++ .../Notifications/DeletingNotification.cs | 2 +- .../DeletingVersionsNotification.cs | 2 +- .../EmptiedRecycleBinNotification.cs | 7 ++--- .../EmptyingRecycleBinNotification.cs | 9 +++--- .../Notifications/MovedNotification.cs | 2 +- .../MovedToRecycleBinNotification.cs | 2 +- .../Notifications/MovingNotification.cs | 2 +- .../MovingToRecycleBinNotification.cs | 2 +- .../Notifications/PublishedNotification.cs | 2 +- .../Notifications/PublishingNotification.cs | 2 +- .../Notifications/RolledBackNotification.cs | 2 +- .../Notifications/RollingBackNotification.cs | 2 +- .../SavedBlueprintNotification.cs | 2 +- .../Notifications/SavedNotification.cs | 2 +- .../Notifications/SavingNotification.cs | 2 +- .../SendingToPublishNotification.cs | 2 +- .../SentToPublishNotification.cs | 2 +- .../Notifications/SortedNotification.cs | 2 +- .../Notifications/SortingNotification.cs | 2 +- .../Notifications/UnpublishedNotification.cs | 2 +- .../Notifications/UnpublishingNotification.cs | 2 +- 27 files changed, 61 insertions(+), 49 deletions(-) create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotificationBase.cs diff --git a/src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs index f70780e1fa..46da7fe11e 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class CopiedNotification : ObjectNotification where T : class + public sealed class CopiedNotification : ObjectNotification where T : class { public CopiedNotification(T original, T copy, int parentId, bool relateToOriginal, EventMessages messages) : base(original, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/CopyingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/CopyingNotification.cs index a62b3ba869..1780ec6dea 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/CopyingNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/CopyingNotification.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class CopyingNotification : CancelableObjectNotification where T : class + public sealed class CopyingNotification : CancelableObjectNotification where T : class { public CopyingNotification(T original, T copy, int parentId, EventMessages messages) : base(original, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletedBlueprintNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletedBlueprintNotification.cs index 4a4ad777bf..2b4b822517 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/DeletedBlueprintNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletedBlueprintNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class DeletedBlueprintNotification : EnumerableObjectNotification + public sealed class DeletedBlueprintNotification : EnumerableObjectNotification { public DeletedBlueprintNotification(T target, EventMessages messages) : base(target, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletedNotification.cs index 17d165109c..07008974d2 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/DeletedNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletedNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class DeletedNotification : EnumerableObjectNotification + public sealed class DeletedNotification : EnumerableObjectNotification { public DeletedNotification(T target, EventMessages messages) : base(target, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotification.cs index 3f55abf4b4..3459795c12 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotification.cs @@ -6,25 +6,11 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class DeletedVersionsNotification : INotification where T : class + public sealed class DeletedVersionsNotification : DeletedVersionsNotificationBase where T : class { public DeletedVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) + : base(id, messages, specificVersion, deletePriorVersions, dateToRetain) { - Id = id; - Messages = messages; - SpecificVersion = specificVersion; - DeletePriorVersions = deletePriorVersions; - DateToRetain = dateToRetain; } - - public int Id { get; } - - public EventMessages Messages { get; } - - public int SpecificVersion { get; } - - public bool DeletePriorVersions { get; } - - public DateTime DateToRetain { get; } } } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotificationBase.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotificationBase.cs new file mode 100644 index 0000000000..a5b4654245 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotificationBase.cs @@ -0,0 +1,30 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public abstract class DeletedVersionsNotificationBase : INotification where T : class + { + protected DeletedVersionsNotificationBase(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) + { + Id = id; + Messages = messages; + SpecificVersion = specificVersion; + DeletePriorVersions = deletePriorVersions; + DateToRetain = dateToRetain; + } + + public int Id { get; } + + public EventMessages Messages { get; } + + public int SpecificVersion { get; } + + public bool DeletePriorVersions { get; } + + public DateTime DateToRetain { get; } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletingNotification.cs index b821ac2d30..6ebc9b8dd8 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/DeletingNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletingNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class DeletingNotification : CancelableEnumerableObjectNotification + public sealed class DeletingNotification : CancelableEnumerableObjectNotification { public DeletingNotification(T target, EventMessages messages) : base(target, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletingVersionsNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletingVersionsNotification.cs index 6a02ad402e..e87317d0d5 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/DeletingVersionsNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletingVersionsNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class DeletingVersionsNotification : DeletedVersionsNotification, ICancelableNotification where T : class + public sealed class DeletingVersionsNotification : DeletedVersionsNotificationBase, ICancelableNotification where T : class { public DeletingVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) : base(id, messages, specificVersion, deletePriorVersions, dateToRetain) diff --git a/src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs index 6f8b36cd12..656a048bfc 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs @@ -5,12 +5,9 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class EmptiedRecycleBinNotification : INotification where T : class + public sealed class EmptiedRecycleBinNotification : INotification where T : class { - public EmptiedRecycleBinNotification(EventMessages messages) - { - Messages = messages; - } + public EmptiedRecycleBinNotification(EventMessages messages) => Messages = messages; public EventMessages Messages { get; } } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs index e6b55fc79d..e8d419bf8d 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs @@ -5,12 +5,11 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class EmptyingRecycleBinNotification : EmptiedRecycleBinNotification, ICancelableNotification where T : class + public sealed class EmptyingRecycleBinNotification : ICancelableNotification where T : class { - public EmptyingRecycleBinNotification(EventMessages messages) - : base(messages) - { - } + public EmptyingRecycleBinNotification(EventMessages messages) => Messages = messages; + + public EventMessages Messages { get; } public bool Cancel { get; set; } } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MovedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MovedNotification.cs index d0b934a1b5..f483b95a14 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/MovedNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/MovedNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class MovedNotification : ObjectNotification>> + public sealed class MovedNotification : ObjectNotification>> { public MovedNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MovedToRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MovedToRecycleBinNotification.cs index 785484152a..a4bb7138e7 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/MovedToRecycleBinNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/MovedToRecycleBinNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class MovedToRecycleBinNotification : ObjectNotification>> + public sealed class MovedToRecycleBinNotification : ObjectNotification>> { public MovedToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MovingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MovingNotification.cs index e3f567346e..bd72c759d7 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/MovingNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/MovingNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class MovingNotification : CancelableObjectNotification>> + public sealed class MovingNotification : CancelableObjectNotification>> { public MovingNotification(MoveEventInfo target, EventMessages messages) : base(new[] {target}, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MovingToRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MovingToRecycleBinNotification.cs index 9984136d74..de36871801 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/MovingToRecycleBinNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/MovingToRecycleBinNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class MovingToRecycleBinNotification : CancelableObjectNotification>> + public sealed class MovingToRecycleBinNotification : CancelableObjectNotification>> { public MovingToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/PublishedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/PublishedNotification.cs index fcb4d5de34..a0f584cdb6 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/PublishedNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/PublishedNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class PublishedNotification : EnumerableObjectNotification + public sealed class PublishedNotification : EnumerableObjectNotification { public PublishedNotification(T target, EventMessages messages) : base(target, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/PublishingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/PublishingNotification.cs index 60972e441d..f8ede11ba2 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/PublishingNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/PublishingNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class PublishingNotification : CancelableEnumerableObjectNotification + public sealed class PublishingNotification : CancelableEnumerableObjectNotification { public PublishingNotification(T target, EventMessages messages) : base(target, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/RolledBackNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/RolledBackNotification.cs index 6960133e0c..825daf6c94 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/RolledBackNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/RolledBackNotification.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class RolledBackNotification : ObjectNotification where T : class + public sealed class RolledBackNotification : ObjectNotification where T : class { public RolledBackNotification(T target, EventMessages messages) : base(target, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/RollingBackNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/RollingBackNotification.cs index ec2542d415..88739d4b10 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/RollingBackNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/RollingBackNotification.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class RollingBackNotification : CancelableObjectNotification where T : class + public sealed class RollingBackNotification : CancelableObjectNotification where T : class { public RollingBackNotification(T target, EventMessages messages) : base(target, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SavedBlueprintNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SavedBlueprintNotification.cs index 5fde3145bc..440df1fd99 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/SavedBlueprintNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/SavedBlueprintNotification.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class SavedBlueprintNotification : ObjectNotification where T : class + public sealed class SavedBlueprintNotification : ObjectNotification where T : class { public SavedBlueprintNotification(T target, EventMessages messages) : base(target, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SavedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SavedNotification.cs index 6c1265c878..2599efb1a3 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/SavedNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/SavedNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class SavedNotification : EnumerableObjectNotification + public sealed class SavedNotification : EnumerableObjectNotification { public SavedNotification(T target, EventMessages messages) : base(target, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SavingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SavingNotification.cs index 3f1869a955..a70e577ee7 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/SavingNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/SavingNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class SavingNotification : CancelableEnumerableObjectNotification + public sealed class SavingNotification : CancelableEnumerableObjectNotification { public SavingNotification(T target, EventMessages messages) : base(target, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SendingToPublishNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SendingToPublishNotification.cs index 4d0b1d8782..9f6a83f11c 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/SendingToPublishNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/SendingToPublishNotification.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class SendingToPublishNotification : CancelableObjectNotification where T : class + public sealed class SendingToPublishNotification : CancelableObjectNotification where T : class { public SendingToPublishNotification(T target, EventMessages messages) : base(target, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SentToPublishNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SentToPublishNotification.cs index d870bbb5db..94642dd92d 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/SentToPublishNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/SentToPublishNotification.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class SentToPublishNotification : ObjectNotification where T : class + public sealed class SentToPublishNotification : ObjectNotification where T : class { public SentToPublishNotification(T target, EventMessages messages) : base(target, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SortedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SortedNotification.cs index 10e3c08e03..b1169bfb83 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/SortedNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/SortedNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class SortedNotification : EnumerableObjectNotification + public sealed class SortedNotification : EnumerableObjectNotification { public SortedNotification(IEnumerable target, EventMessages messages) : base(target, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SortingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SortingNotification.cs index 18430b353b..dabc0497fd 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/SortingNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/SortingNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class SortingNotification : CancelableEnumerableObjectNotification + public sealed class SortingNotification : CancelableEnumerableObjectNotification { public SortingNotification(IEnumerable target, EventMessages messages) : base(target, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UnpublishedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UnpublishedNotification.cs index 201d37ddfc..14321f46b3 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/UnpublishedNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/UnpublishedNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class UnpublishedNotification : EnumerableObjectNotification + public sealed class UnpublishedNotification : EnumerableObjectNotification { public UnpublishedNotification(T target, EventMessages messages) : base(target, messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UnpublishingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UnpublishingNotification.cs index ac997fad4a..4acf0a6b6f 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/UnpublishingNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/UnpublishingNotification.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public class UnpublishingNotification : CancelableEnumerableObjectNotification + public sealed class UnpublishingNotification : CancelableEnumerableObjectNotification { public UnpublishingNotification(T target, EventMessages messages) : base(target, messages) { From 78bb319c79f98e79998ad8bb41204baf7b16e9c2 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 8 Mar 2021 11:01:14 +0100 Subject: [PATCH 24/42] Re-introduce state in notifications and utilize it in content and media service --- .../Events/IStatefulNotification.cs | 9 ++ .../Events/NotificationExtensions.cs | 17 +++ .../Routing/RedirectTrackingHandler.cs | 31 ++--- .../Services/Implement/ContentService.cs | 110 +++++++++++------- .../Services/Implement/MediaService.cs | 45 ++++--- .../DeletedVersionsNotificationBase.cs | 2 +- .../EmptiedRecycleBinNotification.cs | 2 +- .../EmptyingRecycleBinNotification.cs | 2 +- .../Notifications/ObjectNotification.cs | 2 +- .../Notifications/StatefulNotification.cs | 22 ++++ 10 files changed, 162 insertions(+), 80 deletions(-) create mode 100644 src/Umbraco.Core/Events/IStatefulNotification.cs create mode 100644 src/Umbraco.Core/Events/NotificationExtensions.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/StatefulNotification.cs diff --git a/src/Umbraco.Core/Events/IStatefulNotification.cs b/src/Umbraco.Core/Events/IStatefulNotification.cs new file mode 100644 index 0000000000..dafebe6173 --- /dev/null +++ b/src/Umbraco.Core/Events/IStatefulNotification.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Umbraco.Cms.Core.Events +{ + public interface IStatefulNotification : INotification + { + IDictionary State { get; set; } + } +} diff --git a/src/Umbraco.Core/Events/NotificationExtensions.cs b/src/Umbraco.Core/Events/NotificationExtensions.cs new file mode 100644 index 0000000000..de51f016e4 --- /dev/null +++ b/src/Umbraco.Core/Events/NotificationExtensions.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Umbraco.Cms.Core.Events +{ + public static class NotificationExtensions + { + public static T WithState(this T notification, IDictionary state) where T : IStatefulNotification + { + notification.State = state; + return notification; + } + + public static T WithStateFrom(this T notification, TSource source) + where T : IStatefulNotification where TSource : IStatefulNotification + => notification.WithState(source.State); + } +} diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs index 75a554867d..1499897b11 100644 --- a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; @@ -34,51 +33,57 @@ namespace Umbraco.Cms.Core.Routing private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly IRedirectUrlService _redirectUrlService; private readonly IVariationContextAccessor _variationContextAccessor; - private readonly IRequestCache _requestCache; private const string NotificationStateKey = "Umbraco.Cms.Core.Routing.RedirectTrackingHandler"; - public RedirectTrackingHandler(IOptionsMonitor webRoutingSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService, IVariationContextAccessor variationContextAccessor, IRequestCache requestCache) + public RedirectTrackingHandler(IOptionsMonitor webRoutingSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService, IVariationContextAccessor variationContextAccessor) { _webRoutingSettings = webRoutingSettings; _publishedSnapshotAccessor = publishedSnapshotAccessor; _redirectUrlService = redirectUrlService; _variationContextAccessor = variationContextAccessor; - _requestCache = requestCache; } - public void Handle(PublishingNotification notification) => StoreOldRoutes(notification.PublishedEntities); + public void Handle(PublishingNotification notification) => StoreOldRoutes(notification.PublishedEntities, notification); - public void Handle(PublishedNotification notification) => CreateRedirectsForOldRoutes(); + public void Handle(PublishedNotification notification) => CreateRedirectsForOldRoutes(notification); - public void Handle(MovingNotification notification) => StoreOldRoutes(notification.MoveInfoCollection.Select(m => m.Entity)); + public void Handle(MovingNotification notification) => StoreOldRoutes(notification.MoveInfoCollection.Select(m => m.Entity), notification); - public void Handle(MovedNotification notification) => CreateRedirectsForOldRoutes(); + public void Handle(MovedNotification notification) => CreateRedirectsForOldRoutes(notification); - private void StoreOldRoutes(IEnumerable entities) + private void StoreOldRoutes(IEnumerable entities, IStatefulNotification notification) { // don't let the notification handlers kick in if Redirect Tracking is turned off in the config if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) return; - var oldRoutes = GetOldRoutes(); + var oldRoutes = GetOldRoutes(notification); foreach (var entity in entities) { StoreOldRoute(entity, oldRoutes); } } - private void CreateRedirectsForOldRoutes() + private void CreateRedirectsForOldRoutes(IStatefulNotification notification) { // don't let the notification handlers kick in if Redirect Tracking is turned off in the config if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) return; - var oldRoutes = GetOldRoutes(); + var oldRoutes = GetOldRoutes(notification); CreateRedirects(oldRoutes); } - private OldRoutesDictionary GetOldRoutes() => (OldRoutesDictionary)_requestCache.Get(NotificationStateKey, () => new OldRoutesDictionary()); + private OldRoutesDictionary GetOldRoutes(IStatefulNotification notification) + { + if (notification.State.ContainsKey(NotificationStateKey) == false) + { + notification.State[NotificationStateKey] = new OldRoutesDictionary(); + } + + return (OldRoutesDictionary)notification.State[NotificationStateKey]; + } private void StoreOldRoute(IContent entity, OldRoutesDictionary oldRoutes) { diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index 0dfc864acb..01474d2d72 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -748,7 +748,8 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(content, evtMsgs))) + var savingNotification = new SavingNotification(content, evtMsgs); + if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -773,7 +774,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - scope.Notifications.Publish(new SavedNotification(content, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(content, evtMsgs).WithStateFrom(savingNotification)); } var changeType = TreeChangeTypes.RefreshNode; scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); @@ -802,7 +803,8 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(contentsA, evtMsgs))) + var savingNotification = new SavingNotification(contentsA, evtMsgs); + if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -822,7 +824,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - scope.Notifications.Publish(new SavedNotification(contentsA, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(contentsA, evtMsgs).WithStateFrom(savingNotification)); } scope.Events.Dispatch(TreeChanged, this, treeChanges.ToEventArgs()); Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Saved multiple content"); @@ -866,7 +868,8 @@ namespace Umbraco.Cms.Core.Services.Implement var allLangs = _languageRepository.GetMany().ToList(); - if (scope.Notifications.PublishCancelable(new SavingNotification(content, evtMsgs))) + var savingNotification = new SavingNotification(content, evtMsgs); + if (scope.Notifications.PublishCancelable(savingNotification)) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); } @@ -881,7 +884,7 @@ namespace Umbraco.Cms.Core.Services.Implement // we don't care about the response here, this response will be rechecked below but we need to set the culture info values now. content.PublishCulture(impact); - var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, userId, raiseEvents); + var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId, raiseEvents); scope.Complete(); return result; } @@ -906,7 +909,8 @@ namespace Umbraco.Cms.Core.Services.Implement var evtMsgs = EventMessagesFactory.Get(); - if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(content, evtMsgs))) + var savingNotification = new SavingNotification(content, evtMsgs); + if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); } @@ -929,7 +933,7 @@ namespace Umbraco.Cms.Core.Services.Implement foreach (var impact in impacts) content.PublishCulture(impact); - var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, userId, raiseEvents); + var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId, raiseEvents); scope.Complete(); return result; } @@ -971,7 +975,8 @@ namespace Umbraco.Cms.Core.Services.Implement var allLangs = _languageRepository.GetMany().ToList(); - if (scope.Notifications.PublishCancelable(new SavingNotification(content, evtMsgs))) + var savingNotification = new SavingNotification(content, evtMsgs); + if (scope.Notifications.PublishCancelable(savingNotification)) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); } @@ -985,7 +990,7 @@ namespace Umbraco.Cms.Core.Services.Implement // to be non-routable so that when it's re-published all variants were as they were. content.PublishedState = PublishedState.Unpublishing; - var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, userId); + var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId); scope.Complete(); return result; } @@ -999,7 +1004,7 @@ namespace Umbraco.Cms.Core.Services.Implement var removed = content.UnpublishCulture(culture); //save and publish any changes - var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, userId); + var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId); scope.Complete(); @@ -1042,14 +1047,15 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); - if (scope.Notifications.PublishCancelable(new SavingNotification(content, evtMsgs))) + var savingNotification = new SavingNotification(content, evtMsgs); + if (scope.Notifications.PublishCancelable(savingNotification)) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); } var allLangs = _languageRepository.GetMany().ToList(); - var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, userId, raiseEvents); + var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId, raiseEvents); scope.Complete(); return result; } @@ -1060,7 +1066,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// /// - /// + /// /// /// /// @@ -1074,6 +1080,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// private PublishResult CommitDocumentChangesInternal(IScope scope, IContent content, EventMessages evtMsgs, IReadOnlyCollection allLangs, + IDictionary notificationState, int userId = Cms.Core.Constants.Security.SuperUserId, bool raiseEvents = true, bool branchOne = false, bool branchRoot = false) { @@ -1127,7 +1134,7 @@ namespace Umbraco.Cms.Core.Services.Implement : null; // ensure that the document can be published, and publish handling events, business rules, etc - publishResult = StrategyCanPublish(scope, content, /*checkPath:*/ (!branchOne || branchRoot), culturesPublishing, culturesUnpublishing, evtMsgs, allLangs); + publishResult = StrategyCanPublish(scope, content, /*checkPath:*/ (!branchOne || branchRoot), culturesPublishing, culturesUnpublishing, evtMsgs, allLangs, notificationState); if (publishResult.Success) { // note: StrategyPublish flips the PublishedState to Publishing! @@ -1212,7 +1219,7 @@ namespace Umbraco.Cms.Core.Services.Implement // raise the Saved event, always if (raiseEvents) { - scope.Notifications.Publish(new SavedNotification(content, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(content, evtMsgs).WithState(notificationState)); } if (unpublishing) // we have tried to unpublish - won't happen in a branch @@ -1220,7 +1227,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (unpublishResult.Success) // and succeeded, trigger events { // events and audit - scope.Notifications.Publish(new UnpublishedNotification(content, evtMsgs)); + scope.Notifications.Publish(new UnpublishedNotification(content, evtMsgs).WithState(notificationState)); scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, TreeChangeTypes.RefreshBranch).ToEventArgs()); if (culturesUnpublishing != null) @@ -1275,7 +1282,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!branchOne) // for branches, handled by SaveAndPublishBranch { scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); - scope.Notifications.Publish(new PublishedNotification(content, evtMsgs)); + scope.Notifications.Publish(new PublishedNotification(content, evtMsgs).WithState(notificationState)); } // it was not published and now is... descendants that were 'published' (but @@ -1284,7 +1291,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!branchOne && isNew == false && previouslyPublished == false && HasChildren(content.Id)) { var descendants = GetPublishedDescendantsLocked(content).ToArray(); - scope.Notifications.Publish(new PublishedNotification(descendants, evtMsgs)); + scope.Notifications.Publish(new PublishedNotification(descendants, evtMsgs).WithState(notificationState)); } switch (publishResult.Result) @@ -1377,7 +1384,8 @@ namespace Umbraco.Cms.Core.Services.Implement if (pendingCultures.Count == 0) continue; //shouldn't happen but no point in processing this document if there's nothing there - if (scope.Notifications.PublishCancelable(new SavingNotification(d, evtMsgs))) + var savingNotification = new SavingNotification(d, evtMsgs); + if (scope.Notifications.PublishCancelable(savingNotification)) { results.Add(new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, d)); continue; @@ -1391,7 +1399,7 @@ namespace Umbraco.Cms.Core.Services.Implement d.UnpublishCulture(c); } - var result = CommitDocumentChangesInternal(scope, d, evtMsgs, allLangs.Value, d.WriterId); + var result = CommitDocumentChangesInternal(scope, d, evtMsgs, allLangs.Value, savingNotification.State, d.WriterId); if (result.Success == false) _logger.LogError(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result); results.Add(result); @@ -1437,7 +1445,8 @@ namespace Umbraco.Cms.Core.Services.Implement if (pendingCultures.Count == 0) continue; //shouldn't happen but no point in processing this document if there's nothing there - if (scope.Notifications.PublishCancelable(new SavingNotification(d, evtMsgs))) + var savingNotification = new SavingNotification(d, evtMsgs); + if (scope.Notifications.PublishCancelable(savingNotification)) { results.Add(new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, d)); continue; @@ -1470,7 +1479,7 @@ namespace Umbraco.Cms.Core.Services.Implement else if (!publishing) result = new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, d); else - result = CommitDocumentChangesInternal(scope, d, evtMsgs, allLangs.Value, d.WriterId); + result = CommitDocumentChangesInternal(scope, d, evtMsgs, allLangs.Value, savingNotification.State, d.WriterId); if (result.Success == false) _logger.LogError(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result); @@ -1718,7 +1727,8 @@ namespace Umbraco.Cms.Core.Services.Implement if (culturesToPublish.Count == 0) // empty = already published return new PublishResult(PublishResultType.SuccessPublishAlready, evtMsgs, document); - if (scope.Notifications.PublishCancelable(new SavingNotification(document, evtMsgs))) + var savingNotification = new SavingNotification(document, evtMsgs); + if (scope.Notifications.PublishCancelable(savingNotification)) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, document); } @@ -1730,7 +1740,7 @@ namespace Umbraco.Cms.Core.Services.Implement return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, document); } - var result = CommitDocumentChangesInternal(scope, document, evtMsgs, allLangs, userId, branchOne: true, branchRoot: isRoot); + var result = CommitDocumentChangesInternal(scope, document, evtMsgs, allLangs, savingNotification.State, userId, branchOne: true, branchRoot: isRoot); if (result.Success) publishedDocuments.Add(document); return result; @@ -1814,7 +1824,8 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (scope.Notifications.PublishCancelable(new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate))) + var deletingVersionsNotification = new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate); + if (scope.Notifications.PublishCancelable(deletingVersionsNotification)) { scope.Complete(); return; @@ -1823,7 +1834,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); _documentRepository.DeleteVersions(id, versionDate); - scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate)); + scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate).WithStateFrom(deletingVersionsNotification)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version date)"); scope.Complete(); @@ -1844,7 +1855,8 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (scope.Notifications.PublishCancelable(new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId))) + var deletingVersionsNotification = new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId); + if (scope.Notifications.PublishCancelable(deletingVersionsNotification)) { scope.Complete(); return; @@ -1861,7 +1873,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (c.VersionId != versionId && c.PublishedVersionId != versionId) // don't delete the current or published version _documentRepository.DeleteVersion(versionId); - scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId)); + scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId).WithStateFrom(deletingVersionsNotification)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version)"); scope.Complete(); @@ -1885,7 +1897,8 @@ namespace Umbraco.Cms.Core.Services.Implement var originalPath = content.Path; var moveEventInfo = new MoveEventInfo(content, originalPath, Cms.Core.Constants.System.RecycleBinContent); - if (scope.Notifications.PublishCancelable(new MovingToRecycleBinNotification(moveEventInfo, evtMsgs))) + var movingToRecycleBinNotification = new MovingToRecycleBinNotification(moveEventInfo, evtMsgs); + if (scope.Notifications.PublishCancelable(movingToRecycleBinNotification)) { scope.Complete(); return OperationResult.Cancel(evtMsgs); // causes rollback @@ -1904,7 +1917,7 @@ namespace Umbraco.Cms.Core.Services.Implement .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs)); + scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs).WithStateFrom(movingToRecycleBinNotification)); Audit(AuditType.Move, userId, content.Id, "Moved to recycle bin"); scope.Complete(); @@ -1947,7 +1960,8 @@ namespace Umbraco.Cms.Core.Services.Implement var moveEventInfo = new MoveEventInfo(content, content.Path, parentId); - if (scope.Notifications.PublishCancelable(new MovingNotification(moveEventInfo, evtMsgs))) + var movingNotification = new MovingNotification(moveEventInfo, evtMsgs); + if (scope.Notifications.PublishCancelable(movingNotification)) { scope.Complete(); return; // causes rollback @@ -1976,7 +1990,7 @@ namespace Umbraco.Cms.Core.Services.Implement .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - scope.Notifications.Publish(new MovedNotification(moveInfo, evtMsgs)); + scope.Notifications.Publish(new MovedNotification(moveInfo, evtMsgs).WithStateFrom(movingNotification)); Audit(AuditType.Move, userId, content.Id); @@ -2061,7 +2075,8 @@ namespace Umbraco.Cms.Core.Services.Implement // are managed by Delete, and not here. // no idea what those events are for, keep a simplified version - if (scope.Notifications.PublishCancelable(new EmptyingRecycleBinNotification(evtMsgs))) + var emptyingRecycleBinNotification = new EmptyingRecycleBinNotification(evtMsgs); + if (scope.Notifications.PublishCancelable(emptyingRecycleBinNotification)) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -2076,7 +2091,7 @@ namespace Umbraco.Cms.Core.Services.Implement deleted.Add(content); } - scope.Notifications.Publish(new EmptiedRecycleBinNotification(evtMsgs)); + scope.Notifications.Publish(new EmptiedRecycleBinNotification(evtMsgs).WithStateFrom(emptyingRecycleBinNotification)); scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange(x, TreeChangeTypes.Remove)).ToEventArgs()); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.RecycleBinContent, "Recycle bin emptied"); @@ -2228,7 +2243,8 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (scope.Notifications.PublishCancelable(new SendingToPublishNotification(content, evtMsgs))) + var sendingToPublishNotification = new SendingToPublishNotification(content, evtMsgs); + if (scope.Notifications.PublishCancelable(sendingToPublishNotification)) { scope.Complete(); return false; @@ -2253,7 +2269,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!saveResult.Success) return saveResult.Success; - scope.Notifications.Publish(new SentToPublishNotification(content, evtMsgs)); + scope.Notifications.Publish(new SentToPublishNotification(content, evtMsgs).WithStateFrom(sendingToPublishNotification)); if (culturesChanging != null) Audit(AuditType.SendToPublishVariant, userId, content.Id, $"Send To Publish for cultures: {culturesChanging}", culturesChanging); @@ -2325,16 +2341,18 @@ namespace Umbraco.Cms.Core.Services.Implement private OperationResult Sort(IScope scope, IContent[] itemsA, int userId, EventMessages evtMsgs, bool raiseEvents) { + var sortingNotification = new SortingNotification(itemsA, evtMsgs); + var savingNotification = new SavingNotification(itemsA, evtMsgs); if (raiseEvents) { // raise cancelable sorting event - if (scope.Notifications.PublishCancelable(new SortingNotification(itemsA, evtMsgs))) + if (scope.Notifications.PublishCancelable(sortingNotification)) { return OperationResult.Cancel(evtMsgs); } // raise cancelable saving event - if (scope.Notifications.PublishCancelable(new SavingNotification(itemsA, evtMsgs))) + if (scope.Notifications.PublishCancelable(savingNotification)) { return OperationResult.Cancel(evtMsgs); } @@ -2371,8 +2389,8 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { //first saved, then sorted - scope.Notifications.Publish(new SavedNotification(itemsA, evtMsgs)); - scope.Notifications.Publish(new SortedNotification(itemsA, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(itemsA, evtMsgs).WithStateFrom(savingNotification)); + scope.Notifications.Publish(new SortedNotification(itemsA, evtMsgs).WithStateFrom(sortingNotification)); } scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)).ToEventArgs()); @@ -2482,12 +2500,13 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// /// + /// /// private PublishResult StrategyCanPublish(IScope scope, IContent content, bool checkPath, IReadOnlyList culturesPublishing, - IReadOnlyCollection culturesUnpublishing, EventMessages evtMsgs, IReadOnlyCollection allLangs) + IReadOnlyCollection culturesUnpublishing, EventMessages evtMsgs, IReadOnlyCollection allLangs, IDictionary notificationState) { // raise Publishing notification - if (scope.Notifications.PublishCancelable(new PublishingNotification(content, evtMsgs))) + if (scope.Notifications.PublishCancelable(new PublishingNotification(content, evtMsgs).WithState(notificationState))) { _logger.LogInformation("Document {ContentName} (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "publishing was cancelled"); return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); @@ -3010,7 +3029,8 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (scope.Notifications.PublishCancelable(new RollingBackNotification(content, evtMsgs))) + var rollingBackNotification = new RollingBackNotification(content, evtMsgs); + if (scope.Notifications.PublishCancelable(rollingBackNotification)) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -3030,7 +3050,7 @@ namespace Umbraco.Cms.Core.Services.Implement } else { - scope.Notifications.Publish(new RolledBackNotification(content, evtMsgs)); + scope.Notifications.Publish(new RolledBackNotification(content, evtMsgs).WithStateFrom(rollingBackNotification)); //Logging & Audit message _logger.LogInformation("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId); diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index 6a904bced9..40d1cb0c9b 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -296,14 +296,15 @@ namespace Umbraco.Cms.Core.Services.Implement if (withIdentity) { - if (scope.Notifications.PublishCancelable(new SavingNotification(media, evtMsgs))) + var savingNotification = new SavingNotification(media, evtMsgs); + if (scope.Notifications.PublishCancelable(savingNotification)) { return; } _mediaRepository.Save(media); - scope.Notifications.Publish(new SavedNotification(media, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(media, evtMsgs).WithStateFrom(savingNotification)); scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, TreeChangeTypes.RefreshNode).ToEventArgs()); } @@ -661,7 +662,8 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(media, evtMsgs))) + var savingNotification = new SavingNotification(media, evtMsgs); + if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -683,7 +685,7 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaRepository.Save(media); if (raiseEvents) { - scope.Notifications.Publish(new SavedNotification(media, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(media, evtMsgs).WithStateFrom(savingNotification)); } var changeType = TreeChangeTypes.RefreshNode; scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, changeType).ToEventArgs()); @@ -708,7 +710,8 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(mediasA, evtMsgs))) + var savingNotification = new SavingNotification(mediasA, evtMsgs); + if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -726,7 +729,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - scope.Notifications.Publish(new SavedNotification(mediasA, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(mediasA, evtMsgs).WithStateFrom(savingNotification)); } scope.Events.Dispatch(TreeChanged, this, treeChanges.ToEventArgs()); Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Bulk save media"); @@ -819,7 +822,8 @@ namespace Umbraco.Cms.Core.Services.Implement { var evtMsgs = EventMessagesFactory.Get(); - if (scope.Notifications.PublishCancelable(new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate))) + var deletingVersionsNotification = new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate); + if (scope.Notifications.PublishCancelable(deletingVersionsNotification)) { return; } @@ -828,7 +832,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.MediaTree); _mediaRepository.DeleteVersions(id, versionDate); - scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate)); + scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate).WithStateFrom(deletingVersionsNotification)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete Media by version date"); } @@ -846,7 +850,8 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (scope.Notifications.PublishCancelable(new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId))) + var deletingVersionsNotification = new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId); + if (scope.Notifications.PublishCancelable(deletingVersionsNotification)) { scope.Complete(); return; @@ -864,7 +869,7 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaRepository.DeleteVersion(versionId); - scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId)); + scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId).WithStateFrom(deletingVersionsNotification)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete Media by version"); scope.Complete(); @@ -896,7 +901,8 @@ namespace Umbraco.Cms.Core.Services.Implement var moveEventInfo = new MoveEventInfo(media, originalPath, Cms.Core.Constants.System.RecycleBinMedia); - if (scope.Notifications.PublishCancelable(new MovingToRecycleBinNotification(moveEventInfo, evtMsgs))) + var movingToRecycleBinNotification = new MovingToRecycleBinNotification(moveEventInfo, evtMsgs); + if (scope.Notifications.PublishCancelable(movingToRecycleBinNotification)) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -906,7 +912,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, TreeChangeTypes.RefreshBranch).ToEventArgs()); var moveInfo = moves.Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)).ToArray(); - scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs)); + scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs).WithStateFrom(movingToRecycleBinNotification)); Audit(AuditType.Move, userId, media.Id, "Move Media to recycle bin"); scope.Complete(); @@ -943,7 +949,8 @@ namespace Umbraco.Cms.Core.Services.Implement throw new InvalidOperationException("Parent does not exist or is trashed."); // causes rollback var moveEventInfo = new MoveEventInfo(media, media.Path, parentId); - if (scope.Notifications.PublishCancelable(new MovingNotification(moveEventInfo, evtMsgs))) + var movingNotification = new MovingNotification(moveEventInfo, evtMsgs); + if (scope.Notifications.PublishCancelable(movingNotification)) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -959,7 +966,7 @@ namespace Umbraco.Cms.Core.Services.Implement var moveInfo = moves //changes .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - scope.Notifications.Publish(new MovedNotification(moveInfo, evtMsgs)); + scope.Notifications.Publish(new MovedNotification(moveInfo, evtMsgs).WithStateFrom(movingNotification)); Audit(AuditType.Move, userId, media.Id); scope.Complete(); } @@ -1040,7 +1047,8 @@ namespace Umbraco.Cms.Core.Services.Implement // v7 EmptyingRecycleBin and EmptiedRecycleBin events are greatly simplified since // each deleted items will have its own deleting/deleted events. so, files and such // are managed by Delete, and not here. - if (scope.Notifications.PublishCancelable(new EmptyingRecycleBinNotification(evtMsgs))) + var emptyingRecycleBinNotification = new EmptyingRecycleBinNotification(evtMsgs); + if (scope.Notifications.PublishCancelable(emptyingRecycleBinNotification)) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -1054,7 +1062,7 @@ namespace Umbraco.Cms.Core.Services.Implement DeleteLocked(scope, media, evtMsgs); deleted.Add(media); } - scope.Notifications.Publish(new EmptiedRecycleBinNotification(new EventMessages())); + scope.Notifications.Publish(new EmptiedRecycleBinNotification(new EventMessages()).WithStateFrom(emptyingRecycleBinNotification)); scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange(x, TreeChangeTypes.Remove)).ToEventArgs()); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.RecycleBinMedia, "Empty Media recycle bin"); scope.Complete(); @@ -1084,7 +1092,8 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (raiseEvents && scope.Notifications.PublishCancelable(new SavingNotification(itemsA, evtMsgs))) + var savingNotification = new SavingNotification(itemsA, evtMsgs); + if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return false; @@ -1113,7 +1122,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - scope.Notifications.Publish(new SavedNotification(itemsA, evtMsgs)); + scope.Notifications.Publish(new SavedNotification(itemsA, evtMsgs).WithStateFrom(savingNotification)); } scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)).ToEventArgs()); Audit(AuditType.Sort, userId, 0); diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotificationBase.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotificationBase.cs index a5b4654245..938a174fbe 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotificationBase.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotificationBase.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public abstract class DeletedVersionsNotificationBase : INotification where T : class + public abstract class DeletedVersionsNotificationBase : StatefulNotification where T : class { protected DeletedVersionsNotificationBase(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs index 656a048bfc..4ade0bea82 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class EmptiedRecycleBinNotification : INotification where T : class + public sealed class EmptiedRecycleBinNotification : StatefulNotification where T : class { public EmptiedRecycleBinNotification(EventMessages messages) => Messages = messages; diff --git a/src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs index e8d419bf8d..7d6293cbd7 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class EmptyingRecycleBinNotification : ICancelableNotification where T : class + public sealed class EmptyingRecycleBinNotification : StatefulNotification, ICancelableNotification where T : class { public EmptyingRecycleBinNotification(EventMessages messages) => Messages = messages; diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ObjectNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ObjectNotification.cs index a6b84e875d..16c57f210e 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/ObjectNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/ObjectNotification.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public abstract class ObjectNotification : INotification where T : class + public abstract class ObjectNotification : StatefulNotification where T : class { protected ObjectNotification(T target, EventMessages messages) { diff --git a/src/Umbraco.Infrastructure/Services/Notifications/StatefulNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/StatefulNotification.cs new file mode 100644 index 0000000000..07831611fe --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/StatefulNotification.cs @@ -0,0 +1,22 @@ +// Copyright (c) Umbraco. + +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public abstract class StatefulNotification : IStatefulNotification + { + private IDictionary _state; + + /// + /// This can be used by event subscribers to store state in the notification so they easily deal with custom state data between + /// a starting ("ing") and an ending ("ed") notification + /// + public IDictionary State + { + get => _state ??= new Dictionary(); + set => _state = value; + } + } +} From 52812672fc23ccf77d0b91c6e132c3e09f10b1b0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 10 Mar 2021 15:22:45 +1100 Subject: [PATCH 25/42] Allow for having appsettings.Local.json this file is not checked in and can be used to overwrite any custom settings that are in the repo without worrying about accidentally checking in developer settings as you work. --- .gitignore | 1 + src/Umbraco.Web.UI.NetCore/Program.cs | 19 +++++++++++-------- .../Umbraco.Web.UI.NetCore.csproj | 1 + src/Umbraco.Web.UI.NetCore/appsettings.json | 4 ++-- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 0cffac8343..5f2768f575 100644 --- a/.gitignore +++ b/.gitignore @@ -201,3 +201,4 @@ src/Umbraco.Tests/TEMP/ /src/Umbraco.Web.UI/config/umbracoSettings.config /src/Umbraco.Web.UI.NetCore/Umbraco/models/* +/src/Umbraco.Web.UI.NetCore/appsettings.local.json diff --git a/src/Umbraco.Web.UI.NetCore/Program.cs b/src/Umbraco.Web.UI.NetCore/Program.cs index 89ce7d92bc..7a6ee4dcfd 100644 --- a/src/Umbraco.Web.UI.NetCore/Program.cs +++ b/src/Umbraco.Web.UI.NetCore/Program.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -7,18 +8,20 @@ namespace Umbraco.Cms.Web.UI.NetCore public class Program { public static void Main(string[] args) - { - CreateHostBuilder(args) + => CreateHostBuilder(args) .Build() .Run(); - } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) - .ConfigureLogging(x => - { - x.ClearProviders(); - }) - .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); +#if DEBUG + .ConfigureAppConfiguration(config + => config.AddJsonFile( + "appsettings.Local.json", + optional: true, + reloadOnChange: true)) +#endif + .ConfigureLogging(x => x.ClearProviders()) + .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup()); } } diff --git a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj index 5aeb0e8bb1..2324c4c252 100644 --- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -81,6 +81,7 @@ false + false diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.json b/src/Umbraco.Web.UI.NetCore/appsettings.json index 382ee11590..0fae4fd724 100644 --- a/src/Umbraco.Web.UI.NetCore/appsettings.json +++ b/src/Umbraco.Web.UI.NetCore/appsettings.json @@ -38,8 +38,8 @@ "ConvertUrlsToAscii": "try" }, "RuntimeMinification": { - "dataFolder": "App_Data/TEMP/Smidge", - "version": "637432008251409860" + "dataFolder": "Umbraco/TEMP/Smidge", + "version": "1" }, "Security": { "KeepUserLoggedIn": false, From e6f73b26ce7a8ef67eb96326fa18049085bdcf8a Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Thu, 11 Mar 2021 14:09:23 +1100 Subject: [PATCH 26/42] Update src/Umbraco.Web.UI.NetCore/appsettings.json --- src/Umbraco.Web.UI.NetCore/appsettings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.json b/src/Umbraco.Web.UI.NetCore/appsettings.json index 0fae4fd724..72af7afb5b 100644 --- a/src/Umbraco.Web.UI.NetCore/appsettings.json +++ b/src/Umbraco.Web.UI.NetCore/appsettings.json @@ -38,7 +38,7 @@ "ConvertUrlsToAscii": "try" }, "RuntimeMinification": { - "dataFolder": "Umbraco/TEMP/Smidge", + "dataFolder": "Umbraco/Data/TEMP/Smidge", "version": "1" }, "Security": { @@ -71,4 +71,4 @@ } } } -} \ No newline at end of file +} From 06a202e30eeb646ea91aab5bcf1c1de2b52781aa Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 12 Mar 2021 21:48:24 +0100 Subject: [PATCH 27/42] Migrated CacheRefresher events to IEventAggregator pattern. --- .../Cache/ApplicationCacheRefresher.cs | 9 +- src/Umbraco.Core/Cache/CacheRefresherBase.cs | 34 ++--- .../Cache/CacheRefresherEventArgs.cs | 19 --- .../Cache/CacheRefresherNotificationBase.cs | 83 ++++++++++ .../Cache/ContentCacheRefresher.cs | 15 +- .../Cache/ContentTypeCacheRefresher.cs | 16 +- .../Cache/DataTypeCacheRefresher.cs | 15 +- .../Cache/DictionaryCacheRefresher.cs | 9 +- .../Cache/DomainCacheRefresher.cs | 13 +- .../Cache/JsonCacheRefresherBase.cs | 15 +- .../Cache/LanguageCacheRefresher.cs | 13 +- src/Umbraco.Core/Cache/MacroCacheRefresher.cs | 12 +- src/Umbraco.Core/Cache/MediaCacheRefresher.cs | 9 +- .../Cache/MemberCacheRefresher.cs | 9 +- .../Cache/MemberGroupCacheRefresher.cs | 9 +- .../Cache/PayloadCacheRefresherBase.cs | 13 +- .../Cache/PublicAccessCacheRefresher.cs | 9 +- .../Cache/RelationTypeCacheRefresher.cs | 9 +- .../Cache/TemplateCacheRefresher.cs | 9 +- src/Umbraco.Core/Cache/UserCacheRefresher.cs | 11 +- .../Cache/UserGroupCacheRefresher.cs | 9 +- src/Umbraco.Core/Umbraco.Core.csproj | 4 + .../UmbracoBuilder.Examine.cs} | 23 ++- .../ModelsBuilder/LiveModelsProvider.cs | 26 ++-- .../ModelsBuilder/OutOfDateModelsStatus.cs | 29 ++-- ...onent.cs => ExamineNotificationHandler.cs} | 142 ++++++++++-------- .../Testing/UmbracoIntegrationTest.cs | 2 +- .../Services/ContentEventsTests.cs | 69 +++++---- .../UmbracoBuilderExtensions.cs | 7 +- .../SignalR/PreviewHubComposer.cs | 13 -- ...ewHubComponent.cs => PreviewHubUpdater.cs} | 24 +-- ...acoBuilderDependencyInjectionExtensions.cs | 8 +- 32 files changed, 390 insertions(+), 297 deletions(-) delete mode 100644 src/Umbraco.Core/Cache/CacheRefresherEventArgs.cs create mode 100644 src/Umbraco.Core/Cache/CacheRefresherNotificationBase.cs rename src/Umbraco.Infrastructure/{Search/ExamineComposer.cs => DependencyInjection/UmbracoBuilder.Examine.cs} (69%) rename src/Umbraco.Infrastructure/Search/{ExamineComponent.cs => ExamineNotificationHandler.cs} (86%) delete mode 100644 src/Umbraco.Web.BackOffice/SignalR/PreviewHubComposer.cs rename src/Umbraco.Web.BackOffice/SignalR/{PreviewHubComponent.cs => PreviewHubUpdater.cs} (54%) diff --git a/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs b/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs index 360fd44ba8..b8dccd1f59 100644 --- a/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs @@ -1,17 +1,16 @@ using System; +using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Core.Cache { - public sealed class ApplicationCacheRefresher : CacheRefresherBase + public sealed class ApplicationCacheRefresher : CacheRefresherBase { - public ApplicationCacheRefresher(AppCaches appCaches) - : base(appCaches) + public ApplicationCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator) + : base(appCaches, eventAggregator) { } #region Define - protected override ApplicationCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("B15F34A1-BC1D-4F8B-8369-3222728AB4C8"); public override Guid RefresherUniqueId => UniqueId; diff --git a/src/Umbraco.Core/Cache/CacheRefresherBase.cs b/src/Umbraco.Core/Cache/CacheRefresherBase.cs index d3a09dbf8f..be4129f928 100644 --- a/src/Umbraco.Core/Cache/CacheRefresherBase.cs +++ b/src/Umbraco.Core/Cache/CacheRefresherBase.cs @@ -10,33 +10,21 @@ namespace Umbraco.Cms.Core.Cache /// /// The actual cache refresher type. /// The actual cache refresher type is used for strongly typed events. - public abstract class CacheRefresherBase : ICacheRefresher - where TInstanceType : class, ICacheRefresher + public abstract class CacheRefresherBase< TNotification> : ICacheRefresher + where TNotification : CacheRefresherNotificationBase, new() { /// /// Initializes a new instance of the . /// /// A cache helper. - protected CacheRefresherBase(AppCaches appCaches) + protected CacheRefresherBase(AppCaches appCaches, IEventAggregator eventAggregator) { AppCaches = appCaches; + EventAggregator = eventAggregator; } - /// - /// Triggers when the cache is updated on the server. - /// - /// - /// Triggers on each server configured for an Umbraco project whenever a cache refresher is updated. - /// - public static event TypedEventHandler CacheUpdated; - #region Define - /// - /// Gets the typed 'this' for events. - /// - protected abstract TInstanceType This { get; } - /// /// Gets the unique identifier of the refresher. /// @@ -56,7 +44,7 @@ namespace Umbraco.Cms.Core.Cache /// public virtual void RefreshAll() { - OnCacheUpdated(This, new CacheRefresherEventArgs(null, MessageType.RefreshAll)); + OnCacheUpdated(new TNotification().Init(null, MessageType.RefreshAll)); } /// @@ -65,7 +53,7 @@ namespace Umbraco.Cms.Core.Cache /// The entity's identifier. public virtual void Refresh(int id) { - OnCacheUpdated(This, new CacheRefresherEventArgs(id, MessageType.RefreshById)); + OnCacheUpdated(new TNotification().Init(id, MessageType.RefreshById)); } /// @@ -74,7 +62,7 @@ namespace Umbraco.Cms.Core.Cache /// The entity's identifier. public virtual void Refresh(Guid id) { - OnCacheUpdated(This, new CacheRefresherEventArgs(id, MessageType.RefreshById)); + OnCacheUpdated(new TNotification().Init(id, MessageType.RefreshById)); } /// @@ -83,7 +71,7 @@ namespace Umbraco.Cms.Core.Cache /// The entity's identifier. public virtual void Remove(int id) { - OnCacheUpdated(This, new CacheRefresherEventArgs(id, MessageType.RemoveById)); + OnCacheUpdated(new TNotification().Init(id, MessageType.RemoveById)); } #endregion @@ -95,6 +83,8 @@ namespace Umbraco.Cms.Core.Cache /// protected AppCaches AppCaches { get; } + protected IEventAggregator EventAggregator { get; } + /// /// Clears the cache for all repository entities of a specified type. /// @@ -110,9 +100,9 @@ namespace Umbraco.Cms.Core.Cache /// /// The event sender. /// The event arguments. - protected static void OnCacheUpdated(TInstanceType sender, CacheRefresherEventArgs args) + protected void OnCacheUpdated(CacheRefresherNotificationBase notification) { - CacheUpdated?.Invoke(sender, args); + EventAggregator.Publish(notification); } #endregion diff --git a/src/Umbraco.Core/Cache/CacheRefresherEventArgs.cs b/src/Umbraco.Core/Cache/CacheRefresherEventArgs.cs deleted file mode 100644 index e1d04a7095..0000000000 --- a/src/Umbraco.Core/Cache/CacheRefresherEventArgs.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using Umbraco.Cms.Core.Sync; - -namespace Umbraco.Cms.Core.Cache -{ - /// - /// Event args for cache refresher updates - /// - public class CacheRefresherEventArgs : EventArgs - { - public CacheRefresherEventArgs(object msgObject, MessageType type) - { - MessageType = type; - MessageObject = msgObject; - } - public object MessageObject { get; private set; } - public MessageType MessageType { get; private set; } - } -} diff --git a/src/Umbraco.Core/Cache/CacheRefresherNotificationBase.cs b/src/Umbraco.Core/Cache/CacheRefresherNotificationBase.cs new file mode 100644 index 0000000000..7818678cfe --- /dev/null +++ b/src/Umbraco.Core/Cache/CacheRefresherNotificationBase.cs @@ -0,0 +1,83 @@ +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + /// + /// Event args for cache refresher updates + /// + public abstract class CacheRefresherNotificationBase : INotification + { + public CacheRefresherNotificationBase Init(object msgObject, MessageType type) + { + MessageType = type; + MessageObject = msgObject; + + return this; + } + public object MessageObject { get; private set; } + public MessageType MessageType { get; private set;} + } + public class DataTypeCacheRefresherNotification : CacheRefresherNotificationBase + { + } + + public class UserCacheRefresherNotification : CacheRefresherNotificationBase + { + } + + + public class ContentCacheRefresherNotification : CacheRefresherNotificationBase + { + } + + public class TemplateCacheRefresherNotification : CacheRefresherNotificationBase + { + } + + public class RelationTypeCacheRefresherNotification : CacheRefresherNotificationBase + { + } + + public class PublicAccessCacheRefresherNotification : CacheRefresherNotificationBase + { + } + + public class MemberGroupCacheRefresherNotification : CacheRefresherNotificationBase + { + } + + public class MemberCacheRefresherNotification : CacheRefresherNotificationBase + { + } + + public class MediaCacheRefresherNotification : CacheRefresherNotificationBase + { + } + + public class UserGroupCacheRefresherNotification : CacheRefresherNotificationBase + { + } + + public class LanguageCacheRefresherNotification : CacheRefresherNotificationBase + { + } + public class MacroCacheRefresherNotification : CacheRefresherNotificationBase + { + } + + public class DomainCacheRefresherNotification : CacheRefresherNotificationBase + { + } + + public class ContentTypeCacheRefresherNotification : CacheRefresherNotificationBase + { + } + + public class ApplicationCacheRefresherNotification : CacheRefresherNotificationBase + { + } + public class DictionaryCacheRefresherNotification : CacheRefresherNotificationBase + { + } +} diff --git a/src/Umbraco.Core/Cache/ContentCacheRefresher.cs b/src/Umbraco.Core/Cache/ContentCacheRefresher.cs index e77fa7abef..42674d689b 100644 --- a/src/Umbraco.Core/Cache/ContentCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/ContentCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PublishedCache; @@ -11,14 +12,20 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Cache { - public sealed class ContentCacheRefresher : PayloadCacheRefresherBase + public sealed class ContentCacheRefresher : PayloadCacheRefresherBase { private readonly IPublishedSnapshotService _publishedSnapshotService; private readonly IIdKeyMap _idKeyMap; private readonly IDomainService _domainService; - public ContentCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IIdKeyMap idKeyMap, IDomainService domainService) - : base(appCaches, serializer) + public ContentCacheRefresher( + AppCaches appCaches, + IJsonSerializer serializer, + IPublishedSnapshotService publishedSnapshotService, + IIdKeyMap idKeyMap, + IDomainService domainService, + IEventAggregator eventAggregator) + : base(appCaches, serializer, eventAggregator) { _publishedSnapshotService = publishedSnapshotService; _idKeyMap = idKeyMap; @@ -27,8 +34,6 @@ namespace Umbraco.Cms.Core.Cache #region Define - protected override ContentCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("900A4FBE-DF3C-41E6-BB77-BE896CD158EA"); public override Guid RefresherUniqueId => UniqueId; diff --git a/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs index 8a1ba1234e..00838865ce 100644 --- a/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Persistence.Repositories; @@ -11,15 +12,22 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Cache { - public sealed class ContentTypeCacheRefresher : PayloadCacheRefresherBase + public sealed class ContentTypeCacheRefresher : PayloadCacheRefresherBase { private readonly IPublishedSnapshotService _publishedSnapshotService; private readonly IPublishedModelFactory _publishedModelFactory; private readonly IContentTypeCommonRepository _contentTypeCommonRepository; private readonly IIdKeyMap _idKeyMap; - public ContentTypeCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IPublishedModelFactory publishedModelFactory, IIdKeyMap idKeyMap, IContentTypeCommonRepository contentTypeCommonRepository) - : base(appCaches, serializer) + public ContentTypeCacheRefresher( + AppCaches appCaches, + IJsonSerializer serializer, + IPublishedSnapshotService publishedSnapshotService, + IPublishedModelFactory publishedModelFactory, + IIdKeyMap idKeyMap, + IContentTypeCommonRepository contentTypeCommonRepository, + IEventAggregator eventAggregator) + : base(appCaches, serializer, eventAggregator) { _publishedSnapshotService = publishedSnapshotService; _publishedModelFactory = publishedModelFactory; @@ -29,8 +37,6 @@ namespace Umbraco.Cms.Core.Cache #region Define - protected override ContentTypeCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("6902E22C-9C10-483C-91F3-66B7CAE9E2F5"); public override Guid RefresherUniqueId => UniqueId; diff --git a/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs index d5e11e17d3..0eca1a0c20 100644 --- a/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs @@ -1,4 +1,5 @@ using System; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PropertyEditors.ValueConverters; @@ -9,14 +10,20 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Cache { - public sealed class DataTypeCacheRefresher : PayloadCacheRefresherBase + public sealed class DataTypeCacheRefresher : PayloadCacheRefresherBase { private readonly IPublishedSnapshotService _publishedSnapshotService; private readonly IPublishedModelFactory _publishedModelFactory; private readonly IIdKeyMap _idKeyMap; - public DataTypeCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IPublishedModelFactory publishedModelFactory, IIdKeyMap idKeyMap) - : base(appCaches, serializer) + public DataTypeCacheRefresher( + AppCaches appCaches, + IJsonSerializer serializer, + IPublishedSnapshotService publishedSnapshotService, + IPublishedModelFactory publishedModelFactory, + IIdKeyMap idKeyMap, + IEventAggregator eventAggregator) + : base(appCaches, serializer, eventAggregator) { _publishedSnapshotService = publishedSnapshotService; _publishedModelFactory = publishedModelFactory; @@ -25,8 +32,6 @@ namespace Umbraco.Cms.Core.Cache #region Define - protected override DataTypeCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("35B16C25-A17E-45D7-BC8F-EDAB1DCC28D2"); public override Guid RefresherUniqueId => UniqueId; diff --git a/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs b/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs index 922afab8da..8e21146a96 100644 --- a/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs @@ -1,18 +1,17 @@ using System; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Cache { - public sealed class DictionaryCacheRefresher : CacheRefresherBase + public sealed class DictionaryCacheRefresher : CacheRefresherBase { - public DictionaryCacheRefresher(AppCaches appCaches) - : base(appCaches) + public DictionaryCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator) + : base(appCaches, eventAggregator) { } #region Define - protected override DictionaryCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("D1D7E227-F817-4816-BFE9-6C39B6152884"); public override Guid RefresherUniqueId => UniqueId; diff --git a/src/Umbraco.Core/Cache/DomainCacheRefresher.cs b/src/Umbraco.Core/Cache/DomainCacheRefresher.cs index 2773ca2d0f..098b200632 100644 --- a/src/Umbraco.Core/Cache/DomainCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/DomainCacheRefresher.cs @@ -1,4 +1,5 @@ using System; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Serialization; @@ -6,20 +7,22 @@ using Umbraco.Cms.Core.Services.Changes; namespace Umbraco.Cms.Core.Cache { - public sealed class DomainCacheRefresher : PayloadCacheRefresherBase + public sealed class DomainCacheRefresher : PayloadCacheRefresherBase { private readonly IPublishedSnapshotService _publishedSnapshotService; - public DomainCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService) - : base(appCaches, serializer) + public DomainCacheRefresher( + AppCaches appCaches, + IJsonSerializer serializer, + IPublishedSnapshotService publishedSnapshotService, + IEventAggregator eventAggregator) + : base(appCaches, serializer, eventAggregator) { _publishedSnapshotService = publishedSnapshotService; } #region Define - protected override DomainCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("11290A79-4B57-4C99-AD72-7748A3CF38AF"); public override Guid RefresherUniqueId => UniqueId; diff --git a/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs b/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs index 3e70bc54eb..4ad5842373 100644 --- a/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs +++ b/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs @@ -1,4 +1,5 @@ -using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Core.Cache @@ -8,8 +9,8 @@ namespace Umbraco.Cms.Core.Cache /// /// The actual cache refresher type. /// The actual cache refresher type is used for strongly typed events. - public abstract class JsonCacheRefresherBase : CacheRefresherBase, IJsonCacheRefresher - where TInstanceType : class, ICacheRefresher + public abstract class JsonCacheRefresherBase : CacheRefresherBase, IJsonCacheRefresher + where TNotification : CacheRefresherNotificationBase, new() { protected IJsonSerializer JsonSerializer { get; } @@ -17,7 +18,11 @@ namespace Umbraco.Cms.Core.Cache /// Initializes a new instance of the . /// /// A cache helper. - protected JsonCacheRefresherBase(AppCaches appCaches, IJsonSerializer jsonSerializer) : base(appCaches) + protected JsonCacheRefresherBase( + AppCaches appCaches, + IJsonSerializer jsonSerializer, + IEventAggregator eventAggregator) + : base(appCaches, eventAggregator) { JsonSerializer = jsonSerializer; } @@ -28,7 +33,7 @@ namespace Umbraco.Cms.Core.Cache /// The json payload. public virtual void Refresh(string json) { - OnCacheUpdated(This, new CacheRefresherEventArgs(json, MessageType.RefreshByJson)); + OnCacheUpdated(new TNotification().Init(json, MessageType.RefreshByJson)); } #region Json diff --git a/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs b/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs index b15d247ddf..1e9ff228df 100644 --- a/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs @@ -1,4 +1,5 @@ using System; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Serialization; @@ -7,18 +8,20 @@ using static Umbraco.Cms.Core.Cache.LanguageCacheRefresher.JsonPayload; namespace Umbraco.Cms.Core.Cache { - public sealed class LanguageCacheRefresher : PayloadCacheRefresherBase + public sealed class LanguageCacheRefresher : PayloadCacheRefresherBase { - public LanguageCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService) - : base(appCaches, serializer) + public LanguageCacheRefresher( + AppCaches appCaches, + IJsonSerializer serializer, + IPublishedSnapshotService publishedSnapshotService, + IEventAggregator eventAggregator) + : base(appCaches, serializer, eventAggregator) { _publishedSnapshotService = publishedSnapshotService; } #region Define - protected override LanguageCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("3E0F95D8-0BE5-44B8-8394-2B8750B62654"); private readonly IPublishedSnapshotService _publishedSnapshotService; diff --git a/src/Umbraco.Core/Cache/MacroCacheRefresher.cs b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs index dd4c4c73de..5e6c3294ab 100644 --- a/src/Umbraco.Core/Cache/MacroCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs @@ -1,23 +1,25 @@ using System; using System.Linq; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Serialization; namespace Umbraco.Cms.Core.Cache { - public sealed class MacroCacheRefresher : PayloadCacheRefresherBase + public sealed class MacroCacheRefresher : PayloadCacheRefresherBase { - public MacroCacheRefresher(AppCaches appCaches, IJsonSerializer jsonSerializer) - : base(appCaches, jsonSerializer) + public MacroCacheRefresher( + AppCaches appCaches, + IJsonSerializer jsonSerializer, + IEventAggregator eventAggregator) + : base(appCaches, jsonSerializer, eventAggregator) { } #region Define - protected override MacroCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("7B1E683C-5F34-43dd-803D-9699EA1E98CA"); public override Guid RefresherUniqueId => UniqueId; diff --git a/src/Umbraco.Core/Cache/MediaCacheRefresher.cs b/src/Umbraco.Core/Cache/MediaCacheRefresher.cs index 997083b0a7..a4f424acf2 100644 --- a/src/Umbraco.Core/Cache/MediaCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MediaCacheRefresher.cs @@ -1,4 +1,5 @@ using System; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PublishedCache; @@ -9,13 +10,13 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Cache { - public sealed class MediaCacheRefresher : PayloadCacheRefresherBase + public sealed class MediaCacheRefresher : PayloadCacheRefresherBase { private readonly IPublishedSnapshotService _publishedSnapshotService; private readonly IIdKeyMap _idKeyMap; - public MediaCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IIdKeyMap idKeyMap) - : base(appCaches, serializer) + public MediaCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IIdKeyMap idKeyMap, IEventAggregator eventAggregator) + : base(appCaches, serializer, eventAggregator) { _publishedSnapshotService = publishedSnapshotService; _idKeyMap = idKeyMap; @@ -23,8 +24,6 @@ namespace Umbraco.Cms.Core.Cache #region Define - protected override MediaCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("B29286DD-2D40-4DDB-B325-681226589FEC"); public override Guid RefresherUniqueId => UniqueId; diff --git a/src/Umbraco.Core/Cache/MemberCacheRefresher.cs b/src/Umbraco.Core/Cache/MemberCacheRefresher.cs index 0932725fe4..40f324384b 100644 --- a/src/Umbraco.Core/Cache/MemberCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MemberCacheRefresher.cs @@ -1,6 +1,7 @@ //using Newtonsoft.Json; using System; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Serialization; @@ -9,12 +10,12 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Cache { - public sealed class MemberCacheRefresher : PayloadCacheRefresherBase + public sealed class MemberCacheRefresher : PayloadCacheRefresherBase { private readonly IIdKeyMap _idKeyMap; - public MemberCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IIdKeyMap idKeyMap) - : base(appCaches, serializer) + public MemberCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IIdKeyMap idKeyMap, IEventAggregator eventAggregator) + : base(appCaches, serializer, eventAggregator) { _idKeyMap = idKeyMap; } @@ -36,8 +37,6 @@ namespace Umbraco.Cms.Core.Cache #region Define - protected override MemberCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("E285DF34-ACDC-4226-AE32-C0CB5CF388DA"); public override Guid RefresherUniqueId => UniqueId; diff --git a/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs b/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs index 2db947d026..637d4c8558 100644 --- a/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs @@ -1,21 +1,20 @@ using System; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Serialization; namespace Umbraco.Cms.Core.Cache { - public sealed class MemberGroupCacheRefresher : PayloadCacheRefresherBase + public sealed class MemberGroupCacheRefresher : PayloadCacheRefresherBase { - public MemberGroupCacheRefresher(AppCaches appCaches, IJsonSerializer jsonSerializer) - : base(appCaches, jsonSerializer) + public MemberGroupCacheRefresher(AppCaches appCaches, IJsonSerializer jsonSerializer, IEventAggregator eventAggregator) + : base(appCaches, jsonSerializer, eventAggregator) { } #region Define - protected override MemberGroupCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("187F236B-BD21-4C85-8A7C-29FBA3D6C00C"); public override Guid RefresherUniqueId => UniqueId; diff --git a/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs b/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs index 08d3e65506..dd11e899c0 100644 --- a/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs +++ b/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs @@ -1,4 +1,5 @@ -using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Core.Cache @@ -9,8 +10,9 @@ namespace Umbraco.Cms.Core.Cache /// The actual cache refresher type. /// The payload type. /// The actual cache refresher type is used for strongly typed events. - public abstract class PayloadCacheRefresherBase : JsonCacheRefresherBase, IPayloadCacheRefresher - where TInstanceType : class, ICacheRefresher + public abstract class PayloadCacheRefresherBase : JsonCacheRefresherBase, IPayloadCacheRefresher + where TNotification : CacheRefresherNotificationBase, new() + { /// @@ -18,7 +20,8 @@ namespace Umbraco.Cms.Core.Cache /// /// A cache helper. /// - protected PayloadCacheRefresherBase(AppCaches appCaches, IJsonSerializer serializer) : base(appCaches, serializer) + protected PayloadCacheRefresherBase(AppCaches appCaches, IJsonSerializer serializer, IEventAggregator eventAggregator) + : base(appCaches, serializer, eventAggregator) { } @@ -37,7 +40,7 @@ namespace Umbraco.Cms.Core.Cache /// The payload. public virtual void Refresh(TPayload[] payloads) { - OnCacheUpdated(This, new CacheRefresherEventArgs(payloads, MessageType.RefreshByPayload)); + OnCacheUpdated(new TNotification().Init(payloads, MessageType.RefreshByPayload)); } #endregion diff --git a/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs b/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs index 19064a8031..44b108fa23 100644 --- a/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs @@ -1,18 +1,17 @@ using System; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Cache { - public sealed class PublicAccessCacheRefresher : CacheRefresherBase + public sealed class PublicAccessCacheRefresher : CacheRefresherBase { - public PublicAccessCacheRefresher(AppCaches appCaches) - : base(appCaches) + public PublicAccessCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator) + : base(appCaches, eventAggregator) { } #region Define - protected override PublicAccessCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("1DB08769-B104-4F8B-850E-169CAC1DF2EC"); public override Guid RefresherUniqueId => UniqueId; diff --git a/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs index daa954b257..d82cef759d 100644 --- a/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs @@ -1,19 +1,18 @@ using System; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; namespace Umbraco.Cms.Core.Cache { - public sealed class RelationTypeCacheRefresher : CacheRefresherBase + public sealed class RelationTypeCacheRefresher : CacheRefresherBase { - public RelationTypeCacheRefresher(AppCaches appCaches) - : base(appCaches) + public RelationTypeCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator) + : base(appCaches, eventAggregator) { } #region Define - protected override RelationTypeCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("D8375ABA-4FB3-4F86-B505-92FBA1B6F7C9"); public override Guid RefresherUniqueId => UniqueId; diff --git a/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs b/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs index d02d3190eb..6c33a44545 100644 --- a/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs @@ -1,17 +1,18 @@ using System; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Services; namespace Umbraco.Cms.Core.Cache { - public sealed class TemplateCacheRefresher : CacheRefresherBase + public sealed class TemplateCacheRefresher : CacheRefresherBase { private readonly IIdKeyMap _idKeyMap; private readonly IContentTypeCommonRepository _contentTypeCommonRepository; - public TemplateCacheRefresher(AppCaches appCaches, IIdKeyMap idKeyMap, IContentTypeCommonRepository contentTypeCommonRepository) - : base(appCaches) + public TemplateCacheRefresher(AppCaches appCaches, IIdKeyMap idKeyMap, IContentTypeCommonRepository contentTypeCommonRepository, IEventAggregator eventAggregator) + : base(appCaches, eventAggregator) { _idKeyMap = idKeyMap; _contentTypeCommonRepository = contentTypeCommonRepository; @@ -19,8 +20,6 @@ namespace Umbraco.Cms.Core.Cache #region Define - protected override TemplateCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("DD12B6A0-14B9-46e8-8800-C154F74047C8"); public override Guid RefresherUniqueId => UniqueId; diff --git a/src/Umbraco.Core/Cache/UserCacheRefresher.cs b/src/Umbraco.Core/Cache/UserCacheRefresher.cs index 6cb3eb7f88..b8fd75702e 100644 --- a/src/Umbraco.Core/Cache/UserCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/UserCacheRefresher.cs @@ -1,19 +1,18 @@ using System; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Persistence.Repositories; namespace Umbraco.Cms.Core.Cache { - public sealed class UserCacheRefresher : CacheRefresherBase + public sealed class UserCacheRefresher : CacheRefresherBase { - public UserCacheRefresher(AppCaches appCaches) - : base(appCaches) + public UserCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator) + : base(appCaches, eventAggregator) { } #region Define - protected override UserCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("E057AF6D-2EE6-41F4-8045-3694010F0AA6"); public override Guid RefresherUniqueId => UniqueId; @@ -47,7 +46,7 @@ namespace Umbraco.Cms.Core.Cache userCache.Result.ClearByKey(CacheKeys.UserAllContentStartNodesPrefix + id); userCache.Result.ClearByKey(CacheKeys.UserAllMediaStartNodesPrefix + id); } - + base.Remove(id); } diff --git a/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs b/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs index 7519994069..f4456ef696 100644 --- a/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs @@ -1,4 +1,5 @@ using System; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Persistence.Repositories; @@ -10,16 +11,14 @@ namespace Umbraco.Cms.Core.Cache /// /// This also needs to clear the user cache since IReadOnlyUserGroup's are attached to IUser objects /// - public sealed class UserGroupCacheRefresher : CacheRefresherBase + public sealed class UserGroupCacheRefresher : CacheRefresherBase { - public UserGroupCacheRefresher(AppCaches appCaches) - : base(appCaches) + public UserGroupCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator) + : base(appCaches, eventAggregator) { } #region Define - protected override UserGroupCacheRefresher This => this; - public static readonly Guid UniqueId = Guid.Parse("45178038-B232-4FE8-AA1A-F2B949C44762"); public override Guid RefresherUniqueId => UniqueId; diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index ce524a09a1..0cbbee528e 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -51,4 +51,8 @@ + + + + diff --git a/src/Umbraco.Infrastructure/Search/ExamineComposer.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs similarity index 69% rename from src/Umbraco.Infrastructure/Search/ExamineComposer.cs rename to src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs index 45ba3c461e..033ab76298 100644 --- a/src/Umbraco.Infrastructure/Search/ExamineComposer.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs @@ -1,25 +1,25 @@ using Microsoft.Extensions.DependencyInjection; -using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Examine; +using Umbraco.Cms.Infrastructure.Search; using Umbraco.Extensions; -namespace Umbraco.Cms.Infrastructure.Search +namespace Umbraco.Cms.Infrastructure.DependencyInjection { /// - /// Configures and installs Examine. + /// Provides extension methods to the class. /// - public sealed class ExamineComposer : ComponentComposer, ICoreComposer + public static partial class UmbracoBuilderExtensions { - public override void Compose(IUmbracoBuilder builder) + public static IUmbracoBuilder AddExamine(this IUmbracoBuilder builder) { - base.Compose(builder); - // populators are not a collection: one cannot remove ours, and can only add more // the container can inject IEnumerable and get them all builder.Services.AddSingleton(); @@ -49,6 +49,15 @@ namespace Umbraco.Cms.Infrastructure.Search builder.Services.AddUnique, MediaValueSetBuilder>(); builder.Services.AddUnique, MemberValueSetBuilder>(); builder.Services.AddUnique(); + + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + + return builder; } } } diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs b/src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs index 2c0a71016a..9ff03e4d45 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs @@ -12,7 +12,10 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.ModelsBuilder { // supports LiveAppData - but not PureLive - public sealed class LiveModelsProvider : INotificationHandler, INotificationHandler + public sealed class LiveModelsProvider : INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler { private static int s_req; private readonly ILogger _logger; @@ -53,16 +56,6 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder { return; } - - // Must register with maindom in order to function. - // If registration is not successful then events are not bound - // and we also don't generate models. - _mainDom.Register(() => - { - // anything changes, and we want to re-generate models. - ContentTypeCacheRefresher.CacheUpdated += RequestModelsGeneration; - DataTypeCacheRefresher.CacheUpdated += RequestModelsGeneration; - }); } // NOTE @@ -72,8 +65,13 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder // need to be generated. Could be by another request. Anyway. We could // have collisions but... you know the risk. - private void RequestModelsGeneration(object sender, EventArgs args) + private void RequestModelsGeneration() { + if (!_mainDom.IsMainDom) + { + return; + } + _logger.LogDebug("Requested to generate models."); Interlocked.Exchange(ref s_req, 1); } @@ -121,5 +119,9 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder GenerateModelsIfRequested(); } } + + public void Handle(ContentTypeCacheRefresherNotification notification) => RequestModelsGeneration(); + + public void Handle(DataTypeCacheRefresherNotification notification) => RequestModelsGeneration(); } } diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/OutOfDateModelsStatus.cs b/src/Umbraco.Infrastructure/ModelsBuilder/OutOfDateModelsStatus.cs index 65a7ac3ef8..8b14a6030b 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/OutOfDateModelsStatus.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/OutOfDateModelsStatus.cs @@ -11,7 +11,8 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder /// /// Used to track if ModelsBuilder models are out of date/stale /// - public sealed class OutOfDateModelsStatus : INotificationHandler + public sealed class OutOfDateModelsStatus : INotificationHandler, + INotificationHandler { private readonly ModelsBuilderSettings _config; private readonly IHostingEnvironment _hostingEnvironment; @@ -47,22 +48,6 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder } } - /// - /// Handles the notification - /// - public void Handle(UmbracoApplicationStarting notification) => Install(); - - private void Install() - { - // don't run if not configured - if (!IsEnabled) - { - return; - } - - ContentTypeCacheRefresher.CacheUpdated += (sender, args) => Write(); - DataTypeCacheRefresher.CacheUpdated += (sender, args) => Write(); - } private string GetFlagPath() { @@ -77,6 +62,12 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder private void Write() { + // don't run if not configured + if (!IsEnabled) + { + return; + } + var path = GetFlagPath(); if (path == null || File.Exists(path)) { @@ -101,5 +92,9 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder File.Delete(path); } + + public void Handle(ContentTypeCacheRefresherNotification notification) => Write(); + + public void Handle(DataTypeCacheRefresherNotification notification) => Write(); } } diff --git a/src/Umbraco.Infrastructure/Search/ExamineComponent.cs b/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs similarity index 86% rename from src/Umbraco.Infrastructure/Search/ExamineComponent.cs rename to src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs index 30dc01dc9a..dd95b6931e 100644 --- a/src/Umbraco.Infrastructure/Search/ExamineComponent.cs +++ b/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs @@ -7,7 +7,7 @@ using Examine; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Runtime; @@ -20,7 +20,13 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Search { - public sealed class ExamineComponent : IComponent + public sealed class ExamineNotificationHandler : + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler { private readonly IExamineManager _examineManager; private readonly IContentValueSetBuilder _contentValueSetBuilder; @@ -33,18 +39,19 @@ namespace Umbraco.Cms.Infrastructure.Search private readonly ServiceContext _services; private readonly IMainDom _mainDom; private readonly IProfilingLogger _profilingLogger; - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly IUmbracoIndexesCreator _indexCreator; + private static bool s_deactivate_handlers; // the default enlist priority is 100 // enlist with a lower priority to ensure that anything "default" runs after us // but greater that SafeXmlReaderWriter priority which is 60 private const int EnlistPriority = 80; - public ExamineComponent(IMainDom mainDom, + public ExamineNotificationHandler(IMainDom mainDom, IExamineManager examineManager, IProfilingLogger profilingLogger, - ILoggerFactory loggerFactory, + ILogger logger, IScopeProvider scopeProvider, IUmbracoIndexesCreator indexCreator, ServiceContext services, @@ -66,16 +73,15 @@ namespace Umbraco.Cms.Infrastructure.Search _taskHelper = taskHelper; _mainDom = mainDom; _profilingLogger = profilingLogger; - _logger = loggerFactory.CreateLogger(); + _logger = logger; _indexCreator = indexCreator; } - - public void Initialize() + public void Handle(UmbracoApplicationStarting notification) { //let's deal with shutting down Examine with MainDom var examineShutdownRegistered = _mainDom.Register(release: () => { - using (_profilingLogger.TraceDuration("Examine shutting down")) + using (_profilingLogger.TraceDuration("Examine shutting down")) { _examineManager.Dispose(); } @@ -105,26 +111,12 @@ namespace Umbraco.Cms.Infrastructure.Search // don't bind event handlers if we're not suppose to listen if (registeredIndexers == 0) { - return; + s_deactivate_handlers = true; } - // bind to distributed cache events - this ensures that this logic occurs on ALL servers - // that are taking part in a load balanced environment. - ContentCacheRefresher.CacheUpdated += ContentCacheRefresherUpdated; - ContentTypeCacheRefresher.CacheUpdated += ContentTypeCacheRefresherUpdated; - MediaCacheRefresher.CacheUpdated += MediaCacheRefresherUpdated; - MemberCacheRefresher.CacheUpdated += MemberCacheRefresherUpdated; - LanguageCacheRefresher.CacheUpdated += LanguageCacheRefresherUpdated; + } - public void Terminate() - { - ContentCacheRefresher.CacheUpdated -= ContentCacheRefresherUpdated; - ContentTypeCacheRefresher.CacheUpdated -= ContentTypeCacheRefresherUpdated; - MediaCacheRefresher.CacheUpdated -= MediaCacheRefresherUpdated; - MemberCacheRefresher.CacheUpdated -= MemberCacheRefresherUpdated; - LanguageCacheRefresher.CacheUpdated -= LanguageCacheRefresherUpdated; - } #region Cache refresher updated event handlers @@ -133,8 +125,12 @@ namespace Umbraco.Cms.Infrastructure.Search /// /// /// - private void ContentCacheRefresherUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args) + public void Handle(ContentCacheRefresherNotification args) { + if (s_deactivate_handlers) + { + return;; + } if (Suspendable.ExamineEvents.CanIndex == false) { return; @@ -237,8 +233,13 @@ namespace Umbraco.Cms.Infrastructure.Search } } - private void MemberCacheRefresherUpdated(MemberCacheRefresher sender, CacheRefresherEventArgs args) + public void Handle(MemberCacheRefresherNotification args) { + if (s_deactivate_handlers) + { + return;; + } + if (Suspendable.ExamineEvents.CanIndex == false) { return; @@ -300,8 +301,13 @@ namespace Umbraco.Cms.Infrastructure.Search } } - private void MediaCacheRefresherUpdated(MediaCacheRefresher sender, CacheRefresherEventArgs args) + public void Handle(MediaCacheRefresherNotification args) { + if (s_deactivate_handlers) + { + return;; + } + if (Suspendable.ExamineEvents.CanIndex == false) { return; @@ -364,9 +370,14 @@ namespace Umbraco.Cms.Infrastructure.Search } } - private void LanguageCacheRefresherUpdated(LanguageCacheRefresher sender, CacheRefresherEventArgs e) + public void Handle(LanguageCacheRefresherNotification args) { - if (!(e.MessageObject is LanguageCacheRefresher.JsonPayload[] payloads)) + if (s_deactivate_handlers) + { + return;; + } + + if (!(args.MessageObject is LanguageCacheRefresher.JsonPayload[] payloads)) { return; } @@ -393,8 +404,13 @@ namespace Umbraco.Cms.Infrastructure.Search /// /// /// - private void ContentTypeCacheRefresherUpdated(ContentTypeCacheRefresher sender, CacheRefresherEventArgs args) + public void Handle(ContentTypeCacheRefresherNotification args) { + if (s_deactivate_handlers) + { + return;; + } + if (Suspendable.ExamineEvents.CanIndex == false) { return; @@ -668,34 +684,34 @@ namespace Umbraco.Cms.Infrastructure.Search private class DeferedReIndexForContent : DeferedAction { private readonly TaskHelper _taskHelper; - private readonly ExamineComponent _examineComponent; + private readonly ExamineNotificationHandler _ExamineNotificationHandler; private readonly IContent _content; private readonly bool _isPublished; - public DeferedReIndexForContent(TaskHelper taskHelper, ExamineComponent examineComponent, IContent content, bool isPublished) + public DeferedReIndexForContent(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IContent content, bool isPublished) { _taskHelper = taskHelper; - _examineComponent = examineComponent; + _ExamineNotificationHandler = ExamineNotificationHandler; _content = content; _isPublished = isPublished; } - public override void Execute() => Execute(_taskHelper, _examineComponent, _content, _isPublished); + public override void Execute() => Execute(_taskHelper, _ExamineNotificationHandler, _content, _isPublished); - public static void Execute(TaskHelper taskHelper, ExamineComponent examineComponent, IContent content, bool isPublished) + public static void Execute(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IContent content, bool isPublished) => taskHelper.RunBackgroundTask(() => { - using IScope scope = examineComponent._scopeProvider.CreateScope(autoComplete: true); + using IScope scope = ExamineNotificationHandler._scopeProvider.CreateScope(autoComplete: true); // for content we have a different builder for published vs unpublished // we don't want to build more value sets than is needed so we'll lazily build 2 one for published one for non-published var builders = new Dictionary>> { - [true] = new Lazy>(() => examineComponent._publishedContentValueSetBuilder.GetValueSets(content).ToList()), - [false] = new Lazy>(() => examineComponent._contentValueSetBuilder.GetValueSets(content).ToList()) + [true] = new Lazy>(() => ExamineNotificationHandler._publishedContentValueSetBuilder.GetValueSets(content).ToList()), + [false] = new Lazy>(() => ExamineNotificationHandler._contentValueSetBuilder.GetValueSets(content).ToList()) }; - foreach (IUmbracoIndex index in examineComponent._examineManager.Indexes.OfType() + foreach (IUmbracoIndex index in ExamineNotificationHandler._examineManager.Indexes.OfType() //filter the indexers .Where(x => isPublished || !x.PublishedValuesOnly) .Where(x => x.EnableDefaultEventHandler)) @@ -714,29 +730,29 @@ namespace Umbraco.Cms.Infrastructure.Search private class DeferedReIndexForMedia : DeferedAction { private readonly TaskHelper _taskHelper; - private readonly ExamineComponent _examineComponent; + private readonly ExamineNotificationHandler _ExamineNotificationHandler; private readonly IMedia _media; private readonly bool _isPublished; - public DeferedReIndexForMedia(TaskHelper taskHelper, ExamineComponent examineComponent, IMedia media, bool isPublished) + public DeferedReIndexForMedia(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMedia media, bool isPublished) { _taskHelper = taskHelper; - _examineComponent = examineComponent; + _ExamineNotificationHandler = ExamineNotificationHandler; _media = media; _isPublished = isPublished; } - public override void Execute() => Execute(_taskHelper, _examineComponent, _media, _isPublished); + public override void Execute() => Execute(_taskHelper, _ExamineNotificationHandler, _media, _isPublished); - public static void Execute(TaskHelper taskHelper, ExamineComponent examineComponent, IMedia media, bool isPublished) => + public static void Execute(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMedia media, bool isPublished) => // perform the ValueSet lookup on a background thread taskHelper.RunBackgroundTask(() => { - using IScope scope = examineComponent._scopeProvider.CreateScope(autoComplete: true); + using IScope scope = ExamineNotificationHandler._scopeProvider.CreateScope(autoComplete: true); - var valueSet = examineComponent._mediaValueSetBuilder.GetValueSets(media).ToList(); + var valueSet = ExamineNotificationHandler._mediaValueSetBuilder.GetValueSets(media).ToList(); - foreach (IUmbracoIndex index in examineComponent._examineManager.Indexes.OfType() + foreach (IUmbracoIndex index in ExamineNotificationHandler._examineManager.Indexes.OfType() //filter the indexers .Where(x => isPublished || !x.PublishedValuesOnly) .Where(x => x.EnableDefaultEventHandler)) @@ -753,27 +769,27 @@ namespace Umbraco.Cms.Infrastructure.Search /// private class DeferedReIndexForMember : DeferedAction { - private readonly ExamineComponent _examineComponent; + private readonly ExamineNotificationHandler _ExamineNotificationHandler; private readonly IMember _member; private readonly TaskHelper _taskHelper; - public DeferedReIndexForMember(TaskHelper taskHelper, ExamineComponent examineComponent, IMember member) + public DeferedReIndexForMember(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMember member) { - _examineComponent = examineComponent; + _ExamineNotificationHandler = ExamineNotificationHandler; _member = member; _taskHelper = taskHelper; } - public override void Execute() => Execute(_taskHelper, _examineComponent, _member); + public override void Execute() => Execute(_taskHelper, _ExamineNotificationHandler, _member); - public static void Execute(TaskHelper taskHelper, ExamineComponent examineComponent, IMember member) => + public static void Execute(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMember member) => // perform the ValueSet lookup on a background thread taskHelper.RunBackgroundTask(() => { - using IScope scope = examineComponent._scopeProvider.CreateScope(autoComplete: true); + using IScope scope = ExamineNotificationHandler._scopeProvider.CreateScope(autoComplete: true); - var valueSet = examineComponent._memberValueSetBuilder.GetValueSets(member).ToList(); - foreach (IUmbracoIndex index in examineComponent._examineManager.Indexes.OfType() + var valueSet = ExamineNotificationHandler._memberValueSetBuilder.GetValueSets(member).ToList(); + foreach (IUmbracoIndex index in ExamineNotificationHandler._examineManager.Indexes.OfType() //filter the indexers .Where(x => x.EnableDefaultEventHandler)) { @@ -786,23 +802,23 @@ namespace Umbraco.Cms.Infrastructure.Search private class DeferedDeleteIndex : DeferedAction { - private readonly ExamineComponent _examineComponent; + private readonly ExamineNotificationHandler _ExamineNotificationHandler; private readonly int _id; private readonly bool _keepIfUnpublished; - public DeferedDeleteIndex(ExamineComponent examineComponent, int id, bool keepIfUnpublished) + public DeferedDeleteIndex(ExamineNotificationHandler ExamineNotificationHandler, int id, bool keepIfUnpublished) { - _examineComponent = examineComponent; + _ExamineNotificationHandler = ExamineNotificationHandler; _id = id; _keepIfUnpublished = keepIfUnpublished; } - public override void Execute() => Execute(_examineComponent, _id, _keepIfUnpublished); + public override void Execute() => Execute(_ExamineNotificationHandler, _id, _keepIfUnpublished); - public static void Execute(ExamineComponent examineComponent, int id, bool keepIfUnpublished) + public static void Execute(ExamineNotificationHandler ExamineNotificationHandler, int id, bool keepIfUnpublished) { var strId = id.ToString(CultureInfo.InvariantCulture); - foreach (var index in examineComponent._examineManager.Indexes.OfType() + foreach (var index in ExamineNotificationHandler._examineManager.Indexes.OfType() .Where(x => x.PublishedValuesOnly || !keepIfUnpublished) .Where(x => x.EnableDefaultEventHandler)) { @@ -811,7 +827,5 @@ namespace Umbraco.Cms.Infrastructure.Search } } #endregion - - } } diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index e73c0a5c5f..dbf047cf48 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -25,7 +25,6 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Web; @@ -217,6 +216,7 @@ namespace Umbraco.Cms.Tests.Integration.Testing .AddBackOfficeAuthentication() .AddBackOfficeIdentity() .AddMembersIdentity() + .AddExamine() .AddTestServices(TestHelper, GetAppCaches()); if (TestOptions.Mapper) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentEventsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentEventsTests.cs index c26d2e0e7b..0cf091ac65 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentEventsTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentEventsTests.cs @@ -8,7 +8,10 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; using NUnit.Framework; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Core.Web; @@ -18,7 +21,6 @@ using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; using Umbraco.Extensions; -using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services { @@ -34,6 +36,40 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services #region Setup + private class TestNotificationHandler : INotificationHandler + { + public void Handle(ContentCacheRefresherNotification args) + { + // reports the event as: "ContentCache/,.../X + // where + // is(are) the action(s) + // X is the event content ID + if (args.MessageType != MessageType.RefreshByPayload) + { + throw new NotSupportedException(); + } + + foreach (ContentCacheRefresher.JsonPayload payload in (ContentCacheRefresher.JsonPayload[])args.MessageObject) + { + var e = new EventInstance + { + Message = _msgCount, + Sender = "ContentCacheRefresher", + EventArgs = payload, + Name = payload.ChangeTypes.ToString().Replace(" ", string.Empty), + Args = payload.Id.ToInvariantString() + }; + _events.Add(e); + } + + _msgCount++; + } + } + protected override void CustomTestSetup(IUmbracoBuilder builder) + { + builder.AddNotificationHandler(); + } + [SetUp] public void SetUp() { @@ -45,7 +81,6 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services DocumentRepository.ScopedEntityRefresh += ContentRepositoryRefreshed; DocumentRepository.ScopeEntityRemove += ContentRepositoryRemoved; DocumentRepository.ScopeVersionRemove += ContentRepositoryRemovedVersion; - ContentCacheRefresher.CacheUpdated += ContentCacheUpdated; // prepare content type Template template = TemplateBuilder.CreateTextPageTemplate(); @@ -66,12 +101,11 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services DocumentRepository.ScopedEntityRefresh -= ContentRepositoryRefreshed; DocumentRepository.ScopeEntityRemove -= ContentRepositoryRemoved; DocumentRepository.ScopeVersionRemove -= ContentRepositoryRemovedVersion; - ContentCacheRefresher.CacheUpdated -= ContentCacheUpdated; } private DistributedCacheBinder _distributedCacheBinder; - private IList _events; - private int _msgCount; + private static IList _events; + private static int _msgCount; private IContentType _contentType; private void ResetEvents() @@ -324,32 +358,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services _events.Add(e); } - private void ContentCacheUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args) - { - // reports the event as: "ContentCache/,.../X - // where - // is(are) the action(s) - // X is the event content ID - if (args.MessageType != MessageType.RefreshByPayload) - { - throw new NotSupportedException(); - } - foreach (ContentCacheRefresher.JsonPayload payload in (ContentCacheRefresher.JsonPayload[])args.MessageObject) - { - var e = new EventInstance - { - Message = _msgCount, - Sender = sender.Name, - EventArgs = payload, - Name = payload.ChangeTypes.ToString().Replace(" ", string.Empty), - Args = payload.Id.ToInvariantString() - }; - _events.Add(e); - } - - _msgCount++; - } private void WriteEvents() { diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs index 098b2ba879..5356bc08f1 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs @@ -5,11 +5,11 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.WebAssets; using Umbraco.Cms.Infrastructure.DependencyInjection; using Umbraco.Cms.Infrastructure.WebAssets; using Umbraco.Cms.Web.BackOffice.Controllers; @@ -19,6 +19,7 @@ using Umbraco.Cms.Web.BackOffice.ModelsBuilder; using Umbraco.Cms.Web.BackOffice.Routing; using Umbraco.Cms.Web.BackOffice.Security; using Umbraco.Cms.Web.BackOffice.Services; +using Umbraco.Cms.Web.BackOffice.SignalR; using Umbraco.Cms.Web.BackOffice.Trees; using Umbraco.Cms.Web.Common.Authorization; @@ -49,7 +50,8 @@ namespace Umbraco.Extensions .AddHostedServices() .AddDistributedCache() .AddModelsBuilderDashboard() - .AddUnattedInstallCreateUser(); + .AddUnattedInstallCreateUser() + .AddExamine(); /// /// Adds Umbraco back office authentication requirements @@ -153,6 +155,7 @@ namespace Umbraco.Extensions builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); + builder.AddNotificationAsyncHandler(); builder.Services.AddUnique(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/src/Umbraco.Web.BackOffice/SignalR/PreviewHubComposer.cs b/src/Umbraco.Web.BackOffice/SignalR/PreviewHubComposer.cs deleted file mode 100644 index 18b8f90825..0000000000 --- a/src/Umbraco.Web.BackOffice/SignalR/PreviewHubComposer.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.DependencyInjection; - -namespace Umbraco.Cms.Web.BackOffice.SignalR -{ - public class PreviewHubComposer : ComponentComposer, ICoreComposer - { - public override void Compose(IUmbracoBuilder builder) - { - base.Compose(builder); - } - } -} diff --git a/src/Umbraco.Web.BackOffice/SignalR/PreviewHubComponent.cs b/src/Umbraco.Web.BackOffice/SignalR/PreviewHubUpdater.cs similarity index 54% rename from src/Umbraco.Web.BackOffice/SignalR/PreviewHubComponent.cs rename to src/Umbraco.Web.BackOffice/SignalR/PreviewHubUpdater.cs index 00d3dc8013..a71b5439d4 100644 --- a/src/Umbraco.Web.BackOffice/SignalR/PreviewHubComponent.cs +++ b/src/Umbraco.Web.BackOffice/SignalR/PreviewHubUpdater.cs @@ -1,38 +1,26 @@ using System; +using System.Threading; +using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR; using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Web.BackOffice.SignalR { - public class PreviewHubComponent : IComponent + public class PreviewHubUpdater :INotificationAsyncHandler { private readonly Lazy> _hubContext; // using a lazy arg here means that we won't create the hub until necessary // and therefore we won't have too bad an impact on boot time - public PreviewHubComponent(Lazy> hubContext) + public PreviewHubUpdater(Lazy> hubContext) { _hubContext = hubContext; } - public void Initialize() - { - // ContentService.Saved is too soon - the content cache is not ready yet, - // so use the content cache refresher event, because when it triggers - // the cache has already been notified of the changes - ContentCacheRefresher.CacheUpdated += HandleCacheUpdated; - } - - public void Terminate() - { - ContentCacheRefresher.CacheUpdated -= HandleCacheUpdated; - } - - private async void HandleCacheUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args) - { + public async Task HandleAsync(ContentCacheRefresherNotification args, CancellationToken cancellationToken) { if (args.MessageType != MessageType.RefreshByPayload) return; var payloads = (ContentCacheRefresher.JsonPayload[])args.MessageObject; var hubContextInstance = _hubContext.Value; diff --git a/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs b/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs index 5ce05e62cd..e8b54d3af0 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; @@ -102,10 +103,15 @@ namespace Umbraco.Extensions builder.AddNotificationHandler(); builder.AddNotificationHandler(); builder.AddNotificationHandler(); - builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); From abf11c2d62dd0296bffeeb900cb0bb4b7b30f5f5 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 12 Mar 2021 21:50:47 +0100 Subject: [PATCH 28/42] Fixed issue with BackOfficeIdentity. Previews did not work, because we did not check for multiple identities on the priciple, after the move the pure ClaimsIdentity --- .../Extensions/ClaimsIdentityExtensions.cs | 8 +++- .../Security/ClaimsPrincipalExtensions.cs | 46 ++++++++++++++----- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs b/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs index 7569b64cb7..57c69ee9aa 100644 --- a/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs +++ b/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs @@ -101,6 +101,12 @@ namespace Umbraco.Extensions /// True if ClaimsIdentity public static bool VerifyBackOfficeIdentity(this ClaimsIdentity identity, out ClaimsIdentity verifiedIdentity) { + if (identity is null) + { + verifiedIdentity = null; + return false; + } + // Validate that all required claims exist foreach (var claimType in RequiredBackOfficeClaimTypes) { @@ -112,7 +118,7 @@ namespace Umbraco.Extensions } } - verifiedIdentity = new ClaimsIdentity(identity.Claims, Constants.Security.BackOfficeAuthenticationType); + verifiedIdentity = identity.AuthenticationType == Constants.Security.BackOfficeAuthenticationType ? identity : new ClaimsIdentity(identity.Claims, Constants.Security.BackOfficeAuthenticationType); return true; } diff --git a/src/Umbraco.Core/Security/ClaimsPrincipalExtensions.cs b/src/Umbraco.Core/Security/ClaimsPrincipalExtensions.cs index ce0e0eb774..1ee5699868 100644 --- a/src/Umbraco.Core/Security/ClaimsPrincipalExtensions.cs +++ b/src/Umbraco.Core/Security/ClaimsPrincipalExtensions.cs @@ -7,31 +7,53 @@ using System.Linq; using System.Security.Claims; using System.Security.Principal; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Security; namespace Umbraco.Extensions { public static class ClaimsPrincipalExtensions { + + public static bool IsBackOfficeAuthenticationType(this ClaimsIdentity claimsIdentity) + { + if (claimsIdentity is null) + { + return false; + } + + return claimsIdentity.IsAuthenticated && claimsIdentity.AuthenticationType == Constants.Security.BackOfficeAuthenticationType; + } /// /// This will return the current back office identity if the IPrincipal is the correct type and authenticated. /// - /// + /// /// - public static ClaimsIdentity GetUmbracoIdentity(this IPrincipal user) + public static ClaimsIdentity GetUmbracoIdentity(this IPrincipal principal) { - // Check if the identity is a ClaimsIdentity, and that's it's authenticated and has all required claims. - if (user.Identity is ClaimsIdentity claimsIdentity - && claimsIdentity.IsAuthenticated - && claimsIdentity.VerifyBackOfficeIdentity(out ClaimsIdentity umbracoIdentity)) + //If it's already a UmbracoBackOfficeIdentity + if (principal.Identity is ClaimsIdentity claimsIdentity + && claimsIdentity.IsBackOfficeAuthenticationType() + && claimsIdentity.VerifyBackOfficeIdentity(out var backOfficeIdentity)) { - if (claimsIdentity.AuthenticationType == Constants.Security.BackOfficeAuthenticationType) - { - return claimsIdentity; - } - return umbracoIdentity; + return backOfficeIdentity; } + //Check if there's more than one identity assigned and see if it's a UmbracoBackOfficeIdentity and use that + // We can have assigned more identities if it is a preview request. + if (principal is ClaimsPrincipal claimsPrincipal ) + { + claimsIdentity = claimsPrincipal.Identities.FirstOrDefault(x=>x.IsBackOfficeAuthenticationType()); + if (claimsIdentity.VerifyBackOfficeIdentity(out backOfficeIdentity)) + { + return backOfficeIdentity; + } + } + + //Otherwise convert to a UmbracoBackOfficeIdentity if it's auth'd + if (principal.Identity is ClaimsIdentity claimsIdentity2 + && claimsIdentity2.VerifyBackOfficeIdentity(out backOfficeIdentity)) + { + return backOfficeIdentity; + } return null; } From e77d2d58c2ffbed2faf8999295a8bc5c9eb17d9b Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 12 Mar 2021 22:03:30 +0100 Subject: [PATCH 29/42] Clean up --- src/Umbraco.Core/Umbraco.Core.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 0cbbee528e..ce524a09a1 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -51,8 +51,4 @@ - - - - From 86eb87a8858b8fc4764056ad12ed2f79a6e31532 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 12 Mar 2021 22:09:51 +0100 Subject: [PATCH 30/42] Clean up --- .../Search/ExamineNotificationHandler.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs b/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs index dd95b6931e..b10bf70c10 100644 --- a/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs @@ -129,7 +129,7 @@ namespace Umbraco.Cms.Infrastructure.Search { if (s_deactivate_handlers) { - return;; + return; } if (Suspendable.ExamineEvents.CanIndex == false) { @@ -237,7 +237,7 @@ namespace Umbraco.Cms.Infrastructure.Search { if (s_deactivate_handlers) { - return;; + return; } if (Suspendable.ExamineEvents.CanIndex == false) @@ -305,7 +305,7 @@ namespace Umbraco.Cms.Infrastructure.Search { if (s_deactivate_handlers) { - return;; + return; } if (Suspendable.ExamineEvents.CanIndex == false) @@ -374,7 +374,7 @@ namespace Umbraco.Cms.Infrastructure.Search { if (s_deactivate_handlers) { - return;; + return; } if (!(args.MessageObject is LanguageCacheRefresher.JsonPayload[] payloads)) @@ -408,7 +408,7 @@ namespace Umbraco.Cms.Infrastructure.Search { if (s_deactivate_handlers) { - return;; + return; } if (Suspendable.ExamineEvents.CanIndex == false) From 71e59e2ba8c2bccae391239330dbaf148ff41371 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Mon, 15 Mar 2021 11:35:09 +1100 Subject: [PATCH 31/42] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5f2768f575..95295e44c4 100644 --- a/.gitignore +++ b/.gitignore @@ -201,4 +201,4 @@ src/Umbraco.Tests/TEMP/ /src/Umbraco.Web.UI/config/umbracoSettings.config /src/Umbraco.Web.UI.NetCore/Umbraco/models/* -/src/Umbraco.Web.UI.NetCore/appsettings.local.json +/src/Umbraco.Web.UI.NetCore/appsettings.Local.json From f03fc25b4c49718218c1d0da64739b2aa1714d10 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 15 Mar 2021 13:39:34 +1100 Subject: [PATCH 32/42] Splits into files, adds ICacheRefresherNotificationFactory, removes Init method --- .../Cache/ApplicationCacheRefresher.cs | 9 +- .../ApplicationCacheRefresherNotification.cs | 11 +++ src/Umbraco.Core/Cache/CacheRefresherBase.cs | 24 ++++-- .../Cache/CacheRefresherNotification.cs | 22 +++++ .../Cache/CacheRefresherNotificationBase.cs | 83 ------------------- .../CacheRefresherNotificationFactory.cs | 22 +++++ .../Cache/ContentCacheRefresher.cs | 7 +- .../ContentCacheRefresherNotification.cs | 11 +++ .../Cache/ContentTypeCacheRefresher.cs | 7 +- .../ContentTypeCacheRefresherNotification.cs | 11 +++ .../Cache/DataTypeCacheRefresher.cs | 7 +- .../DataTypeCacheRefresherNotification.cs | 11 +++ .../Cache/DictionaryCacheRefresher.cs | 6 +- .../DictionaryCacheRefresherNotification.cs | 11 +++ .../Cache/DomainCacheRefresher.cs | 7 +- .../Cache/DomainCacheRefresherNotification.cs | 11 +++ .../ICacheRefresherNotificationFactory.cs | 16 ++++ .../Cache/JsonCacheRefresherBase.cs | 11 +-- .../Cache/LanguageCacheRefresher.cs | 7 +- .../LanguageCacheRefresherNotification.cs | 11 +++ src/Umbraco.Core/Cache/MacroCacheRefresher.cs | 7 +- .../Cache/MacroCacheRefresherNotification.cs | 11 +++ src/Umbraco.Core/Cache/MediaCacheRefresher.cs | 6 +- .../Cache/MediaCacheRefresherNotification.cs | 11 +++ .../Cache/MemberCacheRefresher.cs | 4 +- .../Cache/MemberCacheRefresherNotification.cs | 11 +++ .../Cache/MemberGroupCacheRefresher.cs | 6 +- .../MemberGroupCacheRefresherNotification.cs | 11 +++ .../Cache/PayloadCacheRefresherBase.cs | 11 ++- .../Cache/PublicAccessCacheRefresher.cs | 6 +- .../PublicAccessCacheRefresherNotification.cs | 11 +++ .../Cache/RelationTypeCacheRefresher.cs | 6 +- .../RelationTypeCacheRefresherNotification.cs | 11 +++ .../Cache/TemplateCacheRefresher.cs | 6 +- .../TemplateCacheRefresherNotification.cs | 11 +++ src/Umbraco.Core/Cache/UserCacheRefresher.cs | 6 +- .../Cache/UserCacheRefresherNotification.cs | 11 +++ .../Cache/UserGroupCacheRefresher.cs | 6 +- .../UserGroupCacheRefresherNotification.cs | 11 +++ .../DependencyInjection/UmbracoBuilder.cs | 1 + 40 files changed, 318 insertions(+), 151 deletions(-) create mode 100644 src/Umbraco.Core/Cache/ApplicationCacheRefresherNotification.cs create mode 100644 src/Umbraco.Core/Cache/CacheRefresherNotification.cs delete mode 100644 src/Umbraco.Core/Cache/CacheRefresherNotificationBase.cs create mode 100644 src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs create mode 100644 src/Umbraco.Core/Cache/ContentCacheRefresherNotification.cs create mode 100644 src/Umbraco.Core/Cache/ContentTypeCacheRefresherNotification.cs create mode 100644 src/Umbraco.Core/Cache/DataTypeCacheRefresherNotification.cs create mode 100644 src/Umbraco.Core/Cache/DictionaryCacheRefresherNotification.cs create mode 100644 src/Umbraco.Core/Cache/DomainCacheRefresherNotification.cs create mode 100644 src/Umbraco.Core/Cache/ICacheRefresherNotificationFactory.cs create mode 100644 src/Umbraco.Core/Cache/LanguageCacheRefresherNotification.cs create mode 100644 src/Umbraco.Core/Cache/MacroCacheRefresherNotification.cs create mode 100644 src/Umbraco.Core/Cache/MediaCacheRefresherNotification.cs create mode 100644 src/Umbraco.Core/Cache/MemberCacheRefresherNotification.cs create mode 100644 src/Umbraco.Core/Cache/MemberGroupCacheRefresherNotification.cs create mode 100644 src/Umbraco.Core/Cache/PublicAccessCacheRefresherNotification.cs create mode 100644 src/Umbraco.Core/Cache/RelationTypeCacheRefresherNotification.cs create mode 100644 src/Umbraco.Core/Cache/TemplateCacheRefresherNotification.cs create mode 100644 src/Umbraco.Core/Cache/UserCacheRefresherNotification.cs create mode 100644 src/Umbraco.Core/Cache/UserGroupCacheRefresherNotification.cs diff --git a/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs b/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs index b8dccd1f59..8106da11e6 100644 --- a/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs @@ -1,13 +1,14 @@ -using System; +using System; using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Core.Cache { public sealed class ApplicationCacheRefresher : CacheRefresherBase { - public ApplicationCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator) - : base(appCaches, eventAggregator) - { } + public ApplicationCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory) + : base(appCaches, eventAggregator, factory) + { + } #region Define diff --git a/src/Umbraco.Core/Cache/ApplicationCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/ApplicationCacheRefresherNotification.cs new file mode 100644 index 0000000000..3602a1488f --- /dev/null +++ b/src/Umbraco.Core/Cache/ApplicationCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class ApplicationCacheRefresherNotification : CacheRefresherNotification + { + public ApplicationCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/Cache/CacheRefresherBase.cs b/src/Umbraco.Core/Cache/CacheRefresherBase.cs index be4129f928..c5f3d903ab 100644 --- a/src/Umbraco.Core/Cache/CacheRefresherBase.cs +++ b/src/Umbraco.Core/Cache/CacheRefresherBase.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Entities; using Umbraco.Cms.Core.Sync; @@ -10,17 +10,18 @@ namespace Umbraco.Cms.Core.Cache /// /// The actual cache refresher type. /// The actual cache refresher type is used for strongly typed events. - public abstract class CacheRefresherBase< TNotification> : ICacheRefresher - where TNotification : CacheRefresherNotificationBase, new() + public abstract class CacheRefresherBase : ICacheRefresher + where TNotification : CacheRefresherNotification { /// /// Initializes a new instance of the . /// /// A cache helper. - protected CacheRefresherBase(AppCaches appCaches, IEventAggregator eventAggregator) + protected CacheRefresherBase(AppCaches appCaches, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory) { AppCaches = appCaches; EventAggregator = eventAggregator; + NotificationFactory = factory; } #region Define @@ -35,6 +36,11 @@ namespace Umbraco.Cms.Core.Cache /// public abstract string Name { get; } + /// + /// Gets the for + /// + protected ICacheRefresherNotificationFactory NotificationFactory { get; } + #endregion #region Refresher @@ -44,7 +50,7 @@ namespace Umbraco.Cms.Core.Cache /// public virtual void RefreshAll() { - OnCacheUpdated(new TNotification().Init(null, MessageType.RefreshAll)); + OnCacheUpdated(NotificationFactory.Create(null, MessageType.RefreshAll)); } /// @@ -53,7 +59,7 @@ namespace Umbraco.Cms.Core.Cache /// The entity's identifier. public virtual void Refresh(int id) { - OnCacheUpdated(new TNotification().Init(id, MessageType.RefreshById)); + OnCacheUpdated(NotificationFactory.Create(id, MessageType.RefreshById)); } /// @@ -62,7 +68,7 @@ namespace Umbraco.Cms.Core.Cache /// The entity's identifier. public virtual void Refresh(Guid id) { - OnCacheUpdated(new TNotification().Init(id, MessageType.RefreshById)); + OnCacheUpdated(NotificationFactory.Create(id, MessageType.RefreshById)); } /// @@ -71,7 +77,7 @@ namespace Umbraco.Cms.Core.Cache /// The entity's identifier. public virtual void Remove(int id) { - OnCacheUpdated(new TNotification().Init(id, MessageType.RemoveById)); + OnCacheUpdated(NotificationFactory.Create(id, MessageType.RemoveById)); } #endregion @@ -100,7 +106,7 @@ namespace Umbraco.Cms.Core.Cache /// /// The event sender. /// The event arguments. - protected void OnCacheUpdated(CacheRefresherNotificationBase notification) + protected void OnCacheUpdated(CacheRefresherNotification notification) { EventAggregator.Publish(notification); } diff --git a/src/Umbraco.Core/Cache/CacheRefresherNotification.cs b/src/Umbraco.Core/Cache/CacheRefresherNotification.cs new file mode 100644 index 0000000000..442fad0147 --- /dev/null +++ b/src/Umbraco.Core/Cache/CacheRefresherNotification.cs @@ -0,0 +1,22 @@ +using System; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Sync; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.Cache +{ + /// + /// Base class for cache refresher notifications + /// + public abstract class CacheRefresherNotification : INotification + { + public CacheRefresherNotification(object messageObject, MessageType messageType) + { + MessageObject = messageObject ?? throw new ArgumentNullException(nameof(messageObject)); + MessageType = messageType; + } + + public object MessageObject { get; } + public MessageType MessageType { get; } + } +} diff --git a/src/Umbraco.Core/Cache/CacheRefresherNotificationBase.cs b/src/Umbraco.Core/Cache/CacheRefresherNotificationBase.cs deleted file mode 100644 index 7818678cfe..0000000000 --- a/src/Umbraco.Core/Cache/CacheRefresherNotificationBase.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Sync; - -namespace Umbraco.Cms.Core.Cache -{ - /// - /// Event args for cache refresher updates - /// - public abstract class CacheRefresherNotificationBase : INotification - { - public CacheRefresherNotificationBase Init(object msgObject, MessageType type) - { - MessageType = type; - MessageObject = msgObject; - - return this; - } - public object MessageObject { get; private set; } - public MessageType MessageType { get; private set;} - } - public class DataTypeCacheRefresherNotification : CacheRefresherNotificationBase - { - } - - public class UserCacheRefresherNotification : CacheRefresherNotificationBase - { - } - - - public class ContentCacheRefresherNotification : CacheRefresherNotificationBase - { - } - - public class TemplateCacheRefresherNotification : CacheRefresherNotificationBase - { - } - - public class RelationTypeCacheRefresherNotification : CacheRefresherNotificationBase - { - } - - public class PublicAccessCacheRefresherNotification : CacheRefresherNotificationBase - { - } - - public class MemberGroupCacheRefresherNotification : CacheRefresherNotificationBase - { - } - - public class MemberCacheRefresherNotification : CacheRefresherNotificationBase - { - } - - public class MediaCacheRefresherNotification : CacheRefresherNotificationBase - { - } - - public class UserGroupCacheRefresherNotification : CacheRefresherNotificationBase - { - } - - public class LanguageCacheRefresherNotification : CacheRefresherNotificationBase - { - } - public class MacroCacheRefresherNotification : CacheRefresherNotificationBase - { - } - - public class DomainCacheRefresherNotification : CacheRefresherNotificationBase - { - } - - public class ContentTypeCacheRefresherNotification : CacheRefresherNotificationBase - { - } - - public class ApplicationCacheRefresherNotification : CacheRefresherNotificationBase - { - } - public class DictionaryCacheRefresherNotification : CacheRefresherNotificationBase - { - } -} diff --git a/src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs b/src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs new file mode 100644 index 0000000000..9e4210cd2b --- /dev/null +++ b/src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs @@ -0,0 +1,22 @@ +using System; +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + /// + /// A that uses ActivatorUtilities to create the instances + /// + public sealed class CacheRefresherNotificationFactory : ICacheRefresherNotificationFactory + { + private readonly IServiceProvider _serviceProvider; + + public CacheRefresherNotificationFactory(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider; + + /// + /// Create a using ActivatorUtilities + /// + /// The to create + public TNotification Create(object msgObject, MessageType type) where TNotification : CacheRefresherNotification + => _serviceProvider.CreateInstance(new object[] { msgObject, type }); + } +} diff --git a/src/Umbraco.Core/Cache/ContentCacheRefresher.cs b/src/Umbraco.Core/Cache/ContentCacheRefresher.cs index 42674d689b..26cf00a2d9 100644 --- a/src/Umbraco.Core/Cache/ContentCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/ContentCacheRefresher.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Umbraco.Cms.Core.Events; @@ -24,8 +24,9 @@ namespace Umbraco.Cms.Core.Cache IPublishedSnapshotService publishedSnapshotService, IIdKeyMap idKeyMap, IDomainService domainService, - IEventAggregator eventAggregator) - : base(appCaches, serializer, eventAggregator) + IEventAggregator eventAggregator, + ICacheRefresherNotificationFactory factory) + : base(appCaches, serializer, eventAggregator, factory) { _publishedSnapshotService = publishedSnapshotService; _idKeyMap = idKeyMap; diff --git a/src/Umbraco.Core/Cache/ContentCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/ContentCacheRefresherNotification.cs new file mode 100644 index 0000000000..dd76786393 --- /dev/null +++ b/src/Umbraco.Core/Cache/ContentCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class ContentCacheRefresherNotification : CacheRefresherNotification + { + public ContentCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs index 00838865ce..b093df85b8 100644 --- a/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; @@ -26,8 +26,9 @@ namespace Umbraco.Cms.Core.Cache IPublishedModelFactory publishedModelFactory, IIdKeyMap idKeyMap, IContentTypeCommonRepository contentTypeCommonRepository, - IEventAggregator eventAggregator) - : base(appCaches, serializer, eventAggregator) + IEventAggregator eventAggregator, + ICacheRefresherNotificationFactory factory) + : base(appCaches, serializer, eventAggregator, factory) { _publishedSnapshotService = publishedSnapshotService; _publishedModelFactory = publishedModelFactory; diff --git a/src/Umbraco.Core/Cache/ContentTypeCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/ContentTypeCacheRefresherNotification.cs new file mode 100644 index 0000000000..17bd955d03 --- /dev/null +++ b/src/Umbraco.Core/Cache/ContentTypeCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class ContentTypeCacheRefresherNotification : CacheRefresherNotification + { + public ContentTypeCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs index 0eca1a0c20..dfbf9230e2 100644 --- a/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; @@ -22,8 +22,9 @@ namespace Umbraco.Cms.Core.Cache IPublishedSnapshotService publishedSnapshotService, IPublishedModelFactory publishedModelFactory, IIdKeyMap idKeyMap, - IEventAggregator eventAggregator) - : base(appCaches, serializer, eventAggregator) + IEventAggregator eventAggregator, + ICacheRefresherNotificationFactory factory) + : base(appCaches, serializer, eventAggregator, factory) { _publishedSnapshotService = publishedSnapshotService; _publishedModelFactory = publishedModelFactory; diff --git a/src/Umbraco.Core/Cache/DataTypeCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/DataTypeCacheRefresherNotification.cs new file mode 100644 index 0000000000..d64dd53431 --- /dev/null +++ b/src/Umbraco.Core/Cache/DataTypeCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class DataTypeCacheRefresherNotification : CacheRefresherNotification + { + public DataTypeCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs b/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs index 8e21146a96..c812a4aadc 100644 --- a/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; @@ -6,8 +6,8 @@ namespace Umbraco.Cms.Core.Cache { public sealed class DictionaryCacheRefresher : CacheRefresherBase { - public DictionaryCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator) - : base(appCaches, eventAggregator) + public DictionaryCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory) + : base(appCaches, eventAggregator , factory) { } #region Define diff --git a/src/Umbraco.Core/Cache/DictionaryCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/DictionaryCacheRefresherNotification.cs new file mode 100644 index 0000000000..57474466d0 --- /dev/null +++ b/src/Umbraco.Core/Cache/DictionaryCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class DictionaryCacheRefresherNotification : CacheRefresherNotification + { + public DictionaryCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/Cache/DomainCacheRefresher.cs b/src/Umbraco.Core/Cache/DomainCacheRefresher.cs index 098b200632..228baf4b9a 100644 --- a/src/Umbraco.Core/Cache/DomainCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/DomainCacheRefresher.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PublishedCache; @@ -15,8 +15,9 @@ namespace Umbraco.Cms.Core.Cache AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, - IEventAggregator eventAggregator) - : base(appCaches, serializer, eventAggregator) + IEventAggregator eventAggregator, + ICacheRefresherNotificationFactory factory) + : base(appCaches, serializer, eventAggregator, factory) { _publishedSnapshotService = publishedSnapshotService; } diff --git a/src/Umbraco.Core/Cache/DomainCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/DomainCacheRefresherNotification.cs new file mode 100644 index 0000000000..53495ec6cc --- /dev/null +++ b/src/Umbraco.Core/Cache/DomainCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class DomainCacheRefresherNotification : CacheRefresherNotification + { + public DomainCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/Cache/ICacheRefresherNotificationFactory.cs b/src/Umbraco.Core/Cache/ICacheRefresherNotificationFactory.cs new file mode 100644 index 0000000000..c79f7579a7 --- /dev/null +++ b/src/Umbraco.Core/Cache/ICacheRefresherNotificationFactory.cs @@ -0,0 +1,16 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + /// + /// Factory for creating cache refresher notification instances + /// + public interface ICacheRefresherNotificationFactory + { + /// + /// Creates a + /// + /// The to create + TNotification Create(object msgObject, MessageType type) where TNotification : CacheRefresherNotification; + } +} diff --git a/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs b/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs index 4ad5842373..f0946e0e52 100644 --- a/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs +++ b/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Sync; @@ -10,7 +10,7 @@ namespace Umbraco.Cms.Core.Cache /// The actual cache refresher type. /// The actual cache refresher type is used for strongly typed events. public abstract class JsonCacheRefresherBase : CacheRefresherBase, IJsonCacheRefresher - where TNotification : CacheRefresherNotificationBase, new() + where TNotification : CacheRefresherNotification { protected IJsonSerializer JsonSerializer { get; } @@ -21,8 +21,9 @@ namespace Umbraco.Cms.Core.Cache protected JsonCacheRefresherBase( AppCaches appCaches, IJsonSerializer jsonSerializer, - IEventAggregator eventAggregator) - : base(appCaches, eventAggregator) + IEventAggregator eventAggregator, + ICacheRefresherNotificationFactory factory) + : base(appCaches, eventAggregator, factory) { JsonSerializer = jsonSerializer; } @@ -33,7 +34,7 @@ namespace Umbraco.Cms.Core.Cache /// The json payload. public virtual void Refresh(string json) { - OnCacheUpdated(new TNotification().Init(json, MessageType.RefreshByJson)); + OnCacheUpdated(NotificationFactory.Create(json, MessageType.RefreshByJson)); } #region Json diff --git a/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs b/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs index 1e9ff228df..fb65aaa58d 100644 --- a/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PublishedCache; @@ -14,8 +14,9 @@ namespace Umbraco.Cms.Core.Cache AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, - IEventAggregator eventAggregator) - : base(appCaches, serializer, eventAggregator) + IEventAggregator eventAggregator, + ICacheRefresherNotificationFactory factory) + : base(appCaches, serializer, eventAggregator, factory) { _publishedSnapshotService = publishedSnapshotService; } diff --git a/src/Umbraco.Core/Cache/LanguageCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/LanguageCacheRefresherNotification.cs new file mode 100644 index 0000000000..fde0090c28 --- /dev/null +++ b/src/Umbraco.Core/Cache/LanguageCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class LanguageCacheRefresherNotification : CacheRefresherNotification + { + public LanguageCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/Cache/MacroCacheRefresher.cs b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs index 5e6c3294ab..fa4dca5ecc 100644 --- a/src/Umbraco.Core/Cache/MacroCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; @@ -12,8 +12,9 @@ namespace Umbraco.Cms.Core.Cache public MacroCacheRefresher( AppCaches appCaches, IJsonSerializer jsonSerializer, - IEventAggregator eventAggregator) - : base(appCaches, jsonSerializer, eventAggregator) + IEventAggregator eventAggregator, + ICacheRefresherNotificationFactory factory) + : base(appCaches, jsonSerializer, eventAggregator, factory) { } diff --git a/src/Umbraco.Core/Cache/MacroCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/MacroCacheRefresherNotification.cs new file mode 100644 index 0000000000..f5ca7985c8 --- /dev/null +++ b/src/Umbraco.Core/Cache/MacroCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class MacroCacheRefresherNotification : CacheRefresherNotification + { + public MacroCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/Cache/MediaCacheRefresher.cs b/src/Umbraco.Core/Cache/MediaCacheRefresher.cs index a4f424acf2..a0101ab66c 100644 --- a/src/Umbraco.Core/Cache/MediaCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MediaCacheRefresher.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; @@ -15,8 +15,8 @@ namespace Umbraco.Cms.Core.Cache private readonly IPublishedSnapshotService _publishedSnapshotService; private readonly IIdKeyMap _idKeyMap; - public MediaCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IIdKeyMap idKeyMap, IEventAggregator eventAggregator) - : base(appCaches, serializer, eventAggregator) + public MediaCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IIdKeyMap idKeyMap, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory) + : base(appCaches, serializer, eventAggregator, factory) { _publishedSnapshotService = publishedSnapshotService; _idKeyMap = idKeyMap; diff --git a/src/Umbraco.Core/Cache/MediaCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/MediaCacheRefresherNotification.cs new file mode 100644 index 0000000000..afdd60bb4a --- /dev/null +++ b/src/Umbraco.Core/Cache/MediaCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class MediaCacheRefresherNotification : CacheRefresherNotification + { + public MediaCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/Cache/MemberCacheRefresher.cs b/src/Umbraco.Core/Cache/MemberCacheRefresher.cs index 40f324384b..b416889363 100644 --- a/src/Umbraco.Core/Cache/MemberCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MemberCacheRefresher.cs @@ -14,8 +14,8 @@ namespace Umbraco.Cms.Core.Cache { private readonly IIdKeyMap _idKeyMap; - public MemberCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IIdKeyMap idKeyMap, IEventAggregator eventAggregator) - : base(appCaches, serializer, eventAggregator) + public MemberCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IIdKeyMap idKeyMap, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory) + : base(appCaches, serializer, eventAggregator, factory) { _idKeyMap = idKeyMap; } diff --git a/src/Umbraco.Core/Cache/MemberCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/MemberCacheRefresherNotification.cs new file mode 100644 index 0000000000..6154505947 --- /dev/null +++ b/src/Umbraco.Core/Cache/MemberCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class MemberCacheRefresherNotification : CacheRefresherNotification + { + public MemberCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs b/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs index 637d4c8558..1f019e8f30 100644 --- a/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Serialization; @@ -7,8 +7,8 @@ namespace Umbraco.Cms.Core.Cache { public sealed class MemberGroupCacheRefresher : PayloadCacheRefresherBase { - public MemberGroupCacheRefresher(AppCaches appCaches, IJsonSerializer jsonSerializer, IEventAggregator eventAggregator) - : base(appCaches, jsonSerializer, eventAggregator) + public MemberGroupCacheRefresher(AppCaches appCaches, IJsonSerializer jsonSerializer, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory) + : base(appCaches, jsonSerializer, eventAggregator, factory) { } diff --git a/src/Umbraco.Core/Cache/MemberGroupCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/MemberGroupCacheRefresherNotification.cs new file mode 100644 index 0000000000..643e9bd51e --- /dev/null +++ b/src/Umbraco.Core/Cache/MemberGroupCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class MemberGroupCacheRefresherNotification : CacheRefresherNotification + { + public MemberGroupCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs b/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs index dd11e899c0..f7867ae3fe 100644 --- a/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs +++ b/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Sync; @@ -11,8 +11,7 @@ namespace Umbraco.Cms.Core.Cache /// The payload type. /// The actual cache refresher type is used for strongly typed events. public abstract class PayloadCacheRefresherBase : JsonCacheRefresherBase, IPayloadCacheRefresher - where TNotification : CacheRefresherNotificationBase, new() - + where TNotification : CacheRefresherNotification { /// @@ -20,8 +19,8 @@ namespace Umbraco.Cms.Core.Cache /// /// A cache helper. /// - protected PayloadCacheRefresherBase(AppCaches appCaches, IJsonSerializer serializer, IEventAggregator eventAggregator) - : base(appCaches, serializer, eventAggregator) + protected PayloadCacheRefresherBase(AppCaches appCaches, IJsonSerializer serializer, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory) + : base(appCaches, serializer, eventAggregator, factory) { } @@ -40,7 +39,7 @@ namespace Umbraco.Cms.Core.Cache /// The payload. public virtual void Refresh(TPayload[] payloads) { - OnCacheUpdated(new TNotification().Init(payloads, MessageType.RefreshByPayload)); + OnCacheUpdated(NotificationFactory.Create(payloads, MessageType.RefreshByPayload)); } #endregion diff --git a/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs b/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs index 44b108fa23..d833a0aea5 100644 --- a/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; @@ -6,8 +6,8 @@ namespace Umbraco.Cms.Core.Cache { public sealed class PublicAccessCacheRefresher : CacheRefresherBase { - public PublicAccessCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator) - : base(appCaches, eventAggregator) + public PublicAccessCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory) + : base(appCaches, eventAggregator, factory) { } #region Define diff --git a/src/Umbraco.Core/Cache/PublicAccessCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/PublicAccessCacheRefresherNotification.cs new file mode 100644 index 0000000000..d3334571b3 --- /dev/null +++ b/src/Umbraco.Core/Cache/PublicAccessCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class PublicAccessCacheRefresherNotification : CacheRefresherNotification + { + public PublicAccessCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs index d82cef759d..6f15d09554 100644 --- a/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; @@ -7,8 +7,8 @@ namespace Umbraco.Cms.Core.Cache { public sealed class RelationTypeCacheRefresher : CacheRefresherBase { - public RelationTypeCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator) - : base(appCaches, eventAggregator) + public RelationTypeCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory) + : base(appCaches, eventAggregator, factory) { } #region Define diff --git a/src/Umbraco.Core/Cache/RelationTypeCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/RelationTypeCacheRefresherNotification.cs new file mode 100644 index 0000000000..851eba915d --- /dev/null +++ b/src/Umbraco.Core/Cache/RelationTypeCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class RelationTypeCacheRefresherNotification : CacheRefresherNotification + { + public RelationTypeCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs b/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs index 6c33a44545..c098ccb967 100644 --- a/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; @@ -11,8 +11,8 @@ namespace Umbraco.Cms.Core.Cache private readonly IIdKeyMap _idKeyMap; private readonly IContentTypeCommonRepository _contentTypeCommonRepository; - public TemplateCacheRefresher(AppCaches appCaches, IIdKeyMap idKeyMap, IContentTypeCommonRepository contentTypeCommonRepository, IEventAggregator eventAggregator) - : base(appCaches, eventAggregator) + public TemplateCacheRefresher(AppCaches appCaches, IIdKeyMap idKeyMap, IContentTypeCommonRepository contentTypeCommonRepository, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory) + : base(appCaches, eventAggregator, factory) { _idKeyMap = idKeyMap; _contentTypeCommonRepository = contentTypeCommonRepository; diff --git a/src/Umbraco.Core/Cache/TemplateCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/TemplateCacheRefresherNotification.cs new file mode 100644 index 0000000000..88ff2284cb --- /dev/null +++ b/src/Umbraco.Core/Cache/TemplateCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class TemplateCacheRefresherNotification : CacheRefresherNotification + { + public TemplateCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/Cache/UserCacheRefresher.cs b/src/Umbraco.Core/Cache/UserCacheRefresher.cs index b8fd75702e..201ecc1f19 100644 --- a/src/Umbraco.Core/Cache/UserCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/UserCacheRefresher.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Persistence.Repositories; @@ -7,8 +7,8 @@ namespace Umbraco.Cms.Core.Cache { public sealed class UserCacheRefresher : CacheRefresherBase { - public UserCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator) - : base(appCaches, eventAggregator) + public UserCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory) + : base(appCaches, eventAggregator, factory) { } #region Define diff --git a/src/Umbraco.Core/Cache/UserCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/UserCacheRefresherNotification.cs new file mode 100644 index 0000000000..b91a7b93c8 --- /dev/null +++ b/src/Umbraco.Core/Cache/UserCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class UserCacheRefresherNotification : CacheRefresherNotification + { + public UserCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs b/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs index f4456ef696..2d278972ec 100644 --- a/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Persistence.Repositories; @@ -13,8 +13,8 @@ namespace Umbraco.Cms.Core.Cache /// public sealed class UserGroupCacheRefresher : CacheRefresherBase { - public UserGroupCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator) - : base(appCaches, eventAggregator) + public UserGroupCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory) + : base(appCaches, eventAggregator, factory) { } #region Define diff --git a/src/Umbraco.Core/Cache/UserGroupCacheRefresherNotification.cs b/src/Umbraco.Core/Cache/UserGroupCacheRefresherNotification.cs new file mode 100644 index 0000000000..a0adf915d8 --- /dev/null +++ b/src/Umbraco.Core/Cache/UserGroupCacheRefresherNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Cache +{ + public class UserGroupCacheRefresherNotification : CacheRefresherNotification + { + public UserGroupCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType) + { + } + } +} diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index 5249676fb6..82cf6ffa84 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -189,6 +189,7 @@ namespace Umbraco.Cms.Core.DependencyInjection // register distributed cache Services.AddUnique(f => new DistributedCache(f.GetRequiredService(), f.GetRequiredService())); + Services.AddUnique(); // register the http context and umbraco context accessors // we *should* use the HttpContextUmbracoContextAccessor, however there are cases when From 4071df322afd67872fe11c411299b8c7a9dc822a Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 15 Mar 2021 13:40:00 +1100 Subject: [PATCH 33/42] Adds missing null check on tagHelperOutput.TagName which can be null --- .../Views/UmbracoViewPage.cs | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.Common/Views/UmbracoViewPage.cs b/src/Umbraco.Web.Common/Views/UmbracoViewPage.cs index 66d9b3c3f2..f8627dc2e9 100644 --- a/src/Umbraco.Web.Common/Views/UmbracoViewPage.cs +++ b/src/Umbraco.Web.Common/Views/UmbracoViewPage.cs @@ -118,31 +118,29 @@ namespace Umbraco.Cms.Web.Common.Views // ASP.NET default value is text/html if (Context.Response.ContentType.InvariantContains("text/html")) { - if (UmbracoContext.IsDebug || UmbracoContext.InPreviewMode) + if ((UmbracoContext.IsDebug || UmbracoContext.InPreviewMode) + && tagHelperOutput.TagName != null + && tagHelperOutput.TagName.Equals("body", StringComparison.InvariantCultureIgnoreCase)) { + string markupToInject; - if (tagHelperOutput.TagName.Equals("body", StringComparison.InvariantCultureIgnoreCase)) + if (UmbracoContext.InPreviewMode) { - string markupToInject; - - if (UmbracoContext.InPreviewMode) - { - // creating previewBadge markup - markupToInject = - string.Format( - ContentSettings.PreviewBadge, - IOHelper.ResolveUrl(GlobalSettings.UmbracoPath), - Context.Request.GetEncodedUrl(), - UmbracoContext.PublishedRequest.PublishedContent.Id); - } - else - { - // creating mini-profiler markup - markupToInject = ProfilerHtml.Render(); - } - - tagHelperOutput.Content.AppendHtml(markupToInject); + // creating previewBadge markup + markupToInject = + string.Format( + ContentSettings.PreviewBadge, + IOHelper.ResolveUrl(GlobalSettings.UmbracoPath), + Context.Request.GetEncodedUrl(), + UmbracoContext.PublishedRequest.PublishedContent.Id); } + else + { + // creating mini-profiler markup + markupToInject = ProfilerHtml.Render(); + } + + tagHelperOutput.Content.AppendHtml(markupToInject); } } } From db863b3250fd5604861c7c2039c2ddf06cdfb20e Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 15 Mar 2021 13:45:09 +1100 Subject: [PATCH 34/42] missing namespace after file split. --- src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs b/src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs index 9e4210cd2b..fc9cdefe27 100644 --- a/src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs +++ b/src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs @@ -1,4 +1,5 @@ -using System; +using System; +using Umbraco.Extensions; using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Core.Cache From 0374f6c2f547e4f828e5ce0f128491c19e59f46d Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 15 Mar 2021 06:32:40 +0100 Subject: [PATCH 35/42] Update src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs Co-authored-by: Shannon Deminick --- .../UmbracoBuilderDependencyInjectionExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs b/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs index e8b54d3af0..8762dc6cc3 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs @@ -111,7 +111,6 @@ namespace Umbraco.Extensions builder.Services.AddSingleton(); builder.AddNotificationHandler(); builder.AddNotificationHandler(); - builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); From 4ad48321b93022c18a34bbe3fd9255bd7a4ca0ee Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 15 Mar 2021 11:17:23 +0100 Subject: [PATCH 36/42] Remove debugger break/launch --- .../Upgrade/V_9_0_0/MigrateLogViewerQueriesFromFileToDb.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MigrateLogViewerQueriesFromFileToDb.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MigrateLogViewerQueriesFromFileToDb.cs index 88ffe1a66c..0211e6b108 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MigrateLogViewerQueriesFromFileToDb.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MigrateLogViewerQueriesFromFileToDb.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using Newtonsoft.Json; @@ -66,8 +65,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0 public override void Migrate() { - Debugger.Launch(); - Debugger.Break(); CreateDatabaseTable(); MigrateFileContentToDB(); } From fa11a812a026b0e17056ab3739f099be42ceddb5 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Mon, 15 Mar 2021 13:41:14 +0000 Subject: [PATCH 37/42] Fix karma.conf JS test for folder names to make UNIX tests pass --- src/Umbraco.Web.UI.Client/test/config/karma.conf.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js index 86a65d1698..ff05ee6a57 100644 --- a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js +++ b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js @@ -34,12 +34,12 @@ module.exports = function (config) { 'test/config/app.unit.js', //application files - '../Umbraco.Web.UI/Umbraco/js/*.controllers.min.js', - '../Umbraco.Web.UI/Umbraco/js/*.directives.min.js', - '../Umbraco.Web.UI/Umbraco/js/*.filters.min.js', - '../Umbraco.Web.UI/Umbraco/js/*.services.min.js', - '../Umbraco.Web.UI/Umbraco/js/*.interceptors.min.js', - '../Umbraco.Web.UI/Umbraco/js/*.resources.min.js', + '../Umbraco.Web.UI/umbraco/js/*.controllers.min.js', + '../Umbraco.Web.UI/umbraco/js/*.directives.min.js', + '../Umbraco.Web.UI/umbraco/js/*.filters.min.js', + '../Umbraco.Web.UI/umbraco/js/*.services.min.js', + '../Umbraco.Web.UI/umbraco/js/*.interceptors.min.js', + '../Umbraco.Web.UI/umbraco/js/*.resources.min.js', //mocked data and routing 'src/common/mocks/umbraco.servervariables.js', From f47298d2dbe6b23c7fef37c137e7074085ca1065 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 15 Mar 2021 15:00:01 +0100 Subject: [PATCH 38/42] Removed unused code --- .../Security/BackOfficeUserManagerAuditer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs index bb6d9563ec..c74d445a89 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs @@ -22,7 +22,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security private readonly IAuditService _auditService; private readonly IUserService _userService; - public BackOfficeUserManagerAuditer(IAuditService auditService, IUserService userService/*, GlobalSettings globalSettings*/) + public BackOfficeUserManagerAuditer(IAuditService auditService, IUserService userService) { _auditService = auditService; _userService = userService; From d8c7193eef9ff09110005d08cebbdca170b61ca4 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 15 Mar 2021 15:00:58 +0100 Subject: [PATCH 39/42] Fixed issue with multiple threads reading the appSettings file at the same time, by locking (Found when site has to upgrade) --- .../Configuration/JsonConfigManipulator.cs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Infrastructure/Configuration/JsonConfigManipulator.cs b/src/Umbraco.Infrastructure/Configuration/JsonConfigManipulator.cs index 6057ce8a82..da83a7c913 100644 --- a/src/Umbraco.Infrastructure/Configuration/JsonConfigManipulator.cs +++ b/src/Umbraco.Infrastructure/Configuration/JsonConfigManipulator.cs @@ -5,12 +5,12 @@ using Microsoft.Extensions.Configuration.Json; using Microsoft.Extensions.FileProviders; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Umbraco.Cms.Core.Configuration; namespace Umbraco.Cms.Core.Configuration { public class JsonConfigManipulator : IConfigManipulator { + private static readonly object s_locker = new object(); private readonly IConfiguration _configuration; public JsonConfigManipulator(IConfiguration configuration) @@ -162,22 +162,26 @@ namespace Umbraco.Cms.Core.Configuration token?.Parent?.Remove(); } + + private static void SaveJson(JsonConfigurationProvider provider, JObject json) { - if (provider.Source.FileProvider is PhysicalFileProvider physicalFileProvider) + lock (s_locker) { - var jsonFilePath = Path.Combine(physicalFileProvider.Root, provider.Source.Path); + if (provider.Source.FileProvider is PhysicalFileProvider physicalFileProvider) + { + var jsonFilePath = Path.Combine(physicalFileProvider.Root, provider.Source.Path); - using (var sw = new StreamWriter(jsonFilePath, false)) - using (var jsonTextWriter = new JsonTextWriter(sw) - { - Formatting = Formatting.Indented, - }) - { - json?.WriteTo(jsonTextWriter); + using (var sw = new StreamWriter(jsonFilePath, false)) + using (var jsonTextWriter = new JsonTextWriter(sw) + { + Formatting = Formatting.Indented, + }) + { + json?.WriteTo(jsonTextWriter); + } } } - } private static JObject GetJson(JsonConfigurationProvider provider) From 2e8f5fef6ae145822941f3dfb01af52e67271417 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 15 Mar 2021 18:36:13 +0100 Subject: [PATCH 40/42] Fix typo --- src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs | 2 +- ...tifcationHandler.cs => RelateOnCopyNotificationHandler.cs} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/Umbraco.Infrastructure/Events/{RelateOnCopyNotifcationHandler.cs => RelateOnCopyNotificationHandler.cs} (89%) diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs index 906f10c524..16b89bb955 100644 --- a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs +++ b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs @@ -35,7 +35,7 @@ namespace Umbraco.Cms.Core.Compose // add handlers for building content relations builder - .AddNotificationHandler, RelateOnCopyNotifcationHandler>() + .AddNotificationHandler, RelateOnCopyNotificationHandler>() .AddNotificationHandler, RelateOnTrashNotificationHandler>() .AddNotificationHandler, RelateOnTrashNotificationHandler>() .AddNotificationHandler, RelateOnTrashNotificationHandler>() diff --git a/src/Umbraco.Infrastructure/Events/RelateOnCopyNotifcationHandler.cs b/src/Umbraco.Infrastructure/Events/RelateOnCopyNotificationHandler.cs similarity index 89% rename from src/Umbraco.Infrastructure/Events/RelateOnCopyNotifcationHandler.cs rename to src/Umbraco.Infrastructure/Events/RelateOnCopyNotificationHandler.cs index f8e162dc9e..58c5096ad4 100644 --- a/src/Umbraco.Infrastructure/Events/RelateOnCopyNotifcationHandler.cs +++ b/src/Umbraco.Infrastructure/Events/RelateOnCopyNotificationHandler.cs @@ -7,12 +7,12 @@ using Umbraco.Cms.Infrastructure.Services.Notifications; namespace Umbraco.Cms.Core.Events { - public class RelateOnCopyNotifcationHandler : INotificationHandler> + public class RelateOnCopyNotificationHandler : INotificationHandler> { private readonly IRelationService _relationService; private readonly IAuditService _auditService; - public RelateOnCopyNotifcationHandler(IRelationService relationService, IAuditService auditService) + public RelateOnCopyNotificationHandler(IRelationService relationService, IAuditService auditService) { _relationService = relationService; _auditService = auditService; From 7593ddfaa830b8fe2c3b57c94ffc35a4b133a8c6 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 16 Mar 2021 06:55:55 +0100 Subject: [PATCH 41/42] Explicitly typed notifications instead of generic ones --- .../Compose/NotificationsComposer.cs | 60 ++++++------ .../Events/RelateOnCopyNotificationHandler.cs | 4 +- .../RelateOnTrashNotificationHandler.cs | 16 +-- .../Events/UserNotificationsHandler.cs | 36 +++---- ...ropertyEditorContentNotificationHandler.cs | 8 +- .../FileUploadPropertyEditor.cs | 12 +-- .../ImageCropperPropertyEditor.cs | 12 +-- .../Routing/RedirectTrackingHandler.cs | 16 +-- .../Services/Implement/ContentService.cs | 98 +++++++++---------- .../Services/Implement/MediaService.cs | 44 ++++----- .../ContentCopiedNotification.cs | 16 +++ .../ContentCopyingNotification.cs | 16 +++ .../ContentDeletedBlueprintNotification.cs | 22 +++++ .../ContentDeletedNotification.cs | 15 +++ .../ContentDeletedVersionsNotification.cs | 16 +++ .../ContentDeletingNotification.cs | 20 ++++ .../ContentDeletingVersionsNotification.cs | 16 +++ .../ContentEmptiedRecycleBinNotification.cs | 15 +++ .../ContentEmptyingRecycleBinNotification.cs | 15 +++ .../Notifications/ContentMovedNotification.cs | 20 ++++ .../ContentMovedToRecycleBinNotification.cs | 20 ++++ .../ContentMovingNotification.cs | 20 ++++ .../ContentMovingToRecycleBinNotification.cs | 20 ++++ .../ContentNotificationExtensions.cs | 12 +-- .../ContentPublishedNotification.cs | 22 +++++ .../ContentPublishingNotification.cs | 22 +++++ .../ContentRolledBackNotification.cs | 15 +++ .../ContentRollingBackNotification.cs | 15 +++ .../ContentSavedBlueprintNotification.cs | 17 ++++ .../Notifications/ContentSavedNotification.cs | 20 ++++ .../ContentSavingNotification.cs | 20 ++++ .../ContentSendingToPublishNotification.cs | 17 ++++ .../ContentSentToPublishNotification.cs | 17 ++++ .../ContentSortedNotification.cs | 16 +++ .../ContentSortingNotification.cs | 16 +++ .../ContentUnpublishedNotification.cs | 22 +++++ .../ContentUnpublishingNotification.cs | 22 +++++ .../Notifications/CopiedNotification.cs | 4 +- .../Notifications/CopyingNotification.cs | 4 +- .../Notifications/DeletedNotification.cs | 4 +- .../DeletedVersionsNotification.cs | 4 +- .../Notifications/DeletingNotification.cs | 6 +- .../DeletingVersionsNotification.cs | 4 +- .../EmptiedRecycleBinNotification.cs | 4 +- .../EmptyingRecycleBinNotification.cs | 4 +- .../Notifications/MediaDeletedNotification.cs | 15 +++ .../MediaDeletedVersionsNotification.cs | 16 +++ .../MediaDeletingNotification.cs | 20 ++++ .../MediaDeletingVersionsNotification.cs | 16 +++ .../MediaEmptiedRecycleBinNotification.cs | 15 +++ .../MediaEmptyingRecycleBinNotification.cs | 15 +++ .../Notifications/MediaMovedNotification.cs | 20 ++++ .../MediaMovedToRecycleBinNotification.cs | 20 ++++ .../Notifications/MediaMovingNotification.cs | 20 ++++ .../MediaMovingToRecycleBinNotification.cs | 20 ++++ .../Notifications/MediaSavedNotification.cs | 20 ++++ ...fication.cs => MediaSavingNotification.cs} | 9 +- .../Notifications/MovedNotification.cs | 6 +- .../MovedToRecycleBinNotification.cs | 6 +- .../Notifications/MovingNotification.cs | 6 +- .../MovingToRecycleBinNotification.cs | 6 +- .../Notifications/PublishedNotification.cs | 21 ---- .../Notifications/PublishingNotification.cs | 21 ---- .../Notifications/RolledBackNotification.cs | 4 +- .../Notifications/RollingBackNotification.cs | 4 +- .../SavedBlueprintNotification.cs | 16 --- .../Notifications/SavedNotification.cs | 6 +- .../Notifications/SavingNotification.cs | 6 +- .../SendingToPublishNotification.cs | 16 --- .../SentToPublishNotification.cs | 16 --- .../Notifications/SortedNotification.cs | 4 +- .../Notifications/SortingNotification.cs | 4 +- .../Notifications/UnpublishedNotification.cs | 21 ---- .../Notifications/UnpublishingNotification.cs | 21 ---- .../ContentServiceNotificationTests.cs | 48 ++++----- .../Services/ContentServiceTests.cs | 24 ++--- .../Services/ContentTypeServiceTests.cs | 10 +- .../Services/MediaTypeServiceTests.cs | 10 +- .../Scoping/ScopedNuCacheTests.cs | 8 +- src/Umbraco.Tests/Scoping/ScopedXmlTests.cs | 8 +- 80 files changed, 929 insertions(+), 393 deletions(-) create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentCopiedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentCopyingNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentDeletedBlueprintNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentDeletedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentDeletedVersionsNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentDeletingNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentDeletingVersionsNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentEmptiedRecycleBinNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentEmptyingRecycleBinNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentMovedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentMovedToRecycleBinNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentMovingNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentMovingToRecycleBinNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentPublishedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentPublishingNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentRolledBackNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentRollingBackNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentSavedBlueprintNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentSavedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentSavingNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentSendingToPublishNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentSentToPublishNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentSortedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentSortingNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentUnpublishedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/ContentUnpublishingNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/MediaDeletedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/MediaDeletedVersionsNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/MediaDeletingNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/MediaDeletingVersionsNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/MediaEmptiedRecycleBinNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/MediaEmptyingRecycleBinNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/MediaMovedNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/MediaMovedToRecycleBinNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/MediaMovingNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/MediaMovingToRecycleBinNotification.cs create mode 100644 src/Umbraco.Infrastructure/Services/Notifications/MediaSavedNotification.cs rename src/Umbraco.Infrastructure/Services/Notifications/{DeletedBlueprintNotification.cs => MediaSavingNotification.cs} (54%) delete mode 100644 src/Umbraco.Infrastructure/Services/Notifications/PublishedNotification.cs delete mode 100644 src/Umbraco.Infrastructure/Services/Notifications/PublishingNotification.cs delete mode 100644 src/Umbraco.Infrastructure/Services/Notifications/SavedBlueprintNotification.cs delete mode 100644 src/Umbraco.Infrastructure/Services/Notifications/SendingToPublishNotification.cs delete mode 100644 src/Umbraco.Infrastructure/Services/Notifications/SentToPublishNotification.cs delete mode 100644 src/Umbraco.Infrastructure/Services/Notifications/UnpublishedNotification.cs delete mode 100644 src/Umbraco.Infrastructure/Services/Notifications/UnpublishingNotification.cs diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs index 16b89bb955..c760c33b71 100644 --- a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs +++ b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs @@ -23,45 +23,45 @@ namespace Umbraco.Cms.Core.Compose // add handlers for sending user notifications (i.e. emails) builder.Services.AddUnique(); builder - .AddNotificationHandler, UserNotificationsHandler>() - .AddNotificationHandler, UserNotificationsHandler>() - .AddNotificationHandler, UserNotificationsHandler>() - .AddNotificationHandler, UserNotificationsHandler>() - .AddNotificationHandler, UserNotificationsHandler>() - .AddNotificationHandler, UserNotificationsHandler>() - .AddNotificationHandler, UserNotificationsHandler>() - .AddNotificationHandler, UserNotificationsHandler>() - .AddNotificationHandler, UserNotificationsHandler>(); + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler(); // add handlers for building content relations builder - .AddNotificationHandler, RelateOnCopyNotificationHandler>() - .AddNotificationHandler, RelateOnTrashNotificationHandler>() - .AddNotificationHandler, RelateOnTrashNotificationHandler>() - .AddNotificationHandler, RelateOnTrashNotificationHandler>() - .AddNotificationHandler, RelateOnTrashNotificationHandler>(); + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler(); // add notification handlers for property editors builder - .AddNotificationHandler, BlockEditorPropertyHandler>() - .AddNotificationHandler, BlockEditorPropertyHandler>() - .AddNotificationHandler, NestedContentPropertyHandler>() - .AddNotificationHandler, NestedContentPropertyHandler>() - .AddNotificationHandler, FileUploadPropertyEditor>() - .AddNotificationHandler, FileUploadPropertyEditor>() - .AddNotificationHandler, FileUploadPropertyEditor>() - .AddNotificationHandler, FileUploadPropertyEditor>() - .AddNotificationHandler, ImageCropperPropertyEditor>() - .AddNotificationHandler, ImageCropperPropertyEditor>() - .AddNotificationHandler, ImageCropperPropertyEditor>() - .AddNotificationHandler, ImageCropperPropertyEditor>(); + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler(); // add notification handlers for redirect tracking builder - .AddNotificationHandler, RedirectTrackingHandler>() - .AddNotificationHandler, RedirectTrackingHandler>() - .AddNotificationHandler, RedirectTrackingHandler>() - .AddNotificationHandler, RedirectTrackingHandler>(); + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler(); } } } diff --git a/src/Umbraco.Infrastructure/Events/RelateOnCopyNotificationHandler.cs b/src/Umbraco.Infrastructure/Events/RelateOnCopyNotificationHandler.cs index 58c5096ad4..f88b529461 100644 --- a/src/Umbraco.Infrastructure/Events/RelateOnCopyNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/Events/RelateOnCopyNotificationHandler.cs @@ -7,7 +7,7 @@ using Umbraco.Cms.Infrastructure.Services.Notifications; namespace Umbraco.Cms.Core.Events { - public class RelateOnCopyNotificationHandler : INotificationHandler> + public class RelateOnCopyNotificationHandler : INotificationHandler { private readonly IRelationService _relationService; private readonly IAuditService _auditService; @@ -18,7 +18,7 @@ namespace Umbraco.Cms.Core.Events _auditService = auditService; } - public void Handle(CopiedNotification notification) + public void Handle(ContentCopiedNotification notification) { if (notification.RelateToOriginal == false) { diff --git a/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs b/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs index 9ec248b728..0ea5844b92 100644 --- a/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs @@ -12,10 +12,10 @@ namespace Umbraco.Cms.Core.Events { // TODO: lots of duplicate code in this one, refactor public sealed class RelateOnTrashNotificationHandler : - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler> + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler { private readonly IRelationService _relationService; private readonly IEntityService _entityService; @@ -37,7 +37,7 @@ namespace Umbraco.Cms.Core.Events _scopeProvider = scopeProvider; } - public void Handle(MovedNotification notification) + public void Handle(ContentMovedNotification notification) { foreach (var item in notification.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinContentString))) { @@ -51,7 +51,7 @@ namespace Umbraco.Cms.Core.Events } } - public void Handle(MovedToRecycleBinNotification notification) + public void Handle(ContentMovedToRecycleBinNotification notification) { using (var scope = _scopeProvider.CreateScope()) { @@ -97,7 +97,7 @@ namespace Umbraco.Cms.Core.Events } } - public void Handle(MovedNotification notification) + public void Handle(MediaMovedNotification notification) { foreach (var item in notification.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinMediaString))) { @@ -111,7 +111,7 @@ namespace Umbraco.Cms.Core.Events } - public void Handle(MovedToRecycleBinNotification notification) + public void Handle(MediaMovedToRecycleBinNotification notification) { using (var scope = _scopeProvider.CreateScope()) { diff --git a/src/Umbraco.Infrastructure/Events/UserNotificationsHandler.cs b/src/Umbraco.Infrastructure/Events/UserNotificationsHandler.cs index a099a66d8e..62da73c28b 100644 --- a/src/Umbraco.Infrastructure/Events/UserNotificationsHandler.cs +++ b/src/Umbraco.Infrastructure/Events/UserNotificationsHandler.cs @@ -21,15 +21,15 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Events { public sealed class UserNotificationsHandler : - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler> + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler { private readonly Notifier _notifier; private readonly ActionCollection _actions; @@ -42,7 +42,7 @@ namespace Umbraco.Cms.Core.Events _contentService = contentService; } - public void Handle(SavedNotification notification) + public void Handle(ContentSavedNotification notification) { var newEntities = new List(); var updatedEntities = new List(); @@ -66,7 +66,7 @@ namespace Umbraco.Cms.Core.Events _notifier.Notify(_actions.GetAction(), updatedEntities.ToArray()); } - public void Handle(SortedNotification notification) + public void Handle(ContentSortedNotification notification) { var parentId = notification.SortedEntities.Select(x => x.ParentId).Distinct().ToList(); if (parentId.Count != 1) @@ -84,9 +84,9 @@ namespace Umbraco.Cms.Core.Events _notifier.Notify(_actions.GetAction(), new[] { parent }); } - public void Handle(PublishedNotification notification) => _notifier.Notify(_actions.GetAction(), notification.PublishedEntities.ToArray()); + public void Handle(ContentPublishedNotification notification) => _notifier.Notify(_actions.GetAction(), notification.PublishedEntities.ToArray()); - public void Handle(MovedNotification notification) + public void Handle(ContentMovedNotification notification) { // notify about the move for all moved items _notifier.Notify(_actions.GetAction(), notification.MoveInfoCollection.Select(m => m.Entity).ToArray()); @@ -102,15 +102,15 @@ namespace Umbraco.Cms.Core.Events } } - public void Handle(MovedToRecycleBinNotification notification) => _notifier.Notify(_actions.GetAction(), notification.MoveInfoCollection.Select(m => m.Entity).ToArray()); + public void Handle(ContentMovedToRecycleBinNotification notification) => _notifier.Notify(_actions.GetAction(), notification.MoveInfoCollection.Select(m => m.Entity).ToArray()); - public void Handle(CopiedNotification notification) => _notifier.Notify(_actions.GetAction(), notification.Original); + public void Handle(ContentCopiedNotification notification) => _notifier.Notify(_actions.GetAction(), notification.Original); - public void Handle(RolledBackNotification notification) => _notifier.Notify(_actions.GetAction(), notification.Entity); + public void Handle(ContentRolledBackNotification notification) => _notifier.Notify(_actions.GetAction(), notification.Entity); - public void Handle(SentToPublishNotification notification) => _notifier.Notify(_actions.GetAction(), notification.Entity); + public void Handle(ContentSentToPublishNotification notification) => _notifier.Notify(_actions.GetAction(), notification.Entity); - public void Handle(UnpublishedNotification notification) => _notifier.Notify(_actions.GetAction(), notification.UnpublishedEntities.ToArray()); + public void Handle(ContentUnpublishedNotification notification) => _notifier.Notify(_actions.GetAction(), notification.UnpublishedEntities.ToArray()); /// /// This class is used to send the notifications diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs index c2d9ab20be..23fb498d77 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs @@ -10,14 +10,14 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors { public abstract class ComplexPropertyEditorContentNotificationHandler : - INotificationHandler>, - INotificationHandler> + INotificationHandler, + INotificationHandler { protected abstract string EditorAlias { get; } protected abstract string FormatPropertyValue(string rawJson, bool onlyMissingKeys); - public void Handle(SavingNotification notification) + public void Handle(ContentSavingNotification notification) { foreach (var entity in notification.SavedEntities) { @@ -26,7 +26,7 @@ namespace Umbraco.Cms.Core.PropertyEditors } } - public void Handle(CopyingNotification notification) + public void Handle(ContentCopyingNotification notification) { var props = notification.Copy.GetPropertiesByEditor(EditorAlias); UpdatePropertyValues(props, false); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs index a43531bb83..15d56abfb3 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs @@ -26,8 +26,8 @@ namespace Umbraco.Cms.Core.PropertyEditors Group = Constants.PropertyEditors.Groups.Media, Icon = "icon-download-alt")] public class FileUploadPropertyEditor : DataEditor, IMediaUrlGenerator, - INotificationHandler>, INotificationHandler>, - INotificationHandler>, INotificationHandler> + INotificationHandler, INotificationHandler, + INotificationHandler, INotificationHandler { private readonly IMediaFileSystem _mediaFileSystem; private readonly ContentSettings _contentSettings; @@ -126,7 +126,7 @@ namespace Umbraco.Cms.Core.PropertyEditors } } - public void Handle(CopiedNotification notification) + public void Handle(ContentCopiedNotification notification) { // get the upload field properties with a value var properties = notification.Original.Properties.Where(IsUploadField); @@ -158,9 +158,9 @@ namespace Umbraco.Cms.Core.PropertyEditors } } - public void Handle(DeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); + public void Handle(ContentDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); - public void Handle(DeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); + public void Handle(MediaDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); private void DeleteContainedFiles(IEnumerable deletedEntities) { @@ -168,7 +168,7 @@ namespace Umbraco.Cms.Core.PropertyEditors _mediaFileSystem.DeleteMediaFiles(filePathsToDelete); } - public void Handle(SavingNotification notification) + public void Handle(MediaSavingNotification notification) { foreach (var entity in notification.SavedEntities) { diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs index d4c0c94054..52972e9f49 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs @@ -33,8 +33,8 @@ namespace Umbraco.Cms.Core.PropertyEditors Group = Constants.PropertyEditors.Groups.Media, Icon = "icon-crop")] public class ImageCropperPropertyEditor : DataEditor, IMediaUrlGenerator, - INotificationHandler>, INotificationHandler>, - INotificationHandler>, INotificationHandler> + INotificationHandler, INotificationHandler, + INotificationHandler, INotificationHandler { private readonly IMediaFileSystem _mediaFileSystem; private readonly ContentSettings _contentSettings; @@ -182,7 +182,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// /// After a content has been copied, also copy uploaded files. /// - public void Handle(CopiedNotification notification) + public void Handle(ContentCopiedNotification notification) { // get the image cropper field properties var properties = notification.Original.Properties.Where(IsCropperField); @@ -214,9 +214,9 @@ namespace Umbraco.Cms.Core.PropertyEditors } } - public void Handle(DeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); + public void Handle(ContentDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); - public void Handle(DeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); + public void Handle(MediaDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities); private void DeleteContainedFiles(IEnumerable deletedEntities) { @@ -224,7 +224,7 @@ namespace Umbraco.Cms.Core.PropertyEditors _mediaFileSystem.DeleteMediaFiles(filePathsToDelete); } - public void Handle(SavingNotification notification) + public void Handle(MediaSavingNotification notification) { foreach (var entity in notification.SavedEntities) { diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs index 1499897b11..19dc90181a 100644 --- a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs @@ -24,10 +24,10 @@ namespace Umbraco.Cms.Core.Routing /// /// recycle bin = moving to and from does nothing: to = the node is gone, where would we redirect? from = same public sealed class RedirectTrackingHandler : - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler> + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler { private readonly IOptionsMonitor _webRoutingSettings; private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; @@ -44,13 +44,13 @@ namespace Umbraco.Cms.Core.Routing _variationContextAccessor = variationContextAccessor; } - public void Handle(PublishingNotification notification) => StoreOldRoutes(notification.PublishedEntities, notification); + public void Handle(ContentPublishingNotification notification) => StoreOldRoutes(notification.PublishedEntities, notification); - public void Handle(PublishedNotification notification) => CreateRedirectsForOldRoutes(notification); + public void Handle(ContentPublishedNotification notification) => CreateRedirectsForOldRoutes(notification); - public void Handle(MovingNotification notification) => StoreOldRoutes(notification.MoveInfoCollection.Select(m => m.Entity), notification); + public void Handle(ContentMovingNotification notification) => StoreOldRoutes(notification.MoveInfoCollection.Select(m => m.Entity), notification); - public void Handle(MovedNotification notification) => CreateRedirectsForOldRoutes(notification); + public void Handle(ContentMovedNotification notification) => CreateRedirectsForOldRoutes(notification); private void StoreOldRoutes(IEnumerable entities, IStatefulNotification notification) { diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index 9829430ec2..7c3b3db346 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -748,7 +748,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var savingNotification = new SavingNotification(content, evtMsgs); + var savingNotification = new ContentSavingNotification(content, evtMsgs); if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); @@ -774,7 +774,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - scope.Notifications.Publish(new SavedNotification(content, evtMsgs).WithStateFrom(savingNotification)); + scope.Notifications.Publish(new ContentSavedNotification(content, evtMsgs).WithStateFrom(savingNotification)); } var changeType = TreeChangeTypes.RefreshNode; scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); @@ -803,7 +803,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var savingNotification = new SavingNotification(contentsA, evtMsgs); + var savingNotification = new ContentSavingNotification(contentsA, evtMsgs); if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); @@ -824,7 +824,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - scope.Notifications.Publish(new SavedNotification(contentsA, evtMsgs).WithStateFrom(savingNotification)); + scope.Notifications.Publish(new ContentSavedNotification(contentsA, evtMsgs).WithStateFrom(savingNotification)); } scope.Events.Dispatch(TreeChanged, this, treeChanges.ToEventArgs()); Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Saved multiple content"); @@ -868,7 +868,7 @@ namespace Umbraco.Cms.Core.Services.Implement var allLangs = _languageRepository.GetMany().ToList(); - var savingNotification = new SavingNotification(content, evtMsgs); + var savingNotification = new ContentSavingNotification(content, evtMsgs); if (scope.Notifications.PublishCancelable(savingNotification)) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); @@ -909,7 +909,7 @@ namespace Umbraco.Cms.Core.Services.Implement var evtMsgs = EventMessagesFactory.Get(); - var savingNotification = new SavingNotification(content, evtMsgs); + var savingNotification = new ContentSavingNotification(content, evtMsgs); if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); @@ -975,7 +975,7 @@ namespace Umbraco.Cms.Core.Services.Implement var allLangs = _languageRepository.GetMany().ToList(); - var savingNotification = new SavingNotification(content, evtMsgs); + var savingNotification = new ContentSavingNotification(content, evtMsgs); if (scope.Notifications.PublishCancelable(savingNotification)) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); @@ -1047,7 +1047,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); - var savingNotification = new SavingNotification(content, evtMsgs); + var savingNotification = new ContentSavingNotification(content, evtMsgs); if (scope.Notifications.PublishCancelable(savingNotification)) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); @@ -1219,7 +1219,7 @@ namespace Umbraco.Cms.Core.Services.Implement // raise the Saved event, always if (raiseEvents) { - scope.Notifications.Publish(new SavedNotification(content, evtMsgs).WithState(notificationState)); + scope.Notifications.Publish(new ContentSavedNotification(content, evtMsgs).WithState(notificationState)); } if (unpublishing) // we have tried to unpublish - won't happen in a branch @@ -1227,7 +1227,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (unpublishResult.Success) // and succeeded, trigger events { // events and audit - scope.Notifications.Publish(new UnpublishedNotification(content, evtMsgs).WithState(notificationState)); + scope.Notifications.Publish(new ContentUnpublishedNotification(content, evtMsgs).WithState(notificationState)); scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, TreeChangeTypes.RefreshBranch).ToEventArgs()); if (culturesUnpublishing != null) @@ -1282,7 +1282,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!branchOne) // for branches, handled by SaveAndPublishBranch { scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); - scope.Notifications.Publish(new PublishedNotification(content, evtMsgs).WithState(notificationState)); + scope.Notifications.Publish(new ContentPublishedNotification(content, evtMsgs).WithState(notificationState)); } // it was not published and now is... descendants that were 'published' (but @@ -1291,7 +1291,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!branchOne && isNew == false && previouslyPublished == false && HasChildren(content.Id)) { var descendants = GetPublishedDescendantsLocked(content).ToArray(); - scope.Notifications.Publish(new PublishedNotification(descendants, evtMsgs).WithState(notificationState)); + scope.Notifications.Publish(new ContentPublishedNotification(descendants, evtMsgs).WithState(notificationState)); } switch (publishResult.Result) @@ -1384,7 +1384,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (pendingCultures.Count == 0) continue; //shouldn't happen but no point in processing this document if there's nothing there - var savingNotification = new SavingNotification(d, evtMsgs); + var savingNotification = new ContentSavingNotification(d, evtMsgs); if (scope.Notifications.PublishCancelable(savingNotification)) { results.Add(new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, d)); @@ -1445,7 +1445,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (pendingCultures.Count == 0) continue; //shouldn't happen but no point in processing this document if there's nothing there - var savingNotification = new SavingNotification(d, evtMsgs); + var savingNotification = new ContentSavingNotification(d, evtMsgs); if (scope.Notifications.PublishCancelable(savingNotification)) { results.Add(new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, d)); @@ -1703,7 +1703,7 @@ namespace Umbraco.Cms.Core.Services.Implement // trigger events for the entire branch // (SaveAndPublishBranchOne does *not* do it) scope.Events.Dispatch(TreeChanged, this, new TreeChange(document, TreeChangeTypes.RefreshBranch).ToEventArgs()); - scope.Notifications.Publish(new PublishedNotification(publishedDocuments, evtMsgs)); + scope.Notifications.Publish(new ContentPublishedNotification(publishedDocuments, evtMsgs)); scope.Complete(); } @@ -1727,7 +1727,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (culturesToPublish.Count == 0) // empty = already published return new PublishResult(PublishResultType.SuccessPublishAlready, evtMsgs, document); - var savingNotification = new SavingNotification(document, evtMsgs); + var savingNotification = new ContentSavingNotification(document, evtMsgs); if (scope.Notifications.PublishCancelable(savingNotification)) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, document); @@ -1757,7 +1757,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (scope.Notifications.PublishCancelable(new DeletingNotification(content, evtMsgs))) + if (scope.Notifications.PublishCancelable(new ContentDeletingNotification(content, evtMsgs))) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -1770,7 +1770,7 @@ namespace Umbraco.Cms.Core.Services.Implement // just raise the event if (content.Trashed == false && content.Published) { - scope.Notifications.Publish(new UnpublishedNotification(content, evtMsgs)); + scope.Notifications.Publish(new ContentUnpublishedNotification(content, evtMsgs)); } DeleteLocked(scope, content, evtMsgs); @@ -1789,7 +1789,7 @@ namespace Umbraco.Cms.Core.Services.Implement void DoDelete(IContent c) { _documentRepository.Delete(c); - scope.Notifications.Publish(new DeletedNotification(c, evtMsgs)); + scope.Notifications.Publish(new ContentDeletedNotification(c, evtMsgs)); // media files deleted by QueuingEventDispatcher } @@ -1824,7 +1824,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var deletingVersionsNotification = new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate); + var deletingVersionsNotification = new ContentDeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate); if (scope.Notifications.PublishCancelable(deletingVersionsNotification)) { scope.Complete(); @@ -1834,7 +1834,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); _documentRepository.DeleteVersions(id, versionDate); - scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate).WithStateFrom(deletingVersionsNotification)); + scope.Notifications.Publish(new ContentDeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate).WithStateFrom(deletingVersionsNotification)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version date)"); scope.Complete(); @@ -1855,7 +1855,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var deletingVersionsNotification = new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId); + var deletingVersionsNotification = new ContentDeletingVersionsNotification(id, evtMsgs, specificVersion: versionId); if (scope.Notifications.PublishCancelable(deletingVersionsNotification)) { scope.Complete(); @@ -1873,7 +1873,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (c.VersionId != versionId && c.PublishedVersionId != versionId) // don't delete the current or published version _documentRepository.DeleteVersion(versionId); - scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId).WithStateFrom(deletingVersionsNotification)); + scope.Notifications.Publish(new ContentDeletedVersionsNotification(id, evtMsgs, specificVersion: versionId).WithStateFrom(deletingVersionsNotification)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version)"); scope.Complete(); @@ -1897,7 +1897,7 @@ namespace Umbraco.Cms.Core.Services.Implement var originalPath = content.Path; var moveEventInfo = new MoveEventInfo(content, originalPath, Cms.Core.Constants.System.RecycleBinContent); - var movingToRecycleBinNotification = new MovingToRecycleBinNotification(moveEventInfo, evtMsgs); + var movingToRecycleBinNotification = new ContentMovingToRecycleBinNotification(moveEventInfo, evtMsgs); if (scope.Notifications.PublishCancelable(movingToRecycleBinNotification)) { scope.Complete(); @@ -1917,7 +1917,7 @@ namespace Umbraco.Cms.Core.Services.Implement .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs).WithStateFrom(movingToRecycleBinNotification)); + scope.Notifications.Publish(new ContentMovedToRecycleBinNotification(moveInfo, evtMsgs).WithStateFrom(movingToRecycleBinNotification)); Audit(AuditType.Move, userId, content.Id, "Moved to recycle bin"); scope.Complete(); @@ -1960,7 +1960,7 @@ namespace Umbraco.Cms.Core.Services.Implement var moveEventInfo = new MoveEventInfo(content, content.Path, parentId); - var movingNotification = new MovingNotification(moveEventInfo, evtMsgs); + var movingNotification = new ContentMovingNotification(moveEventInfo, evtMsgs); if (scope.Notifications.PublishCancelable(movingNotification)) { scope.Complete(); @@ -1990,7 +1990,7 @@ namespace Umbraco.Cms.Core.Services.Implement .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - scope.Notifications.Publish(new MovedNotification(moveInfo, evtMsgs).WithStateFrom(movingNotification)); + scope.Notifications.Publish(new ContentMovedNotification(moveInfo, evtMsgs).WithStateFrom(movingNotification)); Audit(AuditType.Move, userId, content.Id); @@ -2075,7 +2075,7 @@ namespace Umbraco.Cms.Core.Services.Implement // are managed by Delete, and not here. // no idea what those events are for, keep a simplified version - var emptyingRecycleBinNotification = new EmptyingRecycleBinNotification(evtMsgs); + var emptyingRecycleBinNotification = new ContentEmptyingRecycleBinNotification(evtMsgs); if (scope.Notifications.PublishCancelable(emptyingRecycleBinNotification)) { scope.Complete(); @@ -2091,7 +2091,7 @@ namespace Umbraco.Cms.Core.Services.Implement deleted.Add(content); } - scope.Notifications.Publish(new EmptiedRecycleBinNotification(evtMsgs).WithStateFrom(emptyingRecycleBinNotification)); + scope.Notifications.Publish(new ContentEmptiedRecycleBinNotification(evtMsgs).WithStateFrom(emptyingRecycleBinNotification)); scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange(x, TreeChangeTypes.Remove)).ToEventArgs()); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.RecycleBinContent, "Recycle bin emptied"); @@ -2138,7 +2138,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (scope.Notifications.PublishCancelable(new CopyingNotification(content, copy, parentId, evtMsgs))) + if (scope.Notifications.PublishCancelable(new ContentCopyingNotification(content, copy, parentId, evtMsgs))) { scope.Complete(); return null; @@ -2193,7 +2193,7 @@ namespace Umbraco.Cms.Core.Services.Implement var descendantCopy = descendant.DeepCloneWithResetIdentities(); descendantCopy.ParentId = parentId; - if (scope.Notifications.PublishCancelable(new CopyingNotification(descendant, descendantCopy, parentId, evtMsgs))) + if (scope.Notifications.PublishCancelable(new ContentCopyingNotification(descendant, descendantCopy, parentId, evtMsgs))) { continue; } @@ -2221,7 +2221,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.Events.Dispatch(TreeChanged, this, new TreeChange(copy, TreeChangeTypes.RefreshBranch).ToEventArgs()); foreach (var x in copies) { - scope.Notifications.Publish(new CopiedNotification(x.Item1, x.Item2, parentId, relateToOriginal, evtMsgs)); + scope.Notifications.Publish(new ContentCopiedNotification(x.Item1, x.Item2, parentId, relateToOriginal, evtMsgs)); } Audit(AuditType.Copy, userId, content.Id); @@ -2243,7 +2243,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var sendingToPublishNotification = new SendingToPublishNotification(content, evtMsgs); + var sendingToPublishNotification = new ContentSendingToPublishNotification(content, evtMsgs); if (scope.Notifications.PublishCancelable(sendingToPublishNotification)) { scope.Complete(); @@ -2269,7 +2269,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (!saveResult.Success) return saveResult.Success; - scope.Notifications.Publish(new SentToPublishNotification(content, evtMsgs).WithStateFrom(sendingToPublishNotification)); + scope.Notifications.Publish(new ContentSentToPublishNotification(content, evtMsgs).WithStateFrom(sendingToPublishNotification)); if (culturesChanging != null) Audit(AuditType.SendToPublishVariant, userId, content.Id, $"Send To Publish for cultures: {culturesChanging}", culturesChanging); @@ -2341,8 +2341,8 @@ namespace Umbraco.Cms.Core.Services.Implement private OperationResult Sort(IScope scope, IContent[] itemsA, int userId, EventMessages evtMsgs, bool raiseEvents) { - var sortingNotification = new SortingNotification(itemsA, evtMsgs); - var savingNotification = new SavingNotification(itemsA, evtMsgs); + var sortingNotification = new ContentSortingNotification(itemsA, evtMsgs); + var savingNotification = new ContentSavingNotification(itemsA, evtMsgs); if (raiseEvents) { // raise cancelable sorting event @@ -2389,15 +2389,15 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { //first saved, then sorted - scope.Notifications.Publish(new SavedNotification(itemsA, evtMsgs).WithStateFrom(savingNotification)); - scope.Notifications.Publish(new SortedNotification(itemsA, evtMsgs).WithStateFrom(sortingNotification)); + scope.Notifications.Publish(new ContentSavedNotification(itemsA, evtMsgs).WithStateFrom(savingNotification)); + scope.Notifications.Publish(new ContentSortedNotification(itemsA, evtMsgs).WithStateFrom(sortingNotification)); } scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)).ToEventArgs()); if (raiseEvents && published.Any()) { - scope.Notifications.Publish(new PublishedNotification(published, evtMsgs)); + scope.Notifications.Publish(new ContentPublishedNotification(published, evtMsgs)); } Audit(AuditType.Sort, userId, 0, "Sorting content performed by user"); @@ -2506,7 +2506,7 @@ namespace Umbraco.Cms.Core.Services.Implement IReadOnlyCollection culturesUnpublishing, EventMessages evtMsgs, IReadOnlyCollection allLangs, IDictionary notificationState) { // raise Publishing notification - if (scope.Notifications.PublishCancelable(new PublishingNotification(content, evtMsgs).WithState(notificationState))) + if (scope.Notifications.PublishCancelable(new ContentPublishingNotification(content, evtMsgs).WithState(notificationState))) { _logger.LogInformation("Document {ContentName} (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "publishing was cancelled"); return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); @@ -2666,7 +2666,7 @@ namespace Umbraco.Cms.Core.Services.Implement private PublishResult StrategyCanUnpublish(IScope scope, IContent content, EventMessages evtMsgs) { // raise Unpublishing notification - if (scope.Notifications.PublishCancelable(new UnpublishingNotification(content, evtMsgs))) + if (scope.Notifications.PublishCancelable(new ContentUnpublishingNotification(content, evtMsgs))) { _logger.LogInformation("Document {ContentName} (id={ContentId}) cannot be unpublished: unpublishing was cancelled.", content.Name, content.Id); return new PublishResult(PublishResultType.FailedUnpublishCancelledByEvent, evtMsgs, content); @@ -2748,7 +2748,7 @@ namespace Umbraco.Cms.Core.Services.Implement var query = Query().WhereIn(x => x.ContentTypeId, contentTypeIdsA); var contents = _documentRepository.Get(query).ToArray(); - if (scope.Notifications.PublishCancelable(new DeletingNotification(contents, evtMsgs))) + if (scope.Notifications.PublishCancelable(new ContentDeletingNotification(contents, evtMsgs))) { scope.Complete(); return; @@ -2763,7 +2763,7 @@ namespace Umbraco.Cms.Core.Services.Implement // just raise the event if (content.Trashed == false && content.Published) { - scope.Notifications.Publish(new UnpublishedNotification(content, evtMsgs)); + scope.Notifications.Publish(new ContentUnpublishedNotification(content, evtMsgs)); } // if current content has children, move them to trash @@ -2788,7 +2788,7 @@ namespace Umbraco.Cms.Core.Services.Implement .ToArray(); if (moveInfos.Length > 0) { - scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfos, evtMsgs)); + scope.Notifications.Publish(new ContentMovedToRecycleBinNotification(moveInfos, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, changes.ToEventArgs()); @@ -2888,7 +2888,7 @@ namespace Umbraco.Cms.Core.Services.Implement Audit(AuditType.Save, Cms.Core.Constants.Security.SuperUserId, content.Id, $"Saved content template: {content.Name}"); - scope.Notifications.Publish(new SavedBlueprintNotification(content, evtMsgs)); + scope.Notifications.Publish(new ContentSavedBlueprintNotification(content, evtMsgs)); scope.Complete(); } @@ -2902,7 +2902,7 @@ namespace Umbraco.Cms.Core.Services.Implement { scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); _documentBlueprintRepository.Delete(content); - scope.Notifications.Publish(new DeletedBlueprintNotification(content, evtMsgs)); + scope.Notifications.Publish(new ContentDeletedBlueprintNotification(content, evtMsgs)); scope.Complete(); } } @@ -2994,7 +2994,7 @@ namespace Umbraco.Cms.Core.Services.Implement _documentBlueprintRepository.Delete(blueprint); } - scope.Notifications.Publish(new DeletedBlueprintNotification(blueprints, evtMsgs)); + scope.Notifications.Publish(new ContentDeletedBlueprintNotification(blueprints, evtMsgs)); scope.Complete(); } } @@ -3029,7 +3029,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var rollingBackNotification = new RollingBackNotification(content, evtMsgs); + var rollingBackNotification = new ContentRollingBackNotification(content, evtMsgs); if (scope.Notifications.PublishCancelable(rollingBackNotification)) { scope.Complete(); @@ -3050,7 +3050,7 @@ namespace Umbraco.Cms.Core.Services.Implement } else { - scope.Notifications.Publish(new RolledBackNotification(content, evtMsgs).WithStateFrom(rollingBackNotification)); + scope.Notifications.Publish(new ContentRolledBackNotification(content, evtMsgs).WithStateFrom(rollingBackNotification)); //Logging & Audit message _logger.LogInformation("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId); diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index f33cb306e6..38a7ad7b28 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -296,7 +296,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (withIdentity) { - var savingNotification = new SavingNotification(media, evtMsgs); + var savingNotification = new MediaSavingNotification(media, evtMsgs); if (scope.Notifications.PublishCancelable(savingNotification)) { return; @@ -304,7 +304,7 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaRepository.Save(media); - scope.Notifications.Publish(new SavedNotification(media, evtMsgs).WithStateFrom(savingNotification)); + scope.Notifications.Publish(new MediaSavedNotification(media, evtMsgs).WithStateFrom(savingNotification)); scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, TreeChangeTypes.RefreshNode).ToEventArgs()); } @@ -662,7 +662,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var savingNotification = new SavingNotification(media, evtMsgs); + var savingNotification = new MediaSavingNotification(media, evtMsgs); if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); @@ -685,7 +685,7 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaRepository.Save(media); if (raiseEvents) { - scope.Notifications.Publish(new SavedNotification(media, evtMsgs).WithStateFrom(savingNotification)); + scope.Notifications.Publish(new MediaSavedNotification(media, evtMsgs).WithStateFrom(savingNotification)); } var changeType = TreeChangeTypes.RefreshNode; scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, changeType).ToEventArgs()); @@ -710,7 +710,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var savingNotification = new SavingNotification(mediasA, evtMsgs); + var savingNotification = new MediaSavingNotification(mediasA, evtMsgs); if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); @@ -729,7 +729,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - scope.Notifications.Publish(new SavedNotification(mediasA, evtMsgs).WithStateFrom(savingNotification)); + scope.Notifications.Publish(new MediaSavedNotification(mediasA, evtMsgs).WithStateFrom(savingNotification)); } scope.Events.Dispatch(TreeChanged, this, treeChanges.ToEventArgs()); Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Bulk save media"); @@ -755,7 +755,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - if (scope.Notifications.PublishCancelable(new DeletingNotification(media, evtMsgs))) + if (scope.Notifications.PublishCancelable(new MediaDeletingNotification(media, evtMsgs))) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); @@ -779,7 +779,7 @@ namespace Umbraco.Cms.Core.Services.Implement void DoDelete(IMedia c) { _mediaRepository.Delete(c); - scope.Notifications.Publish(new DeletedNotification(c, evtMsgs)); + scope.Notifications.Publish(new MediaDeletedNotification(c, evtMsgs)); // media files deleted by QueuingEventDispatcher } @@ -822,7 +822,7 @@ namespace Umbraco.Cms.Core.Services.Implement { var evtMsgs = EventMessagesFactory.Get(); - var deletingVersionsNotification = new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate); + var deletingVersionsNotification = new MediaDeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate); if (scope.Notifications.PublishCancelable(deletingVersionsNotification)) { return; @@ -832,7 +832,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.MediaTree); _mediaRepository.DeleteVersions(id, versionDate); - scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate).WithStateFrom(deletingVersionsNotification)); + scope.Notifications.Publish(new MediaDeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate).WithStateFrom(deletingVersionsNotification)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete Media by version date"); } @@ -850,7 +850,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var deletingVersionsNotification = new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId); + var deletingVersionsNotification = new MediaDeletingVersionsNotification(id, evtMsgs, specificVersion: versionId); if (scope.Notifications.PublishCancelable(deletingVersionsNotification)) { scope.Complete(); @@ -869,7 +869,7 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaRepository.DeleteVersion(versionId); - scope.Notifications.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId).WithStateFrom(deletingVersionsNotification)); + scope.Notifications.Publish(new MediaDeletedVersionsNotification(id, evtMsgs, specificVersion: versionId).WithStateFrom(deletingVersionsNotification)); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete Media by version"); scope.Complete(); @@ -901,7 +901,7 @@ namespace Umbraco.Cms.Core.Services.Implement var moveEventInfo = new MoveEventInfo(media, originalPath, Cms.Core.Constants.System.RecycleBinMedia); - var movingToRecycleBinNotification = new MovingToRecycleBinNotification(moveEventInfo, evtMsgs); + var movingToRecycleBinNotification = new MediaMovingToRecycleBinNotification(moveEventInfo, evtMsgs); if (scope.Notifications.PublishCancelable(movingToRecycleBinNotification)) { scope.Complete(); @@ -912,7 +912,7 @@ namespace Umbraco.Cms.Core.Services.Implement scope.Events.Dispatch(TreeChanged, this, new TreeChange(media, TreeChangeTypes.RefreshBranch).ToEventArgs()); var moveInfo = moves.Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)).ToArray(); - scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfo, evtMsgs).WithStateFrom(movingToRecycleBinNotification)); + scope.Notifications.Publish(new MediaMovedToRecycleBinNotification(moveInfo, evtMsgs).WithStateFrom(movingToRecycleBinNotification)); Audit(AuditType.Move, userId, media.Id, "Move Media to recycle bin"); scope.Complete(); @@ -949,7 +949,7 @@ namespace Umbraco.Cms.Core.Services.Implement throw new InvalidOperationException("Parent does not exist or is trashed."); // causes rollback var moveEventInfo = new MoveEventInfo(media, media.Path, parentId); - var movingNotification = new MovingNotification(moveEventInfo, evtMsgs); + var movingNotification = new MediaMovingNotification(moveEventInfo, evtMsgs); if (scope.Notifications.PublishCancelable(movingNotification)) { scope.Complete(); @@ -966,7 +966,7 @@ namespace Umbraco.Cms.Core.Services.Implement var moveInfo = moves //changes .Select(x => new MoveEventInfo(x.Item1, x.Item2, x.Item1.ParentId)) .ToArray(); - scope.Notifications.Publish(new MovedNotification(moveInfo, evtMsgs).WithStateFrom(movingNotification)); + scope.Notifications.Publish(new MediaMovedNotification(moveInfo, evtMsgs).WithStateFrom(movingNotification)); Audit(AuditType.Move, userId, media.Id); scope.Complete(); } @@ -1047,7 +1047,7 @@ namespace Umbraco.Cms.Core.Services.Implement // v7 EmptyingRecycleBin and EmptiedRecycleBin events are greatly simplified since // each deleted items will have its own deleting/deleted events. so, files and such // are managed by Delete, and not here. - var emptyingRecycleBinNotification = new EmptyingRecycleBinNotification(evtMsgs); + var emptyingRecycleBinNotification = new MediaEmptyingRecycleBinNotification(evtMsgs); if (scope.Notifications.PublishCancelable(emptyingRecycleBinNotification)) { scope.Complete(); @@ -1062,7 +1062,7 @@ namespace Umbraco.Cms.Core.Services.Implement DeleteLocked(scope, media, evtMsgs); deleted.Add(media); } - scope.Notifications.Publish(new EmptiedRecycleBinNotification(new EventMessages()).WithStateFrom(emptyingRecycleBinNotification)); + scope.Notifications.Publish(new MediaEmptiedRecycleBinNotification(new EventMessages()).WithStateFrom(emptyingRecycleBinNotification)); scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange(x, TreeChangeTypes.Remove)).ToEventArgs()); Audit(AuditType.Delete, userId, Cms.Core.Constants.System.RecycleBinMedia, "Empty Media recycle bin"); scope.Complete(); @@ -1092,7 +1092,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { - var savingNotification = new SavingNotification(itemsA, evtMsgs); + var savingNotification = new MediaSavingNotification(itemsA, evtMsgs); if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); @@ -1122,7 +1122,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (raiseEvents) { - scope.Notifications.Publish(new SavedNotification(itemsA, evtMsgs).WithStateFrom(savingNotification)); + scope.Notifications.Publish(new MediaSavedNotification(itemsA, evtMsgs).WithStateFrom(savingNotification)); } scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)).ToEventArgs()); Audit(AuditType.Sort, userId, 0); @@ -1243,7 +1243,7 @@ namespace Umbraco.Cms.Core.Services.Implement var query = Query().WhereIn(x => x.ContentTypeId, mediaTypeIdsA); var medias = _mediaRepository.Get(query).ToArray(); - if (scope.Notifications.PublishCancelable(new DeletingNotification(medias, evtMsgs))) + if (scope.Notifications.PublishCancelable(new MediaDeletingNotification(medias, evtMsgs))) { scope.Complete(); return; @@ -1274,7 +1274,7 @@ namespace Umbraco.Cms.Core.Services.Implement .ToArray(); if (moveInfos.Length > 0) { - scope.Notifications.Publish(new MovedToRecycleBinNotification(moveInfos, evtMsgs)); + scope.Notifications.Publish(new MediaMovedToRecycleBinNotification(moveInfos, evtMsgs)); } scope.Events.Dispatch(TreeChanged, this, changes.ToEventArgs()); diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentCopiedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentCopiedNotification.cs new file mode 100644 index 0000000000..55946366e5 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentCopiedNotification.cs @@ -0,0 +1,16 @@ +// 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 ContentCopiedNotification : CopiedNotification + { + public ContentCopiedNotification(IContent original, IContent copy, int parentId, bool relateToOriginal, EventMessages messages) + : base(original, copy, parentId, relateToOriginal, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentCopyingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentCopyingNotification.cs new file mode 100644 index 0000000000..85f5d1e189 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentCopyingNotification.cs @@ -0,0 +1,16 @@ +// 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 ContentCopyingNotification : CopyingNotification + { + public ContentCopyingNotification(IContent original, IContent copy, int parentId, EventMessages messages) + : base(original, copy, parentId, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentDeletedBlueprintNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentDeletedBlueprintNotification.cs new file mode 100644 index 0000000000..55e61276d0 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentDeletedBlueprintNotification.cs @@ -0,0 +1,22 @@ +// 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 ContentDeletedBlueprintNotification : EnumerableObjectNotification + { + public ContentDeletedBlueprintNotification(IContent target, EventMessages messages) : base(target, messages) + { + } + + public ContentDeletedBlueprintNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable DeletedBlueprints => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentDeletedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentDeletedNotification.cs new file mode 100644 index 0000000000..4c0d6c3e98 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentDeletedNotification.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 ContentDeletedNotification : DeletedNotification + { + public ContentDeletedNotification(IContent target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentDeletedVersionsNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentDeletedVersionsNotification.cs new file mode 100644 index 0000000000..19a2ae45d3 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentDeletedVersionsNotification.cs @@ -0,0 +1,16 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class ContentDeletedVersionsNotification : DeletedVersionsNotification + { + public ContentDeletedVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) : base(id, messages, specificVersion, deletePriorVersions, dateToRetain) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentDeletingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentDeletingNotification.cs new file mode 100644 index 0000000000..d38eb13beb --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentDeletingNotification.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 ContentDeletingNotification : DeletingNotification + { + public ContentDeletingNotification(IContent target, EventMessages messages) : base(target, messages) + { + } + + public ContentDeletingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentDeletingVersionsNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentDeletingVersionsNotification.cs new file mode 100644 index 0000000000..7b791eafef --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentDeletingVersionsNotification.cs @@ -0,0 +1,16 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class ContentDeletingVersionsNotification : DeletingVersionsNotification + { + public ContentDeletingVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) : base(id, messages, specificVersion, deletePriorVersions, dateToRetain) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentEmptiedRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentEmptiedRecycleBinNotification.cs new file mode 100644 index 0000000000..53bc62fe91 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentEmptiedRecycleBinNotification.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 ContentEmptiedRecycleBinNotification : EmptiedRecycleBinNotification + { + public ContentEmptiedRecycleBinNotification(EventMessages messages) : base(messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentEmptyingRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentEmptyingRecycleBinNotification.cs new file mode 100644 index 0000000000..5d0ab67e26 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentEmptyingRecycleBinNotification.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 ContentEmptyingRecycleBinNotification : EmptyingRecycleBinNotification + { + public ContentEmptyingRecycleBinNotification(EventMessages messages) : base(messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentMovedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentMovedNotification.cs new file mode 100644 index 0000000000..50bf0d1417 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentMovedNotification.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 ContentMovedNotification : MovedNotification + { + public ContentMovedNotification(MoveEventInfo target, EventMessages messages) : base(target, messages) + { + } + + public ContentMovedNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentMovedToRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentMovedToRecycleBinNotification.cs new file mode 100644 index 0000000000..b9da66e9aa --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentMovedToRecycleBinNotification.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 ContentMovedToRecycleBinNotification : MovedToRecycleBinNotification + { + public ContentMovedToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(target, messages) + { + } + + public ContentMovedToRecycleBinNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentMovingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentMovingNotification.cs new file mode 100644 index 0000000000..b57c9e6cf0 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentMovingNotification.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 ContentMovingNotification : MovingNotification + { + public ContentMovingNotification(MoveEventInfo target, EventMessages messages) : base(target, messages) + { + } + + public ContentMovingNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentMovingToRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentMovingToRecycleBinNotification.cs new file mode 100644 index 0000000000..fc5a5f401a --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentMovingToRecycleBinNotification.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 ContentMovingToRecycleBinNotification : MovingToRecycleBinNotification + { + public ContentMovingToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(target, messages) + { + } + + public ContentMovingToRecycleBinNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentNotificationExtensions.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentNotificationExtensions.cs index 046aa5b07b..c04a04ef87 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/ContentNotificationExtensions.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentNotificationExtensions.cs @@ -22,37 +22,37 @@ namespace Umbraco.Cms.Infrastructure.Services.Notifications /// /// Determines whether a culture is being published, during a Publishing notification /// - public static bool IsPublishingCulture(this PublishingNotification notification, IContent content, string culture) + public static bool IsPublishingCulture(this ContentPublishingNotification notification, IContent content, string culture) => IsPublishingCulture(content, culture); /// /// Determines whether a culture is being unpublished, during an Publishing notification /// - public static bool IsUnpublishingCulture(this PublishingNotification notification, IContent content, string culture) + public static bool IsUnpublishingCulture(this ContentPublishingNotification notification, IContent content, string culture) => IsUnpublishingCulture(content, culture); /// /// Determines whether a culture is being unpublished, during a Unpublishing notification /// - public static bool IsUnpublishingCulture(this UnpublishingNotification notification, IContent content, string culture) + public static bool IsUnpublishingCulture(this ContentUnpublishingNotification notification, IContent content, string culture) => IsUnpublishingCulture(content, culture); /// /// Determines whether a culture has been published, during a Published notification /// - public static bool HasPublishedCulture(this PublishedNotification notification, IContent content, string culture) + public static bool HasPublishedCulture(this ContentPublishedNotification notification, IContent content, string culture) => content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.ChangedCulture + culture); /// /// Determines whether a culture has been unpublished, during a Published notification /// - public static bool HasUnpublishedCulture(this PublishedNotification notification, IContent content, string culture) + public static bool HasUnpublishedCulture(this ContentPublishedNotification notification, IContent content, string culture) => HasUnpublishedCulture(content, culture); /// /// Determines whether a culture has been unpublished, during an Unpublished notification /// - public static bool HasUnpublishedCulture(this UnpublishedNotification notification, IContent content, string culture) + public static bool HasUnpublishedCulture(this ContentUnpublishedNotification notification, IContent content, string culture) => HasUnpublishedCulture(content, culture); private static bool IsUnpublishingCulture(IContent content, string culture) diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentPublishedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentPublishedNotification.cs new file mode 100644 index 0000000000..f19899136e --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentPublishedNotification.cs @@ -0,0 +1,22 @@ +// 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 ContentPublishedNotification : EnumerableObjectNotification + { + public ContentPublishedNotification(IContent target, EventMessages messages) : base(target, messages) + { + } + + public ContentPublishedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable PublishedEntities => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentPublishingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentPublishingNotification.cs new file mode 100644 index 0000000000..b31b8fa391 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentPublishingNotification.cs @@ -0,0 +1,22 @@ +// 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 ContentPublishingNotification : CancelableEnumerableObjectNotification + { + public ContentPublishingNotification(IContent target, EventMessages messages) : base(target, messages) + { + } + + public ContentPublishingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable PublishedEntities => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentRolledBackNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentRolledBackNotification.cs new file mode 100644 index 0000000000..fbc64eebb2 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentRolledBackNotification.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 ContentRolledBackNotification : RolledBackNotification + { + public ContentRolledBackNotification(IContent target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentRollingBackNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentRollingBackNotification.cs new file mode 100644 index 0000000000..ae3065ad58 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentRollingBackNotification.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 ContentRollingBackNotification : RollingBackNotification + { + public ContentRollingBackNotification(IContent target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentSavedBlueprintNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentSavedBlueprintNotification.cs new file mode 100644 index 0000000000..a57f278ef0 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentSavedBlueprintNotification.cs @@ -0,0 +1,17 @@ +// 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 ContentSavedBlueprintNotification : ObjectNotification + { + public ContentSavedBlueprintNotification(IContent target, EventMessages messages) : base(target, messages) + { + } + + public IContent SavedBlueprint => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentSavedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentSavedNotification.cs new file mode 100644 index 0000000000..0a79bf1285 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentSavedNotification.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 ContentSavedNotification : SavedNotification + { + public ContentSavedNotification(IContent target, EventMessages messages) : base(target, messages) + { + } + + public ContentSavedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentSavingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentSavingNotification.cs new file mode 100644 index 0000000000..c32f584cc6 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentSavingNotification.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 ContentSavingNotification : SavingNotification + { + public ContentSavingNotification(IContent target, EventMessages messages) : base(target, messages) + { + } + + public ContentSavingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentSendingToPublishNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentSendingToPublishNotification.cs new file mode 100644 index 0000000000..9719580c62 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentSendingToPublishNotification.cs @@ -0,0 +1,17 @@ +// 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 ContentSendingToPublishNotification : CancelableObjectNotification + { + public ContentSendingToPublishNotification(IContent target, EventMessages messages) : base(target, messages) + { + } + + public IContent Entity => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentSentToPublishNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentSentToPublishNotification.cs new file mode 100644 index 0000000000..733fbfbb93 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentSentToPublishNotification.cs @@ -0,0 +1,17 @@ +// 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 ContentSentToPublishNotification : ObjectNotification + { + public ContentSentToPublishNotification(IContent target, EventMessages messages) : base(target, messages) + { + } + + public IContent Entity => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentSortedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentSortedNotification.cs new file mode 100644 index 0000000000..fa51155f96 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentSortedNotification.cs @@ -0,0 +1,16 @@ +// 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 ContentSortedNotification : SortedNotification + { + public ContentSortedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentSortingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentSortingNotification.cs new file mode 100644 index 0000000000..e5b011276d --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentSortingNotification.cs @@ -0,0 +1,16 @@ +// 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 ContentSortingNotification : SortingNotification + { + public ContentSortingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentUnpublishedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentUnpublishedNotification.cs new file mode 100644 index 0000000000..2beb8f2d55 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentUnpublishedNotification.cs @@ -0,0 +1,22 @@ +// 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 ContentUnpublishedNotification : EnumerableObjectNotification + { + public ContentUnpublishedNotification(IContent target, EventMessages messages) : base(target, messages) + { + } + + public ContentUnpublishedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable UnpublishedEntities => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/ContentUnpublishingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/ContentUnpublishingNotification.cs new file mode 100644 index 0000000000..85db0015c3 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/ContentUnpublishingNotification.cs @@ -0,0 +1,22 @@ +// 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 ContentUnpublishingNotification : CancelableEnumerableObjectNotification + { + public ContentUnpublishingNotification(IContent target, EventMessages messages) : base(target, messages) + { + } + + public ContentUnpublishingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + + public IEnumerable UnpublishedEntities => Target; + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs index 46da7fe11e..653f3913aa 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/CopiedNotification.cs @@ -5,9 +5,9 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class CopiedNotification : ObjectNotification where T : class + public abstract class CopiedNotification : ObjectNotification where T : class { - public CopiedNotification(T original, T copy, int parentId, bool relateToOriginal, EventMessages messages) : base(original, messages) + protected CopiedNotification(T original, T copy, int parentId, bool relateToOriginal, EventMessages messages) : base(original, messages) { Copy = copy; ParentId = parentId; diff --git a/src/Umbraco.Infrastructure/Services/Notifications/CopyingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/CopyingNotification.cs index 1780ec6dea..ae658577b6 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/CopyingNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/CopyingNotification.cs @@ -5,9 +5,9 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class CopyingNotification : CancelableObjectNotification where T : class + public abstract class CopyingNotification : CancelableObjectNotification where T : class { - public CopyingNotification(T original, T copy, int parentId, EventMessages messages) : base(original, messages) + protected CopyingNotification(T original, T copy, int parentId, EventMessages messages) : base(original, messages) { Copy = copy; ParentId = parentId; diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletedNotification.cs index 07008974d2..99016ca073 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/DeletedNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletedNotification.cs @@ -6,9 +6,9 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class DeletedNotification : EnumerableObjectNotification + public abstract class DeletedNotification : EnumerableObjectNotification { - public DeletedNotification(T target, EventMessages messages) : base(target, messages) + protected DeletedNotification(T target, EventMessages messages) : base(target, messages) { } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotification.cs index 3459795c12..4e8e4d5ba4 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletedVersionsNotification.cs @@ -6,9 +6,9 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class DeletedVersionsNotification : DeletedVersionsNotificationBase where T : class + public abstract class DeletedVersionsNotification : DeletedVersionsNotificationBase where T : class { - public DeletedVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) + protected DeletedVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) : base(id, messages, specificVersion, deletePriorVersions, dateToRetain) { } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletingNotification.cs index 6ebc9b8dd8..2dd8e09c6b 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/DeletingNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletingNotification.cs @@ -6,13 +6,13 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class DeletingNotification : CancelableEnumerableObjectNotification + public abstract class DeletingNotification : CancelableEnumerableObjectNotification { - public DeletingNotification(T target, EventMessages messages) : base(target, messages) + protected DeletingNotification(T target, EventMessages messages) : base(target, messages) { } - public DeletingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + protected DeletingNotification(IEnumerable target, EventMessages messages) : base(target, messages) { } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletingVersionsNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/DeletingVersionsNotification.cs index e87317d0d5..4c6d8423fd 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/DeletingVersionsNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/DeletingVersionsNotification.cs @@ -6,9 +6,9 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class DeletingVersionsNotification : DeletedVersionsNotificationBase, ICancelableNotification where T : class + public abstract class DeletingVersionsNotification : DeletedVersionsNotificationBase, ICancelableNotification where T : class { - public DeletingVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) + protected DeletingVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) : base(id, messages, specificVersion, deletePriorVersions, dateToRetain) { } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs index 4ade0bea82..1859687170 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/EmptiedRecycleBinNotification.cs @@ -5,9 +5,9 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class EmptiedRecycleBinNotification : StatefulNotification where T : class + public abstract class EmptiedRecycleBinNotification : StatefulNotification where T : class { - public EmptiedRecycleBinNotification(EventMessages messages) => Messages = messages; + protected EmptiedRecycleBinNotification(EventMessages messages) => Messages = messages; public EventMessages Messages { get; } } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs index 7d6293cbd7..86ee7f303d 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/EmptyingRecycleBinNotification.cs @@ -5,9 +5,9 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class EmptyingRecycleBinNotification : StatefulNotification, ICancelableNotification where T : class + public abstract class EmptyingRecycleBinNotification : StatefulNotification, ICancelableNotification where T : class { - public EmptyingRecycleBinNotification(EventMessages messages) => Messages = messages; + protected EmptyingRecycleBinNotification(EventMessages messages) => Messages = messages; public EventMessages Messages { get; } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MediaDeletedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MediaDeletedNotification.cs new file mode 100644 index 0000000000..c78bfa863d --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MediaDeletedNotification.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 MediaDeletedNotification : DeletedNotification + { + public MediaDeletedNotification(IMedia target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MediaDeletedVersionsNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MediaDeletedVersionsNotification.cs new file mode 100644 index 0000000000..789079bd5e --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MediaDeletedVersionsNotification.cs @@ -0,0 +1,16 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class MediaDeletedVersionsNotification : DeletedVersionsNotification + { + public MediaDeletedVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) : base(id, messages, specificVersion, deletePriorVersions, dateToRetain) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MediaDeletingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MediaDeletingNotification.cs new file mode 100644 index 0000000000..524865bbfb --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MediaDeletingNotification.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 MediaDeletingNotification : DeletingNotification + { + public MediaDeletingNotification(IMedia target, EventMessages messages) : base(target, messages) + { + } + + public MediaDeletingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MediaDeletingVersionsNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MediaDeletingVersionsNotification.cs new file mode 100644 index 0000000000..fd979b5641 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MediaDeletingVersionsNotification.cs @@ -0,0 +1,16 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Services.Notifications +{ + public sealed class MediaDeletingVersionsNotification : DeletingVersionsNotification + { + public MediaDeletingVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) : base(id, messages, specificVersion, deletePriorVersions, dateToRetain) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MediaEmptiedRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MediaEmptiedRecycleBinNotification.cs new file mode 100644 index 0000000000..8d984eceab --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MediaEmptiedRecycleBinNotification.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 MediaEmptiedRecycleBinNotification : EmptiedRecycleBinNotification + { + public MediaEmptiedRecycleBinNotification(EventMessages messages) : base(messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MediaEmptyingRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MediaEmptyingRecycleBinNotification.cs new file mode 100644 index 0000000000..1fd72c7d99 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MediaEmptyingRecycleBinNotification.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 MediaEmptyingRecycleBinNotification : EmptyingRecycleBinNotification + { + public MediaEmptyingRecycleBinNotification(EventMessages messages) : base(messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MediaMovedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MediaMovedNotification.cs new file mode 100644 index 0000000000..43f8f8a3ff --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MediaMovedNotification.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 MediaMovedNotification : MovedNotification + { + public MediaMovedNotification(MoveEventInfo target, EventMessages messages) : base(target, messages) + { + } + + public MediaMovedNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MediaMovedToRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MediaMovedToRecycleBinNotification.cs new file mode 100644 index 0000000000..bb9274f941 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MediaMovedToRecycleBinNotification.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 MediaMovedToRecycleBinNotification : MovedToRecycleBinNotification + { + public MediaMovedToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(target, messages) + { + } + + public MediaMovedToRecycleBinNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MediaMovingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MediaMovingNotification.cs new file mode 100644 index 0000000000..407c95918a --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MediaMovingNotification.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 MediaMovingNotification : MovingNotification + { + public MediaMovingNotification(MoveEventInfo target, EventMessages messages) : base(target, messages) + { + } + + public MediaMovingNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MediaMovingToRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MediaMovingToRecycleBinNotification.cs new file mode 100644 index 0000000000..72e20ca424 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MediaMovingToRecycleBinNotification.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 MediaMovingToRecycleBinNotification : MovingToRecycleBinNotification + { + public MediaMovingToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(target, messages) + { + } + + public MediaMovingToRecycleBinNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MediaSavedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MediaSavedNotification.cs new file mode 100644 index 0000000000..4337cd1eb3 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Notifications/MediaSavedNotification.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 MediaSavedNotification : SavedNotification + { + public MediaSavedNotification(IMedia target, EventMessages messages) : base(target, messages) + { + } + + public MediaSavedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/DeletedBlueprintNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MediaSavingNotification.cs similarity index 54% rename from src/Umbraco.Infrastructure/Services/Notifications/DeletedBlueprintNotification.cs rename to src/Umbraco.Infrastructure/Services/Notifications/MediaSavingNotification.cs index 2b4b822517..cca0de8d19 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/DeletedBlueprintNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/MediaSavingNotification.cs @@ -3,19 +3,18 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class DeletedBlueprintNotification : EnumerableObjectNotification + public sealed class MediaSavingNotification : SavingNotification { - public DeletedBlueprintNotification(T target, EventMessages messages) : base(target, messages) + public MediaSavingNotification(IMedia target, EventMessages messages) : base(target, messages) { } - public DeletedBlueprintNotification(IEnumerable target, EventMessages messages) : base(target, messages) + public MediaSavingNotification(IEnumerable target, EventMessages messages) : base(target, messages) { } - - public IEnumerable DeletedBlueprints => Target; } } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MovedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MovedNotification.cs index f483b95a14..35b587f73c 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/MovedNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/MovedNotification.cs @@ -6,13 +6,13 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class MovedNotification : ObjectNotification>> + public abstract class MovedNotification : ObjectNotification>> { - public MovedNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) + protected MovedNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) { } - public MovedNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + protected MovedNotification(IEnumerable> target, EventMessages messages) : base(target, messages) { } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MovedToRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MovedToRecycleBinNotification.cs index a4bb7138e7..4562cdab30 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/MovedToRecycleBinNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/MovedToRecycleBinNotification.cs @@ -6,13 +6,13 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class MovedToRecycleBinNotification : ObjectNotification>> + public abstract class MovedToRecycleBinNotification : ObjectNotification>> { - public MovedToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) + protected MovedToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) { } - public MovedToRecycleBinNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + protected MovedToRecycleBinNotification(IEnumerable> target, EventMessages messages) : base(target, messages) { } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MovingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MovingNotification.cs index bd72c759d7..2781c0ba57 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/MovingNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/MovingNotification.cs @@ -6,13 +6,13 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class MovingNotification : CancelableObjectNotification>> + public abstract class MovingNotification : CancelableObjectNotification>> { - public MovingNotification(MoveEventInfo target, EventMessages messages) : base(new[] {target}, messages) + protected MovingNotification(MoveEventInfo target, EventMessages messages) : base(new[] {target}, messages) { } - public MovingNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + protected MovingNotification(IEnumerable> target, EventMessages messages) : base(target, messages) { } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/MovingToRecycleBinNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/MovingToRecycleBinNotification.cs index de36871801..38e989a418 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/MovingToRecycleBinNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/MovingToRecycleBinNotification.cs @@ -6,13 +6,13 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class MovingToRecycleBinNotification : CancelableObjectNotification>> + public abstract class MovingToRecycleBinNotification : CancelableObjectNotification>> { - public MovingToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) + protected MovingToRecycleBinNotification(MoveEventInfo target, EventMessages messages) : base(new[] { target }, messages) { } - public MovingToRecycleBinNotification(IEnumerable> target, EventMessages messages) : base(target, messages) + protected MovingToRecycleBinNotification(IEnumerable> target, EventMessages messages) : base(target, messages) { } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/PublishedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/PublishedNotification.cs deleted file mode 100644 index a0f584cdb6..0000000000 --- a/src/Umbraco.Infrastructure/Services/Notifications/PublishedNotification.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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 PublishedNotification : EnumerableObjectNotification - { - public PublishedNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public PublishedNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable PublishedEntities => Target; - } -} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/PublishingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/PublishingNotification.cs deleted file mode 100644 index f8ede11ba2..0000000000 --- a/src/Umbraco.Infrastructure/Services/Notifications/PublishingNotification.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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 PublishingNotification : CancelableEnumerableObjectNotification - { - public PublishingNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public PublishingNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable PublishedEntities => Target; - } -} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/RolledBackNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/RolledBackNotification.cs index 825daf6c94..326b224478 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/RolledBackNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/RolledBackNotification.cs @@ -5,9 +5,9 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class RolledBackNotification : ObjectNotification where T : class + public abstract class RolledBackNotification : ObjectNotification where T : class { - public RolledBackNotification(T target, EventMessages messages) : base(target, messages) + protected RolledBackNotification(T target, EventMessages messages) : base(target, messages) { } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/RollingBackNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/RollingBackNotification.cs index 88739d4b10..6afa46d6f1 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/RollingBackNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/RollingBackNotification.cs @@ -5,9 +5,9 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class RollingBackNotification : CancelableObjectNotification where T : class + public abstract class RollingBackNotification : CancelableObjectNotification where T : class { - public RollingBackNotification(T target, EventMessages messages) : base(target, messages) + protected RollingBackNotification(T target, EventMessages messages) : base(target, messages) { } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SavedBlueprintNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SavedBlueprintNotification.cs deleted file mode 100644 index 440df1fd99..0000000000 --- a/src/Umbraco.Infrastructure/Services/Notifications/SavedBlueprintNotification.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Umbraco.Cms.Core.Events; - -namespace Umbraco.Cms.Infrastructure.Services.Notifications -{ - public sealed class SavedBlueprintNotification : ObjectNotification where T : class - { - public SavedBlueprintNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public T SavedBlueprint => Target; - } -} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SavedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SavedNotification.cs index 2599efb1a3..b6a363019f 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/SavedNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/SavedNotification.cs @@ -6,13 +6,13 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class SavedNotification : EnumerableObjectNotification + public abstract class SavedNotification : EnumerableObjectNotification { - public SavedNotification(T target, EventMessages messages) : base(target, messages) + protected SavedNotification(T target, EventMessages messages) : base(target, messages) { } - public SavedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + protected SavedNotification(IEnumerable target, EventMessages messages) : base(target, messages) { } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SavingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SavingNotification.cs index a70e577ee7..df84628b93 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/SavingNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/SavingNotification.cs @@ -6,13 +6,13 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class SavingNotification : CancelableEnumerableObjectNotification + public abstract class SavingNotification : CancelableEnumerableObjectNotification { - public SavingNotification(T target, EventMessages messages) : base(target, messages) + protected SavingNotification(T target, EventMessages messages) : base(target, messages) { } - public SavingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + protected SavingNotification(IEnumerable target, EventMessages messages) : base(target, messages) { } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SendingToPublishNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SendingToPublishNotification.cs deleted file mode 100644 index 9f6a83f11c..0000000000 --- a/src/Umbraco.Infrastructure/Services/Notifications/SendingToPublishNotification.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Umbraco.Cms.Core.Events; - -namespace Umbraco.Cms.Infrastructure.Services.Notifications -{ - public sealed class SendingToPublishNotification : CancelableObjectNotification where T : class - { - public SendingToPublishNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public T Entity => Target; - } -} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SentToPublishNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SentToPublishNotification.cs deleted file mode 100644 index 94642dd92d..0000000000 --- a/src/Umbraco.Infrastructure/Services/Notifications/SentToPublishNotification.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Umbraco.Cms.Core.Events; - -namespace Umbraco.Cms.Infrastructure.Services.Notifications -{ - public sealed class SentToPublishNotification : ObjectNotification where T : class - { - public SentToPublishNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public T Entity => Target; - } -} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SortedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SortedNotification.cs index b1169bfb83..88b0e11cf6 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/SortedNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/SortedNotification.cs @@ -6,9 +6,9 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class SortedNotification : EnumerableObjectNotification + public abstract class SortedNotification : EnumerableObjectNotification { - public SortedNotification(IEnumerable target, EventMessages messages) : base(target, messages) + protected SortedNotification(IEnumerable target, EventMessages messages) : base(target, messages) { } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/SortingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/SortingNotification.cs index dabc0497fd..7d040a8b28 100644 --- a/src/Umbraco.Infrastructure/Services/Notifications/SortingNotification.cs +++ b/src/Umbraco.Infrastructure/Services/Notifications/SortingNotification.cs @@ -6,9 +6,9 @@ using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Infrastructure.Services.Notifications { - public sealed class SortingNotification : CancelableEnumerableObjectNotification + public abstract class SortingNotification : CancelableEnumerableObjectNotification { - public SortingNotification(IEnumerable target, EventMessages messages) : base(target, messages) + protected SortingNotification(IEnumerable target, EventMessages messages) : base(target, messages) { } diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UnpublishedNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UnpublishedNotification.cs deleted file mode 100644 index 14321f46b3..0000000000 --- a/src/Umbraco.Infrastructure/Services/Notifications/UnpublishedNotification.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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 UnpublishedNotification : EnumerableObjectNotification - { - public UnpublishedNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public UnpublishedNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable UnpublishedEntities => Target; - } -} diff --git a/src/Umbraco.Infrastructure/Services/Notifications/UnpublishingNotification.cs b/src/Umbraco.Infrastructure/Services/Notifications/UnpublishingNotification.cs deleted file mode 100644 index 4acf0a6b6f..0000000000 --- a/src/Umbraco.Infrastructure/Services/Notifications/UnpublishingNotification.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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 UnpublishingNotification : CancelableEnumerableObjectNotification - { - public UnpublishingNotification(T target, EventMessages messages) : base(target, messages) - { - } - - public UnpublishingNotification(IEnumerable target, EventMessages messages) : base(target, messages) - { - } - - public IEnumerable UnpublishedEntities => Target; - } -} diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs index e872640ebe..db9c2634dc 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs @@ -50,12 +50,12 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services } protected override void CustomTestSetup(IUmbracoBuilder builder) => builder - .AddNotificationHandler, ContentNotificationHandler>() - .AddNotificationHandler, ContentNotificationHandler>() - .AddNotificationHandler, ContentNotificationHandler>() - .AddNotificationHandler, ContentNotificationHandler>() - .AddNotificationHandler, ContentNotificationHandler>() - .AddNotificationHandler, ContentNotificationHandler>(); + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler(); private void CreateTestData() { @@ -421,36 +421,36 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services } public class ContentNotificationHandler : - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler>, - INotificationHandler> + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler { - public void Handle(SavingNotification notification) => SavingContent?.Invoke(notification); + public void Handle(ContentSavingNotification notification) => SavingContent?.Invoke(notification); - public void Handle(SavedNotification notification) => SavedContent?.Invoke(notification); + public void Handle(ContentSavedNotification notification) => SavedContent?.Invoke(notification); - public void Handle(PublishingNotification notification) => PublishingContent?.Invoke(notification); + public void Handle(ContentPublishingNotification notification) => PublishingContent?.Invoke(notification); - public void Handle(PublishedNotification notification) => PublishedContent?.Invoke(notification); + public void Handle(ContentPublishedNotification notification) => PublishedContent?.Invoke(notification); - public void Handle(UnpublishingNotification notification) => UnpublishingContent?.Invoke(notification); + public void Handle(ContentUnpublishingNotification notification) => UnpublishingContent?.Invoke(notification); - public void Handle(UnpublishedNotification notification) => UnpublishedContent?.Invoke(notification); + public void Handle(ContentUnpublishedNotification notification) => UnpublishedContent?.Invoke(notification); - public static Action> SavingContent { get; set; } + public static Action SavingContent { get; set; } - public static Action> SavedContent { get; set; } + public static Action SavedContent { get; set; } - public static Action> PublishingContent { get; set; } + public static Action PublishingContent { get; set; } - public static Action> PublishedContent { get; set; } + public static Action PublishedContent { get; set; } - public static Action> UnpublishingContent { get; set; } + public static Action UnpublishingContent { get; set; } - public static Action> UnpublishedContent { get; set; } + public static Action UnpublishedContent { get; set; } } } } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs index 612dbb87ae..819e41c257 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs @@ -76,9 +76,9 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services public void Setup() => ContentRepositoryBase.ThrowOnWarning = true; protected override void CustomTestSetup(IUmbracoBuilder builder) => builder - .AddNotificationHandler, ContentNotificationHandler>() - .AddNotificationHandler, ContentNotificationHandler>() - .AddNotificationHandler, ContentNotificationHandler>(); + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler(); [TearDown] public void Teardown() => ContentRepositoryBase.ThrowOnWarning = false; @@ -3296,21 +3296,21 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services } public class ContentNotificationHandler : - INotificationHandler>, - INotificationHandler>, - INotificationHandler> + INotificationHandler, + INotificationHandler, + INotificationHandler { - public void Handle(PublishingNotification notification) => PublishingContent?.Invoke(notification); + public void Handle(ContentPublishingNotification notification) => PublishingContent?.Invoke(notification); - public void Handle(CopyingNotification notification) => CopyingContent?.Invoke(notification); + public void Handle(ContentCopyingNotification notification) => CopyingContent?.Invoke(notification); - public void Handle(CopiedNotification notification) => CopiedContent?.Invoke(notification); + public void Handle(ContentCopiedNotification notification) => CopiedContent?.Invoke(notification); - public static Action> PublishingContent { get; set; } + public static Action PublishingContent { get; set; } - public static Action> CopyingContent { get; set; } + public static Action CopyingContent { get; set; } - public static Action> CopiedContent { get; set; } + public static Action CopiedContent { get; set; } } } } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs index 66d3c3488a..dff4e80e4c 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs @@ -33,7 +33,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services private ContentTypeService ContentTypeService => (ContentTypeService)GetRequiredService(); protected override void CustomTestSetup(IUmbracoBuilder builder) => builder - .AddNotificationHandler, ContentNotificationHandler>(); + .AddNotificationHandler(); [Test] public void CanSaveAndGetIsElement() @@ -203,7 +203,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services } } - private void MovedContentToRecycleBin(MovedToRecycleBinNotification notification) + private void MovedContentToRecycleBin(ContentMovedToRecycleBinNotification notification) { foreach (MoveEventInfo item in notification.MoveInfoCollection) { @@ -1736,11 +1736,11 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services } public class ContentNotificationHandler : - INotificationHandler> + INotificationHandler { - public void Handle(MovedToRecycleBinNotification notification) => MovedContentToRecycleBin?.Invoke(notification); + public void Handle(ContentMovedToRecycleBinNotification notification) => MovedContentToRecycleBin?.Invoke(notification); - public static Action> MovedContentToRecycleBin { get; set; } + public static Action MovedContentToRecycleBin { get; set; } } } } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs index 45732beb83..b28e22e50a 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs @@ -28,7 +28,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services private IMediaTypeService MediaTypeService => GetRequiredService(); protected override void CustomTestSetup(IUmbracoBuilder builder) => builder - .AddNotificationHandler, ContentNotificationHandler>(); + .AddNotificationHandler(); [Test] public void Get_With_Missing_Guid() @@ -139,7 +139,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services } } - private void MovedMediaToRecycleBin(MovedToRecycleBinNotification notification) + private void MovedMediaToRecycleBin(MediaMovedToRecycleBinNotification notification) { foreach (MoveEventInfo item in notification.MoveInfoCollection) { @@ -220,11 +220,11 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services } public class ContentNotificationHandler : - INotificationHandler> + INotificationHandler { - public void Handle(MovedToRecycleBinNotification notification) => MovedMediaToRecycleBin?.Invoke(notification); + public void Handle(MediaMovedToRecycleBinNotification notification) => MovedMediaToRecycleBin?.Invoke(notification); - public static Action> MovedMediaToRecycleBin { get; set; } + public static Action MovedMediaToRecycleBin { get; set; } } } } diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index be6d1f6d86..718b54a939 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -50,14 +50,14 @@ namespace Umbraco.Tests.Scoping Builder.Services.AddUnique(f => Mock.Of()); Builder.WithCollectionBuilder() .Add(() => Builder.TypeLoader.GetCacheRefreshers()); - Builder.AddNotificationHandler, NotificationHandler>(); + Builder.AddNotificationHandler(); } - public class NotificationHandler : INotificationHandler> + public class NotificationHandler : INotificationHandler { - public void Handle(PublishedNotification notification) => PublishedContent?.Invoke(notification); + public void Handle(ContentPublishedNotification notification) => PublishedContent?.Invoke(notification); - public static Action> PublishedContent { get; set; } + public static Action PublishedContent { get; set; } } public override void TearDown() diff --git a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs index 5731c71930..f943eada80 100644 --- a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs @@ -41,7 +41,7 @@ namespace Umbraco.Tests.Scoping Builder.Services.AddUnique(f => Mock.Of()); Builder.WithCollectionBuilder() .Add(() => Builder.TypeLoader.GetCacheRefreshers()); - Builder.AddNotificationHandler, NotificationHandler>(); + Builder.AddNotificationHandler(); } protected override void ComposeSettings() @@ -56,11 +56,11 @@ namespace Umbraco.Tests.Scoping } - public class NotificationHandler : INotificationHandler> + public class NotificationHandler : INotificationHandler { - public void Handle(PublishedNotification notification) => PublishedContent?.Invoke(notification); + public void Handle(ContentPublishedNotification notification) => PublishedContent?.Invoke(notification); - public static Action> PublishedContent { get; set; } + public static Action PublishedContent { get; set; } } [TearDown] From 375609bc6e601645309dde16bb41e9064c2c5849 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 16 Mar 2021 08:05:33 +0100 Subject: [PATCH 42/42] Update src/Umbraco.Web.UI.NetCore/appsettings.json --- src/Umbraco.Web.UI.NetCore/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.json b/src/Umbraco.Web.UI.NetCore/appsettings.json index 72af7afb5b..53a30cbcea 100644 --- a/src/Umbraco.Web.UI.NetCore/appsettings.json +++ b/src/Umbraco.Web.UI.NetCore/appsettings.json @@ -38,7 +38,7 @@ "ConvertUrlsToAscii": "try" }, "RuntimeMinification": { - "dataFolder": "Umbraco/Data/TEMP/Smidge", + "dataFolder": "umbraco/Data/TEMP/Smidge", "version": "1" }, "Security": {