Merge remote-tracking branch 'origin/netcore/dev' into netcore/feature/individual-nuget-pr-assembly

This commit is contained in:
Mole
2021-03-05 14:07:16 +01:00
41 changed files with 250 additions and 222 deletions

View File

@@ -1,4 +1,4 @@
// Copyright (c) Umbraco.
// Copyright (c) Umbraco.
// See LICENSE for more details.
using Umbraco.Cms.Core.Web;

View File

@@ -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
{
/// <summary>
/// Notification raised when Umbraco routes a front-end request.
/// </summary>
public class UmbracoRoutedRequest : INotification
{
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoRequestBegin"/> class.
/// </summary>
public UmbracoRoutedRequest(IUmbracoContext umbracoContext)
{
if (!umbracoContext.IsFrontEndUmbracoRequest())
{
throw new InvalidOperationException($"{nameof(UmbracoRoutedRequest)} is only valid for Umbraco front-end requests");
}
UmbracoContext = umbracoContext;
}
/// <summary>
/// Gets the <see cref="IUmbracoContext"/>
/// </summary>
public IUmbracoContext UmbracoContext { get; }
}
}

View File

@@ -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<T>.GetData(ItemKey);
set
{
CallContext<T>.SetData(ItemKey, value);
}
set => CallContext<T>.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<T>.GetData(itemKey);
T value = CallContext<T>.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<T>.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);
}
}
}
}

View File

@@ -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<EventMessages>, IEventMessagesAccessor
{
protected override string ItemKey => "Umbraco.Core.Events.HybridEventMessagesAccessor";
public HybridEventMessagesAccessor(IRequestCache requestCache)
: base(requestCache)
{ }

View File

@@ -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)
{ }
/// <inheritdoc />
protected override string ItemKey => "Umbraco.Web.HybridVariationContextAccessor";
/// <summary>
/// Gets or sets the <see cref="VariationContext"/> object.
/// </summary>

View File

@@ -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)
{ }
/// <inheritdoc />
protected override string ItemKey => "Umbraco.Web.HybridBackofficeSecurityAccessor";
/// <summary>
/// Gets or sets the <see cref="IBackOfficeSecurity"/> object.
/// </summary>

View File

@@ -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)
{ }
/// <inheritdoc />
protected override string ItemKey => "Umbraco.Web.HybridUmbracoWebsiteSecurityAccessor";
/// <summary>
/// Gets or sets the <see cref="IUmbracoWebsiteSecurity"/> object.
/// </summary>

View File

