From 402958c591a721da75f8fec03e0d284fdcba763d Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 29 Jun 2020 20:45:51 +0200 Subject: [PATCH 1/7] Updated Cypress to latest + Created tests for creating member and member group --- .../integration/Members/memberGroups.js | 32 +++++++++++++++ .../cypress/integration/Members/members.js | 41 +++++++++++++++++++ .../Users/{userGroups.ts => userGroups.js} | 1 - src/Umbraco.Tests.AcceptanceTest/package.json | 4 +- 4 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/memberGroups.js create mode 100644 src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/members.js rename src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/{userGroups.ts => userGroups.js} (99%) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/memberGroups.js b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/memberGroups.js new file mode 100644 index 0000000000..be9b93134d --- /dev/null +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/memberGroups.js @@ -0,0 +1,32 @@ +context('User Groups', () => { + + beforeEach(() => { + cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); + }); + + it('Create member group', () => { + const name = "Test Group"; + + cy.umbracoEnsureMemberGroupNameNotExists(name); + + cy.umbracoSection('member'); + cy.get('li .umb-tree-root:contains("Members")').should("be.visible"); + + cy.umbracoTreeItem("member", ["Member Groups"]).rightclick(); + + cy.umbracoContextMenuAction("action-create").click(); + + //Type name + cy.umbracoEditorHeaderName(name); + + // Save + cy.get('.btn-success').click(); + + //Assert + cy.umbracoSuccessNotification().should('be.visible'); + + //Clean up + cy.umbracoEnsureMemberGroupNameNotExists(name); + }); + +}); diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/members.js b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/members.js new file mode 100644 index 0000000000..aeb4576ccd --- /dev/null +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/members.js @@ -0,0 +1,41 @@ +/// +context('Members', () => { + + beforeEach(() => { + cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); + }); + + it('Create member', () => { + const name = "Alice Bobson"; + const email = "alice-bobson@acceptancetest.umbraco"; + const password = "$AUlkoF*St0kgPiyyVEk5iU5JWdN*F7&@OSl5Y4pOofnidfifkBj5Ns2ONv%FzsTl36V1E924Gw97zcuSeT7UwK&qb5l&O9h!d!w"; + + cy.umbracoEnsureMemberEmailNotExists(email); + cy.umbracoSection('member'); + cy.get('li .umb-tree-root:contains("Members")').should("be.visible"); + + cy.umbracoTreeItem("member", ["Members"]).rightclick(); + + cy.umbracoContextMenuAction("action-create").click(); + cy.get('.menu-label').first().click(); + + //Type name + cy.umbracoEditorHeaderName(name); + + cy.get('input#_umb_login').clear().type(email); + cy.get('input#_umb_email').clear().type(email); + cy.get('input#password').clear().type(password); + cy.get('input#confirmPassword').clear().type(password); + + // Save + cy.get('.btn-success').click(); + + //Assert + cy.umbracoSuccessNotification().should('be.visible'); + + //Clean up + cy.umbracoEnsureMemberEmailNotExists(email); + + }); + +}); diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/userGroups.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/userGroups.js similarity index 99% rename from src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/userGroups.ts rename to src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/userGroups.js index 905d0fc25c..afef0e7701 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/userGroups.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Users/userGroups.js @@ -1,4 +1,3 @@ - context('User Groups', () => { beforeEach(() => { diff --git a/src/Umbraco.Tests.AcceptanceTest/package.json b/src/Umbraco.Tests.AcceptanceTest/package.json index ad125d090a..3b4177ce3f 100644 --- a/src/Umbraco.Tests.AcceptanceTest/package.json +++ b/src/Umbraco.Tests.AcceptanceTest/package.json @@ -6,8 +6,8 @@ "devDependencies": { "cross-env": "^7.0.2", "ncp": "^2.0.0", - "cypress": "^4.6.0", - "umbraco-cypress-testhelpers": "1.0.0-beta-39" + "cypress": "^4.9.0", + "umbraco-cypress-testhelpers": "1.0.0-beta-44" }, "dependencies": { "typescript": "^3.9.2" From b078f856b9f6c77dd0233f8d7d51bd02b5d7f6da Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 2 Jul 2020 15:36:15 +1000 Subject: [PATCH 2/7] Remove the usage of Parallel to run the populators --- src/Umbraco.Examine/IndexRebuilder.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Examine/IndexRebuilder.cs b/src/Umbraco.Examine/IndexRebuilder.cs index 786aecac71..02fb4fc2da 100644 --- a/src/Umbraco.Examine/IndexRebuilder.cs +++ b/src/Umbraco.Examine/IndexRebuilder.cs @@ -50,8 +50,11 @@ namespace Umbraco.Examine index.CreateIndex(); // clear the index } - //run the populators in parallel against all indexes - Parallel.ForEach(_populators, populator => populator.Populate(indexes)); + // run each populator over the indexes + foreach(var populator in _populators) + { + populator.Populate(indexes); + } } } From 8400b53f70d95f9065c14ca287eb2a27d9048095 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 6 Jul 2020 12:55:23 +0200 Subject: [PATCH 3/7] Cleanup based on review Signed-off-by: Bjarke Berg --- .../UmbracoServiceProviderFactory.cs | 1 - .../Media/UploadAutoFillProperties.cs | 8 +- .../UmbracoTestServerTestBase.cs | 9 + src/Umbraco.Tests/Umbraco.Tests.csproj | 1 - .../Web/Controllers/ContentControllerTests.cs | 518 ------------------ ...rmineAmbiguousActionByPassingParameters.cs | 6 + .../Controllers/MediaController.cs | 4 +- .../PrefixlessBodyModelValidatorAttribute.cs | 3 + .../ModelBinders/BlueprintItemBinder.cs | 6 +- .../Umbraco.Web.BackOffice.csproj | 1 - ...racoApiBehaviorApplicationModelProvider.cs | 1 - .../AngularJsonOnlyConfigurationAttribute.cs | 1 - 12 files changed, 27 insertions(+), 532 deletions(-) delete mode 100644 src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs diff --git a/src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs b/src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs index cc78577f7e..56a972a64b 100644 --- a/src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs +++ b/src/Umbraco.Infrastructure/Composing/UmbracoServiceProviderFactory.cs @@ -2,7 +2,6 @@ using LightInject.Microsoft.DependencyInjection; using Microsoft.Extensions.DependencyInjection; using System; -using System.Diagnostics; using Umbraco.Composing; using Umbraco.Core.Composing.LightInject; using Umbraco.Core.Configuration; diff --git a/src/Umbraco.Infrastructure/Media/UploadAutoFillProperties.cs b/src/Umbraco.Infrastructure/Media/UploadAutoFillProperties.cs index 6c338fc260..05d4744526 100644 --- a/src/Umbraco.Infrastructure/Media/UploadAutoFillProperties.cs +++ b/src/Umbraco.Infrastructure/Media/UploadAutoFillProperties.cs @@ -112,16 +112,16 @@ namespace Umbraco.Web.Media if (content == null) throw new ArgumentNullException(nameof(content)); if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig)); - if (!string.IsNullOrEmpty(autoFillConfig.WidthFieldAlias) && content.Properties.Contains(autoFillConfig.WidthFieldAlias)) + if (!string.IsNullOrWhiteSpace(autoFillConfig.WidthFieldAlias) && content.Properties.Contains(autoFillConfig.WidthFieldAlias)) content.Properties[autoFillConfig.WidthFieldAlias].SetValue(size.HasValue ? size.Value.Width.ToInvariantString() : string.Empty, culture, segment); - if (!string.IsNullOrEmpty(autoFillConfig.HeightFieldAlias) && content.Properties.Contains(autoFillConfig.HeightFieldAlias)) + if (!string.IsNullOrWhiteSpace(autoFillConfig.HeightFieldAlias) && content.Properties.Contains(autoFillConfig.HeightFieldAlias)) content.Properties[autoFillConfig.HeightFieldAlias].SetValue(size.HasValue ? size.Value.Height.ToInvariantString() : string.Empty, culture, segment); - if (!string.IsNullOrEmpty(autoFillConfig.LengthFieldAlias) && content.Properties.Contains(autoFillConfig.LengthFieldAlias)) + if (!string.IsNullOrWhiteSpace(autoFillConfig.LengthFieldAlias) && content.Properties.Contains(autoFillConfig.LengthFieldAlias)) content.Properties[autoFillConfig.LengthFieldAlias].SetValue(length, culture, segment); - if (!string.IsNullOrEmpty(autoFillConfig.ExtensionFieldAlias) && content.Properties.Contains(autoFillConfig.ExtensionFieldAlias)) + if (!string.IsNullOrWhiteSpace(autoFillConfig.ExtensionFieldAlias) && content.Properties.Contains(autoFillConfig.ExtensionFieldAlias)) content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(extension, culture, segment); } diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs index d366ea49ee..d5c2616c6c 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -32,7 +32,16 @@ namespace Umbraco.Tests.Integration.TestServerTest } + /// + /// Get the service from the underlying container that is also used by the . + /// protected T GetRequiredService() => Factory.Services.GetRequiredService(); + + /// + /// Prepare a url before using . + /// This returns the url but also sets the HttpContext.request into to use this url. + /// + /// The string URL of the controller action. protected string PrepareUrl(Expression> methodSelector) where T : UmbracoApiController { diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index a2ebdd5e66..d09c756cdd 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -252,7 +252,6 @@ - diff --git a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs deleted file mode 100644 index b92db6d4ee..0000000000 --- a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs +++ /dev/null @@ -1,518 +0,0 @@ -// using System; -// using System.Collections.Generic; -// using System.Globalization; -// using System.Linq; -// using System.Net; -// using System.Net.Http; -// using System.Net.Http.Headers; -// using System.Web.Http; -// using Moq; -// using Newtonsoft.Json; -// using Newtonsoft.Json.Linq; -// using NUnit.Framework; -// using Umbraco.Core; -// using Umbraco.Core.Cache; -// using Umbraco.Core.Configuration; -// using Umbraco.Core.Dictionary; -// using Umbraco.Core.Logging; -// using Umbraco.Core.Models; -// using Umbraco.Core.Models.Entities; -// using Umbraco.Core.Models.Membership; -// using Umbraco.Core.Persistence; -// using Umbraco.Core.PropertyEditors; -// using Umbraco.Core.Services; -// using Umbraco.Tests.TestHelpers; -// using Umbraco.Tests.TestHelpers.ControllerTesting; -// using Umbraco.Tests.TestHelpers.Entities; -// using Umbraco.Tests.Testing; -// using Umbraco.Web; -// using Umbraco.Web.Actions; -// using Umbraco.Web.Editors; -// using Umbraco.Web.Models.ContentEditing; -// using Umbraco.Web.PropertyEditors; -// using Umbraco.Web.Trees; -// using Umbraco.Web.WebApi; -// using Umbraco.Web.Composing; -// using Task = System.Threading.Tasks.Task; -// using Umbraco.Core.Mapping; -// using Umbraco.Web.Routing; -// -// namespace Umbraco.Tests.Web.Controllers -// { -// [TestFixture] -// [UmbracoTest(Database = UmbracoTestOptions.Database.None)] -// public class ContentControllerTests : TestWithDatabaseBase -// { -// private IContentType _contentTypeForMockedContent; -// -// public override void SetUp() -// { -// base.SetUp(); -// _contentTypeForMockedContent = null; -// } -// -// protected override void ComposeApplication(bool withApplication) -// { -// base.ComposeApplication(withApplication); -// -// //Replace with mockable services: -// -// var userServiceMock = new Mock(); -// userServiceMock.Setup(service => service.GetUserById(It.IsAny())) -// .Returns((int id) => id == 1234 ? new User(TestObjects.GetGlobalSettings(), 1234, "Test", "test@test.com", "test@test.com", "", null, new List(), new int[0], new int[0]) : null); -// userServiceMock.Setup(x => x.GetProfileById(It.IsAny())) -// .Returns((int id) => id == 1234 ? new User(TestObjects.GetGlobalSettings(), 1234, "Test", "test@test.com", "test@test.com", "", null, new List(), new int[0], new int[0]) : null); -// userServiceMock.Setup(service => service.GetPermissionsForPath(It.IsAny(), It.IsAny())) -// .Returns(new EntityPermissionSet(123, new EntityPermissionCollection(new[] -// { -// new EntityPermission(0, 123, new[] -// { -// ActionBrowse.ActionLetter.ToString(), -// ActionUpdate.ActionLetter.ToString(), -// ActionPublish.ActionLetter.ToString(), -// ActionNew.ActionLetter.ToString() -// }), -// }))); -// -// var entityService = new Mock(); -// entityService.Setup(x => x.GetAllPaths(UmbracoObjectTypes.Document, It.IsAny())) -// .Returns((UmbracoObjectTypes objType, int[] ids) => ids.Select(x => new TreeEntityPath { Path = $"-1,{x}", Id = x }).ToList()); -// entityService.Setup(x => x.GetKey(It.IsAny(), UmbracoObjectTypes.DataType)) -// .Returns((int id, UmbracoObjectTypes objType) => -// { -// switch (id) -// { -// case Constants.DataTypes.Textbox: -// return Attempt.Succeed(Constants.DataTypes.Guids.TextstringGuid); -// case Constants.DataTypes.RichtextEditor: -// return Attempt.Succeed(Constants.DataTypes.Guids.RichtextEditorGuid); -// } -// return Attempt.Fail(); -// }); -// -// -// var dataTypeService = new Mock(); -// dataTypeService.Setup(service => service.GetDataType(It.IsAny())) -// .Returns(Mock.Of(type => type.Id == 9876 && type.Name == "text")); -// dataTypeService.Setup(service => service.GetDataType(-87)) //the RTE -// .Returns(Mock.Of(type => type.Id == -87 && type.Name == "Rich text" && type.Configuration == new RichTextConfiguration())); -// -// var langService = new Mock(); -// langService.Setup(x => x.GetAllLanguages()).Returns(new[] { -// Mock.Of(x => x.IsoCode == "en-US"), -// Mock.Of(x => x.IsoCode == "es-ES"), -// Mock.Of(x => x.IsoCode == "fr-FR") -// }); -// -// var textService = new Mock(); -// textService.Setup(x => x.Localize(It.IsAny(), It.IsAny(), It.IsAny>())).Returns("text"); -// -// Composition.RegisterUnique(f => Mock.Of()); -// Composition.RegisterUnique(f => Mock.Of()); -// Composition.RegisterUnique(f => userServiceMock.Object); -// Composition.RegisterUnique(f => entityService.Object); -// Composition.RegisterUnique(f => dataTypeService.Object); -// Composition.RegisterUnique(f => langService.Object); -// Composition.RegisterUnique(f => textService.Object); -// Composition.RegisterUnique(f => Mock.Of()); -// Composition.RegisterUnique(f => new UmbracoApiControllerTypeCollection(new Type[] { })); -// } -// -// private MultipartFormDataContent GetMultiPartRequestContent(string json) -// { -// var multiPartBoundary = "----WebKitFormBoundary123456789"; -// return new MultipartFormDataContent(multiPartBoundary) -// { -// new StringContent(json) -// { -// Headers = -// { -// ContentDisposition = new ContentDispositionHeaderValue("form-data") -// { -// Name = "contentItem" -// } -// } -// } -// }; -// } -// -// private IContentType GetMockedContentType() -// { -// var contentType = MockedContentTypes.CreateSimpleContentType(); -// //ensure things have ids -// var ids = 888; -// foreach (var g in contentType.CompositionPropertyGroups) -// { -// g.Id = ids; -// ids++; -// } -// foreach (var p in contentType.CompositionPropertyGroups) -// { -// p.Id = ids; -// ids++; -// } -// -// return contentType; -// } -// -// private IContent GetMockedContent() -// { -// if (_contentTypeForMockedContent == null) -// { -// _contentTypeForMockedContent = GetMockedContentType(); -// Mock.Get(ServiceContext.ContentTypeService) -// .Setup(x => x.Get(_contentTypeForMockedContent.Id)) -// .Returns(_contentTypeForMockedContent); -// Mock.Get(ServiceContext.ContentTypeService) -// .As() -// .Setup(x => x.Get(_contentTypeForMockedContent.Id)) -// .Returns(_contentTypeForMockedContent); -// } -// -// var content = MockedContent.CreateSimpleContent(_contentTypeForMockedContent); -// content.Id = 123; -// content.Path = "-1,123"; -// return content; -// } -// -// private const string PublishJsonInvariant = @"{ -// ""id"": 123, -// ""contentTypeAlias"": ""page"", -// ""parentId"": -1, -// ""action"": ""save"", -// ""variants"": [ -// { -// ""name"": ""asdf"", -// ""properties"": [ -// { -// ""id"": 1, -// ""alias"": ""title"", -// ""value"": ""asdf"" -// } -// ], -// ""culture"": null, -// ""save"": true, -// ""publish"": true -// } -// ] -// }"; -// -// private const string PublishJsonVariant = @"{ -// ""id"": 123, -// ""contentTypeAlias"": ""page"", -// ""parentId"": -1, -// ""action"": ""save"", -// ""variants"": [ -// { -// ""name"": ""asdf"", -// ""properties"": [ -// { -// ""id"": 1, -// ""alias"": ""title"", -// ""value"": ""asdf"" -// } -// ], -// ""culture"": ""en-US"", -// ""save"": true, -// ""publish"": true -// }, -// { -// ""name"": ""asdf"", -// ""properties"": [ -// { -// ""id"": 1, -// ""alias"": ""title"", -// ""value"": ""asdf"" -// } -// ], -// ""culture"": ""fr-FR"", -// ""save"": true, -// ""publish"": true -// }, -// { -// ""name"": ""asdf"", -// ""properties"": [ -// { -// ""id"": 1, -// ""alias"": ""title"", -// ""value"": ""asdf"" -// } -// ], -// ""culture"": ""es-ES"", -// ""save"": true, -// ""publish"": true -// } -// ] -// }"; -// -// /// -// /// Returns 404 if the content wasn't found based on the ID specified -// /// -// /// -// [Test] -// public async Task PostSave_Validate_Existing_Content() -// { -// ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) -// { -// var contentServiceMock = Mock.Get(ServiceContext.ContentService); -// contentServiceMock.Setup(x => x.GetById(123)).Returns(() => null); //do not find it -// -// var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); -// -// var controller = new ContentController( -// Factory.GetInstance(), -// propertyEditorCollection, -// Factory.GetInstance(), -// umbracoContextAccessor, -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// ShortStringHelper, -// Factory.GetInstance(), -// Factory.GetInstance()); -// -// return controller; -// } -// -// var runner = new TestRunner(CtrlFactory); -// var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, -// content: GetMultiPartRequestContent(PublishJsonInvariant), -// mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), -// assertOkResponse: false); -// -// Assert.AreEqual(HttpStatusCode.NotFound, response.Item1.StatusCode); -// Assert.AreEqual(")]}',\n{\"Message\":\"content was not found\"}", response.Item1.Content.ReadAsStringAsync().Result); -// -// //var obj = JsonConvert.DeserializeObject>(response.Item2); -// //Assert.AreEqual(0, obj.TotalItems); -// } -// -// [Test] -// public async Task PostSave_Validate_At_Least_One_Variant_Flagged_For_Saving() -// { -// ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) -// { -// var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); -// var controller = new ContentController( -// Factory.GetInstance(), -// propertyEditorCollection, -// Factory.GetInstance(), -// umbracoContextAccessor, -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// ShortStringHelper, -// Factory.GetInstance(), -// Factory.GetInstance()); -// -// return controller; -// } -// -// var json = JsonConvert.DeserializeObject(PublishJsonInvariant); -// //remove all save flaggs -// ((JArray)json["variants"])[0]["save"] = false; -// -// var runner = new TestRunner(CtrlFactory); -// var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, -// content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), -// mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), -// assertOkResponse: false); -// -// Assert.AreEqual(HttpStatusCode.NotFound, response.Item1.StatusCode); -// Assert.AreEqual(")]}',\n{\"Message\":\"No variants flagged for saving\"}", response.Item1.Content.ReadAsStringAsync().Result); -// } -// -// /// -// /// Returns 404 if any of the posted properties dont actually exist -// /// -// /// -// [Test] -// public async Task PostSave_Validate_Properties_Exist() -// { -// ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) -// { -// var contentServiceMock = Mock.Get(ServiceContext.ContentService); -// contentServiceMock.Setup(x => x.GetById(123)).Returns(() => GetMockedContent()); -// -// var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); -// var controller = new ContentController( -// Factory.GetInstance(), -// propertyEditorCollection, -// Factory.GetInstance(), -// umbracoContextAccessor, -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// ShortStringHelper, -// Factory.GetInstance(), -// Factory.GetInstance()); -// -// return controller; -// } -// -// var json = JsonConvert.DeserializeObject(PublishJsonInvariant); -// //add a non-existent property to a variant being saved -// var variantProps = (JArray)json["variants"].ElementAt(0)["properties"]; -// variantProps.Add(JObject.FromObject(new -// { -// id = 2, -// alias = "doesntExist", -// value = "hello" -// })); -// -// var runner = new TestRunner(CtrlFactory); -// var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, -// content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), -// mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), -// assertOkResponse: false); -// -// Assert.AreEqual(HttpStatusCode.NotFound, response.Item1.StatusCode); -// } -// -// [Test] -// public async Task PostSave_Simple_Invariant() -// { -// var content = GetMockedContent(); -// -// ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) -// { -// var contentServiceMock = Mock.Get(ServiceContext.ContentService); -// contentServiceMock.Setup(x => x.GetById(123)).Returns(() => content); -// contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) -// .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success -// -// var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); -// var controller = new ContentController( -// Factory.GetInstance(), -// propertyEditorCollection, -// Factory.GetInstance(), -// umbracoContextAccessor, -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// ShortStringHelper, -// Factory.GetInstance(), -// Factory.GetInstance()); -// -// return controller; -// } -// -// var runner = new TestRunner(CtrlFactory); -// var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, -// content: GetMultiPartRequestContent(PublishJsonInvariant), -// mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), -// assertOkResponse: false); -// -// Assert.AreEqual(HttpStatusCode.OK, response.Item1.StatusCode); -// var display = JsonConvert.DeserializeObject(response.Item2); -// Assert.AreEqual(1, display.Variants.Count()); -// } -// -// [Test] -// public async Task PostSave_Validate_Empty_Name() -// { -// var content = GetMockedContent(); -// -// ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) -// { -// var contentServiceMock = Mock.Get(ServiceContext.ContentService); -// contentServiceMock.Setup(x => x.GetById(123)).Returns(() => content); -// contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) -// .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success -// -// var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); -// var controller = new ContentController( -// Factory.GetInstance(), -// propertyEditorCollection, -// Factory.GetInstance(), -// umbracoContextAccessor, -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// ShortStringHelper, -// Factory.GetInstance(), -// Factory.GetInstance() -// ); -// -// return controller; -// } -// -// //clear out the name -// var json = JsonConvert.DeserializeObject(PublishJsonInvariant); -// json["variants"].ElementAt(0)["name"] = null; -// -// var runner = new TestRunner(CtrlFactory); -// var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, -// content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), -// mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), -// assertOkResponse: false); -// -// Assert.AreEqual(HttpStatusCode.BadRequest, response.Item1.StatusCode); -// var display = JsonConvert.DeserializeObject(response.Item2); -// Assert.AreEqual(1, display.Errors.Count()); -// Assert.IsTrue(display.Errors.ContainsKey("Variants[0].Name")); -// //ModelState":{"Variants[0].Name":["Required"]} -// } -// -// [Test] -// public async Task PostSave_Validate_Variants_Empty_Name() -// { -// var content = GetMockedContent(); -// -// ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) -// { -// var contentServiceMock = Mock.Get(ServiceContext.ContentService); -// contentServiceMock.Setup(x => x.GetById(123)).Returns(() => content); -// contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) -// .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success -// -// var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); -// var controller = new ContentController( -// Factory.GetInstance(), -// propertyEditorCollection, -// Factory.GetInstance(), -// umbracoContextAccessor, -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// Factory.GetInstance(), -// ShortStringHelper, -// Factory.GetInstance(), -// Factory.GetInstance() -// ); -// -// return controller; -// } -// -// //clear out one of the names -// var json = JsonConvert.DeserializeObject(PublishJsonVariant); -// json["variants"].ElementAt(0)["name"] = null; -// -// var runner = new TestRunner(CtrlFactory); -// var response = await runner.Execute("Content", "PostSave", HttpMethod.Post, -// content: GetMultiPartRequestContent(JsonConvert.SerializeObject(json)), -// mediaTypeHeader: new MediaTypeWithQualityHeaderValue("multipart/form-data"), -// assertOkResponse: false); -// -// Assert.AreEqual(HttpStatusCode.BadRequest, response.Item1.StatusCode); -// var display = JsonConvert.DeserializeObject(response.Item2); -// Assert.AreEqual(2, display.Errors.Count()); -// Assert.IsTrue(display.Errors.ContainsKey("Variants[0].Name")); -// Assert.IsTrue(display.Errors.ContainsKey("_content_variant_en-US_null_")); -// } -// -// // TODO: There are SOOOOO many more tests we should write - a lot of them to do with validation -// -// } -// } diff --git a/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs b/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs index caa3b8348a..726d61fe80 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DetermineAmbiguousActionByPassingParameters.cs @@ -15,6 +15,12 @@ using Newtonsoft.Json.Linq; namespace Umbraco.Web.BackOffice.Controllers { + /// + /// This method determine ambiguous controller actions by making a tryparse of the string (from request) into the type of the argument. + /// + /// + /// By using this methods you are allowed to to have multiple controller actions named equally. E.g. GetById(Guid id), GetById(int id),... + /// public class DetermineAmbiguousActionByPassingParameters : ActionMethodSelectorAttribute { private string _requestBody = null; diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs index efe14a2c3c..21b403c9cd 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs @@ -215,7 +215,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// /// - //[FilterAllowedOutgoingMedia(typeof(IEnumerable))] // TODO introduce when moved to .NET Core + [FilterAllowedOutgoingMedia(typeof(IEnumerable))] public IEnumerable GetByIds([FromQuery]int[] ids) { var foundMedia = _mediaService.GetByIds(ids); @@ -259,7 +259,7 @@ namespace Umbraco.Web.BackOffice.Controllers /// /// Returns the root media objects /// - //[FilterAllowedOutgoingMedia(typeof(IEnumerable>))] // TODO introduce when moved to .NET Core + [FilterAllowedOutgoingMedia(typeof(IEnumerable>))] public IEnumerable> GetRootMedia() { // TODO: Add permissions check! diff --git a/src/Umbraco.Web.BackOffice/Filters/PrefixlessBodyModelValidatorAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/PrefixlessBodyModelValidatorAttribute.cs index a19655f8ce..36cc241976 100644 --- a/src/Umbraco.Web.BackOffice/Filters/PrefixlessBodyModelValidatorAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/PrefixlessBodyModelValidatorAttribute.cs @@ -6,6 +6,9 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; namespace Umbraco.Web.BackOffice.Filters { + /// + /// Applying this attribute to any controller will ensure that the parameter name (prefix) is not part of the validation error keys. + /// public class PrefixlessBodyModelValidatorAttribute : TypeFilterAttribute { public PrefixlessBodyModelValidatorAttribute() : base(typeof(PrefixlessBodyModelValidatorFilter)) diff --git a/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs b/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs index 0e9444ae3a..744d125bf9 100644 --- a/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs +++ b/src/Umbraco.Web.BackOffice/ModelBinders/BlueprintItemBinder.cs @@ -10,11 +10,11 @@ namespace Umbraco.Web.BackOffice.ModelBinders { internal class BlueprintItemBinder : ContentItemBinder { - private readonly ContentService _contentService; + private readonly IContentService _contentService; - public BlueprintItemBinder(IJsonSerializer jsonSerializer, UmbracoMapper umbracoMapper, IContentService contentService, IContentTypeService contentTypeService, IHostingEnvironment hostingEnvironment, ContentService contentService2) : base(jsonSerializer, umbracoMapper, contentService, contentTypeService, hostingEnvironment) + public BlueprintItemBinder(IJsonSerializer jsonSerializer, UmbracoMapper umbracoMapper, IContentService contentService, IContentTypeService contentTypeService, IHostingEnvironment hostingEnvironment) : base(jsonSerializer, umbracoMapper, contentService, contentTypeService, hostingEnvironment) { - _contentService = contentService2; + _contentService = contentService; } protected override IContent GetExisting(ContentItemSave model) diff --git a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj index eecac784c8..911a157223 100644 --- a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj +++ b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj @@ -14,7 +14,6 @@ - diff --git a/src/Umbraco.Web.Common/ApplicationModels/UmbracoApiBehaviorApplicationModelProvider.cs b/src/Umbraco.Web.Common/ApplicationModels/UmbracoApiBehaviorApplicationModelProvider.cs index ac23516b9b..e76ae1ff6b 100644 --- a/src/Umbraco.Web.Common/ApplicationModels/UmbracoApiBehaviorApplicationModelProvider.cs +++ b/src/Umbraco.Web.Common/ApplicationModels/UmbracoApiBehaviorApplicationModelProvider.cs @@ -36,7 +36,6 @@ namespace Umbraco.Web.Common.ApplicationModels ActionModelConventions = new List() { new ClientErrorResultFilterConvention(), // Ensures the responses without any body is converted into a simple json object with info instead of a string like "Status Code: 404; Not Found" - //new InvalidModelStateFilterConvention(), // automatically 400 responses if ModelState is invalid before hitting the controller new ConsumesConstraintForFormFileParameterConvention(), // If an controller accepts files, it must accept multipart/form-data. new InferParameterBindingInfoConvention(modelMetadataProvider), // no need for [FromBody] everywhere, A complex type parameter is assigned to FromBody diff --git a/src/Umbraco.Web.Common/Filters/AngularJsonOnlyConfigurationAttribute.cs b/src/Umbraco.Web.Common/Filters/AngularJsonOnlyConfigurationAttribute.cs index e185491f0e..85eb55b6d9 100644 --- a/src/Umbraco.Web.Common/Filters/AngularJsonOnlyConfigurationAttribute.cs +++ b/src/Umbraco.Web.Common/Filters/AngularJsonOnlyConfigurationAttribute.cs @@ -13,7 +13,6 @@ namespace Umbraco.Web.Common.Filters { public AngularJsonOnlyConfigurationAttribute() : base(typeof(AngularJsonOnlyConfigurationFilter)) { - Order = -2400; } private class AngularJsonOnlyConfigurationFilter : IResultFilter From a9c043c53749406ca7bc48cfb01f4059b08e9e6c Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 6 Jul 2020 12:58:11 +0200 Subject: [PATCH 4/7] Cleanup based on review Signed-off-by: Bjarke Berg --- src/Umbraco.Web.BackOffice/Controllers/ContentController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs index ccc33b1f91..157df177e9 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs @@ -1733,7 +1733,7 @@ namespace Umbraco.Web.Editors { try { - var uri = DomainUtilities.ParseUriFromDomainName(domain.Name, new Uri(Request.GetEncodedUrl(), UriKind.RelativeOrAbsolute)); + var uri = DomainUtilities.ParseUriFromDomainName(domain.Name, new Uri(Request.GetEncodedUrl())); } catch (UriFormatException) { From 7e90db5b304347635cfaef61cb6c72ed17e2bdfc Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 6 Jul 2020 13:01:25 +0200 Subject: [PATCH 5/7] Cleanup based on review Signed-off-by: Bjarke Berg --- src/Umbraco.Core/Composing/Current.cs | 2 +- .../TestServerTest/UmbracoTestServerTestBase.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Composing/Current.cs b/src/Umbraco.Core/Composing/Current.cs index 41e1ea2c21..055a29228a 100644 --- a/src/Umbraco.Core/Composing/Current.cs +++ b/src/Umbraco.Core/Composing/Current.cs @@ -24,7 +24,7 @@ namespace Umbraco.Composing public static IBackOfficeInfo BackOfficeInfo => EnsureInitialized(_backOfficeInfo); public static IProfiler Profiler => EnsureInitialized(_profiler); - public static bool IsInitialized { get; private set; } + public static bool IsInitialized { get; internal set; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static T EnsureInitialized(T returnValue) diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs index d5c2616c6c..66635c12ce 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -78,7 +78,7 @@ namespace Umbraco.Tests.Integration.TestServerTest if (Current.IsInitialized) { - typeof(Current).GetProperty(nameof(Current.IsInitialized), BindingFlags.Public | BindingFlags.Static).SetValue(null, false); + Current.IsInitialized = false; } } From 610bbee245e8e6acc6eb6d6e3a5ba7209e16fff3 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 7 Jul 2020 12:39:24 +0200 Subject: [PATCH 6/7] Added OverrideAuthorizationAttribute Signed-off-by: Bjarke Berg --- .../Controllers/ContentController.cs | 2 +- ...coBackOfficeServiceCollectionExtensions.cs | 3 ++ .../Filters/OverrideAuthorizationAttribute.cs | 24 +++++++++++++ .../OverrideAuthorizationFilterProvider.cs | 34 +++++++++++++++++++ .../Filters/UmbracoAuthorizeFilter.cs | 4 +-- src/Umbraco.Web/Umbraco.Web.csproj | 4 +-- 6 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationAttribute.cs create mode 100644 src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationFilterProvider.cs diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs index 157df177e9..1335ee049d 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs @@ -130,7 +130,7 @@ namespace Umbraco.Web.Editors /// /// [HttpGet] - [UmbracoAuthorize] + [UmbracoAuthorize, OverrideAuthorization] public bool AllowsCultureVariation() { var contentTypes = _contentTypeService.GetAll(); diff --git a/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs index acd468191a..2c7db69b84 100644 --- a/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Umbraco.Core; @@ -7,6 +8,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Security; using Umbraco.Core.Serialization; using Umbraco.Net; +using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.BackOffice.Security; using Umbraco.Web.Common.AspNetCore; using Umbraco.Web.Common.Security; @@ -26,6 +28,7 @@ namespace Umbraco.Extensions // TODO: We had this check in v8 where we don't enable these unless we can run... //if (runtimeState.Level != RuntimeLevel.Upgrade && runtimeState.Level != RuntimeLevel.Run) return app; + services.AddSingleton(); services .AddAuthentication(Constants.Security.BackOfficeAuthenticationType) .AddCookie(Constants.Security.BackOfficeAuthenticationType); diff --git a/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationAttribute.cs new file mode 100644 index 0000000000..ed05d831f4 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationAttribute.cs @@ -0,0 +1,24 @@ +using System; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Umbraco.Web.BackOffice.Filters +{ + public class OverrideAuthorizationAttribute : ActionFilterAttribute + { + /// + /// Ensures a special type of authorization filter is ignored. Defaults to . + /// + /// The type of authorication filter to override. if null then is used. + /// + /// https://stackoverflow.com/questions/33558095/overrideauthorizationattribute-in-asp-net-5 + /// + public OverrideAuthorizationAttribute(Type filtersToOverride = null) + { + FiltersToOverride = filtersToOverride ?? typeof(IAuthorizationFilter); + } + + public Type FiltersToOverride { get;} + + + } +} diff --git a/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationFilterProvider.cs b/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationFilterProvider.cs new file mode 100644 index 0000000000..6dbf6d747a --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationFilterProvider.cs @@ -0,0 +1,34 @@ +using System.Linq; +using Microsoft.AspNetCore.Mvc.Filters; +using Umbraco.Core; + +namespace Umbraco.Web.BackOffice.Filters +{ + public class OverrideAuthorizationFilterProvider : IFilterProvider, IFilterMetadata + { + public void OnProvidersExecuted(FilterProviderContext context) + { + + } + + public void OnProvidersExecuting(FilterProviderContext context) + { + if (context.ActionContext.ActionDescriptor.FilterDescriptors != null) + { + //Does the action have any UmbracoAuthorizeFilter? + var overrideFilters = context.Results.Where(filterItem => filterItem.Filter is OverrideAuthorizationAttribute).ToArray(); + foreach (var overrideFilter in overrideFilters) + { + context.Results.RemoveAll(filterItem => + //Remove any filter for the type indicated in the UmbracoAuthorizeFilter attribute + filterItem.Descriptor.Filter.GetType() == ((OverrideAuthorizationAttribute)overrideFilter.Filter).FiltersToOverride && + //Remove filters with lower scope (ie controller) than the override filter (ie action method) + filterItem.Descriptor.Scope < overrideFilter.Descriptor.Scope); + } + } + } + + //all framework providers have negative orders, so ours will come later + public int Order => 1; + } +} diff --git a/src/Umbraco.Web.BackOffice/Filters/UmbracoAuthorizeFilter.cs b/src/Umbraco.Web.BackOffice/Filters/UmbracoAuthorizeFilter.cs index 16d4b7ba33..e166a0e7e7 100644 --- a/src/Umbraco.Web.BackOffice/Filters/UmbracoAuthorizeFilter.cs +++ b/src/Umbraco.Web.BackOffice/Filters/UmbracoAuthorizeFilter.cs @@ -1,5 +1,4 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Routing; using System; @@ -46,6 +45,7 @@ namespace Umbraco.Web.BackOffice.Filters /// /// Default constructor /// + /// /// /// /// diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 95de02a38a..3f52c71366 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -159,6 +159,8 @@ + + @@ -314,9 +316,7 @@ - - From cc6c6c3d3e23ed824308deecc058e8e59fe84509 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 7 Jul 2020 12:49:22 +0200 Subject: [PATCH 7/7] Added todo message to PrefixlessBodyModelValidatorAttribute Signed-off-by: Bjarke Berg --- .../Filters/PrefixlessBodyModelValidatorAttribute.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Filters/PrefixlessBodyModelValidatorAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/PrefixlessBodyModelValidatorAttribute.cs index 36cc241976..840149ef14 100644 --- a/src/Umbraco.Web.BackOffice/Filters/PrefixlessBodyModelValidatorAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/PrefixlessBodyModelValidatorAttribute.cs @@ -1,8 +1,6 @@ -using System; using System.Linq; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.AspNetCore.Mvc.ModelBinding; namespace Umbraco.Web.BackOffice.Filters { @@ -11,6 +9,10 @@ namespace Umbraco.Web.BackOffice.Filters /// public class PrefixlessBodyModelValidatorAttribute : TypeFilterAttribute { + //TODO: Could be a better solution to replace the IModelValidatorProvider and ensure the errors are created + //without the prefix, instead of removing it afterwards. But I couldn't find any way to do this for only some + //of the controllers. IObjectModelValidator seems to be the interface to implement and replace in the container + //to handle it for the entire solution. public PrefixlessBodyModelValidatorAttribute() : base(typeof(PrefixlessBodyModelValidatorFilter)) { } @@ -26,8 +28,6 @@ namespace Umbraco.Web.BackOffice.Filters if (context.ModelState.IsValid) return; //Remove prefix from errors - - foreach (var modelStateItem in context.ModelState) { foreach (var prefix in context.ActionArguments.Keys)