mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-14 19:04:51 +08:00
Merge
--HG-- branch : dev
This commit is contained in:
@@ -153,7 +153,68 @@ namespace Orchard.Tests.DisplayManagement.Descriptors {
|
||||
Assert.That(foo.Displaying.Single(), Is.SameAs(cb3));
|
||||
Assert.That(foo.Displayed.Single(), Is.SameAs(cb4));
|
||||
}
|
||||
[Test]
|
||||
public void DefaultPlacementIsReturnedByDefault() {
|
||||
var manager = _container.Resolve<IShapeTableManager>();
|
||||
|
||||
var hello = manager.GetShapeTable(null).Descriptors["Hello"];
|
||||
hello.DefaultPlacement = "Header:5";
|
||||
var result = hello.Placement(null);
|
||||
Assert.That(result, Is.EqualTo("Header:5"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DescribedPlacementIsReturnedIfNotNull() {
|
||||
|
||||
_container.Resolve<TestShapeProvider>().Discover =
|
||||
builder => builder.Describe("Hello")
|
||||
.Placement(ctx => ctx.DisplayType == "Detail" ? "Main" : null)
|
||||
.Placement(ctx => ctx.DisplayType == "Summary" ? "" : null);
|
||||
|
||||
var manager = _container.Resolve<IShapeTableManager>();
|
||||
var hello = manager.GetShapeTable(null).Descriptors["Hello"];
|
||||
var result1 = hello.Placement(new ShapePlacementContext { DisplayType = "Detail" });
|
||||
var result2 = hello.Placement(new ShapePlacementContext { DisplayType = "Summary" });
|
||||
var result3 = hello.Placement(new ShapePlacementContext { DisplayType = "Tile" });
|
||||
hello.DefaultPlacement = "Header:5";
|
||||
var result4 = hello.Placement(new ShapePlacementContext { DisplayType = "Detail" });
|
||||
var result5 = hello.Placement(new ShapePlacementContext { DisplayType = "Summary" });
|
||||
var result6 = hello.Placement(new ShapePlacementContext { DisplayType = "Tile" });
|
||||
|
||||
Assert.That(result1, Is.EqualTo("Main"));
|
||||
Assert.That(result2, Is.EqualTo(""));
|
||||
Assert.That(result3, Is.Null);
|
||||
Assert.That(result4, Is.EqualTo("Main"));
|
||||
Assert.That(result5, Is.EqualTo(""));
|
||||
Assert.That(result6, Is.EqualTo("Header:5"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TwoArgumentVariationDoesSameThing() {
|
||||
|
||||
_container.Resolve<TestShapeProvider>().Discover =
|
||||
builder => builder.Describe("Hello")
|
||||
.Placement(ctx => ctx.DisplayType == "Detail", "Main")
|
||||
.Placement(ctx => ctx.DisplayType == "Summary", "");
|
||||
|
||||
var manager = _container.Resolve<IShapeTableManager>();
|
||||
var hello = manager.GetShapeTable(null).Descriptors["Hello"];
|
||||
var result1 = hello.Placement(new ShapePlacementContext { DisplayType = "Detail" });
|
||||
var result2 = hello.Placement(new ShapePlacementContext { DisplayType = "Summary" });
|
||||
var result3 = hello.Placement(new ShapePlacementContext { DisplayType = "Tile" });
|
||||
hello.DefaultPlacement = "Header:5";
|
||||
var result4 = hello.Placement(new ShapePlacementContext { DisplayType = "Detail" });
|
||||
var result5 = hello.Placement(new ShapePlacementContext { DisplayType = "Summary" });
|
||||
var result6 = hello.Placement(new ShapePlacementContext { DisplayType = "Tile" });
|
||||
|
||||
Assert.That(result1, Is.EqualTo("Main"));
|
||||
Assert.That(result2, Is.EqualTo(""));
|
||||
Assert.That(result3, Is.Null);
|
||||
Assert.That(result4, Is.EqualTo("Main"));
|
||||
Assert.That(result5, Is.EqualTo(""));
|
||||
Assert.That(result6, Is.EqualTo("Header:5"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnlyShapesFromTheGivenThemeAreProvided() {
|
||||
_container.Resolve<TestShapeProvider>();
|
||||
@@ -182,5 +243,7 @@ namespace Orchard.Tests.DisplayManagement.Descriptors {
|
||||
Assert.IsTrue(table.Bindings.ContainsKey("OverriddenShape"));
|
||||
Assert.AreEqual("DerivedTheme", table.Descriptors["OverriddenShape"].BindingSource);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Orchard.Caching;
|
||||
using Orchard.FileSystems.WebSite;
|
||||
|
||||
namespace Orchard.Tests.DisplayManagement.Descriptors {
|
||||
public class InMemoryWebSiteFolder : IWebSiteFolder {
|
||||
public InMemoryWebSiteFolder() {
|
||||
Contents = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public IDictionary<string, string> Contents { get; set; }
|
||||
|
||||
public IEnumerable<string> ListDirectories(string virtualPath) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<string> ListFiles(string virtualPath, bool recursive) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool FileExists(string virtualPath) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string ReadFile(string virtualPath) {
|
||||
string result;
|
||||
return Contents.TryGetValue(virtualPath, out result) ? result : null;
|
||||
}
|
||||
|
||||
public string ReadFile(string virtualPath, bool actualContent) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void CopyFileTo(string virtualPath, Stream destination) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void CopyFileTo(string virtualPath, Stream destination, bool actualContent) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IVolatileToken WhenPathChanges(string virtualPath) {
|
||||
return new Token { IsCurrent = true };
|
||||
}
|
||||
|
||||
public class Token : IVolatileToken {
|
||||
public bool IsCurrent { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Autofac;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Caching;
|
||||
using Orchard.DisplayManagement.Descriptors.ShapePlacementStrategy;
|
||||
using Orchard.FileSystems.WebSite;
|
||||
using Orchard.Tests.Stubs;
|
||||
|
||||
namespace Orchard.Tests.DisplayManagement.Descriptors {
|
||||
[TestFixture]
|
||||
public class PlacementFileParserTests : ContainerTestBase {
|
||||
private IPlacementFileParser _parser;
|
||||
private InMemoryWebSiteFolder _folder;
|
||||
|
||||
protected override void Register(Autofac.ContainerBuilder builder) {
|
||||
builder.RegisterType<PlacementFileParser>().As<IPlacementFileParser>();
|
||||
builder.RegisterType<StubCacheManager>().As<ICacheManager>();
|
||||
builder.RegisterType<InMemoryWebSiteFolder>().As<IWebSiteFolder>()
|
||||
.As<InMemoryWebSiteFolder>().InstancePerLifetimeScope();
|
||||
}
|
||||
|
||||
|
||||
protected override void Resolve(IContainer container) {
|
||||
_parser = container.Resolve<IPlacementFileParser>();
|
||||
_folder = container.Resolve<InMemoryWebSiteFolder>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParsingMissingFileIsNull() {
|
||||
var result = _parser.Parse("~/hello.xml");
|
||||
Assert.That(result, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParsingEmptyFileAsNothing() {
|
||||
_folder.Contents["~/hello.xml"] = "<Placement/>";
|
||||
var result = _parser.Parse("~/hello.xml");
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result.Nodes, Is.Not.Null);
|
||||
Assert.That(result.Nodes.Count(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ItemsComeBackAsPlacementNodes() {
|
||||
_folder.Contents["~/hello.xml"] = @"
|
||||
<Placement>
|
||||
<Match ContentType=""BlogPost""/>
|
||||
<Match ContentType=""Page""/>
|
||||
</Placement>
|
||||
";
|
||||
var result = _parser.Parse("~/hello.xml");
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result.Nodes, Is.Not.Null);
|
||||
Assert.That(result.Nodes.Count(), Is.EqualTo(2));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void NestedItemsComeBackAsNestedNodes() {
|
||||
_folder.Contents["~/hello.xml"] = @"
|
||||
<Placement>
|
||||
<Match ContentType=""BlogPost"">
|
||||
<Match DisplayType=""Detail""/>
|
||||
</Match>
|
||||
<Match ContentType=""Page""/>
|
||||
</Placement>
|
||||
";
|
||||
var result = _parser.Parse("~/hello.xml");
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result.Nodes, Is.Not.Null);
|
||||
Assert.That(result.Nodes.Count(), Is.EqualTo(2));
|
||||
Assert.That(result.Nodes.First().Nodes.Count(), Is.EqualTo(1));
|
||||
Assert.That(result.Nodes.Last().Nodes.Count(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EachPlaceAttributeIsShapeLocation() {
|
||||
_folder.Contents["~/hello.xml"] = @"
|
||||
<Place Foo=""Header"" Bar=""Content:after""/>
|
||||
";
|
||||
var result = _parser.Parse("~/hello.xml");
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result.Nodes, Is.Not.Null);
|
||||
Assert.That(result.Nodes.Count(), Is.EqualTo(2));
|
||||
var foo = result.Nodes.OfType<PlacementShapeLocation>().Single(x=>x.ShapeType == "Foo");
|
||||
var bar = result.Nodes.OfType<PlacementShapeLocation>().Single(x=>x.ShapeType == "Bar");
|
||||
Assert.That(foo.Location, Is.EqualTo("Header"));
|
||||
Assert.That(bar.Location, Is.EqualTo("Content:after"));
|
||||
}
|
||||
}
|
||||
}
|
@@ -216,6 +216,8 @@
|
||||
<Compile Include="ContainerTestBase.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\BasicShapeTemplateHarvesterTests.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\DefaultShapeTableManagerTests.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\InMemoryWebSiteFolder.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\PlacementFileParserTests.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\ShapeAttributeBindingStrategyTests.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\ShapeTemplateBindingStrategyTests.cs" />
|
||||
<Compile Include="DisplayManagement\ShapeFactoryTests.cs" />
|
||||
|
@@ -34,19 +34,17 @@ namespace Orchard.Core.Common.Drivers {
|
||||
}
|
||||
|
||||
protected override DriverResult Display(BodyPart part, string displayType, dynamic shapeHelper) {
|
||||
var bodyText = _htmlFilters.Aggregate(part.Text, (text, filter) => filter.ProcessContent(text));
|
||||
var body = shapeHelper.Parts_Common_Body(ContentPart: part, Html: new HtmlString(bodyText));
|
||||
if (!string.IsNullOrWhiteSpace(displayType))
|
||||
body.Metadata.Type = string.Format("{0}.{1}", body.Metadata.Type, displayType);
|
||||
var location = part.GetLocation(displayType);
|
||||
|
||||
//return Combined(
|
||||
// Services.Authorizer.Authorize(Permissions.ChangeOwner) ? ContentPartTemplate(model, "Parts/Common.Body.ManageWrapperPre").LongestMatch(displayType, "SummaryAdmin").Location(location) : null,
|
||||
// Services.Authorizer.Authorize(Permissions.ChangeOwner) ? ContentPartTemplate(model, "Parts/Common.Body.Manage").LongestMatch(displayType, "SummaryAdmin").Location(location) : null,
|
||||
// ContentPartTemplate(model, TemplateName, Prefix).LongestMatch(displayType, "Summary", "SummaryAdmin").Location(location),
|
||||
// Services.Authorizer.Authorize(Permissions.ChangeOwner) ? ContentPartTemplate(model, "Parts/Common.Body.ManageWrapperPost").LongestMatch(displayType, "SummaryAdmin").Location(location) : null);
|
||||
|
||||
return ContentShape(body).Location(location);
|
||||
return Combined(
|
||||
ContentShape("Parts_Common_Body", displayType == "Detail" ? "Content" : null, () => {
|
||||
var bodyText = _htmlFilters.Aggregate(part.Text, (text, filter) => filter.ProcessContent(text));
|
||||
return shapeHelper.Parts_Common_Body(ContentPart: part, Html: new HtmlString(bodyText));
|
||||
}),
|
||||
ContentShape("Parts_Common_Body_Summary", displayType == "Summary" ? "Content" : null, () => {
|
||||
var bodyText = _htmlFilters.Aggregate(part.Text, (text, filter) => filter.ProcessContent(text));
|
||||
return shapeHelper.Parts_Common_Body_Summary(ContentPart: part, Html: new HtmlString(bodyText));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
protected override DriverResult Editor(BodyPart part, dynamic shapeHelper) {
|
||||
|
@@ -1,15 +1,27 @@
|
||||
using Orchard.DisplayManagement.Implementation;
|
||||
using System;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.DisplayManagement.Descriptors;
|
||||
using Orchard.DisplayManagement.Implementation;
|
||||
using Orchard.UI.Zones;
|
||||
|
||||
namespace Orchard.Core.Contents {
|
||||
public class Shapes : IShapeFactoryEvents {
|
||||
|
||||
public void Creating(ShapeCreatingContext creating) {
|
||||
if (creating.ShapeType.StartsWith("Items_Content"))
|
||||
creating.Behaviors.Add(new ZoneHoldingBehavior(name => creating.New.ContentZone()));
|
||||
public class Shapes : IShapeTableProvider {
|
||||
public void Discover(ShapeTableBuilder builder) {
|
||||
builder.Describe("Items_Content")
|
||||
.OnCreating(creating => creating.Behaviors.Add(new ZoneHoldingBehavior(name => ContentZone(creating, name))))
|
||||
.OnDisplaying(displaying => {
|
||||
ContentItem contentItem = displaying.Shape.ContentItem;
|
||||
if (contentItem != null) {
|
||||
displaying.ShapeMetadata.Alternates.Add("Items_Content__" + contentItem.ContentType);
|
||||
displaying.ShapeMetadata.Alternates.Add("Items_Content__" + contentItem.Id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Created(ShapeCreatedContext created) {
|
||||
private static object ContentZone(ShapeCreatingContext creating, string name) {
|
||||
var zone = creating.New.ContentZone();
|
||||
zone.ZoneName = name;
|
||||
return zone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,6 +3,6 @@
|
||||
@Display(Model.Header)
|
||||
</header>
|
||||
<section>
|
||||
@Display(Model.Primary)
|
||||
@Display(Model.Content)
|
||||
</section>
|
||||
</article>
|
@@ -392,7 +392,7 @@
|
||||
<Content Include="Shapes\Views\MenuItem.cshtml" />
|
||||
<Content Include="Shapes\Views\Web.config" />
|
||||
<Content Include="Dashboard\Views\Helper\Index.cshtml" />
|
||||
<Content Include="Routable\Views\Parts\Routable.RoutePart.cshtml" />
|
||||
<None Include="Routable\Views\Parts\RoutableTitle.cshtml" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
|
||||
|
@@ -45,11 +45,7 @@ namespace Orchard.Core.Routable.Drivers {
|
||||
}
|
||||
|
||||
protected override DriverResult Display(RoutePart part, string displayType, dynamic shapeHelper) {
|
||||
var routePart = shapeHelper.Parts_Routable_RoutePart(ContentPart: part, Title: part.Title);
|
||||
if (!string.IsNullOrWhiteSpace(displayType))
|
||||
routePart.Metadata.Type = string.Format("{0}.{1}", routePart.Metadata.Type, displayType);
|
||||
var location = part.GetLocation(displayType, "Header", "5");
|
||||
return ContentShape(routePart).Location(location);
|
||||
return ContentShape("Parts_RoutableTitle", "Header:5", () => shapeHelper.Parts_RoutableTitle(ContentPart: part, Title: part.Title));
|
||||
}
|
||||
|
||||
protected override DriverResult Editor(RoutePart part, dynamic shapeHelper) {
|
||||
@@ -80,7 +76,7 @@ namespace Orchard.Core.Routable.Drivers {
|
||||
}
|
||||
|
||||
protected override DriverResult Editor(RoutePart part, IUpdateModel updater, dynamic shapeHelper) {
|
||||
|
||||
|
||||
var model = new RoutableEditorViewModel();
|
||||
updater.TryUpdateModel(model, Prefix, null, null);
|
||||
part.Title = model.Title;
|
||||
|
@@ -215,6 +215,7 @@ namespace Orchard.Setup.Services {
|
||||
contentDefinitionManager.AlterPartDefinition("BodyPart", cfg => cfg
|
||||
.WithSetting("BodyPartSettings.FlavorDefault", BodyPartSettings.FlavorDefaultDefault));
|
||||
|
||||
|
||||
// add a layer for the homepage
|
||||
var layer = contentManager.Create("Layer");
|
||||
layer.As<LayerPart>().Name = "TheHomepage";
|
||||
|
@@ -115,6 +115,7 @@
|
||||
<None Include="Views\EditorTemplates\Parts\Widgets.WidgetPart.cshtml" />
|
||||
<None Include="Views\EditorTemplates\Parts\Widgets.LayerPart.cshtml" />
|
||||
<None Include="Views\EditorTemplates\Parts\Widgets.WidgetBagPart.cshtml" />
|
||||
<Content Include="Views\Items_Widget.cshtml" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
|
||||
|
@@ -0,0 +1,4 @@
|
||||
TheWidget
|
||||
@Display(Model.Content)
|
||||
@Display(Model.Primary)
|
||||
@Display(Model.Body)
|
@@ -125,6 +125,7 @@
|
||||
<Content Include="Default.aspx" />
|
||||
<Content Include="Global.asax" />
|
||||
<Content Include="Refresh.html" />
|
||||
<None Include="Themes\Classic\Placement.info" />
|
||||
<Content Include="Themes\Contoso\Styles\search.css" />
|
||||
<Content Include="Themes\Contoso\Zones\Footer.html" />
|
||||
<Content Include="Themes\Corporate\Views\DisplayTemplates\Parts\Blogs.BlogPost.Metadata.ascx" />
|
||||
|
21
src/Orchard.Web/Themes/Classic/Placement.info
Normal file
21
src/Orchard.Web/Themes/Classic/Placement.info
Normal file
@@ -0,0 +1,21 @@
|
||||
<Placement>
|
||||
<!--
|
||||
note - commas, and wildcards, do NOT work, but would be very nice
|
||||
<Match ContentType="Page">
|
||||
|
||||
<Match DisplayType="Detail,Summary*" >
|
||||
<Place
|
||||
Parts_RoutableTitle="Header"
|
||||
Parts_BodyText="Content:8"
|
||||
/>
|
||||
</Match>
|
||||
</Match>
|
||||
-->
|
||||
|
||||
<Match ContentType="Page" DisplayType="Detail">
|
||||
<Place
|
||||
Parts_RoutableTitle="Content:5.b"
|
||||
Parts_Common_Body="Header:after" />
|
||||
</Match>
|
||||
|
||||
</Placement>
|
@@ -141,6 +141,21 @@ p {
|
||||
margin-top:12px;
|
||||
}
|
||||
|
||||
/*Two Sidebars on */
|
||||
.has-sidebars #content {
|
||||
background-color:#e8e8e8;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.has-sidebars .sidebar {
|
||||
background-color:#f6f6f6;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.has-sidebars .primary, .has-sidebars .secondary, .has-sidebars #content {
|
||||
float:left;
|
||||
}
|
||||
|
||||
|
||||
/* Navigation */
|
||||
|
||||
|
@@ -85,7 +85,6 @@
|
||||
<aside class="aside first">
|
||||
@Zone(Model.AsideFirst)
|
||||
</aside>
|
||||
}
|
||||
|
||||
@* Create a zone and only show it on the home page. *@
|
||||
|
||||
|
@@ -5,6 +5,8 @@
|
||||
<add path="*.aspx" verb="*" type="System.Web.HttpNotFoundHandler"/>
|
||||
<add path="*.ascx" verb="*" type="System.Web.HttpNotFoundHandler"/>
|
||||
<add path="*.master" verb="*" type="System.Web.HttpNotFoundHandler"/>
|
||||
<add path="*.cshtml" verb="*" type="System.Web.HttpNotFoundHandler"/>
|
||||
<add path="*.info" verb="*" type="System.Web.HttpNotFoundHandler"/>
|
||||
</httpHandlers>
|
||||
|
||||
<!--
|
||||
@@ -29,7 +31,7 @@
|
||||
<validation validateIntegratedModeConfiguration="false"/>
|
||||
<handlers>
|
||||
<remove name="BlockViewHandler"/>
|
||||
<add name="BlockViewHandler" path="*.aspx,*.ascx,*.master" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler"/>
|
||||
<add name="BlockViewHandler" path="*.aspx,*.ascx,*.master,*.cshtml,*.info" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler"/>
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
<runtime>
|
||||
|
@@ -130,7 +130,7 @@
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
|
||||
<bindingRedirect oldVersion="2.0.0.0" newVersion="3.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
|
123
src/Orchard/ContentManagement/DefaultContentDisplay.cs
Normal file
123
src/Orchard/ContentManagement/DefaultContentDisplay.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web.Routing;
|
||||
using Microsoft.CSharp.RuntimeBinder;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.DisplayManagement.Descriptors;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Mvc;
|
||||
using Orchard.Themes;
|
||||
|
||||
namespace Orchard.ContentManagement {
|
||||
public class DefaultContentDisplay : IContentDisplay {
|
||||
private readonly Lazy<IEnumerable<IContentHandler>> _handlers;
|
||||
private readonly IShapeHelperFactory _shapeHelperFactory;
|
||||
private readonly IShapeTableManager _shapeTableManager;
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly Lazy<IThemeService> _themeService;
|
||||
private readonly RequestContext _requestContext;
|
||||
|
||||
public DefaultContentDisplay(
|
||||
Lazy<IEnumerable<IContentHandler>> handlers,
|
||||
IShapeHelperFactory shapeHelperFactory,
|
||||
IShapeTableManager shapeTableManager,
|
||||
IWorkContextAccessor workContextAccessor,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
Lazy<IThemeService> themeService,
|
||||
RequestContext requestContext) {
|
||||
_handlers = handlers;
|
||||
_shapeHelperFactory = shapeHelperFactory;
|
||||
_shapeTableManager = shapeTableManager;
|
||||
_workContextAccessor = workContextAccessor;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_themeService = themeService;
|
||||
_requestContext = requestContext;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
static readonly CallSiteCollection _shapeHelperCalls = new CallSiteCollection(shapeTypeName => Binder.InvokeMember(
|
||||
CSharpBinderFlags.None,
|
||||
shapeTypeName,
|
||||
Enumerable.Empty<Type>(),
|
||||
null,
|
||||
new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
|
||||
|
||||
|
||||
public dynamic BuildDisplay(IContent content, string displayType) {
|
||||
var contentTypeDefinition = content.ContentItem.TypeDefinition;
|
||||
string stereotype;
|
||||
if (!contentTypeDefinition.Settings.TryGetValue("Stereotype", out stereotype))
|
||||
stereotype = "Content";
|
||||
|
||||
var shapeTypeName = "Items_" + stereotype;
|
||||
var shapeDisplayType = string.IsNullOrWhiteSpace(displayType) ? "Detail" : displayType;
|
||||
|
||||
var shapeHelper = _shapeHelperFactory.CreateHelper();
|
||||
var itemShape = _shapeHelperCalls.Invoke(shapeHelper, shapeTypeName);
|
||||
|
||||
itemShape.ContentItem = content.ContentItem;
|
||||
itemShape.Metadata.DisplayType = shapeDisplayType;
|
||||
|
||||
var context = new BuildDisplayContext(itemShape, content, shapeDisplayType, _shapeHelperFactory);
|
||||
BindPlacement(context, displayType);
|
||||
|
||||
_handlers.Value.Invoke(handler => handler.BuildDisplay(context), Logger);
|
||||
return context.Shape;
|
||||
}
|
||||
|
||||
public dynamic BuildEditor(IContent content) {
|
||||
var shapeHelper = _shapeHelperFactory.CreateHelper();
|
||||
var itemShape = shapeHelper.Items_Content_Edit();
|
||||
|
||||
IContent iContent = content;
|
||||
if (iContent != null)
|
||||
itemShape.ContentItem = iContent.ContentItem;
|
||||
|
||||
var context = new BuildEditorContext(itemShape, content, _shapeHelperFactory);
|
||||
BindPlacement(context, null);
|
||||
|
||||
_handlers.Value.Invoke(handler => handler.BuildEditor(context), Logger);
|
||||
return context.Shape;
|
||||
}
|
||||
|
||||
public dynamic UpdateEditor(IContent content, IUpdateModel updater) {
|
||||
var shapeHelper = _shapeHelperFactory.CreateHelper();
|
||||
var itemShape = shapeHelper.Items_Content_Edit();
|
||||
|
||||
IContent iContent = content;
|
||||
if (iContent != null)
|
||||
itemShape.ContentItem = iContent.ContentItem;
|
||||
|
||||
var context = new UpdateEditorContext(itemShape, content, updater, _shapeHelperFactory);
|
||||
BindPlacement(context, null);
|
||||
|
||||
_handlers.Value.Invoke(handler => handler.UpdateEditor(context), Logger);
|
||||
return context.Shape;
|
||||
}
|
||||
|
||||
private void BindPlacement(BuildShapeContext context, string displayType) {
|
||||
context.FindPlacement = (partShapeType, defaultLocation) => {
|
||||
//var workContext = _workContextAccessor.GetContext();
|
||||
//var theme = workContext.CurrentTheme;
|
||||
var theme = _themeService.Value.GetRequestTheme(_requestContext);
|
||||
var shapeTable = _shapeTableManager.GetShapeTable(theme.ThemeName);
|
||||
ShapeDescriptor descriptor;
|
||||
if (shapeTable.Descriptors.TryGetValue(partShapeType, out descriptor)) {
|
||||
var placementContext = new ShapePlacementContext {
|
||||
ContentType = context.ContentItem.ContentType,
|
||||
DisplayType = displayType
|
||||
};
|
||||
var location = descriptor.Placement(placementContext);
|
||||
return location ?? defaultLocation;
|
||||
}
|
||||
return defaultLocation;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,14 +4,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Autofac;
|
||||
using Microsoft.CSharp.RuntimeBinder;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.ContentManagement.MetaData.Builders;
|
||||
using Orchard.ContentManagement.MetaData.Models;
|
||||
using Orchard.ContentManagement.Records;
|
||||
using Orchard.Data;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Indexing;
|
||||
using Orchard.Logging;
|
||||
|
||||
@@ -23,7 +21,7 @@ namespace Orchard.ContentManagement {
|
||||
private readonly IRepository<ContentItemVersionRecord> _contentItemVersionRepository;
|
||||
private readonly IContentDefinitionManager _contentDefinitionManager;
|
||||
private readonly Func<IContentManagerSession> _contentManagerSession;
|
||||
private readonly IShapeHelperFactory _shapeHelperFactory;
|
||||
private readonly Lazy<IContentDisplay> _contentDisplay;
|
||||
|
||||
public DefaultContentManager(
|
||||
IComponentContext context,
|
||||
@@ -32,14 +30,14 @@ namespace Orchard.ContentManagement {
|
||||
IRepository<ContentItemVersionRecord> contentItemVersionRepository,
|
||||
IContentDefinitionManager contentDefinitionManager,
|
||||
Func<IContentManagerSession> contentManagerSession,
|
||||
IShapeHelperFactory shapeHelperFactory) {
|
||||
Lazy<IContentDisplay> contentDisplay) {
|
||||
_context = context;
|
||||
_contentTypeRepository = contentTypeRepository;
|
||||
_contentItemRepository = contentItemRepository;
|
||||
_contentItemVersionRepository = contentItemVersionRepository;
|
||||
_contentDefinitionManager = contentDefinitionManager;
|
||||
_contentManagerSession = contentManagerSession;
|
||||
_shapeHelperFactory = shapeHelperFactory;
|
||||
_contentDisplay = contentDisplay;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
@@ -370,54 +368,17 @@ namespace Orchard.ContentManagement {
|
||||
return context.Metadata;
|
||||
}
|
||||
|
||||
static readonly CallSiteCollection _shapeHelperCalls = new CallSiteCollection(shapeTypeName => Binder.InvokeMember(
|
||||
CSharpBinderFlags.None,
|
||||
shapeTypeName,
|
||||
Enumerable.Empty<Type>(),
|
||||
null,
|
||||
new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
|
||||
|
||||
|
||||
public dynamic BuildDisplay(dynamic content, string displayType = "") {
|
||||
var shapeHelper = _shapeHelperFactory.CreateHelper();
|
||||
|
||||
var shapeTypeName = string.IsNullOrEmpty(displayType) ? "Items_Content" : ("Items_Content_" + displayType);
|
||||
var itemShape = _shapeHelperCalls.Invoke(shapeHelper, shapeTypeName);
|
||||
|
||||
IContent iContent = content;
|
||||
if (iContent != null)
|
||||
itemShape.ContentItem = iContent.ContentItem;
|
||||
|
||||
var context = new BuildDisplayContext(itemShape, content, displayType, _shapeHelperFactory);
|
||||
Handlers.Invoke(handler => handler.BuildDisplay(context), Logger);
|
||||
return context.Shape;
|
||||
public dynamic BuildDisplay(IContent content, string displayType = "") {
|
||||
return _contentDisplay.Value.BuildDisplay(content, displayType);
|
||||
}
|
||||
|
||||
|
||||
public dynamic BuildEditor(dynamic content) {
|
||||
var shapeHelper = _shapeHelperFactory.CreateHelper();
|
||||
var itemShape = shapeHelper.Items_Content_Edit();
|
||||
|
||||
IContent iContent = content;
|
||||
if (iContent != null)
|
||||
itemShape.ContentItem = iContent.ContentItem;
|
||||
|
||||
var context = new BuildEditorContext(itemShape, content, _shapeHelperFactory);
|
||||
Handlers.Invoke(handler => handler.BuildEditor(context), Logger);
|
||||
return context.Shape;
|
||||
public dynamic BuildEditor(IContent content) {
|
||||
return _contentDisplay.Value.BuildEditor(content);
|
||||
}
|
||||
|
||||
public dynamic UpdateEditor(dynamic content, IUpdateModel updater) {
|
||||
var shapeHelper = _shapeHelperFactory.CreateHelper();
|
||||
var itemShape = shapeHelper.Items_Content_Edit();
|
||||
|
||||
IContent iContent = content;
|
||||
if (iContent != null)
|
||||
itemShape.ContentItem = iContent.ContentItem;
|
||||
|
||||
var context = new UpdateEditorContext(itemShape, content, updater, _shapeHelperFactory);
|
||||
Handlers.Invoke(handler => handler.UpdateEditor(context), Logger);
|
||||
return context.Shape;
|
||||
public dynamic UpdateEditor(IContent content, IUpdateModel updater) {
|
||||
return _contentDisplay.Value.UpdateEditor(content, updater);
|
||||
}
|
||||
|
||||
public IContentQuery<ContentItem> Query() {
|
||||
|
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.DisplayManagement;
|
||||
|
||||
namespace Orchard.ContentManagement.Drivers {
|
||||
public abstract class ContentPartDriver<TContent> : IContentPartDriver where TContent : ContentPart, new() {
|
||||
@@ -27,8 +28,21 @@ namespace Orchard.ContentManagement.Drivers {
|
||||
protected virtual DriverResult Editor(TContent part, dynamic shapeHelper) { return null; }
|
||||
protected virtual DriverResult Editor(TContent part, IUpdateModel updater, dynamic shapeHelper) { return null; }
|
||||
|
||||
public ContentShapeResult ContentShape(dynamic shape) {
|
||||
return new ContentShapeResult(shape, Prefix).Location(Zone);
|
||||
[Obsolete("Provided while transitioning to factory variations")]
|
||||
public ContentShapeResult ContentShape(IShape shape) {
|
||||
return ContentShapeImplementation(shape.Metadata.Type, Zone, () => shape);
|
||||
}
|
||||
|
||||
public ContentShapeResult ContentShape(string shapeType, Func<dynamic> factory) {
|
||||
return ContentShapeImplementation(shapeType, null, factory);
|
||||
}
|
||||
|
||||
public ContentShapeResult ContentShape(string shapeType, string defaultLocation, Func<dynamic> factory) {
|
||||
return ContentShapeImplementation(shapeType, defaultLocation, factory);
|
||||
}
|
||||
|
||||
private ContentShapeResult ContentShapeImplementation(string shapeType, string defaultLocation, Func<object> factory) {
|
||||
return new ContentShapeResult(shapeType, Prefix, factory).Location(defaultLocation);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
|
@@ -1,37 +1,57 @@
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using System;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.DisplayManagement.Shapes;
|
||||
|
||||
namespace Orchard.ContentManagement.Drivers {
|
||||
public class ContentShapeResult : DriverResult {
|
||||
public dynamic Shape { get; set; }
|
||||
public string Prefix { get; set; }
|
||||
public string Zone { get; set; }
|
||||
public string Position { get; set; }
|
||||
private string _defaultLocation;
|
||||
private readonly string _shapeType;
|
||||
private readonly string _prefix;
|
||||
private readonly Func<dynamic> _shapeBuilder;
|
||||
|
||||
public ContentShapeResult(dynamic shape, string prefix) {
|
||||
Shape = shape;
|
||||
Prefix = prefix;
|
||||
public ContentShapeResult(string shapeType, string prefix, Func<dynamic> shapeBuilder) {
|
||||
_shapeType = shapeType;
|
||||
_prefix = prefix;
|
||||
_shapeBuilder = shapeBuilder;
|
||||
}
|
||||
|
||||
public override void Apply(BuildDisplayContext context) {
|
||||
context.Shape.Zones[Zone].Add(Shape, Position);
|
||||
ApplyImplementation(context, context.DisplayType);
|
||||
}
|
||||
|
||||
public override void Apply(BuildEditorContext context) {
|
||||
IShape iShape = Shape;
|
||||
if (iShape != null )
|
||||
Shape.Metadata.Prefix = Prefix;
|
||||
context.Shape.Zones[Zone].Add(Shape, Position);
|
||||
ApplyImplementation(context, null);
|
||||
}
|
||||
|
||||
private void ApplyImplementation(BuildShapeContext context, string displayType) {
|
||||
var location = context.FindPlacement(_shapeType, _defaultLocation);
|
||||
if (string.IsNullOrEmpty(location) || location == "-")
|
||||
return;
|
||||
|
||||
dynamic parentShape = context.Shape;
|
||||
IShape contentShape = _shapeBuilder();
|
||||
contentShape.Metadata.Prefix = _prefix;
|
||||
contentShape.Metadata.DisplayType = displayType;
|
||||
|
||||
var delimiterIndex = location.IndexOf(':');
|
||||
if (delimiterIndex < 0) {
|
||||
parentShape.Zones[location].Add(contentShape);
|
||||
}
|
||||
else {
|
||||
var zoneName = location.Substring(0, delimiterIndex);
|
||||
var position = location.Substring(delimiterIndex + 1);
|
||||
parentShape.Zones[zoneName].Add(contentShape, position);
|
||||
}
|
||||
}
|
||||
|
||||
public ContentShapeResult Location(string zone) {
|
||||
Zone = zone;
|
||||
_defaultLocation = zone;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContentShapeResult Location(string zone, string position) {
|
||||
Zone = zone;
|
||||
Position = position;
|
||||
_defaultLocation = zone + ":" + position;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Orchard.DisplayManagement;
|
||||
|
||||
namespace Orchard.ContentManagement.Handlers {
|
||||
@@ -6,10 +7,13 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
Shape = shape;
|
||||
ContentItem = content.ContentItem;
|
||||
New = shapeHelperFactory.CreateHelper();
|
||||
FindPlacement = (partType, defaultLocation) => defaultLocation;
|
||||
}
|
||||
|
||||
public dynamic Shape { get; private set; }
|
||||
public ContentItem ContentItem { get; private set; }
|
||||
public dynamic New { get; private set; }
|
||||
|
||||
public Func<string, string, string> FindPlacement { get; set; }
|
||||
}
|
||||
}
|
@@ -1,5 +1,10 @@
|
||||
namespace Orchard.ContentManagement {
|
||||
public interface IContent {
|
||||
ContentItem ContentItem { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The ContentItem's identifier.
|
||||
/// </summary>
|
||||
int Id { get; }
|
||||
}
|
||||
}
|
||||
|
@@ -26,9 +26,15 @@ namespace Orchard.ContentManagement {
|
||||
|
||||
ContentItemMetadata GetItemMetadata(IContent contentItem);
|
||||
|
||||
dynamic BuildDisplay(dynamic content, string displayType = "");
|
||||
dynamic BuildEditor(dynamic content);
|
||||
dynamic UpdateEditor(dynamic content, IUpdateModel updater);
|
||||
dynamic BuildDisplay(IContent content, string displayType = "");
|
||||
dynamic BuildEditor(IContent content);
|
||||
dynamic UpdateEditor(IContent content, IUpdateModel updater);
|
||||
}
|
||||
|
||||
public interface IContentDisplay : IDependency {
|
||||
dynamic BuildDisplay(IContent content, string displayType = "");
|
||||
dynamic BuildEditor(IContent content);
|
||||
dynamic UpdateEditor(IContent content, IUpdateModel updater);
|
||||
}
|
||||
|
||||
public class VersionOptions {
|
||||
|
@@ -73,7 +73,7 @@ namespace Orchard.DisplayManagement.Descriptors {
|
||||
descriptor.Created = existing.Concat(new[] { action });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public ShapeAlterationBuilder OnDisplaying(Action<ShapeDisplayingContext> action) {
|
||||
return Configure(descriptor => {
|
||||
var existing = descriptor.Displaying ?? Enumerable.Empty<Action<ShapeDisplayingContext>>();
|
||||
@@ -88,8 +88,28 @@ namespace Orchard.DisplayManagement.Descriptors {
|
||||
});
|
||||
}
|
||||
|
||||
public ShapeAlterationBuilder Placement(Func<ShapePlacementContext, string> action) {
|
||||
return Configure(descriptor => {
|
||||
var next = descriptor.Placement;
|
||||
descriptor.Placement = ctx => action(ctx) ?? next(ctx);
|
||||
});
|
||||
}
|
||||
|
||||
public ShapeAlterationBuilder Placement(Func<ShapePlacementContext, bool> predicate, string location) {
|
||||
return Configure(descriptor => {
|
||||
var next = descriptor.Placement;
|
||||
descriptor.Placement = ctx => predicate(ctx) ? location : next(ctx);
|
||||
});
|
||||
}
|
||||
|
||||
public ShapeAlteration Build() {
|
||||
return new ShapeAlteration(_shapeType, _feature, _configurations.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ShapePlacementContext {
|
||||
public string ContentType { get; set; }
|
||||
public string DisplayType { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@ namespace Orchard.DisplayManagement.Descriptors {
|
||||
Displayed = Enumerable.Empty<Action<ShapeDisplayedContext>>();
|
||||
Wrappers = new List<string>();
|
||||
Bindings = new Dictionary<string, ShapeBinding>();
|
||||
Placement = ctx => DefaultPlacement;
|
||||
}
|
||||
|
||||
public string ShapeType { get; set; }
|
||||
@@ -41,6 +42,9 @@ namespace Orchard.DisplayManagement.Descriptors {
|
||||
public IEnumerable<Action<ShapeDisplayingContext>> Displaying { get; set; }
|
||||
public IEnumerable<Action<ShapeDisplayedContext>> Displayed { get; set; }
|
||||
|
||||
public Func<ShapePlacementContext, string> Placement { get; set; }
|
||||
public string DefaultPlacement { get; set; }
|
||||
|
||||
public IList<string> Wrappers { get; set; }
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Orchard.DisplayManagement.Descriptors.ShapePlacementStrategy {
|
||||
public class PlacementFile : PlacementNode {
|
||||
}
|
||||
|
||||
public class PlacementNode {
|
||||
public IEnumerable<PlacementNode> Nodes { get; set; }
|
||||
}
|
||||
|
||||
public class PlacementMatch : PlacementNode {
|
||||
public IDictionary<string, string> Terms { get; set; }
|
||||
}
|
||||
|
||||
public class PlacementShapeLocation : PlacementNode {
|
||||
public string ShapeType { get; set; }
|
||||
public string Location { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.Caching;
|
||||
using Orchard.FileSystems.WebSite;
|
||||
|
||||
namespace Orchard.DisplayManagement.Descriptors.ShapePlacementStrategy {
|
||||
|
||||
/// <summary>
|
||||
/// Parses and caches the Placement.info file contents for a given IWebSiteFolder vdir
|
||||
/// </summary>
|
||||
public interface IPlacementFileParser : IDependency {
|
||||
PlacementFile Parse(string virtualPath);
|
||||
}
|
||||
|
||||
|
||||
public class PlacementFileParser : IPlacementFileParser {
|
||||
private readonly ICacheManager _cacheManager;
|
||||
private readonly IWebSiteFolder _webSiteFolder;
|
||||
|
||||
public PlacementFileParser(ICacheManager cacheManager, IWebSiteFolder webSiteFolder) {
|
||||
_cacheManager = cacheManager;
|
||||
_webSiteFolder = webSiteFolder;
|
||||
}
|
||||
|
||||
public PlacementFile Parse(string virtualPath) {
|
||||
return _cacheManager.Get(virtualPath, context => {
|
||||
context.Monitor(_webSiteFolder.WhenPathChanges(virtualPath));
|
||||
var placementText = _webSiteFolder.ReadFile(virtualPath);
|
||||
return ParseImplementation(virtualPath, placementText);
|
||||
});
|
||||
}
|
||||
|
||||
private PlacementFile ParseImplementation(string virtualPath, string placementText) {
|
||||
if (placementText == null)
|
||||
return null;
|
||||
|
||||
|
||||
var element = XElement.Parse(placementText);
|
||||
return new PlacementFile {
|
||||
Nodes = Accept(element).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<PlacementNode> Accept(XElement element) {
|
||||
switch (element.Name.LocalName) {
|
||||
case "Placement":
|
||||
return AcceptMatch(element);
|
||||
case "Match":
|
||||
return AcceptMatch(element);
|
||||
case "Place":
|
||||
return AcceptPlace(element);
|
||||
}
|
||||
return Enumerable.Empty<PlacementNode>();
|
||||
}
|
||||
|
||||
|
||||
private IEnumerable<PlacementNode> AcceptMatch(XElement element) {
|
||||
if (element.HasAttributes == false) {
|
||||
// Match with no attributes will collapse child results upward
|
||||
// rather than return an unconditional node
|
||||
return element.Elements().SelectMany(Accept);
|
||||
}
|
||||
|
||||
// return match node that carries back key/value dictionary of condition,
|
||||
// and has child rules nested as Nodes
|
||||
return new[]{new PlacementMatch{
|
||||
Terms = element.Attributes().ToDictionary(attr=>attr.Name.LocalName, attr=>attr.Value),
|
||||
Nodes=element.Elements().SelectMany(Accept).ToArray(),
|
||||
}};
|
||||
}
|
||||
|
||||
private IEnumerable<PlacementShapeLocation> AcceptPlace(XElement element) {
|
||||
// return attributes as part locations
|
||||
return element.Attributes().Select(attr => new PlacementShapeLocation {
|
||||
ShapeType = attr.Name.LocalName,
|
||||
Location = attr.Value
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Environment.Descriptor.Models;
|
||||
using Orchard.Environment.Extensions;
|
||||
using Orchard.Environment.Extensions.Models;
|
||||
|
||||
namespace Orchard.DisplayManagement.Descriptors.ShapePlacementStrategy {
|
||||
/// <summary>
|
||||
/// This component discovers and announces the shape alterations implied by the contents of the Placement.info files
|
||||
/// </summary>
|
||||
public class ShapePlacementParsingStrategy : IShapeTableProvider {
|
||||
private readonly IExtensionManager _extensionManager;
|
||||
private readonly ShellDescriptor _shellDescriptor;
|
||||
private readonly IPlacementFileParser _placementFileParser;
|
||||
|
||||
public ShapePlacementParsingStrategy(
|
||||
IExtensionManager extensionManager,
|
||||
ShellDescriptor shellDescriptor,
|
||||
IPlacementFileParser placementFileParser) {
|
||||
_extensionManager = extensionManager;
|
||||
_shellDescriptor = shellDescriptor;
|
||||
_placementFileParser = placementFileParser;
|
||||
}
|
||||
|
||||
public void Discover(ShapeTableBuilder builder) {
|
||||
|
||||
var availableFeatures = _extensionManager.AvailableFeatures();
|
||||
var activeFeatures = availableFeatures.Where(fd => FeatureIsTheme(fd) || FeatureIsEnabled(fd));
|
||||
var activeExtensions = Once(activeFeatures);
|
||||
|
||||
foreach (var extensionDescriptor in activeExtensions) {
|
||||
foreach (var featureDescriptor in extensionDescriptor.Features.Where(fd=>fd.Name == fd.Extension.Name)) {
|
||||
ProcessFeatureDescriptor(builder, featureDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessFeatureDescriptor(ShapeTableBuilder builder, FeatureDescriptor featureDescriptor) {
|
||||
// The App_Data subfolder is used because it is not served.
|
||||
// But it's still considered a site-deployable content file, which is why the IWebSiteFolder (not the IAppDataFolder) is appropriate
|
||||
var virtualPath = featureDescriptor.Extension.Location + "/" + featureDescriptor.Extension.Name + "/App_Data/Placement.xml";
|
||||
var placementFile = _placementFileParser.Parse(virtualPath);
|
||||
if (placementFile != null) {
|
||||
ProcessPlacementFile(builder, featureDescriptor, placementFile);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessPlacementFile(ShapeTableBuilder builder, FeatureDescriptor featureDescriptor, PlacementFile placementFile) {
|
||||
var feature = new Feature {Descriptor = featureDescriptor};
|
||||
|
||||
// invert the tree into a list of leaves and the stack
|
||||
var entries = DrillDownShapeLocations(placementFile.Nodes, Enumerable.Empty<PlacementMatch>());
|
||||
foreach (var entry in entries) {
|
||||
var shapeLocation = entry.Item1;
|
||||
var matches = entry.Item2;
|
||||
|
||||
Func<ShapePlacementContext, bool> predicate = ctx => true;
|
||||
predicate = matches.SelectMany(match=>match.Terms).Aggregate(predicate, BuildPredicate);
|
||||
|
||||
builder.Describe(shapeLocation.ShapeType)
|
||||
.From(feature)
|
||||
.Placement(predicate, shapeLocation.Location);
|
||||
}
|
||||
}
|
||||
|
||||
private Func<ShapePlacementContext, bool> BuildPredicate(Func<ShapePlacementContext, bool> predicate, KeyValuePair<string, string> term) {
|
||||
var expression = term.Value;
|
||||
switch(term.Key) {
|
||||
case "ContentType":
|
||||
return ctx=>ctx.ContentType == expression ? true : predicate(ctx);
|
||||
case "DisplayType":
|
||||
return ctx=>ctx.DisplayType == expression ? true : predicate(ctx);
|
||||
}
|
||||
return predicate;
|
||||
}
|
||||
|
||||
|
||||
private static IEnumerable<Tuple<PlacementShapeLocation, IEnumerable<PlacementMatch>>> DrillDownShapeLocations(
|
||||
IEnumerable<PlacementNode> nodes,
|
||||
IEnumerable<PlacementMatch> path) {
|
||||
|
||||
// return shape locations nodes in this place
|
||||
foreach (var placementShapeLocation in nodes.OfType<PlacementShapeLocation>()) {
|
||||
yield return new Tuple<PlacementShapeLocation, IEnumerable<PlacementMatch>>(placementShapeLocation, path);
|
||||
}
|
||||
// recurse down into match nodes
|
||||
foreach (var placementMatch in nodes.OfType<PlacementMatch>()) {
|
||||
foreach (var findShapeLocation in DrillDownShapeLocations(placementMatch.Nodes, path.Concat(new[] {placementMatch}))) {
|
||||
yield return findShapeLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool FeatureIsTheme(FeatureDescriptor fd) {
|
||||
return fd.Extension.ExtensionType == "Theme";
|
||||
}
|
||||
|
||||
private bool FeatureIsEnabled(FeatureDescriptor fd) {
|
||||
return _shellDescriptor.Features.Any(sf => sf.Name == fd.Name);
|
||||
}
|
||||
|
||||
private static IEnumerable<ExtensionDescriptor> Once(IEnumerable<FeatureDescriptor> featureDescriptors) {
|
||||
var once = new ConcurrentDictionary<string, object>();
|
||||
return featureDescriptors.Select(fd => fd.Extension).Where(ed => once.TryAdd(ed.Name, null)).ToList();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -9,6 +9,7 @@ namespace Orchard.DisplayManagement.Shapes {
|
||||
}
|
||||
|
||||
public string Type { get; set; }
|
||||
public string DisplayType { get; set; }
|
||||
public string Position { get; set; }
|
||||
public string Prefix { get; set; }
|
||||
public IList<string> Wrappers { get; set; }
|
||||
|
@@ -159,11 +159,14 @@
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ContentManagement\DefaultContentDisplay.cs" />
|
||||
<Compile Include="ContentManagement\Drivers\ContentShapeResult.cs" />
|
||||
<Compile Include="ContentManagement\Handlers\BuildShapeContext.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\ShapeDescriptor.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\ShapeAlteration.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\ShapeAlterationBuilder.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\ShapePlacementStrategy\PlacementFileParser.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\ShapePlacementStrategy\ShapePlacementParsingStrategy.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\ShapeTable.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\ShapeTableBuilder.cs" />
|
||||
<Compile Include="DisplayManagement\Implementation\IShapeDisplayEvents.cs" />
|
||||
|
@@ -24,6 +24,7 @@ MenuItem
|
||||
-RouteValues
|
||||
-Item (clr object)
|
||||
|
||||
|
||||
List: ul|ol + li*
|
||||
.Items (meta-property, bound to shape children or passed in)
|
||||
.Id
|
||||
|
Reference in New Issue
Block a user