diff --git a/.hgsubstate b/.hgsubstate index d2c2adc20..d2167433f 100644 --- a/.hgsubstate +++ b/.hgsubstate @@ -1 +1 @@ -627628209d0b427eb6446e22da5ac046b2cd77c9 Clay +ccaf72b5e2d5fea43a6f15b3a912b631e3c0302d Clay diff --git a/src/Orchard.Web/Core/Feeds/Services/FeedFilter.cs b/src/Orchard.Web/Core/Feeds/Services/FeedFilter.cs index 52e272354..16d6cc37a 100644 --- a/src/Orchard.Web/Core/Feeds/Services/FeedFilter.cs +++ b/src/Orchard.Web/Core/Feeds/Services/FeedFilter.cs @@ -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) {} diff --git a/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs b/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs index 655225e8e..d287b24f8 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs @@ -71,8 +71,8 @@ namespace Orchard.Setup { builder.RegisterType().As(); builder.RegisterType().As(); - builder.RegisterType().As(); - + builder.RegisterType().As(); + builder.RegisterType().As().As(); } diff --git a/src/Orchard.Web/Modules/Orchard.Themes/Preview/PreviewThemeFilter.cs b/src/Orchard.Web/Modules/Orchard.Themes/Preview/PreviewThemeFilter.cs index 60f862198..c718b1da5 100644 --- a/src/Orchard.Web/Modules/Orchard.Themes/Preview/PreviewThemeFilter.cs +++ b/src/Orchard.Web/Modules/Orchard.Themes/Preview/PreviewThemeFilter.cs @@ -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) {} diff --git a/src/Orchard/DisplayManagement/Implementation/DefaultShapeFactory.cs b/src/Orchard/DisplayManagement/Implementation/DefaultShapeFactory.cs index d212cd865..d4a605c70 100644 --- a/src/Orchard/DisplayManagement/Implementation/DefaultShapeFactory.cs +++ b/src/Orchard/DisplayManagement/Implementation/DefaultShapeFactory.cs @@ -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 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 Behaviors { get; set; } + public IList> 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> _events; + + public DefaultShapeFactory(IEnumerable> events) { + _events = events; + } + + public IShape Create(string shapeType, INamedEnumerable parameters) { + var creatingContext = new ShapeCreatingContext { + ShapeFactory = this, + ShapeType = shapeType, + OnCreated = new List>() + }; var positional = parameters.Positional; - var baseType = positional.Take(1).OfType().SingleOrDefault(); - if (baseType == null) { + creatingContext.BaseType = positional.Take(1).OfType().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 { 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 { 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; } } + + } \ No newline at end of file diff --git a/src/Orchard/DisplayManagement/Implementation/PageWorkContextStateProvider.cs b/src/Orchard/DisplayManagement/Implementation/PageWorkContextStateProvider.cs new file mode 100644 index 000000000..a28918229 --- /dev/null +++ b/src/Orchard/DisplayManagement/Implementation/PageWorkContextStateProvider.cs @@ -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(string name) { + if (name == "Page") { + var page = (dynamic)_shapeFactory.Create("Layout", Arguments.From(Enumerable.Empty(), Enumerable.Empty())); + 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) { + } + } + + +} diff --git a/src/Orchard/DisplayManagement/Zones/ZoneHoldingBehavior.cs b/src/Orchard/DisplayManagement/Zones/ZoneHoldingBehavior.cs new file mode 100644 index 000000000..0377f8702 --- /dev/null +++ b/src/Orchard/DisplayManagement/Zones/ZoneHoldingBehavior.cs @@ -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 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 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 proceed, System.Collections.Generic.IEnumerable 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 proceed, object self, string name, INamedEnumerable 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(); + } + } + } +} \ No newline at end of file diff --git a/src/Orchard/Environment/DefaultWorkContextAccessor.cs b/src/Orchard/Environment/DefaultWorkContextAccessor.cs index 2f2e780c0..f32ca7aa5 100644 --- a/src/Orchard/Environment/DefaultWorkContextAccessor.cs +++ b/src/Orchard/Environment/DefaultWorkContextAccessor.cs @@ -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 _state = new ConcurrentDictionary(); + readonly IEnumerable _workContextStateProviders; public WorkContextImplementation(IComponentContext componentContext) { _componentContext = componentContext; + _workContextStateProviders = componentContext.Resolve>(); } public override T Resolve() { return _componentContext.Resolve(); } - public override T State() { - throw new NotImplementedException(); + public override T GetState(string name) { + return (T)_state.GetOrAdd(name, x => _workContextStateProviders.Select(wcsp => wcsp.Get(x)).Where(t => !Equals(t, default(T))).FirstOrDefault()); + } + + public override void SetState(string name, T value) { + _state[name] = value; } } diff --git a/src/Orchard/IWorkContextAccessor.cs b/src/Orchard/IWorkContextAccessor.cs index eab5d8595..7fddb2406 100644 --- a/src/Orchard/IWorkContextAccessor.cs +++ b/src/Orchard/IWorkContextAccessor.cs @@ -9,6 +9,10 @@ namespace Orchard { WorkContext GetContext(); IWorkContextScope CreateWorkContextScope(); } + + public interface IWorkContextStateProvider : IDependency { + T Get(string name); + } public interface IWorkContextScope : IDisposable { WorkContext WorkContext { get; } diff --git a/src/Orchard/Mvc/ViewEngines/ThemeAwareness/LayoutAwareViewEngine.cs b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/LayoutAwareViewEngine.cs index 2aab9e42b..8484e5c88 100644 --- a/src/Orchard/Mvc/ViewEngines/ThemeAwareness/LayoutAwareViewEngine.cs +++ b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/LayoutAwareViewEngine.cs @@ -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()) - .Concat((layoutView.SearchedLocations ?? Enumerable.Empty())) - .Concat((documentView.SearchedLocations ?? Enumerable.Empty())) - ); - - 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 _render; + private readonly Action _releaseView; - public LayoutView(ViewEngineResult[] viewEngineResults) { - _viewEngineResults = viewEngineResults; + public LayoutView(Action render, Action 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); } } } diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 718cdd4e5..8351214ff 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -139,6 +139,8 @@ + + diff --git a/src/Orchard/UI/Admin/Notification/NotificationFilter.cs b/src/Orchard/UI/Admin/Notification/NotificationFilter.cs index 8cfb89a6d..bc006e9e7 100644 --- a/src/Orchard/UI/Admin/Notification/NotificationFilter.cs +++ b/src/Orchard/UI/Admin/Notification/NotificationFilter.cs @@ -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)); } diff --git a/src/Orchard/UI/IPage.cs b/src/Orchard/UI/IPage.cs index f54bd022f..109e4ab08 100644 --- a/src/Orchard/UI/IPage.cs +++ b/src/Orchard/UI/IPage.cs @@ -24,29 +24,36 @@ namespace Orchard.UI { } public interface IZone { - void Add(object item); - void Add(object item, string position); - void Add(Action action); - void Add(Action action, string position); + string ZoneName { get; set; } + IZone Add(object item); + IZone Add(object item, string position); + IZone Add(Action action); + IZone Add(Action action, string position); } - class Zone : IZone { + public class Zone : IZone { private readonly IList _items = new List(); - 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 action) { + public virtual IZone Add(Action action) { //throw new NotImplementedException(); + return this; } - public void Add(Action action, string position) { + public virtual IZone Add(Action action, string position) { //throw new NotImplementedException(); + return this; } } } \ No newline at end of file diff --git a/src/Orchard/UI/Notify/NotifyFilter.cs b/src/Orchard/UI/Notify/NotifyFilter.cs index f33238bff..ea2cfc567 100644 --- a/src/Orchard/UI/Notify/NotifyFilter.cs +++ b/src/Orchard/UI/Notify/NotifyFilter.cs @@ -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)); diff --git a/src/Orchard/WorkContext.cs b/src/Orchard/WorkContext.cs index 2b297612b..187ba0a40 100644 --- a/src/Orchard/WorkContext.cs +++ b/src/Orchard/WorkContext.cs @@ -8,22 +8,35 @@ using Orchard.UI; namespace Orchard { public abstract class WorkContext { - public HttpContextBase HttpContext { - get { return State(); } - } - public IPage CurrentPage { - get { return State(); } - } - public ISite CurrentSite { - get { return State(); } - } - public IUser CurrentUser { - get { return State(); } - } - - public ITheme CurrentTheme { get; set; } - public abstract T Resolve(); - public abstract T State(); + + public abstract T GetState(string name); + public abstract void SetState(string name, T value); + + public HttpContextBase HttpContext { + get { return GetState("HttpContext"); } + set { SetState("HttpContext", value); } + } + + public IPage Page { + get { return GetState("Page"); } + set { SetState("Page", value); } + } + + public ISite CurrentSite { + get { return GetState("CurrentSite"); } + set { SetState("CurrentSite", value); } + } + + public IUser CurrentUser { + get { return GetState("CurrentUser"); } + set { SetState("CurrentUser", value); } + } + + public ITheme CurrentTheme { + get { return GetState("CurrentTheme"); } + set { SetState("CurrentTheme", value); } + } + } } \ No newline at end of file