mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-14 19:04:51 +08:00
Placement.info support enabled
Enabled Module and Theme Placement.info files are parsed, and influence the location of certain shapes This only applies to part or field shapes that are added by returning ContentShape(...) from a display/editor driver --HG-- branch : composition extra : transplant_source : %CF%B0%17%E1%18%C1%06o%B2%91a%23%A1%3D%872%BE%F8%01%F3
This commit is contained in:
@@ -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" />
|
||||
|
@@ -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>
|
@@ -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>
|
||||
|
@@ -21,7 +21,7 @@ namespace Orchard.ContentManagement {
|
||||
private readonly IRepository<ContentItemVersionRecord> _contentItemVersionRepository;
|
||||
private readonly IContentDefinitionManager _contentDefinitionManager;
|
||||
private readonly Func<IContentManagerSession> _contentManagerSession;
|
||||
private readonly IContentDisplay _contentDisplay;
|
||||
private readonly Lazy<IContentDisplay> _contentDisplay;
|
||||
|
||||
public DefaultContentManager(
|
||||
IComponentContext context,
|
||||
@@ -30,7 +30,7 @@ namespace Orchard.ContentManagement {
|
||||
IRepository<ContentItemVersionRecord> contentItemVersionRepository,
|
||||
IContentDefinitionManager contentDefinitionManager,
|
||||
Func<IContentManagerSession> contentManagerSession,
|
||||
IContentDisplay contentDisplay) {
|
||||
Lazy<IContentDisplay> contentDisplay) {
|
||||
_context = context;
|
||||
_contentTypeRepository = contentTypeRepository;
|
||||
_contentItemRepository = contentItemRepository;
|
||||
@@ -370,15 +370,15 @@ namespace Orchard.ContentManagement {
|
||||
|
||||
|
||||
public dynamic BuildDisplay(IContent content, string displayType = "") {
|
||||
return _contentDisplay.BuildDisplay(content, displayType);
|
||||
return _contentDisplay.Value.BuildDisplay(content, displayType);
|
||||
}
|
||||
|
||||
public dynamic BuildEditor(IContent content) {
|
||||
return _contentDisplay.BuildEditor(content);
|
||||
return _contentDisplay.Value.BuildEditor(content);
|
||||
}
|
||||
|
||||
public dynamic UpdateEditor(IContent content, IUpdateModel updater) {
|
||||
return _contentDisplay.UpdateEditor(content, updater);
|
||||
return _contentDisplay.Value.UpdateEditor(content, updater);
|
||||
}
|
||||
|
||||
public IContentQuery<ContentItem> Query() {
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Orchard.Environment.Descriptor.Models;
|
||||
using Orchard.Environment.Extensions;
|
||||
using Orchard.Environment.Extensions.Models;
|
||||
|
||||
namespace Orchard.DisplayManagement.Descriptors.ShapePlacements {
|
||||
public class ShapePlacementParsingStrategy : IShapeTableProvider {
|
||||
private readonly IExtensionManager _extensionManager;
|
||||
private readonly ShellDescriptor _shellDescriptor;
|
||||
|
||||
public ShapePlacementParsingStrategy(
|
||||
IExtensionManager extensionManager,
|
||||
ShellDescriptor shellDescriptor) {
|
||||
_extensionManager = extensionManager;
|
||||
_shellDescriptor = shellDescriptor;
|
||||
}
|
||||
|
||||
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)) {
|
||||
builder.Describe("Parts_RoutableTitle")
|
||||
.From(new Feature{Descriptor = featureDescriptor})
|
||||
.Placement(ctx => ctx.ContentType == "WidgetPage", "Content:after");
|
||||
}
|
||||
//var featureDescriptors = extensionDescriptor.Where(fd => fd.Name == hit.extensionDescriptor.Name);
|
||||
//foreach (var featureDescriptor in featureDescriptors) {
|
||||
}
|
||||
//builder.Describe("Parts_RoutableTitle")
|
||||
//.Placement(ctx => ctx.ContentType == "Page", "Content:after");
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -165,7 +165,8 @@
|
||||
<Compile Include="DisplayManagement\Descriptors\ShapeDescriptor.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\ShapeAlteration.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\ShapeAlterationBuilder.cs" />
|
||||
<Compile Include="DisplayManagement\Descriptors\ShapePlacements\ShapePlacementParsingStrategy.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" />
|
||||
|
Reference in New Issue
Block a user