mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +08:00
Fixing templates rendering in background tasks
This commit is contained in:
@@ -169,7 +169,6 @@
|
||||
<Compile Include="Migrations.cs" />
|
||||
<Compile Include="Services\ITemplateProcessor.cs" />
|
||||
<Compile Include="Services\ITemplateService.cs" />
|
||||
<Compile Include="Services\StubHttpContext.cs" />
|
||||
<Compile Include="Services\TemplateProcessorImpl.cs" />
|
||||
<Compile Include="Services\RazorTemplateProcessor.cs" />
|
||||
<Compile Include="Services\TemplateBindingStrategy.cs" />
|
||||
|
||||
@@ -6,7 +6,6 @@ using System.Web.Mvc;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.DisplayManagement.Implementation;
|
||||
using Orchard.Mvc;
|
||||
using Orchard.Templates.Models;
|
||||
|
||||
namespace Orchard.Templates.Services {
|
||||
@@ -17,7 +16,7 @@ namespace Orchard.Templates.Services {
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IEnumerable<ITemplateProcessor> _processors;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly HttpContextBase _httpContextBase;
|
||||
|
||||
public DefaultTemplateService(
|
||||
IShapeFactory shapeFactory,
|
||||
@@ -25,13 +24,13 @@ namespace Orchard.Templates.Services {
|
||||
IWorkContextAccessor workContextAccessor,
|
||||
IContentManager contentManager,
|
||||
IEnumerable<ITemplateProcessor> processors,
|
||||
IHttpContextAccessor httpContextAccessor) {
|
||||
HttpContextBase httpContextBase) {
|
||||
_shapeFactory = shapeFactory;
|
||||
_displayHelperFactory = displayHelperFactory;
|
||||
_workContextAccessor = workContextAccessor;
|
||||
_contentManager = contentManager;
|
||||
_processors = processors;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_httpContextBase = httpContextBase;
|
||||
}
|
||||
|
||||
public string ExecuteShape(string shapeType) {
|
||||
@@ -40,16 +39,12 @@ namespace Orchard.Templates.Services {
|
||||
|
||||
public string ExecuteShape(string shapeType, INamedEnumerable<object> parameters) {
|
||||
var shape = _shapeFactory.Create(shapeType, parameters);
|
||||
var result = "";
|
||||
|
||||
ExecuteInHttpContext(httpContext => {
|
||||
var viewContext = new ViewContext { HttpContext = httpContext };
|
||||
viewContext.RouteData.DataTokens["IWorkContextAccessor"] = _workContextAccessor;
|
||||
var display = _displayHelperFactory.CreateHelper(viewContext, new ViewDataContainer());
|
||||
result = ((DisplayHelper)display).ShapeExecute(shape).ToString();
|
||||
});
|
||||
var viewContext = new ViewContext { HttpContext = _httpContextBase };
|
||||
viewContext.RouteData.DataTokens["IWorkContextAccessor"] = _workContextAccessor;
|
||||
var display = _displayHelperFactory.CreateHelper(viewContext, new ViewDataContainer());
|
||||
|
||||
return result;
|
||||
return ((DisplayHelper)display).ShapeExecute(shape).ToString();
|
||||
}
|
||||
|
||||
public string Execute<TModel>(string template, string name, string language, TModel model = default(TModel)) {
|
||||
@@ -65,26 +60,6 @@ namespace Orchard.Templates.Services {
|
||||
return _contentManager.Query<ShapePart>(versionOptions ?? VersionOptions.Published).List();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the action in an HttpContext, regardless of whether or not we're in a ThreadStaticScope or HttpContextScope.
|
||||
/// </summary>
|
||||
private void ExecuteInHttpContext(Action<HttpContextBase> action) {
|
||||
var httpContext = _httpContextAccessor.Current();
|
||||
|
||||
if (httpContext != null) {
|
||||
action(httpContext);
|
||||
return;
|
||||
}
|
||||
|
||||
// We're not using the ViewContext.HttpContext because that will be an EmptyHttpContext when we're on a background thread.
|
||||
// EmptyHttpContext does not implement Items, which is necessary for Orchard when storing and accessing certain services such as IWorkContextAccessor.
|
||||
httpContext = new StubHttpContext();
|
||||
using (var scope = _workContextAccessor.CreateWorkContextScope(httpContext)) {
|
||||
_httpContextAccessor.Set(httpContext);
|
||||
action(httpContext);
|
||||
}
|
||||
}
|
||||
|
||||
private class ViewDataContainer : IViewDataContainer {
|
||||
public ViewDataDictionary ViewData { get; set; }
|
||||
|
||||
|
||||
@@ -2,31 +2,26 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.UI;
|
||||
using System.Web.WebPages;
|
||||
using Orchard.Compilation.Razor;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.DisplayManagement.Implementation;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Mvc;
|
||||
using Orchard.Settings;
|
||||
using Orchard.Themes.Models;
|
||||
|
||||
namespace Orchard.Templates.Services {
|
||||
using Orchard.Logging;namespace Orchard.Templates.Services {
|
||||
public class RazorTemplateProcessor : TemplateProcessorImpl {
|
||||
private readonly IRazorCompiler _compiler;
|
||||
private readonly ISiteService _siteService;
|
||||
private readonly IHttpContextAccessor _hca;
|
||||
private readonly HttpContextBase _httpContextBase;
|
||||
|
||||
public override string Type {
|
||||
get { return "Razor"; }
|
||||
}
|
||||
|
||||
public RazorTemplateProcessor(IRazorCompiler compiler, ISiteService siteService, IHttpContextAccessor hca) {
|
||||
public RazorTemplateProcessor(
|
||||
IRazorCompiler compiler,
|
||||
HttpContextBase httpContextBase) {
|
||||
_compiler = compiler;
|
||||
_siteService = siteService;
|
||||
_hca = hca;
|
||||
_httpContextBase = httpContextBase;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
@@ -48,24 +43,32 @@ namespace Orchard.Templates.Services {
|
||||
private string ActivateAndRenderTemplate(IRazorTemplateBase obj, DisplayContext displayContext, string templateVirtualPath, object model) {
|
||||
var buffer = new StringBuilder(1024);
|
||||
using (var writer = new StringWriter(buffer)) {
|
||||
var htmlWriter = new HtmlTextWriter(writer);
|
||||
var httpContext = _hca.Current();
|
||||
using (var htmlWriter = new HtmlTextWriter(writer)) {
|
||||
|
||||
// this should be done better - if no display context is provided we should fallback to current controller context somehow, if possible
|
||||
if (displayContext != null) {
|
||||
var shapeViewContext = new ViewContext(displayContext.ViewContext.Controller.ControllerContext, displayContext.ViewContext.View, displayContext.ViewContext.ViewData, displayContext.ViewContext.TempData, htmlWriter);
|
||||
obj.WebPageContext = new WebPageContext(displayContext.ViewContext.HttpContext, obj as WebPageRenderingBase, model);
|
||||
obj.ViewContext = shapeViewContext;
|
||||
obj.ViewData = new ViewDataDictionary(displayContext.ViewDataContainer.ViewData) { Model = model };
|
||||
}
|
||||
else {
|
||||
obj.ViewData = new ViewDataDictionary(model);
|
||||
obj.WebPageContext = new WebPageContext(httpContext, obj as WebPageRenderingBase, model);
|
||||
}
|
||||
if (displayContext != null && displayContext.ViewContext.Controller != null) {
|
||||
var shapeViewContext = new ViewContext(
|
||||
displayContext.ViewContext.Controller.ControllerContext,
|
||||
displayContext.ViewContext.View,
|
||||
displayContext.ViewContext.ViewData,
|
||||
displayContext.ViewContext.TempData,
|
||||
htmlWriter
|
||||
);
|
||||
|
||||
obj.VirtualPath = templateVirtualPath ?? "~/Themes/" + _siteService.GetSiteSettings().As<ThemeSiteSettingsPart>().CurrentThemeName;
|
||||
obj.InitHelpers();
|
||||
obj.Render(htmlWriter);
|
||||
obj.WebPageContext = new WebPageContext(displayContext.ViewContext.HttpContext, obj as WebPageRenderingBase, model);
|
||||
obj.ViewContext = shapeViewContext;
|
||||
|
||||
obj.ViewData = new ViewDataDictionary(displayContext.ViewDataContainer.ViewData) {Model = model};
|
||||
obj.InitHelpers();
|
||||
}
|
||||
else {
|
||||
|
||||
obj.ViewData = new ViewDataDictionary(model);
|
||||
obj.WebPageContext = new WebPageContext(_httpContextBase, obj as WebPageRenderingBase, model);
|
||||
}
|
||||
|
||||
obj.VirtualPath = templateVirtualPath ?? "~/Themes/Orchard.Templates";
|
||||
obj.Render(htmlWriter);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.ToString();
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Security.Principal;
|
||||
using System.Web;
|
||||
using System.Web.ClientServices;
|
||||
using System.Web.Routing;
|
||||
using System.Web.Security;
|
||||
|
||||
namespace Orchard.Templates.Services {
|
||||
internal class StubHttpContext : HttpContextBase {
|
||||
private IDictionary _items;
|
||||
private HttpRequestBase _request;
|
||||
private HttpSessionStateBase _session;
|
||||
private IPrincipal _user;
|
||||
|
||||
public override IDictionary Items {
|
||||
get { return _items ?? (_items = new Hashtable()); }
|
||||
}
|
||||
|
||||
public override HttpRequestBase Request {
|
||||
get { return _request ?? (_request = new StubHttpRequest(this)); }
|
||||
}
|
||||
|
||||
public override HttpSessionStateBase Session {
|
||||
get { return _session ?? (_session = new StubHttpSessionState()); }
|
||||
}
|
||||
|
||||
public override IPrincipal User {
|
||||
get { return _user ?? (_user = new ClientRolePrincipal(new FormsIdentity(new FormsAuthenticationTicket(FormsAuthentication.FormsCookieName, true, int.MaxValue)))); }
|
||||
set { _user = value; }
|
||||
}
|
||||
|
||||
public override IHttpHandler Handler { get; set; }
|
||||
|
||||
private class StubHttpRequest : HttpRequestBase {
|
||||
private readonly HttpContextBase _httpContext;
|
||||
private RequestContext _requestContext;
|
||||
private NameValueCollection _queryString;
|
||||
private NameValueCollection _headers;
|
||||
|
||||
public StubHttpRequest(HttpContextBase httpContext) {
|
||||
_httpContext = httpContext;
|
||||
}
|
||||
|
||||
public override RequestContext RequestContext {
|
||||
get { return _requestContext ?? (_requestContext = new StubRequestContext(_httpContext)); }
|
||||
set { _requestContext = value; }
|
||||
}
|
||||
|
||||
public override NameValueCollection QueryString {
|
||||
get { return _queryString ?? (_queryString = new NameValueCollection()); }
|
||||
}
|
||||
|
||||
public override NameValueCollection Headers {
|
||||
get { return _headers ?? (_headers = new NameValueCollection()); }
|
||||
}
|
||||
|
||||
public override bool IsAuthenticated {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override string AppRelativeCurrentExecutionFilePath {
|
||||
get { return "/"; }
|
||||
}
|
||||
}
|
||||
|
||||
private class StubRequestContext : RequestContext {
|
||||
private HttpContextBase _httpContext;
|
||||
|
||||
public StubRequestContext(HttpContextBase httpContext) {
|
||||
_httpContext = httpContext;
|
||||
}
|
||||
|
||||
public override HttpContextBase HttpContext {
|
||||
get { return _httpContext; }
|
||||
set { _httpContext = value; }
|
||||
}
|
||||
}
|
||||
|
||||
private class StubHttpSessionState : HttpSessionStateBase {
|
||||
private readonly Hashtable _stub = new Hashtable();
|
||||
public override object this[int index] {
|
||||
get { return _stub[index]; }
|
||||
set { _stub[index] = value; }
|
||||
}
|
||||
|
||||
public override object this[string index] {
|
||||
get { return _stub[index]; }
|
||||
set { _stub[index] = value; }
|
||||
}
|
||||
|
||||
public override int Count {
|
||||
get { return _stub.Count; }
|
||||
}
|
||||
|
||||
public override void Clear() {
|
||||
_stub.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +1,33 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Orchard.Caching;
|
||||
using Orchard.Compilation.Razor;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.DisplayManagement.Descriptors;
|
||||
using Orchard.DisplayManagement.Implementation;
|
||||
using Orchard.Environment.Extensions;
|
||||
using Orchard.Environment.Extensions.Models;
|
||||
using Orchard.Mvc.Spooling;
|
||||
using Orchard.Settings;
|
||||
using Orchard.Themes.Models;
|
||||
|
||||
namespace Orchard.Templates.Services {
|
||||
public class TemplateBindingStrategy : IShapeTableProvider {
|
||||
private readonly IWorkContextAccessor _wca;
|
||||
private readonly ITemplateService _templateService;
|
||||
private readonly IRazorTemplateHolder _templateProvider;
|
||||
|
||||
public TemplateBindingStrategy(
|
||||
IWorkContextAccessor wca,
|
||||
ITemplateService templateService,
|
||||
IRazorTemplateHolder templateProvider) {
|
||||
_wca = wca;
|
||||
_templateService = templateService;
|
||||
_templateProvider = templateProvider;
|
||||
}
|
||||
|
||||
public virtual Feature Feature { get; set; }
|
||||
|
||||
public void Discover(ShapeTableBuilder builder) {
|
||||
EnsureWorkContext(() => BuildShapes(builder));
|
||||
BuildShapes(builder);
|
||||
}
|
||||
|
||||
private void BuildShapes(ShapeTableBuilder builder) {
|
||||
|
||||
var templateService = _wca.GetContext().Resolve<ITemplateService>();
|
||||
var siteService = _wca.GetContext().Resolve<ISiteService>();
|
||||
var extensionManager = _wca.GetContext().Resolve<IExtensionManager>();
|
||||
|
||||
var currentTheme = extensionManager.GetExtension(siteService.GetSiteSettings().As<ThemeSiteSettingsPart>().CurrentThemeName);
|
||||
var themeFeature = currentTheme.Features.FirstOrDefault();
|
||||
|
||||
var hackedDescriptor = new FeatureDescriptor
|
||||
{
|
||||
Category = themeFeature.Category,
|
||||
Dependencies = themeFeature.Dependencies,
|
||||
Description = themeFeature.Description,
|
||||
Extension = themeFeature.Extension,
|
||||
Id = themeFeature.Id,
|
||||
Name = themeFeature.Name,
|
||||
Priority = int.MaxValue
|
||||
};
|
||||
|
||||
var shapes = templateService.GetTemplates().Select(r =>
|
||||
var shapes = _templateService.GetTemplates().Select(r =>
|
||||
new {
|
||||
r.Name,
|
||||
r.Language,
|
||||
@@ -61,7 +40,7 @@ namespace Orchard.Templates.Services {
|
||||
var shapeType = AdjustName(record.Name);
|
||||
|
||||
builder.Describe(shapeType)
|
||||
.From(new Feature { Descriptor = hackedDescriptor })
|
||||
.From(new Feature { Descriptor = Feature.Descriptor })
|
||||
.BoundAs("Template::" + shapeType,
|
||||
descriptor => context => {
|
||||
var template = _templateProvider.Get(record.Name);
|
||||
@@ -72,13 +51,12 @@ namespace Orchard.Templates.Services {
|
||||
|
||||
private IHtmlString PerformInvoke(DisplayContext displayContext, string name, string type, string template)
|
||||
{
|
||||
var service = _wca.GetContext().Resolve<ITemplateService>();
|
||||
var output = new HtmlStringWriter();
|
||||
|
||||
if (String.IsNullOrEmpty(template))
|
||||
if (String.IsNullOrEmpty(template)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
output.Write(CoerceHtmlString(service.Execute(template, name, type, displayContext, displayContext.Value)));
|
||||
var output = new HtmlStringWriter();
|
||||
output.Write(CoerceHtmlString(_templateService.Execute(template, name, type, displayContext, displayContext.Value)));
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -115,16 +93,5 @@ namespace Orchard.Templates.Services {
|
||||
return invoke as IHtmlString ?? (invoke != null ? new HtmlString(invoke.ToString()) : null);
|
||||
}
|
||||
|
||||
private void EnsureWorkContext(Action action) {
|
||||
var workContext = _wca.GetContext();
|
||||
if (workContext != null) {
|
||||
action();
|
||||
}
|
||||
else {
|
||||
using (_wca.CreateWorkContextScope()) {
|
||||
action();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,6 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.UI;
|
||||
using System.Web.WebPages;
|
||||
using System.Web.WebPages.Instrumentation;
|
||||
|
||||
namespace Orchard.Compilation.Razor {
|
||||
public interface IRazorTemplateBase
|
||||
@@ -22,13 +17,13 @@ namespace Orchard.Compilation.Razor {
|
||||
|
||||
public interface IRazorTemplateBase<TModel> : IRazorTemplateBase
|
||||
{
|
||||
TModel Model { get; }
|
||||
ViewDataDictionary<TModel> ViewData { get; set; }
|
||||
new TModel Model { get; }
|
||||
new ViewDataDictionary<TModel> ViewData { get; set; }
|
||||
}
|
||||
|
||||
public abstract class RazorTemplateBase<T> : Mvc.ViewEngines.Razor.WebViewPage<T>, IRazorTemplateBase<T> {
|
||||
public WebPageContext WebPageContext { get; set; }
|
||||
public void Render(TextWriter writer) {
|
||||
public virtual void Render(TextWriter writer) {
|
||||
PushContext(WebPageContext, writer);
|
||||
OutputStack.Push(writer);
|
||||
Execute();
|
||||
|
||||
Reference in New Issue
Block a user