diff --git a/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs new file mode 100644 index 0000000000..462936c26e --- /dev/null +++ b/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs @@ -0,0 +1,435 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Web.Mvc; +using System.Web.Routing; +using System.Xml; +using NUnit.Framework; +using umbraco.BusinessLogic; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Tests.PublishedContent; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Stubs; +using Umbraco.Web; +using Umbraco.Web.Models; +using Umbraco.Web.Mvc; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.PublishedCache.XmlPublishedCache; +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.Mvc +{ + [TestFixture] + public class UmbracoViewPageTests + { + #region RenderModel To ... + + [Test] + public void RenderModel_To_RenderModel() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelTestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.AreSame(model, view.Model); + } + + [Test] + public void RenderModel_ContentType1_To_ContentType1() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new ContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf(view.Model); + } + + [Test] + public void RenderModel_ContentType2_To_ContentType1() + { + var content = new ContentType2(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new ContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf(view.Model); + } + + [Test] + public void RenderModel_ContentType1_To_ContentType2() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new ContentType2TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + + Assert.Throws(() => view.SetViewDataX(viewData)); + } + + [Test] + public void RenderModel_ContentType1_To_RenderModelOf_ContentType1() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelOfContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public void RenderModel_ContentType2_To_RenderModelOf_ContentType1() + { + var content = new ContentType2(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelOfContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public void RenderModel_ContentType1_To_RenderModelOf_ContentType2() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelOfContentType2TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + + Assert.Throws(() => view.SetViewDataX(viewData)); + } + + #endregion + + #region RenderModelOf To ... + + [Test] + public void RenderModelOf_ContentType1_To_RenderModel() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelTestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.AreSame(model, view.Model); + } + + [Test] + public void RenderModelOf_ContentType1_To_ContentType1() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new ContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf(view.Model); + } + + [Test] + public void RenderModelOf_ContentType2_To_ContentType1() + { + var content = new ContentType2(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new ContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf(view.Model); + } + + [Test] + public void RenderModelOf_ContentType1_To_ContentType2() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new ContentType2TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + Assert.Throws(() => view.SetViewDataX(viewData)); + } + + [Test] + public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType1() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelOfContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public void RenderModelOf_ContentType2_To_RenderModelOf_ContentType1() + { + var content = new ContentType2(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelOfContentType1TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType2() + { + var content = new ContentType1(null); + var model = new RenderModel(content, CultureInfo.InvariantCulture); + var view = new RenderModelOfContentType2TestPage(); + var viewData = new ViewDataDictionary(model); + + view.ViewContext = GetViewContext(); + Assert.Throws(() => view.SetViewDataX(viewData)); + } + + #endregion + + #region ContentType To ... + + [Test] + public void ContentType1_To_RenderModel() + { + var content = new ContentType1(null); + var view = new RenderModelTestPage(); + var viewData = new ViewDataDictionary(content); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf(view.Model); + } + + [Test] + public void ContentType1_To_RenderModelOf_ContentType1() + { + var content = new ContentType1(null); + var view = new RenderModelOfContentType1TestPage(); + var viewData = new ViewDataDictionary(content); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public void ContentType2_To_RenderModelOf_ContentType1() + { + var content = new ContentType2(null); + var view = new RenderModelOfContentType1TestPage(); + var viewData = new ViewDataDictionary(content); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf>(view.Model); + Assert.IsInstanceOf(view.Model.Content); + } + + [Test] + public void ContentType1_To_RenderModelOf_ContentType2() + { + var content = new ContentType1(null); + var view = new RenderModelOfContentType2TestPage(); + var viewData = new ViewDataDictionary(content); + + view.ViewContext = GetViewContext(); + Assert.Throws(() =>view.SetViewDataX(viewData)); + } + + [Test] + public void ContentType1_To_ContentType1() + { + var content = new ContentType1(null); + var view = new ContentType1TestPage(); + var viewData = new ViewDataDictionary(content); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf(view.Model); + } + + [Test] + public void ContentType1_To_ContentType2() + { + var content = new ContentType1(null); + var view = new ContentType2TestPage(); + var viewData = new ViewDataDictionary(content); + + view.ViewContext = GetViewContext(); + Assert.Throws(() => view.SetViewDataX(viewData)); + } + + [Test] + public void ContentType2_To_ContentType1() + { + var content = new ContentType2(null); + var view = new ContentType1TestPage(); + var viewData = new ViewDataDictionary(content); + + view.ViewContext = GetViewContext(); + view.SetViewDataX(viewData); + + Assert.IsInstanceOf(view.Model); + } + + #endregion + + #region Test elements + + public class TestPage : UmbracoViewPage + { + public override void Execute() + { + throw new NotImplementedException(); + } + + public void SetViewDataX(ViewDataDictionary viewData) + { + SetViewData(viewData); + } + } + + public class RenderModelTestPage : TestPage + { } + + public class RenderModelOfContentType1TestPage : TestPage> + { } + + public class RenderModelOfContentType2TestPage : TestPage> + { } + + public class ContentType1TestPage : TestPage + { } + + public class ContentType2TestPage : TestPage + { } + + public class ContentType1 : PublishedContentWrapped + { + public ContentType1(IPublishedContent content) : base(content) {} + } + + public class ContentType2 : ContentType1 + { + public ContentType2(IPublishedContent content) : base(content) { } + } + + #endregion + + #region Test helpers + + ViewContext GetViewContext() + { + var umbracoContext = GetUmbracoContext("/dang", 0); + + var urlProvider = new UrlProvider(umbracoContext, new IUrlProvider[] { new DefaultUrlProvider() }); + var routingContext = new RoutingContext( + umbracoContext, + Enumerable.Empty(), + new FakeLastChanceFinder(), + urlProvider); + umbracoContext.RoutingContext = routingContext; + + var request = new PublishedContentRequest(new Uri("http://localhost/dang"), routingContext); + request.Culture = CultureInfo.InvariantCulture; + umbracoContext.PublishedContentRequest = request; + + var context = new ViewContext(); + context.RouteData = new RouteData(); + context.RouteData.DataTokens.Add("umbraco-context", umbracoContext); + + return context; + } + + protected UmbracoContext GetUmbracoContext(string url, int templateId, RouteData routeData = null, bool setSingleton = false) + { + var cache = new PublishedContentCache(); + + //cache.GetXmlDelegate = (context, preview) => + //{ + // var doc = new XmlDocument(); + // doc.LoadXml(GetXmlContent(templateId)); + // return doc; + //}; + + //PublishedContentCache.UnitTesting = true; + + // ApplicationContext.Current = new ApplicationContext(false) { IsReady = true }; + var appCtx = new ApplicationContext(false) { IsReady = true }; + + var ctx = new UmbracoContext( + GetHttpContextFactory(url, routeData).HttpContext, + appCtx, + new PublishedCaches(cache, new PublishedMediaCache())); + + //if (setSingleton) + //{ + // UmbracoContext.Current = ctx; + //} + + return ctx; + } + + protected FakeHttpContextFactory GetHttpContextFactory(string url, RouteData routeData = null) + { + var factory = routeData != null + ? new FakeHttpContextFactory(url, routeData) + : new FakeHttpContextFactory(url); + + + //set the state helper + StateHelper.HttpContext = factory.HttpContext; + + return factory; + } + + #endregion + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index f1fb84e71c..af40388b00 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -145,6 +145,7 @@ + diff --git a/src/Umbraco.Web/Models/IRenderModel.cs b/src/Umbraco.Web/Models/IRenderModel.cs new file mode 100644 index 0000000000..21e3f765ac --- /dev/null +++ b/src/Umbraco.Web/Models/IRenderModel.cs @@ -0,0 +1,9 @@ +using Umbraco.Core.Models; + +namespace Umbraco.Web.Models +{ + public interface IRenderModel + { + IPublishedContent Content { get; } + } +} diff --git a/src/Umbraco.Web/Models/RenderModel.cs b/src/Umbraco.Web/Models/RenderModel.cs index f88bcf1ad5..ff2ea8a674 100644 --- a/src/Umbraco.Web/Models/RenderModel.cs +++ b/src/Umbraco.Web/Models/RenderModel.cs @@ -1,13 +1,14 @@ using System; using System.Globalization; using Umbraco.Core.Models; +using Umbraco.Web.Mvc; namespace Umbraco.Web.Models { /// /// Represents the model for the current rendering page in Umbraco /// - public class RenderModel + public class RenderModel : IRenderModel { /// /// Constructor specifying both the IPublishedContent and the CultureInfo @@ -16,7 +17,7 @@ namespace Umbraco.Web.Models /// public RenderModel(IPublishedContent content, CultureInfo culture) { - if (content == null) throw new ArgumentNullException("content"); + if (content == null) throw new ArgumentNullException("content"); if (culture == null) throw new ArgumentNullException("culture"); Content = content; CurrentCulture = culture; @@ -28,7 +29,7 @@ namespace Umbraco.Web.Models /// public RenderModel(IPublishedContent content) { - if (content == null) throw new ArgumentNullException("content"); + if (content == null) throw new ArgumentNullException("content"); if (UmbracoContext.Current == null) { throw new InvalidOperationException("Cannot construct a RenderModel without specifying a CultureInfo when no UmbracoContext has been initialized"); diff --git a/src/Umbraco.Web/Models/RenderModelOfTContent.cs b/src/Umbraco.Web/Models/RenderModelOfTContent.cs new file mode 100644 index 0000000000..9645c82f14 --- /dev/null +++ b/src/Umbraco.Web/Models/RenderModelOfTContent.cs @@ -0,0 +1,36 @@ +using System.Globalization; +using Umbraco.Core.Models; +using Umbraco.Web.Mvc; + +namespace Umbraco.Web.Models +{ + public class RenderModel : RenderModel + where TContent : class, IPublishedContent + { + /// + /// Constructor specifying both the IPublishedContent and the CultureInfo + /// + /// + /// + public RenderModel(TContent content, CultureInfo culture) + : base(content, culture) + { + Content = content; + } + + /// + /// Constructor to set the IPublishedContent and the CurrentCulture is set by the UmbracoContext + /// + /// + public RenderModel(TContent content) + : base(content) + { + Content = content; + } + + /// + /// Returns the current IPublishedContent object + /// + public new TContent Content { get; private set; } + } +} diff --git a/src/Umbraco.Web/Mvc/UmbracoTemplatePage.cs b/src/Umbraco.Web/Mvc/UmbracoTemplatePage.cs index 231c6e12b2..b9f9f4690f 100644 --- a/src/Umbraco.Web/Mvc/UmbracoTemplatePage.cs +++ b/src/Umbraco.Web/Mvc/UmbracoTemplatePage.cs @@ -1,13 +1,4 @@ -using System; -using StackExchange.Profiling; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.Dictionary; using Umbraco.Core.Dynamics; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Profiling; using Umbraco.Web.Models; namespace Umbraco.Web.Mvc @@ -17,58 +8,19 @@ namespace Umbraco.Web.Mvc /// public abstract class UmbracoTemplatePage : UmbracoViewPage { - protected UmbracoTemplatePage() - { - - } - - protected override void InitializePage() - { - base.InitializePage(); - //set the model to the current node if it is not set, this is generally not the case - if (Model != null) - { - ////this.ViewData.Model = Model; - //var backingItem = new DynamicBackingItem(Model.CurrentNode); - var dynamicNode = new DynamicPublishedContent(Model.Content); - CurrentPage = dynamicNode.AsDynamic(); - } - } - - protected override void SetViewData(System.Web.Mvc.ViewDataDictionary viewData) - { - //Here we're going to check if the viewData's model is of IPublishedContent, this is basically just a helper for - //syntax on the front-end so we can just pass in an IPublishedContent object to partial views that inherit from - //UmbracoTemplatePage. Then we're going to manually contruct a RenderViewModel to pass back in to SetViewData - if (viewData.Model is IPublishedContent) - { - //change the model to a RenderModel and auto set the culture - viewData.Model = new RenderModel((IPublishedContent)viewData.Model, UmbracoContext.PublishedContentRequest.Culture); - } - - base.SetViewData(viewData); - } + private object _currentPage; /// - /// Returns the a DynamicPublishedContent object + /// Returns the content as a dynamic object /// - public dynamic CurrentPage { get; private set; } - - private UmbracoHelper _helper; - - /// - /// Gets an UmbracoHelper - /// - /// - /// This ensures that the UmbracoHelper is constructed with the content model of this view - /// - public override UmbracoHelper Umbraco + public dynamic CurrentPage { get { - return _helper ?? (_helper = Model == null - ? new UmbracoHelper(UmbracoContext) - : new UmbracoHelper(UmbracoContext, Model.Content)); + // it's invalid to create a DynamicPublishedContent around a null content anyway + // fixme - should we return null or DynamicNull.Null? + if (Model == null || Model.Content == null) return null; + return _currentPage ?? (_currentPage = Model.Content.AsDynamic()); } } } diff --git a/src/Umbraco.Web/Mvc/UmbracoTemplatePageOfTContent.cs b/src/Umbraco.Web/Mvc/UmbracoTemplatePageOfTContent.cs new file mode 100644 index 0000000000..d25fdb9141 --- /dev/null +++ b/src/Umbraco.Web/Mvc/UmbracoTemplatePageOfTContent.cs @@ -0,0 +1,26 @@ +using Umbraco.Core.Dynamics; +using Umbraco.Core.Models; +using Umbraco.Web.Models; + +namespace Umbraco.Web.Mvc +{ + public abstract class UmbracoTemplatePage : UmbracoViewPage> + where TContent : class, IPublishedContent + { + private object _currentPage; + + /// + /// Returns the content as a dynamic object + /// + public dynamic CurrentPage + { + get + { + // it's invalid to create a DynamicPublishedContent around a null content anyway + // fixme - should we return null or DynamicNull.Null? + if (Model == null || Model.Content == null) return null; + return _currentPage ?? (_currentPage = Model.Content.AsDynamic()); + } + } + } +} diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPage.cs b/src/Umbraco.Web/Mvc/UmbracoViewPage.cs index 0f56fe6e55..4e067c2c82 100644 --- a/src/Umbraco.Web/Mvc/UmbracoViewPage.cs +++ b/src/Umbraco.Web/Mvc/UmbracoViewPage.cs @@ -1,188 +1,11 @@ -using System; +using System; +using System.Collections.Generic; +using System.Linq; using System.Text; -using System.Web; -using System.Web.Mvc; -using System.Web.WebPages; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Web.Routing; +using Umbraco.Core.Models; namespace Umbraco.Web.Mvc { - /// - /// The View that umbraco front-end views inherit from - /// - public abstract class UmbracoViewPage : WebViewPage - { - protected UmbracoViewPage() - { - - } - - /// - /// Returns the current UmbracoContext - /// - public UmbracoContext UmbracoContext - { - get - { - //we should always try to return the context from the data tokens just in case its a custom context and not - //using the UmbracoContext.Current. - //we will fallback to the singleton if necessary. - if (ViewContext.RouteData.DataTokens.ContainsKey("umbraco-context")) - { - return (UmbracoContext)ViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-context"); - } - //next check if it is a child action and see if the parent has it set in data tokens - if (ViewContext.IsChildAction) - { - if (ViewContext.ParentActionViewContext.RouteData.DataTokens.ContainsKey("umbraco-context")) - { - return (UmbracoContext)ViewContext.ParentActionViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-context"); - } - } - - //lastly, we will use the singleton, the only reason this should ever happen is is someone is rendering a page that inherits from this - //class and are rendering it outside of the normal Umbraco routing process. Very unlikely. - return UmbracoContext.Current; - } - } - - /// - /// Returns the current ApplicationContext - /// - public ApplicationContext ApplicationContext - { - get { return UmbracoContext.Application; } - } - - /// - /// Returns the current PublishedContentRequest - /// - internal PublishedContentRequest PublishedContentRequest - { - get - { - //we should always try to return the object from the data tokens just in case its a custom object and not - //using the UmbracoContext.Current. - //we will fallback to the singleton if necessary. - if (ViewContext.RouteData.DataTokens.ContainsKey("umbraco-doc-request")) - { - return (PublishedContentRequest)ViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-doc-request"); - } - //next check if it is a child action and see if the parent has it set in data tokens - if (ViewContext.IsChildAction) - { - if (ViewContext.ParentActionViewContext.RouteData.DataTokens.ContainsKey("umbraco-doc-request")) - { - return (PublishedContentRequest)ViewContext.ParentActionViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-doc-request"); - } - } - - //lastly, we will use the singleton, the only reason this should ever happen is is someone is rendering a page that inherits from this - //class and are rendering it outside of the normal Umbraco routing process. Very unlikely. - return UmbracoContext.Current.PublishedContentRequest; - } - } - - private UmbracoHelper _helper; - - /// - /// Gets an UmbracoHelper - /// - /// - /// This constructs the UmbracoHelper with the content model of the page routed to - /// - public virtual UmbracoHelper Umbraco - { - get { return _helper ?? (_helper = new UmbracoHelper(UmbracoContext)); } - } - - /// - /// Ensure that the current view context is added to the route data tokens so we can extract it if we like - /// - /// - /// Currently this is required by mvc macro engines - /// - protected override void InitializePage() - { - base.InitializePage(); - if (!ViewContext.IsChildAction) - { - if (!ViewContext.RouteData.DataTokens.ContainsKey(Constants.DataTokenCurrentViewContext)) - { - ViewContext.RouteData.DataTokens.Add(Constants.DataTokenCurrentViewContext, this.ViewContext); - } - } - - } - - /// - /// This will detect the end /body tag and insert the preview badge if in preview mode - /// - /// - public override void WriteLiteral(object value) - { - // filter / add preview banner - if (Response.ContentType.InvariantEquals("text/html")) // ASP.NET default value - { - if (UmbracoContext.Current.IsDebug || UmbracoContext.Current.InPreviewMode) - { - var text = value.ToString().ToLowerInvariant(); - var pos = text.IndexOf("", StringComparison.InvariantCultureIgnoreCase); - - if (pos > -1) - { - string markupToInject; - - if (UmbracoContext.Current.InPreviewMode) - { - // creating previewBadge markup - markupToInject = - String.Format(UmbracoSettings.PreviewBadge, - IOHelper.ResolveUrl(SystemDirectories.Umbraco), - IOHelper.ResolveUrl(SystemDirectories.UmbracoClient), - Server.UrlEncode(UmbracoContext.Current.HttpContext.Request.Path)); - } - else - { - // creating mini-profiler markup - markupToInject = Html.RenderProfiler().ToHtmlString(); - } - - var sb = new StringBuilder(text); - sb.Insert(pos, markupToInject); - - base.WriteLiteral(sb.ToString()); - return; - } - } - } - - base.WriteLiteral(value); - - - } - - public HelperResult RenderSection(string name, Func defaultContents) - { - return WebViewPageExtensions.RenderSection(this, name, defaultContents); - } - - public HelperResult RenderSection(string name, HelperResult defaultContents) - { - return WebViewPageExtensions.RenderSection(this, name, defaultContents); - } - - public HelperResult RenderSection(string name, string defaultContents) - { - return WebViewPageExtensions.RenderSection(this, name, defaultContents); - } - - public HelperResult RenderSection(string name, IHtmlString defaultContents) - { - return WebViewPageExtensions.RenderSection(this, name, defaultContents); - } - } -} \ No newline at end of file + public abstract class UmbracoViewPage : UmbracoViewPage + { } +} diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs new file mode 100644 index 0000000000..f13c8b32ef --- /dev/null +++ b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs @@ -0,0 +1,282 @@ +using System; +using System.Text; +using System.Web; +using System.Web.Mvc; +using System.Web.WebPages; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; +using Umbraco.Core.Models; +using Umbraco.Web.Models; +using Umbraco.Web.Routing; + +namespace Umbraco.Web.Mvc +{ + /// + /// The View that umbraco front-end views inherit from + /// + public abstract class UmbracoViewPage : WebViewPage + { + /// + /// Returns the current UmbracoContext + /// + public UmbracoContext UmbracoContext + { + get + { + //we should always try to return the context from the data tokens just in case its a custom context and not + //using the UmbracoContext.Current. + //we will fallback to the singleton if necessary. + if (ViewContext.RouteData.DataTokens.ContainsKey("umbraco-context")) + { + return (UmbracoContext)ViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-context"); + } + //next check if it is a child action and see if the parent has it set in data tokens + if (ViewContext.IsChildAction) + { + if (ViewContext.ParentActionViewContext.RouteData.DataTokens.ContainsKey("umbraco-context")) + { + return (UmbracoContext)ViewContext.ParentActionViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-context"); + } + } + + //lastly, we will use the singleton, the only reason this should ever happen is is someone is rendering a page that inherits from this + //class and are rendering it outside of the normal Umbraco routing process. Very unlikely. + return UmbracoContext.Current; + } + } + + /// + /// Returns the current ApplicationContext + /// + public ApplicationContext ApplicationContext + { + get { return UmbracoContext.Application; } + } + + /// + /// Returns the current PublishedContentRequest + /// + internal PublishedContentRequest PublishedContentRequest + { + get + { + //we should always try to return the object from the data tokens just in case its a custom object and not + //using the UmbracoContext.Current. + //we will fallback to the singleton if necessary. + if (ViewContext.RouteData.DataTokens.ContainsKey("umbraco-doc-request")) + { + return (PublishedContentRequest)ViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-doc-request"); + } + //next check if it is a child action and see if the parent has it set in data tokens + if (ViewContext.IsChildAction) + { + if (ViewContext.ParentActionViewContext.RouteData.DataTokens.ContainsKey("umbraco-doc-request")) + { + return (PublishedContentRequest)ViewContext.ParentActionViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-doc-request"); + } + } + + //lastly, we will use the singleton, the only reason this should ever happen is is someone is rendering a page that inherits from this + //class and are rendering it outside of the normal Umbraco routing process. Very unlikely. + return UmbracoContext.Current.PublishedContentRequest; + } + } + + private UmbracoHelper _helper; + + /// + /// Gets an UmbracoHelper + /// + /// + /// This constructs the UmbracoHelper with the content model of the page routed to + /// + public virtual UmbracoHelper Umbraco + { + get + { + if (_helper == null) + { + var model = ViewData.Model; + var content = model as IPublishedContent; + if (content == null && model is IRenderModel) + content = ((IRenderModel) model).Content; + _helper = content == null + ? new UmbracoHelper(UmbracoContext) + : new UmbracoHelper(UmbracoContext, content); + } + return _helper; + } + } + + /// + /// Ensure that the current view context is added to the route data tokens so we can extract it if we like + /// + /// + /// Currently this is required by mvc macro engines + /// + protected override void InitializePage() + { + base.InitializePage(); + if (ViewContext.IsChildAction == false) + { + if (ViewContext.RouteData.DataTokens.ContainsKey(Constants.DataTokenCurrentViewContext) == false) + { + ViewContext.RouteData.DataTokens.Add(Constants.DataTokenCurrentViewContext, ViewContext); + } + } + + } + + // maps model + protected override void SetViewData(ViewDataDictionary viewData) + { + var source = viewData.Model; + if (source == null) + { + base.SetViewData(viewData); + return; + } + + var sourceType = source.GetType(); + var targetType = typeof (TModel); + + // it types already match, nothing to do + if (sourceType.Inherits()) // includes == + { + base.SetViewData(viewData); + return; + } + + // try to grab the content + // if no content is found, return, nothing we can do + var sourceContent = source as IPublishedContent; + if (sourceContent == null && sourceType.Implements()) + { + sourceContent = ((IRenderModel)source).Content; + } + if (sourceContent == null) + { + var attempt = source.TryConvertTo(); + if (attempt.Success) sourceContent = attempt.Result; + } + + var ok = sourceContent != null; + if (sourceContent != null) + { + // try to grab the culture + // using context's culture by default + var culture = UmbracoContext.PublishedContentRequest.Culture; + var sourceRenderModel = source as RenderModel; + if (sourceRenderModel != null) + culture = sourceRenderModel.CurrentCulture; + + // reassign the model depending on its type + if (targetType.Implements()) + { + // it TModel implements IPublishedContent then use the content + // provided that the content is of the proper type + if ((sourceContent is TModel) == false) + throw new InvalidCastException(string.Format("Cannot cast source content type {0} to view model type {1}.", + sourceContent.GetType(), targetType)); + viewData.Model = sourceContent; + } + else if (targetType == typeof(RenderModel)) + { + // if TModel is a basic RenderModel just create it + viewData.Model = new RenderModel(sourceContent, culture); + } + else if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(RenderModel<>)) + { + // if TModel is a strongly-typed RenderModel<> then create it + // provided that the content is of the proper type + var targetContentType = targetType.GetGenericArguments()[0]; + if ((sourceContent.GetType().Inherits(targetContentType)) == false) + throw new InvalidCastException(string.Format("Cannot cast source content type {0} to view model content type {1}.", + sourceContent.GetType(), targetContentType)); + viewData.Model = Activator.CreateInstance(targetType, sourceContent, culture); + } + else + { + ok = false; + } + } + + if (ok == false) + { + // last chance : try to convert + var attempt = source.TryConvertTo(); + if (attempt.Success) viewData.Model = attempt.Result; + } + + base.SetViewData(viewData); + } + + /// + /// This will detect the end /body tag and insert the preview badge if in preview mode + /// + /// + public override void WriteLiteral(object value) + { + // filter / add preview banner + if (Response.ContentType.InvariantEquals("text/html")) // ASP.NET default value + { + if (UmbracoContext.Current.IsDebug || UmbracoContext.Current.InPreviewMode) + { + var text = value.ToString().ToLowerInvariant(); + var pos = text.IndexOf("", StringComparison.InvariantCultureIgnoreCase); + + if (pos > -1) + { + string markupToInject; + + if (UmbracoContext.Current.InPreviewMode) + { + // creating previewBadge markup + markupToInject = + String.Format(UmbracoSettings.PreviewBadge, + IOHelper.ResolveUrl(SystemDirectories.Umbraco), + IOHelper.ResolveUrl(SystemDirectories.UmbracoClient), + Server.UrlEncode(UmbracoContext.Current.HttpContext.Request.Path)); + } + else + { + // creating mini-profiler markup + markupToInject = Html.RenderProfiler().ToHtmlString(); + } + + var sb = new StringBuilder(text); + sb.Insert(pos, markupToInject); + + base.WriteLiteral(sb.ToString()); + return; + } + } + } + + base.WriteLiteral(value); + + + } + + public HelperResult RenderSection(string name, Func defaultContents) + { + return WebViewPageExtensions.RenderSection(this, name, defaultContents); + } + + public HelperResult RenderSection(string name, HelperResult defaultContents) + { + return WebViewPageExtensions.RenderSection(this, name, defaultContents); + } + + public HelperResult RenderSection(string name, string defaultContents) + { + return WebViewPageExtensions.RenderSection(this, name, defaultContents); + } + + public HelperResult RenderSection(string name, IHtmlString defaultContents) + { + return WebViewPageExtensions.RenderSection(this, name, defaultContents); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 0f2e5e5ce2..85dc520e4f 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -294,6 +294,10 @@ + + + + @@ -421,7 +425,7 @@ - +