diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs index 742838c224..fb993d1e4e 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs @@ -149,7 +149,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [FilterAllowedOutgoingContent(typeof(IEnumerable))] - public IEnumerable GetByIds([FromQuery]int[] ids) + public IEnumerable GetByIds([FromQuery] int[] ids) { var foundContent = _contentService.GetByIds(ids); return foundContent.Select(MapToDisplay); @@ -164,7 +164,8 @@ namespace Umbraco.Web.BackOffice.Controllers /// Permission check is done for letter 'R' which is for which the user must have access to update /// public async Task>> PostSaveUserGroupPermissions(UserGroupPermissionsSave saveModel) - { if (saveModel.ContentId <= 0) return NotFound(); + { + if (saveModel.ContentId <= 0) return NotFound(); // TODO: Should non-admins be allowed to set granular permissions? @@ -347,7 +348,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] [Authorize(Policy = AuthorizationPolicies.ContentPermissionBrowseById)] [DetermineAmbiguousActionByPassingParameters] public ContentItemDisplay GetById(int id) @@ -367,7 +368,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] [Authorize(Policy = AuthorizationPolicies.ContentPermissionBrowseById)] [DetermineAmbiguousActionByPassingParameters] public ContentItemDisplay GetById(Guid id) @@ -388,7 +389,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] [Authorize(Policy = AuthorizationPolicies.ContentPermissionBrowseById)] [DetermineAmbiguousActionByPassingParameters] public ContentItemDisplay GetById(Udi id) @@ -407,7 +408,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] [DetermineAmbiguousActionByPassingParameters] public ContentItemDisplay GetEmpty(string contentTypeAlias, int parentId) { @@ -426,7 +427,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] public ContentItemDisplay GetEmptyByKey(Guid contentTypeKey, int parentId) { var contentType = _contentTypeService.Get(contentTypeKey); @@ -454,7 +455,7 @@ namespace Umbraco.Web.BackOffice.Controllers return mapped; } - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] [DetermineAmbiguousActionByPassingParameters] public ContentItemDisplay GetEmpty(int blueprintId, int parentId) { @@ -598,7 +599,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// The name of the blueprint /// [HttpPost] - public ActionResult CreateBlueprintFromContent([FromQuery]int contentId, [FromQuery]string name) + public ActionResult CreateBlueprintFromContent([FromQuery] int contentId, [FromQuery] string name) { if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(name)); @@ -632,32 +633,32 @@ namespace Umbraco.Web.BackOffice.Controllers } } - /// - /// Saves content - /// - /// - [FileUploadCleanupFilter] - [ContentSaveValidation] - public async Task PostSaveBlueprint([ModelBinder(typeof(BlueprintItemBinder))] ContentItemSave contentItem) - { - var contentItemDisplay = await PostSaveInternal(contentItem, - content => - { - EnsureUniqueName(content.Name, content, "Name"); + /// + /// Saves content + /// + /// + [FileUploadCleanupFilter] + [ContentSaveValidation] + public async Task PostSaveBlueprint([ModelBinder(typeof(BlueprintItemBinder))] ContentItemSave contentItem) + { + var contentItemDisplay = await PostSaveInternal(contentItem, + content => + { + EnsureUniqueName(content.Name, content, "Name"); - _contentService.SaveBlueprint(contentItem.PersistedContent, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + _contentService.SaveBlueprint(contentItem.PersistedContent, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); //we need to reuse the underlying logic so return the result that it wants return OperationResult.Succeed(new EventMessages()); - }, - content => - { - var display = MapToDisplay(content); - SetupBlueprint(display, content); - return display; - }); + }, + content => + { + var display = MapToDisplay(content); + SetupBlueprint(display, content); + return display; + }); - return contentItemDisplay; - } + return contentItemDisplay; + } /// /// Saves content @@ -665,7 +666,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// [FileUploadCleanupFilter] [ContentSaveValidation] - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] public async Task PostSave([ModelBinder(typeof(ContentItemBinder))] ContentItemSave contentItem) { var contentItemDisplay = await PostSaveInternal( @@ -1459,7 +1460,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// private string GetVariantName(string culture, string segment) { - if(culture.IsNullOrWhiteSpace() && segment.IsNullOrWhiteSpace()) + if (culture.IsNullOrWhiteSpace() && segment.IsNullOrWhiteSpace()) { // TODO: Get name for default variant from somewhere? return "Default"; @@ -1677,7 +1678,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// The content and variants to unpublish /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] public async Task> PostUnpublish(UnpublishContent model) { var foundContent = _contentService.GetById(model.Id); @@ -1685,7 +1686,7 @@ namespace Umbraco.Web.BackOffice.Controllers if (foundContent == null) { HandleContentNotFound(model.Id); - } + } // Authorize... var resource = new ContentPermissionsResource(foundContent, ActionUnpublish.ActionLetter); @@ -2309,7 +2310,7 @@ namespace Umbraco.Web.BackOffice.Controllers return notifications; } - public IActionResult PostNotificationOptions(int contentId, [FromQuery(Name="notifyOptions[]")] string[] notifyOptions) + public IActionResult PostNotificationOptions(int contentId, [FromQuery(Name = "notifyOptions[]")] string[] notifyOptions) { if (contentId <= 0) return NotFound(); var content = _contentService.GetById(contentId); @@ -2459,7 +2460,7 @@ namespace Umbraco.Web.BackOffice.Controllers // set up public access using role based access [Authorize(Policy = AuthorizationPolicies.ContentPermissionProtectById)] [HttpPost] - public IActionResult PostPublicAccess(int contentId, [FromQuery(Name = "groups[]")]string[] groups, [FromQuery(Name = "usernames[]")]string[] usernames, int loginPageId, int errorPageId) + public IActionResult PostPublicAccess(int contentId, [FromQuery(Name = "groups[]")] string[] groups, [FromQuery(Name = "usernames[]")] string[] usernames, int loginPageId, int errorPageId) { if ((groups == null || groups.Any() == false) && (usernames == null || usernames.Any() == false)) { @@ -2520,7 +2521,7 @@ namespace Umbraco.Web.BackOffice.Controllers } return _publicAccessService.Save(entry).Success - ? (IActionResult) Ok() + ? (IActionResult)Ok() : Problem(); } @@ -2541,7 +2542,7 @@ namespace Umbraco.Web.BackOffice.Controllers } return _publicAccessService.Delete(entry).Success - ? (IActionResult) Ok() + ? (IActionResult)Ok() : Problem(); } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs b/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs index d96708b11d..a69cc6739b 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs @@ -210,7 +210,7 @@ namespace Umbraco.Web.BackOffice.Controllers // return IDashboardSlim - we don't need sections nor access rules [ValidateAngularAntiForgeryToken] - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] public IEnumerable> GetDashboard(string section) { var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser; diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs index 409967fb67..49a20f1150 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs @@ -122,7 +122,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] public MediaItemDisplay GetEmpty(string contentTypeAlias, int parentId) { var contentType = _mediaTypeService.Get(contentTypeAlias); @@ -170,7 +170,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] [Authorize(Policy = AuthorizationPolicies.MediaPermissionPathById)] [DetermineAmbiguousActionByPassingParameters] public MediaItemDisplay GetById(int id) @@ -191,7 +191,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] [Authorize(Policy = AuthorizationPolicies.MediaPermissionPathById)] [DetermineAmbiguousActionByPassingParameters] public MediaItemDisplay GetById(Guid id) @@ -212,7 +212,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] [Authorize(Policy = AuthorizationPolicies.MediaPermissionPathById)] [DetermineAmbiguousActionByPassingParameters] public MediaItemDisplay GetById(Udi id) @@ -513,7 +513,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// [FileUploadCleanupFilter] [MediaItemSaveValidation] - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] public MediaItemDisplay PostSave( [ModelBinder(typeof(MediaItemBinder))] MediaItemSave contentItem) diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs index a97ed9c2ad..267539c97f 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs @@ -151,7 +151,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] public MemberDisplay GetByKey(Guid key) { var foundMember = _memberService.GetByKey(key); @@ -167,7 +167,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] public MemberDisplay GetEmpty(string contentTypeAlias = null) { IMember emptyContent; @@ -194,7 +194,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [FileUploadCleanupFilter] - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] [MemberSaveValidation] public async Task> PostSave( [ModelBinder(typeof(MemberBinder))] diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs index 1fb3010a7d..069338750a 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs @@ -225,7 +225,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] [Authorize(Policy = AuthorizationPolicies.AdminUserEditsRequireAdmin)] public UserDisplay GetById(int id) { @@ -243,7 +243,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] [Authorize(Policy = AuthorizationPolicies.AdminUserEditsRequireAdmin)] public IEnumerable GetByIds([FromJsonPath]int[] ids) { @@ -578,7 +578,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - [TypeFilter(typeof(OutgoingEditorModelEventAttribute))] + [OutgoingEditorModelEvent] public UserDisplay PostSaveUser(UserSave userSave) { if (userSave == null) throw new ArgumentNullException(nameof(userSave)); diff --git a/src/Umbraco.Web.BackOffice/Filters/EditorModelEventManager.cs b/src/Umbraco.Web.BackOffice/Filters/EditorModelEventManager.cs index d1856ee7d9..617e2804c5 100644 --- a/src/Umbraco.Web.BackOffice/Filters/EditorModelEventManager.cs +++ b/src/Umbraco.Web.BackOffice/Filters/EditorModelEventManager.cs @@ -12,38 +12,38 @@ namespace Umbraco.Web.BackOffice.Filters /// public sealed class EditorModelEventManager { - public static event TypedEventHandler> SendingContentModel; - public static event TypedEventHandler> SendingMediaModel; - public static event TypedEventHandler> SendingMemberModel; - public static event TypedEventHandler> SendingUserModel; + public static event TypedEventHandler> SendingContentModel; + public static event TypedEventHandler> SendingMediaModel; + public static event TypedEventHandler> SendingMemberModel; + public static event TypedEventHandler> SendingUserModel; - public static event TypedEventHandler>>> SendingDashboardSlimModel; + public static event TypedEventHandler>>> SendingDashboardSlimModel; - private static void OnSendingDashboardModel(ActionExecutedContext sender, EditorModelEventArgs>> e) + private static void OnSendingDashboardModel(ResultExecutedContext sender, EditorModelEventArgs>> e) { var handler = SendingDashboardSlimModel; handler?.Invoke(sender, e); } - private static void OnSendingUserModel(ActionExecutedContext sender, EditorModelEventArgs e) + private static void OnSendingUserModel(ResultExecutedContext sender, EditorModelEventArgs e) { var handler = SendingUserModel; handler?.Invoke(sender, e); } - private static void OnSendingContentModel(ActionExecutedContext sender, EditorModelEventArgs e) + private static void OnSendingContentModel(ResultExecutedContext sender, EditorModelEventArgs e) { var handler = SendingContentModel; handler?.Invoke(sender, e); } - private static void OnSendingMediaModel(ActionExecutedContext sender, EditorModelEventArgs e) + private static void OnSendingMediaModel(ResultExecutedContext sender, EditorModelEventArgs e) { var handler = SendingMediaModel; handler?.Invoke(sender, e); } - private static void OnSendingMemberModel(ActionExecutedContext sender, EditorModelEventArgs e) + private static void OnSendingMemberModel(ResultExecutedContext sender, EditorModelEventArgs e) { var handler = SendingMemberModel; handler?.Invoke(sender, e); @@ -54,7 +54,7 @@ namespace Umbraco.Web.BackOffice.Filters /// /// /// - internal static void EmitEvent(ActionExecutedContext sender, EditorModelEventArgs e) + internal static void EmitEvent(ResultExecutedContext sender, EditorModelEventArgs e) { if (e.Model is ContentItemDisplay) OnSendingContentModel(sender, new EditorModelEventArgs(e)); diff --git a/src/Umbraco.Web.BackOffice/Filters/OutgoingEditorModelEventAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/OutgoingEditorModelEventAttribute.cs index d5df536dae..3cad31cdf7 100644 --- a/src/Umbraco.Web.BackOffice/Filters/OutgoingEditorModelEventAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/OutgoingEditorModelEventAttribute.cs @@ -4,47 +4,60 @@ using Microsoft.AspNetCore.Mvc.Filters; using Umbraco.Core; using Umbraco.Core.Security; using Umbraco.Web.Editors; -using Umbraco.Web.Security; namespace Umbraco.Web.BackOffice.Filters { /// /// Used to emit outgoing editor model events /// - internal sealed class OutgoingEditorModelEventAttribute : ActionFilterAttribute + internal sealed class OutgoingEditorModelEventAttribute : TypeFilterAttribute { - private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; - - public OutgoingEditorModelEventAttribute(IUmbracoContextAccessor umbracoContextAccessor, IBackOfficeSecurityAccessor backofficeSecurityAccessor) + public OutgoingEditorModelEventAttribute() : base(typeof(OutgoingEditorModelEventFilter)) { - _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); - _backofficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor)); } - public override void OnActionExecuted(ActionExecutedContext context) + + private class OutgoingEditorModelEventFilter : IResultFilter { - if (context.Result == null) return; - var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); - var user = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser; - if (user == null) return; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; - if (context.Result is ObjectResult objectContent) + private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; + + public OutgoingEditorModelEventFilter( + IUmbracoContextAccessor umbracoContextAccessor, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor) { - var model = objectContent.Value; + _umbracoContextAccessor = umbracoContextAccessor + ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); + _backOfficeSecurityAccessor = backOfficeSecurityAccessor + ?? throw new ArgumentNullException(nameof(backOfficeSecurityAccessor)); + } - if (model != null) + public void OnResultExecuted(ResultExecutedContext context) + { + if (context.Result == null) return; + + var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); + var user = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser; + if (user == null) return; + + if (context.Result is ObjectResult objectContent) { - var args = new EditorModelEventArgs( - model, - umbracoContext); - EditorModelEventManager.EmitEvent(context, args); - objectContent.Value = args.Model; + var model = objectContent.Value; + + if (model != null) + { + var args = new EditorModelEventArgs(model, umbracoContext); + EditorModelEventManager.EmitEvent(context, args); + objectContent.Value = args.Model; + } } } - base.OnActionExecuted(context); + public void OnResultExecuting(ResultExecutingContext context) + { + } } } }