From c228c24ba51a5cb01da90844eb3bc4699db4eeb6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 30 Aug 2013 12:03:16 +1000 Subject: [PATCH] Creted MergeParentContextViewData filter, this is by default applied to SurfaceController's and the PartialViewMacroController. Now if you are rendering a partial view macro or a child action from a SurfaceController and are putting data into ViewData on a POST, you don't have to access the ViewData from the ParentViewContext. --- src/Umbraco.Core/DictionaryExtensions.cs | 64 ++++++++++++------- .../Mvc/ViewDataDictionaryExtensionTests.cs | 44 +++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../Macros/PartialViewMacroController.cs | 2 + .../MergeParentContextViewDataAttribute.cs | 36 +++++++++++ src/Umbraco.Web/Mvc/SurfaceController.cs | 1 + .../Mvc/ViewDataContainerExtensions.cs | 2 +- .../Mvc/ViewDataDictionaryExtensions.cs | 18 ++++++ src/Umbraco.Web/Umbraco.Web.csproj | 2 + 9 files changed, 146 insertions(+), 24 deletions(-) create mode 100644 src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs create mode 100644 src/Umbraco.Web/Mvc/MergeParentContextViewDataAttribute.cs create mode 100644 src/Umbraco.Web/Mvc/ViewDataDictionaryExtensions.cs diff --git a/src/Umbraco.Core/DictionaryExtensions.cs b/src/Umbraco.Core/DictionaryExtensions.cs index de12e0b3b1..194b461fd5 100644 --- a/src/Umbraco.Core/DictionaryExtensions.cs +++ b/src/Umbraco.Core/DictionaryExtensions.cs @@ -131,30 +131,48 @@ namespace Umbraco.Core return n; } - /// - /// Returns a new dictionary of this ... others merged leftward. - /// - /// - /// - /// - /// - /// - /// - /// - /// Reference: http://stackoverflow.com/questions/294138/merging-dictionaries-in-c - /// - public static T MergeLeft(this T me, params IDictionary[] others) - where T : IDictionary, new() - { - var newMap = new T(); - foreach (var p in (new List> { me }).Concat(others).SelectMany(src => src)) - { - newMap[p.Key] = p.Value; - } - return newMap; - } + /// + /// Merges all key/values from the sources dictionaries into the destination dictionary + /// + /// + /// + /// + /// The source dictionary to merge other dictionaries into + /// + /// By default all values will be retained in the destination if the same keys exist in the sources but + /// this can changed if overwrite = true, then any key/value found in any of the sources will overwritten in the destination. Note that + /// it will just use the last found key/value if this is true. + /// + /// The other dictionaries to merge values from + public static void MergeLeft(this T destination, IEnumerable> sources, bool overwrite = false) + where T : IDictionary + { + foreach (var p in sources.SelectMany(src => src).Where(p => overwrite || destination.ContainsKey(p.Key) == false)) + { + destination[p.Key] = p.Value; + } + } - /// + /// + /// Merges all key/values from the sources dictionaries into the destination dictionary + /// + /// + /// + /// + /// The source dictionary to merge other dictionaries into + /// + /// By default all values will be retained in the destination if the same keys exist in the sources but + /// this can changed if overwrite = true, then any key/value found in any of the sources will overwritten in the destination. Note that + /// it will just use the last found key/value if this is true. + /// + /// The other dictionary to merge values from + public static void MergeLeft(this T destination, IDictionary source, bool overwrite = false) + where T : IDictionary + { + destination.MergeLeft(new[] {source}, overwrite); + } + + /// /// Returns the value of the key value based on the key, if the key is not found, a null value is returned /// /// The type of the key. diff --git a/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs b/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs new file mode 100644 index 0000000000..2f35aede2d --- /dev/null +++ b/src/Umbraco.Tests/Mvc/ViewDataDictionaryExtensionTests.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web.Mvc; +using NUnit.Framework; +using Umbraco.Web.Mvc; + +namespace Umbraco.Tests.Mvc +{ + [TestFixture] + public class ViewDataDictionaryExtensionTests + { + [Test] + public void Merge_View_Data() + { + var source = new ViewDataDictionary(); + var dest = new ViewDataDictionary(); + source.Add("Test1", "Test1"); + dest.Add("Test2", "Test2"); + + dest.MergeViewDataFrom(source); + + Assert.AreEqual(2, dest.Count); + } + + [Test] + public void Merge_View_Data_Retains_Destination_Values() + { + var source = new ViewDataDictionary(); + var dest = new ViewDataDictionary(); + source.Add("Test1", "Test1"); + dest.Add("Test1", "MyValue"); + dest.Add("Test2", "Test2"); + + dest.MergeViewDataFrom(source); + + Assert.AreEqual(2, dest.Count); + Assert.AreEqual("MyValue", dest["Test1"]); + Assert.AreEqual("Test2", dest["Test2"]); + } + + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 0d2d690a46..d8fae9ebac 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -187,6 +187,7 @@ + diff --git a/src/Umbraco.Web/Macros/PartialViewMacroController.cs b/src/Umbraco.Web/Macros/PartialViewMacroController.cs index 765846c5c5..b858d1c283 100644 --- a/src/Umbraco.Web/Macros/PartialViewMacroController.cs +++ b/src/Umbraco.Web/Macros/PartialViewMacroController.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Web.Mvc; using Umbraco.Web.Models; +using Umbraco.Web.Mvc; using umbraco.cms.businesslogic.macro; using umbraco.interfaces; using System.Linq; @@ -10,6 +11,7 @@ namespace Umbraco.Web.Macros /// /// Controller to render macro content for Parital View Macros /// + [MergeParentContextViewData] internal class PartialViewMacroController : Controller { private readonly UmbracoContext _umbracoContext; diff --git a/src/Umbraco.Web/Mvc/MergeParentContextViewDataAttribute.cs b/src/Umbraco.Web/Mvc/MergeParentContextViewDataAttribute.cs new file mode 100644 index 0000000000..219447d353 --- /dev/null +++ b/src/Umbraco.Web/Mvc/MergeParentContextViewDataAttribute.cs @@ -0,0 +1,36 @@ +using System.Web.Mvc; + +namespace Umbraco.Web.Mvc +{ + /// + /// This attribute can be used for when child actions execute and will automatically merge in the viewdata from the parent context to the + /// child action result. + /// + /// + /// This will retain any custom viewdata put into the child viewdata if the same key persists in the parent context's view data. You can always still + /// access the parent's view data normally. + /// This just simplifies working with ChildActions and view data. + /// + /// NOTE: This does not mean that the parent context's view data will be merged before the action executes, if you need access to the parent context's view + /// data during controller execution you can access it normally. + /// + public class MergeParentContextViewDataAttribute : ActionFilterAttribute + { + /// + /// Merge in the parent context's view data if this is a child action when the result is being executed + /// + /// + public override void OnResultExecuting(ResultExecutingContext filterContext) + { + if (filterContext.IsChildAction) + { + if (filterContext.ParentActionViewContext != null && filterContext.ParentActionViewContext.ViewData != null) + { + filterContext.Controller.ViewData.MergeViewDataFrom(filterContext.ParentActionViewContext.ViewData); + } + } + + base.OnResultExecuting(filterContext); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Mvc/SurfaceController.cs b/src/Umbraco.Web/Mvc/SurfaceController.cs index 37cb38751a..23caa16619 100644 --- a/src/Umbraco.Web/Mvc/SurfaceController.cs +++ b/src/Umbraco.Web/Mvc/SurfaceController.cs @@ -12,6 +12,7 @@ namespace Umbraco.Web.Mvc /// The base controller that all Presentation Add-in controllers should inherit from /// [MergeModelStateToChildAction] + [MergeParentContextViewData] public abstract class SurfaceController : PluginController { diff --git a/src/Umbraco.Web/Mvc/ViewDataContainerExtensions.cs b/src/Umbraco.Web/Mvc/ViewDataContainerExtensions.cs index 5e9ad89b9d..ef611e96c4 100644 --- a/src/Umbraco.Web/Mvc/ViewDataContainerExtensions.cs +++ b/src/Umbraco.Web/Mvc/ViewDataContainerExtensions.cs @@ -2,7 +2,7 @@ using System.Web.Mvc; namespace Umbraco.Web.Mvc { - internal static class ViewDataContainerExtensions + internal static class ViewDataContainerExtensions { private class ViewDataContainer : IViewDataContainer { diff --git a/src/Umbraco.Web/Mvc/ViewDataDictionaryExtensions.cs b/src/Umbraco.Web/Mvc/ViewDataDictionaryExtensions.cs new file mode 100644 index 0000000000..930163bfe4 --- /dev/null +++ b/src/Umbraco.Web/Mvc/ViewDataDictionaryExtensions.cs @@ -0,0 +1,18 @@ +using System.Web.Mvc; +using Umbraco.Core; + +namespace Umbraco.Web.Mvc +{ + internal static class ViewDataDictionaryExtensions + { + /// + /// Merges the source view data into the destination view data + /// + /// + /// + public static void MergeViewDataFrom(this ViewDataDictionary destination, ViewDataDictionary source) + { + destination.MergeLeft(source); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 249849013f..cddb3dc51f 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -299,6 +299,8 @@ + +