2012-07-20 01:04:35 +06:00
using System ;
using System.Linq ;
using System.Text ;
2012-10-26 02:55:57 +05:00
using System.Threading ;
using System.Web ;
2012-07-20 01:04:35 +06:00
using System.Xml ;
using System.Globalization ;
using System.Diagnostics ;
// legacy
2012-07-30 22:52:59 +06:00
using Umbraco.Core ;
using Umbraco.Core.Logging ;
2012-08-10 13:08:47 +06:00
using Umbraco.Core.Models ;
2012-08-30 08:26:01 +07:00
using umbraco ;
2012-07-21 00:20:50 +06:00
using umbraco.BusinessLogic ;
2012-08-08 23:10:34 +06:00
using umbraco.NodeFactory ;
2012-07-20 01:04:35 +06:00
using umbraco.cms.businesslogic.web ;
using umbraco.cms.businesslogic.template ;
using umbraco.cms.businesslogic.member ;
2012-08-08 23:10:34 +06:00
using umbraco.interfaces ;
2012-07-20 01:04:35 +06:00
namespace Umbraco.Web.Routing
{
2012-08-06 22:40:06 +06:00
/// <summary>
/// represents a request for one specified Umbraco document to be rendered
/// by one specified template, using one particular culture.
/// </summary>
2012-10-02 01:40:19 +05:00
internal class PublishedContentRequest
2012-07-20 01:04:35 +06:00
{
2012-10-26 02:55:57 +05:00
/// <summary>
2012-10-29 12:35:39 -01:00
/// Assigns the request to the http context and proceeds to process the request. If everything is successful, invoke the callback.
2012-10-26 02:55:57 +05:00
/// </summary>
/// <param name="httpContext"></param>
/// <param name="umbracoContext"></param>
/// <param name="onSuccess"></param>
2012-10-29 12:35:39 -01:00
internal void ProcessRequest ( HttpContextBase httpContext , UmbracoContext umbracoContext , Action < PublishedContentRequest > onSuccess )
2012-10-26 02:55:57 +05:00
{
if ( umbracoContext = = null )
throw new NullReferenceException ( "The UmbracoContext.Current is null, ProcessRequest cannot proceed unless there is a current UmbracoContext" ) ;
if ( umbracoContext . RoutingContext = = null )
throw new NullReferenceException ( "The UmbracoContext.RoutingContext has not been assigned, ProcessRequest cannot proceed unless there is a RoutingContext assigned to the UmbracoContext" ) ;
//assign back since this is a front-end request
2012-10-29 12:35:39 -01:00
umbracoContext . PublishedContentRequest = this ;
2012-10-26 02:55:57 +05:00
// note - at that point the original legacy module did something do handle IIS custom 404 errors
// ie pages looking like /anything.aspx?404;/path/to/document - I guess the reason was to support
// "directory urls" without having to do wildcard mapping to ASP.NET on old IIS. This is a pain
// to maintain and probably not used anymore - removed as of 06/2012. @zpqrtbnk.
//
// to trigger Umbraco's not-found, one should configure IIS and/or ASP.NET custom 404 errors
// so that they point to a non-existing page eg /redirect-404.aspx
// TODO: SD: We need more information on this for when we release 4.10.0 as I'm not sure what this means.
//find domain
2012-10-29 12:35:39 -01:00
_builder . LookupDomain ( ) ;
2012-10-26 02:55:57 +05:00
// redirect if it has been flagged
2012-10-29 12:35:39 -01:00
if ( this . IsRedirect )
httpContext . Response . Redirect ( this . RedirectUrl , true ) ;
2012-11-19 14:11:34 -01:00
//set the culture on the thread - once, so it's set when running document lookups
2012-10-29 12:35:39 -01:00
Thread . CurrentThread . CurrentUICulture = Thread . CurrentThread . CurrentCulture = this . Culture ;
2012-10-26 02:55:57 +05:00
//find the document, found will be true if the doc request has found BOTH a node and a template
// though currently we don't use this value.
2012-10-29 12:35:39 -01:00
var found = _builder . LookupDocument ( ) ;
2012-11-19 14:11:34 -01:00
//set the culture on the thread -- again, 'cos it might have changed due to a wildcard domain
Thread . CurrentThread . CurrentUICulture = Thread . CurrentThread . CurrentCulture = this . Culture ;
2012-10-26 02:55:57 +05:00
//this could be called in the LookupDocument method, but I've just put it here for clarity.
2012-10-29 12:35:39 -01:00
_builder . DetermineRenderingEngine ( ) ;
2012-10-26 02:55:57 +05:00
//TODO: here we should launch an event so that people can modify the doc request to do whatever they want.
// redirect if it has been flagged
2012-10-29 12:35:39 -01:00
if ( this . IsRedirect )
httpContext . Response . Redirect ( this . RedirectUrl , true ) ;
2012-10-26 02:55:57 +05:00
// handle 404
2012-10-29 12:35:39 -01:00
if ( this . Is404 )
2012-10-26 02:55:57 +05:00
{
httpContext . Response . StatusCode = 404 ;
2012-10-29 12:35:39 -01:00
if ( ! this . HasNode )
2012-10-26 02:55:57 +05:00
{
httpContext . RemapHandler ( new PublishedContentNotFoundHandler ( ) ) ;
return ;
}
// else we have a document to render
// not having a template is ok here, MVC will take care of it
}
// just be safe - should never ever happen
2012-10-29 12:35:39 -01:00
if ( ! this . HasNode )
2012-10-26 02:55:57 +05:00
throw new Exception ( "No document to render." ) ;
2012-11-19 14:11:34 -01:00
// trigger PublishedContentRequest.Rendering event?
// with complete access to the content request?
2012-10-26 02:55:57 +05:00
// render even though we might have no template
// to give MVC a chance to hijack routes
// pass off to our handlers (mvc or webforms)
// assign the legacy page back to the docrequest
// handlers like default.aspx will want it and most macros currently need it
2012-10-29 12:35:39 -01:00
this . UmbracoPage = new page ( this ) ;
2012-10-26 02:55:57 +05:00
// these two are used by many legacy objects
2012-10-29 12:35:39 -01:00
httpContext . Items [ "pageID" ] = this . DocumentId ;
httpContext . Items [ "pageElements" ] = this . UmbracoPage . Elements ;
2012-10-26 02:55:57 +05:00
if ( onSuccess ! = null )
2012-10-29 12:35:39 -01:00
onSuccess ( this ) ;
2012-10-26 02:55:57 +05:00
}
2012-10-30 06:49:36 +06:00
/// <summary>
/// After execution is handed off to MVC, we can finally check if the request has: No Template assigned and also the
/// route is not hijacked. When this occurs, we need to send the routing back through the builder to check for
/// not found handlers.
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
2012-10-29 12:35:39 -01:00
internal IHttpHandler ProcessNoTemplateInMvc ( HttpContextBase httpContext )
{
var content = this . PublishedContent ;
this . PublishedContent = null ;
_builder . LookupDocument2 ( ) ;
_builder . DetermineRenderingEngine ( ) ;
// redirect if it has been flagged
if ( this . IsRedirect )
2012-10-30 06:49:36 +06:00
{
2012-10-29 12:35:39 -01:00
httpContext . Response . Redirect ( this . RedirectUrl , true ) ;
2012-10-30 06:49:36 +06:00
}
2012-10-29 12:35:39 -01:00
// here .Is404 _has_ to be true
httpContext . Response . StatusCode = 404 ;
if ( ! this . HasNode )
{
// means the builder could not find a proper document to handle 404
// restore the saved content so we know it exists
this . PublishedContent = content ;
return new PublishedContentNotFoundHandler ( ) ;
}
if ( ! this . HasTemplate )
{
// means the builder could find a proper document, but the document has no template
// at that point there isn't much we can do and there is no point returning
// to Mvc since Mvc can't do much
return new PublishedContentNotFoundHandler ( "In addition, no template exists to render the custom 404." ) ;
}
// render even though we might have no template
// to give MVC a chance to hijack routes
// pass off to our handlers (mvc or webforms)
// assign the legacy page back to the docrequest
// handlers like default.aspx will want it and most macros currently need it
this . UmbracoPage = new page ( this ) ;
// these two are used by many legacy objects
httpContext . Items [ "pageID" ] = this . DocumentId ;
httpContext . Items [ "pageElements" ] = this . UmbracoPage . Elements ;
switch ( this . RenderingEngine )
{
case Core . RenderingEngine . Mvc :
return null ;
case Core . RenderingEngine . WebForms :
default :
return ( global :: umbraco . UmbracoDefault ) System . Web . Compilation . BuildManager . CreateInstanceFromVirtualPath ( "~/default.aspx" , typeof ( global :: umbraco . UmbracoDefault ) ) ;
}
}
private PublishedContentRequestBuilder _builder ;
2012-10-26 02:55:57 +05:00
/// <summary>
2012-10-29 12:35:39 -01:00
/// Initializes a new instance of the <see cref="PublishedContentRequest"/> class with a specific Uri and routing context.
2012-10-26 02:55:57 +05:00
/// </summary>
2012-10-29 12:35:39 -01:00
/// <param name="uri">The request <c>Uri</c>.</param>
/// <param name="routingContext">A routing context.</param>
2012-10-02 01:40:19 +05:00
public PublishedContentRequest ( Uri uri , RoutingContext routingContext )
2012-07-20 01:04:35 +06:00
{
2012-10-26 02:55:57 +05:00
if ( uri = = null ) throw new ArgumentNullException ( "uri" ) ;
if ( routingContext = = null ) throw new ArgumentNullException ( "routingContext" ) ;
2012-07-20 18:54:59 -02:00
this . Uri = uri ;
2012-10-29 12:35:39 -01:00
this . RoutingContext = routingContext ;
_builder = new PublishedContentRequestBuilder ( this ) ;
2012-10-26 02:55:57 +05:00
2012-10-29 12:35:39 -01:00
// set default
this . RenderingEngine = RenderingEngine . Mvc ;
2012-07-20 01:04:35 +06:00
}
2012-10-29 12:35:39 -01:00
#region Properties
2012-07-20 23:10:43 +06:00
/// <summary>
2012-10-29 12:35:39 -01:00
/// The identifier of the requested node, if any, else zero.
2012-07-20 23:10:43 +06:00
/// </summary>
int _nodeId = 0 ;
2012-10-29 12:35:39 -01:00
/// <summary>
/// The requested node, if any, else <c>null</c>.
/// </summary>
2012-10-02 01:35:39 +05:00
private IPublishedContent _publishedContent = null ;
2012-07-20 01:04:35 +06:00
2012-10-29 12:35:39 -01:00
/// <summary>
/// The "umbraco page" object.
/// </summary>
private page _umbracoPage ;
2012-07-20 01:04:35 +06:00
2012-07-20 23:10:43 +06:00
/// <summary>
2012-10-29 12:35:39 -01:00
/// Gets or sets the current RoutingContext.
2012-07-20 23:10:43 +06:00
/// </summary>
2012-07-21 00:20:50 +06:00
public RoutingContext RoutingContext { get ; private set ; }
2012-08-09 04:15:35 +06:00
2012-08-08 23:55:55 +06:00
/// <summary>
2012-10-29 12:35:39 -01:00
/// Gets or sets the cleaned up Uri used for routing.
2012-08-08 23:55:55 +06:00
/// </summary>
2012-07-20 18:54:59 -02:00
public Uri Uri { get ; private set ; }
2012-07-20 01:04:35 +06:00
/// <summary>
2012-10-29 12:35:39 -01:00
/// Gets or sets the content request's domain.
2012-07-20 01:04:35 +06:00
/// </summary>
2012-08-09 04:15:35 +06:00
public Domain Domain { get ; internal set ; }
2012-07-20 01:04:35 +06:00
2012-10-29 12:35:39 -01:00
/// <summary>
/// Gets or sets the content request's domain Uri.
/// </summary>
/// <remarks>The <c>Domain</c> may contain "example.com" whereas the <c>Uri</c> will be fully qualified eg "http://example.com/".</remarks>
2012-08-09 04:15:35 +06:00
public Uri DomainUri { get ; internal set ; }
2012-07-20 18:54:59 -02:00
2012-08-29 08:54:29 +07:00
/// <summary>
2012-10-29 12:35:39 -01:00
/// Gets or sets whether the rendering engine is MVC or WebForms.
2012-08-29 08:54:29 +07:00
/// </summary>
2012-09-07 07:57:25 +07:00
public RenderingEngine RenderingEngine { get ; internal set ; }
2012-08-29 08:54:29 +07:00
2012-07-20 01:04:35 +06:00
/// <summary>
2012-10-29 12:35:39 -01:00
/// Gets a value indicating whether the content request has a domain.
2012-07-20 01:04:35 +06:00
/// </summary>
public bool HasDomain
{
get { return this . Domain ! = null ; }
}
/// <summary>
2012-10-29 12:35:39 -01:00
/// Gets or sets the content request's culture.
2012-07-20 01:04:35 +06:00
/// </summary>
2012-08-08 23:10:34 +06:00
public CultureInfo Culture { get ; set ; }
2012-07-20 01:04:35 +06:00
2012-08-30 08:26:01 +07:00
/// <summary>
2012-10-29 12:35:39 -01:00
/// Gets or sets the "umbraco page" object.
2012-08-30 08:26:01 +07:00
/// </summary>
/// <remarks>
2012-09-01 07:03:56 +07:00
/// This value is only used for legacy/webforms code.
2012-08-30 08:26:01 +07:00
/// </remarks>
2012-09-01 07:03:56 +07:00
internal page UmbracoPage
2012-08-30 08:26:01 +07:00
{
2012-09-01 07:03:56 +07:00
get
2012-08-30 08:26:01 +07:00
{
2012-09-01 07:03:56 +07:00
if ( _umbracoPage = = null )
throw new InvalidOperationException ( "The umbraco page object is only available once Finalize()" ) ;
2012-10-29 12:35:39 -01:00
2012-09-01 07:03:56 +07:00
return _umbracoPage ;
2012-08-30 08:26:01 +07:00
}
2012-09-01 07:03:56 +07:00
set { _umbracoPage = value ; }
2012-08-30 08:26:01 +07:00
}
2012-09-01 07:03:56 +07:00
2012-08-30 08:26:01 +07:00
// TODO: fixme - do we want to have an ordered list of alternate cultures,
2012-07-20 01:04:35 +06:00
// to allow for fallbacks when doing dictionnary lookup and such?
2012-10-29 12:35:39 -01:00
/// <summary>
/// Gets or sets the requested content.
/// </summary>
2012-11-21 10:12:04 -01:00
/// <remarks>Setting the requested content clears both <c>Template</c> and <c>AlternateTemplateAlias</c>.</remarks>
2012-10-02 01:35:39 +05:00
public IPublishedContent PublishedContent
2012-08-14 23:35:34 +06:00
{
2012-10-02 01:35:39 +05:00
get { return _publishedContent ; }
2012-08-10 13:08:47 +06:00
set
2012-08-08 23:10:34 +06:00
{
2012-10-02 01:35:39 +05:00
_publishedContent = value ;
2012-08-30 08:26:01 +07:00
this . Template = null ;
2012-11-21 10:12:04 -01:00
this . AlternateTemplateAlias = null ;
2012-10-02 01:35:39 +05:00
_nodeId = _publishedContent ! = null ? _publishedContent . Id : 0 ;
2012-08-08 23:10:34 +06:00
}
}
2012-07-20 01:04:35 +06:00
/// <summary>
2012-10-29 12:35:39 -01:00
/// Gets or sets the template to use to display the requested content.
2012-07-20 01:04:35 +06:00
/// </summary>
2012-08-30 08:26:01 +07:00
public Template Template { get ; set ; }
2012-07-20 01:04:35 +06:00
/// <summary>
2012-10-29 12:35:39 -01:00
/// Gets a value indicating whether the content request has a template.
2012-07-20 01:04:35 +06:00
/// </summary>
public bool HasTemplate
{
2012-08-30 08:26:01 +07:00
get { return this . Template ! = null ; }
2012-07-20 01:04:35 +06:00
}
2012-11-21 10:12:04 -01:00
/// <summary>
/// Gets or sets the alternate template alias.
/// </summary>
/// <remarks>
/// <para>When <c>null</c> or empty, use the default template.</para>
/// <para>Alternate template works only when displaying the intended document and should be set
/// after <c>PublishedContent</c> since setting <c>PublishedContent</c> clears the alternate template.</para>
/// </remarks>
public string AlternateTemplateAlias { get ; set ; }
2012-07-20 01:04:35 +06:00
/// <summary>
2012-10-29 12:35:39 -01:00
/// Gets the identifier of the requested content.
2012-07-20 01:04:35 +06:00
/// </summary>
2012-10-29 12:35:39 -01:00
/// <exception cref="InvalidOperationException">Thrown when the content request has no content.</exception>
2012-08-30 08:26:01 +07:00
public int DocumentId
2012-07-20 01:04:35 +06:00
{
get
{
2012-10-02 01:35:39 +05:00
if ( this . PublishedContent = = null )
2012-10-02 01:40:19 +05:00
throw new InvalidOperationException ( "PublishedContentRequest has no document." ) ;
2012-07-20 01:04:35 +06:00
return _nodeId ;
}
}
/// <summary>
2012-10-29 12:35:39 -01:00
/// Gets a value indicating whether the content request has a content.
2012-07-20 01:04:35 +06:00
/// </summary>
public bool HasNode
{
2012-10-02 01:35:39 +05:00
get { return this . PublishedContent ! = null ; }
2012-07-20 01:04:35 +06:00
}
/// <summary>
2012-10-29 12:35:39 -01:00
/// Gets or sets a value indicating whether the requested content could not be found.
2012-07-20 01:04:35 +06:00
/// </summary>
2012-10-29 12:35:39 -01:00
/// <remarks>This is set in the <c>PublishedContentRequestBuilder</c>.</remarks>
2012-09-29 07:20:23 +07:00
internal bool Is404 { get ; set ; }
2012-07-20 01:04:35 +06:00
/// <summary>
2012-10-29 12:35:39 -01:00
/// Gets a value indicating whether the content request triggers a redirect.
2012-07-20 01:04:35 +06:00
/// </summary>
public bool IsRedirect { get { return ! string . IsNullOrWhiteSpace ( this . RedirectUrl ) ; } }
/// <summary>
2012-10-29 12:35:39 -01:00
/// Gets or sets the url to redirect to, when the content request triggers a redirect.
2012-07-20 01:04:35 +06:00
/// </summary>
public string RedirectUrl { get ; set ; }
2012-10-29 12:35:39 -01:00
#endregion
2012-07-20 01:04:35 +06:00
}
}