2012-12-04 03:29:02 +05:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Text ;
2012-12-07 07:04:11 +05:00
using System.Text.RegularExpressions ;
2012-12-04 03:29:02 +05:00
using System.Web ;
using System.Web.Mvc ;
2012-12-04 22:06:55 +05:00
using System.Web.Routing ;
2012-12-04 03:29:02 +05:00
using System.Web.WebPages ;
2012-12-04 11:31:03 +05:00
using Umbraco.Core.IO ;
2012-12-04 03:29:02 +05:00
using umbraco.cms.businesslogic.macro ;
using umbraco.interfaces ;
2012-12-04 22:06:55 +05:00
using Umbraco.Web.Mvc ;
2012-12-09 00:50:58 +05:00
using Umbraco.Core ;
2013-01-19 06:38:02 +03:00
using System.Web.Mvc.Html ;
2012-12-04 03:29:02 +05:00
namespace Umbraco.Web.Macros
{
2013-03-11 20:11:11 +06:00
/// <summary>
/// A macro engine using MVC Partial Views to execute
/// </summary>
public class PartialViewMacroEngine : IMacroEngine
{
private readonly Func < HttpContextBase > _getHttpContext ;
private readonly Func < UmbracoContext > _getUmbracoContext ;
public const string EngineName = "Partial View Macro Engine" ;
public PartialViewMacroEngine ( )
{
_getHttpContext = ( ) = >
{
if ( HttpContext . Current = = null )
throw new InvalidOperationException ( "The " + this . GetType ( ) + " cannot execute with a null HttpContext.Current reference" ) ;
return new HttpContextWrapper ( HttpContext . Current ) ;
} ;
_getUmbracoContext = ( ) = >
{
if ( UmbracoContext . Current = = null )
throw new InvalidOperationException ( "The " + this . GetType ( ) + " cannot execute with a null UmbracoContext.Current reference" ) ;
return UmbracoContext . Current ;
} ;
}
/// <summary>
/// Constructor generally used for unit testing
/// </summary>
/// <param name="httpContext"></param>
/// <param name="umbracoContext"> </param>
internal PartialViewMacroEngine ( HttpContextBase httpContext , UmbracoContext umbracoContext )
{
_getHttpContext = ( ) = > httpContext ;
_getUmbracoContext = ( ) = > umbracoContext ;
}
public string Name
{
get { return EngineName ; }
}
2013-01-01 00:42:20 +03:00
//NOTE: We do not return any supported extensions because we don't want the MacroEngineFactory to return this
// macro engine when searching for engines via extension. Those types of engines are reserved for files that are
// stored in the ~/macroScripts folder and each engine must support unique extensions. This is a total Hack until
// we rewrite how macro engines work.
2012-12-04 03:29:02 +05:00
public IEnumerable < string > SupportedExtensions
{
2013-01-19 06:38:02 +03:00
get { return Enumerable . Empty < string > ( ) ; }
2012-12-04 03:29:02 +05:00
}
2013-01-01 00:42:20 +03:00
//NOTE: We do not return any supported extensions because we don't want the MacroEngineFactory to return this
// macro engine when searching for engines via extension. Those types of engines are reserved for files that are
// stored in the ~/macroScripts folder and each engine must support unique extensions. This is a total Hack until
// we rewrite how macro engines work.
2012-12-04 03:29:02 +05:00
public IEnumerable < string > SupportedUIExtensions
{
2013-01-01 00:42:20 +03:00
get { return Enumerable . Empty < string > ( ) ; }
2012-12-04 03:29:02 +05:00
}
2013-03-11 20:11:11 +06:00
public Dictionary < string , IMacroGuiRendering > SupportedProperties
{
get { throw new NotSupportedException ( ) ; }
}
public bool Validate ( string code , string tempFileName , INode currentPage , out string errorMessage )
{
var temp = GetVirtualPathFromPhysicalPath ( tempFileName ) ;
try
{
CompileAndInstantiate ( temp ) ;
}
catch ( Exception exception )
{
errorMessage = exception . Message ;
return false ;
}
errorMessage = string . Empty ;
return true ;
}
public string Execute ( MacroModel macro , INode currentPage )
{
if ( macro = = null ) throw new ArgumentNullException ( "macro" ) ;
if ( currentPage = = null ) throw new ArgumentNullException ( "currentPage" ) ;
2012-12-09 00:50:58 +05:00
if ( macro . ScriptName . IsNullOrWhiteSpace ( ) ) throw new ArgumentException ( "The ScriptName property of the macro object cannot be null or empty" ) ;
2013-09-06 15:34:19 -04:00
//if (!macro.ScriptName.StartsWith(SystemDirectories.MvcViews + "/MacroPartials/")
// && (!Regex.IsMatch(macro.ScriptName, "~/App_Plugins/.+?/Views/MacroPartials", RegexOptions.Compiled)))
//{
// throw new InvalidOperationException("Cannot render the Partial View Macro with file: " + macro.ScriptName + ". All Partial View Macros must exist in the " + SystemDirectories.MvcViews + "/MacroPartials/ folder");
//}
2013-03-11 20:11:11 +06:00
var http = _getHttpContext ( ) ;
var umbCtx = _getUmbracoContext ( ) ;
var routeVals = new RouteData ( ) ;
routeVals . Values . Add ( "controller" , "PartialViewMacro" ) ;
routeVals . Values . Add ( "action" , "Index" ) ;
routeVals . DataTokens . Add ( "umbraco-context" , umbCtx ) ; //required for UmbracoViewPage
2012-12-07 06:31:53 +05:00
2013-01-19 06:38:02 +03:00
//lets render this controller as a child action if we are currently executing using MVC
//(otherwise don't do this since we're using webforms)
var mvcHandler = http . CurrentHandler as MvcHandler ;
var viewContext = new ViewContext { ViewData = new ViewDataDictionary ( ) } ; ;
if ( mvcHandler ! = null )
{
//try and extract the current view context from the route values, this would be set in the UmbracoViewPage.
2013-03-14 22:01:09 +00:00
if ( mvcHandler . RequestContext . RouteData . DataTokens . ContainsKey ( Umbraco . Web . Mvc . Constants . DataTokenCurrentViewContext ) )
2013-01-19 06:38:02 +03:00
{
2013-03-14 22:01:09 +00:00
viewContext = ( ViewContext ) mvcHandler . RequestContext . RouteData . DataTokens [ Umbraco . Web . Mvc . Constants . DataTokenCurrentViewContext ] ;
2013-01-19 06:38:02 +03:00
}
routeVals . DataTokens . Add ( "ParentActionViewContext" , viewContext ) ;
}
2012-12-07 06:31:53 +05:00
2013-03-11 20:11:11 +06:00
var request = new RequestContext ( http , routeVals ) ;
string output ;
using ( var controller = new PartialViewMacroController ( umbCtx , macro , currentPage ) )
{
2013-01-19 06:38:02 +03:00
//bubble up the model state from the main view context to our custom controller.
//when merging we'll create a new dictionary, otherwise you might run into an enumeration error
// caused from ModelStateDictionary
controller . ModelState . Merge ( new ModelStateDictionary ( viewContext . ViewData . ModelState ) ) ;
controller . ControllerContext = new ControllerContext ( request , controller ) ;
//call the action to render
2013-03-11 20:11:11 +06:00
var result = controller . Index ( ) ;
2013-01-19 06:38:02 +03:00
output = controller . RenderViewResultAsString ( result ) ;
2013-03-11 20:11:11 +06:00
}
return output ;
}
private string GetVirtualPathFromPhysicalPath ( string physicalPath )
{
string rootpath = _getHttpContext ( ) . Server . MapPath ( "~/" ) ;
physicalPath = physicalPath . Replace ( rootpath , "" ) ;
physicalPath = physicalPath . Replace ( "\\" , "/" ) ;
return "~/" + physicalPath ;
}
private static PartialViewMacroPage CompileAndInstantiate ( string virtualPath )
{
//Compile Razor - We Will Leave This To ASP.NET Compilation Engine & ASP.NET WebPages
//Security in medium trust is strict around here, so we can only pass a virtual file path
//ASP.NET Compilation Engine caches returned types
//Changed From BuildManager As Other Properties Are Attached Like Context Path/
var webPageBase = WebPageBase . CreateInstanceFromVirtualPath ( virtualPath ) ;
var webPage = webPageBase as PartialViewMacroPage ;
if ( webPage = = null )
throw new InvalidCastException ( "All Partial View Macro views must inherit from " + typeof ( PartialViewMacroPage ) . FullName ) ;
return webPage ;
}
}
2013-01-19 06:38:02 +03:00
2012-12-04 03:29:02 +05:00
}