From 7bc7ff03036f79bd8c62d71c0069e3a83892b576 Mon Sep 17 00:00:00 2001 From: Thierry Fleury Date: Mon, 24 Aug 2020 10:32:44 +0200 Subject: [PATCH] Add IShapePlacementProvider and ShapeTablePlacementProvider (#6780) --- .../DefaultContentDefinitionDisplayManager.cs | 7 +- .../Services/ActivityDisplayManager.cs | 12 ++- .../ContentDisplayManager.cs | 7 +- .../BaseDisplayManager.cs | 89 +++++++++++++------ .../Descriptors/Interfaces.cs | 28 ++++++ .../ShapeTablePlacementProvider.cs | 61 +++++++++++++ .../DisplayManager.cs | 6 +- .../Handlers/FindPlacementDelegate.cs | 5 +- .../OrchardCoreBuilderExtensions.cs | 2 + .../Views/ShapeResult.cs | 2 +- 10 files changed, 171 insertions(+), 48 deletions(-) create mode 100644 src/OrchardCore/OrchardCore.DisplayManagement/Descriptors/ShapeTablePlacementProvider.cs diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/DefaultContentDefinitionDisplayManager.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/DefaultContentDefinitionDisplayManager.cs index bad5c34cbae..e3e66bee6a7 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/DefaultContentDefinitionDisplayManager.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/DefaultContentDefinitionDisplayManager.cs @@ -9,7 +9,6 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Layout; using OrchardCore.DisplayManagement.ModelBinding; -using OrchardCore.DisplayManagement.Theming; using OrchardCore.Modules; namespace OrchardCore.ContentTypes.Editors @@ -20,7 +19,6 @@ public class DefaultContentDefinitionDisplayManager : BaseDisplayManager, IConte private readonly IShapeTableManager _shapeTableManager; private readonly IContentDefinitionManager _contentDefinitionManager; private readonly IShapeFactory _shapeFactory; - private readonly IThemeManager _themeManager; private readonly ILayoutAccessor _layoutAccessor; private readonly ILogger _logger; @@ -29,16 +27,15 @@ public DefaultContentDefinitionDisplayManager( IShapeTableManager shapeTableManager, IContentDefinitionManager contentDefinitionManager, IShapeFactory shapeFactory, - IThemeManager themeManager, + IEnumerable placementProviders, ILogger logger, ILayoutAccessor layoutAccessor - ) : base(shapeTableManager, shapeFactory, themeManager) + ) : base(shapeFactory, placementProviders) { _handlers = handlers; _shapeTableManager = shapeTableManager; _contentDefinitionManager = contentDefinitionManager; _shapeFactory = shapeFactory; - _themeManager = themeManager; _layoutAccessor = layoutAccessor; _logger = logger; } diff --git a/src/OrchardCore.Modules/OrchardCore.Workflows/Services/ActivityDisplayManager.cs b/src/OrchardCore.Modules/OrchardCore.Workflows/Services/ActivityDisplayManager.cs index 2c101b2d17a..374fff545d5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Workflows/Services/ActivityDisplayManager.cs +++ b/src/OrchardCore.Modules/OrchardCore.Workflows/Services/ActivityDisplayManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -8,7 +9,6 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Layout; using OrchardCore.DisplayManagement.ModelBinding; -using OrchardCore.DisplayManagement.Theming; using OrchardCore.Workflows.Activities; using OrchardCore.Workflows.Helpers; using OrchardCore.Workflows.Options; @@ -18,10 +18,16 @@ namespace OrchardCore.Workflows.Services public class ActivityDisplayManager : IActivityDisplayManager { private readonly DisplayManager _displayManager; - public ActivityDisplayManager(IOptions workflowOptions, IServiceProvider serviceProvider, IShapeTableManager shapeTableManager, IShapeFactory shapeFactory, IThemeManager themeManager, ILogger> displayManagerLogger, ILayoutAccessor layoutAccessor) + public ActivityDisplayManager( + IOptions workflowOptions, + IServiceProvider serviceProvider, + IShapeFactory shapeFactory, + IEnumerable placementProviders, + ILogger> displayManagerLogger, + ILayoutAccessor layoutAccessor) { var drivers = workflowOptions.Value.ActivityDisplayDriverTypes.Select(x => serviceProvider.CreateInstance>(x)); - _displayManager = new DisplayManager(drivers, shapeTableManager, shapeFactory, themeManager, displayManagerLogger, layoutAccessor); + _displayManager = new DisplayManager(drivers, shapeFactory, placementProviders, displayManagerLogger, layoutAccessor); } public Task BuildDisplayAsync(IActivity model, IUpdateModel updater, string displayType = "", string groupId = "") diff --git a/src/OrchardCore/OrchardCore.ContentManagement.Display/ContentDisplayManager.cs b/src/OrchardCore/OrchardCore.ContentManagement.Display/ContentDisplayManager.cs index 105fcb83a58..9b58b2af902 100644 --- a/src/OrchardCore/OrchardCore.ContentManagement.Display/ContentDisplayManager.cs +++ b/src/OrchardCore/OrchardCore.ContentManagement.Display/ContentDisplayManager.cs @@ -13,7 +13,6 @@ using OrchardCore.DisplayManagement.Layout; using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.DisplayManagement.Shapes; -using OrchardCore.DisplayManagement.Theming; using OrchardCore.Modules; namespace OrchardCore.ContentManagement.Display @@ -31,7 +30,6 @@ public class ContentItemDisplayManager : BaseDisplayManager, IContentItemDisplay private readonly IShapeTableManager _shapeTableManager; private readonly IContentDefinitionManager _contentDefinitionManager; private readonly IShapeFactory _shapeFactory; - private readonly IThemeManager _themeManager; private readonly ILayoutAccessor _layoutAccessor; private readonly ILogger _logger; @@ -41,17 +39,16 @@ public ContentItemDisplayManager( IShapeTableManager shapeTableManager, IContentDefinitionManager contentDefinitionManager, IShapeFactory shapeFactory, - IThemeManager themeManager, + IEnumerable placementProviders, ILogger logger, ILayoutAccessor layoutAccessor - ) : base(shapeTableManager, shapeFactory, themeManager) + ) : base(shapeFactory, placementProviders) { _handlers = handlers; _contentHandlers = contentHandlers; _shapeTableManager = shapeTableManager; _contentDefinitionManager = contentDefinitionManager; _shapeFactory = shapeFactory; - _themeManager = themeManager; _layoutAccessor = layoutAccessor; _logger = logger; } diff --git a/src/OrchardCore/OrchardCore.DisplayManagement/BaseDisplayManager.cs b/src/OrchardCore/OrchardCore.DisplayManagement/BaseDisplayManager.cs index ec2dd46a0b7..a88d287d73f 100644 --- a/src/OrchardCore/OrchardCore.DisplayManagement/BaseDisplayManager.cs +++ b/src/OrchardCore/OrchardCore.DisplayManagement/BaseDisplayManager.cs @@ -1,45 +1,46 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using OrchardCore.DisplayManagement.Descriptors; using OrchardCore.DisplayManagement.Handlers; -using OrchardCore.DisplayManagement.Theming; +using OrchardCore.DisplayManagement.Shapes; using OrchardCore.DisplayManagement.Zones; namespace OrchardCore.DisplayManagement { public abstract class BaseDisplayManager { - private readonly IShapeTableManager _shapeTableManager; private readonly IShapeFactory _shapeFactory; - private readonly IThemeManager _themeManager; + private readonly IEnumerable _placementProviders; public BaseDisplayManager( - IShapeTableManager shapeTableManager, IShapeFactory shapeFactory, - IThemeManager themeManager + IEnumerable placementProviders ) { - _shapeTableManager = shapeTableManager; _shapeFactory = shapeFactory; - _themeManager = themeManager; + _placementProviders = placementProviders; } protected async Task BindPlacementAsync(IBuildShapeContext context) { - var theme = await _themeManager.GetThemeAsync(); + var resolvers = new List(); - // If there is no active theme, do nothing - if (theme == null) + foreach (var provider in _placementProviders) { - return; - } + var resolver = await provider.BuildPlacementInfoResolverAsync(context); - var shapeTable = _shapeTableManager.GetShapeTable(theme.Id); + if (resolver != null) + { + resolvers.Add(resolver); + } + } - context.FindPlacement = (shapeType, differentiator, displayType, displayContext) => FindPlacementImpl(shapeTable, shapeType, differentiator, displayType, context); + context.FindPlacement = (shapeType, differentiator, displayType, displayContext) => FindPlacementImpl(resolvers, shapeType, differentiator, displayType, context); } - private static PlacementInfo FindPlacementImpl(ShapeTable shapeTable, string shapeType, string differentiator, string displayType, IBuildShapeContext context) + private static PlacementInfo FindPlacementImpl(IList placementResolvers, string shapeType, string differentiator, string displayType, IBuildShapeContext context) { var delimiterIndex = shapeType.IndexOf("__", StringComparison.Ordinal); @@ -48,24 +49,56 @@ private static PlacementInfo FindPlacementImpl(ShapeTable shapeTable, string sha shapeType = shapeType.Substring(0, delimiterIndex); } - if (shapeTable.Descriptors.TryGetValue(shapeType, out var descriptor)) - { - var placementContext = new ShapePlacementContext( - shapeType, - displayType, - differentiator, - context.Shape - ); + var placementContext = new ShapePlacementContext( + shapeType, + displayType, + differentiator, + context.Shape + ); - var placement = descriptor.Placement(placementContext); - if (placement != null) + return placementResolvers.Aggregate(null, (prev, resolver) => + CombinePlacements(prev, resolver.ResolvePlacement(placementContext)) + ); + } + + private static PlacementInfo CombinePlacements(PlacementInfo first, PlacementInfo second) + { + if (first == null) + { + return second; + } + else if (second != null) + { + CombineAlternates(first.Alternates, second.Alternates); + CombineAlternates(first.Wrappers, second.Wrappers); + if (!String.IsNullOrEmpty(second.ShapeType)) { - placement.Source = placementContext.Source; - return placement; + first.ShapeType = second.ShapeType; } + if (!String.IsNullOrEmpty(second.Location)) + { + first.Location = second.Location; + } + if (!String.IsNullOrEmpty(second.DefaultPosition)) + { + first.DefaultPosition = second.DefaultPosition; + } + first.Source += "," + second.Source; } + return first; + } - return null; + private static AlternatesCollection CombineAlternates(AlternatesCollection first, AlternatesCollection second) + { + if (first == null) + { + return second; + } + else if (second != null) + { + first.AddRange(second); + } + return first; } protected ValueTask CreateContentShapeAsync(string actualShapeType) diff --git a/src/OrchardCore/OrchardCore.DisplayManagement/Descriptors/Interfaces.cs b/src/OrchardCore/OrchardCore.DisplayManagement/Descriptors/Interfaces.cs index babfcc5e88c..49263ef3ae3 100644 --- a/src/OrchardCore/OrchardCore.DisplayManagement/Descriptors/Interfaces.cs +++ b/src/OrchardCore/OrchardCore.DisplayManagement/Descriptors/Interfaces.cs @@ -1,4 +1,6 @@ +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using OrchardCore.DisplayManagement.Handlers; namespace OrchardCore.DisplayManagement.Descriptors { @@ -33,4 +35,30 @@ public static IServiceCollection AddShapeAttributes(this IServiceCollection s return services; } } + + /// + /// Represents a marker interface for classes that provide Shape placement informations + /// + public interface IShapePlacementProvider + { + /// + /// Builds a contextualized + /// + /// The for which we need a placement resolver + /// An instance of for the current context or if this provider is not concerned. + Task BuildPlacementInfoResolverAsync(IBuildShapeContext context); + } + + /// + /// Represents a class capable of resolving of Shapes + /// + public interface IPlacementInfoResolver + { + /// + /// Resolves for the provided + /// + /// The + /// An or if not concerned. + PlacementInfo ResolvePlacement(ShapePlacementContext placementContext); + } } diff --git a/src/OrchardCore/OrchardCore.DisplayManagement/Descriptors/ShapeTablePlacementProvider.cs b/src/OrchardCore/OrchardCore.DisplayManagement/Descriptors/ShapeTablePlacementProvider.cs new file mode 100644 index 00000000000..fd2dab2ddea --- /dev/null +++ b/src/OrchardCore/OrchardCore.DisplayManagement/Descriptors/ShapeTablePlacementProvider.cs @@ -0,0 +1,61 @@ +using System.Threading.Tasks; +using OrchardCore.DisplayManagement.Handlers; +using OrchardCore.DisplayManagement.Theming; + +namespace OrchardCore.DisplayManagement.Descriptors +{ + public class ShapeTablePlacementProvider : IShapePlacementProvider + { + private readonly IShapeTableManager _shapeTableManager; + private readonly IThemeManager _themeManager; + + public ShapeTablePlacementProvider( + IShapeTableManager shapeTableManager, + IThemeManager themeManager + ) + { + _shapeTableManager = shapeTableManager; + _themeManager = themeManager; + } + + public async Task BuildPlacementInfoResolverAsync(IBuildShapeContext context) + { + var theme = await _themeManager.GetThemeAsync(); + + // If there is no active theme, do nothing + if (theme == null) + { + return null; + } + + var shapeTable = _shapeTableManager.GetShapeTable(theme.Id); + + return new ShapeTablePlacementResolver(shapeTable); + } + + private class ShapeTablePlacementResolver : IPlacementInfoResolver + { + private readonly ShapeTable _shapeTable; + + internal ShapeTablePlacementResolver(ShapeTable shapeTable) + { + _shapeTable = shapeTable; + } + + public PlacementInfo ResolvePlacement(ShapePlacementContext placementContext) + { + if (_shapeTable.Descriptors.TryGetValue(placementContext.ShapeType, out var descriptor)) + { + var placement = descriptor.Placement(placementContext); + if (placement != null) + { + placement.Source = placementContext.Source; + return placement; + } + } + + return null; + } + } + } +} diff --git a/src/OrchardCore/OrchardCore.DisplayManagement/DisplayManager.cs b/src/OrchardCore/OrchardCore.DisplayManagement/DisplayManager.cs index f044a9ba882..a65c3802c7d 100644 --- a/src/OrchardCore/OrchardCore.DisplayManagement/DisplayManager.cs +++ b/src/OrchardCore/OrchardCore.DisplayManagement/DisplayManager.cs @@ -5,7 +5,6 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Layout; using OrchardCore.DisplayManagement.ModelBinding; -using OrchardCore.DisplayManagement.Theming; using OrchardCore.Modules; namespace OrchardCore.DisplayManagement @@ -19,12 +18,11 @@ public class DisplayManager : BaseDisplayManager, IDisplayManager> drivers, - IShapeTableManager shapeTableManager, IShapeFactory shapeFactory, - IThemeManager themeManager, + IEnumerable placementProviders, ILogger> logger, ILayoutAccessor layoutAccessor - ) : base(shapeTableManager, shapeFactory, themeManager) + ) : base(shapeFactory, placementProviders) { _shapeFactory = shapeFactory; _layoutAccessor = layoutAccessor; diff --git a/src/OrchardCore/OrchardCore.DisplayManagement/Handlers/FindPlacementDelegate.cs b/src/OrchardCore/OrchardCore.DisplayManagement/Handlers/FindPlacementDelegate.cs index debcbe99f7a..bcd12fc71ce 100644 --- a/src/OrchardCore/OrchardCore.DisplayManagement/Handlers/FindPlacementDelegate.cs +++ b/src/OrchardCore/OrchardCore.DisplayManagement/Handlers/FindPlacementDelegate.cs @@ -1,17 +1,18 @@ -using OrchardCore.DisplayManagement.Descriptors; +using OrchardCore.DisplayManagement.Descriptors; namespace OrchardCore.DisplayManagement.Handlers { /// /// A function that provides a instance for a shape. /// - /// The shape to render. + /// The shape type to render. /// /// A discriminator that differentiates this specific shape to the others of the same type. /// For instance multiple field shape smight be displayed in different locations even though /// they are of the same type. /// /// The display type the content item owning the shape is rendered with. + /// The in which the shape is placed. /// The to use or if this function is not concerned. public delegate PlacementInfo FindPlacementDelegate(string shapeType, string differentiator, string displayType, IBuildShapeContext context); } diff --git a/src/OrchardCore/OrchardCore.DisplayManagement/OrchardCoreBuilderExtensions.cs b/src/OrchardCore/OrchardCore.DisplayManagement/OrchardCoreBuilderExtensions.cs index b0cbe962e87..d04f7cf447d 100644 --- a/src/OrchardCore/OrchardCore.DisplayManagement/OrchardCoreBuilderExtensions.cs +++ b/src/OrchardCore/OrchardCore.DisplayManagement/OrchardCoreBuilderExtensions.cs @@ -63,6 +63,8 @@ public static OrchardCoreBuilder AddTheming(this OrchardCoreBuilder builder) services.AddScoped(); + services.AddScoped(); + services.TryAddEnumerable( ServiceDescriptor.Transient, ShapeTemplateOptionsSetup>()); services.TryAddSingleton(); diff --git a/src/OrchardCore/OrchardCore.DisplayManagement/Views/ShapeResult.cs b/src/OrchardCore/OrchardCore.DisplayManagement/Views/ShapeResult.cs index 23ed24876f7..017aef2521e 100644 --- a/src/OrchardCore/OrchardCore.DisplayManagement/Views/ShapeResult.cs +++ b/src/OrchardCore/OrchardCore.DisplayManagement/Views/ShapeResult.cs @@ -58,7 +58,7 @@ private async Task ApplyImplementationAsync(BuildShapeContext context, string di _defaultLocation = context.DefaultZone; } - // Look into specific implementations of placements (like placement.json files) + // Look into specific implementations of placements (like placement.json files and IShapePlacementProviders) var placement = context.FindPlacement(_shapeType, _differentiator, displayType, context); // Look for mapped display type locations