diff --git a/src/Umbraco.Configuration/GlobalSettings.cs b/src/Umbraco.Configuration/GlobalSettings.cs index a44f7ae636..6ed786f473 100644 --- a/src/Umbraco.Configuration/GlobalSettings.cs +++ b/src/Umbraco.Configuration/GlobalSettings.cs @@ -407,5 +407,22 @@ namespace Umbraco.Core.Configuration return backingField; } + + /// + /// Gets the path to the razor file used when no published content is available. + /// + public string NoNodesViewPath + { + get + { + var configuredValue = ConfigurationManager.AppSettings[Constants.AppSettings.NoNodesViewPath]; + if (!string.IsNullOrWhiteSpace(configuredValue)) + { + return configuredValue; + } + + return "~/config/splashes/NoNodes.cshtml"; + } + } } } diff --git a/src/Umbraco.Core/Configuration/IGlobalSettings.cs b/src/Umbraco.Core/Configuration/IGlobalSettings.cs index 1b1f328142..d63b1ddc6a 100644 --- a/src/Umbraco.Core/Configuration/IGlobalSettings.cs +++ b/src/Umbraco.Core/Configuration/IGlobalSettings.cs @@ -95,5 +95,10 @@ bool DisableElectionForSingleServer { get; } string RegisterType { get; } string DatabaseFactoryServerVersion { get; } + + /// + /// Gets the path to the razor file used when no published content is available. + /// + string NoNodesViewPath { get; } } } diff --git a/src/Umbraco.Core/Constants-AppSettings.cs b/src/Umbraco.Core/Constants-AppSettings.cs index 4c47f12ba0..c55b4b0314 100644 --- a/src/Umbraco.Core/Constants-AppSettings.cs +++ b/src/Umbraco.Core/Constants-AppSettings.cs @@ -115,6 +115,11 @@ namespace Umbraco.Core /// public const string DisableElectionForSingleServer = "Umbraco.Core.DisableElectionForSingleServer"; + /// + /// Gets the path to the razor file used when no published content is available. + /// + public const string NoNodesViewPath = "Umbraco.Core.NoNodesViewPath"; + /// /// Debug specific web.config AppSetting keys for Umbraco /// diff --git a/src/Umbraco.Core/Constants-Web.cs b/src/Umbraco.Core/Constants-Web.cs index a1e138116d..17fddd98ea 100644 --- a/src/Umbraco.Core/Constants-Web.cs +++ b/src/Umbraco.Core/Constants-Web.cs @@ -34,6 +34,11 @@ /// The header name that angular uses to pass in the token to validate the cookie /// public const string AngularHeadername = "X-UMB-XSRF-TOKEN"; + + /// + /// The route for rendering a page when no content is published. + /// + public const string NoContentRoute = "/UmbNoContent"; } } } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 9815c94728..e0bd475a46 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -286,6 +286,7 @@ + diff --git a/src/Umbraco.Tests/Web/Mvc/RenderNoContentControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderNoContentControllerTests.cs new file mode 100644 index 0000000000..e728d75dc5 --- /dev/null +++ b/src/Umbraco.Tests/Web/Mvc/RenderNoContentControllerTests.cs @@ -0,0 +1,54 @@ +using System.Web.Mvc; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; +using Umbraco.Web; +using Umbraco.Web.Models; +using Umbraco.Web.Mvc; + +namespace Umbraco.Tests.Web.Mvc +{ + [TestFixture] + public class RenderNoContentControllerTests + { + [Test] + public void Redirects_To_Root_When_Content_Published() + { + var mockUmbracoContext = new Mock(); + mockUmbracoContext.Setup(x => x.Content.HasContent()).Returns(true); + var mockIOHelper = new Mock(); + var mockGlobalSettings = new Mock(); + var controller = new RenderNoContentController(mockUmbracoContext.Object, mockIOHelper.Object, mockGlobalSettings.Object); + + var result = controller.Index() as RedirectResult; + + Assert.IsNotNull(result); + Assert.AreEqual("~/", result.Url); + } + + [Test] + public void Renders_View_When_No_Content_Published() + { + const string UmbracoPathSetting = "~/umbraco"; + const string UmbracoPath = "/umbraco"; + const string ViewPath = "~/config/splashes/NoNodes.cshtml"; + var mockUmbracoContext = new Mock(); + mockUmbracoContext.Setup(x => x.Content.HasContent()).Returns(false); + var mockIOHelper = new Mock(); + mockIOHelper.Setup(x => x.ResolveUrl(It.Is(y => y == UmbracoPathSetting))).Returns(UmbracoPath); + var mockGlobalSettings = new Mock(); + mockGlobalSettings.SetupGet(x => x.UmbracoPath).Returns(UmbracoPathSetting); + mockGlobalSettings.SetupGet(x => x.NoNodesViewPath).Returns(ViewPath); + var controller = new RenderNoContentController(mockUmbracoContext.Object, mockIOHelper.Object, mockGlobalSettings.Object); + + var result = controller.Index() as ViewResult; + Assert.IsNotNull(result); + Assert.AreEqual(ViewPath, result.ViewName); + + var model = result.Model as NoNodesViewModel; + Assert.IsNotNull(model); + Assert.AreEqual(UmbracoPath, model.UmbracoPath); + } + } +} diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 7a7b1bada5..6e955140d9 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -141,13 +141,6 @@ Properties\SolutionInfo.cs - - noNodes.aspx - ASPXCodeBehind - - - noNodes.aspx - True @@ -173,11 +166,10 @@ - + - ClientDependency.config diff --git a/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.cs b/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.cs deleted file mode 100644 index 51653d54ca..0000000000 --- a/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using Umbraco.Web.Composing; - -namespace Umbraco.Web.UI.Config.Splashes -{ - public partial class NoNodes : System.Web.UI.Page - { - - protected override void OnInit(EventArgs e) - { - base.OnInit(e); - - var store = Current.UmbracoContext.Content; - if (store.HasContent()) - { - //if there is actually content, go to the root - Response.Redirect("~/"); - } - } - - } -} diff --git a/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.designer.cs b/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.designer.cs deleted file mode 100644 index 350b610bf0..0000000000 --- a/src/Umbraco.Web.UI/config/splashes/NoNodes.aspx.designer.cs +++ /dev/null @@ -1,15 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Config.Splashes { - - - public partial class NoNodes { - } -} diff --git a/src/Umbraco.Web.UI/config/splashes/NoNodes.cshtml b/src/Umbraco.Web.UI/config/splashes/NoNodes.cshtml new file mode 100644 index 0000000000..4193e58873 --- /dev/null +++ b/src/Umbraco.Web.UI/config/splashes/NoNodes.cshtml @@ -0,0 +1,49 @@ +@inherits System.Web.Mvc.WebViewPage + + + + + + + + Umbraco: No Published Content + + + + + +
+
+
+ + +

