From 016bed03ae9fb5a2658d6eefd2fcb46173344bbc Mon Sep 17 00:00:00 2001 From: elitsa Date: Fri, 11 Oct 2019 15:38:26 +0200 Subject: [PATCH 01/88] Deleting a language will set the selectedLanguage to the default language and update the mculture attribute in the url. --- .../src/navigation.controller.js | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/navigation.controller.js index e4c94f3c66..b585d22e9f 100644 --- a/src/Umbraco.Web.UI.Client/src/navigation.controller.js +++ b/src/Umbraco.Web.UI.Client/src/navigation.controller.js @@ -212,6 +212,22 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar evts.push(eventsService.on("editors.languages.languageDeleted", function (e, args) { loadLanguages().then(function (languages) { $scope.languages = languages; + const defaultCulture = $scope.languages[0].culture; + + if (args.language.culture === $scope.selectedLanguage.culture) { + $scope.selectedLanguage = defaultCulture; + + if ($scope.languages.length > 1) { + $location.search("mculture", defaultCulture); + } else { + $location.search("mculture", null); + } + + var currentEditorState = editorState.getCurrent(); + if (currentEditorState && currentEditorState.path) { + $scope.treeApi.syncTree({ path: currentEditorState.path, activate: true }); + } + } }); })); @@ -255,7 +271,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar evts.push(eventsService.on("treeService.removeNode", function (e, args) { //check to see if the current page has been removed - var currentEditorState = editorState.getCurrent() + var currentEditorState = editorState.getCurrent(); if (currentEditorState && currentEditorState.id.toString() === args.node.id.toString()) { //current page is loaded, so navigate to root var section = appState.getSectionState("currentSection"); @@ -279,6 +295,9 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar //select the current language if set in the query string if (mainCulture && $scope.languages && $scope.languages.length > 1) { var found = _.find($scope.languages, function (l) { + if (mainCulture === true) { + return false; + } return l.culture.toLowerCase() === mainCulture.toLowerCase(); }); if (found) { @@ -348,7 +367,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar return contentResource.allowsCultureVariation().then(function (b) { if (b === true) { - return languageResource.getAll() + return languageResource.getAll(); } else { return $q.when([]); //resolve an empty collection } From 175253a1a62bf86ca76b37afdc9a9b549cc3fa7e Mon Sep 17 00:00:00 2001 From: elitsa Date: Fri, 11 Oct 2019 15:40:55 +0200 Subject: [PATCH 02/88] Clear Cache and remove mculture from the url if the last non-default language is deleted --- .../src/common/services/navigation.service.js | 4 +++- src/Umbraco.Web/Cache/LanguageCacheRefresher.cs | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js index 1c28654a1a..ca89f7824b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js @@ -225,8 +225,10 @@ function navigationService($routeParams, $location, $q, $injector, eventsService retainQueryStrings: function (currRouteParams, nextRouteParams) { var toRetain = angular.copy(nextRouteParams); var updated = false; + _.each(retainedQueryStrings, function (r) { - if (currRouteParams[r] && !nextRouteParams[r]) { + // if mculture is set to null in nextRouteParams, the value will be undefined and we will not retain any query string that has a value of "null" + if (currRouteParams[r] && nextRouteParams[r] !== undefined && !nextRouteParams[r]) { toRetain[r] = currRouteParams[r]; updated = true; } diff --git a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs b/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs index 9093124609..b3df913ad6 100644 --- a/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/LanguageCacheRefresher.cs @@ -45,6 +45,8 @@ namespace Umbraco.Web.Cache ClearAllIsolatedCacheByEntityType(); //if a language is removed, then all dictionary cache needs to be removed ClearAllIsolatedCacheByEntityType(); + // clear since each IContent contains a reference to a string culture + ClearAllIsolatedCacheByEntityType(); RefreshDomains(id); base.Remove(id); } From 913db213503021f74578333bc6f8ab53412812d8 Mon Sep 17 00:00:00 2001 From: elitsa Date: Fri, 11 Oct 2019 15:41:16 +0200 Subject: [PATCH 03/88] Added missing ; --- .../src/views/languages/overview.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js index d1b984ed5b..90d418cf77 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/languages/overview.controller.js @@ -85,7 +85,7 @@ overlayService.open(dialog); }); - event.preventDefault() + event.preventDefault(); event.stopPropagation(); } From 29de4fd63f19b2634a9257f758d3e8627bb56b31 Mon Sep 17 00:00:00 2001 From: Emma Burstow Date: Tue, 15 Oct 2019 11:50:26 +0100 Subject: [PATCH 04/88] Everything was squished but now it is not. --- src/Umbraco.Web.UI.Client/src/less/main.less | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/main.less b/src/Umbraco.Web.UI.Client/src/less/main.less index 920fcdb1eb..e47566ae31 100644 --- a/src/Umbraco.Web.UI.Client/src/less/main.less +++ b/src/Umbraco.Web.UI.Client/src/less/main.less @@ -116,8 +116,9 @@ h5.-black { margin: 20px; } .umb-control-group { - border-bottom: 1px solid @gray-11; - padding-bottom: 20px; + border-bottom: 1px solid @gray-11; + padding-bottom: 20px; + padding-top: 20px; } .umb-control-group.-no-border { From 1b8f9e91dfc4a094bfcde43d61c39529eb874b7a Mon Sep 17 00:00:00 2001 From: Lars-Erik Aabech Date: Wed, 24 Apr 2019 22:12:17 +0200 Subject: [PATCH 05/88] TestOptionAttibute seam to discover testassemblies --- .../Testing/TestOptionAttributeBase.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Tests/Testing/TestOptionAttributeBase.cs b/src/Umbraco.Tests/Testing/TestOptionAttributeBase.cs index 9f72a022f3..a46d29180f 100644 --- a/src/Umbraco.Tests/Testing/TestOptionAttributeBase.cs +++ b/src/Umbraco.Tests/Testing/TestOptionAttributeBase.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; using NUnit.Framework; @@ -8,6 +9,8 @@ namespace Umbraco.Tests.Testing { public abstract class TestOptionAttributeBase : Attribute { + public static readonly List ScanAssemblies = new List(); + public static TOptions GetTestOptions(MethodInfo method) where TOptions : TestOptionAttributeBase, new() { @@ -30,7 +33,15 @@ namespace Umbraco.Tests.Testing var methodName = test.MethodName; var type = Type.GetType(typeName, true); if (type == null) - throw new PanicException($"Could not resolve the type from type name {typeName}"); // makes no sense + { + type = ScanAssemblies + .Select(x => Type.GetType(typeName + ", " + x, false)) + .FirstOrDefault(x => x != null); + if (type == null) + { + throw new PanicException($"Could not resolve the type from type name {typeName}"); // makes no sense + } + } var methodInfo = type.GetMethod(methodName); // what about overloads? var options = GetTestOptions(methodInfo); return options; From c56f6c161ecf6fe55a06952ab7a344879ee90d3a Mon Sep 17 00:00:00 2001 From: Lars-Erik Aabech Date: Tue, 15 Oct 2019 23:57:54 +0200 Subject: [PATCH 06/88] Don't throw when type not found by TestOptionAttribute --- src/Umbraco.Tests/Testing/TestOptionAttributeBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Tests/Testing/TestOptionAttributeBase.cs b/src/Umbraco.Tests/Testing/TestOptionAttributeBase.cs index a46d29180f..dd92edd7e7 100644 --- a/src/Umbraco.Tests/Testing/TestOptionAttributeBase.cs +++ b/src/Umbraco.Tests/Testing/TestOptionAttributeBase.cs @@ -31,7 +31,7 @@ namespace Umbraco.Tests.Testing var test = TestContext.CurrentContext.Test; var typeName = test.ClassName; var methodName = test.MethodName; - var type = Type.GetType(typeName, true); + var type = Type.GetType(typeName, false); if (type == null) { type = ScanAssemblies From 7af15b3a28a01b3141280b8a9dde545bdf188edf Mon Sep 17 00:00:00 2001 From: Jeavon Date: Thu, 17 Oct 2019 17:26:32 +0100 Subject: [PATCH 07/88] Add additional null conditionals as this method is sometimes called for front end rendering where this is no current user (DTGE) --- src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 3eed40c8bf..5743e9c1d5 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -114,7 +114,7 @@ namespace Umbraco.Web.PropertyEditors if (editorValue.Value == null) return null; - var userId = _umbracoContextAccessor.UmbracoContext?.Security.CurrentUser.Id ?? Constants.Security.SuperUserId; + var userId = _umbracoContextAccessor.UmbracoContext?.Security?.CurrentUser?.Id ?? Constants.Security.SuperUserId; var config = editorValue.DataTypeConfiguration as RichTextConfiguration; var mediaParent = config?.MediaParentId; From c131759688cc4bc0d240db3fa6647eabb520ccd9 Mon Sep 17 00:00:00 2001 From: Dennis Adolfi Date: Fri, 18 Oct 2019 14:18:19 +0200 Subject: [PATCH 08/88] * Removed service locator resolvning UmbracoMapper (Current.Mapper) in the UmbracoApiControllerBase constructor. * Added Obsolete marker to old UmbracoApiController constructor injecting Current.Mapper for backward compability, use new full construcor injection instead. --- src/Umbraco.Web/WebApi/UmbracoApiController.cs | 12 ++++++++++-- src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs | 9 ++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web/WebApi/UmbracoApiController.cs b/src/Umbraco.Web/WebApi/UmbracoApiController.cs index 8a2e54ca02..60b225ae96 100644 --- a/src/Umbraco.Web/WebApi/UmbracoApiController.cs +++ b/src/Umbraco.Web/WebApi/UmbracoApiController.cs @@ -1,8 +1,10 @@ -using Umbraco.Core; +using System; +using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; +using Umbraco.Core.Mapping; using Umbraco.Core.Persistence; using Umbraco.Core.Services; @@ -17,8 +19,14 @@ namespace Umbraco.Web.WebApi { } + [Obsolete("This constructor uses the service locator to fetch the UmbracoMapper from Current.Mapper, which is not good for testability. Inject the UmbracoMapper using full constructor injection instead.")] protected UmbracoApiController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, Current.Mapper) + { + } + + protected UmbracoApiController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, UmbracoMapper umbracoMapper) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, umbracoMapper) { } } diff --git a/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs b/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs index 89a630fb5d..874c1c8a4f 100644 --- a/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs +++ b/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs @@ -40,14 +40,15 @@ namespace Umbraco.Web.WebApi Current.Factory.GetInstance(), Current.Factory.GetInstance(), Current.Factory.GetInstance(), - Current.Factory.GetInstance() + Current.Factory.GetInstance(), + Current.Factory.GetInstance() ) { } /// /// Initializes a new instance of the class with all its dependencies. /// - protected UmbracoApiControllerBase(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) + protected UmbracoApiControllerBase(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, UmbracoMapper umbracoMapper) { UmbracoContextAccessor = umbracoContextAccessor; GlobalSettings = globalSettings; @@ -57,9 +58,7 @@ namespace Umbraco.Web.WebApi Logger = logger; RuntimeState = runtimeState; Umbraco = umbracoHelper; - - // fixme - can we break all ctors? - Mapper = Current.Mapper; + Mapper = umbracoMapper; } /// From 8d70d53ed6462dc73368be714ab8d83d69e03f41 Mon Sep 17 00:00:00 2001 From: Dennis Adolfi Date: Fri, 18 Oct 2019 14:27:55 +0200 Subject: [PATCH 09/88] * Added test for mocking UmbracoApiController dependencies with injected UmbracoMapper * Added test for mocking UmbracoApiController dependencies with ServiceLocator resolved UmbracoMapper for backward compability. --- .../Testing/TestingTests/MockTests.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 28fecd6b2b..042ca7dd62 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -7,10 +7,13 @@ using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Dictionary; using Umbraco.Core.Logging; +using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; @@ -19,6 +22,7 @@ using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.Security; +using Umbraco.Web.WebApi; using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Tests.Testing.TestingTests @@ -88,5 +92,41 @@ namespace Umbraco.Tests.Testing.TestingTests Assert.AreEqual("/hello/world/1234", theUrlProvider.GetUrl(publishedContent)); } + + [Test] + public void Can_Mock_UmbracoApiController_Dependencies_With_Injected_UmbracoMapper() + { + var umbracoContext = TestObjects.GetUmbracoContextMock(); + + var membershipHelper = new MembershipHelper(umbracoContext.HttpContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of()); + var umbracoHelper = new UmbracoHelper(Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), membershipHelper); + var umbracoMapper = new UmbracoMapper(new MapDefinitionCollection(new[] { Mock.Of() })); + + // ReSharper disable once UnusedVariable + var umbracoApiController = new FakeUmbracoApiController(Mock.Of(), Mock.Of(), Mock.Of(), ServiceContext.CreatePartial(), AppCaches.NoCache, Mock.Of(), Mock.Of(), umbracoHelper, umbracoMapper); + + Assert.Pass(); + } + + [Test] + public void Can_Mock_UmbracoApiController_Dependencies_With_ServiceLocator_UmbracoMapper() + { + var umbracoContext = TestObjects.GetUmbracoContextMock(); + + var membershipHelper = new MembershipHelper(umbracoContext.HttpContext, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of()); + var umbracoHelper = new UmbracoHelper(Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), membershipHelper); + Composition.Register(new UmbracoMapper(new MapDefinitionCollection(new[] { Mock.Of() }))); + + // ReSharper disable once UnusedVariable + var umbracoApiController = new FakeUmbracoApiController(Mock.Of(), Mock.Of(), Mock.Of(), ServiceContext.CreatePartial(), AppCaches.NoCache, Mock.Of(), Mock.Of(), umbracoHelper); + + Assert.Pass(); + } + } + + internal class FakeUmbracoApiController : UmbracoApiController + { + public FakeUmbracoApiController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { } + public FakeUmbracoApiController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, UmbracoMapper umbracoMapper) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, umbracoMapper) { } } } From 5c670b620cc08cdf50e1452ee3578249becc0fc4 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 20 Oct 2019 11:08:29 +0200 Subject: [PATCH 10/88] Allow tabbing through the section tray and adjust styles accordingly --- src/Umbraco.Web.UI.Client/src/less/sections.less | 8 ++++++++ .../src/views/components/application/umb-sections.html | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/sections.less b/src/Umbraco.Web.UI.Client/src/less/sections.less index 5551ba6376..40921c5b76 100644 --- a/src/Umbraco.Web.UI.Client/src/less/sections.less +++ b/src/Umbraco.Web.UI.Client/src/less/sections.less @@ -118,6 +118,7 @@ ul.sections-tray { text-decoration: none; display: block; position: relative; + outline: none; &::after { content: ""; @@ -131,6 +132,13 @@ ul.sections-tray { top: 0; left: 0; } + + &:focus .section__name { + .tabbing-active & { + border: 1px solid; + border-color: @gray-9; + } + } } } } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-sections.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-sections.html index 4defc3fdb0..d55542ec8e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-sections.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-sections.html @@ -12,9 +12,11 @@
  • - + + + -