diff --git a/build/build-bootstrap.ps1 b/build/build-bootstrap.ps1
index 645f6c7d41..4c946ba289 100644
--- a/build/build-bootstrap.ps1
+++ b/build/build-bootstrap.ps1
@@ -23,7 +23,7 @@
$cache = 4
$nuget = "$scriptTemp\nuget.exe"
# ensure the correct NuGet-source is used. This one is used by Umbraco
- $nugetsourceUmbraco = "https://www.myget.org/F/umbracocore/api/v3/index.json"
+ $nugetsourceUmbraco = "https://www.myget.org/F/umbracoprereleases/api/v3/index.json"
if (-not $local)
{
$source = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
diff --git a/build/build.ps1 b/build/build.ps1
index ae59874e6a..07d856d075 100644
--- a/build/build.ps1
+++ b/build/build.ps1
@@ -51,12 +51,12 @@
{
param ( $semver )
- $release = "" + $semver.Major + "." + $semver.Minor + "." + $semver.Patch
-
- Write-Host "Update IIS Express port in csproj"
- $updater = New-Object "Umbraco.Build.ExpressPortUpdater"
- $csproj = "$($this.SolutionRoot)\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj"
- $updater.Update($csproj, $release)
+ $port = "" + $semver.Major + $semver.Minor + ("" + $semver.Patch).PadLeft(2, '0')
+ Write-Host "Update port in launchSettings.json to $port"
+ $filePath = "$($this.SolutionRoot)\src\Umbraco.Web.UI.NetCore\Properties\launchSettings.json"
+ $this.ReplaceFileText($filePath, `
+ "http://localhost:(\d+)?", `
+ "http://localhost:$port")
})
$ubuild.DefineMethod("SandboxNode",
diff --git a/build/templates/UmbracoSolution/UmbracoSolution.csproj b/build/templates/UmbracoSolution/UmbracoSolution.csproj
index 87271c3dd7..278720afef 100644
--- a/build/templates/UmbracoSolution/UmbracoSolution.csproj
+++ b/build/templates/UmbracoSolution/UmbracoSolution.csproj
@@ -26,6 +26,16 @@
+
+ true
+ PreserveNewest
+ Always
+
+
+ true
+ PreserveNewest
+ Always
+
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 23b3080ad2..cdce38df2f 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -1,12 +1,13 @@
-
+
- 0.5.0
- 0.5.0
- 0.5.0-beta001
- 0.5.0
+ 9.0.0
+ 9.0.0
+ 9.0.0-beta001
+ 9.0.0
9.0
en-US
Umbraco CMS
Copyright © Umbraco 2021
+
diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs
deleted file mode 100644
index dc2c74bebb..0000000000
--- a/src/SolutionInfo.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System.Reflection;
-using System.Resources;
-
-[assembly: AssemblyCompany("Umbraco")]
-[assembly: AssemblyCopyright("Copyright © Umbraco 2021")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-[assembly: NeutralResourcesLanguage("en-US")]
-
-// versions
-// read https://stackoverflow.com/questions/64602/what-are-differences-between-assemblyversion-assemblyfileversion-and-assemblyin
-
-// note: do NOT change anything here manually, use the build scripts
-
-// this is the ONLY ONE the CLR cares about for compatibility
-// should change ONLY when "hard" breaking compatibility (manual change)
-[assembly: AssemblyVersion("0.5.0")]
-
-// these are FYI and changed automatically
-[assembly: AssemblyFileVersion("0.5.0")]
-[assembly: AssemblyInformationalVersion("0.5.0-beta001")]
\ No newline at end of file
diff --git a/src/Umbraco.Core/IO/ViewHelper.cs b/src/Umbraco.Core/IO/ViewHelper.cs
index df1a87f6c6..9a7016b6be 100644
--- a/src/Umbraco.Core/IO/ViewHelper.cs
+++ b/src/Umbraco.Core/IO/ViewHelper.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.IO;
using System.Linq;
using System.Text;
diff --git a/src/Umbraco.Core/Models/ContentModel.cs b/src/Umbraco.Core/Models/ContentModel.cs
index 0aa123f030..e62f51fd16 100644
--- a/src/Umbraco.Core/Models/ContentModel.cs
+++ b/src/Umbraco.Core/Models/ContentModel.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using Umbraco.Cms.Core.Models.PublishedContent;
namespace Umbraco.Cms.Core.Models
@@ -11,12 +11,7 @@ namespace Umbraco.Cms.Core.Models
///
/// Initializes a new instance of the class with a content.
///
- ///
- public ContentModel(IPublishedContent content)
- {
- if (content == null) throw new ArgumentNullException(nameof(content));
- Content = content;
- }
+ public ContentModel(IPublishedContent content) => Content = content ?? throw new ArgumentNullException(nameof(content));
///
/// Gets the content.
diff --git a/src/Umbraco.Core/Models/ContentModelOfTContent.cs b/src/Umbraco.Core/Models/ContentModelOfTContent.cs
index 7b07b08847..ab882342b5 100644
--- a/src/Umbraco.Core/Models/ContentModelOfTContent.cs
+++ b/src/Umbraco.Core/Models/ContentModelOfTContent.cs
@@ -1,4 +1,4 @@
-using Umbraco.Cms.Core.Models.PublishedContent;
+using Umbraco.Cms.Core.Models.PublishedContent;
namespace Umbraco.Cms.Core.Models
{
@@ -8,12 +8,8 @@ namespace Umbraco.Cms.Core.Models
///
/// Initializes a new instance of the class with a content.
///
- ///
public ContentModel(TContent content)
- : base(content)
- {
- Content = content;
- }
+ : base(content) => Content = content;
///
/// Gets the content.
diff --git a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs
index b225f2b051..acab6caae4 100644
--- a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs
+++ b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -33,7 +33,7 @@ namespace Umbraco.Core.Services.Implement
private readonly GlobalSettings _globalSettings;
private readonly IHostingEnvironment _hostingEnvironment;
- private const string PartialViewHeader = "@inherits Umbraco.Web.Common.AspNetCore.UmbracoViewPage";
+ private const string PartialViewHeader = "@inherits Umbraco.Web.Common.Views.UmbracoViewPage";
private const string PartialViewMacroHeader = "@inherits Umbraco.Web.Common.Macros.PartialViewMacroPage";
public FileService(IScopeProvider uowProvider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory,
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/ViewHelperTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/ViewHelperTests.cs
index 5e8c6ce3fc..186e75b2f5 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/ViewHelperTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/ViewHelperTests.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Umbraco.
+// Copyright (c) Umbraco.
// See LICENSE for more details.
using NUnit.Framework;
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/ControllerActionSearcherTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/ControllerActionSearcherTests.cs
index 9728fd7195..c0ed21e1f7 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/ControllerActionSearcherTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/ControllerActionSearcherTests.cs
@@ -49,7 +49,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Website.Routing
private class Render2Controller : RenderController
{
- public Render2Controller(ILogger logger, ICompositeViewEngine compositeViewEngine, IUmbracoContextAccessor umbracoContextAccessor)
+ public Render2Controller(ILogger logger, ICompositeViewEngine compositeViewEngine, IUmbracoContextAccessor umbracoContextAccessor)
: base(logger, compositeViewEngine, umbracoContextAccessor)
{
}
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 d2e2597415..4d74ea6427 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformerTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformerTests.cs
@@ -189,7 +189,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Website.Routing
private class TestController : RenderController
{
- public TestController(ILogger logger, ICompositeViewEngine compositeViewEngine, IUmbracoContextAccessor umbracoContextAccessor)
+ public TestController(ILogger logger, ICompositeViewEngine compositeViewEngine, IUmbracoContextAccessor umbracoContextAccessor)
: base(logger, compositeViewEngine, umbracoContextAccessor)
{
}
diff --git a/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs
index 3877f9a0e2..84f2b5f574 100644
--- a/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs
@@ -35,14 +35,15 @@ namespace Umbraco.Cms.Web.BackOffice.Trees
private readonly IControllerFactory _controllerFactory;
private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;
-
+ ///
+ /// Initializes a new instance of the class.
+ ///
public ApplicationTreeController(
ITreeService treeService,
ISectionService sectionService,
ILocalizedTextService localizedTextService,
IControllerFactory controllerFactory,
- IActionDescriptorCollectionProvider actionDescriptorCollectionProvider
- )
+ IActionDescriptorCollectionProvider actionDescriptorCollectionProvider)
{
_treeService = treeService;
_sectionService = sectionService;
@@ -56,28 +57,31 @@ namespace Umbraco.Cms.Web.BackOffice.Trees
///
/// The application to load tree for
/// An optional single tree alias, if specified will only load the single tree for the request app
- ///
+ /// The query strings
/// Tree use.
- ///
public async Task> GetApplicationTrees(string application, string tree, [ModelBinder(typeof(HttpQueryStringModelBinder))] FormCollection queryStrings, TreeUse use = TreeUse.Main)
{
application = application.CleanForXss();
if (string.IsNullOrEmpty(application))
+ {
return NotFound();
+ }
var section = _sectionService.GetByAlias(application);
if (section == null)
+ {
return NotFound();
+ }
- //find all tree definitions that have the current application alias
+ // find all tree definitions that have the current application alias
var groupedTrees = _treeService.GetBySectionGrouped(application, use);
var allTrees = groupedTrees.Values.SelectMany(x => x).ToList();
if (allTrees.Count == 0)
{
- //if there are no trees defined for this section but the section is defined then we can have a simple
- //full screen section without trees
+ // if there are no trees defined for this section but the section is defined then we can have a simple
+ // full screen section without trees
var name = _localizedTextService.Localize("sections/" + application);
return TreeRootNode.CreateSingleTreeRoot(Constants.System.RootString, null, null, name, TreeNodeCollection.Empty, true);
}
@@ -90,7 +94,9 @@ namespace Umbraco.Cms.Web.BackOffice.Trees
: allTrees.FirstOrDefault(x => x.TreeAlias == tree);
if (t == null)
+ {
return NotFound();
+ }
var treeRootNode = await GetTreeRootNode(t, Constants.System.Root, queryStrings);
@@ -114,9 +120,12 @@ namespace Umbraco.Cms.Web.BackOffice.Trees
{
return nodeResult.Result;
}
+
var node = nodeResult.Value;
if (node != null)
+ {
nodes.Add(node);
+ }
}
var name = _localizedTextService.Localize("sections/" + application);
@@ -148,11 +157,15 @@ namespace Umbraco.Cms.Web.BackOffice.Trees
var node = nodeResult.Value;
if (node != null)
+ {
nodes.Add(node);
+ }
}
if (nodes.Count == 0)
+ {
continue;
+ }
// no name => third party
// use localization key treeHeaders/thirdPartyGroup
@@ -179,7 +192,9 @@ namespace Umbraco.Cms.Web.BackOffice.Trees
private async Task> TryGetRootNode(Tree tree, FormCollection querystring)
{
if (tree == null)
+ {
throw new ArgumentNullException(nameof(tree));
+ }
return await GetRootNode(tree, querystring);
}
@@ -190,7 +205,9 @@ namespace Umbraco.Cms.Web.BackOffice.Trees
private async Task> GetTreeRootNode(Tree tree, int id, FormCollection querystring)
{
if (tree == null)
+ {
throw new ArgumentNullException(nameof(tree));
+ }
var childrenResult = await GetChildren(tree, id, querystring);
if (!(childrenResult.Result is null))
@@ -222,7 +239,9 @@ namespace Umbraco.Cms.Web.BackOffice.Trees
sectionRoot.Path = rootNode.Path;
foreach (var d in rootNode.AdditionalData)
+ {
sectionRoot.AdditionalData[d.Key] = d.Value;
+ }
return sectionRoot;
}
@@ -233,7 +252,9 @@ namespace Umbraco.Cms.Web.BackOffice.Trees
private async Task> GetRootNode(Tree tree, FormCollection querystring)
{
if (tree == null)
+ {
throw new ArgumentNullException(nameof(tree));
+ }
var result = await GetApiControllerProxy(tree.TreeControllerType, "GetRootNode", querystring);
@@ -253,7 +274,10 @@ namespace Umbraco.Cms.Web.BackOffice.Trees
var rootNode = rootNodeResult.Value;
if (rootNode == null)
+ {
throw new InvalidOperationException($"Failed to get root node for tree \"{tree.TreeAlias}\".");
+ }
+
return rootNode;
}
@@ -263,7 +287,9 @@ namespace Umbraco.Cms.Web.BackOffice.Trees
private async Task> GetChildren(Tree tree, int id, FormCollection querystring)
{
if (tree == null)
+ {
throw new ArgumentNullException(nameof(tree));
+ }
// the method we proxy has an 'id' parameter which is *not* in the querystring,
// we need to add it for the proxy to work (else, it does not find the method,
@@ -299,7 +325,8 @@ namespace Umbraco.Cms.Web.BackOffice.Trees
// note: this is all required in order to execute the auth-filters for the sub request, we
// need to "trick" mvc into thinking that it is actually executing the proxied controller.
- var controllerName = controllerType.Name.Substring(0, controllerType.Name.Length - 10); // remove controller part of name;
+ var controllerName = ControllerExtensions.GetControllerName(controllerType);
+
// create proxy route data specifying the action & controller to execute
var routeData = new RouteData(new RouteValueDictionary()
{
@@ -324,9 +351,12 @@ namespace Umbraco.Cms.Web.BackOffice.Trees
var proxyControllerContext = new ControllerContext(actionContext);
var controller = (TreeController)_controllerFactory.CreateController(proxyControllerContext);
+ // TODO: What about other filters? Will they execute?
var isAllowed = await controller.ControllerContext.InvokeAuthorizationFiltersForRequest(actionContext);
if (!isAllowed)
+ {
return Forbid();
+ }
return controller;
}
diff --git a/src/Umbraco.Web.Common/ApplicationModels/BackOfficeApplicationModelProvider.cs b/src/Umbraco.Web.Common/ApplicationModels/BackOfficeApplicationModelProvider.cs
index 7a14dee606..146edb19e9 100644
--- a/src/Umbraco.Web.Common/ApplicationModels/BackOfficeApplicationModelProvider.cs
+++ b/src/Umbraco.Web.Common/ApplicationModels/BackOfficeApplicationModelProvider.cs
@@ -6,6 +6,7 @@ using Umbraco.Cms.Web.Common.Attributes;
namespace Umbraco.Cms.Web.Common.ApplicationModels
{
+
// TODO: This should just exist in the back office project
///
@@ -13,45 +14,43 @@ namespace Umbraco.Cms.Web.Common.ApplicationModels
///
public class BackOfficeApplicationModelProvider : IApplicationModelProvider
{
- public BackOfficeApplicationModelProvider(IModelMetadataProvider modelMetadataProvider)
+ private readonly List _actionModelConventions = new List()
{
- ActionModelConventions = new List()
- {
- new BackOfficeIdentityCultureConvention()
- };
- }
+ new BackOfficeIdentityCultureConvention()
+ };
+ ///
///
/// Will execute after
///
public int Order => 0;
- public List ActionModelConventions { get; }
-
+ ///
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
}
+ ///
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
- foreach (var controller in context.Result.Controllers)
+ foreach (ControllerModel controller in context.Result.Controllers)
{
if (!IsBackOfficeController(controller))
- continue;
-
- foreach (var action in controller.Actions)
{
- foreach (var convention in ActionModelConventions)
+ continue;
+ }
+
+ foreach (ActionModel action in controller.Actions)
+ {
+ foreach (IActionModelConvention convention in _actionModelConventions)
{
convention.Apply(action);
}
}
-
}
}
private bool IsBackOfficeController(ControllerModel controller)
=> controller.Attributes.OfType().Any();
-
}
}
diff --git a/src/Umbraco.Web.Common/ApplicationModels/BackOfficeIdentityCultureConvention.cs b/src/Umbraco.Web.Common/ApplicationModels/BackOfficeIdentityCultureConvention.cs
index 537ae58e89..8414662816 100644
--- a/src/Umbraco.Web.Common/ApplicationModels/BackOfficeIdentityCultureConvention.cs
+++ b/src/Umbraco.Web.Common/ApplicationModels/BackOfficeIdentityCultureConvention.cs
@@ -8,9 +8,7 @@ namespace Umbraco.Cms.Web.Common.ApplicationModels
public class BackOfficeIdentityCultureConvention : IActionModelConvention
{
- public void Apply(ActionModel action)
- {
- action.Filters.Add(new BackOfficeCultureFilter());
- }
+ ///
+ public void Apply(ActionModel action) => action.Filters.Add(new BackOfficeCultureFilter());
}
}
diff --git a/src/Umbraco.Web.Common/ApplicationModels/UmbracoApiBehaviorApplicationModelProvider.cs b/src/Umbraco.Web.Common/ApplicationModels/UmbracoApiBehaviorApplicationModelProvider.cs
index 118603ced9..b80104a7bf 100644
--- a/src/Umbraco.Web.Common/ApplicationModels/UmbracoApiBehaviorApplicationModelProvider.cs
+++ b/src/Umbraco.Web.Common/ApplicationModels/UmbracoApiBehaviorApplicationModelProvider.cs
@@ -26,13 +26,18 @@ namespace Umbraco.Cms.Web.Common.ApplicationModels
///
public class UmbracoApiBehaviorApplicationModelProvider : IApplicationModelProvider
{
+ private readonly List _actionModelConventions;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
public UmbracoApiBehaviorApplicationModelProvider(IModelMetadataProvider modelMetadataProvider)
{
// see see https://docs.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-3.1#apicontroller-attribute
// for what these things actually do
// NOTE: we don't have attribute routing requirements and we cannot use ApiVisibilityConvention without attribute routing
- ActionModelConventions = new List()
+ _actionModelConventions = new List()
{
new ClientErrorResultFilterConvention(), // Ensures the responses without any body is converted into a simple json object with info instead of a string like "Status Code: 404; Not Found"
new ConsumesConstraintForFormFileParameterConvention(), // If an controller accepts files, it must accept multipart/form-data.
@@ -46,32 +51,33 @@ namespace Umbraco.Cms.Web.Common.ApplicationModels
// TODO: Need to determine exactly how this affects errors
var defaultErrorType = typeof(ProblemDetails);
var defaultErrorTypeAttribute = new ProducesErrorResponseTypeAttribute(defaultErrorType);
- ActionModelConventions.Add(new ApiConventionApplicationModelConvention(defaultErrorTypeAttribute));
+ _actionModelConventions.Add(new ApiConventionApplicationModelConvention(defaultErrorTypeAttribute));
}
+ ///
///
/// Will execute after
///
public int Order => 0;
- public List ActionModelConventions { get; }
-
+ ///
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
}
+ ///
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
foreach (var controller in context.Result.Controllers)
{
if (!IsUmbracoApiController(controller))
+ {
continue;
-
-
+ }
foreach (var action in controller.Actions)
{
- foreach (var convention in ActionModelConventions)
+ foreach (var convention in _actionModelConventions)
{
convention.Apply(action);
}
diff --git a/src/Umbraco.Web.Common/ApplicationModels/UmbracoJsonModelBinderConvention.cs b/src/Umbraco.Web.Common/ApplicationModels/UmbracoJsonModelBinderConvention.cs
index e5f4f1c307..e96bda8771 100644
--- a/src/Umbraco.Web.Common/ApplicationModels/UmbracoJsonModelBinderConvention.cs
+++ b/src/Umbraco.Web.Common/ApplicationModels/UmbracoJsonModelBinderConvention.cs
@@ -1,4 +1,4 @@
-using System.Linq;
+using System.Linq;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Umbraco.Cms.Web.Common.ModelBinders;
@@ -13,14 +13,13 @@ namespace Umbraco.Cms.Web.Common.ApplicationModels
///
public class UmbracoJsonModelBinderConvention : IActionModelConvention
{
+ ///
public void Apply(ActionModel action)
{
- foreach (var p in action.Parameters.Where(p => p.BindingInfo?.BindingSource == BindingSource.Body))
+ foreach (ParameterModel p in action.Parameters.Where(p => p.BindingInfo?.BindingSource == BindingSource.Body))
{
p.BindingInfo.BinderType = typeof(UmbracoJsonModelBinder);
}
}
}
-
-
}
diff --git a/src/Umbraco.Web.Common/ApplicationModels/VirtualPageApplicationModelProvider.cs b/src/Umbraco.Web.Common/ApplicationModels/VirtualPageApplicationModelProvider.cs
new file mode 100644
index 0000000000..62867d045b
--- /dev/null
+++ b/src/Umbraco.Web.Common/ApplicationModels/VirtualPageApplicationModelProvider.cs
@@ -0,0 +1,61 @@
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.AspNetCore.Mvc.ApplicationModels;
+using Umbraco.Core;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Web.Common.Controllers;
+
+namespace Umbraco.Web.Common.ApplicationModels
+{
+ ///
+ /// Applies the to any action on a controller that is
+ ///
+ public class VirtualPageApplicationModelProvider : IApplicationModelProvider
+ {
+ private readonly List _actionModelConventions = new List()
+ {
+ new VirtualPageConvention()
+ };
+
+ ///
+ ///
+ /// Will execute after
+ ///
+ public int Order => 0;
+
+ ///
+ public void OnProvidersExecuted(ApplicationModelProviderContext context) { }
+
+ ///
+ public void OnProvidersExecuting(ApplicationModelProviderContext context)
+ {
+ foreach (ControllerModel controller in context.Result.Controllers)
+ {
+ if (!IsVirtualPageController(controller))
+ {
+ continue;
+ }
+
+ foreach (ActionModel action in controller.Actions.ToList())
+ {
+ if (action.ActionName == nameof(IVirtualPageController.FindContent)
+ && action.ActionMethod.ReturnType == typeof(IPublishedContent))
+ {
+ // this is not an action, it's just the implementation of IVirtualPageController
+ controller.Actions.Remove(action);
+ }
+ else
+ {
+ foreach (IActionModelConvention convention in _actionModelConventions)
+ {
+ convention.Apply(action);
+ }
+ }
+ }
+ }
+ }
+
+ private bool IsVirtualPageController(ControllerModel controller)
+ => controller.ControllerType.Implements();
+ }
+}
diff --git a/src/Umbraco.Web.Common/ApplicationModels/VirtualPageConvention.cs b/src/Umbraco.Web.Common/ApplicationModels/VirtualPageConvention.cs
new file mode 100644
index 0000000000..d35af70bb0
--- /dev/null
+++ b/src/Umbraco.Web.Common/ApplicationModels/VirtualPageConvention.cs
@@ -0,0 +1,16 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ApplicationModels;
+using Umbraco.Web.Common.Controllers;
+using Umbraco.Web.Common.Filters;
+
+namespace Umbraco.Web.Common.ApplicationModels
+{
+ ///
+ /// Adds the as a convention
+ ///
+ public class VirtualPageConvention : IActionModelConvention
+ {
+ ///
+ public void Apply(ActionModel action) => action.Filters.Add(new UmbracoVirtualPageFilterAttribute());
+ }
+}
diff --git a/src/Umbraco.Web.Common/Controllers/IVirtualPageController.cs b/src/Umbraco.Web.Common/Controllers/IVirtualPageController.cs
new file mode 100644
index 0000000000..bfea5c8d87
--- /dev/null
+++ b/src/Umbraco.Web.Common/Controllers/IVirtualPageController.cs
@@ -0,0 +1,16 @@
+using Microsoft.AspNetCore.Mvc.Filters;
+using Umbraco.Core.Models.PublishedContent;
+
+namespace Umbraco.Web.Common.Controllers
+{
+ ///
+ /// Used for custom routed controllers to execute within the context of Umbraco
+ ///
+ public interface IVirtualPageController
+ {
+ ///
+ /// Returns the to use as the current page for the request
+ ///
+ IPublishedContent FindContent(ActionExecutingContext actionExecutingContext);
+ }
+}
diff --git a/src/Umbraco.Web.Common/Controllers/RenderController.cs b/src/Umbraco.Web.Common/Controllers/RenderController.cs
index de9e51d145..5fe8adb174 100644
--- a/src/Umbraco.Web.Common/Controllers/RenderController.cs
+++ b/src/Umbraco.Web.Common/Controllers/RenderController.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
@@ -15,107 +14,32 @@ using Umbraco.Cms.Web.Common.Routing;
namespace Umbraco.Cms.Web.Common.Controllers
{
+
///
/// Represents the default front-end rendering controller.
///
[ModelBindingException]
[PublishedRequestFilter]
- public class RenderController : UmbracoController, IRenderController
+ public class RenderController : UmbracoPageController, IRenderController
{
private readonly ILogger _logger;
- private readonly ICompositeViewEngine _compositeViewEngine;
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
- private UmbracoRouteValues _umbracoRouteValues;
///
/// Initializes a new instance of the class.
///
public RenderController(ILogger logger, ICompositeViewEngine compositeViewEngine, IUmbracoContextAccessor umbracoContextAccessor)
+ : base(logger, compositeViewEngine)
{
_logger = logger;
- _compositeViewEngine = compositeViewEngine;
_umbracoContextAccessor = umbracoContextAccessor;
}
- ///
- /// Gets the current content item.
- ///
- protected IPublishedContent CurrentPage
- {
- get
- {
- if (!UmbracoRouteValues.PublishedRequest.HasPublishedContent())
- {
- // This will never be accessed this way since the controller will handle redirects and not founds
- // before this can be accessed but we need to be explicit.
- throw new InvalidOperationException("There is no published content found in the request");
- }
-
- return UmbracoRouteValues.PublishedRequest.PublishedContent;
- }
- }
-
///
/// Gets the umbraco context
///
protected IUmbracoContext UmbracoContext => _umbracoContextAccessor.UmbracoContext;
- ///
- /// Gets the
- ///
- protected UmbracoRouteValues UmbracoRouteValues
- {
- get
- {
- if (_umbracoRouteValues != null)
- {
- return _umbracoRouteValues;
- }
-
- _umbracoRouteValues = HttpContext.Features.Get();
-
- if (_umbracoRouteValues == null)
- {
- throw new InvalidOperationException($"No {nameof(UmbracoRouteValues)} feature was found in the HttpContext");
- }
-
- return _umbracoRouteValues;
- }
- }
-
- ///
- /// Ensures that a physical view file exists on disk.
- ///
- /// The view name.
- protected bool EnsurePhsyicalViewExists(string template)
- {
- ViewEngineResult result = _compositeViewEngine.FindView(ControllerContext, template, false);
- if (result.View != null)
- {
- return true;
- }
-
- _logger.LogWarning("No physical template file was found for template {Template}", template);
- return false;
- }
-
- ///
- /// Gets an action result based on the template name found in the route values and a model.
- ///
- /// The type of the model.
- /// The model.
- /// The action result.
- /// If the template found in the route values doesn't physically exist and exception is thrown
- protected IActionResult CurrentTemplate(T model)
- {
- if (EnsurePhsyicalViewExists(UmbracoRouteValues.TemplateName) == false)
- {
- throw new InvalidOperationException("No physical template file was found for template " + UmbracoRouteValues.TemplateName);
- }
-
- return View(UmbracoRouteValues.TemplateName, model);
- }
-
///
/// The default action to render the front-end view.
///
diff --git a/src/Umbraco.Web.Common/Controllers/UmbracoPageController.cs b/src/Umbraco.Web.Common/Controllers/UmbracoPageController.cs
new file mode 100644
index 0000000000..33fa4ca53e
--- /dev/null
+++ b/src/Umbraco.Web.Common/Controllers/UmbracoPageController.cs
@@ -0,0 +1,104 @@
+using System;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ViewEngines;
+using Microsoft.Extensions.Logging;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Web.Common.Routing;
+using Umbraco.Web.Routing;
+
+namespace Umbraco.Web.Common.Controllers
+{
+ ///
+ /// An abstract controller for a front-end Umbraco page
+ ///
+ public abstract class UmbracoPageController : UmbracoController
+ {
+ private UmbracoRouteValues _umbracoRouteValues;
+ private readonly ICompositeViewEngine _compositeViewEngine;
+ private readonly ILogger _logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected UmbracoPageController(ILogger logger, ICompositeViewEngine compositeViewEngine)
+ {
+ _logger = logger;
+ _compositeViewEngine = compositeViewEngine;
+ }
+
+ ///
+ /// Gets the
+ ///
+ protected virtual UmbracoRouteValues UmbracoRouteValues
+ {
+ get
+ {
+ if (_umbracoRouteValues != null)
+ {
+ return _umbracoRouteValues;
+ }
+
+ _umbracoRouteValues = HttpContext.Features.Get();
+
+ if (_umbracoRouteValues == null)
+ {
+ throw new InvalidOperationException($"No {nameof(UmbracoRouteValues)} feature was found in the HttpContext");
+ }
+
+ return _umbracoRouteValues;
+ }
+ }
+
+ ///
+ /// Gets the current content item.
+ ///
+ protected virtual IPublishedContent CurrentPage
+ {
+ get
+ {
+ if (!UmbracoRouteValues.PublishedRequest.HasPublishedContent())
+ {
+ // This will never be accessed this way since the controller will handle redirects and not founds
+ // before this can be accessed but we need to be explicit.
+ throw new InvalidOperationException("There is no published content found in the request");
+ }
+
+ return UmbracoRouteValues.PublishedRequest.PublishedContent;
+ }
+ }
+
+ ///
+ /// Gets an action result based on the template name found in the route values and a model.
+ ///
+ /// The type of the model.
+ /// The model.
+ /// The action result.
+ /// If the template found in the route values doesn't physically exist and exception is thrown
+ protected IActionResult CurrentTemplate(T model)
+ {
+ if (EnsurePhsyicalViewExists(UmbracoRouteValues.TemplateName) == false)
+ {
+ throw new InvalidOperationException("No physical template file was found for template " + UmbracoRouteValues.TemplateName);
+ }
+
+ return View(UmbracoRouteValues.TemplateName, model);
+ }
+
+ ///
+ /// Ensures that a physical view file exists on disk.
+ ///
+ /// The view name.
+ protected bool EnsurePhsyicalViewExists(string template)
+ {
+ ViewEngineResult result = _compositeViewEngine.FindView(ControllerContext, template, false);
+ if (result.View != null)
+ {
+ return true;
+ }
+
+ _logger.LogWarning("No physical template file was found for template {Template}", template);
+ return false;
+ }
+
+ }
+}
diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs
index 067c55225f..e80096dcbe 100644
--- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs
+++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs
@@ -57,6 +57,7 @@ using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment;
namespace Umbraco.Extensions
{
+
// TODO: We could add parameters to configure each of these for flexibility
///
@@ -102,6 +103,10 @@ namespace Umbraco.Extensions
ILoggerFactory loggerFactory = LoggerFactory.Create(cfg => cfg.AddSerilog(Log.Logger, false));
TypeLoader typeLoader = services.AddTypeLoader(Assembly.GetEntryAssembly(), webHostEnvironment, tempHostingEnvironment, loggerFactory, appCaches, config, profiler);
+ // adds the umbraco startup filter which will call UseUmbraco early on before
+ // other start filters are applied (depending on the ordering of IStartupFilters in DI).
+ services.AddTransient();
+
return new UmbracoBuilder(services, config, typeLoader, loggerFactory);
}
@@ -228,6 +233,7 @@ namespace Umbraco.Extensions
builder.Services.ConfigureOptions();
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient());
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient());
+ builder.Services.TryAddEnumerable(ServiceDescriptor.Transient());
builder.Services.AddUmbracoImageSharp(builder.Config);
// AspNetCore specific services
diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoStartupFilter.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoStartupFilter.cs
new file mode 100644
index 0000000000..008f8b0b35
--- /dev/null
+++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoStartupFilter.cs
@@ -0,0 +1,22 @@
+using System;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Umbraco.Extensions;
+
+namespace Umbraco.Web.Common.DependencyInjection
+{
+ ///
+ /// A registered early in DI so that it executes before any user IStartupFilters
+ /// to ensure that all Umbraco service and requirements are started correctly and in order.
+ ///
+ public sealed class UmbracoStartupFilter : IStartupFilter
+ {
+ ///
+ public Action Configure(Action next) =>
+ app =>
+ {
+ app.UseUmbraco();
+ next(app);
+ };
+ }
+}
diff --git a/src/Umbraco.Web.Common/Extensions/ControllerActionEndpointConventionBuilderExtensions.cs b/src/Umbraco.Web.Common/Extensions/ControllerActionEndpointConventionBuilderExtensions.cs
new file mode 100644
index 0000000000..d30436c87b
--- /dev/null
+++ b/src/Umbraco.Web.Common/Extensions/ControllerActionEndpointConventionBuilderExtensions.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.Routing;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Web.Common.Filters;
+
+namespace Umbraco.Web.Common.Extensions
+{
+ public static class ControllerActionEndpointConventionBuilderExtensions
+ {
+ ///
+ /// Allows for defining a callback to set the returned for the current request for this route
+ ///
+ public static void ForUmbracoPage(
+ this ControllerActionEndpointConventionBuilder builder,
+ Func findContent)
+ => builder.Add(convention =>
+ {
+ // filter out matched endpoints that are suppressed
+ if (convention.Metadata.OfType().FirstOrDefault()?.SuppressMatching != true)
+ {
+ // Get the controller action descriptor
+ ControllerActionDescriptor actionDescriptor = convention.Metadata.OfType().FirstOrDefault();
+ if (actionDescriptor != null)
+ {
+ // This is more or less like the IApplicationModelProvider, it allows us to add filters, etc... to the ControllerActionDescriptor
+ // dynamically. Here we will add our custom virtual page filter along with a callback in the endpoint's metadata
+ // to execute in order to find the IPublishedContent for the request.
+
+ var filter = new UmbracoVirtualPageFilterAttribute();
+
+ // Check if this already contains this filter since we don't want it applied twice.
+ // This could occur if the controller being routed is IVirtualPageController AND
+ // is being routed with ForUmbracoPage. In that case, ForUmbracoPage wins
+ // because the UmbracoVirtualPageFilterAttribute will check for the metadata first since
+ // that is more explicit and flexible in case the same controller is routed multiple times.
+ if (!actionDescriptor.FilterDescriptors.Any(x => x.Filter is UmbracoVirtualPageFilterAttribute))
+ {
+ actionDescriptor.FilterDescriptors.Add(new FilterDescriptor(filter, 0));
+ convention.Metadata.Add(filter);
+ }
+
+ convention.Metadata.Add(new CustomRouteContentFinderDelegate(findContent));
+ }
+ }
+ });
+ }
+}
diff --git a/src/Umbraco.Web.Common/Filters/UmbracoVirtualPageFilterAttribute.cs b/src/Umbraco.Web.Common/Filters/UmbracoVirtualPageFilterAttribute.cs
new file mode 100644
index 0000000000..988608c2c2
--- /dev/null
+++ b/src/Umbraco.Web.Common/Filters/UmbracoVirtualPageFilterAttribute.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.Extensions.DependencyInjection;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Web.Common.Controllers;
+using Umbraco.Web.Common.Extensions;
+using Umbraco.Web.Common.Routing;
+using Umbraco.Web.Routing;
+
+namespace Umbraco.Web.Common.Filters
+{
+ ///
+ /// Used to set the request feature based on the specified (if any)
+ /// for the custom route.
+ ///
+ public class UmbracoVirtualPageFilterAttribute : Attribute, IAsyncActionFilter
+ {
+ ///
+ public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
+ {
+ Endpoint endpoint = context.HttpContext.GetEndpoint();
+
+ // Check if there is any delegate in the metadata of the route, this
+ // will occur when using the ForUmbraco method during routing.
+ CustomRouteContentFinderDelegate contentFinder = endpoint.Metadata.OfType().FirstOrDefault();
+
+ if (contentFinder != null)
+ {
+ await SetUmbracoRouteValues(context, contentFinder.FindContent(context));
+ }
+ else
+ {
+ // Check if the controller is IVirtualPageController and then use that to FindContent
+ if (context.Controller is IVirtualPageController ctrl)
+ {
+ await SetUmbracoRouteValues(context, ctrl.FindContent(context));
+ }
+ }
+
+ // if we've assigned not found, just exit
+ if (!(context.Result is NotFoundResult))
+ {
+ await next();
+ }
+ }
+
+ private async Task SetUmbracoRouteValues(ActionExecutingContext context, IPublishedContent content)
+ {
+ if (content != null)
+ {
+ IUmbracoContextAccessor umbracoContext = context.HttpContext.RequestServices.GetRequiredService();
+ IPublishedRouter router = context.HttpContext.RequestServices.GetRequiredService();
+ IPublishedRequestBuilder requestBuilder = await router.CreateRequestAsync(umbracoContext.UmbracoContext.CleanedUmbracoUrl);
+ requestBuilder.SetPublishedContent(content);
+ IPublishedRequest publishedRequest = requestBuilder.Build();
+
+ var routeValues = new UmbracoRouteValues(
+ publishedRequest,
+ (ControllerActionDescriptor)context.ActionDescriptor);
+
+ context.HttpContext.Features.Set(routeValues);
+ }
+ else
+ {
+ // if there is no content then it should be a not found
+ context.Result = new NotFoundResult();
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Web.Common/Macros/PartialViewMacroPage.cs b/src/Umbraco.Web.Common/Macros/PartialViewMacroPage.cs
index 82a7a7b036..2c8a77bd05 100644
--- a/src/Umbraco.Web.Common/Macros/PartialViewMacroPage.cs
+++ b/src/Umbraco.Web.Common/Macros/PartialViewMacroPage.cs
@@ -1,4 +1,4 @@
-using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Web.Common.AspNetCore;
namespace Umbraco.Cms.Web.Common.Macros
diff --git a/src/Umbraco.Web.Common/Routing/CustomRouteContentFinderDelegate.cs b/src/Umbraco.Web.Common/Routing/CustomRouteContentFinderDelegate.cs
new file mode 100644
index 0000000000..5be3b4b952
--- /dev/null
+++ b/src/Umbraco.Web.Common/Routing/CustomRouteContentFinderDelegate.cs
@@ -0,0 +1,16 @@
+using System;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Umbraco.Core.Models.PublishedContent;
+
+namespace Umbraco.Web.Common.Extensions
+{
+ internal class CustomRouteContentFinderDelegate
+ {
+ private readonly Func _findContent;
+
+ public CustomRouteContentFinderDelegate(Func findContent) => _findContent = findContent;
+
+ public IPublishedContent FindContent(ActionExecutingContext actionExecutingContext) => _findContent(actionExecutingContext);
+ }
+}
diff --git a/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs b/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs
index 33d08ae252..a4311b988c 100644
--- a/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs
+++ b/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs
@@ -177,7 +177,19 @@ namespace Umbraco.Cms.Web.Common.Routing
// We don't want to include dynamic endpoints in this check since we would have no idea if that
// matches since they will probably match everything.
bool isDynamic = x.Metadata.OfType().Any(x => x.IsDynamic);
- return !isDynamic;
+ if (isDynamic)
+ {
+ return false;
+ }
+
+ // filter out matched endpoints that are suppressed
+ var isSuppressed = x.Metadata.OfType().FirstOrDefault()?.SuppressMatching == true;
+ if (isSuppressed)
+ {
+ return false;
+ }
+
+ return true;
});
var routeValues = new RouteValueDictionary();
diff --git a/src/Umbraco.Web.Common/Routing/UmbracoRouteValues.cs b/src/Umbraco.Web.Common/Routing/UmbracoRouteValues.cs
index 64fd998065..6088f21931 100644
--- a/src/Umbraco.Web.Common/Routing/UmbracoRouteValues.cs
+++ b/src/Umbraco.Web.Common/Routing/UmbracoRouteValues.cs
@@ -21,12 +21,10 @@ namespace Umbraco.Cms.Web.Common.Routing
public UmbracoRouteValues(
IPublishedRequest publishedRequest,
ControllerActionDescriptor controllerActionDescriptor,
- string templateName = null,
- bool hasHijackedRoute = false)
+ string templateName = null)
{
PublishedRequest = publishedRequest;
ControllerActionDescriptor = controllerActionDescriptor;
- HasHijackedRoute = hasHijackedRoute;
TemplateName = templateName;
}
@@ -59,10 +57,5 @@ namespace Umbraco.Cms.Web.Common.Routing
/// Gets the
///
public IPublishedRequest PublishedRequest { get; }
-
- ///
- /// Gets a value indicating whether the current request has a hijacked route/user controller routed for it
- ///
- public bool HasHijackedRoute { get; }
}
}
diff --git a/src/Umbraco.Web.Common/AspNetCore/UmbracoViewPage.cs b/src/Umbraco.Web.Common/Views/UmbracoViewPage.cs
similarity index 99%
rename from src/Umbraco.Web.Common/AspNetCore/UmbracoViewPage.cs
rename to src/Umbraco.Web.Common/Views/UmbracoViewPage.cs
index 0d85d16a7e..d96ed397c5 100644
--- a/src/Umbraco.Web.Common/AspNetCore/UmbracoViewPage.cs
+++ b/src/Umbraco.Web.Common/Views/UmbracoViewPage.cs
@@ -20,8 +20,6 @@ using Umbraco.Extensions;
namespace Umbraco.Cms.Web.Common.AspNetCore
{
- // TODO: Should be in Views namespace?
-
public abstract class UmbracoViewPage : UmbracoViewPage
{
diff --git a/src/Umbraco.Web.UI.NetCore/Properties/launchSettings.json b/src/Umbraco.Web.UI.NetCore/Properties/launchSettings.json
index 65e9736518..b16945dcb0 100644
--- a/src/Umbraco.Web.UI.NetCore/Properties/launchSettings.json
+++ b/src/Umbraco.Web.UI.NetCore/Properties/launchSettings.json
@@ -1,4 +1,4 @@
-{
+{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs
index 9c5606fa4d..85aa067627 100644
--- a/src/Umbraco.Web.UI.NetCore/Startup.cs
+++ b/src/Umbraco.Web.UI.NetCore/Startup.cs
@@ -59,7 +59,6 @@ namespace Umbraco.Cms.Web.UI.NetCore
app.UseDeveloperExceptionPage();
}
- app.UseUmbraco();
app.UseUmbracoBackOffice();
app.UseUmbracoWebsite();
}
diff --git a/src/Umbraco.Web.UI.NetCore/Views/Partials/blocklist/default.cshtml b/src/Umbraco.Web.UI.NetCore/Views/Partials/blocklist/default.cshtml
index b9207ece9b..38f7431f25 100644
--- a/src/Umbraco.Web.UI.NetCore/Views/Partials/blocklist/default.cshtml
+++ b/src/Umbraco.Web.UI.NetCore/Views/Partials/blocklist/default.cshtml
@@ -1,4 +1,4 @@
-@inherits Umbraco.Cms.Web.Common.AspNetCore.UmbracoViewPage
+@inherits Umbraco.Cms.Web.Common.AspNetCore.UmbracoViewPage
@{
if (!Model.Any()) { return; }
}
diff --git a/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/bootstrap3-fluid.cshtml b/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/bootstrap3-fluid.cshtml
index cc9bb2bb06..840c9c1218 100644
--- a/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/bootstrap3-fluid.cshtml
+++ b/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/bootstrap3-fluid.cshtml
@@ -1,4 +1,4 @@
-@using System.Web
+@using System.Web
@using Microsoft.AspNetCore.Html
@using Newtonsoft.Json.Linq
@inherits Umbraco.Cms.Web.Common.AspNetCore.UmbracoViewPage
diff --git a/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/bootstrap3.cshtml b/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/bootstrap3.cshtml
index d6728a5019..892dc84afe 100644
--- a/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/bootstrap3.cshtml
+++ b/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/bootstrap3.cshtml
@@ -1,4 +1,4 @@
-@using System.Web
+@using System.Web
@using Microsoft.AspNetCore.Html
@using Newtonsoft.Json.Linq
@inherits Umbraco.Cms.Web.Common.AspNetCore.UmbracoViewPage
diff --git a/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/editors/embed.cshtml b/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/editors/embed.cshtml
index c28b375fc8..dbd9438fa7 100644
--- a/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/editors/embed.cshtml
+++ b/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/editors/embed.cshtml
@@ -1,5 +1,4 @@
-@using Umbraco.Core
-@using Umbraco.Cms.Core
+@using Umbraco.Cms.Core
@inherits Umbraco.Cms.Web.Common.AspNetCore.UmbracoViewPage
@{
diff --git a/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/editors/macro.cshtml b/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/editors/macro.cshtml
index 59416a4ea5..4305a1e4cf 100644
--- a/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/editors/macro.cshtml
+++ b/src/Umbraco.Web.UI.NetCore/Views/Partials/grid/editors/macro.cshtml
@@ -1,4 +1,4 @@
-@inherits Umbraco.Cms.Web.Common.AspNetCore.UmbracoViewPage
+@inherits Umbraco.Cms.Web.Common.AspNetCore.UmbracoViewPage
@if (Model.value != null)
{
diff --git a/src/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactory.cs b/src/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactory.cs
index e217ea2777..f44890cf2f 100644
--- a/src/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactory.cs
+++ b/src/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactory.cs
@@ -93,9 +93,9 @@ namespace Umbraco.Cms.Web.Website.Routing
_defaultControllerDescriptor.Value,
templateName: customActionName);
- def = CheckHijackedRoute(httpContext, def);
+ def = CheckHijackedRoute(httpContext, def, out bool hasHijackedRoute);
- def = CheckNoTemplate(httpContext, def);
+ def = CheckNoTemplate(httpContext, def, hasHijackedRoute);
return def;
}
@@ -103,7 +103,7 @@ namespace Umbraco.Cms.Web.Website.Routing
///
/// Check if the route is hijacked and return new route values
///
- private UmbracoRouteValues CheckHijackedRoute(HttpContext httpContext, UmbracoRouteValues def)
+ private UmbracoRouteValues CheckHijackedRoute(HttpContext httpContext, UmbracoRouteValues def, out bool hasHijackedRoute)
{
IPublishedRequest request = def.PublishedRequest;
@@ -113,21 +113,23 @@ namespace Umbraco.Cms.Web.Website.Routing
ControllerActionDescriptor descriptor = _controllerActionSearcher.Find(httpContext, customControllerName, def.TemplateName);
if (descriptor != null)
{
+ hasHijackedRoute = true;
+
return new UmbracoRouteValues(
request,
descriptor,
- def.TemplateName,
- true);
+ def.TemplateName);
}
}
+ hasHijackedRoute = false;
return def;
}
///
/// Special check for when no template or hijacked route is done which needs to re-run through the routing pipeline again for last chance finders
///
- private UmbracoRouteValues CheckNoTemplate(HttpContext httpContext, UmbracoRouteValues def)
+ private UmbracoRouteValues CheckNoTemplate(HttpContext httpContext, UmbracoRouteValues def, bool hasHijackedRoute)
{
IPublishedRequest request = def.PublishedRequest;
@@ -138,7 +140,7 @@ namespace Umbraco.Cms.Web.Website.Routing
if (request.HasPublishedContent()
&& !request.HasTemplate()
&& !_umbracoFeatures.Disabled.DisableTemplates
- && !def.HasHijackedRoute)
+ && !hasHijackedRoute)
{
IPublishedContent content = request.PublishedContent;
@@ -162,7 +164,7 @@ namespace Umbraco.Cms.Web.Website.Routing
// if the content has changed, we must then again check for hijacked routes
if (content != request.PublishedContent)
{
- def = CheckHijackedRoute(httpContext, def);
+ def = CheckHijackedRoute(httpContext, def, out _);
}
}
diff --git a/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs
index 2961e84dd3..e69de29bb2 100644
--- a/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs
+++ b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs
@@ -1,128 +0,0 @@
-using System;
-using System.Web.Mvc;
-using Microsoft.Extensions.DependencyInjection;
-using Umbraco.Web.Routing;
-using Umbraco.Core;
-using Current = Umbraco.Web.Composing.Current;
-
-namespace Umbraco.Web.Mvc
-{
- ///
- /// Used for custom routed pages that are being integrated with the Umbraco data but are not
- /// part of the umbraco request pipeline. This allows umbraco macros to be able to execute in this scenario.
- ///
- ///
- /// This is inspired from this discussion:
- /// https://our.umbraco.com/forum/developers/extending-umbraco/41367-Umbraco-6-MVC-Custom-MVC-Route?p=3
- ///
- /// which is based on custom routing found here:
- /// http://shazwazza.com/post/Custom-MVC-routing-in-Umbraco
- ///
- public class EnsurePublishedContentRequestAttribute : ActionFilterAttribute
- {
- // TODO: Need to port this to netcore and figure out if its needed or how this should work (part of a different task)
-
- //private readonly string _dataTokenName;
- //private IUmbracoContextAccessor _umbracoContextAccessor;
- //private readonly int? _contentId;
- //private IPublishedContentQuery _publishedContentQuery;
-
- /////
- ///// Constructor - can be used for testing
- /////
- /////
- /////
- //public EnsurePublishedContentRequestAttribute(IUmbracoContextAccessor umbracoContextAccessor, int contentId)
- //{
- // _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
- // _contentId = contentId;
- //}
-
- /////
- ///// A constructor used to set an explicit content Id to the PublishedRequest that will be created
- /////
- /////
- //public EnsurePublishedContentRequestAttribute(int contentId)
- //{
- // _contentId = contentId;
- //}
-
- /////
- ///// A constructor used to set the data token key name that contains a reference to a PublishedContent instance
- /////
- /////
- //public EnsurePublishedContentRequestAttribute(string dataTokenName)
- //{
- // _dataTokenName = dataTokenName;
- //}
-
- /////
- ///// Constructor - can be used for testing
- /////
- /////
- /////
- //public EnsurePublishedContentRequestAttribute(IUmbracoContextAccessor umbracoContextAccessor, string dataTokenName)
- //{
- // _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
- // _dataTokenName = dataTokenName;
- //}
-
- /////
- ///// Exposes the UmbracoContext
- /////
- //protected IUmbracoContext UmbracoContext => _umbracoContextAccessor?.UmbracoContext ?? Current.UmbracoContext;
-
- //// TODO: try lazy property injection?
- //private IPublishedRouter PublishedRouter => Current.Factory.GetRequiredService();
-
- //private IPublishedContentQuery PublishedContentQuery => _publishedContentQuery ?? (_publishedContentQuery = Current.Factory.GetRequiredService());
-
- //public override void OnActionExecuted(ActionExecutedContext filterContext)
- //{
- // base.OnActionExecuted(filterContext);
-
- // // First we need to check if the published content request has been set, if it has we're going to ignore this and not actually do anything
- // if (Current.UmbracoContext.PublishedRequest != null)
- // {
- // return;
- // }
-
- // Current.UmbracoContext.PublishedRequest = PublishedRouter.CreateRequest(Current.UmbracoContext);
- // ConfigurePublishedContentRequest(Current.UmbracoContext.PublishedRequest, filterContext);
- //}
-
- /////
- ///// This assigns the published content to the request, developers can override this to specify
- ///// any other custom attributes required.
- /////
- /////
- /////
- //protected virtual void ConfigurePublishedContentRequest(IPublishedRequest request, ActionExecutedContext filterContext)
- //{
- // if (_contentId.HasValue)
- // {
- // var content = PublishedContentQuery.Content(_contentId.Value);
- // if (content == null)
- // {
- // throw new InvalidOperationException("Could not resolve content with id " + _contentId);
- // }
- // request.PublishedContent = content;
- // }
- // else if (_dataTokenName.IsNullOrWhiteSpace() == false)
- // {
- // var result = filterContext.RouteData.DataTokens[_dataTokenName];
- // if (result == null)
- // {
- // throw new InvalidOperationException("No data token could be found with the name " + _dataTokenName);
- // }
- // if (result is IPublishedContent == false)
- // {
- // throw new InvalidOperationException("The data token resolved with name " + _dataTokenName + " was not an instance of " + typeof(IPublishedContent));
- // }
- // request.PublishedContent = (IPublishedContent)result;
- // }
-
- // PublishedRouter.PrepareRequest(request);
- //}
- }
-}
diff --git a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs
index 32807448db..7a4c9474a0 100644
--- a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs
+++ b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs
@@ -1,15 +1,10 @@
+using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Web.Models;
using Umbraco.Web.Routing;
-using Umbraco.Core;
-using Umbraco.Web.Composing;
-using System;
-using Umbraco.Cms.Core.Models.PublishedContent;
-using Umbraco.Cms.Core.Routing;
-using Umbraco.Cms.Core.Web;
namespace Umbraco.Web.Mvc
{
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 92009208b1..93cb8da83d 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -170,7 +170,6 @@
-