From 9466d914382e06c47fe10fdc9a5fa93bef0adf92 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 12 Jun 2015 13:28:53 +0300 Subject: [PATCH] #5379: Improved LayoutPartDriver to safely handle recursive layout rendering. This prevents a SO exception when you for example have a Projection element that renders content including the content item rendering that Projection element. --- .../Drivers/LayoutPartDriver.cs | 30 +++++++++++++++++-- .../Orchard.Layouts/Orchard.Layouts.csproj | 4 +++ .../Views/Parts.Layout.Recursive.cshtml | 1 + .../Parts.Layout.Summary.Recursive.cshtml | 1 + 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 src/Orchard.Web/Modules/Orchard.Layouts/Views/Parts.Layout.Recursive.cshtml create mode 100644 src/Orchard.Web/Modules/Orchard.Layouts/Views/Parts.Layout.Summary.Recursive.cshtml diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/LayoutPartDriver.cs b/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/LayoutPartDriver.cs index d229f2f04..d58f4c13c 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/LayoutPartDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Drivers/LayoutPartDriver.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; @@ -11,6 +12,7 @@ using Orchard.Layouts.Helpers; using Orchard.Layouts.Models; using Orchard.Layouts.Services; using Orchard.Layouts.ViewModels; +using Orchard.Logging; namespace Orchard.Layouts.Drivers { public class LayoutPartDriver : ContentPartDriver { @@ -22,6 +24,7 @@ namespace Orchard.Layouts.Drivers { private readonly IShapeDisplay _shapeDisplay; private readonly ILayoutModelMapper _mapper; private readonly ILayoutEditorFactory _layoutEditorFactory; + private readonly HashSet _stack; public LayoutPartDriver( ILayoutSerializer serializer, @@ -30,7 +33,7 @@ namespace Orchard.Layouts.Drivers { ILayoutManager layoutManager, Lazy contentPartDisplay, IShapeDisplay shapeDisplay, - ILayoutModelMapper mapper, + ILayoutModelMapper mapper, ILayoutEditorFactory layoutEditorFactory) { _serializer = serializer; @@ -41,22 +44,45 @@ namespace Orchard.Layouts.Drivers { _shapeDisplay = shapeDisplay; _mapper = mapper; _layoutEditorFactory = layoutEditorFactory; + _stack = new HashSet(); + + Logger = NullLogger.Instance; } + public ILogger Logger { get; set; } + protected override DriverResult Display(LayoutPart part, string displayType, dynamic shapeHelper) { return Combined( ContentShape("Parts_Layout", () => { + if (DetectRecursion(part, "Parts_Layout")) + return shapeHelper.Parts_Layout_Recursive(); + var elements = _layoutManager.LoadElements(part); var layoutRoot = _elementDisplay.DisplayElements(elements, part, displayType: displayType); return shapeHelper.Parts_Layout(LayoutRoot: layoutRoot); }), ContentShape("Parts_Layout_Summary", () => { + if (DetectRecursion(part, "Parts_Layout_Summary")) + return shapeHelper.Parts_Layout_Summary_Recursive(); + var layoutShape = _contentPartDisplay.Value.BuildDisplay(part); var layoutHtml = _shapeDisplay.Display(layoutShape); return shapeHelper.Parts_Layout_Summary(LayoutHtml: layoutHtml); })); } + private bool DetectRecursion(LayoutPart part, string shapeName) { + var key = String.Format("{0}:{1}", shapeName, part.Id); + + if (_stack.Contains(key)) { + Logger.Debug(String.Format("Detected recursive layout rendering of layout with ID = {0} and shape = {1}", part.Id, shapeName)); + return true; + } + + _stack.Add(key); + return false; + } + protected override DriverResult Editor(LayoutPart part, dynamic shapeHelper) { return Editor(part, null, shapeHelper); } @@ -85,7 +111,7 @@ namespace Orchard.Layouts.Drivers { part.LayoutData = _serializer.Serialize(elementInstances); part.TemplateId = viewModel.LayoutEditor.TemplateId; part.SessionKey = viewModel.LayoutEditor.SessionKey; - viewModel.LayoutEditor.Data = _mapper.ToEditorModel(part.LayoutData, new DescribeElementsContext {Content = part}).ToJson(); + viewModel.LayoutEditor.Data = _mapper.ToEditorModel(part.LayoutData, new DescribeElementsContext { Content = part }).ToJson(); } return shapeHelper.EditorTemplate(TemplateName: "Parts.Layout", Model: viewModel, Prefix: Prefix); diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Orchard.Layouts.csproj b/src/Orchard.Web/Modules/Orchard.Layouts/Orchard.Layouts.csproj index a3debb909..e3d62841f 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Orchard.Layouts.csproj +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Orchard.Layouts.csproj @@ -535,6 +535,10 @@ + + + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Views/Parts.Layout.Recursive.cshtml b/src/Orchard.Web/Modules/Orchard.Layouts/Views/Parts.Layout.Recursive.cshtml new file mode 100644 index 000000000..4760bf5ca --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Views/Parts.Layout.Recursive.cshtml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Views/Parts.Layout.Summary.Recursive.cshtml b/src/Orchard.Web/Modules/Orchard.Layouts/Views/Parts.Layout.Summary.Recursive.cshtml new file mode 100644 index 000000000..4760bf5ca --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Views/Parts.Layout.Summary.Recursive.cshtml @@ -0,0 +1 @@ + \ No newline at end of file