Welcome to your Umbraco installation

+

You're seeing this wonderful page because your website doesn't contain any published content yet.

+ + + +
+
+

Easy start with Umbraco.tv

+

We have created a bunch of 'how-to' videos, to get you easily started with Umbraco. Learn how to build projects in just a couple of minutes. Easiest CMS in the world.

+ + Umbraco.tv → +
+ +
+

Be a part of the community

+

The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we're sure that you can get your answers from the community.

+ + our.Umbraco → +
+
+ +
+
+ +
+ + + diff --git a/src/Umbraco.Web.UI/config/splashes/noNodes.aspx b/src/Umbraco.Web.UI/config/splashes/noNodes.aspx deleted file mode 100644 index 961f2e006d..0000000000 --- a/src/Umbraco.Web.UI/config/splashes/noNodes.aspx +++ /dev/null @@ -1,63 +0,0 @@ -<%@ Page Language="C#" AutoEventWireup="True" Inherits="Umbraco.Web.UI.Config.Splashes.NoNodes" CodeBehind="NoNodes.aspx.cs" %> -<%@ Import Namespace="Umbraco.Core" %> -<%@ Import Namespace="Umbraco.Web.Composing" %> -<%@ Import Namespace="Umbraco.Core.Configuration" %> -<%@ Import Namespace="Umbraco.Core.IO" %> - - - - - - - - - - - - - - - - - - - - -
-
-
- - -

Welcome to your Umbraco installation

-

You're seeing this wonderful page because your website doesn't contain any published content yet.

- - - - -
-
-

Easy start with Umbraco.tv

-

We have created a bunch of 'how-to' videos, to get you easily started with Umbraco. Learn how to build projects in just a couple of minutes. Easiest CMS in the world.

- - Umbraco.tv → -
- -
-

Be a part of the community

-

The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we're sure that you can get your answers from the community.

