Last active
August 30, 2017 17:43
-
-
Save MatteoPiovanelli-Laser/2a96626f0e22e5172796ee1d9d446df9 to your computer and use it in GitHub Desktop.
ShapePlacementParsingStrategy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Concurrent; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Web; | |
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; | |
private readonly IEnumerable<IPlacementParseStategyMatchProvider> _parseMatchProviders; | |
public ShapePlacementParsingStrategy( | |
IExtensionManager extensionManager, | |
ShellDescriptor shellDescriptor, | |
IPlacementFileParser placementFileParser, | |
IEnumerable<IPlacementParseStategyMatchProvider> parseMatchProviders) { | |
_extensionManager = extensionManager; | |
_shellDescriptor = shellDescriptor; | |
_placementFileParser = placementFileParser; | |
_parseMatchProviders = parseMatchProviders; | |
} | |
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.Id == fd.Extension.Id)) { | |
ProcessFeatureDescriptor(builder, featureDescriptor); | |
} | |
} | |
} | |
private void ProcessFeatureDescriptor(ShapeTableBuilder builder, FeatureDescriptor featureDescriptor) { | |
var virtualPath = featureDescriptor.Extension.Location + "/" + featureDescriptor.Extension.Id + "/Placement.info"; | |
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; | |
string shapeType; | |
string differentiator; | |
GetShapeType(shapeLocation, out shapeType, out differentiator); | |
Func<ShapePlacementContext, bool> originalPredicate = ctx => true; | |
Func<ShapePlacementContext, bool> customPredicate = ctx => true; | |
if (differentiator != "") { | |
originalPredicate = ctx => (ctx.Differentiator ?? "") == differentiator; | |
} | |
if (matches.Any()) { | |
originalPredicate = matches.SelectMany(match => match.Terms).Aggregate(originalPredicate, BuildPredicate); | |
customPredicate = matches.SelectMany(match => match.Terms).Aggregate(customPredicate, CustomBuildPredicate); | |
} | |
var placement = new PlacementInfo(); | |
var segments = shapeLocation.Location.Split(';').Select(s => s.Trim()); | |
foreach (var segment in segments) { | |
if (!segment.Contains('=')) { | |
placement.Location = segment; | |
} | |
else { | |
var index = segment.IndexOf('='); | |
var property = segment.Substring(0, index).ToLower(); | |
var value = segment.Substring(index + 1); | |
switch (property) { | |
case "shape": | |
placement.ShapeType = value; | |
break; | |
case "alternate": | |
placement.Alternates = new[] { value }; | |
break; | |
case "wrapper": | |
placement.Wrappers = new[] { value }; | |
break; | |
} | |
} | |
} | |
builder.Describe(shapeType) | |
.From(feature) | |
.Placement(ctx => { | |
var hit = customPredicate(ctx) && originalPredicate; | |
// generate 'debugging' information to trace which file originated the actual location | |
if (hit) { | |
var virtualPath = featureDescriptor.Extension.Location + "/" + featureDescriptor.Extension.Id + "/Placement.info"; | |
ctx.Source = virtualPath; | |
} | |
return hit; | |
}, placement); | |
} | |
} | |
private void GetShapeType(PlacementShapeLocation shapeLocation, out string shapeType, out string differentiator) { | |
differentiator = ""; | |
shapeType = shapeLocation.ShapeType; | |
var separatorLengh = 2; | |
var separatorIndex = shapeType.LastIndexOf("__"); | |
var dashIndex = shapeType.LastIndexOf('-'); | |
if (dashIndex > separatorIndex) { | |
separatorIndex = dashIndex; | |
separatorLengh = 1; | |
} | |
if (separatorIndex > 0 && separatorIndex < shapeType.Length - separatorLengh) { | |
differentiator = shapeType.Substring(separatorIndex + separatorLengh); | |
shapeType = shapeType.Substring(0, separatorIndex); | |
} | |
} | |
private static Func<ShapePlacementContext, bool> BuildPredicate(Func<ShapePlacementContext, bool> predicate, KeyValuePair<string, string> term) { | |
var expression = term.Value; | |
switch (term.Key) { | |
case "ContentPart": | |
return ctx => ctx.Content != null | |
&& ctx.Content.ContentItem.Parts.Any(part => part.PartDefinition.Name == expression) | |
&& predicate(ctx); | |
case "ContentType": | |
if (expression.EndsWith("*")) { | |
var prefix = expression.Substring(0, expression.Length - 1); | |
return ctx => ((ctx.ContentType ?? "").StartsWith(prefix) || (ctx.Stereotype ?? "").StartsWith(prefix)) && predicate(ctx); | |
} | |
return ctx => ((ctx.ContentType == expression) || (ctx.Stereotype == expression)) && predicate(ctx); | |
case "DisplayType": | |
if (expression.EndsWith("*")) { | |
var prefix = expression.Substring(0, expression.Length - 1); | |
return ctx => (ctx.DisplayType ?? "").StartsWith(prefix) && predicate(ctx); | |
} | |
return ctx => (ctx.DisplayType == expression) && predicate(ctx); | |
case "Path": | |
var normalizedPath = VirtualPathUtility.IsAbsolute(expression) | |
? VirtualPathUtility.ToAppRelative(expression) | |
: VirtualPathUtility.Combine("~/", expression); | |
if (normalizedPath.EndsWith("*")) { | |
var prefix = normalizedPath.Substring(0, normalizedPath.Length - 1); | |
return ctx => VirtualPathUtility.ToAppRelative(String.IsNullOrEmpty(ctx.Path) ? "/" : ctx.Path).StartsWith(prefix, StringComparison.OrdinalIgnoreCase) && predicate(ctx); | |
} | |
normalizedPath = VirtualPathUtility.AppendTrailingSlash(normalizedPath); | |
return ctx => (ctx.Path.Equals(normalizedPath, StringComparison.OrdinalIgnoreCase)) && predicate(ctx); | |
} | |
return predicate; | |
} | |
private Func<ShapePlacementContext, bool> CustomBuildPredicate(Func<ShapePlacementContext, bool> predicate, KeyValuePair<string, string> term) { | |
var expression = term.Value; | |
if (_parseMatchProviders != null) { | |
var providersForTerm = _parseMatchProviders.Where(x => x.Key.Equals(term.Key)); | |
if (providersForTerm.Any()) { | |
return ctx => providersForTerm.Any(x => x.IsMatch(ctx, expression)) && 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 DefaultExtensionTypes.IsTheme(fd.Extension.ExtensionType); | |
} | |
private bool FeatureIsEnabled(FeatureDescriptor fd) { | |
return _shellDescriptor.Features.Any(sf => sf.Name == fd.Id); | |
} | |
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.Id, null)).ToList(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment