More theming work

Continued work towards making zone classes obsolete
Shape table construction per theme
Adding display helper to webforms view classes

--HG--
branch : theming
This commit is contained in:
Louis DeJardin
2010-09-05 04:06:18 -07:00
parent b3e489a27b
commit 77c48fb42a
44 changed files with 572 additions and 250 deletions

View File

@@ -1 +1 @@
ccaf72b5e2d5fea43a6f15b3a912b631e3c0302d Clay
3bc513c8049ad19173130a0e0e6a5486ee8247d2 Clay

View File

@@ -75,7 +75,6 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\Castle Windsor 2.0\bin\Castle.DynamicProxy2.dll</HintPath>
</Reference>
<Reference Include="ClaySharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL" />
<Reference Include="FluentNHibernate, Version=1.0.0.593, Culture=neutral, PublicKeyToken=8aa435e3cb308880, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\fluentnhibernate\FluentNHibernate.dll</HintPath>
@@ -233,6 +232,7 @@
<Compile Include="Stubs\StubVirtualPathMonitor.cs" />
<Compile Include="Stubs\StubCacheManager.cs" />
<Compile Include="Stubs\StubWebSiteFolder.cs" />
<Compile Include="UI\ShapeTests.cs" />
<Compile Include="Utility\ContainerExtensions.cs" />
<Compile Include="Environment\TestDependencies\TestDependency.cs" />
<Compile Include="Environment\Blueprint\DefaultShellDescriptorCacheTests.cs" />
@@ -272,6 +272,10 @@
<Compile Include="Utility\ReflectTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Clay\src\ClaySharp\ClaySharp.csproj">
<Project>{76BCD43B-7BA5-4B63-B1E1-861641CA2686}</Project>
<Name>ClaySharp</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard\Orchard.Framework.csproj">
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
<Name>Orchard.Framework</Name>

View File

@@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Autofac;
using ClaySharp;
using NUnit.Framework;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment;
using Orchard.Mvc;
using Orchard.UI.Zones;
namespace Orchard.Tests.UI {
[TestFixture]
public class ShapeTests : ContainerTestBase {
private WorkContext _workContext;
protected override void Register(ContainerBuilder builder) {
builder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>();
builder.RegisterType<DefaultWorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
builder.RegisterType<PageWorkContext>().As<IWorkContextStateProvider>();
builder.RegisterType<CoreShapes>().As<IShapeDescriptorBindingStrategy>();
builder.RegisterType<NumberIsAlwaysFortyTwo>().As<IShapeFactoryEvents>();
}
protected override void Resolve(IContainer container) {
_workContext = container.Resolve<IWorkContextAccessor>().CreateWorkContextScope().WorkContext;
}
[Test]
public void WorkContextPageIsLayoutShape() {
var page = _workContext.Page;
IShapeMetadata pageMetadata = page.Metadata;
Assert.That(pageMetadata.Type, Is.EqualTo("Layout"));
Assert.That(page.Metadata.Type, Is.EqualTo("Layout"));
}
[Test]
public void PagePropertiesAreNil() {
var page = _workContext.Page;
var pageFoo = page.Foo;
Assert.That(pageFoo == null);
}
[Test]
public void PageZonesPropertyIsNotNil() {
var page = _workContext.Page;
var pageZones = page.Zones;
Assert.That(pageZones != null);
Assert.That(pageZones.Foo == null);
}
[Test]
public void AddingToZonePropertyMakesItExist() {
var page = _workContext.Page;
Assert.That(page.Zones.Foo == null);
var pageZonesFoo = page.Zones.Foo;
pageZonesFoo.Add("hello");
Assert.That(page.Zones.Foo != null);
Assert.That(page.Foo != null);
Assert.That(page.Foo.Metadata.Type, Is.EqualTo("Zone"));
}
[Test]
public void AddingToZoneIndexedMakesItExist() {
var page = _workContext.Page;
Assert.That(page.Zones["Foo"] == null);
var pageZonesFoo = page.Zones["Foo"];
pageZonesFoo.Add("hello");
Assert.That(page.Zones["Foo"] != null);
Assert.That(page["Foo"] != null);
Assert.That(page["Foo"].Metadata.Type, Is.EqualTo("Zone"));
}
[Test]
public void CallingAddOnNilPropertyMakesItBecomeZone() {
var page = _workContext.Page;
Assert.That(page.Foo == null);
page.Foo.Add("hello");
Assert.That(page.Foo != null);
Assert.That(page.Foo.Metadata.Type, Is.EqualTo("Zone"));
}
[Test]
public void ZoneContentsAreEnumerable() {
var page = _workContext.Page;
Assert.That(page.Foo == null);
page.Foo.Add("hello");
page.Foo.Add("world");
var list = new List<object>();
foreach (var item in page.Foo) {
list.Add(item);
}
Assert.That(list.Count(), Is.EqualTo(2));
Assert.That(list.First(), Is.EqualTo("hello"));
Assert.That(list.Last(), Is.EqualTo("world"));
}
class NumberIsAlwaysFortyTwo : ShapeFactoryEvents {
public override void Creating(ShapeCreatingContext context) {
context.Behaviors.Add(new Behavior());
}
class Behavior : ClayBehavior {
public override object GetMember(Func<object> proceed, object self, string name) {
return name == "Number" ? 42 : proceed();
}
}
}
[Test]
public void NumberIsFortyTwo() {
var page = _workContext.Page;
Assert.That(page.Number, Is.EqualTo(42));
Assert.That(page.Foo.Number == null);
page.Foo.Add("yarg");
Assert.That(page.Foo.Number, Is.EqualTo(42));
}
}
}