@@ -1,4 +1,4 @@
namespace Umbraco.Cms.Core
namespace Umbraco.Cms.Core.Security
{
/// <summary>
/// Creates and manages <see cref="IBackOfficeSecurity"/> instances.

View File

@@ -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
{
/// <summary>
@@ -35,18 +36,10 @@ namespace Umbraco.Cms.Core.Security
/// <returns>Result of update profile operation.</returns>
Task<UpdateMemberProfileResult> UpdateMemberProfileAsync(ProfileModel model);
/// <summary>
/// A helper method to perform the validation and logging in of a member.
/// </summary>
/// <param name="username">The username.</param>
/// <param name="password">The password.</param>
/// <returns>Result of login operation.</returns>
// TODO: Kill this, we will just use the MemberManager / MemberSignInManager
Task<bool> LoginAsync(string username, string password);
/// <summary>
/// Check if a member is logged in
/// </summary>
/// <returns>True if logged in, false if not.</returns>
// TODO: Kill this, we will just use the MemberManager
bool IsLoggedIn();
/// <summary>
@@ -55,9 +48,7 @@ namespace Umbraco.Cms.Core.Security
/// <returns>Instance of <see cref="LoginStatusModel"/></returns>
Task<LoginStatusModel> GetCurrentLoginStatusAsync();
/// <summary>
/// Logs out the current member.
/// </summary>
// TODO: Kill this, we will just use the MemberManager
Task LogOutAsync();
/// <summary>

View File

@@ -2,11 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8</LangVersion>
<RootNamespace>Umbraco.Cms.Core</RootNamespace>
<AssemblyVersion>0.5.0</AssemblyVersion>
<InformationalVersion>0.5.0</InformationalVersion>
<FileVersion>0.5.0</FileVersion>
<Product>Umbraco CMS</Product>
<PackageId>Umbraco.Cms.Core</PackageId>
<Title>Umbraco CMS Core</Title>

View File

@@ -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)
{ }
/// <inheritdoc />
protected override string ItemKey => "Umbraco.Web.HybridUmbracoContextAccessor";
/// <summary>
/// Gets or sets the <see cref="UmbracoContext"/> object.
/// </summary>

View File

@@ -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

View File

@@ -80,6 +80,7 @@ _hostingEnvironment = hostingEnvironment;
{
db = _dbFactory.CreateDatabase();
_hasTable = db.HasTable(Cms.Core.Constants.DatabaseSchema.Tables.KeyValue);
if (!_hasTable)
{

View File

@@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8</LangVersion>
<RootNamespace>Umbraco.Cms.Infrastructure</RootNamespace>
<PackageId>Umbraco.Cms.Infrastructure</PackageId>
<Title>Umbraco CMS Infrastructure</Title>

View File

@@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<RootNamespace>Umbraco.Cms.Tests.Common</RootNamespace>
<PackageId>Umbraco.Cms.Tests</PackageId>
<Title>Umbraco CMS Test Tools</Title>

View File

@@ -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();

View File

@@ -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
{

View File

@@ -4,7 +4,6 @@
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<LangVersion>8</LangVersion>
<RootNamespace>Umbraco.Cms.Tests.Integration</RootNamespace>
</PropertyGroup>

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<IUmbracoRouteValuesFactory>(),
filter ?? Mock.Of<IRoutableDocumentFilter>(x => x.IsDocumentRequest(It.IsAny<string>()) == true),
Mock.Of<IDataProtectionProvider>(),
Mock.Of<IControllerActionSearcher>());
Mock.Of<IControllerActionSearcher>(),
Mock.Of<IEventAggregator>());
return transformer;
}

View File

@@ -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

View File

@@ -3,7 +3,6 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<OutputType>Library</OutputType>
<LangVersion>latest</LangVersion>
<RootNamespace>Umbraco.Cms.Web.BackOffice</RootNamespace>
<PackageId>Umbraco.Cms.Web.BackOffice</PackageId>
<Title>Umbraco CMS Back Office</Title>

View File

@@ -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
/// <summary>
/// Initializes a new instance of the <see cref="ProxyViewDataFeature"/> class.
/// </summary>
public ProxyViewDataFeature(ViewDataDictionary viewData) => ViewData = viewData;
public ProxyViewDataFeature(ViewDataDictionary viewData, ITempDataDictionary tempData)
{
ViewData = viewData;
TempData = tempData;
}
/// <summary>
/// Gets the <see cref="ViewDataDictionary"/>
/// </summary>
public ViewDataDictionary ViewData { get; }
/// <summary>
/// Gets the <see cref="ITempDataDictionary"/>
/// </summary>
public ITempDataDictionary TempData { get; }
}
}

View File

@@ -255,6 +255,7 @@ namespace Umbraco.Extensions
builder.Services.AddUnique<IProfilerHtml, WebProfilerHtml>();
builder.Services.AddUnique<IMacroRenderer, MacroRenderer>();
builder.Services.AddUnique<PartialViewMacroEngine>();
builder.Services.AddUnique<IMemberUserKeyProvider, MemberUserKeyProvider>();
// register the umbraco context factory
@@ -262,6 +263,7 @@ namespace Umbraco.Extensions
builder.Services.AddUnique<IUmbracoContextFactory, UmbracoContextFactory>();
builder.Services.AddUnique<IBackOfficeSecurityFactory, BackOfficeSecurityFactory>();
builder.Services.AddUnique<IBackOfficeSecurityAccessor, HybridBackofficeSecurityAccessor>();
builder.AddNotificationHandler<UmbracoRoutedRequest, UmbracoWebsiteSecurityFactory>();
builder.Services.AddUnique<IUmbracoWebsiteSecurityAccessor, HybridUmbracoWebsiteSecurityAccessor>();
var umbracoApiControllerTypes = builder.TypeLoader.GetUmbracoApiControllers().ToList();

