Starting to implement shapes for page/zone

--HG--
branch : theming
This commit is contained in:
Louis DeJardin
2010-09-03 21:57:01 -07:00
parent 8cd5c5f651
commit b3e489a27b
15 changed files with 297 additions and 109 deletions

View File

@@ -1 +1 @@
627628209d0b427eb6446e22da5ac046b2cd77c9 Clay
ccaf72b5e2d5fea43a6f15b3a912b631e3c0302d Clay

View File

@@ -14,7 +14,7 @@ namespace Orchard.Core.Feeds.Services {
}
public void OnResultExecuting(ResultExecutingContext filterContext) {
_workContextAccessor.GetContext(filterContext).CurrentPage.Zones["Head"].Add(html => html.ViewContext.Writer.Write(_feedManager.GetRegisteredLinks(html)), ":after");
_workContextAccessor.GetContext(filterContext).Page.Zones["Head"].Add(html => html.ViewContext.Writer.Write(_feedManager.GetRegisteredLinks(html)), ":after");
}
public void OnResultExecuted(ResultExecutedContext filterContext) {}

View File

@@ -71,8 +71,8 @@ namespace Orchard.Setup {
builder.RegisterType<ThemeAwareViewEngine>().As<IThemeAwareViewEngine>();
builder.RegisterType<LayoutAwareViewEngine>().As<ILayoutAwareViewEngine>();
builder.RegisterType<ConfiguredEnginesCache>().As<IConfiguredEnginesCache>();
builder.RegisterType<ConfiguredEnginesCache>().As<IConfiguredEnginesCache>();
builder.RegisterType<PageWorkContextStateProvider>().As<IWorkContextStateProvider>().As<IShapeEvents>();
}

View File

@@ -33,7 +33,7 @@ namespace Orchard.Themes.Preview {
};
var shape = _shapeHelperFactory.CreateHelper();
_workContextAccessor.GetContext(filterContext).CurrentPage.Zones["Body"].Add(shape.ThemePreview(model), ":before");
_workContextAccessor.GetContext(filterContext).Page.Zones["Body"].Add(shape.ThemePreview(model), ":before");
}
public void OnResultExecuted(ResultExecutedContext filterContext) {}

View File