View File

@@ -1,6 +1,7 @@
using System.Web.Mvc;
using JetBrains.Annotations;
using Orchard.Mvc.Filters;
using Orchard.UI;
namespace Orchard.Core.Feeds.Services {
[UsedImplicitly]
@@ -14,7 +15,8 @@ namespace Orchard.Core.Feeds.Services {
}
public void OnResultExecuting(ResultExecutingContext filterContext) {
_workContextAccessor.GetContext(filterContext).Page.Zones["Head"].Add(html => html.ViewContext.Writer.Write(_feedManager.GetRegisteredLinks(html)), ":after");
IPage page =_workContextAccessor.GetContext(filterContext).Page;
page.Zones["Head"].Add((HtmlHelper html) => html.ViewContext.Writer.Write(_feedManager.GetRegisteredLinks(html)), ":after");
}
public void OnResultExecuted(ResultExecutedContext filterContext) {}

View File

@@ -13,6 +13,7 @@ using Orchard.Data.Providers;
using Orchard.Data.Migration;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy;
using Orchard.DisplayManagement.Implementation;
using Orchard.DisplayManagement.Shapes;
using Orchard.Environment;
@@ -31,6 +32,7 @@ using Orchard.Themes;
using Orchard.UI.Notify;
using Orchard.UI.PageClass;
using Orchard.UI.PageTitle;
using Orchard.UI.Zones;
namespace Orchard.Setup {
public class SetupMode : Module {
@@ -41,8 +43,8 @@ namespace Orchard.Setup {
builder.RegisterModule(new CommandModule());
builder.RegisterType<RoutePublisher>().As<IRoutePublisher>().InstancePerLifetimeScope();
builder.RegisterType<ModelBinderPublisher>().As<IModelBinderPublisher>().InstancePerLifetimeScope();
builder.RegisterType<WebFormViewEngineProvider>().As<IViewEngineProvider>().InstancePerLifetimeScope();
builder.RegisterType<RazorViewEngineProvider>().As<IViewEngineProvider>().InstancePerLifetimeScope();
builder.RegisterType<WebFormViewEngineProvider>().As<IViewEngineProvider>().As<IShapeTemplateViewEngine>().InstancePerLifetimeScope();
builder.RegisterType<RazorViewEngineProvider>().As<IViewEngineProvider>().As<IShapeTemplateViewEngine>().InstancePerLifetimeScope();
builder.RegisterType<ThemedViewResultFilter>().As<IFilterProvider>().InstancePerLifetimeScope();
builder.RegisterType<ThemeFilter>().As<IFilterProvider>().InstancePerLifetimeScope();
builder.RegisterType<PageTitleBuilder>().As<IPageTitleBuilder>().InstancePerLifetimeScope();
@@ -52,7 +54,7 @@ namespace Orchard.Setup {
builder.RegisterType<DataServicesProviderFactory>().As<IDataServicesProviderFactory>().InstancePerLifetimeScope();
builder.RegisterType<DefaultCommandManager>().As<ICommandManager>().InstancePerLifetimeScope();
builder.RegisterType<HelpCommand>().As<ICommandHandler>().InstancePerLifetimeScope();
builder.RegisterType<DefaultWorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<DefaultWorkContextAccessor>().As<IWorkContextAccessor>().InstancePerMatchingLifetimeScope("shell");
// setup mode specific implementations of needed service interfaces
builder.RegisterType<SafeModeThemeService>().As<IThemeService>().InstancePerLifetimeScope();
@@ -72,7 +74,11 @@ namespace Orchard.Setup {
builder.RegisterType<ThemeAwareViewEngine>().As<IThemeAwareViewEngine>();
builder.RegisterType<LayoutAwareViewEngine>().As<ILayoutAwareViewEngine>();
builder.RegisterType<ConfiguredEnginesCache>().As<IConfiguredEnginesCache>();
builder.RegisterType<PageWorkContextStateProvider>().As<IWorkContextStateProvider>().As<IShapeEvents>();
builder.RegisterType<PageWorkContext>().As<IWorkContextStateProvider>();
builder.RegisterType<CoreShapes>().As<IShapeDescriptorBindingStrategy>();
builder.RegisterType<ShapeTemplateBindingStrategy>().As<IShapeDescriptorBindingStrategy>();
builder.RegisterType<BasicShapeTemplateHarvester>().As<IShapeTemplateHarvester>();
}

View File

@@ -105,6 +105,7 @@
<Content Include="Themes\Contoso\Views\Orchard.Search\Search\Index.ascx" />
<Content Include="Themes\Corporate\Views\DisplayTemplates\Parts\Blogs.BlogPost.Metadata.ascx" />
<Content Include="Themes\Green\Views\Orchard.Search\Search\Index.ascx" />
<Content Include="Themes\SafeMode\Views\Zone.ascx" />
<Content Include="Themes\TheAdmin\Scripts\admin.js" />
<Content Include="Themes\TheAdmin\Styles\ie.css" />
<Content Include="Themes\TheAdmin\Styles\images\menuClosed.gif" />

View File

@@ -1,6 +1,6 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<BaseViewModel>" %>
<%@ Import Namespace="Orchard.Mvc.ViewModels" %>
<% //todo: (heskew) this should really be using the IResourceManager if it's to be a theme especially for the jquery dep (w/out needing to copy into this theme...)
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %>
<%-- //todo: (heskew) this should really be using the IResourceManager if it's to be a theme especially for the jquery dep (w/out needing to copy into this theme...)
var jquery = ResolveUrl("~/Modules/Orchard.Themes/Scripts/jquery-1.4.2.js");
Model.Zones.AddAction("head:scripts", html =>
html.ViewContext.Writer.Write(@"<script type=""text/javascript"" src=""" + jquery + @"""></script>"));
@@ -15,10 +15,12 @@
html.ViewContext.Writer.Write(@"<link rel=""stylesheet"" type=""text/css"" href=""" + siteCss + @"""/>"));
var ie6Css = ResolveUrl("../Styles/ie6.css");
Model.Zones.AddAction("head:styles", html =>
html.ViewContext.Writer.Write(@"<!--[if lte IE 6]><link rel=""stylesheet"" type=""text/css"" media=""screen, projection"" href=""" + ie6Css + @"""/><![endif]-->")); %>
html.ViewContext.Writer.Write(@"<!--[if lte IE 6]><link rel=""stylesheet"" type=""text/css"" media=""screen, projection"" href=""" + ie6Css + @"""/><![endif]-->"));
--%>
<div id="header">
<div id="branding"><h1>Welcome to Orchard</h1></div>
</div>
<div id="main">
<%Html.ZoneBody("content"); %>
<%: Display(Model.Content) %>
</div>

View File

@@ -0,0 +1,4 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %>
<div class="zone zone-<%:Model.ZoneName %>">
<%foreach (var item in Model.Items) {%><%:Display(item)%><%}%>
</div>

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace Orchard.DisplayManagement.Descriptors {
@@ -9,30 +10,41 @@ namespace Orchard.DisplayManagement.Descriptors {
_bindingStrategies = bindingStrategies;
}
private ShapeTable _shapeTable;
ConcurrentDictionary<string, ShapeTable> _tables = new ConcurrentDictionary<string, ShapeTable>();
public ShapeTable GetShapeTable(string themeName) {
if (_shapeTable == null) {
return _tables.GetOrAdd(themeName ?? "", x => {
var builder = new ShapeTableBuilder();
foreach (var bindingStrategy in _bindingStrategies) {
bindingStrategy.Discover(builder);
}
// placeholder - alterations will need to be selective and in a particular order
// GroupBy has been determined to preserve the order of items in original series
_shapeTable = new ShapeTable {
Descriptors = builder.Build()
.GroupBy(alteration => alteration.ShapeType)
var alterations = builder.Build()
.Where(alteration => IsModuleOrRequestedTheme(alteration, themeName));
var descriptors = alterations.GroupBy(alteration => alteration.ShapeType)
.Select(group => group.Aggregate(
new ShapeDescriptor { ShapeType = group.Key },
(d, a) => {
a.Alter(d);
return d;
}))
.ToDictionary(sd => sd.ShapeType)
(descriptor, alteration) => {
alteration.Alter(descriptor);
return descriptor;
}));
return new ShapeTable {
Descriptors = descriptors.ToDictionary(sd => sd.ShapeType)
};
});
}
return _shapeTable;
static bool IsModuleOrRequestedTheme(ShapeDescriptorAlteration alteration, string themeName) {
if (alteration == null ||
alteration.Feature == null ||
alteration.Feature.Extension == null) {
return false;
}
return alteration.Feature.Extension.ExtensionType == "Module" ||
(alteration.Feature.Extension.ExtensionType == "Theme" && alteration.Feature.Name == themeName);
}
}
}

View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment.Extensions.Models;
using System.Collections.Generic;
namespace Orchard.DisplayManagement.Descriptors {
@@ -18,104 +13,4 @@ namespace Orchard.DisplayManagement.Descriptors {
public interface IShapeDescriptorBindingStrategy : IDependency {
void Discover(ShapeTableBuilder builder);
}
public class ShapeTable {
public IDictionary<string, ShapeDescriptor> Descriptors { get; set; }
}
public class ShapeDescriptor {
public string ShapeType { get; set; }
/// <summary>
/// The BindingSource is informational text about the source of the Binding delegate. Not used except for
/// troubleshooting.
/// </summary>
public string BindingSource { get; set; }
public Func<DisplayContext, IHtmlString> Binding { get; set; }
}
public class ShapeTableBuilder {
readonly IList<ShapeDescriptorAlterationBuilderImpl> _descriptorBuilders = new List<ShapeDescriptorAlterationBuilderImpl>();
public ShapeDescriptorAlterationBuilder Describe {
get {
var db = new ShapeDescriptorAlterationBuilderImpl();
_descriptorBuilders.Add(db);
return db;
}
}
public IEnumerable<ShapeDescriptorAlteration> Build() {
return _descriptorBuilders.Select(b => b.Build());
}
class ShapeDescriptorAlterationBuilderImpl : ShapeDescriptorAlterationBuilder {
public ShapeDescriptorAlteration Build() {
return new ShapeDescriptorAlteration(_shapeType, _feature, _configurations.ToArray());
}
}
}
public class ShapeDescriptorAlteration {
private readonly IList<Action<ShapeDescriptor>> _configurations;
public ShapeDescriptorAlteration(string shapeType, FeatureDescriptor feature, IList<Action<ShapeDescriptor>> configurations) {
_configurations = configurations;
ShapeType = shapeType;
Feature = feature;
}
public string ShapeType { get; private set; }
public FeatureDescriptor Feature { get; private set; }
public void Alter(ShapeDescriptor descriptor) {
foreach (var configuration in _configurations) {
configuration(descriptor);
}
}
}
public class ShapeDescriptorAlterationBuilder {
protected FeatureDescriptor _feature;
protected string _shapeType;
protected readonly IList<Action<ShapeDescriptor>> _configurations = new List<Action<ShapeDescriptor>>();
public ShapeDescriptorAlterationBuilder Named(string shapeType) {
_shapeType = shapeType;
return this;
}
public ShapeDescriptorAlterationBuilder From(FeatureDescriptor feature) {
_feature = feature;
return this;
}
public ShapeDescriptorAlterationBuilder Configure(Action<ShapeDescriptor> action) {
_configurations.Add(action);
return this;
}
public ShapeDescriptorAlterationBuilder BoundAs(string bindingSource, Func<ShapeDescriptor, Func<DisplayContext, IHtmlString>> binder) {
// schedule the configuration
return Configure(descriptor => {
Func<DisplayContext, IHtmlString> target = null;
descriptor.BindingSource = bindingSource;
// announce the binding, which may be reconfigured before it's used
descriptor.Binding = displayContext => {
// when used, first realize the actual target once
if (target == null)
target = binder(descriptor);
// and execute the re
return target(displayContext);
};
});
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Web;
using Orchard.DisplayManagement.Implementation;
namespace Orchard.DisplayManagement.Descriptors {
public class ShapeDescriptor {
public string ShapeType { get; set; }
/// <summary>
/// The BindingSource is informational text about the source of the Binding delegate. Not used except for
/// troubleshooting.
/// </summary>
public string BindingSource { get; set; }
public Func<DisplayContext, IHtmlString> Binding { get; set; }
public IEnumerable<Action<ShapeCreatingContext>> Creating {get;set;}
public IEnumerable<Action<ShapeCreatedContext>> Created {get;set;}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using Orchard.Environment.Extensions.Models;
namespace Orchard.DisplayManagement.Descriptors {
public class ShapeDescriptorAlteration {
private readonly IList<Action<ShapeDescriptor>> _configurations;
public ShapeDescriptorAlteration(string shapeType, FeatureDescriptor feature, IList<Action<ShapeDescriptor>> configurations) {
_configurations = configurations;
ShapeType = shapeType;
Feature = feature;
}
public string ShapeType { get; private set; }
public FeatureDescriptor Feature { get; private set; }
public void Alter(ShapeDescriptor descriptor) {
foreach (var configuration in _configurations) {
configuration(descriptor);
}
}
}
}

View File

@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment.Extensions.Models;
namespace Orchard.DisplayManagement.Descriptors {
public class ShapeDescriptorAlterationBuilder {
protected FeatureDescriptor _feature;
protected string _shapeType;
protected readonly IList<Action<ShapeDescriptor>> _configurations = new List<Action<ShapeDescriptor>>();
public ShapeDescriptorAlterationBuilder Named(string shapeType) {
_shapeType = shapeType;
return this;
}
public ShapeDescriptorAlterationBuilder From(FeatureDescriptor feature) {
_feature = feature;
return this;
}
public ShapeDescriptorAlterationBuilder Configure(Action<ShapeDescriptor> action) {
_configurations.Add(action);
return this;
}
public ShapeDescriptorAlterationBuilder BoundAs(string bindingSource, Func<ShapeDescriptor, Func<DisplayContext, IHtmlString>> binder) {
// schedule the configuration
return Configure(descriptor => {
Func<DisplayContext, IHtmlString> target = null;
descriptor.BindingSource = bindingSource;
// announce the binding, which may be reconfigured before it's used
descriptor.Binding = displayContext => {
// when used, first realize the actual target once
if (target == null)
target = binder(descriptor);
// and execute the re
return target(displayContext);
};
});
}
public ShapeDescriptorAlterationBuilder OnCreating(Action<ShapeCreatingContext> action) {
return Configure(descriptor => {
var existing = descriptor.Creating ?? Enumerable.Empty<Action<ShapeCreatingContext>>();
descriptor.Creating = existing.Concat(new[] { action });
});
}
public ShapeDescriptorAlterationBuilder OnCreated(Action<ShapeCreatedContext> action) {
return Configure(descriptor => {
var existing = descriptor.Created ?? Enumerable.Empty<Action<ShapeCreatedContext>>();
descriptor.Created = existing.Concat(new[] { action });
});
}
}
}

View File

@@ -0,0 +1,7 @@
using System.Collections.Generic;
namespace Orchard.DisplayManagement.Descriptors {
public class ShapeTable {
public IDictionary<string, ShapeDescriptor> Descriptors { get; set; }
}
}

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
using System.Linq;
namespace Orchard.DisplayManagement.Descriptors {
public class ShapeTableBuilder {
readonly IList<ShapeDescriptorAlterationBuilderImpl> _descriptorBuilders = new List<ShapeDescriptorAlterationBuilderImpl>();
public ShapeDescriptorAlterationBuilder Describe {
get {
var db = new ShapeDescriptorAlterationBuilderImpl();
_descriptorBuilders.Add(db);
return db;
}
}
public IEnumerable<ShapeDescriptorAlteration> Build() {
return _descriptorBuilders.Select(b => b.Build());
}
class ShapeDescriptorAlterationBuilderImpl : ShapeDescriptorAlterationBuilder {
public ShapeDescriptorAlteration Build() {
return new ShapeDescriptorAlteration(_shapeType, _feature, _configurations.ToArray());
}
}
}
}

View File

@@ -11,6 +11,7 @@ using Orchard.Localization;
namespace Orchard.DisplayManagement.Implementation {
public class DefaultDisplayManager : IDisplayManager {
private readonly IShapeTableManager _shapeTableManager;
private readonly IWorkContextAccessor _workContextAccessor;
// this need to be Shape instead of IShape - cast to interface throws error on clr types like HtmlString
private static readonly CallSite<Func<CallSite, object, Shape>> _convertAsShapeCallsite = CallSite<Func<CallSite, object, Shape>>.Create(
@@ -20,8 +21,11 @@ namespace Orchard.DisplayManagement.Implementation {
typeof(Shape),
null/*typeof(DefaultDisplayManager)*/)));
public DefaultDisplayManager(IShapeTableManager shapeTableManager) {
public DefaultDisplayManager(
IShapeTableManager shapeTableManager,
IWorkContextAccessor workContextAccessor) {
_shapeTableManager = shapeTableManager;
_workContextAccessor = workContextAccessor;
T = NullLocalizer.Instance;
}
@@ -32,7 +36,7 @@ namespace Orchard.DisplayManagement.Implementation {
var shape = _convertAsShapeCallsite.Target(_convertAsShapeCallsite, context.Value);
// non-shape arguements are returned as a no-op
// non-shape arguments are returned as a no-op
if (shape == null)
return CoerceHtmlString(context.Value);
@@ -41,7 +45,8 @@ namespace Orchard.DisplayManagement.Implementation {
if (shapeMetadata == null || string.IsNullOrEmpty(shapeMetadata.Type))
return CoerceHtmlString(context.Value);
var shapeTable = _shapeTableManager.GetShapeTable(null);
var workContext = _workContextAccessor.GetContext(context.ViewContext);
var shapeTable = _shapeTableManager.GetShapeTable(workContext.CurrentTheme.ThemeName);
//preproc loop / event (alter shape, swapping type)
ShapeDescriptor shapeDescriptor;

View File

@@ -2,14 +2,20 @@
using System.Collections.Generic;
using System.Linq;
using ClaySharp;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Shapes;
namespace Orchard.DisplayManagement.Implementation {
public interface IShapeEvents : IDependency {
public interface IShapeFactoryEvents : IDependency {
void Creating(ShapeCreatingContext context);
void Created(ShapeCreatedContext context);
}
public abstract class ShapeFactoryEvents : IShapeFactoryEvents{
public virtual void Creating(ShapeCreatingContext context) {}
public virtual void Created(ShapeCreatedContext context) {}
}
public class ShapeCreatingContext {
public IShapeFactory ShapeFactory { get; set; }
public string ShapeType { get; set; }
@@ -27,13 +33,19 @@ namespace Orchard.DisplayManagement.Implementation {
public class DefaultShapeFactory : IShapeFactory {
private readonly IEnumerable<Lazy<IShapeEvents>> _events;
private readonly IEnumerable<Lazy<IShapeFactoryEvents>> _events;
private readonly IShapeTableManager _shapeTableManager;
public DefaultShapeFactory(IEnumerable<Lazy<IShapeEvents>> events) {
public DefaultShapeFactory(IEnumerable<Lazy<IShapeFactoryEvents>> events, IShapeTableManager shapeTableManager) {
_events = events;
_shapeTableManager = shapeTableManager;
}
public IShape Create(string shapeType, INamedEnumerable<object> parameters) {
var defaultShapeTable = _shapeTableManager.GetShapeTable(null);
ShapeDescriptor shapeDescriptor;
defaultShapeTable.Descriptors.TryGetValue(shapeType, out shapeDescriptor);
var creatingContext = new ShapeCreatingContext {
ShapeFactory = this,
ShapeType = shapeType,
@@ -50,7 +62,6 @@ namespace Orchard.DisplayManagement.Implementation {
// consume the first argument
positional = positional.Skip(1);
}
IClayBehavior[] behaviors;
if (creatingContext.BaseType == typeof(Array)) {
// array is a hint - not an intended base class
@@ -70,10 +81,17 @@ namespace Orchard.DisplayManagement.Implementation {
};
}
// "creating" events may add behaviors and alter base type
foreach (var ev in _events) {
ev.Value.Creating(creatingContext);
}
if (shapeDescriptor != null && shapeDescriptor.Creating != null) {
foreach (var ev in shapeDescriptor.Creating) {
ev(creatingContext);
}
}
// create the new instance
var createdContext = new ShapeCreatedContext {
ShapeFactory = this,
ShapeType = creatingContext.ShapeType,
@@ -81,6 +99,22 @@ namespace Orchard.DisplayManagement.Implementation {
};
createdContext.Shape.Metadata = new ShapeMetadata { Type = shapeType };
// "created" events provides default values and new object initialization
foreach (var ev in _events) {
ev.Value.Created(createdContext);
}
if (shapeDescriptor != null && shapeDescriptor.Created != null) {
foreach (var ev in shapeDescriptor.Created) {
ev(createdContext);
}
}
foreach (var ev in creatingContext.OnCreated) {
ev(createdContext);
}
// other properties passed with call overlay any defaults, so are after the created events
// only one non-Type, non-named argument is allowed
var initializer = positional.SingleOrDefault();
if (initializer != null) {
@@ -93,13 +127,6 @@ namespace Orchard.DisplayManagement.Implementation {
createdContext.Shape[kv.Key] = kv.Value;
}
foreach (var ev in creatingContext.OnCreated) {
ev(createdContext);
}
foreach (var ev in _events) {
ev.Value.Created(createdContext);
}
return createdContext.Shape;
}
}

View File

@@ -1,36 +0,0 @@
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

@@ -100,7 +100,12 @@ namespace Orchard.Environment {
}
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());
return (T)_state.GetOrAdd(name, x => GetStateInternal<T>(x));
}
private T GetStateInternal<T>(string name) {
return _workContextStateProviders.Select(wcsp => wcsp.Get<T>(name))
.FirstOrDefault(value => !Equals(value, default(T)));
}
public override void SetState<T>(string name, T value) {

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
namespace Orchard.Mvc.ViewEngines.Razor {
public class RazorViewEngine : CshtmlViewEngine {
protected override bool FileExists(ControllerContext controllerContext, string virtualPath) {
if (!virtualPath.EndsWith(".cshtml", StringComparison.InvariantCultureIgnoreCase))
return false;
return base.FileExists(controllerContext, virtualPath);
}
}
}

View File

@@ -79,7 +79,7 @@ namespace Orchard.Mvc.ViewEngines.Razor {
//Logger.Debug("UniversalFormats (module): \r\n\t-{0}", string.Join("\r\n\t-", universalFormats));
var viewEngine = new CshtmlViewEngine {
var viewEngine = new RazorViewEngine {
MasterLocationFormats = DisabledFormats,
ViewLocationFormats = universalFormats,
PartialViewLocationFormats = universalFormats,
@@ -92,7 +92,7 @@ namespace Orchard.Mvc.ViewEngines.Razor {
}
public IViewEngine CreateBareViewEngine() {
return new CshtmlViewEngine {
return new RazorViewEngine {
MasterLocationFormats = DisabledFormats,
ViewLocationFormats = DisabledFormats,
PartialViewLocationFormats = DisabledFormats,

View File

@@ -1,16 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using Autofac;
using ClaySharp;
using Orchard.ContentManagement;
using Orchard.Data.Migration;
using Autofac;
using Orchard.DisplayManagement;
using Orchard.Environment;
using Orchard.Localization;
using Orchard.Mvc.Html;
using Orchard.Security;
using Orchard.Security.Permissions;

View File

@@ -37,7 +37,7 @@ namespace Orchard.Mvc.ViewEngines.ThemeAwareness {
var buffer = new StringWriter();
findBody.View.Render(viewContext, buffer);
_workContext.Page.Zones["Content"].Add(new HtmlString(buffer.ToString()), "5");
_workContext.Page.Zones.Content.Add(new HtmlString(buffer.ToString()), "5");
var display = _displayHelperFactory.CreateHelper(viewContext, viewDataContainer);
IHtmlString result = display(_workContext.Page);

View File

@@ -1,13 +1,21 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy;
using Orchard.FileSystems.VirtualPath;
using Orchard.Logging;
namespace Orchard.Mvc.ViewEngines.WebForms {
public class WebFormViewEngineProvider : IViewEngineProvider {
public WebFormViewEngineProvider() {
public class WebFormViewEngineProvider : IViewEngineProvider, IShapeTemplateViewEngine {
private readonly IVirtualPathProvider _virtualPathProvider;
public WebFormViewEngineProvider(IVirtualPathProvider virtualPathProvider) {
_virtualPathProvider = virtualPathProvider;
Logger = NullLogger.Instance;
}
static string[] DisabledFormats = new[] { "~/Disabled" };
public ILogger Logger { get; set; }
@@ -98,5 +106,15 @@ namespace Orchard.Mvc.ViewEngines.WebForms {
AreaPartialViewLocationFormats = DisabledFormats,
};
}
public IEnumerable<string> DetectTemplateFileNames(string virtualPath) {
var fileNames = _virtualPathProvider.ListFiles(virtualPath).Select(Path.GetFileName);
foreach (var fileName in fileNames) {
if (fileName.EndsWith(".aspx", StringComparison.OrdinalIgnoreCase) ||
fileName.EndsWith(".ascx", StringComparison.OrdinalIgnoreCase)) {
yield return fileName;
}
}
}
}
}

View File

@@ -1,4 +1,7 @@
using System.Web.Mvc;
using Autofac;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Implementation;
using Orchard.Localization;
using Orchard.Mvc.Html;
using Orchard.Security;
@@ -6,15 +9,23 @@ using Orchard.Security.Permissions;
namespace Orchard.Mvc {
public class ViewPage<TModel> : System.Web.Mvc.ViewPage<TModel> {
public ViewPage() {
T = NullLocalizer.Instance;
}
private object _display;
private Localizer _localizer = NullLocalizer.Instance;
public Localizer T { get; set; }
public Localizer T { get { return _localizer; } }
public dynamic Display { get { return _display; } }
public IDisplayHelperFactory DisplayHelperFactory { get; set; }
public override void RenderView(ViewContext viewContext) {
T = LocalizationUtilities.Resolve(viewContext, AppRelativeVirtualPath);
base.RenderView(viewContext);
public IAuthorizer Authorizer { get; set; }
public override void InitHelpers() {
base.InitHelpers();
var workContext = ViewContext.GetWorkContext();
workContext.Resolve<IComponentContext>().InjectUnsetProperties(this);
_localizer = LocalizationUtilities.Resolve(ViewContext, AppRelativeVirtualPath);
_display = DisplayHelperFactory.CreateHelper(ViewContext, this);
}
public MvcHtmlString H(string value) {
@@ -22,7 +33,7 @@ namespace Orchard.Mvc {
}
public bool AuthorizedFor(Permission permission) {
return Html.Resolve<IAuthorizer>().Authorize(permission);
return Authorizer.Authorize(permission);
}
}
}

View File

@@ -1,19 +1,28 @@
using System.Web.Mvc;
using Autofac;
using Orchard.DisplayManagement;
using Orchard.Localization;
using Orchard.Mvc.Html;
using Orchard.Security;
using Orchard.Security.Permissions;
namespace Orchard.Mvc {
public class ViewUserControl<TModel> : System.Web.Mvc.ViewUserControl<TModel> {
public ViewUserControl() {
T = NullLocalizer.Instance;
}
private object _display;
private Localizer _localizer = NullLocalizer.Instance;
public Localizer T { get; set; }
public Localizer T { get { return _localizer; } }
public dynamic Display { get { return _display; } }
public IDisplayHelperFactory DisplayHelperFactory { get; set; }
public IAuthorizer Authorizer { get; set; }
public override void RenderView(ViewContext viewContext) {
T = LocalizationUtilities.Resolve(viewContext, AppRelativeVirtualPath);
var workContext = viewContext.GetWorkContext();
workContext.Resolve<IComponentContext>().InjectUnsetProperties(this);
_localizer = LocalizationUtilities.Resolve(viewContext, AppRelativeVirtualPath);
_display = DisplayHelperFactory.CreateHelper(viewContext, this);
base.RenderView(viewContext);
}
@@ -22,7 +31,10 @@ namespace Orchard.Mvc {
}
public bool AuthorizedFor(Permission permission) {
return Html.Resolve<IAuthorizer>().Authorize(permission);
return Authorizer.Authorize(permission);
}
}
public class ViewUserControl : ViewUserControl<dynamic> {
}
}

View File

@@ -139,8 +139,14 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="DisplayManagement\Implementation\PageWorkContextStateProvider.cs" />
<Compile Include="DisplayManagement\Zones\ZoneHoldingBehavior.cs" />
<Compile Include="DisplayManagement\Descriptors\ShapeDescriptor.cs" />
<Compile Include="DisplayManagement\Descriptors\ShapeDescriptorAlteration.cs" />
<Compile Include="DisplayManagement\Descriptors\ShapeDescriptorAlterationBuilder.cs" />
<Compile Include="DisplayManagement\Descriptors\ShapeTable.cs" />
<Compile Include="DisplayManagement\Descriptors\ShapeTableBuilder.cs" />
<Compile Include="Mvc\ViewEngines\Razor\RazorViewEngine.cs" />
<Compile Include="UI\Zones\PageWorkContext.cs" />
<Compile Include="UI\Zones\ZoneHoldingBehavior.cs" />
<Compile Include="Mvc\ViewEngines\ThemeAwareness\ConfiguredEnginesCache.cs" />
<Compile Include="Mvc\ViewEngines\ThemeAwareness\LayoutAwareViewEngine.cs" />
<Compile Include="Mvc\ViewEngines\ThemeAwareness\ThemeAwareViewEngine.cs" />
@@ -731,18 +737,18 @@
<Compile Include="UI\Resources\LinkEntry.cs" />
<Compile Include="UI\Resources\ResourceFilter.cs" />
<Compile Include="UI\Resources\ResourceManager.cs" />
<Compile Include="UI\Zones\DelegateZoneItem.cs" />
<Compile Include="UI\Zones\ContentItemDisplayZoneItem.cs" />
<Compile Include="UI\Zones\IZoneManager.cs" />
<Compile Include="UI\Zones\ContentPartDisplayZoneItem.cs" />
<Compile Include="UI\Zones\ContentPartEditorZoneItem.cs" />
<Compile Include="UI\Zones\IZoneManagerEvents.cs" />
<Compile Include="UI\Zones\RenderActionZoneItem.cs" />
<Compile Include="UI\Zones\RenderPartialZoneItem.cs" />
<Compile Include="UI\Zones\RenderStaticZoneItem.cs" />
<Compile Include="UI\Zones\ZoneCollection.cs" />
<Compile Include="UI\Zones\ZoneEntry.cs" />
<Compile Include="UI\Zones\ZoneItem.cs" />
<Compile Include="UI\Zones\Obsolete\DelegateZoneItem.cs" />
<Compile Include="UI\Zones\Obsolete\ContentItemDisplayZoneItem.cs" />
<Compile Include="UI\Zones\Obsolete\IZoneManager.cs" />
<Compile Include="UI\Zones\Obsolete\ContentPartDisplayZoneItem.cs" />
<Compile Include="UI\Zones\Obsolete\ContentPartEditorZoneItem.cs" />
<Compile Include="UI\Zones\Obsolete\IZoneManagerEvents.cs" />
<Compile Include="UI\Zones\Obsolete\RenderActionZoneItem.cs" />
<Compile Include="UI\Zones\Obsolete\RenderPartialZoneItem.cs" />
<Compile Include="UI\Zones\Obsolete\RenderStaticZoneItem.cs" />
<Compile Include="UI\Zones\Obsolete\ZoneCollection.cs" />
<Compile Include="UI\Zones\Obsolete\ZoneEntry.cs" />
<Compile Include="UI\Zones\Obsolete\ZoneItem.cs" />
<Compile Include="Mvc\ViewPage.cs">
<SubType>ASPXCodeBehind</SubType>
</Compile>
@@ -784,7 +790,7 @@
<Compile Include="FileSystems\Media\IStorageFile.cs" />
<Compile Include="FileSystems\Media\IStorageFolder.cs" />
<Compile Include="FileSystems\Media\IStorageProvider.cs" />
<Compile Include="UI\Zones\ZoneManager.cs" />
<Compile Include="UI\Zones\Obsolete\ZoneManager.cs" />
<Compile Include="Utility\Extensions\ReadOnlyCollectionExtensions.cs" />
<Compile Include="Utility\Extensions\RouteValueDictionaryExtensions.cs" />
<Compile Include="Utility\Extensions\StringExtensions.cs" />

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Web.Mvc;
using Orchard.DisplayManagement.Shapes;
namespace Orchard.UI {
public interface IPage {
@@ -31,7 +33,7 @@ namespace Orchard.UI {
IZone Add(Action<HtmlHelper> action, string position);
}
public class Zone : IZone {
public class Zone : Shape, IZone {
private readonly IList<object> _items = new List<object>();
public virtual string ZoneName { get; set; }
@@ -55,5 +57,11 @@ namespace Orchard.UI {
//throw new NotImplementedException();
return this;
}
public virtual IList<object> Items {
get {
return _items;
}
}
}
}

View File

@@ -0,0 +1,39 @@
using System.Linq;
using ClaySharp.Implementation;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.Environment.Extensions.Models;
namespace Orchard.UI.Zones {
public class PageWorkContext : IWorkContextStateProvider {
private readonly IShapeFactory _shapeFactory;
public PageWorkContext(IShapeFactory shapeFactory) {
_shapeFactory = shapeFactory;
}
public T Get<T>(string name) {
if (name == "Page") {
return (dynamic)_shapeFactory.Create("Layout", Arguments.Empty());
}
return default(T);
}
}
public class CoreShapes : IShapeDescriptorBindingStrategy {
public void Discover(ShapeTableBuilder builder) {
var feature = new FeatureDescriptor {
Name = "Orchard.Framework",
Extension = new ExtensionDescriptor {
Name = "Orchard.Framework",
ExtensionType = "Module",
}
};
builder.Describe.Named("Layout").From(feature)
.OnCreating(context => context.Behaviors.Add(new ZoneHoldingBehavior(context.ShapeFactory)));
builder.Describe.Named("Zone").From(feature)
.OnCreating(context => context.BaseType = typeof(Zone));
}
}
}

View File

@@ -3,9 +3,22 @@ using System.Linq;
using ClaySharp;
using ClaySharp.Behaviors;
using ClaySharp.Implementation;
using Orchard.UI;
using Orchard.DisplayManagement;
namespace Orchard.DisplayManagement.Zones {
namespace Orchard.UI.Zones {
/// <summary>
/// Provides the behavior of shapes that have a Zones property.
/// Examples include Layout and Item
///
/// * Returns a fake parent object for zones
/// Foo.Zones
///
/// *
/// Foo.Zones.Alpha :
/// Foo.Zones["Alpha"]
/// Foo.Alpha :same
///
/// </summary>
public class ZoneHoldingBehavior : ClayBehavior {
private readonly IShapeFactory _shapeFactory;
@@ -15,18 +28,21 @@ namespace Orchard.DisplayManagement.Zones {
public override object GetMember(Func<object> proceed, object self, string name) {
if (name == "Zones") {
// provide a robot for zone manipulation on parent object
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) {
// substitute nil results with a robot that turns adds a zone on
// the parent when .Add is invoked
return ClayActivator.CreateInstance(new IClayBehavior[] {
new NilResultBehavior(),
new InterfaceProxyBehavior(),
new NilBehavior(),
new ZoneOnDemandBehavior(_shapeFactory, self, name)
});
}
@@ -47,6 +63,7 @@ namespace Orchard.DisplayManagement.Zones {
if (parentMember == null) {
return ClayActivator.CreateInstance(new IClayBehavior[] {
new InterfaceProxyBehavior(),
new NilBehavior(),
new ZoneOnDemandBehavior(_shapeFactory, _parent, name)
});
}
@@ -63,12 +80,12 @@ namespace Orchard.DisplayManagement.Zones {
public class ZoneOnDemandBehavior : ClayBehavior {
private readonly IShapeFactory _shapeFactory;
private readonly object _parent;
private readonly string _name;
private readonly string _potentialZoneName;
public ZoneOnDemandBehavior(IShapeFactory shapeFactory, object parent, string name) {
public ZoneOnDemandBehavior(IShapeFactory shapeFactory, object parent, string potentialZoneName) {
_shapeFactory = shapeFactory;
_parent = parent;
_name = name;
_potentialZoneName = potentialZoneName;
}
public override object InvokeMember(Func<object> proceed, object self, string name, INamedEnumerable<object> args) {
@@ -77,8 +94,8 @@ namespace Orchard.DisplayManagement.Zones {
dynamic parent = _parent;
dynamic zone = _shapeFactory.Create("Zone", Arguments.Empty());
zone.ZoneName = name;
parent[name] = zone;
zone.ZoneName = _potentialZoneName;
parent[_potentialZoneName] = zone;
if (argsCount == 1)
return zone.Add(args.Single());

View File

@@ -18,8 +18,8 @@ namespace Orchard {
set { SetState("HttpContext", value); }
}
public IPage Page {
get { return GetState<IPage>("Page"); }
public dynamic Page {
get { return GetState<object>("Page"); }
set { SetState("Page", value); }
}