diff --git a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj index e14abad8a1..1fbbd8c42f 100644 --- a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj +++ b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj @@ -14,7 +14,7 @@ buildTransitive - + @@ -63,17 +63,5 @@ A fix was put in place in Web SDK to update for wwwwroot in case someone runs npm build etc in a target, we're borrowing their trick. https://github.com/dotnet/sdk/blob/e2b2b1a4ac56c955b84d62fe71cda3b6f258b42b/src/WebSdk/Publish/Targets/ComputeTargets/Microsoft.NET.Sdk.Publish.ComputeFiles.targets --> - - - <_UmbracoFolderFiles Include="umbraco\config\**" /> - <_UmbracoFolderFiles Include="umbraco\PartialViewMacros\**" /> - <_UmbracoFolderFiles Include="umbraco\UmbracoBackOffice\**" /> - <_UmbracoFolderFiles Include="umbraco\UmbracoInstall\**" /> - <_UmbracoFolderFiles Include="umbraco\UmbracoWebsite\**" /> - <_UmbracoFolderFiles Include="umbraco\UmbracoWebsite\**" /> - <_UmbracoFolderFiles Include="umbraco\Licenses\**" /> - - - diff --git a/src/Umbraco.Cms/buildTransitive/Umbraco.Cms.targets b/src/Umbraco.Cms/buildTransitive/Umbraco.Cms.targets index 9544d89573..bd0fdbf304 100644 --- a/src/Umbraco.Cms/buildTransitive/Umbraco.Cms.targets +++ b/src/Umbraco.Cms/buildTransitive/Umbraco.Cms.targets @@ -13,17 +13,29 @@ DestinationFolder="$(MSBuildProjectDirectory)" SkipUnchangedFiles="true" /> - + - <_AppPluginsFiles Include="App_Plugins\**" /> + <_AppPluginsFiles Include="App_Plugins\**" /> - + + + + + + + <_UmbracoFolderFiles Include="umbraco\config\**" /> + <_UmbracoFolderFiles Include="umbraco\PartialViewMacros\**" /> + <_UmbracoFolderFiles Include="umbraco\UmbracoBackOffice\**" /> + <_UmbracoFolderFiles Include="umbraco\UmbracoInstall\**" /> + <_UmbracoFolderFiles Include="umbraco\UmbracoWebsite\**" /> + <_UmbracoFolderFiles Include="umbraco\Licenses\**" /> + diff --git a/src/Umbraco.Core/Security/IBackofficeSecurity.cs b/src/Umbraco.Core/Security/IBackofficeSecurity.cs index 3b3c956cd6..12b29a0288 100644 --- a/src/Umbraco.Core/Security/IBackofficeSecurity.cs +++ b/src/Umbraco.Core/Security/IBackofficeSecurity.cs @@ -21,7 +21,7 @@ namespace Umbraco.Cms.Core.Security /// The current user's Id that has been authenticated for the request. /// If authentication hasn't taken place this will be unsuccessful. // TODO: This should just be an extension method on ClaimsIdentity - Attempt GetUserId(); + Attempt GetUserId(); /// /// Checks if the specified user as access to the app diff --git a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs index f0bca5f1ea..c870f2b7c6 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs @@ -104,7 +104,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging contentTypeService, contentService, propertyEditors, - scopeProvider, + (Umbraco.Cms.Infrastructure.Scoping.IScopeProvider) scopeProvider, shortStringHelper, serializer, mediaService, diff --git a/src/Umbraco.Infrastructure/Scoping/LegacyIScopeProvider.cs b/src/Umbraco.Infrastructure/Scoping/LegacyIScopeProvider.cs index 371446e10c..b5dc069592 100644 --- a/src/Umbraco.Infrastructure/Scoping/LegacyIScopeProvider.cs +++ b/src/Umbraco.Infrastructure/Scoping/LegacyIScopeProvider.cs @@ -1,9 +1,90 @@ -using System; +using System.Data; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Infrastructure.Persistence; // ReSharper disable once CheckNamespace namespace Umbraco.Cms.Core.Scoping; [Obsolete("Please use Umbraco.Cms.Infrastructure.Scoping.IScopeProvider or Umbraco.Cms.Core.Scoping.ICoreScopeProvider instead.")] -public interface IScopeProvider : Infrastructure.Scoping.IScopeProvider +public interface IScopeProvider { + /// + /// Creates an ambient scope. + /// + /// The transaction isolation level. + /// The repositories cache mode. + /// An optional events dispatcher. + /// An optional notification publisher. + /// A value indicating whether to scope the filesystems. + /// A value indicating whether this scope should always be registered in the call context. + /// A value indicating whether this scope is auto-completed. + /// The created ambient scope. + /// + /// The created scope becomes the ambient scope. + /// If an ambient scope already exists, it becomes the parent of the created scope. + /// When the created scope is disposed, the parent scope becomes the ambient scope again. + /// Parameters must be specified on the outermost scope, or must be compatible with the parents. + /// Auto-completed scopes should be used for read-only operations ONLY. Do not use them if you do not + /// understand the associated issues, such as the scope being completed even though an exception is thrown. + /// + IScope CreateScope( + IsolationLevel isolationLevel = IsolationLevel.Unspecified, + RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, + IEventDispatcher? eventDispatcher = null, + IScopedNotificationPublisher? scopedNotificationPublisher = null, + bool? scopeFileSystems = null, + bool callContext = false, + bool autoComplete = false); + + /// + /// Creates a detached scope. + /// + /// A detached scope. + /// The transaction isolation level. + /// The repositories cache mode. + /// An optional events dispatcher. + /// An option notification publisher. + /// A value indicating whether to scope the filesystems. + /// + /// A detached scope is not ambient and has no parent. + /// It is meant to be attached by . + /// + /// + /// This is not used by CMS but is used by Umbraco Deploy. + /// + IScope CreateDetachedScope( + IsolationLevel isolationLevel = IsolationLevel.Unspecified, + RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, + IEventDispatcher? eventDispatcher = null, + IScopedNotificationPublisher? scopedNotificationPublisher = null, + bool? scopeFileSystems = null); + + /// + /// Attaches a scope. + /// + /// The scope to attach. + /// A value indicating whether to force usage of call context. + /// + /// Only a scope created by can be attached. + /// + void AttachScope(IScope scope, bool callContext = false); + + /// + /// Detaches a scope. + /// + /// The detached scope. + /// + /// Only a scope previously attached by can be detached. + /// + IScope DetachScope(); + + /// + /// Gets the scope context. + /// + IScopeContext? Context { get; } + + /// + /// Gets the sql context. + /// + ISqlContext SqlContext { get; } } diff --git a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs index 6fbfc170b0..4022f366e2 100644 --- a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs @@ -658,6 +658,42 @@ namespace Umbraco.Cms.Infrastructure.Scoping } } #endif + /// + Cms.Core.Scoping.IScope Cms.Core.Scoping.IScopeProvider.CreateScope( + IsolationLevel isolationLevel = IsolationLevel.Unspecified, + RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, + IEventDispatcher? eventDispatcher = null, + IScopedNotificationPublisher? notificationPublisher = null, + bool? scopeFileSystems = null, + bool callContext = false, + bool autoComplete = false) => + (Cms.Core.Scoping.IScope) CreateScope( + isolationLevel, + repositoryCacheMode, + eventDispatcher, + notificationPublisher, + scopeFileSystems, + callContext, + autoComplete); + + /// + Core.Scoping.IScope Core.Scoping.IScopeProvider.CreateDetachedScope(IsolationLevel isolationLevel, + RepositoryCacheMode repositoryCacheMode, IEventDispatcher? eventDispatcher, + IScopedNotificationPublisher? scopedNotificationPublisher, bool? scopeFileSystems) => + (Core.Scoping.IScope)CreateDetachedScope( + isolationLevel, + repositoryCacheMode, + eventDispatcher, + scopedNotificationPublisher, + scopeFileSystems); + + /// + void Core.Scoping.IScopeProvider.AttachScope(Core.Scoping.IScope scope, bool callContext) => + AttachScope(scope, callContext); + + /// + Core.Scoping.IScope Core.Scoping.IScopeProvider.DetachScope() => + (Core.Scoping.IScope)DetachScope(); } #if DEBUG_SCOPES diff --git a/src/Umbraco.Infrastructure/TypeForwards.cs b/src/Umbraco.Infrastructure/TypeForwards.cs new file mode 100644 index 0000000000..98c5cd7688 --- /dev/null +++ b/src/Umbraco.Infrastructure/TypeForwards.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly:TypeForwardedTo(typeof(Umbraco.Cms.Core.PropertyEditors.ConfigurationEditor<>))] diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs index fcf76f4e9c..f15adfd28b 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs @@ -130,7 +130,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] // Needed to enforce the principle set on the request, if one exists. public IDictionary GetPasswordConfig(int userId) { - Attempt currentUserId = _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId() ?? Attempt.Fail(); + Attempt currentUserId = _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId() ?? Attempt.Fail(); return _passwordConfiguration.GetConfiguration( currentUserId.Success ? currentUserId.Result != userId diff --git a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs index 0084c0680d..5c75534a8b 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs @@ -311,7 +311,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [ValidateAngularAntiForgeryToken] public async Task> GetCurrentUserLinkedLogins() { - var identityUser = await _backOfficeUserManager.FindByIdAsync(_backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().ResultOr(0)?.ToString(CultureInfo.InvariantCulture)); + var identityUser = await _backOfficeUserManager.FindByIdAsync(_backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().ResultOr(0).ToString(CultureInfo.InvariantCulture)); // deduplicate in case there are duplicates (there shouldn't be now since we have a unique constraint on the external logins // but there didn't used to be) diff --git a/src/Umbraco.Web.BackOffice/Controllers/TwoFactorLoginController.cs b/src/Umbraco.Web.BackOffice/Controllers/TwoFactorLoginController.cs index 627f1ec5c7..9429423755 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/TwoFactorLoginController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/TwoFactorLoginController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Runtime.Serialization; using System.Threading.Tasks; @@ -68,7 +69,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [HttpGet] public async Task>> Get2FAProvidersForUser(int userId) { - var user = await _backOfficeUserManager.FindByIdAsync(userId.ToString()); + var user = await _backOfficeUserManager.FindByIdAsync(userId.ToString(CultureInfo.InvariantCulture)); var enabledProviderNameHashSet = new HashSet(await _twoFactorLoginService.GetEnabledTwoFactorProviderNamesAsync(user.Key)); diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs index 22a0a47fc5..734f1d6062 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs @@ -742,8 +742,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [Authorize(Policy = AuthorizationPolicies.AdminUserEditsRequireAdmin)] public IActionResult PostDisableUsers([FromQuery]int[] userIds) { - var tryGetCurrentUserId = _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId() ?? Attempt.Fail(); - if (tryGetCurrentUserId.Success && userIds.Contains(tryGetCurrentUserId.Result!.Value)) + var tryGetCurrentUserId = _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId() ?? Attempt.Fail(); + if (tryGetCurrentUserId.Success && userIds.Contains(tryGetCurrentUserId.Result)) { return ValidationProblem("The current user cannot disable itself"); } diff --git a/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs b/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs index e1fa57557b..f772ad583c 100644 --- a/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs +++ b/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs @@ -36,10 +36,10 @@ public class BackOfficeSecurity : IBackOfficeSecurity // Check again if (_currentUser == null) { - Attempt id = GetUserId(); - if (id.Success && id.Result is not null) + Attempt id = GetUserId(); + if (id.Success) { - _currentUser = id.Success ? _userService.GetUserById(id.Result.Value) : null; + _currentUser = id.Success ? _userService.GetUserById(id.Result) : null; } } } @@ -50,10 +50,12 @@ public class BackOfficeSecurity : IBackOfficeSecurity } /// - public Attempt GetUserId() + public Attempt GetUserId() { ClaimsIdentity? identity = _httpContextAccessor.HttpContext?.GetCurrentIdentity(); - return identity == null ? Attempt.Fail() : Attempt.Succeed(identity.GetId()); + + var id = identity?.GetId(); + return id.HasValue is false ? Attempt.Fail() : Attempt.Succeed(id.Value); } /// diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/LegacyScopeProviderTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/LegacyScopeProviderTests.cs new file mode 100644 index 0000000000..ae7859b9cc --- /dev/null +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/LegacyScopeProviderTests.cs @@ -0,0 +1,21 @@ +using NUnit.Framework; +using Umbraco.Cms.Tests.Common.Testing; +using Umbraco.Cms.Tests.Integration.Testing; + +namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping; + +[TestFixture] +[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] +public class LegacyScopeProviderTests : UmbracoIntegrationTest +{ + [Test] + public void CreateScope_Always_ReturnsLegacyIScope() + { + var scopeProvider = GetRequiredService(); + + using (var scope = scopeProvider.CreateScope()) + { + Assert.IsInstanceOf(scope); + } + } +} diff --git a/tests/Umbraco.Tests.UnitTests/TestHelpers/PublishedSnapshotServiceTestBase.cs b/tests/Umbraco.Tests.UnitTests/TestHelpers/PublishedSnapshotServiceTestBase.cs index 4cf6ffdc9e..a74f9e5726 100644 --- a/tests/Umbraco.Tests.UnitTests/TestHelpers/PublishedSnapshotServiceTestBase.cs +++ b/tests/Umbraco.Tests.UnitTests/TestHelpers/PublishedSnapshotServiceTestBase.cs @@ -225,7 +225,7 @@ namespace Umbraco.Cms.Tests.UnitTests.TestHelpers DomainService = serviceContext.DomainService; // create a scope provider - IScopeProvider scopeProvider = Mock.Of(); + Infrastructure.Scoping.IScopeProvider scopeProvider = Mock.Of(); Mock.Get(scopeProvider) .Setup(x => x.CreateScope( It.IsAny(), diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/ObjectExtensionsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/ObjectExtensionsTests.cs index f8a07fdf21..00362a0abe 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/ObjectExtensionsTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/ObjectExtensionsTests.cs @@ -60,7 +60,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.CoreThings public void CanConvertIntToNullableInt() { int i = 1; - Attempt result = i.TryConvertTo(); + Attempt result = i.TryConvertTo(); Assert.That(result.Success, Is.True); } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/HealthCheckNotifierTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/HealthCheckNotifierTests.cs index b6f94c17a8..b60eb9891d 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/HealthCheckNotifierTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/HealthCheckNotifierTests.cs @@ -145,7 +145,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.HostedServices var mockMainDom = new Mock(); mockMainDom.SetupGet(x => x.IsMainDom).Returns(isMainDom); - var mockScopeProvider = new Mock(); + var mockScopeProvider = new Mock(); var mockLogger = new Mock>(); var mockProfilingLogger = new Mock(); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Mapping/MappingTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Mapping/MappingTests.cs index 9c8f9da75d..b3e761c5be 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Mapping/MappingTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Mapping/MappingTests.cs @@ -20,12 +20,12 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Mapping [TestFixture] public class MappingTests { - private IScopeProvider _scopeProvider; + private global::Umbraco.Cms.Infrastructure.Scoping.IScopeProvider _scopeProvider; [SetUp] public void MockScopeProvider() { - var scopeMock = new Mock(); + var scopeMock = new Mock(); scopeMock.Setup(x => x.CreateScope( It.IsAny(), It.IsAny(), diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs index 5fd34dae3f..c2081f2ce4 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs @@ -36,7 +36,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security public MemberManager CreateSut() { - IScopeProvider scopeProvider = new Mock().Object; + global::Umbraco.Cms.Infrastructure.Scoping.IScopeProvider scopeProvider = new Mock().Object; _mockMemberService = new Mock(); var mapDefinitions = new List() @@ -53,8 +53,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security new UmbracoMapper(new MapDefinitionCollection(() => mapDefinitions), scopeProvider), scopeProvider, new IdentityErrorDescriber(), - Mock.Of(), - Mock.Of(), + Mock.Of(), + Mock.Of(), Mock.Of()); _mockIdentityOptions = new Mock>(); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs index 14261e34fb..0773da9c3f 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs @@ -27,7 +27,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security { _mockMemberService = new Mock(); var mockScope = new Mock(); - var mockScopeProvider = new Mock(); + var mockScopeProvider = new Mock(); mockScopeProvider .Setup(x => x.CreateScope(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(mockScope.Object); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs index 2b65cf81ef..b8e4953c50 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs @@ -1171,9 +1171,9 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.PublishedCache.NuCache } } - private static IScopeProvider GetScopeProvider() + private static ICoreScopeProvider GetScopeProvider() { - IScopeProvider scopeProvider = Mock.Of(); + ICoreScopeProvider scopeProvider = Mock.Of(); Mock.Get(scopeProvider) .Setup(x => x.Context).Returns(() => null); return scopeProvider; diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs index 2d703b8d0f..b2b14ceab2 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs @@ -260,7 +260,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers new ActionCollection(() => null), Mock.Of(), Mock.Of(), - Mock.Of(), + Mock.Of(), Mock.Of(), Mock.Of() );