Created
December 18, 2015 04:20
-
-
Save mbcrawfo/2269f5a1a402792ff2d8 to your computer and use it in GitHub Desktop.
Layouts for Postal outside of ASP.Net
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Temporary implementation until this PR is integrated into Postal: | |
// https://github.com/andrewdavey/postal/pull/117 | |
internal class FileSystemWithLayoutsRazorView | |
: IView | |
{ | |
private readonly IRazorEngineService m_razorService; | |
private readonly string m_cacheName; | |
/// <summary> | |
/// Creates a new <see cref="FileSystemRazorView"/> using the | |
/// given view filename. | |
/// </summary> | |
/// <param name="razorService"> | |
/// The RazorEngine IRazorEngineService to use to render the view | |
/// </param> | |
/// <param name="filename"> | |
/// The filename of the view. | |
/// </param> | |
public FileSystemWithLayoutsRazorView(IRazorEngineService razorService, | |
string filename) | |
{ | |
m_razorService = razorService; | |
m_cacheName = filename; | |
} | |
/// <summary> | |
/// Renders the view into the given <see cref="TextWriter"/>. | |
/// </summary> | |
/// <param name="viewContext"> | |
/// The <see cref="ViewContext"/> that contains the view data | |
/// model. | |
/// </param> | |
/// <param name="writer"> | |
/// The <see cref="TextWriter"/> used to write the rendered output. | |
/// </param> | |
public void Render(ViewContext viewContext, TextWriter writer) | |
{ | |
m_razorService.RunCompile(m_cacheName, writer, null, | |
viewContext.ViewData.Model); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// A Razor view engine configured to work outside of ASP.Net. | |
/// </summary> | |
internal class FileSystemWithLayoutsRazorViewEngine | |
: IViewEngine | |
{ | |
private readonly string m_viewPathRoot; | |
private readonly IRazorEngineService m_razorService; | |
/// <summary> | |
/// Create the view engine. | |
/// | |
/// Namespaces and libraries to be used during Razor compilation | |
/// are loaded from the Web.config in the root of the email | |
/// view directory. | |
/// </summary> | |
/// <param name="viewPathRoot"></param> | |
public FileSystemWithLayoutsRazorViewEngine(string viewPathRoot) | |
{ | |
m_viewPathRoot = viewPathRoot; | |
var razorConfig = new TemplateServiceConfiguration | |
{ | |
TemplateManager = new DelegateTemplateManager(ResolveTemplate), | |
ReferenceResolver = new ReferenceResolver() | |
}; | |
var webConfigPath = Path.Combine(viewPathRoot, "Web.config"); | |
if (File.Exists(webConfigPath)) | |
{ | |
var xml = XDocument.Parse(File.ReadAllText(webConfigPath)); | |
Debug.Assert(xml.Root != null); | |
var namespaces = xml.Root.Descendants("namespaces") | |
.SelectMany(n => n.Elements("add")) | |
.Select(e => e.Attribute("namespace").Value); | |
foreach (var ns in namespaces) | |
{ | |
razorConfig.Namespaces.Add(ns); | |
} | |
var libraries = xml.Root.Descendants("libraries") | |
.SelectMany(e => e.Elements("add")) | |
.Select(e => e.Attribute("dll").Value); | |
((ReferenceResolver) razorConfig.ReferenceResolver) | |
.AddLibraries(libraries); | |
} | |
m_razorService = RazorEngineService.Create(razorConfig); | |
} | |
/// <summary> | |
/// Tries to find a razor view (.cshtml or .vbhtml files). | |
/// </summary> | |
public ViewEngineResult FindPartialView( | |
ControllerContext controllerContext, string partialViewName, | |
bool useCache) | |
{ | |
IEnumerable<string> searchedPaths; | |
var existingPath = ResolveTemplatePath(partialViewName, | |
out searchedPaths); | |
if (existingPath != null) | |
{ | |
return new ViewEngineResult( | |
new FileSystemWithLayoutsRazorView(m_razorService, existingPath), | |
this); | |
} | |
return new ViewEngineResult(searchedPaths); | |
} | |
/// <summary> | |
/// Tries to find a razor view (.cshtml or .vbhtml files). | |
/// </summary> | |
public ViewEngineResult FindView(ControllerContext controllerContext, | |
string viewName, string masterName, bool useCache) | |
{ | |
return FindPartialView(controllerContext, viewName, useCache); | |
} | |
/// <summary> | |
/// Does nothing. | |
/// </summary> | |
public void ReleaseView(ControllerContext controllerContext, | |
IView view) | |
{ | |
// Nothing to do here - FileSystemRazorView does not need disposing. | |
} | |
private string GetViewFullPath(string path) | |
{ | |
return Path.Combine(m_viewPathRoot, path); | |
} | |
private string ResolveTemplate(string viewName) | |
{ | |
var path = ResolveTemplatePath(viewName); | |
if (path == null) | |
{ | |
return null; | |
} | |
return File.ReadAllText(path); | |
} | |
private string ResolveTemplatePath(string viewName) | |
{ | |
IEnumerable<string> searchedPaths; | |
var existingPath = ResolveTemplatePath(viewName, out searchedPaths); | |
return existingPath; | |
} | |
private string ResolveTemplatePath(string viewName, | |
out IEnumerable<string> searchedPaths) | |
{ | |
var possibleFilenames = new List<string>(); | |
var needsExtension = | |
!viewName.EndsWith(".cshtml", | |
StringComparison.OrdinalIgnoreCase) && | |
!viewName.EndsWith(".vbhtml", StringComparison.OrdinalIgnoreCase); | |
if (needsExtension) | |
{ | |
possibleFilenames.Add(viewName + ".cshtml"); | |
possibleFilenames.Add(viewName + ".vbhtml"); | |
} | |
else | |
{ | |
possibleFilenames.Add(viewName); | |
} | |
var possibleFullPaths = possibleFilenames | |
.Select(GetViewFullPath) | |
.ToArray(); | |
var existingPath = possibleFullPaths.FirstOrDefault(File.Exists); | |
searchedPaths = possibleFullPaths; | |
return existingPath; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal class HttpContextSafeViewEngineCollection | |
: ViewEngineCollection | |
{ | |
public HttpContextSafeViewEngineCollection() | |
{ | |
// Horrible reflection based hack to pass in custom dependency | |
// resolver. This is needed otherwise ViewEngineCollection uses | |
// global Autofac MVC resolver, which requires a HttpContext.Current, | |
// which doesn't work from background thread. | |
// http://discuss.hangfire.io/t/hangfire-job-throws-autofac-exception-even-though-job-class-is-not-managed-by-autofac/488/5 | |
var resolverField = typeof(ViewEngineCollection).GetField( | |
"_dependencyResolver", | |
BindingFlags.NonPublic | BindingFlags.Instance); | |
Debug.Assert(resolverField != null); | |
var resolver = new DefaultResolver(); | |
resolverField.SetValue(this, resolver); | |
} | |
private class DefaultResolver | |
: IDependencyResolver | |
{ | |
public object GetService(Type serviceType) | |
{ | |
return null; | |
} | |
public IEnumerable<object> GetServices(Type serviceType) | |
{ | |
return Enumerable.Empty<object>(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Resolves library references for the RazorEngine compiler. | |
/// </summary> | |
internal class ReferenceResolver | |
: IReferenceResolver | |
{ | |
private string[] m_libraries; | |
/// <summary> | |
/// Create a resolver to handle a list of library references. | |
/// </summary> | |
/// <param name="libraries"></param> | |
public ReferenceResolver(params string[] libraries) | |
{ | |
if (libraries == null) | |
throw new ArgumentNullException("libraries"); | |
m_libraries = libraries; | |
} | |
/// <summary> | |
/// Add libraries to the resolver. | |
/// </summary> | |
/// <param name="libraries"></param> | |
public void AddLibraries(IEnumerable<string> libraries) | |
{ | |
m_libraries = m_libraries.Union(libraries).ToArray(); | |
} | |
public IEnumerable<CompilerReference> GetReferences( | |
TypeContext context, | |
IEnumerable<CompilerReference> includeAssemblies = null) | |
{ | |
// get the paths of all currently loaded assemblies | |
var loadedAssemblies = | |
(new UseCurrentAssembliesReferenceResolver()) | |
.GetReferences(context, includeAssemblies) | |
.Select(r => r.GetFile()) | |
.ToList(); | |
return m_libraries | |
.Select(lib => CompilerReference.From( | |
FindLoadedAssembly(loadedAssemblies, lib))); | |
} | |
/// <summary> | |
/// Given a list of loaded assembly file references, finds and | |
/// returns the path matching the requested assembly name. | |
/// </summary> | |
/// <param name="references"></param> | |
/// <param name="name"></param> | |
/// <returns></returns> | |
private string FindLoadedAssembly( | |
IEnumerable<string> references, string name) | |
{ | |
return references.First(r => | |
{ | |
var filename = Path.GetFileName(r); | |
Debug.Assert(filename != null); | |
return filename.Equals(name, | |
StringComparison.CurrentCultureIgnoreCase); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment