From 04bb4e99b6ce7f2b32f4051422006522a9a4ad83 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 14 Jan 2021 19:41:32 +0100 Subject: [PATCH] Fixed possible null reference exceptions --- .../Controllers/ContentController.cs | 44 ++++++---- .../Controllers/ContentControllerBase.cs | 7 +- .../Controllers/ContentTypeController.cs | 21 ++++- .../Controllers/EntityController.cs | 27 ++++-- .../Controllers/MediaController.cs | 27 +++++- .../Controllers/MediaTypeController.cs | 21 ++++- .../Controllers/MemberController.cs | 3 +- .../Controllers/MemberTypeController.cs | 27 ++++-- .../Controllers/SectionController.cs | 8 +- .../Controllers/UsersController.cs | 8 +- .../Trees/ApplicationTreeController.cs | 64 ++++++++++---- .../Trees/ContentBlueprintTreeController.cs | 13 +-- .../Trees/ContentTreeController.cs | 35 ++++---- .../Trees/ContentTreeControllerBase.cs | 48 +++++++---- .../Trees/ContentTypeTreeController.cs | 12 ++- .../Trees/DataTypeTreeController.cs | 18 ++-- .../Trees/DictionaryTreeController.cs | 11 ++- .../Trees/FileSystemTreeController.cs | 19 ++++- .../Trees/LanguageTreeController.cs | 14 ++-- .../Trees/LogViewerTreeController.cs | 14 ++-- .../Trees/MacrosTreeController.cs | 23 +++-- .../Trees/MediaTypeTreeController.cs | 17 ++-- .../Trees/MemberGroupTreeController.cs | 12 ++- .../Trees/MemberTreeController.cs | 14 ++-- .../MemberTypeAndGroupTreeControllerBase.cs | 2 +- .../Trees/MemberTypeTreeController.cs | 13 ++- .../Trees/PackagesTreeController.cs | 15 ++-- .../Trees/RelationTypeTreeController.cs | 11 ++- .../Trees/TemplatesTreeController.cs | 14 ++-- .../Trees/TreeControllerBase.cs | 36 ++++++-- .../Trees/UserTreeController.cs | 13 ++- .../Exceptions/HttpResponseException.cs | 84 ------------------- 32 files changed, 411 insertions(+), 284 deletions(-) delete mode 100644 src/Umbraco.Web.Common/Exceptions/HttpResponseException.cs diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs index c1f08f5a85..52273b92ee 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs @@ -6,7 +6,6 @@ using System.Net.Mime; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; @@ -34,7 +33,6 @@ using Umbraco.Web.BackOffice.ModelBinders; using Umbraco.Web.Common.ActionsResults; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Authorization; -using Umbraco.Web.Common.Exceptions; using Umbraco.Web.ContentApps; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.Mapping; @@ -335,13 +333,12 @@ namespace Umbraco.Web.BackOffice.Controllers [OutgoingEditorModelEvent] [Authorize(Policy = AuthorizationPolicies.ContentPermissionBrowseById)] [DetermineAmbiguousActionByPassingParameters] - public ContentItemDisplay GetById(int id) + public ActionResult GetById(int id) { var foundContent = GetObjectFromRequest(() => _contentService.GetById(id)); if (foundContent == null) { - HandleContentNotFound(id); - return null;//irrelevant since the above throws + return HandleContentNotFound(id); } var content = MapToDisplay(foundContent); return content; @@ -355,13 +352,12 @@ namespace Umbraco.Web.BackOffice.Controllers [OutgoingEditorModelEvent] [Authorize(Policy = AuthorizationPolicies.ContentPermissionBrowseById)] [DetermineAmbiguousActionByPassingParameters] - public ContentItemDisplay GetById(Guid id) + public ActionResult GetById(Guid id) { var foundContent = GetObjectFromRequest(() => _contentService.GetById(id)); if (foundContent == null) { - HandleContentNotFound(id); - return null;//irrelevant since the above throws + return HandleContentNotFound(id); } var content = MapToDisplay(foundContent); @@ -590,9 +586,14 @@ namespace Umbraco.Web.BackOffice.Controllers var content = _contentService.GetById(contentId); if (content == null) + { return NotFound(); + } - EnsureUniqueName(name, content, nameof(name)); + if (!EnsureUniqueName(name, content, nameof(name))) + { + return new ValidationErrorResult(ModelState.ToErrorDictionary()); + } var blueprint = _contentService.CreateContentFromBlueprint(content, name, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0)); @@ -1484,7 +1485,7 @@ namespace Umbraco.Web.BackOffice.Controllers if (foundContent == null) { - return HandleContentNotFound(id, false); + return HandleContentNotFound(id); } var publishResult = _contentService.SaveAndPublish(foundContent, userId: _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0)); @@ -1507,7 +1508,7 @@ namespace Umbraco.Web.BackOffice.Controllers if (found == null) { - return HandleContentNotFound(id, false); + return HandleContentNotFound(id); } _contentService.DeleteBlueprint(found); @@ -1533,7 +1534,7 @@ namespace Umbraco.Web.BackOffice.Controllers if (foundContent == null) { - return HandleContentNotFound(id, false); + return HandleContentNotFound(id); } //if the current item is in the recycle bin @@ -1639,7 +1640,12 @@ namespace Umbraco.Web.BackOffice.Controllers return Forbid(); } - var toMove = ValidateMoveOrCopy(move).Value; + var toMoveResult = ValidateMoveOrCopy(move); + if (!(toMoveResult is null)) + { + return toMoveResult.Result; + } + var toMove = toMoveResult.Value; _contentService.Move(toMove, move.ParentId, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0)); @@ -1651,7 +1657,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - public async Task PostCopy(MoveOrCopy copy) + public async Task> PostCopy(MoveOrCopy copy) { // Authorize... var resource = new ContentPermissionsResource(_contentService.GetById(copy.ParentId), ActionCopy.ActionLetter); @@ -1661,8 +1667,12 @@ namespace Umbraco.Web.BackOffice.Controllers return Forbid(); } - var toCopy = ValidateMoveOrCopy(copy).Value; - + var toCopyResult = ValidateMoveOrCopy(copy); + if ((toCopyResult.Result is null)) + { + return toCopyResult.Result; + } + var toCopy = toCopyResult.Value; var c = _contentService.Copy(toCopy, copy.ParentId, copy.RelateToOriginal, copy.Recursive, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0)); return Content(c.Path, MediaTypeNames.Text.Plain, Encoding.UTF8); @@ -1680,7 +1690,7 @@ namespace Umbraco.Web.BackOffice.Controllers if (foundContent == null) { - HandleContentNotFound(model.Id); + return HandleContentNotFound(model.Id); } // Authorize... diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs index 28047ea119..75d62f1863 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs @@ -11,7 +11,6 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Serialization; using Umbraco.Core.Services; using Umbraco.Core.Strings; -using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Common.Filters; using Umbraco.Web.Models.ContentEditing; @@ -71,14 +70,10 @@ namespace Umbraco.Web.BackOffice.Controllers /// protected ILocalizedTextService LocalizedTextService { get; } - protected NotFoundObjectResult HandleContentNotFound(object id, bool throwException = true) + protected NotFoundObjectResult HandleContentNotFound(object id) { ModelState.AddModelError("id", $"content with id: {id} was not found"); var errorResponse = NotFound(ModelState); - if (throwException) - { - throw new HttpResponseException(errorResponse); - } return errorResponse; } diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs index e27968face..ea34005a87 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs @@ -208,9 +208,18 @@ namespace Umbraco.Web.BackOffice.Controllers /// [HttpPost] [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] - public IActionResult GetAvailableCompositeContentTypes(GetAvailableCompositionsFilter filter) + public ActionResult GetAvailableCompositeContentTypes(GetAvailableCompositionsFilter filter) { - var result = PerformGetAvailableCompositeContentTypes(filter.ContentTypeId, UmbracoObjectTypes.DocumentType, filter.FilterContentTypes, filter.FilterPropertyTypes, filter.IsElement).Value + var actionResult = PerformGetAvailableCompositeContentTypes(filter.ContentTypeId, + UmbracoObjectTypes.DocumentType, filter.FilterContentTypes, filter.FilterPropertyTypes, + filter.IsElement); + + if (!(actionResult.Result is null)) + { + return actionResult.Result; + } + + var result = actionResult.Value .Select(x => new { contentType = x.Item1, @@ -361,7 +370,7 @@ namespace Umbraco.Web.BackOffice.Controllers } [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] - public DocumentTypeDisplay PostSave(DocumentTypeSave contentTypeSave) + public ActionResult PostSave(DocumentTypeSave contentTypeSave) { //Before we send this model into this saving/mapping pipeline, we need to do some cleanup on variations. //If the doc type does not allow content variations, we need to update all of it's property types to not allow this either @@ -404,8 +413,14 @@ namespace Umbraco.Web.BackOffice.Controllers } }); + if (!(savedCt.Result is null)) + { + return savedCt.Result; + } + var display = _umbracoMapper.Map(savedCt.Value); + display.AddSuccessNotification( _localizedTextService.Localize("speechBubbles/contentTypeSavedHeader"), string.Empty); diff --git a/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs index 32d09e9a5c..13ef66fa15 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; using Umbraco.Core; using Umbraco.Core.Mapping; using Umbraco.Core.Models; @@ -201,11 +202,16 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [DetermineAmbiguousActionByPassingParameters] - public IEnumerable GetPath(int id, UmbracoEntityTypes type) + public IConvertToActionResult GetPath(int id, UmbracoEntityTypes type) { - var foundContent = GetResultForId(id, type).Value; + var foundContentResult = GetResultForId(id, type); + var foundContent = foundContentResult.Value; + if (foundContent is null) + { + return foundContentResult; + } - return foundContent.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse); + return new ActionResult>(foundContent.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse)); } /// @@ -215,11 +221,16 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [DetermineAmbiguousActionByPassingParameters] - public IEnumerable GetPath(Guid id, UmbracoEntityTypes type) + public IConvertToActionResult GetPath(Guid id, UmbracoEntityTypes type) { - var foundContent = GetResultForKey(id, type).Value; + var foundContentResult = GetResultForKey(id, type); + var foundContent = foundContentResult.Value; + if (foundContent is null) + { + return foundContentResult; + } - return foundContent.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse); + return new ActionResult>(foundContent.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse)); } /// @@ -229,12 +240,12 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// [DetermineAmbiguousActionByPassingParameters] - public ActionResult> GetPath(Udi id, UmbracoEntityTypes type) + public IActionResult GetPath(Udi id, UmbracoEntityTypes type) { var guidUdi = id as GuidUdi; if (guidUdi != null) { - return new ActionResult>(GetPath(guidUdi.Guid, type)); + return GetPath(guidUdi.Guid, type).Convert(); } return NotFound(); diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs index 3af2d7da2a..e822e7df84 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Core; @@ -443,7 +444,7 @@ namespace Umbraco.Web.BackOffice.Controllers if (foundMedia == null) { - return HandleContentNotFound(id, false); + return HandleContentNotFound(id); } //if the current item is in the recycle bin @@ -486,7 +487,13 @@ namespace Umbraco.Web.BackOffice.Controllers return Forbid(); } - var toMove = ValidateMoveOrCopy(move).Value; + var toMoveResult = ValidateMoveOrCopy(move); + var toMove = toMoveResult.Value; + if (toMove is null && toMoveResult is IConvertToActionResult convertToActionResult) + { + return convertToActionResult.Convert(); + } + var destinationParentID = move.ParentId; var sourceParentID = toMove.ParentId; @@ -667,7 +674,12 @@ namespace Umbraco.Web.BackOffice.Controllers public async Task> PostAddFolder(PostedFolder folder) { - var parentId = (await GetParentIdAsIntAsync(folder.ParentId, validatePermissions:true)).Value; + var parentIdResult = await GetParentIdAsIntAsync(folder.ParentId, validatePermissions:true); + if (!(parentIdResult.Result is null)) + { + return new ActionResult(parentIdResult.Result); + } + var parentId = parentIdResult.Value; if (!parentId.HasValue) { return NotFound("The passed id doesn't exist"); @@ -699,11 +711,18 @@ namespace Umbraco.Web.BackOffice.Controllers } //get the string json from the request - var parentId = (await GetParentIdAsIntAsync(currentFolder, validatePermissions: true)).Value; + var parentIdResult = await GetParentIdAsIntAsync(currentFolder, validatePermissions:true); + if (!(parentIdResult.Result is null)) + { + return parentIdResult.Result; + } + + var parentId = parentIdResult.Value; if (!parentId.HasValue) { return NotFound("The passed id doesn't exist"); } + var tempFiles = new PostedFiles(); diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs index deab42db8d..b8952e580f 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs @@ -176,9 +176,16 @@ namespace Umbraco.Web.BackOffice.Controllers [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public IActionResult GetAvailableCompositeMediaTypes(GetAvailableCompositionsFilter filter) { - var result = PerformGetAvailableCompositeContentTypes(filter.ContentTypeId, UmbracoObjectTypes.MediaType, - filter.FilterContentTypes, filter.FilterPropertyTypes, filter.IsElement).Value - .Select(x => new + var actionResult = PerformGetAvailableCompositeContentTypes(filter.ContentTypeId, + UmbracoObjectTypes.MediaType, filter.FilterContentTypes, filter.FilterPropertyTypes, + filter.IsElement); + + if (!(actionResult.Result is null)) + { + return actionResult.Result; + } + + var result = actionResult.Value.Select(x => new { contentType = x.Item1, allowed = x.Item2 @@ -273,15 +280,21 @@ namespace Umbraco.Web.BackOffice.Controllers } [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] - public MediaTypeDisplay PostSave(MediaTypeSave contentTypeSave) + public ActionResult PostSave(MediaTypeSave contentTypeSave) { var savedCt = PerformPostSave( contentTypeSave, i => _mediaTypeService.Get(i), type => _mediaTypeService.Save(type)); + if (!(savedCt.Result is null)) + { + return savedCt.Result; + } + var display = _umbracoMapper.Map(savedCt.Value); + display.AddSuccessNotification( _localizedTextService.Localize("speechBubbles/mediaTypeSavedHeader"), string.Empty); diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs index ba0e22b2bf..26d84756bd 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs @@ -33,7 +33,6 @@ using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.Filters; using Umbraco.Web.ContentApps; using Umbraco.Web.Models.ContentEditing; -using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Controllers { @@ -453,7 +452,7 @@ namespace Umbraco.Web.BackOffice.Controllers var foundMember = _memberService.GetByKey(key); if (foundMember == null) { - return HandleContentNotFound(key, false); + return HandleContentNotFound(key); } _memberService.Delete(foundMember); diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs index 7598c0d449..7944da1f0a 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs @@ -1,20 +1,19 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Umbraco.Core; using Umbraco.Core.Dictionary; +using Umbraco.Core.Mapping; using Umbraco.Core.Models; +using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Core.Strings; -using Umbraco.Web.Models.ContentEditing; -using Constants = Umbraco.Core.Constants; -using Umbraco.Core.Mapping; -using Umbraco.Core.Security; using Umbraco.Web.Common.Attributes; -using Umbraco.Web.Editors; -using Microsoft.AspNetCore.Authorization; using Umbraco.Web.Common.Authorization; +using Umbraco.Web.Editors; +using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.BackOffice.Controllers { @@ -152,7 +151,16 @@ namespace Umbraco.Web.BackOffice.Controllers [FromQuery]string[] filterContentTypes, [FromQuery]string[] filterPropertyTypes) { - var result = PerformGetAvailableCompositeContentTypes(contentTypeId, UmbracoObjectTypes.MemberType, filterContentTypes, filterPropertyTypes, false).Value + var actionResult = PerformGetAvailableCompositeContentTypes(contentTypeId, + UmbracoObjectTypes.MemberType, filterContentTypes, filterPropertyTypes, + false); + + if (!(actionResult.Result is null)) + { + return actionResult.Result; + } + + var result = actionResult.Value .Select(x => new { contentType = x.Item1, @@ -230,6 +238,11 @@ namespace Umbraco.Web.BackOffice.Controllers getContentType: i => ct, saveContentType: type => _memberTypeService.Save(type)); + if (!(savedCt.Result is null)) + { + return savedCt.Result; + } + var display =_umbracoMapper.Map(savedCt.Value); display.AddSuccessNotification( diff --git a/src/Umbraco.Web.BackOffice/Controllers/SectionController.cs b/src/Umbraco.Web.BackOffice/Controllers/SectionController.cs index 41cc6e99ff..5b1e5fb18a 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/SectionController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/SectionController.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Infrastructure; using Umbraco.Core; @@ -48,7 +49,7 @@ namespace Umbraco.Web.BackOffice.Controllers _actionDescriptorCollectionProvider = actionDescriptorCollectionProvider; } - public async Task> GetSections() + public async Task>> GetSections() { var sections = _sectionService.GetAllowedSections(_backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0)); @@ -74,6 +75,11 @@ namespace Umbraco.Web.BackOffice.Controllers // get the first tree in the section and get its root node route path var sectionRoot = await appTreeController.GetApplicationTrees(section.Alias, null, null); + + if (!(sectionRoot.Result is null)) + { + return sectionRoot.Result; + } section.RoutePath = GetRoutePathForFirstTree(sectionRoot.Value); } diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs index fd2623ceab..5e84b7cd65 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs @@ -427,7 +427,13 @@ namespace Umbraco.Web.BackOffice.Controllers else { //first validate the username if we're showing it - user = CheckUniqueUsername(userSave.Username, u => u.LastLoginDate != default || u.EmailConfirmedDate.HasValue).Value; + var userResult = CheckUniqueUsername(userSave.Username, u => u.LastLoginDate != default || u.EmailConfirmedDate.HasValue); + if (!(userResult.Result is null)) + { + return userResult.Result; + } + + user = userResult.Value; } user = CheckUniqueEmail(userSave.Email, u => u.LastLoginDate != default || u.EmailConfirmedDate.HasValue); diff --git a/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs index 1a5aa688d4..22667f0c30 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; -using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -15,13 +13,11 @@ using Umbraco.Core.Services; using Umbraco.Extensions; using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.Common.Attributes; -using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Common.Filters; using Umbraco.Web.Common.ModelBinders; using Umbraco.Web.Models.Trees; using Umbraco.Web.Services; using Umbraco.Web.Trees; -using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Trees { @@ -96,8 +92,11 @@ namespace Umbraco.Web.BackOffice.Trees return NotFound(); var treeRootNode = await GetTreeRootNode(t, Constants.System.Root, queryStrings); + if (treeRootNode != null) + { return treeRootNode; + } return NotFound(); } @@ -109,7 +108,12 @@ namespace Umbraco.Web.BackOffice.Trees var nodes = new TreeNodeCollection(); foreach (var t in allTrees) { - var node = await TryGetRootNode(t, queryStrings); + var nodeResult = await TryGetRootNode(t, queryStrings); + if (!(nodeResult.Result is null)) + { + return nodeResult.Result; + } + var node = nodeResult.Value; if (node != null) nodes.Add(node); } @@ -135,7 +139,13 @@ namespace Umbraco.Web.BackOffice.Trees var nodes = new TreeNodeCollection(); foreach (var t in trees) { - var node = await TryGetRootNode(t, queryStrings); + var nodeResult = await TryGetRootNode(t, queryStrings); + if (!(nodeResult.Result is null)) + { + return nodeResult.Result; + } + var node = nodeResult.Value; + if (node != null) nodes.Add(node); } @@ -165,7 +175,7 @@ namespace Umbraco.Web.BackOffice.Trees /// loading multiple trees we will just return null so that it's not added /// to the list /// - private async Task TryGetRootNode(Tree tree, FormCollection querystring) + private async Task> TryGetRootNode(Tree tree, FormCollection querystring) { if (tree == null) throw new ArgumentNullException(nameof(tree)); @@ -176,13 +186,26 @@ namespace Umbraco.Web.BackOffice.Trees /// /// Get the tree root node of a tree. /// - private async Task GetTreeRootNode(Tree tree, int id, FormCollection querystring) + private async Task> GetTreeRootNode(Tree tree, int id, FormCollection querystring) { if (tree == null) throw new ArgumentNullException(nameof(tree)); - var children = await GetChildren(tree, id, querystring); - var rootNode = await GetRootNode(tree, querystring); + var childrenResult = await GetChildren(tree, id, querystring); + if (!(childrenResult.Result is null)) + { + return new ActionResult(childrenResult.Result); + } + + var children = childrenResult.Value; + var rootNodeResult = await GetRootNode(tree, querystring); + if (!(rootNodeResult.Result is null)) + { + return rootNodeResult.Result; + } + + var rootNode = rootNodeResult.Value; + var sectionRoot = TreeRootNode.CreateSingleTreeRoot( Constants.System.RootString, @@ -206,7 +229,7 @@ namespace Umbraco.Web.BackOffice.Trees /// /// Gets the root node of a tree. /// - private async Task GetRootNode(Tree tree, FormCollection querystring) + private async Task> GetRootNode(Tree tree, FormCollection querystring) { if (tree == null) throw new ArgumentNullException(nameof(tree)); @@ -220,7 +243,14 @@ namespace Umbraco.Web.BackOffice.Trees } var controller = (TreeControllerBase)result.Value; - var rootNode = controller.GetRootNode(querystring); + var rootNodeResult = controller.GetRootNode(querystring); + if (!(rootNodeResult.Result is null)) + { + return rootNodeResult.Result; + } + + var rootNode = rootNodeResult.Value; + if (rootNode == null) throw new InvalidOperationException($"Failed to get root node for tree \"{tree.TreeAlias}\"."); return rootNode; @@ -229,7 +259,7 @@ namespace Umbraco.Web.BackOffice.Trees /// /// Get the child nodes of a tree node. /// - private async Task GetChildren(Tree tree, int id, FormCollection querystring) + private async Task> GetChildren(Tree tree, int id, FormCollection querystring) { if (tree == null) throw new ArgumentNullException(nameof(tree)); @@ -241,7 +271,13 @@ namespace Umbraco.Web.BackOffice.Trees d["id"] = StringValues.Empty; var proxyQuerystring = new FormCollection(d); - var controller = (TreeControllerBase)(await GetApiControllerProxy(tree.TreeControllerType, "GetNodes", proxyQuerystring)).Value; + var controllerResult = await GetApiControllerProxy(tree.TreeControllerType, "GetNodes", proxyQuerystring); + if (!(controllerResult.Result is null)) + { + return new ActionResult(controllerResult.Result); + } + + var controller = (TreeControllerBase)controllerResult.Value; return controller.GetNodes(id.ToInvariantString(), querystring); } diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentBlueprintTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ContentBlueprintTreeController.cs index f1ea8329b8..c0396b68e6 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ContentBlueprintTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ContentBlueprintTreeController.cs @@ -8,13 +8,11 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Services; using Umbraco.Web.Actions; -using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.Trees; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; -using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Trees { @@ -50,9 +48,14 @@ namespace Umbraco.Web.BackOffice.Trees _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); } - protected override TreeNode CreateRootNode(FormCollection queryStrings) + protected override ActionResult CreateRootNode(FormCollection queryStrings) { - var root = base.CreateRootNode(queryStrings); + var rootResult = base.CreateRootNode(queryStrings); + if (!(rootResult.Result is null)) + { + return rootResult; + } + var root = rootResult.Value; //this will load in a custom UI instead of the dashboard for the root node root.RoutePath = $"{Constants.Applications.Settings}/{Constants.Trees.ContentBlueprints}/intro"; @@ -62,7 +65,7 @@ namespace Umbraco.Web.BackOffice.Trees return root; } - protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { var nodes = new TreeNodeCollection(); diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs index 8ec0bfe449..4c139847f0 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs @@ -1,29 +1,26 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Core; +using Umbraco.Core.Configuration.Models; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; -using Umbraco.Core.Services; -using Umbraco.Web.Actions; -using Umbraco.Web.Models.Trees; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Search; using Umbraco.Core.Security; -using Constants = Umbraco.Core.Constants; -using Umbraco.Web.Common.Attributes; -using Umbraco.Web.Common.Exceptions; -using Umbraco.Web.WebApi; -using Umbraco.Core.Configuration.Models; -using Microsoft.Extensions.Options; -using Umbraco.Web.Trees; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Umbraco.Web.Common.Authorization; +using Umbraco.Core.Services; using Umbraco.Core.Trees; +using Umbraco.Web.Actions; +using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Models.Trees; +using Umbraco.Web.Search; +using Umbraco.Web.Trees; +using Umbraco.Web.WebApi; namespace Umbraco.Web.BackOffice.Trees { @@ -240,6 +237,12 @@ namespace Umbraco.Web.BackOffice.Trees protected override ActionResult> GetChildEntities(string id, FormCollection queryStrings) { var result = base.GetChildEntities(id, queryStrings); + + if (!(result.Result is null)) + { + return result.Result; + } + var culture = queryStrings["culture"].TryConvertTo(); //if this is null we'll set it to the default. diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs index 14120825c9..212b4dd890 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs @@ -2,21 +2,18 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Net; -using Microsoft.Extensions.Logging; -using Umbraco.Core; -using Umbraco.Core.Services; -using Umbraco.Core.Models; -using Umbraco.Web.Models.Trees; -using Umbraco.Core.Models.Entities; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Umbraco.Web.Actions; +using Microsoft.Extensions.Logging; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Entities; using Umbraco.Core.Security; +using Umbraco.Core.Services; using Umbraco.Extensions; -using Umbraco.Web.Common.Exceptions; +using Umbraco.Web.Actions; using Umbraco.Web.Common.ModelBinders; -using Umbraco.Web.Security; +using Umbraco.Web.Models.Trees; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; @@ -98,9 +95,14 @@ namespace Umbraco.Web.BackOffice.Trees /// /// /// - protected override TreeNode CreateRootNode(FormCollection queryStrings) + protected override ActionResult CreateRootNode(FormCollection queryStrings) { - var node = base.CreateRootNode(queryStrings); + var nodeResult = base.CreateRootNode(queryStrings); + if ((nodeResult.Result is null)) + { + return nodeResult; + } + var node = nodeResult.Value; if (IsDialog(queryStrings) && UserStartNodes.Contains(Constants.System.Root) == false && IgnoreUserStartNodes(queryStrings) == false) { @@ -174,7 +176,7 @@ namespace Umbraco.Web.BackOffice.Trees /// protected abstract int[] UserStartNodes { get; } - protected virtual TreeNodeCollection PerformGetTreeNodes(string id, FormCollection queryStrings) + protected virtual ActionResult PerformGetTreeNodes(string id, FormCollection queryStrings) { var nodes = new TreeNodeCollection(); @@ -211,7 +213,13 @@ namespace Umbraco.Web.BackOffice.Trees // get child entities - if id is root, but user's start nodes do not contain the // root node, this returns the start nodes instead of root's children - var entities = GetChildEntities(id, queryStrings).Value.ToList(); + var entitiesResult = GetChildEntities(id, queryStrings); + if (!(entitiesResult.Result is null)) + { + return entitiesResult.Result; + } + + var entities = entitiesResult.Value.ToList(); //get the current user start node/paths GetUserStartNodes(out var userStartNodes, out var userStartNodePaths); @@ -325,7 +333,7 @@ namespace Umbraco.Web.BackOffice.Trees /// /// This method is overwritten strictly to render the recycle bin, it should serve no other purpose /// - protected sealed override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected sealed override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { //check if we're rendering the root if (id == Constants.System.RootString && UserStartNodes.Contains(Constants.System.Root)) @@ -341,7 +349,13 @@ namespace Umbraco.Web.BackOffice.Trees id = altStartId; } - var nodes = GetTreeNodesInternal(id, queryStrings); + var nodesResult = GetTreeNodesInternal(id, queryStrings); + if (!(nodesResult.Result is null)) + { + return nodesResult.Result; + } + + var nodes = nodesResult.Value; //only render the recycle bin if we are not in dialog and the start id is still the root //we need to check for the "application" key in the queryString because its value is required here, @@ -410,7 +424,7 @@ namespace Umbraco.Web.BackOffice.Trees /// /// Currently this just checks if it is a container type, if it is we cannot render children. In the future this might check for other things. /// - private TreeNodeCollection GetTreeNodesInternal(string id, FormCollection queryStrings) + private ActionResult GetTreeNodesInternal(string id, FormCollection queryStrings) { var current = GetEntityFromId(id); diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs index defe628123..f115e3e923 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs @@ -9,7 +9,6 @@ using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core.Trees; using Umbraco.Web.Actions; -using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.ContentEditing; @@ -39,15 +38,20 @@ namespace Umbraco.Web.BackOffice.Trees _entityService = entityService; } - protected override TreeNode CreateRootNode(FormCollection queryStrings) + protected override ActionResult CreateRootNode(FormCollection queryStrings) { - var root = base.CreateRootNode(queryStrings); + var rootResult = base.CreateRootNode(queryStrings); + if (!(rootResult.Result is null)) + { + return rootResult; + } + var root = rootResult.Value; //check if there are any types root.HasChildren = _contentTypeService.GetAll().Any(); return root; } - protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { var intId = id.TryConvertTo(); if (intId == false) throw new InvalidOperationException("Id must be an integer"); diff --git a/src/Umbraco.Web.BackOffice/Trees/DataTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/DataTypeTreeController.cs index 9a8c94e686..79cfee1ce7 100644 --- a/src/Umbraco.Web.BackOffice/Trees/DataTypeTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/DataTypeTreeController.cs @@ -1,23 +1,21 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Web.Models.Trees; using Umbraco.Core.Services; +using Umbraco.Core.Trees; using Umbraco.Web.Actions; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Search; -using Constants = Umbraco.Core.Constants; -using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Models.Trees; +using Umbraco.Web.Search; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Umbraco.Web.Common.Authorization; -using Umbraco.Core.Trees; namespace Umbraco.Web.BackOffice.Trees { @@ -41,7 +39,7 @@ namespace Umbraco.Web.BackOffice.Trees _dataTypeService = dataTypeService; } - protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { var intId = id.TryConvertTo(); if (intId == false) throw new InvalidOperationException("Id must be an integer"); diff --git a/src/Umbraco.Web.BackOffice/Trees/DictionaryTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/DictionaryTreeController.cs index ae9131c9e7..87f7a1508f 100644 --- a/src/Umbraco.Web.BackOffice/Trees/DictionaryTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/DictionaryTreeController.cs @@ -33,9 +33,14 @@ namespace Umbraco.Web.BackOffice.Trees _localizationService = localizationService; } - protected override TreeNode CreateRootNode(FormCollection queryStrings) + protected override ActionResult CreateRootNode(FormCollection queryStrings) { - var root = base.CreateRootNode(queryStrings); + var rootResult = base.CreateRootNode(queryStrings); + if (!(rootResult.Result is null)) + { + return rootResult; + } + var root = rootResult.Value; // the default section is settings, falling back to this if we can't // figure out where we are from the querystring parameters @@ -60,7 +65,7 @@ namespace Umbraco.Web.BackOffice.Trees /// We are allowing an arbitrary number of query strings to be passed in so that developers are able to persist custom data from the front-end /// to the back end to be used in the query for model data. /// - protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { var intId = id.TryConvertTo(); if (intId == false) diff --git a/src/Umbraco.Web.BackOffice/Trees/FileSystemTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/FileSystemTreeController.cs index 54c3462fde..ab77e00067 100644 --- a/src/Umbraco.Web.BackOffice/Trees/FileSystemTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/FileSystemTreeController.cs @@ -46,7 +46,7 @@ namespace Umbraco.Web.BackOffice.Trees treeNode.AdditionalData["jsClickCallback"] = "javascript:void(0);"; } - protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { var path = string.IsNullOrEmpty(id) == false && id != Constants.System.RootString ? WebUtility.UrlDecode(id).TrimStart("/") @@ -93,11 +93,22 @@ namespace Umbraco.Web.BackOffice.Trees return nodes; } - protected override TreeNode CreateRootNode(FormCollection queryStrings) + protected override ActionResult CreateRootNode(FormCollection queryStrings) { - var root = base.CreateRootNode(queryStrings); + var rootResult = base.CreateRootNode(queryStrings); + if (!(rootResult.Result is null)) + { + return rootResult; + } + var root = rootResult.Value; + //check if there are any children - root.HasChildren = GetTreeNodes(Constants.System.RootString, queryStrings).Any(); + var treeNodesResult = GetTreeNodes(Constants.System.RootString, queryStrings); + if (!(treeNodesResult.Result is null)) + { + return treeNodesResult.Result; + } + root.HasChildren = treeNodesResult.Value.Any(); return root; } diff --git a/src/Umbraco.Web.BackOffice/Trees/LanguageTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/LanguageTreeController.cs index 44f0111f13..3dcbbc9da8 100644 --- a/src/Umbraco.Web.BackOffice/Trees/LanguageTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/LanguageTreeController.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Umbraco.Core; using Umbraco.Core.Services; -using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.Trees; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; -using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Trees { @@ -25,7 +24,7 @@ namespace Umbraco.Web.BackOffice.Trees : base(textService, umbracoApiControllerTypeCollection) { } - protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { //We don't have any child nodes & only use the root node to load a custom UI return new TreeNodeCollection(); @@ -41,9 +40,14 @@ namespace Umbraco.Web.BackOffice.Trees /// Helper method to create a root model for a tree /// /// - protected override TreeNode CreateRootNode(FormCollection queryStrings) + protected override ActionResult CreateRootNode(FormCollection queryStrings) { - var root = base.CreateRootNode(queryStrings); + var rootResult = base.CreateRootNode(queryStrings); + if (!(rootResult.Result is null)) + { + return rootResult; + } + var root = rootResult.Value; //this will load in a custom UI instead of the dashboard for the root node root.RoutePath = $"{Constants.Applications.Settings}/{Constants.Trees.Languages}/overview"; diff --git a/src/Umbraco.Web.BackOffice/Trees/LogViewerTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/LogViewerTreeController.cs index e3b76b8af1..91b89cee69 100644 --- a/src/Umbraco.Web.BackOffice/Trees/LogViewerTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/LogViewerTreeController.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Umbraco.Core; using Umbraco.Core.Services; -using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.Trees; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; -using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Trees { @@ -25,7 +24,7 @@ namespace Umbraco.Web.BackOffice.Trees { } - protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { //We don't have any child nodes & only use the root node to load a custom UI return new TreeNodeCollection(); @@ -41,9 +40,14 @@ namespace Umbraco.Web.BackOffice.Trees /// Helper method to create a root model for a tree /// /// - protected override TreeNode CreateRootNode(FormCollection queryStrings) + protected override ActionResult CreateRootNode(FormCollection queryStrings) { - var root = base.CreateRootNode(queryStrings); + var rootResult = base.CreateRootNode(queryStrings); + if (!(rootResult.Result is null)) + { + return rootResult; + } + var root = rootResult.Value; //this will load in a custom UI instead of the dashboard for the root node root.RoutePath = string.Format("{0}/{1}/{2}", Constants.Applications.Settings, Constants.Trees.LogViewer, "overview"); diff --git a/src/Umbraco.Web.BackOffice/Trees/MacrosTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MacrosTreeController.cs index 57d2169be5..d59c5c8d3a 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MacrosTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MacrosTreeController.cs @@ -1,16 +1,15 @@ using System.Linq; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Core; using Umbraco.Core.Services; -using Umbraco.Web.Models.Trees; using Umbraco.Web.Actions; -using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; +using Umbraco.Web.Models.Trees; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; -using Constants = Umbraco.Core.Constants; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Trees { @@ -29,15 +28,21 @@ namespace Umbraco.Web.BackOffice.Trees _macroService = macroService; } - protected override TreeNode CreateRootNode(FormCollection queryStrings) + protected override ActionResult CreateRootNode(FormCollection queryStrings) { - var root = base.CreateRootNode(queryStrings); + var rootResult = base.CreateRootNode(queryStrings); + if (!(rootResult.Result is null)) + { + return rootResult; + } + var root = rootResult.Value; + //check if there are any macros root.HasChildren = _macroService.GetAll().Any(); return root; } - protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { var nodes = new TreeNodeCollection(); diff --git a/src/Umbraco.Web.BackOffice/Trees/MediaTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MediaTypeTreeController.cs index 7555895483..ff53a82219 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MediaTypeTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MediaTypeTreeController.cs @@ -1,22 +1,21 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Web.Models.Trees; using Umbraco.Core.Services; +using Umbraco.Core.Trees; using Umbraco.Web.Actions; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Search; -using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Models.Trees; +using Umbraco.Web.Search; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Umbraco.Web.Common.Authorization; -using Umbraco.Core.Trees; namespace Umbraco.Web.BackOffice.Trees { @@ -39,7 +38,7 @@ namespace Umbraco.Web.BackOffice.Trees _entityService = entityService; } - protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { var intId = id.TryConvertTo(); if (intId == false) throw new InvalidOperationException("Id must be an integer"); diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs index 817b32f301..5184325db8 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs @@ -2,9 +2,9 @@ using System.Linq; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Umbraco.Core; using Umbraco.Core.Services; -using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.Trees; @@ -38,9 +38,15 @@ namespace Umbraco.Web.BackOffice.Trees .Select(dt => CreateTreeNode(dt.Id.ToString(), id, queryStrings, dt.Name, Constants.Icons.MemberGroup, false)); } - protected override TreeNode CreateRootNode(FormCollection queryStrings) + protected override ActionResult CreateRootNode(FormCollection queryStrings) { - var root = base.CreateRootNode(queryStrings); + var rootResult = base.CreateRootNode(queryStrings); + if (!(rootResult.Result is null)) + { + return rootResult; + } + var root = rootResult.Value; + //check if there are any groups root.HasChildren = _memberGroupService.GetAll().Any(); return root; diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs index b480e36422..0a68c36e08 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs @@ -1,28 +1,24 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Security; using Umbraco.Core.Services; +using Umbraco.Core.Trees; using Umbraco.Extensions; using Umbraco.Web.Actions; -using Umbraco.Web.BackOffice.Filters; -using Umbraco.Web.BackOffice.Trees; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.ModelBinders; -using Umbraco.Web.Models.Trees; using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Models.Trees; using Umbraco.Web.Search; -using Constants = Umbraco.Core.Constants; -using Umbraco.Web.Security; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; -using Microsoft.AspNetCore.Authorization; -using Umbraco.Web.Common.Authorization; -using Umbraco.Core.Trees; namespace Umbraco.Web.BackOffice.Trees { @@ -101,7 +97,7 @@ namespace Umbraco.Web.BackOffice.Trees return node; } - protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { var nodes = new TreeNodeCollection(); diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberTypeAndGroupTreeControllerBase.cs b/src/Umbraco.Web.BackOffice/Trees/MemberTypeAndGroupTreeControllerBase.cs index b9f5ed7ede..0804e78c8a 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MemberTypeAndGroupTreeControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MemberTypeAndGroupTreeControllerBase.cs @@ -26,7 +26,7 @@ namespace Umbraco.Web.BackOffice.Trees MenuItemCollectionFactory = menuItemCollectionFactory; } - protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { var nodes = new TreeNodeCollection(); nodes.AddRange(GetTreeNodesFromService(id, queryStrings)); diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MemberTypeTreeController.cs index 8a8fe7b11e..5d44d7c832 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MemberTypeTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MemberTypeTreeController.cs @@ -2,12 +2,11 @@ using System.Linq; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core.Trees; -using Umbraco.Web.BackOffice.Filters; -using Umbraco.Web.BackOffice.Trees; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.ContentEditing; @@ -40,9 +39,15 @@ namespace Umbraco.Web.BackOffice.Trees } - protected override TreeNode CreateRootNode(FormCollection queryStrings) + protected override ActionResult CreateRootNode(FormCollection queryStrings) { - var root = base.CreateRootNode(queryStrings); + var rootResult = base.CreateRootNode(queryStrings); + if (!(rootResult.Result is null)) + { + return rootResult; + } + var root = rootResult.Value; + //check if there are any member types root.HasChildren = _memberTypeService.GetAll().Any(); return root; diff --git a/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs index 66c7e4a7d1..9b80782725 100644 --- a/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Umbraco.Core; using Umbraco.Core.Services; -using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.Trees; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; -using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Trees { @@ -34,9 +33,15 @@ namespace Umbraco.Web.BackOffice.Trees /// Helper method to create a root model for a tree /// /// - protected override TreeNode CreateRootNode(FormCollection queryStrings) + protected override ActionResult CreateRootNode(FormCollection queryStrings) { - var root = base.CreateRootNode(queryStrings); + var rootResult = base.CreateRootNode(queryStrings); + if (!(rootResult.Result is null)) + { + return rootResult; + } + var root = rootResult.Value; + //this will load in a custom UI instead of the dashboard for the root node root.RoutePath = $"{Constants.Applications.Packages}/{Constants.Trees.Packages}/repo"; @@ -47,7 +52,7 @@ namespace Umbraco.Web.BackOffice.Trees } - protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { //full screen app without tree nodes return TreeNodeCollection.Empty; diff --git a/src/Umbraco.Web.BackOffice/Trees/RelationTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/RelationTypeTreeController.cs index c394af26aa..2e200e8b0a 100644 --- a/src/Umbraco.Web.BackOffice/Trees/RelationTypeTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/RelationTypeTreeController.cs @@ -1,17 +1,16 @@ using System.Linq; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; -using Umbraco.Web.Models.Trees; +using Microsoft.AspNetCore.Mvc; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.Actions; -using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Authorization; +using Umbraco.Web.Models.Trees; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Umbraco.Web.Common.Authorization; namespace Umbraco.Web.BackOffice.Trees { @@ -61,7 +60,7 @@ namespace Umbraco.Web.BackOffice.Trees return menu; } - protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { var nodes = new TreeNodeCollection(); diff --git a/src/Umbraco.Web.BackOffice/Trees/TemplatesTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/TemplatesTreeController.cs index b3178f8657..a8ebc71581 100644 --- a/src/Umbraco.Web.BackOffice/Trees/TemplatesTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/TemplatesTreeController.cs @@ -11,7 +11,6 @@ using Umbraco.Core.Services; using Umbraco.Core.Trees; using Umbraco.Extensions; using Umbraco.Web.Actions; -using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.ContentEditing; @@ -19,7 +18,6 @@ using Umbraco.Web.Models.Trees; using Umbraco.Web.Search; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; -using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Trees { @@ -46,9 +44,15 @@ namespace Umbraco.Web.BackOffice.Trees _fileService = fileService; } - protected override TreeNode CreateRootNode(FormCollection queryStrings) + protected override ActionResult CreateRootNode(FormCollection queryStrings) { - var root = base.CreateRootNode(queryStrings); + var rootResult = base.CreateRootNode(queryStrings); + if (!(rootResult.Result is null)) + { + return rootResult; + } + var root = rootResult.Value; + //check if there are any templates root.HasChildren = _fileService.GetTemplates(-1).Any(); return root; @@ -65,7 +69,7 @@ namespace Umbraco.Web.BackOffice.Trees /// We are allowing an arbitrary number of query strings to be pased in so that developers are able to persist custom data from the front-end /// to the back end to be used in the query for model data. /// - protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { var nodes = new TreeNodeCollection(); diff --git a/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs b/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs index 3d9bf989b3..5c6f8a7fe8 100644 --- a/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs @@ -6,7 +6,6 @@ using Umbraco.Core; using Umbraco.Core.Events; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; -using Umbraco.Core.Persistence; using Umbraco.Core.Trees; using Umbraco.Extensions; using Umbraco.Web.BackOffice.Controllers; @@ -47,7 +46,7 @@ namespace Umbraco.Web.BackOffice.Trees /// We are allowing an arbitrary number of query strings to be passed in so that developers are able to persist custom data from the front-end /// to the back end to be used in the query for model data. /// - protected abstract TreeNodeCollection GetTreeNodes(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection queryStrings); + protected abstract ActionResult GetTreeNodes(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection queryStrings); /// /// Returns the menu structure for the node @@ -88,10 +87,16 @@ namespace Umbraco.Web.BackOffice.Trees /// /// /// - public TreeNode GetRootNode([ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection queryStrings) + public ActionResult GetRootNode([ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection queryStrings) { if (queryStrings == null) queryStrings = FormCollection.Empty; - var node = CreateRootNode(queryStrings); + var nodeResult = CreateRootNode(queryStrings); + if (!(nodeResult.Result is null)) + { + return nodeResult.Result; + } + + var node = nodeResult.Value; //add the tree alias to the root node.AdditionalData["treeAlias"] = TreeAlias; @@ -123,10 +128,17 @@ namespace Umbraco.Web.BackOffice.Trees /// We are allowing an arbitrary number of query strings to be passed in so that developers are able to persist custom data from the front-end /// to the back end to be used in the query for model data. /// - public TreeNodeCollection GetNodes(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection queryStrings) + public ActionResult GetNodes(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection queryStrings) { if (queryStrings == null) queryStrings = FormCollection.Empty; - var nodes = GetTreeNodes(id, queryStrings); + var nodesResult = GetTreeNodes(id, queryStrings); + + if (!(nodesResult.Result is null)) + { + return nodesResult.Result; + } + + var nodes = nodesResult.Value; foreach (var node in nodes) AddQueryStringsToAdditionalData(node, queryStrings); @@ -151,9 +163,15 @@ namespace Umbraco.Web.BackOffice.Trees public ActionResult GetMenu(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection queryStrings) { if (queryStrings == null) queryStrings = FormCollection.Empty; - var menu = GetMenuForNode(id, queryStrings); + var menuResult = GetMenuForNode(id, queryStrings); + if (!(menuResult.Result is null)) + { + return menuResult.Result; + } + + var menu = menuResult.Value; //raise the event - OnMenuRendering(this, new MenuRenderingEventArgs(id, menu.Value, queryStrings)); + OnMenuRendering(this, new MenuRenderingEventArgs(id, menu, queryStrings)); return menu; } @@ -161,7 +179,7 @@ namespace Umbraco.Web.BackOffice.Trees /// Helper method to create a root model for a tree /// /// - protected virtual TreeNode CreateRootNode(FormCollection queryStrings) + protected virtual ActionResult CreateRootNode(FormCollection queryStrings) { var rootNodeAsString = Constants.System.RootString; queryStrings.TryGetValue(TreeQueryStringParameters.Application, out var currApp); diff --git a/src/Umbraco.Web.BackOffice/Trees/UserTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/UserTreeController.cs index 45bb1cdf83..f43247c09c 100644 --- a/src/Umbraco.Web.BackOffice/Trees/UserTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/UserTreeController.cs @@ -1,13 +1,13 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Umbraco.Core; using Umbraco.Core.Services; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Authorization; using Umbraco.Web.Models.Trees; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; -using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.BackOffice.Trees { @@ -32,9 +32,14 @@ namespace Umbraco.Web.BackOffice.Trees /// Helper method to create a root model for a tree /// /// - protected override TreeNode CreateRootNode(FormCollection queryStrings) + protected override ActionResult CreateRootNode(FormCollection queryStrings) { - var root = base.CreateRootNode(queryStrings); + var rootResult = base.CreateRootNode(queryStrings); + if (!(rootResult.Result is null)) + { + return rootResult; + } + var root = rootResult.Value; //this will load in a custom UI instead of the dashboard for the root node root.RoutePath = $"{Constants.Applications.Users}/{Constants.Trees.Users}/users"; @@ -44,7 +49,7 @@ namespace Umbraco.Web.BackOffice.Trees return root; } - protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings) + protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { //full screen app without tree nodes return TreeNodeCollection.Empty; diff --git a/src/Umbraco.Web.Common/Exceptions/HttpResponseException.cs b/src/Umbraco.Web.Common/Exceptions/HttpResponseException.cs deleted file mode 100644 index cb14a5a546..0000000000 --- a/src/Umbraco.Web.Common/Exceptions/HttpResponseException.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Runtime.Serialization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Infrastructure; -using Umbraco.Web.Models.ContentEditing; - -namespace Umbraco.Web.Common.Exceptions -{ - [Serializable] - public class HttpResponseException : Exception - { - public HttpResponseException(HttpStatusCode status = HttpStatusCode.InternalServerError, object value = null) - { - Status = status; - Value = value; - } - - public HttpResponseException(ActionResult actionResult) - { - - Status = actionResult switch - { - IStatusCodeActionResult x => (HttpStatusCode)x.StatusCode.GetValueOrDefault((int)HttpStatusCode.InternalServerError), - _ => HttpStatusCode.InternalServerError - }; - - Value = actionResult switch - { - ObjectResult x => x.Value, - _ => null - }; - } - - public HttpStatusCode Status { get; set; } - public object Value { get; set; } - - public IDictionary AdditionalHeaders { get; } = new Dictionary(); - - - /// - /// When overridden in a derived class, sets the with information about the exception. - /// - /// The that holds the serialized object data about the exception being thrown. - /// The that contains contextual information about the source or destination. - /// info - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } - - info.AddValue(nameof(Status), Enum.GetName(typeof(HttpStatusCode), Status)); - info.AddValue(nameof(Value), Value); - info.AddValue(nameof(AdditionalHeaders), AdditionalHeaders); - - base.GetObjectData(info, context); - } - - public static HttpResponseException CreateValidationErrorResponse(object model) - { - return new HttpResponseException(HttpStatusCode.BadRequest, model) - { - AdditionalHeaders = - { - ["X-Status-Reason"] = "Validation failed" - } - }; - } - - public static HttpResponseException CreateNotificationValidationErrorResponse(string errorMessage) - { - var notificationModel = new SimpleNotificationModel - { - Message = errorMessage - }; - notificationModel.AddErrorNotification(errorMessage, string.Empty); - return CreateValidationErrorResponse(notificationModel); - } - - } -}