259 lines
10 KiB
C#
259 lines
10 KiB
C#
using System;
|
|
using System.Text;
|
|
using System.Web;
|
|
using System.Web.Mvc;
|
|
using System.Web.WebPages;
|
|
using Umbraco.Core;
|
|
using Umbraco.Core.Cache;
|
|
using Umbraco.Core.Composing;
|
|
using Umbraco.Core.Configuration;
|
|
using Umbraco.Core.Dictionary;
|
|
using Umbraco.Core.IO;
|
|
using Umbraco.Core.Models.PublishedContent;
|
|
using Umbraco.Core.Services;
|
|
using Umbraco.Web.Models;
|
|
using Umbraco.Web.Routing;
|
|
using Umbraco.Web.Security;
|
|
using Current = Umbraco.Web.Composing.Current;
|
|
|
|
namespace Umbraco.Web.Mvc
|
|
{
|
|
/// <summary>
|
|
/// Represents the properties and methods that are needed in order to render an Umbraco view.
|
|
/// </summary>
|
|
public abstract class UmbracoViewPage<TModel> : WebViewPage<TModel>
|
|
{
|
|
private UmbracoContext _umbracoContext;
|
|
private UmbracoHelper _helper;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the database context.
|
|
/// </summary>
|
|
public ServiceContext Services { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the application cache.
|
|
/// </summary>
|
|
public AppCaches AppCaches { get; set; }
|
|
|
|
// TODO: previously, Services and ApplicationCache would derive from UmbracoContext.Application, which
|
|
// was an ApplicationContext - so that everything derived from UmbracoContext.
|
|
// UmbracoContext is fetched from the data tokens, thus allowing the view to be rendered with a
|
|
// custom context and NOT the Current.UmbracoContext - eg outside the normal Umbraco routing
|
|
// process.
|
|
// leaving it as-it for the time being but really - the UmbracoContext should be injected just
|
|
// like the Services & ApplicationCache properties, and have a setter for those special weird
|
|
// cases.
|
|
|
|
/// <summary>
|
|
/// Gets the Umbraco context.
|
|
/// </summary>
|
|
public UmbracoContext UmbracoContext => _umbracoContext
|
|
?? (_umbracoContext = ViewContext.GetUmbracoContext() ?? Current.UmbracoContext);
|
|
|
|
/// <summary>
|
|
/// Gets the public content request.
|
|
/// </summary>
|
|
internal PublishedRequest PublishedRequest
|
|
{
|
|
get
|
|
{
|
|
const string token = Core.Constants.Web.PublishedDocumentRequestDataToken;
|
|
|
|
// we should always try to return the object from the data tokens just in case its a custom object and not
|
|
// the one from UmbracoContext. Fallback to UmbracoContext if necessary.
|
|
|
|
// try view context
|
|
if (ViewContext.RouteData.DataTokens.ContainsKey(token))
|
|
return (PublishedRequest) ViewContext.RouteData.DataTokens.GetRequiredObject(token);
|
|
|
|
// child action, try parent view context
|
|
if (ViewContext.IsChildAction && ViewContext.ParentActionViewContext.RouteData.DataTokens.ContainsKey(token))
|
|
return (PublishedRequest) ViewContext.ParentActionViewContext.RouteData.DataTokens.GetRequiredObject(token);
|
|
|
|
// fallback to UmbracoContext
|
|
return UmbracoContext.PublishedRequest;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the Umbraco helper.
|
|
/// </summary>
|
|
public UmbracoHelper Umbraco
|
|
{
|
|
get
|
|
{
|
|
if (_helper != null) return _helper;
|
|
|
|
var model = ViewData.Model;
|
|
var content = model as IPublishedContent;
|
|
if (content == null && model is IContentModel)
|
|
content = ((IContentModel) model).Content;
|
|
|
|
_helper = Current.UmbracoHelper;
|
|
|
|
if (content != null)
|
|
_helper.AssignedContentItem = content;
|
|
|
|
return _helper;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the membership helper.
|
|
/// </summary>
|
|
public MembershipHelper Members => Umbraco.MembershipHelper;
|
|
|
|
protected UmbracoViewPage()
|
|
: this(
|
|
Current.Factory.GetInstance<ServiceContext>(),
|
|
Current.Factory.GetInstance<AppCaches>()
|
|
)
|
|
{
|
|
}
|
|
|
|
protected UmbracoViewPage(ServiceContext services, AppCaches appCaches)
|
|
{
|
|
Services = services;
|
|
AppCaches = appCaches;
|
|
}
|
|
|
|
// view logic below:
|
|
|
|
/// <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) return;
|
|
|
|
// this is used purely for partial view macros that contain forms and mostly
|
|
// just when rendered within the RTE - this should already be set with the
|
|
// EnsurePartialViewMacroViewContextFilterAttribute
|
|
if (ViewContext.RouteData.DataTokens.ContainsKey(Constants.DataTokenCurrentViewContext) == false)
|
|
ViewContext.RouteData.DataTokens.Add(Constants.DataTokenCurrentViewContext, ViewContext);
|
|
}
|
|
|
|
// maps model
|
|
protected override void SetViewData(ViewDataDictionary viewData)
|
|
{
|
|
// capture the model before we tinker with the viewData
|
|
var viewDataModel = viewData.Model;
|
|
|
|
// map the view data (may change its type, may set model to null)
|
|
viewData = MapViewDataDictionary(viewData, typeof (TModel));
|
|
|
|
// bind the model
|
|
viewData.Model = ContentModelBinder.BindModel(viewDataModel, typeof (TModel));
|
|
|
|
// set the view data
|
|
base.SetViewData(viewData);
|
|
}
|
|
|
|
// viewData is the ViewDataDictionary (maybe <TModel>) that we have
|
|
// modelType is the type of the model that we need to bind to
|
|
//
|
|
// figure out whether viewData can accept modelType else replace it
|
|
//
|
|
private static ViewDataDictionary MapViewDataDictionary(ViewDataDictionary viewData, Type modelType)
|
|
{
|
|
var viewDataType = viewData.GetType();
|
|
|
|
// if viewData is not generic then it is a simple ViewDataDictionary instance and its
|
|
// Model property is of type 'object' and will accept anything, so it is safe to use
|
|
// viewData
|
|
if (viewDataType.IsGenericType == false)
|
|
return viewData;
|
|
|
|
// ensure it is the proper generic type
|
|
var def = viewDataType.GetGenericTypeDefinition();
|
|
if (def != typeof(ViewDataDictionary<>))
|
|
throw new Exception("Could not map viewData of type \"" + viewDataType.FullName + "\".");
|
|
|
|
// get the viewData model type and compare with the actual view model type:
|
|
// viewData is ViewDataDictionary<viewDataModelType> and we will want to assign an
|
|
// object of type modelType to the Model property of type viewDataModelType, we
|
|
// need to check whether that is possible
|
|
var viewDataModelType = viewDataType.GenericTypeArguments[0];
|
|
|
|
if (viewDataModelType.IsAssignableFrom(modelType))
|
|
return viewData;
|
|
|
|
// if not possible then we need to create a new ViewDataDictionary
|
|
var nViewDataType = typeof(ViewDataDictionary<>).MakeGenericType(modelType);
|
|
var tViewData = new ViewDataDictionary(viewData) { Model = null }; // temp view data to copy values
|
|
var nViewData = (ViewDataDictionary)Activator.CreateInstance(nViewDataType, tViewData);
|
|
return nViewData;
|
|
}
|
|
|
|
/// <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 (Current.UmbracoContext.IsDebug || Current.UmbracoContext.InPreviewMode)
|
|
{
|
|
var text = value.ToString();
|
|
var pos = text.IndexOf("</body>", StringComparison.InvariantCultureIgnoreCase);
|
|
|
|
if (pos > -1)
|
|
{
|
|
string markupToInject;
|
|
|
|
if (Current.UmbracoContext.InPreviewMode)
|
|
{
|
|
// creating previewBadge markup
|
|
markupToInject =
|
|
string.Format(Current.Configs.Settings().Content.PreviewBadge,
|
|
IOHelper.ResolveUrl(SystemDirectories.Umbraco),
|
|
Server.UrlEncode(Current.UmbracoContext.HttpContext.Request.Url?.PathAndQuery));
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|