- - our.Umbraco → -
-
- -
-
- -
- - - - - diff --git a/src/Umbraco.Web/Models/NoNodesViewModel.cs b/src/Umbraco.Web/Models/NoNodesViewModel.cs new file mode 100644 index 0000000000..ca75fd3c02 --- /dev/null +++ b/src/Umbraco.Web/Models/NoNodesViewModel.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Web.Models +{ + public class NoNodesViewModel + { + public string UmbracoPath { get; set; } + } +} diff --git a/src/Umbraco.Web/Mvc/RenderNoContentController.cs b/src/Umbraco.Web/Mvc/RenderNoContentController.cs new file mode 100644 index 0000000000..2ffd323440 --- /dev/null +++ b/src/Umbraco.Web/Mvc/RenderNoContentController.cs @@ -0,0 +1,39 @@ +using System; +using System.Web.Mvc; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; +using Umbraco.Web.Models; + +namespace Umbraco.Web.Mvc +{ + public class RenderNoContentController : Controller + { + private readonly IUmbracoContext _umbracoContext; + private readonly IIOHelper _ioHelper; + private readonly IGlobalSettings _globalSettings; + + public RenderNoContentController(IUmbracoContext umbracoContext, IIOHelper ioHelper, IGlobalSettings globalSettings) + { + _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); + _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); + _globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings)); + } + + public ActionResult Index() + { + var store = _umbracoContext.Content; + if (store.HasContent()) + { + // If there is actually content, go to the root. + return Redirect("~/"); + } + + var model = new NoNodesViewModel + { + UmbracoPath = _ioHelper.ResolveUrl(_globalSettings.UmbracoPath), + }; + + return View(_globalSettings.NoNodesViewPath, model); + } + } +} diff --git a/src/Umbraco.Web/Runtime/WebInitialComponent.cs b/src/Umbraco.Web/Runtime/WebInitialComponent.cs index ea7b5b94e7..933cababed 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComponent.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComponent.cs @@ -17,7 +17,6 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Hosting; using Umbraco.Core.Strings; using Umbraco.Core.IO; -using Umbraco.Core.Strings; using Umbraco.Web.Install; using Umbraco.Web.JavaScript; using Umbraco.Web.Mvc; @@ -181,6 +180,9 @@ namespace Umbraco.Web.Runtime ); defaultRoute.RouteHandler = new RenderRouteHandler(umbracoContextAccessor, ControllerBuilder.Current.GetControllerFactory(), shortStringHelper); + // register no content route + RouteNoContentController(umbracoPath); + // register install routes RouteTable.Routes.RegisterArea(); @@ -191,6 +193,14 @@ namespace Umbraco.Web.Runtime RoutePluginControllers(globalSettings, surfaceControllerTypes, apiControllerTypes, ioHelper); } + private static void RouteNoContentController(string umbracoPath) + { + RouteTable.Routes.MapRoute( + "umbraco-no-content", + umbracoPath + Core.Constants.Web.NoContentRoute, + new { controller = "RenderNoContent", action = "Index" }); + } + private static void RoutePluginControllers( IGlobalSettings globalSettings, SurfaceControllerTypeCollection surfaceControllerTypes, @@ -252,6 +262,5 @@ namespace Umbraco.Web.Runtime // make it use our custom/special SurfaceMvcHandler route.RouteHandler = new SurfaceRouteHandler(); } - } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 27f372753a..43ae4d5e57 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -143,6 +143,8 @@ + + diff --git a/src/Umbraco.Web/UmbracoInjectedModule.cs b/src/Umbraco.Web/UmbracoInjectedModule.cs index a311d76f73..d3c72090db 100644 --- a/src/Umbraco.Web/UmbracoInjectedModule.cs +++ b/src/Umbraco.Web/UmbracoInjectedModule.cs @@ -1,17 +1,14 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Web; using System.Web.Routing; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Web.Routing; using Umbraco.Core.Exceptions; -using Umbraco.Core.Security; +using Umbraco.Core.Logging; using Umbraco.Web.Composing; +using Umbraco.Web.Routing; using Umbraco.Web.Security; namespace Umbraco.Web @@ -244,8 +241,8 @@ namespace Umbraco.Web _logger.Warn("Umbraco has no content"); - const string noContentUrl = "~/config/splashes/noNodes.aspx"; - httpContext.RewritePath(_uriUtility.ToAbsolute(noContentUrl)); + var rewriteTo = _uriUtility.ToAbsolute(_globalSettings.UmbracoPath + Constants.Web.NoContentRoute); + httpContext.RewritePath(_uriUtility.ToAbsolute(rewriteTo)); return false; }