Web.Mvc - Refactor UmbracoViewPage, UmbracoTemplatePage, support strongly-typed content

Conflicts:
	src/Umbraco.Web/Umbraco.Web.csproj
This commit is contained in:
Stephan
2013-10-23 10:13:54 +02:00
parent 4487e686d7
commit 128a08f169
10 changed files with 812 additions and 243 deletions

View File

@@ -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<ContentType1>(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<ContentType1>(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<InvalidCastException>(() => 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<RenderModel<ContentType1>>(view.Model);
Assert.IsInstanceOf<ContentType1>(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<RenderModel<ContentType1>>(view.Model);
Assert.IsInstanceOf<ContentType2>(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<InvalidCastException>(() => view.SetViewDataX(viewData));
}
#endregion
#region RenderModelOf To ...
[Test]
public void RenderModelOf_ContentType1_To_RenderModel()
{
var content = new ContentType1(null);
var model = new RenderModel<ContentType1>(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<ContentType1>(content, CultureInfo.InvariantCulture);
var view = new ContentType1TestPage();
var viewData = new ViewDataDictionary(model);
view.ViewContext = GetViewContext();
view.SetViewDataX(viewData);
Assert.IsInstanceOf<ContentType1>(view.Model);
}
[Test]
public void RenderModelOf_ContentType2_To_ContentType1()
{
var content = new ContentType2(null);
var model = new RenderModel<ContentType2>(content, CultureInfo.InvariantCulture);
var view = new ContentType1TestPage();
var viewData = new ViewDataDictionary(model);
view.ViewContext = GetViewContext();
view.SetViewDataX(viewData);
Assert.IsInstanceOf<ContentType1>(view.Model);
}
[Test]
public void RenderModelOf_ContentType1_To_ContentType2()
{
var content = new ContentType1(null);
var model = new RenderModel<ContentType1>(content, CultureInfo.InvariantCulture);
var view = new ContentType2TestPage();
var viewData = new ViewDataDictionary(model);
view.ViewContext = GetViewContext();
Assert.Throws<InvalidCastException>(() => view.SetViewDataX(viewData));
}
[Test]
public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType1()
{
var content = new ContentType1(null);
var model = new RenderModel<ContentType1>(content, CultureInfo.InvariantCulture);
var view = new RenderModelOfContentType1TestPage();
var viewData = new ViewDataDictionary(model);
view.ViewContext = GetViewContext();
view.SetViewDataX(viewData);
Assert.IsInstanceOf<RenderModel<ContentType1>>(view.Model);
Assert.IsInstanceOf<ContentType1>(view.Model.Content);
}
[Test]
public void RenderModelOf_ContentType2_To_RenderModelOf_ContentType1()
{
var content = new ContentType2(null);
var model = new RenderModel<ContentType2>(content, CultureInfo.InvariantCulture);
var view = new RenderModelOfContentType1TestPage();
var viewData = new ViewDataDictionary(model);
view.ViewContext = GetViewContext();
view.SetViewDataX(viewData);
Assert.IsInstanceOf<RenderModel<ContentType1>>(view.Model);
Assert.IsInstanceOf<ContentType2>(view.Model.Content);
}
[Test]
public void RenderModelOf_ContentType1_To_RenderModelOf_ContentType2()
{
var content = new ContentType1(null);
var model = new RenderModel<ContentType1>(content, CultureInfo.InvariantCulture);
var view = new RenderModelOfContentType2TestPage();
var viewData = new ViewDataDictionary(model);
view.ViewContext = GetViewContext();
Assert.Throws<InvalidCastException>(() => 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<RenderModel>(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<RenderModel<ContentType1>>(view.Model);
Assert.IsInstanceOf<ContentType1>(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<RenderModel<ContentType1>>(view.Model);
Assert.IsInstanceOf<ContentType1>(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<InvalidCastException>(() =>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<ContentType1>(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<InvalidCastException>(() => 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<ContentType1>(view.Model);
}
#endregion
#region Test elements
public class TestPage<TModel> : UmbracoViewPage<TModel>
{
public override void Execute()
{
throw new NotImplementedException();
}
public void SetViewDataX(ViewDataDictionary viewData)
{
SetViewData(viewData);
}
}
public class RenderModelTestPage : TestPage<RenderModel>
{ }
public class RenderModelOfContentType1TestPage : TestPage<RenderModel<ContentType1>>
{ }
public class RenderModelOfContentType2TestPage : TestPage<RenderModel<ContentType2>>
{ }
public class ContentType1TestPage : TestPage<ContentType1>
{ }
public class ContentType2TestPage : TestPage<ContentType2>
{ }
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<IContentFinder>(),
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
}
}

View File

@@ -145,6 +145,7 @@
<ItemGroup>
<Compile Include="AttemptTests.cs" />
<Compile Include="MockTests.cs" />
<Compile Include="Mvc\UmbracoViewPageTests.cs" />
<Compile Include="Persistence\Auditing\AuditTests.cs" />
<Compile Include="BootManagers\CoreBootManagerTests.cs" />
<Compile Include="BusinessLogic\DictionaryTest.cs" />

View File

@@ -0,0 +1,9 @@
using Umbraco.Core.Models;
namespace Umbraco.Web.Models
{
public interface IRenderModel
{
IPublishedContent Content { get; }
}
}

View File

@@ -1,13 +1,14 @@
using System;
using System.Globalization;
using Umbraco.Core.Models;
using Umbraco.Web.Mvc;
namespace Umbraco.Web.Models
{
/// <summary>
/// Represents the model for the current rendering page in Umbraco
/// </summary>
public class RenderModel
public class RenderModel : IRenderModel
{
/// <summary>
/// Constructor specifying both the IPublishedContent and the CultureInfo
@@ -16,7 +17,7 @@ namespace Umbraco.Web.Models
/// <param name="culture"></param>
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
/// <param name="content"></param>
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");

View File

@@ -0,0 +1,36 @@
using System.Globalization;
using Umbraco.Core.Models;
using Umbraco.Web.Mvc;
namespace Umbraco.Web.Models
{
public class RenderModel<TContent> : RenderModel
where TContent : class, IPublishedContent
{
/// <summary>
/// Constructor specifying both the IPublishedContent and the CultureInfo
/// </summary>
/// <param name="content"></param>
/// <param name="culture"></param>
public RenderModel(TContent content, CultureInfo culture)
: base(content, culture)
{
Content = content;
}
/// <summary>
/// Constructor to set the IPublishedContent and the CurrentCulture is set by the UmbracoContext
/// </summary>
/// <param name="content"></param>
public RenderModel(TContent content)
: base(content)
{
Content = content;
}
/// <summary>
/// Returns the current IPublishedContent object
/// </summary>
public new TContent Content { get; private set; }
}
}

View File

@@ -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
/// </summary>
public abstract class UmbracoTemplatePage : UmbracoViewPage<RenderModel>
{
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;
/// <summary>
/// Returns the a DynamicPublishedContent object
/// Returns the content as a dynamic object
/// </summary>
public dynamic CurrentPage { get; private set; }
private UmbracoHelper _helper;
/// <summary>
/// Gets an UmbracoHelper
/// </summary>
/// <remarks>
/// This ensures that the UmbracoHelper is constructed with the content model of this view
/// </remarks>
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());
}
}
}

View File

@@ -0,0 +1,26 @@
using Umbraco.Core.Dynamics;
using Umbraco.Core.Models;
using Umbraco.Web.Models;
namespace Umbraco.Web.Mvc
{
public abstract class UmbracoTemplatePage<TContent> : UmbracoViewPage<RenderModel<TContent>>
where TContent : class, IPublishedContent
{
private object _currentPage;
/// <summary>
/// Returns the content as a dynamic object
/// </summary>
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());
}
}
}
}

View File

@@ -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
{
/// <summary>
/// The View that umbraco front-end views inherit from
/// </summary>
public abstract class UmbracoViewPage<T> : WebViewPage<T>
{
protected UmbracoViewPage()
{
}
/// <summary>
/// Returns the current UmbracoContext
/// </summary>
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;
}
}
/// <summary>
/// Returns the current ApplicationContext
/// </summary>
public ApplicationContext ApplicationContext
{
get { return UmbracoContext.Application; }
}
/// <summary>
/// Returns the current PublishedContentRequest
/// </summary>
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;
/// <summary>
/// Gets an UmbracoHelper
/// </summary>
/// <remarks>
/// This constructs the UmbracoHelper with the content model of the page routed to
/// </remarks>
public virtual UmbracoHelper Umbraco
{
get { return _helper ?? (_helper = new UmbracoHelper(UmbracoContext)); }
}
/// <summary>
/// Ensure that the current view context is added to the route data tokens so we can extract it if we like
/// </summary>
/// <remarks>
/// Currently this is required by mvc macro engines
/// </remarks>
protected override void InitializePage()
{
base.InitializePage();
if (!ViewContext.IsChildAction)
{
if (!ViewContext.RouteData.DataTokens.ContainsKey(Constants.DataTokenCurrentViewContext))
{
ViewContext.RouteData.DataTokens.Add(Constants.DataTokenCurrentViewContext, this.ViewContext);
}
}
}
/// <summary>
/// This will detect the end /body tag and insert the preview badge if in preview mode
/// </summary>
/// <param name="value"></param>
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("</body>", 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<dynamic, HelperResult> 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);
}
}
}
public abstract class UmbracoViewPage : UmbracoViewPage<IPublishedContent>
{ }
}

View File

@@ -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
{
/// <summary>
/// The View that umbraco front-end views inherit from
/// </summary>
public abstract class UmbracoViewPage<TModel> : WebViewPage<TModel>
{
/// <summary>
/// Returns the current UmbracoContext
/// </summary>
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;
}
}
/// <summary>
/// Returns the current ApplicationContext
/// </summary>
public ApplicationContext ApplicationContext
{
get { return UmbracoContext.Application; }
}
/// <summary>
/// Returns the current PublishedContentRequest
/// </summary>
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;
/// <summary>
/// Gets an UmbracoHelper
/// </summary>
/// <remarks>
/// This constructs the UmbracoHelper with the content model of the page routed to
/// </remarks>
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;
}
}
/// <summary>
/// Ensure that the current view context is added to the route data tokens so we can extract it if we like
/// </summary>
/// <remarks>
/// Currently this is required by mvc macro engines
/// </remarks>
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<TModel>()) // 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<IRenderModel>())
{
sourceContent = ((IRenderModel)source).Content;
}
if (sourceContent == null)
{
var attempt = source.TryConvertTo<IPublishedContent>();
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<IPublishedContent>())
{
// 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<TModel>();
if (attempt.Success) viewData.Model = attempt.Result;
}
base.SetViewData(viewData);
}
/// <summary>
/// This will detect the end /body tag and insert the preview badge if in preview mode
/// </summary>
/// <param name="value"></param>
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("</body>", 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<dynamic, HelperResult> 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);
}
}
}

View File

@@ -294,6 +294,10 @@
<Compile Include="Controllers\ProfileController.cs" />
<Compile Include="Controllers\LoginStatusController.cs" />
<Compile Include="Editors\MediaController.cs" />
<Compile Include="Models\IRenderModel.cs" />
<Compile Include="Models\RenderModelOfTContent.cs" />
<Compile Include="Mvc\UmbracoTemplatePageOfTContent.cs" />
<Compile Include="Mvc\UmbracoViewPage.cs" />
<Compile Include="Models\ContentEditing\ContentSortOrder.cs" />
<Compile Include="Controllers\RegisterController.cs" />
<Compile Include="Models\ProfileModel.cs" />
@@ -421,7 +425,7 @@
<Compile Include="Mvc\UmbracoController.cs" />
<Compile Include="Mvc\UmbracoControllerFactory.cs" />
<Compile Include="Mvc\UmbracoMvcHandler.cs" />
<Compile Include="Mvc\UmbracoViewPage.cs" />
<Compile Include="Mvc\UmbracoViewPageOfTModel.cs" />
<Compile Include="PublishedCache\IPublishedCache.cs" />
<Compile Include="PublishedCache\IPublishedContentCache.cs" />
<Compile Include="PublishedCache\IPublishedMediaCache.cs" />