Files
Umbraco-CMS/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs

131 lines
5.4 KiB
C#

using System;
using System.Web.Mvc;
using Umbraco.Web.Routing;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Models.PublishedContent;
using Current = Umbraco.Web.Composing.Current;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
public class EnsurePublishedContentRequestAttribute : ActionFilterAttribute
{
private readonly string _dataTokenName;
private IUmbracoContextAccessor _umbracoContextAccessor;
private readonly int? _contentId;
private UmbracoHelper _helper;
/// <summary>
/// Constructor - can be used for testing
/// </summary>
/// <param name="umbracoContextAccessor"></param>
/// <param name="contentId"></param>
public EnsurePublishedContentRequestAttribute(IUmbracoContextAccessor umbracoContextAccessor, int contentId)
{
_umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
_contentId = contentId;
}
/// <summary>
/// A constructor used to set an explicit content Id to the PublishedRequest that will be created
/// </summary>
/// <param name="contentId"></param>
public EnsurePublishedContentRequestAttribute(int contentId)
{
_contentId = contentId;
}
/// <summary>
/// A constructor used to set the data token key name that contains a reference to a PublishedContent instance
/// </summary>
/// <param name="dataTokenName"></param>
public EnsurePublishedContentRequestAttribute(string dataTokenName)
{
_dataTokenName = dataTokenName;
}
/// <summary>
/// Constructor - can be used for testing
/// </summary>
/// <param name="umbracoContextAccessor"></param>
/// <param name="dataTokenName"></param>
public EnsurePublishedContentRequestAttribute(IUmbracoContextAccessor umbracoContextAccessor, string dataTokenName)
{
_umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
_dataTokenName = dataTokenName;
}
/// <summary>
/// Exposes the UmbracoContext
/// </summary>
protected UmbracoContext UmbracoContext => _umbracoContextAccessor?.UmbracoContext ?? Current.UmbracoContext;
// TODO: try lazy property injection?
private IPublishedRouter PublishedRouter => Core.Composing.Current.Factory.GetInstance<IPublishedRouter>();
/// <summary>
/// Exposes an UmbracoHelper
/// </summary>
protected UmbracoHelper Umbraco => _helper ?? (_helper = Current.Factory.GetInstance<UmbracoHelper>());
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);
}
/// <summary>
/// This assigns the published content to the request, developers can override this to specify
/// any other custom attributes required.
/// </summary>
/// <param name="request"></param>
/// <param name="filterContext"></param>
protected virtual void ConfigurePublishedContentRequest(PublishedRequest request, ActionExecutedContext filterContext)
{
if (_contentId.HasValue)
{
var content = Umbraco.Content(_contentId);
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);
}
}
}