View File

@@ -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<MacroContent> 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
/// <summary>
/// Renders a PartialView Macro.
/// </summary>
/// <returns>The text output of the macro execution.</returns>
private MacroContent ExecutePartialView(MacroModel macro, IPublishedContent content)
{
var engine = new PartialViewMacroEngine(_httpContextAccessor, _hostingEnvironment);
return engine.Execute(macro, content);
}
#endregion
#region Execution helpers

View File

@@ -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<IUmbracoContext> _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<IModelMetadataProvider>();
var tempDataProvider = httpContext.RequestServices.GetRequiredService<ITempDataProvider>();
// Check if there's proxied ViewData (i.e. returned from a SurfaceController)
ProxyViewDataFeature proxyViewDataFeature = httpContext.Features.Get<ProxyViewDataFeature>();
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
{
/// <inheritdoc />
public Task RenderAsync(ViewContext context)
{
return Task.CompletedTask;
}
public Task RenderAsync(ViewContext context) => Task.CompletedTask;
/// <inheritdoc />
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;
//}
}
}

View File

@@ -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<IViewComponentResult> 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;
}

View File

@@ -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
{

View File

@@ -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;
}

View File

@@ -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
/// <inheritdoc/>
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<RegisterMemberStatus> RegisterMemberAsync(RegisterModel model, bool logMemberIn = true)
{
throw new System.NotImplementedException();
throw new NotImplementedException();
}
/// <inheritdoc/>

View File

@@ -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
{
/// <summary>
/// Ensures that the <see cref="IUmbracoWebsiteSecurity"/> is populated on a front-end request
/// </summary>
internal sealed class UmbracoWebsiteSecurityFactory : INotificationHandler<UmbracoRoutedRequest>
{
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);
}
}
}
}

View File

@@ -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> 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<IModelMetadataProvider>();
var tempDataProvider = httpContext.RequestServices.GetRequiredService<ITempDataProvider>();
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

View File

@@ -3,7 +3,6 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<OutputType>Library</OutputType>
<LangVersion>latest</LangVersion>
<RootNamespace>Umbraco.Cms.Web.Common</RootNamespace>
<PackageId>Umbraco.Cms.Web.Common</PackageId>
<Title>Umbraco CMS Web</Title>

View File

@@ -3,7 +3,6 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>Umbraco.Cms.Web.UI.NetCore</RootNamespace>
<LangVersion>latest</LangVersion>
<RootNamespace>Umbraco.Cms.Web.UI.NetCore</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">

View File

@@ -98,7 +98,7 @@ namespace Umbraco.Cms.Web.Website.Controllers
/// </summary>
protected UmbracoPageResult CurrentUmbracoPage()
{
HttpContext.Features.Set(new ProxyViewDataFeature(ViewData));
HttpContext.Features.Set(new ProxyViewDataFeature(ViewData, TempData));
return new UmbracoPageResult(ProfilingLogger);
}
}

View File

@@ -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)
{

View File

@@ -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<IUmbracoRenderingDefaults, UmbracoRenderingDefaults>();
builder.Services.AddSingleton<IRoutableDocumentFilter, RoutableDocumentFilter>();
builder.Services.AddSingleton<FrontEndRoutes>();
builder.Services.AddSingleton<FrontEndRoutes>();
builder
.AddDistributedCache()

View File

@@ -165,16 +165,11 @@ namespace Umbraco.Extensions
return htmlHelper.ValidationSummary(excludePropertyErrors, message, htmlAttributes);
}
var htmlGenerator = GetRequiredService<IHtmlGenerator>(htmlHelper);
IHtmlGenerator htmlGenerator = GetRequiredService<IHtmlGenerator>(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,

View File

@@ -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;
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoRouteValueTransformer"/> 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;
}
/// <inheritdoc/>
@@ -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)

View File

@@ -3,7 +3,6 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<OutputType>Library</OutputType>
<LangVersion>latest</LangVersion>
<RootNamespace>Umbraco.Cms.Web.Website</RootNamespace>
<PackageId>Umbraco.Cms.Web.Website</PackageId>
<Title>Umbraco CMS Website</Title>