@@ -1,18 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ClaySharp;
using Orchard.DisplayManagement.Shapes;
namespace Orchard.DisplayManagement.Implementation {
public class DefaultShapeFactory : IShapeFactory {
public IShape Create(string shapeType, INamedEnumerable<object> parameters) {
public interface IShapeEvents : IDependency {
void Creating(ShapeCreatingContext context);
void Created(ShapeCreatedContext context);
}
public class ShapeCreatingContext {
public IShapeFactory ShapeFactory { get; set; }
public string ShapeType { get; set; }
public Type BaseType { get; set; }
public IList<IClayBehavior> Behaviors { get; set; }
public IList<Action<ShapeCreatedContext>> OnCreated { get; set; }
}
public class ShapeCreatedContext {
public IShapeFactory ShapeFactory { get; set; }
public string ShapeType { get; set; }
public dynamic Shape { get; set; }
}
public class DefaultShapeFactory : IShapeFactory {
private readonly IEnumerable<Lazy<IShapeEvents>> _events;
public DefaultShapeFactory(IEnumerable<Lazy<IShapeEvents>> events) {
_events = events;
}
public IShape Create(string shapeType, INamedEnumerable<object> parameters) {
var creatingContext = new ShapeCreatingContext {
ShapeFactory = this,
ShapeType = shapeType,
OnCreated = new List<Action<ShapeCreatedContext>>()
};
var positional = parameters.Positional;
var baseType = positional.Take(1).OfType<Type>().SingleOrDefault();
if (baseType == null) {
creatingContext.BaseType = positional.Take(1).OfType<Type>().SingleOrDefault();
if (creatingContext.BaseType == null) {
// default to common base class
baseType = typeof(Shape);
creatingContext.BaseType = typeof(Shape);
}
else {
// consume the first argument
@@ -20,10 +52,10 @@ namespace Orchard.DisplayManagement.Implementation {
}
IClayBehavior[] behaviors;
if (baseType == typeof(Array)) {
if (creatingContext.BaseType == typeof(Array)) {
// array is a hint - not an intended base class
baseType = typeof (Shape);
behaviors = new IClayBehavior[] {
creatingContext.BaseType = typeof(Shape);
creatingContext.Behaviors = new List<IClayBehavior> {
new ClaySharp.Behaviors.InterfaceProxyBehavior(),
new ClaySharp.Behaviors.PropBehavior(),
new ClaySharp.Behaviors.ArrayBehavior(),
@@ -31,31 +63,46 @@ namespace Orchard.DisplayManagement.Implementation {
};
}
else {
behaviors = new IClayBehavior[] {
creatingContext.Behaviors = new List<IClayBehavior> {
new ClaySharp.Behaviors.InterfaceProxyBehavior(),
new ClaySharp.Behaviors.PropBehavior(),
new ClaySharp.Behaviors.NilResultBehavior()
};
}
// consideration - types without default constructors could consume positional arguments?
var shape = ClayActivator.CreateInstance(baseType, behaviors);
foreach (var ev in _events) {
ev.Value.Creating(creatingContext);
}
shape.Metadata = new ShapeMetadata { Type = shapeType };
var createdContext = new ShapeCreatedContext {
ShapeFactory = this,
ShapeType = creatingContext.ShapeType,
Shape = ClayActivator.CreateInstance(creatingContext.BaseType, creatingContext.Behaviors)
};
createdContext.Shape.Metadata = new ShapeMetadata { Type = shapeType };
// only one non-Type, non-named argument is allowed
var initializer = positional.SingleOrDefault();
if (initializer != null) {
foreach (var prop in initializer.GetType().GetProperties()) {
shape[prop.Name] = prop.GetValue(initializer, null);
createdContext.Shape[prop.Name] = prop.GetValue(initializer, null);
}
}
foreach (var kv in parameters.Named) {
shape[kv.Key] = kv.Value;
createdContext.Shape[kv.Key] = kv.Value;
}
return shape;
foreach (var ev in creatingContext.OnCreated) {
ev(createdContext);
}
foreach (var ev in _events) {
ev.Value.Created(createdContext);
}
return createdContext.Shape;
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Linq;
using ClaySharp.Implementation;
using Orchard.DisplayManagement.Zones;
using Orchard.UI;
namespace Orchard.DisplayManagement.Implementation {
public class PageWorkContextStateProvider : IWorkContextStateProvider, IShapeEvents {
private readonly IShapeFactory _shapeFactory;
public PageWorkContextStateProvider(IShapeFactory shapeFactory) {
_shapeFactory = shapeFactory;
}
public T Get<T>(string name) {
if (name == "Page") {
var page = (dynamic)_shapeFactory.Create("Layout", Arguments.From(Enumerable.Empty<object>(), Enumerable.Empty<string>()));
return page;
}
return default(T);
}
public void Creating(ShapeCreatingContext context) {
if (context.ShapeType == "Layout") {
context.Behaviors.Add(new ZoneHoldingBehavior(context.ShapeFactory));
}
else if (context.ShapeType == "Zone") {
context.BaseType = typeof(Zone);
}
}
public void Created(ShapeCreatedContext context) {
}
}
}

View File

@@ -0,0 +1,92 @@
using System;
using System.Linq;
using ClaySharp;
using ClaySharp.Behaviors;
using ClaySharp.Implementation;
using Orchard.UI;
namespace Orchard.DisplayManagement.Zones {
public class ZoneHoldingBehavior : ClayBehavior {
private readonly IShapeFactory _shapeFactory;
public ZoneHoldingBehavior(IShapeFactory shapeFactory) {
_shapeFactory = shapeFactory;
}
public override object GetMember(Func<object> proceed, object self, string name) {
if (name == "Zones") {
return ClayActivator.CreateInstance(new IClayBehavior[] {
new InterfaceProxyBehavior(),
new ZonesBehavior(_shapeFactory, self)
});
}
//Page.Zones.Sidebar.Add(x,"below")
//Page.Sidebar.Add(x,"below")
var result = proceed();
if (((dynamic)result) == null) {
return ClayActivator.CreateInstance(new IClayBehavior[] {
new NilResultBehavior(),
new ZoneOnDemandBehavior(_shapeFactory, self, name)
});
}
return result;
}
public class ZonesBehavior : ClayBehavior {
private readonly IShapeFactory _shapeFactory;
private readonly object _parent;
public ZonesBehavior(IShapeFactory shapeFactory, object parent) {
_shapeFactory = shapeFactory;
_parent = parent;
}
public override object GetMember(Func<object> proceed, object self, string name) {
var parentMember = ((dynamic)_parent)[name];
if (parentMember == null) {
return ClayActivator.CreateInstance(new IClayBehavior[] {
new InterfaceProxyBehavior(),
new ZoneOnDemandBehavior(_shapeFactory, _parent, name)
});
}
return parentMember;
}
public override object GetIndex(Func<object> proceed, System.Collections.Generic.IEnumerable<object> keys) {
if (keys.Count() == 1) {
return GetMember(proceed, null, System.Convert.ToString(keys.Single()));
}
return proceed();
}
}
public class ZoneOnDemandBehavior : ClayBehavior {
private readonly IShapeFactory _shapeFactory;
private readonly object _parent;
private readonly string _name;
public ZoneOnDemandBehavior(IShapeFactory shapeFactory, object parent, string name) {
_shapeFactory = shapeFactory;
_parent = parent;
_name = name;
}
public override object InvokeMember(Func<object> proceed, object self, string name, INamedEnumerable<object> args) {
var argsCount = args.Count();
if (name == "Add" && (argsCount == 1 || argsCount == 2)) {
dynamic parent = _parent;
dynamic zone = _shapeFactory.Create("Zone", Arguments.Empty());
zone.ZoneName = name;
parent[name] = zone;
if (argsCount == 1)
return zone.Add(args.Single());
return zone.Add(args.First(), (string)args.Last());
}
return proceed();
}
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Autofac;
using Orchard.Mvc;
@@ -85,18 +86,25 @@ namespace Orchard.Environment {
class WorkContextImplementation : WorkContext {
private readonly IComponentContext _componentContext;
readonly IComponentContext _componentContext;
readonly ConcurrentDictionary<string, object> _state = new ConcurrentDictionary<string, object>();
readonly IEnumerable<IWorkContextStateProvider> _workContextStateProviders;
public WorkContextImplementation(IComponentContext componentContext) {
_componentContext = componentContext;
_workContextStateProviders = componentContext.Resolve<IEnumerable<IWorkContextStateProvider>>();
}
public override T Resolve<T>() {
return _componentContext.Resolve<T>();
}
public override T State<T>() {
throw new NotImplementedException();
public override T GetState<T>(string name) {
return (T)_state.GetOrAdd(name, x => _workContextStateProviders.Select(wcsp => wcsp.Get<T>(x)).Where(t => !Equals(t, default(T))).FirstOrDefault());
}
public override void SetState<T>(string name, T value) {
_state[name] = value;
}
}

View File

@@ -9,6 +9,10 @@ namespace Orchard {
WorkContext GetContext();
IWorkContextScope CreateWorkContextScope();
}
public interface IWorkContextStateProvider : IDependency {
T Get<T>(string name);
}
public interface IWorkContextScope : IDisposable {
WorkContext WorkContext { get; }

View File

@@ -1,19 +1,25 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using Orchard.DisplayManagement;
namespace Orchard.Mvc.ViewEngines.ThemeAwareness {
public interface ILayoutAwareViewEngine : IDependency, IViewEngine {
}
public class LayoutAwareViewEngine : ILayoutAwareViewEngine {
private readonly WorkContext _workContext;
private readonly IThemeAwareViewEngine _themeAwareViewEngine;
private readonly IDisplayHelperFactory _displayHelperFactory;
public LayoutAwareViewEngine(IThemeAwareViewEngine themeAwareViewEngine) {
public LayoutAwareViewEngine(
WorkContext workContext,
IThemeAwareViewEngine themeAwareViewEngine,
IDisplayHelperFactory displayHelperFactory) {
_workContext = workContext;
_themeAwareViewEngine = themeAwareViewEngine;
_displayHelperFactory = displayHelperFactory;
}
public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) {
@@ -21,77 +27,50 @@ namespace Orchard.Mvc.ViewEngines.ThemeAwareness {
}
public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) {
var bodyView = _themeAwareViewEngine.FindPartialView(controllerContext, viewName, useCache, true);
var layoutView = _themeAwareViewEngine.FindPartialView(controllerContext, "Layout", useCache, true);
var documentView = _themeAwareViewEngine.FindPartialView(controllerContext, "Document", useCache, true);
var findBody = _themeAwareViewEngine.FindPartialView(controllerContext, viewName, useCache, true);
if (bodyView.View == null ||
layoutView.View == null ||
documentView.View == null) {
var missingTemplatesResult = new ViewEngineResult(
(bodyView.SearchedLocations ?? Enumerable.Empty<string>())
.Concat((layoutView.SearchedLocations ?? Enumerable.Empty<string>()))
.Concat((documentView.SearchedLocations ?? Enumerable.Empty<string>()))
);
return missingTemplatesResult;
if (findBody.View == null) {
return findBody;
}
var view = new LayoutView(new[] {
bodyView,
layoutView,
documentView,
});
var layoutView = new LayoutView((viewContext, writer, viewDataContainer) => {
var buffer = new StringWriter();
findBody.View.Render(viewContext, buffer);
return new ViewEngineResult(view, this);
_workContext.Page.Zones["Content"].Add(new HtmlString(buffer.ToString()), "5");
var display = _displayHelperFactory.CreateHelper(viewContext, viewDataContainer);
IHtmlString result = display(_workContext.Page);
writer.Write(result.ToHtmlString());
}, (context, view) => findBody.ViewEngine.ReleaseView(context, findBody.View));
return new ViewEngineResult(layoutView, this);
}
public void ReleaseView(ControllerContext controllerContext, IView view) {
var layoutView = (LayoutView)view;
layoutView.ReleaseViews(controllerContext);
layoutView.ReleaseView(controllerContext, view);
}
class LayoutView : IView {
private readonly ViewEngineResult[] _viewEngineResults;
class LayoutView : IView, IViewDataContainer {
private readonly Action<ViewContext, TextWriter, IViewDataContainer> _render;
private readonly Action<ControllerContext, IView> _releaseView;
public LayoutView(ViewEngineResult[] viewEngineResults) {
_viewEngineResults = viewEngineResults;
public LayoutView(Action<ViewContext, TextWriter, IViewDataContainer> render, Action<ControllerContext, IView> releaseView) {
_render = render;
_releaseView = releaseView;
}
public ViewDataDictionary ViewData { get; set; }
public void Render(ViewContext viewContext, TextWriter writer) {
var layoutViewContext = LayoutViewContext.From(viewContext);
for (var index = 0; index != _viewEngineResults.Length; ++index) {
bool isFirst = index == 0;
bool isLast = index == _viewEngineResults.Length - 1;
var effectiveWriter = isLast ? viewContext.Writer : new StringWriter();
var effectiveViewData = isFirst ? viewContext.ViewData : CoerceViewData(viewContext.ViewData);
var viewEngineResult = _viewEngineResults[index];
var effectiveContext = new ViewContext(
viewContext,
viewEngineResult.View,
effectiveViewData,
viewContext.TempData,
effectiveWriter);
viewEngineResult.View.Render(effectiveContext, effectiveWriter);
if (!isLast)
layoutViewContext.BodyContent = effectiveWriter.ToString();
}
ViewData = viewContext.ViewData;
_render(viewContext, writer, this);
}
private static ViewDataDictionary CoerceViewData(ViewDataDictionary dictionary) {
return dictionary;
}
public void ReleaseViews(ControllerContext context) {
foreach (var viewEngineResult in _viewEngineResults) {
viewEngineResult.ViewEngine.ReleaseView(context, viewEngineResult.View);
}
public void ReleaseView(ControllerContext context, IView view) {
_releaseView(context, view);
}
}
}

View File

@@ -139,6 +139,8 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="DisplayManagement\Implementation\PageWorkContextStateProvider.cs" />
<Compile Include="DisplayManagement\Zones\ZoneHoldingBehavior.cs" />
<Compile Include="Mvc\ViewEngines\ThemeAwareness\ConfiguredEnginesCache.cs" />
<Compile Include="Mvc\ViewEngines\ThemeAwareness\LayoutAwareViewEngine.cs" />
<Compile Include="Mvc\ViewEngines\ThemeAwareness\ThemeAwareViewEngine.cs" />

View File

@@ -28,7 +28,7 @@ namespace Orchard.UI.Admin.Notification {
return;
var shape = _shapeHelperFactory.CreateHelper();
var messagesZone = _workContextAccessor.GetContext(filterContext).CurrentPage.Zones["Messages"];
var messagesZone = _workContextAccessor.GetContext(filterContext).Page.Zones["Messages"];
foreach(var messageEntry in messageEntries)
messagesZone.Add(shape.Message(messageEntry));
}

View File

@@ -24,29 +24,36 @@ namespace Orchard.UI {
}
public interface IZone {
void Add(object item);
void Add(object item, string position);
void Add(Action<HtmlHelper> action);
void Add(Action<HtmlHelper> action, string position);
string ZoneName { get; set; }
IZone Add(object item);
IZone Add(object item, string position);
IZone Add(Action<HtmlHelper> action);
IZone Add(Action<HtmlHelper> action, string position);
}
class Zone : IZone {
public class Zone : IZone {
private readonly IList<object> _items = new List<object>();
public void Add(object item) {
public virtual string ZoneName { get; set; }
public virtual IZone Add(object item) {
_items.Add(item);
return this;
}
public void Add(object item, string position) {
public virtual IZone Add(object item, string position) {
_items.Add(item); // not messing with position at the moment
return this;
}
public void Add(Action<HtmlHelper> action) {
public virtual IZone Add(Action<HtmlHelper> action) {
//throw new NotImplementedException();
return this;
}
public void Add(Action<HtmlHelper> action, string position) {
public virtual IZone Add(Action<HtmlHelper> action, string position) {
//throw new NotImplementedException();
return this;
}
}
}

View File

@@ -83,7 +83,7 @@ namespace Orchard.UI.Notify {
return;
var shape = _shapeHelperFactory.CreateHelper();
var messagesZone = _workContextAccessor.GetContext(filterContext).CurrentPage.Zones["Messages"];
var messagesZone = _workContextAccessor.GetContext(filterContext).Page.Zones["Messages"];
foreach(var messageEntry in messageEntries)
messagesZone.Add(shape.Message(messageEntry));

View File

@@ -8,22 +8,35 @@ using Orchard.UI;
namespace Orchard {
public abstract class WorkContext {
public HttpContextBase HttpContext {
get { return State<HttpContextBase>(); }
}
public IPage CurrentPage {
get { return State<IPage>(); }
}
public ISite CurrentSite {
get { return State<ISite>(); }
}
public IUser CurrentUser {
get { return State<IUser>(); }
}
public ITheme CurrentTheme { get; set; }
public abstract T Resolve<T>();
public abstract T State<T>();
public abstract T GetState<T>(string name);
public abstract void SetState<T>(string name, T value);
public HttpContextBase HttpContext {
get { return GetState<HttpContextBase>("HttpContext"); }
set { SetState("HttpContext", value); }
}
public IPage Page {
get { return GetState<IPage>("Page"); }
set { SetState("Page", value); }
}
public ISite CurrentSite {
get { return GetState<ISite>("CurrentSite"); }
set { SetState("CurrentSite", value); }
}
public IUser CurrentUser {
get { return GetState<IUser>("CurrentUser"); }
set { SetState("CurrentUser", value); }
}
public ITheme CurrentTheme {
get { return GetState<ITheme>("CurrentTheme"); }
set { SetState("CurrentTheme", value); }
}
}
}