Last active
August 30, 2017 15:49
-
-
Save carlwoodhouse/5b636a85fa8f240034f27073a5cafbb3 to your computer and use it in GitHub Desktop.
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 Orchard.ContentManagement; | |
using Orchard.DisplayManagement.Descriptors; | |
using Patient.Framework.DisplayManagement; | |
namespace Patient.ContentTypes.Clinical.Providers { | |
public class HubPlacementParseStrategyMatchProvider : IPlacementParseStategyMatchProvider { | |
public string Key { get { return "Hub"; } } | |
public bool IsMatch(ShapePlacementContext context, string expression) { | |
var contentItem = context.Content.ContentItem; | |
return contentItem.Has<IClincialArticlePart>() && contentItem.As<IClincialArticlePart>().IsTopicHub; | |
} | |
} | |
} |
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 Orchard; | |
using Orchard.DisplayManagement.Descriptors; | |
namespace Patient.Framework.DisplayManagement { | |
public interface IPlacementParseStategyMatchProvider : IDependency { | |
string Key { get; } | |
bool IsMatch(ShapePlacementContext context, string expression); | |
} | |
} |
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
<Placement> | |
<Place Parts_PageToolsShare="-" /> | |
<Place Parts_Common_Metadata="-" /> | |
<Match DisplayType="Detail"> | |
<Place Parts_PageTools="/ContentMenu:1" /> | |
<Place Parts_PageTools_Readspeaker="/BodyFoot:1" /> | |
<Place Parts_Tabs="-" /> | |
<Place Parts_Title="/ContentHead:0" /> | |
<Place Parts_ClinicalArticle_MetaData="/ContentHead:1" /> | |
<Place Parts_PageTools_PageOptions="-" /> | |
<Place Parts_ClinicalArticle_Introduction="Content:0" /> | |
<Place Parts_ClinicalArticle_SectionNavigation="Content:1" /> | |
<Place Parts_ClinicalArticle_InlineNavigation="Content:1" /> | |
<Place Parts_ClinicalArticle="Content:2" /> | |
<Place Parts_ClinicalArticle_SectionNavigation_Footer="Content:3" /> | |
<Place Parts_ClinicalArticle_References="Content:4" /> | |
<Place Parts_ClinicalArticle_RelatedContent="/ContentFoot:1" /> | |
<Place Parts_ClinicalArticle_Footer="/ContentFoot:2" /> | |
<Place Parts_ClinicalArticle_Related="-" /> | |
<Place Parts_ClinicalArticle_Discussions="-" /> | |
<Place Parts_ClinicalArticle_RelatedArticle_PilNudge="-" /> | |
<Place Parts_ClinicalArticle_RelatedArticle_ProNudge="-" /> | |
<Place Parts_FeedbackForm="-" /> | |
<Place Parts_Taboola="/AfterContentAds:1" /> | |
<Place Parts_ClickBait_Medianet="-" /> | |
<Match Hub="true"> | |
<Place Parts_Title="/ContentHead:0;Alternate=Parts_Title__Hub" /> | |
<Place Parts_ClinicalArticle_MetaData="-" /> | |
<Place Parts_ClinicalArticle_Introduction="Content:1.5" /> | |
<Place Parts_ClinicalArticle_InlineNavigation="-" /> | |
<Place Parts_ClinicalArticle="Content:2" /> | |
<Place Parts_ClinicalArticle_SectionNavigation_Footer="-" /> | |
<Place Parts_ClinicalArticle_References="-" /> | |
<Place Parts_ClinicalArticle_RelatedContent="-" /> | |
<Place Parts_ClinicalArticle_Footer="-" /> | |
</Match> | |
</Match> | |
</Placement> |
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.DisplayManagement.Descriptors; | |
using Orchard.DisplayManagement.Descriptors.ShapePlacementStrategy; | |
using Orchard.Environment.Descriptor.Models; | |
using Orchard.Environment.Extensions; | |
using Orchard.Environment.Extensions.Models; | |
using Patient.Framework.DisplayManagement; | |
namespace Patient.Framework.DisplayManangement { | |
[OrchardSuppressDependency("Orchard.DisplayManagement.Descriptors.ShapePlacementStrategy.ShapePlacementParsingStrategy")] | |
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> predicate = ctx => true; | |
if (differentiator != "") { | |
predicate = ctx => (ctx.Differentiator ?? "") == differentiator; | |
} | |
if (matches.Any()) { | |
predicate = matches.SelectMany(match => match.Terms).Aggregate(predicate, BuildPredicate); | |
} | |
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 = predicate(ctx); | |
// 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 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); | |
} | |
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