diff --git a/src/Umbraco.Core/Events/UmbracoRequestBegin.cs b/src/Umbraco.Core/Events/UmbracoRequestBegin.cs index ffb55938b3..00eb41df96 100644 --- a/src/Umbraco.Core/Events/UmbracoRequestBegin.cs +++ b/src/Umbraco.Core/Events/UmbracoRequestBegin.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using Umbraco.Cms.Core.Web; diff --git a/src/Umbraco.Core/Events/UmbracoRoutedRequest.cs b/src/Umbraco.Core/Events/UmbracoRoutedRequest.cs new file mode 100644 index 0000000000..dd2b4d0d58 --- /dev/null +++ b/src/Umbraco.Core/Events/UmbracoRoutedRequest.cs @@ -0,0 +1,33 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using Umbraco.Cms.Core.Web; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.Events +{ + /// + /// Notification raised when Umbraco routes a front-end request. + /// + public class UmbracoRoutedRequest : INotification + { + /// + /// Initializes a new instance of the class. + /// + public UmbracoRoutedRequest(IUmbracoContext umbracoContext) + { + if (!umbracoContext.IsFrontEndUmbracoRequest()) + { + throw new InvalidOperationException($"{nameof(UmbracoRoutedRequest)} is only valid for Umbraco front-end requests"); + } + + UmbracoContext = umbracoContext; + } + + /// + /// Gets the + /// + public IUmbracoContext UmbracoContext { get; } + } +} diff --git a/src/Umbraco.Core/HybridAccessorBase.cs b/src/Umbraco.Core/HybridAccessorBase.cs index ae3b4471e9..51a51e0d01 100644 --- a/src/Umbraco.Core/HybridAccessorBase.cs +++ b/src/Umbraco.Core/HybridAccessorBase.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Scoping; @@ -18,12 +18,12 @@ namespace Umbraco.Cms.Core { private readonly IRequestCache _requestCache; - // ReSharper disable StaticMemberInGenericType - private static readonly object Locker = new object(); - private static bool _registered; - // ReSharper restore StaticMemberInGenericType + private readonly object _locker = new object(); + private readonly bool _registered; - protected abstract string ItemKey { get; } + private string _itemKey; + + protected string ItemKey => _itemKey ??= GetType().FullName; // read // http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html @@ -43,20 +43,21 @@ namespace Umbraco.Cms.Core private T NonContextValue { get => CallContext.GetData(ItemKey); - set - { - CallContext.SetData(ItemKey, value); - } + set => CallContext.SetData(ItemKey, value); } protected HybridAccessorBase(IRequestCache requestCache) { _requestCache = requestCache ?? throw new ArgumentNullException(nameof(requestCache)); - lock (Locker) + lock (_locker) { // register the itemKey once with SafeCallContext - if (_registered) return; + if (_registered) + { + return; + } + _registered = true; } @@ -64,13 +65,20 @@ namespace Umbraco.Cms.Core var itemKey = ItemKey; // virtual SafeCallContext.Register(() => { - var value = CallContext.GetData(itemKey); + T value = CallContext.GetData(itemKey); return value; }, o => { - if (o == null) return; - var value = o as T; - if (value == null) throw new ArgumentException($"Expected type {typeof(T).FullName}, got {o.GetType().FullName}", nameof(o)); + if (o == null) + { + return; + } + + if (o is not T value) + { + throw new ArgumentException($"Expected type {typeof(T).FullName}, got {o.GetType().FullName}", nameof(o)); + } + CallContext.SetData(itemKey, value); }); } @@ -93,9 +101,13 @@ namespace Umbraco.Cms.Core NonContextValue = value; } else if (value == null) + { _requestCache.Remove(ItemKey); + } else + { _requestCache.Set(ItemKey, value); + } } } } diff --git a/src/Umbraco.Core/HybridEventMessagesAccessor.cs b/src/Umbraco.Core/HybridEventMessagesAccessor.cs index 6f4d33a307..df6a34aae8 100644 --- a/src/Umbraco.Core/HybridEventMessagesAccessor.cs +++ b/src/Umbraco.Core/HybridEventMessagesAccessor.cs @@ -1,12 +1,10 @@ -using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; namespace Umbraco.Cms.Core { public class HybridEventMessagesAccessor : HybridAccessorBase, IEventMessagesAccessor { - protected override string ItemKey => "Umbraco.Core.Events.HybridEventMessagesAccessor"; - public HybridEventMessagesAccessor(IRequestCache requestCache) : base(requestCache) { } diff --git a/src/Umbraco.Core/Models/PublishedContent/HybridVariationContextAccessor.cs b/src/Umbraco.Core/Models/PublishedContent/HybridVariationContextAccessor.cs index c412a4de3a..9c50b60ac1 100644 --- a/src/Umbraco.Core/Models/PublishedContent/HybridVariationContextAccessor.cs +++ b/src/Umbraco.Core/Models/PublishedContent/HybridVariationContextAccessor.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Cache; namespace Umbraco.Cms.Core.Models.PublishedContent { @@ -11,9 +11,6 @@ namespace Umbraco.Cms.Core.Models.PublishedContent : base(requestCache) { } - /// - protected override string ItemKey => "Umbraco.Web.HybridVariationContextAccessor"; - /// /// Gets or sets the object. /// diff --git a/src/Umbraco.Core/Security/HybridBackofficeSecurityAccessor.cs b/src/Umbraco.Core/Security/HybridBackofficeSecurityAccessor.cs index 990715ce39..924f0a31a6 100644 --- a/src/Umbraco.Core/Security/HybridBackofficeSecurityAccessor.cs +++ b/src/Umbraco.Core/Security/HybridBackofficeSecurityAccessor.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Cache; namespace Umbraco.Cms.Core.Security { @@ -11,9 +11,6 @@ namespace Umbraco.Cms.Core.Security : base(requestCache) { } - /// - protected override string ItemKey => "Umbraco.Web.HybridBackofficeSecurityAccessor"; - /// /// Gets or sets the object. /// diff --git a/src/Umbraco.Core/Security/HybridUmbracoWebsiteSecurityAccessor.cs b/src/Umbraco.Core/Security/HybridUmbracoWebsiteSecurityAccessor.cs index cb986588d3..3145f400d1 100644 --- a/src/Umbraco.Core/Security/HybridUmbracoWebsiteSecurityAccessor.cs +++ b/src/Umbraco.Core/Security/HybridUmbracoWebsiteSecurityAccessor.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Cache; namespace Umbraco.Cms.Core.Security { @@ -12,9 +12,6 @@ namespace Umbraco.Cms.Core.Security : base(requestCache) { } - /// - protected override string ItemKey => "Umbraco.Web.HybridUmbracoWebsiteSecurityAccessor"; - /// /// Gets or sets the object. /// diff --git a/src/Umbraco.Core/IBackofficeSecurityFactory.cs b/src/Umbraco.Core/Security/IBackOfficeSecurityFactory.cs similarity index 90% rename from src/Umbraco.Core/IBackofficeSecurityFactory.cs rename to src/Umbraco.Core/Security/IBackOfficeSecurityFactory.cs index ac7c875f16..ee553e85e6 100644 --- a/src/Umbraco.Core/IBackofficeSecurityFactory.cs +++ b/src/Umbraco.Core/Security/IBackOfficeSecurityFactory.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Core +namespace Umbraco.Cms.Core.Security { /// /// Creates and manages instances. diff --git a/src/Umbraco.Core/Security/IUmbracoWebsiteSecurity.cs b/src/Umbraco.Core/Security/IUmbracoWebsiteSecurity.cs index 86dbb9683e..10fb9b5f2c 100644 --- a/src/Umbraco.Core/Security/IUmbracoWebsiteSecurity.cs +++ b/src/Umbraco.Core/Security/IUmbracoWebsiteSecurity.cs @@ -1,9 +1,10 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using Umbraco.Cms.Core.Models.Security; namespace Umbraco.Cms.Core.Security { + // TODO: I think we can kill this whole thing, the logic can just be in the controllers public interface IUmbracoWebsiteSecurity { /// @@ -35,18 +36,10 @@ namespace Umbraco.Cms.Core.Security /// Result of update profile operation. Task UpdateMemberProfileAsync(ProfileModel model); - /// - /// A helper method to perform the validation and logging in of a member. - /// - /// The username. - /// The password. - /// Result of login operation. + // TODO: Kill this, we will just use the MemberManager / MemberSignInManager Task LoginAsync(string username, string password); - /// - /// Check if a member is logged in - /// - /// True if logged in, false if not. + // TODO: Kill this, we will just use the MemberManager bool IsLoggedIn(); /// @@ -55,9 +48,7 @@ namespace Umbraco.Cms.Core.Security /// Instance of Task GetCurrentLoginStatusAsync(); - /// - /// Logs out the current member. - /// + // TODO: Kill this, we will just use the MemberManager Task LogOutAsync(); /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 3f7c1064b2..e4518dc51c 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -2,11 +2,7 @@ netstandard2.0 - 8 Umbraco.Cms.Core - 0.5.0 - 0.5.0 - 0.5.0 Umbraco CMS Umbraco.Cms.Core Umbraco CMS Core diff --git a/src/Umbraco.Core/Web/HybridUmbracoContextAccessor.cs b/src/Umbraco.Core/Web/HybridUmbracoContextAccessor.cs index a266c07769..503bb25b57 100644 --- a/src/Umbraco.Core/Web/HybridUmbracoContextAccessor.cs +++ b/src/Umbraco.Core/Web/HybridUmbracoContextAccessor.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Cache; namespace Umbraco.Cms.Core.Web { @@ -14,9 +14,6 @@ namespace Umbraco.Cms.Core.Web : base(requestCache) { } - /// - protected override string ItemKey => "Umbraco.Web.HybridUmbracoContextAccessor"; - /// /// Gets or sets the object. /// diff --git a/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs b/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs index bc81bfabcf..0fc1809250 100644 --- a/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs +++ b/src/Umbraco.Infrastructure/HostedServices/ScheduledPublishing.cs @@ -106,7 +106,11 @@ namespace Umbraco.Cms.Infrastructure.HostedServices // - batched messenger should not depend on a current HttpContext // but then what should be its "scope"? could we attach it to scopes? // - and we should definitively *not* have to flush it here (should be auto) - // + + // TODO: This dependency chain is broken and needs to be fixed. + // This is required to be called before EnsureUmbracoContext else the UmbracoContext's IBackOfficeSecurity instance is null + // This is a very ugly Temporal Coupling which also means that developers can no longer just use IUmbracoContextFactory the + // way it was intended. _backofficeSecurityFactory.EnsureBackOfficeSecurity(); using UmbracoContextReference contextReference = _umbracoContextFactory.EnsureUmbracoContext(); try diff --git a/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs index 22fa172874..e8f5072e18 100644 --- a/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs +++ b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs @@ -80,6 +80,7 @@ _hostingEnvironment = hostingEnvironment; { db = _dbFactory.CreateDatabase(); + _hasTable = db.HasTable(Cms.Core.Constants.DatabaseSchema.Tables.KeyValue); if (!_hasTable) { diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 0b3e4f909c..43a1c53e43 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -2,7 +2,6 @@ netstandard2.0 - 8 Umbraco.Cms.Infrastructure Umbraco.Cms.Infrastructure Umbraco CMS Infrastructure diff --git a/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj b/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj index f80e8669b4..5c3ebd1a4c 100644 --- a/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj +++ b/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj @@ -2,7 +2,6 @@ netstandard2.0 - latest Umbraco.Cms.Tests.Common Umbraco.Cms.Tests Umbraco CMS Test Tools diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs index caed95ae52..dbb2adef89 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -18,6 +18,7 @@ using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.DependencyInjection; @@ -26,7 +27,6 @@ using Umbraco.Cms.Web.BackOffice.Controllers; using Umbraco.Cms.Web.Common.Controllers; using Umbraco.Cms.Web.Website.Controllers; using Umbraco.Extensions; -using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Tests.Integration.TestServerTest { @@ -122,6 +122,10 @@ namespace Umbraco.Cms.Tests.Integration.TestServerTest } }; + // TODO: This dependency chain is broken and needs to be fixed. + // This is required to be called before EnsureUmbracoContext else the UmbracoContext's IBackOfficeSecurity instance is null + // This is a very ugly Temporal Coupling which also means that developers can no longer just use IUmbracoContextFactory the + // way it was intended. backofficeSecurityFactory.EnsureBackOfficeSecurity(); umbracoContextFactory.EnsureUmbracoContext(); diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index eab085f67e..133320b853 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -25,6 +25,7 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Web; @@ -37,7 +38,6 @@ using Umbraco.Cms.Tests.Integration.DependencyInjection; using Umbraco.Cms.Tests.Integration.Extensions; using Umbraco.Cms.Tests.Integration.Implementations; using Umbraco.Extensions; -using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Tests.Integration.Testing { diff --git a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index 18cc68996d..f45a7bc444 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -4,7 +4,6 @@ Exe net5.0 false - 8 Umbraco.Cms.Tests.Integration diff --git a/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/Filters/ContentModelValidatorTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/Filters/ContentModelValidatorTests.cs index 5cf0dc7b28..91432f142e 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/Filters/ContentModelValidatorTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/Filters/ContentModelValidatorTests.cs @@ -11,12 +11,12 @@ using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NUnit.Framework; -using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/ScheduledPublishingTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/ScheduledPublishingTests.cs index 62ee1fdd29..3ef434edab 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/ScheduledPublishingTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/ScheduledPublishingTests.cs @@ -8,6 +8,7 @@ using Moq; using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Runtime; +using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Core.Web; diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformerTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformerTests.cs index 4d74ea6427..e8e8fec2e0 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformerTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformerTests.cs @@ -13,6 +13,7 @@ using Moq; using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; @@ -56,7 +57,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Website.Routing routeValuesFactory ?? Mock.Of(), filter ?? Mock.Of(x => x.IsDocumentRequest(It.IsAny()) == true), Mock.Of(), - Mock.Of()); + Mock.Of(), + Mock.Of()); return transformer; } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Security/UmbracoWebsiteSecurityTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Security/UmbracoWebsiteSecurityTests.cs index 5e53486d11..badcaae8b5 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Security/UmbracoWebsiteSecurityTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Security/UmbracoWebsiteSecurityTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System.Linq; @@ -13,7 +13,7 @@ using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Tests.Common.Builders; -using Umbraco.Cms.Web.Website.Security; +using Umbraco.Cms.Web.Common.Security; using CoreConstants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Website.Security diff --git a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj index cd80f15b92..667cceb8be 100644 --- a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj +++ b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj @@ -3,7 +3,6 @@ net5.0 Library - latest Umbraco.Cms.Web.BackOffice Umbraco.Cms.Web.BackOffice Umbraco CMS Back Office diff --git a/src/Umbraco.Web.Common/Controllers/ProxyViewDataFeature.cs b/src/Umbraco.Web.Common/Controllers/ProxyViewDataFeature.cs index f926ccbfaa..e44f3270fe 100644 --- a/src/Umbraco.Web.Common/Controllers/ProxyViewDataFeature.cs +++ b/src/Umbraco.Web.Common/Controllers/ProxyViewDataFeature.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Mvc.ViewFeatures; namespace Umbraco.Cms.Web.Common.Controllers { @@ -10,11 +10,20 @@ namespace Umbraco.Cms.Web.Common.Controllers /// /// Initializes a new instance of the class. /// - public ProxyViewDataFeature(ViewDataDictionary viewData) => ViewData = viewData; + public ProxyViewDataFeature(ViewDataDictionary viewData, ITempDataDictionary tempData) + { + ViewData = viewData; + TempData = tempData; + } /// /// Gets the /// public ViewDataDictionary ViewData { get; } + + /// + /// Gets the + /// + public ITempDataDictionary TempData { get; } } } diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index 9dd375c9af..6c11b91a95 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -255,6 +255,7 @@ namespace Umbraco.Extensions builder.Services.AddUnique(); builder.Services.AddUnique(); + builder.Services.AddUnique(); builder.Services.AddUnique(); // register the umbraco context factory @@ -262,6 +263,7 @@ namespace Umbraco.Extensions builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); + builder.AddNotificationHandler(); builder.Services.AddUnique(); var umbracoApiControllerTypes = builder.TypeLoader.GetUmbracoApiControllers().ToList(); diff --git a/src/Umbraco.Web.Common/Macros/MacroRenderer.cs b/src/Umbraco.Web.Common/Macros/MacroRenderer.cs index f798199012..129936071c 100644 --- a/src/Umbraco.Web.Common/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web.Common/Macros/MacroRenderer.cs @@ -37,6 +37,7 @@ namespace Umbraco.Cms.Web.Common.Macros private readonly ISessionManager _sessionManager; private readonly IRequestAccessor _requestAccessor; private readonly IHttpContextAccessor _httpContextAccessor; + private readonly PartialViewMacroEngine _partialViewMacroEngine; public MacroRenderer( IProfilingLogger profilingLogger, @@ -52,7 +53,8 @@ namespace Umbraco.Cms.Web.Common.Macros IMemberUserKeyProvider memberUserKeyProvider, ISessionManager sessionManager, IRequestAccessor requestAccessor, - IHttpContextAccessor httpContextAccessor) + IHttpContextAccessor httpContextAccessor, + PartialViewMacroEngine partialViewMacroEngine) { _profilingLogger = profilingLogger ?? throw new ArgumentNullException(nameof(profilingLogger)); _logger = logger; @@ -68,6 +70,7 @@ namespace Umbraco.Cms.Web.Common.Macros _sessionManager = sessionManager; _requestAccessor = requestAccessor; _httpContextAccessor = httpContextAccessor; + _partialViewMacroEngine = partialViewMacroEngine; } #region MacroContent cache @@ -338,38 +341,27 @@ namespace Umbraco.Cms.Web.Common.Macros private Attempt ExecuteMacroOfType(MacroModel model, IPublishedContent content) { if (model == null) + { throw new ArgumentNullException(nameof(model)); + } // ensure that we are running against a published node (ie available in XML) // that may not be the case if the macro is embedded in a RTE of an unpublished document if (content == null) + { return Attempt.Fail(new MacroContent { Text = "[macro failed (no content)]" }); + } - var textService = _textService; return ExecuteMacroWithErrorWrapper(model, $"Executing PartialView: MacroSource=\"{model.MacroSource}\".", "Executed PartialView.", - () => ExecutePartialView(model, content), - () => textService.Localize("errors/macroErrorLoadingPartialView", new[] { model.MacroSource })); + () => _partialViewMacroEngine.Execute(model, content), + () => _textService.Localize("errors/macroErrorLoadingPartialView", new[] { model.MacroSource })); } - #endregion - - #region Execute engines - - /// - /// Renders a PartialView Macro. - /// - /// The text output of the macro execution. - private MacroContent ExecutePartialView(MacroModel macro, IPublishedContent content) - { - var engine = new PartialViewMacroEngine(_httpContextAccessor, _hostingEnvironment); - return engine.Execute(macro, content); - } - #endregion #region Execution helpers diff --git a/src/Umbraco.Web.Common/Macros/PartialViewMacroEngine.cs b/src/Umbraco.Web.Common/Macros/PartialViewMacroEngine.cs index ef59c9f896..4cbc2d7648 100644 --- a/src/Umbraco.Web.Common/Macros/PartialViewMacroEngine.cs +++ b/src/Umbraco.Web.Common/Macros/PartialViewMacroEngine.cs @@ -16,6 +16,7 @@ using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Macros; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Web.Common.Controllers; using Umbraco.Extensions; using static Umbraco.Cms.Core.Constants.Web.Routing; @@ -27,47 +28,19 @@ namespace Umbraco.Cms.Web.Common.Macros public class PartialViewMacroEngine { private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IHostingEnvironment _hostingEnvironment; - //private readonly Func _getUmbracoContext; + private readonly IModelMetadataProvider _modelMetadataProvider; + private readonly ITempDataProvider _tempDataProvider; public PartialViewMacroEngine( - //IUmbracoContextAccessor umbracoContextAccessor, IHttpContextAccessor httpContextAccessor, - IHostingEnvironment hostingEnvironment) + IModelMetadataProvider modelMetadataProvider, + ITempDataProvider tempDataProvider) { _httpContextAccessor = httpContextAccessor; - _hostingEnvironment = hostingEnvironment; - - //_getUmbracoContext = () => - //{ - // var context = umbracoContextAccessor.UmbracoContext; - // if (context == null) - // { - // throw new InvalidOperationException( - // $"The {GetType()} cannot execute with a null UmbracoContext.Current reference."); - // } - - // return context; - //}; + _modelMetadataProvider = modelMetadataProvider; + _tempDataProvider = tempDataProvider; } - //public bool Validate(string code, string tempFileName, IPublishedContent currentPage, out string errorMessage) - //{ - // var temp = GetVirtualPathFromPhysicalPath(tempFileName); - // try - // { - // CompileAndInstantiate(temp); - // } - // catch (Exception exception) - // { - // errorMessage = exception.Message; - // return false; - // } - - // errorMessage = string.Empty; - // return true; - //} - public MacroContent Execute(MacroModel macro, IPublishedContent content) { if (macro == null) @@ -85,32 +58,24 @@ namespace Umbraco.Cms.Web.Common.Macros throw new ArgumentException("The MacroSource property of the macro object cannot be null or empty"); } - var httpContext = _httpContextAccessor.GetRequiredHttpContext(); - //var umbCtx = _getUmbracoContext(); - var routeVals = new RouteData(); - routeVals.Values.Add(ControllerToken, "PartialViewMacro"); - routeVals.Values.Add(ActionToken, "Index"); + HttpContext httpContext = _httpContextAccessor.GetRequiredHttpContext(); + + RouteData currentRouteData = httpContext.GetRouteData(); - //TODO: Was required for UmbracoViewPage need to figure out if we still need that, i really don't think this is necessary - //routeVals.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, umbCtx); - - var modelMetadataProvider = httpContext.RequestServices.GetRequiredService(); - var tempDataProvider = httpContext.RequestServices.GetRequiredService(); + // Check if there's proxied ViewData (i.e. returned from a SurfaceController) + ProxyViewDataFeature proxyViewDataFeature = httpContext.Features.Get(); + ViewDataDictionary viewData = proxyViewDataFeature?.ViewData ?? new ViewDataDictionary(_modelMetadataProvider, new ModelStateDictionary()); + ITempDataDictionary tempData = proxyViewDataFeature?.TempData ?? new TempDataDictionary(httpContext, _tempDataProvider); var viewContext = new ViewContext( - new ActionContext(httpContext, httpContext.GetRouteData(), new ControllerActionDescriptor()), + new ActionContext(httpContext, currentRouteData, new ControllerActionDescriptor()), new FakeView(), - new ViewDataDictionary(modelMetadataProvider, new ModelStateDictionary()), - new TempDataDictionary(httpContext, tempDataProvider), + viewData, + tempData, TextWriter.Null, new HtmlHelperOptions() ); - - routeVals.DataTokens.Add("ParentActionViewContext", viewContext); - - var viewComponent = new PartialViewMacroViewComponent(macro, content); - var writer = new StringWriter(); var viewComponentContext = new ViewComponentContext( new ViewComponentDescriptor(), @@ -119,7 +84,9 @@ namespace Umbraco.Cms.Web.Common.Macros viewContext, writer); - viewComponent.InvokeAsync().GetAwaiter().GetResult().Execute(viewComponentContext); + var viewComponent = new PartialViewMacroViewComponent(macro, content, viewComponentContext); + + viewComponent.Invoke().Execute(viewComponentContext); var output = writer.GetStringBuilder().ToString(); @@ -129,38 +96,11 @@ namespace Umbraco.Cms.Web.Common.Macros private class FakeView : IView { /// - public Task RenderAsync(ViewContext context) - { - return Task.CompletedTask; - } + public Task RenderAsync(ViewContext context) => Task.CompletedTask; /// public string Path { get; } = "View"; } - - //private string GetVirtualPathFromPhysicalPath(string physicalPath) - //{ - // var rootpath = _hostingEnvironment.MapPathContentRoot("~/"); - // physicalPath = physicalPath.Replace(rootpath, ""); - // physicalPath = physicalPath.Replace("\\", "/"); - // return "~/" + physicalPath; - //} - - //private static PartialViewMacroPage CompileAndInstantiate(string virtualPath) - //{ - // // //Compile Razor - We Will Leave This To ASP.NET Compilation Engine & ASP.NET WebPages - // // //Security in medium trust is strict around here, so we can only pass a virtual file path - // // //ASP.NET Compilation Engine caches returned types - // // //Changed From BuildManager As Other Properties Are Attached Like Context Path/ - // // var webPageBase = WebPageBase.CreateInstanceFromVirtualPath(virtualPath); - // // var webPage = webPageBase as PartialViewMacroPage; - // // if (webPage == null) - // // throw new InvalidCastException("All Partial View Macro views must inherit from " + typeof(PartialViewMacroPage).FullName); - // // return webPage; - - // //TODO? How to check this - // return null; - //} } } diff --git a/src/Umbraco.Web.Common/Macros/PartialViewMacroViewComponent.cs b/src/Umbraco.Web.Common/Macros/PartialViewMacroViewComponent.cs index 2b317585b4..5cab93c00f 100644 --- a/src/Umbraco.Web.Common/Macros/PartialViewMacroViewComponent.cs +++ b/src/Umbraco.Web.Common/Macros/PartialViewMacroViewComponent.cs @@ -1,6 +1,7 @@ -using System.Linq; +using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ViewComponents; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Macros; using Umbraco.Cms.Core.Models; @@ -20,13 +21,17 @@ namespace Umbraco.Cms.Web.Common.Macros public PartialViewMacroViewComponent( MacroModel macro, - IPublishedContent content) + IPublishedContent content, + ViewComponentContext viewComponentContext) { _macro = macro; _content = content; + // This must be set before Invoke is called else the call to View will end up + // using an empty ViewData instance because this hasn't been set yet. + ViewComponentContext = viewComponentContext; } - public async Task InvokeAsync() + public IViewComponentResult Invoke() { var model = new PartialViewMacroModel( _content, @@ -34,7 +39,8 @@ namespace Umbraco.Cms.Web.Common.Macros _macro.Alias, _macro.Name, _macro.Properties.ToDictionary(x => x.Key, x => (object)x.Value)); - var result = View(_macro.MacroSource, model); + + ViewViewComponentResult result = View(_macro.MacroSource, model); return result; } diff --git a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs index 6fdd41a757..467ec29451 100644 --- a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs +++ b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs @@ -10,6 +10,7 @@ using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.PublishedCache; using Umbraco.Cms.Web.Common.Profiler; @@ -83,27 +84,26 @@ namespace Umbraco.Cms.Web.Common.Middleware EnsureContentCacheInitialized(); - _backofficeSecurityFactory.EnsureBackOfficeSecurity(); // Needs to be before UmbracoContext, TODO: Why? + // TODO: This dependency chain is broken and needs to be fixed. + // This is required to be called before EnsureUmbracoContext else the UmbracoContext's IBackOfficeSecurity instance is null + // This is ugly Temporal Coupling which also means that developers can no longer just use IUmbracoContextFactory the + // way it was intended. + _backofficeSecurityFactory.EnsureBackOfficeSecurity(); UmbracoContextReference umbracoContextReference = _umbracoContextFactory.EnsureUmbracoContext(); Uri currentApplicationUrl = GetApplicationUrlFromCurrentRequest(context.Request); _hostingEnvironment.EnsureApplicationMainUrl(currentApplicationUrl); - - bool isFrontEndRequest = umbracoContextReference.UmbracoContext.IsFrontEndUmbracoRequest(); - var pathAndQuery = context.Request.GetEncodedPathAndQuery(); try { - if (isFrontEndRequest) - { - LogHttpRequest.TryGetCurrentHttpRequestId(out Guid httpRequestId, _requestCache); - _logger.LogTrace("Begin request [{HttpRequestId}]: {RequestUrl}", httpRequestId, pathAndQuery); - } + // Verbose log start of every request + LogHttpRequest.TryGetCurrentHttpRequestId(out Guid httpRequestId, _requestCache); + _logger.LogTrace("Begin request [{HttpRequestId}]: {RequestUrl}", httpRequestId, pathAndQuery); try - { + { await _eventAggregator.PublishAsync(new UmbracoRequestBegin(umbracoContextReference.UmbracoContext)); } catch (Exception ex) @@ -126,11 +126,10 @@ namespace Umbraco.Cms.Web.Common.Middleware } finally { - if (isFrontEndRequest) - { - LogHttpRequest.TryGetCurrentHttpRequestId(out Guid httpRequestId, _requestCache); - _logger.LogTrace("End Request [{HttpRequestId}]: {RequestUrl} ({RequestDuration}ms)", httpRequestId, pathAndQuery, DateTime.Now.Subtract(umbracoContextReference.UmbracoContext.ObjectCreated).TotalMilliseconds); - } + // Verbose log end of every request (in v8 we didn't log the end request of ALL requests, only the front-end which was + // strange since we always logged the beginning, so now we just log start/end of all requests) + LogHttpRequest.TryGetCurrentHttpRequestId(out Guid httpRequestId, _requestCache); + _logger.LogTrace("End Request [{HttpRequestId}]: {RequestUrl} ({RequestDuration}ms)", httpRequestId, pathAndQuery, DateTime.Now.Subtract(umbracoContextReference.UmbracoContext.ObjectCreated).TotalMilliseconds); try { diff --git a/src/Umbraco.Web.Common/Security/BackofficeSecurityFactory.cs b/src/Umbraco.Web.Common/Security/BackofficeSecurityFactory.cs index 41e7f6d816..ad0731f790 100644 --- a/src/Umbraco.Web.Common/Security/BackofficeSecurityFactory.cs +++ b/src/Umbraco.Web.Common/Security/BackofficeSecurityFactory.cs @@ -1,11 +1,10 @@ using Microsoft.AspNetCore.Http; -using Umbraco.Cms.Core; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; namespace Umbraco.Cms.Web.Common.Security { - // TODO: This is only for the back office, does it need to be in common? + // TODO: This is only for the back office, does it need to be in common? YES currently UmbracoContext has an transitive dependency on this which needs to be fixed/reviewed. public class BackOfficeSecurityFactory: IBackOfficeSecurityFactory { @@ -14,11 +13,11 @@ namespace Umbraco.Cms.Web.Common.Security private readonly IHttpContextAccessor _httpContextAccessor; public BackOfficeSecurityFactory( - IBackOfficeSecurityAccessor backofficeSecurityAccessor, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, IUserService userService, IHttpContextAccessor httpContextAccessor) { - _backOfficeSecurityAccessor = backofficeSecurityAccessor; + _backOfficeSecurityAccessor = backOfficeSecurityAccessor; _userService = userService; _httpContextAccessor = httpContextAccessor; } diff --git a/src/Umbraco.Web.Website/Security/UmbracoWebsiteSecurity.cs b/src/Umbraco.Web.Common/Security/UmbracoWebsiteSecurity.cs similarity index 97% rename from src/Umbraco.Web.Website/Security/UmbracoWebsiteSecurity.cs rename to src/Umbraco.Web.Common/Security/UmbracoWebsiteSecurity.cs index c878730d90..5a67f6f484 100644 --- a/src/Umbraco.Web.Website/Security/UmbracoWebsiteSecurity.cs +++ b/src/Umbraco.Web.Common/Security/UmbracoWebsiteSecurity.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -11,9 +11,8 @@ using Umbraco.Cms.Core.Models.Security; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; -using Constants = Umbraco.Cms.Core.Constants; -namespace Umbraco.Cms.Web.Website.Security +namespace Umbraco.Cms.Web.Common.Security { public class UmbracoWebsiteSecurity : IUmbracoWebsiteSecurity { @@ -36,7 +35,7 @@ namespace Umbraco.Cms.Web.Website.Security /// public RegisterModel CreateRegistrationModel(string memberTypeAlias = null) { - var providedOrDefaultMemberTypeAlias = memberTypeAlias ?? Constants.Conventions.MemberTypes.DefaultAlias; + var providedOrDefaultMemberTypeAlias = memberTypeAlias ?? Core.Constants.Conventions.MemberTypes.DefaultAlias; var memberType = _memberTypeService.Get(providedOrDefaultMemberTypeAlias); if (memberType == null) { @@ -114,7 +113,7 @@ namespace Umbraco.Cms.Web.Website.Security public Task RegisterMemberAsync(RegisterModel model, bool logMemberIn = true) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } /// diff --git a/src/Umbraco.Web.Common/Security/UmbracoWebsiteSecurityFactory.cs b/src/Umbraco.Web.Common/Security/UmbracoWebsiteSecurityFactory.cs new file mode 100644 index 0000000000..ec256a86cb --- /dev/null +++ b/src/Umbraco.Web.Common/Security/UmbracoWebsiteSecurityFactory.cs @@ -0,0 +1,46 @@ +using Microsoft.AspNetCore.Http; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; + +namespace Umbraco.Cms.Web.Common.Security +{ + /// + /// Ensures that the is populated on a front-end request + /// + internal sealed class UmbracoWebsiteSecurityFactory : INotificationHandler + { + private readonly IUmbracoWebsiteSecurityAccessor _umbracoWebsiteSecurityAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IMemberService _memberService; + private readonly IMemberTypeService _memberTypeService; + private readonly IShortStringHelper _shortStringHelper; + + public UmbracoWebsiteSecurityFactory( + IUmbracoWebsiteSecurityAccessor umbracoWebsiteSecurityAccessor, + IHttpContextAccessor httpContextAccessor, + IMemberService memberService, + IMemberTypeService memberTypeService, + IShortStringHelper shortStringHelper) + { + _umbracoWebsiteSecurityAccessor = umbracoWebsiteSecurityAccessor; + _httpContextAccessor = httpContextAccessor; + _memberService = memberService; + _memberTypeService = memberTypeService; + _shortStringHelper = shortStringHelper; + } + + public void Handle(UmbracoRoutedRequest notification) + { + if (_umbracoWebsiteSecurityAccessor.WebsiteSecurity is null) + { + _umbracoWebsiteSecurityAccessor.WebsiteSecurity = new UmbracoWebsiteSecurity( + _httpContextAccessor, + _memberService, + _memberTypeService, + _shortStringHelper); + } + } + } +} diff --git a/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs b/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs index 5818609aeb..81f034918a 100644 --- a/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs @@ -36,9 +36,10 @@ namespace Umbraco.Cms.Web.Common.Templates private readonly IFileService _fileService; private readonly ILocalizationService _languageService; private readonly WebRoutingSettings _webRoutingSettings; - private readonly IShortStringHelper _shortStringHelper; private readonly IHttpContextAccessor _httpContextAccessor; private readonly ICompositeViewEngine _viewEngine; + private readonly IModelMetadataProvider _modelMetadataProvider; + private readonly ITempDataProvider _tempDataProvider; public TemplateRenderer( IUmbracoContextAccessor umbracoContextAccessor, @@ -46,18 +47,20 @@ namespace Umbraco.Cms.Web.Common.Templates IFileService fileService, ILocalizationService textService, IOptions webRoutingSettings, - IShortStringHelper shortStringHelper, IHttpContextAccessor httpContextAccessor, - ICompositeViewEngine viewEngine) + ICompositeViewEngine viewEngine, + IModelMetadataProvider modelMetadataProvider, + ITempDataProvider tempDataProvider) { _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _publishedRouter = publishedRouter ?? throw new ArgumentNullException(nameof(publishedRouter)); _fileService = fileService ?? throw new ArgumentNullException(nameof(fileService)); _languageService = textService ?? throw new ArgumentNullException(nameof(textService)); _webRoutingSettings = webRoutingSettings.Value ?? throw new ArgumentNullException(nameof(webRoutingSettings)); - _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); _viewEngine = viewEngine ?? throw new ArgumentNullException(nameof(viewEngine)); + _modelMetadataProvider = modelMetadataProvider; + _tempDataProvider = tempDataProvider; } public async Task RenderAsync(int pageId, int? altTemplateId, StringWriter writer) @@ -156,10 +159,7 @@ namespace Umbraco.Cms.Web.Common.Templates throw new InvalidOperationException($"A view with the name {request.GetTemplateAlias()} could not be found"); } - var modelMetadataProvider = httpContext.RequestServices.GetRequiredService(); - var tempDataProvider = httpContext.RequestServices.GetRequiredService(); - - var viewData = new ViewDataDictionary(modelMetadataProvider, new ModelStateDictionary()) + var viewData = new ViewDataDictionary(_modelMetadataProvider, new ModelStateDictionary()) { Model = request.PublishedContent }; @@ -169,7 +169,7 @@ namespace Umbraco.Cms.Web.Common.Templates new ActionContext(httpContext, httpContext.GetRouteData(), new ControllerActionDescriptor()), viewResult.View, viewData, - new TempDataDictionary(httpContext, tempDataProvider), + new TempDataDictionary(httpContext, _tempDataProvider), writer, new HtmlHelperOptions() ); @@ -182,6 +182,9 @@ namespace Umbraco.Cms.Web.Common.Templates sw.Write(output); } + // TODO: I feel like we need to do more than this, pretty sure we need to replace the UmbracoRouteValues + // HttpRequest feature too while this renders. + private void SetNewItemsOnContextObjects(IPublishedRequest request) { // now, set the new ones for this page execution diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index fe8572c1ca..b4c9912f16 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -3,7 +3,6 @@ net5.0 Library - latest Umbraco.Cms.Web.Common Umbraco.Cms.Web.Common Umbraco CMS Web diff --git a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj index 4f6bb1240f..066a27ccee 100644 --- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -3,7 +3,6 @@ net5.0 Umbraco.Cms.Web.UI.NetCore - latest Umbraco.Cms.Web.UI.NetCore diff --git a/src/Umbraco.Web.Website/Controllers/SurfaceController.cs b/src/Umbraco.Web.Website/Controllers/SurfaceController.cs index a0798d2fd0..b86af23999 100644 --- a/src/Umbraco.Web.Website/Controllers/SurfaceController.cs +++ b/src/Umbraco.Web.Website/Controllers/SurfaceController.cs @@ -98,7 +98,7 @@ namespace Umbraco.Cms.Web.Website.Controllers /// protected UmbracoPageResult CurrentUmbracoPage() { - HttpContext.Features.Set(new ProxyViewDataFeature(ViewData)); + HttpContext.Features.Set(new ProxyViewDataFeature(ViewData, TempData)); return new UmbracoPageResult(ProfilingLogger); } } diff --git a/src/Umbraco.Web.Website/Controllers/UmbLoginController.cs b/src/Umbraco.Web.Website/Controllers/UmbLoginController.cs index 67c4cd9bf7..93b1f23b76 100644 --- a/src/Umbraco.Web.Website/Controllers/UmbLoginController.cs +++ b/src/Umbraco.Web.Website/Controllers/UmbLoginController.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Logging; @@ -17,8 +18,13 @@ namespace Umbraco.Cms.Web.Website.Controllers { private readonly IUmbracoWebsiteSecurityAccessor _websiteSecurityAccessor; - public UmbLoginController(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory, - ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger, IPublishedUrlProvider publishedUrlProvider, + public UmbLoginController( + IUmbracoContextAccessor umbracoContextAccessor, + IUmbracoDatabaseFactory databaseFactory, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger profilingLogger, + IPublishedUrlProvider publishedUrlProvider, IUmbracoWebsiteSecurityAccessor websiteSecurityAccessor) : base(umbracoContextAccessor, databaseFactory, services, appCaches, profilingLogger, publishedUrlProvider) { diff --git a/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs index 3823cde255..58074ac9a5 100644 --- a/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs @@ -2,8 +2,10 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Security; using Umbraco.Cms.Infrastructure.DependencyInjection; using Umbraco.Cms.Web.Common.Routing; +using Umbraco.Cms.Web.Common.Security; using Umbraco.Cms.Web.Website.Collections; using Umbraco.Cms.Web.Website.Controllers; using Umbraco.Cms.Web.Website.Routing; @@ -40,7 +42,7 @@ namespace Umbraco.Extensions builder.Services.AddSingleton(); builder.Services.AddSingleton(); - builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder .AddDistributedCache() diff --git a/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs index 1502a51665..ddca7f37aa 100644 --- a/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs @@ -165,16 +165,11 @@ namespace Umbraco.Extensions return htmlHelper.ValidationSummary(excludePropertyErrors, message, htmlAttributes); } - var htmlGenerator = GetRequiredService(htmlHelper); + IHtmlGenerator htmlGenerator = GetRequiredService(htmlHelper); - var viewContext = htmlHelper.ViewContext.Clone(); - foreach (var key in viewContext.ViewData.Keys.ToArray()) - { - if (!key.StartsWith(prefix)) - { - viewContext.ViewData.Remove(key); - } - } + ViewContext viewContext = htmlHelper.ViewContext.Clone(); + //change the HTML field name + viewContext.ViewData.TemplateInfo.HtmlFieldPrefix = prefix; var tagBuilder = htmlGenerator.GenerateValidationSummary( viewContext, diff --git a/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs b/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs index d0e5d4c72a..c5d13b721a 100644 --- a/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs +++ b/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs @@ -13,6 +13,7 @@ using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; @@ -48,6 +49,7 @@ namespace Umbraco.Cms.Web.Website.Routing private readonly IRoutableDocumentFilter _routableDocumentFilter; private readonly IDataProtectionProvider _dataProtectionProvider; private readonly IControllerActionSearcher _controllerActionSearcher; + private readonly IEventAggregator _eventAggregator; /// /// Initializes a new instance of the class. @@ -62,7 +64,8 @@ namespace Umbraco.Cms.Web.Website.Routing IUmbracoRouteValuesFactory routeValuesFactory, IRoutableDocumentFilter routableDocumentFilter, IDataProtectionProvider dataProtectionProvider, - IControllerActionSearcher controllerActionSearcher) + IControllerActionSearcher controllerActionSearcher, + IEventAggregator eventAggregator) { if (globalSettings is null) { @@ -79,6 +82,7 @@ namespace Umbraco.Cms.Web.Website.Routing _routableDocumentFilter = routableDocumentFilter ?? throw new ArgumentNullException(nameof(routableDocumentFilter)); _dataProtectionProvider = dataProtectionProvider; _controllerActionSearcher = controllerActionSearcher; + _eventAggregator = eventAggregator; } /// @@ -117,6 +121,9 @@ namespace Umbraco.Cms.Web.Website.Routing // Store the route values as a httpcontext feature httpContext.Features.Set(umbracoRouteValues); + // publish an event that we've routed a request + await _eventAggregator.PublishAsync(new UmbracoRoutedRequest(_umbracoContextAccessor.UmbracoContext)); + // Need to check if there is form data being posted back to an Umbraco URL PostedDataProxyInfo postedInfo = GetFormInfo(httpContext, values); if (postedInfo != null) diff --git a/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj b/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj index 6815016713..a7c5e7a277 100644 --- a/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj +++ b/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj @@ -3,7 +3,6 @@ net5.0 Library - latest Umbraco.Cms.Web.Website Umbraco.Cms.Web.Website Umbraco CMS Website