From 98e487753f0f359782b4e541bc95f288a26b83ea Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 26 Sep 2013 14:52:48 +0200 Subject: [PATCH 1/7] Pointing to the Belle docs for building from source --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 61a16083d0..53f55f8fe1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ Umbraco CMS =========== +##Note: Building version 7 from source## +If you're interested in using the source code and building version 7, make sure to read the [Belle ReadMe file](src/Umbraco.Web.UI.Client/README.md). Note that you can always [download a nightly build](http://nightly.umbraco.org/umbraco%207.0.0/) so you don't have to build the code yourself. + ## Watch a five minute introduction video ## [![ScreenShot](http://umbraco.com/images/whatisumbraco.png)](http://umbraco.org/help-and-support/video-tutorials/getting-started/what-is-umbraco) From 2e9bc3bb2b9e874dafef9561771e42cd43150ac8 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 26 Sep 2013 15:43:08 +0200 Subject: [PATCH 2/7] Oops updated wrong branch --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 53f55f8fe1..61a16083d0 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,5 @@ Umbraco CMS =========== -##Note: Building version 7 from source## -If you're interested in using the source code and building version 7, make sure to read the [Belle ReadMe file](src/Umbraco.Web.UI.Client/README.md). Note that you can always [download a nightly build](http://nightly.umbraco.org/umbraco%207.0.0/) so you don't have to build the code yourself. - ## Watch a five minute introduction video ## [![ScreenShot](http://umbraco.com/images/whatisumbraco.png)](http://umbraco.org/help-and-support/video-tutorials/getting-started/what-is-umbraco) From b5022bb1831433024758198647f7f8a96c89235c Mon Sep 17 00:00:00 2001 From: Marc Brooks Date: Thu, 26 Sep 2013 13:41:51 -0500 Subject: [PATCH 3/7] Fix order of null-check versus dotting through to .UmbracoContext. The order of operations in the constructor dots through to the _routingContext.UmbracoContext before checking if the value was null and throwing the argument exception (which really isn't an ArgumentException...) --- src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs index 6fc59eb0ed..e7126d2846 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs @@ -28,11 +28,13 @@ namespace Umbraco.Web.Routing /// The content request. public PublishedContentRequestEngine(PublishedContentRequest pcr) { + if (pcr == null) throw new ArgumentException("pcr is null."); _pcr = pcr; + _routingContext = pcr.RoutingContext; - - var umbracoContext = _routingContext.UmbracoContext; if (_routingContext == null) throw new ArgumentException("pcr.RoutingContext is null."); + + var umbracoContext = _routingContext.UmbracoContext; if (umbracoContext == null) throw new ArgumentException("pcr.RoutingContext.UmbracoContext is null."); if (umbracoContext.RoutingContext != _routingContext) throw new ArgumentException("RoutingContext confusion."); // no! not set yet. From 957760d29a7cbab4b9dda073e4fc7fb1efd3e3d3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 30 Sep 2013 12:02:35 +1000 Subject: [PATCH 4/7] Creates IDisposeOnRequestEnd and ensures UmbracoContext and UmbracoDatabase implement it, then we only dispose of these types of objects at the end of the request if they are part of the httpcontext items (U4-2738). Fixes: U4-2988 UmbracoObjectTypesExtensions is not thread safe --- src/Umbraco.Core/IDisposeOnRequestEnd.cs | 14 ++++++++ .../Models/UmbracoObjectTypesExtensions.cs | 32 +++++++++---------- .../Persistence/UmbracoDatabase.cs | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + src/Umbraco.Web/UmbracoContext.cs | 2 +- src/Umbraco.Web/UmbracoModule.cs | 6 +++- 6 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 src/Umbraco.Core/IDisposeOnRequestEnd.cs diff --git a/src/Umbraco.Core/IDisposeOnRequestEnd.cs b/src/Umbraco.Core/IDisposeOnRequestEnd.cs new file mode 100644 index 0000000000..cf1ec3a177 --- /dev/null +++ b/src/Umbraco.Core/IDisposeOnRequestEnd.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Umbraco.Core +{ + /// + /// Any class implementing this interface that is added to the httpcontext.items keys or values will be disposed of at the end of the request. + /// + public interface IDisposeOnRequestEnd : IDisposable + { + } +} diff --git a/src/Umbraco.Core/Models/UmbracoObjectTypesExtensions.cs b/src/Umbraco.Core/Models/UmbracoObjectTypesExtensions.cs index a97f1fe976..7cc130600b 100644 --- a/src/Umbraco.Core/Models/UmbracoObjectTypesExtensions.cs +++ b/src/Umbraco.Core/Models/UmbracoObjectTypesExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using Umbraco.Core.CodeAnnotations; @@ -9,7 +10,8 @@ namespace Umbraco.Core.Models /// public static class UmbracoObjectTypesExtensions { - private static readonly Dictionary UmbracoObjectTypeCache = new Dictionary(); + //MUST be concurrent to avoid thread collisions! + private static readonly ConcurrentDictionary UmbracoObjectTypeCache = new ConcurrentDictionary(); /// /// Get an UmbracoObjectTypes value from it's name @@ -48,24 +50,22 @@ namespace Umbraco.Core.Models /// a GUID value of the UmbracoObjectTypes public static Guid GetGuid(this UmbracoObjectTypes umbracoObjectType) { - if (UmbracoObjectTypeCache.ContainsKey(umbracoObjectType)) - return UmbracoObjectTypeCache[umbracoObjectType]; + return UmbracoObjectTypeCache.GetOrAdd(umbracoObjectType, types => + { + var type = typeof(UmbracoObjectTypes); + var memInfo = type.GetMember(umbracoObjectType.ToString()); + var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute), + false); - var type = typeof(UmbracoObjectTypes); - var memInfo = type.GetMember(umbracoObjectType.ToString()); - var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute), - false); + if (attributes.Length == 0) + return Guid.Empty; - if (attributes.Length == 0) - return Guid.Empty; + var attribute = ((UmbracoObjectTypeAttribute)attributes[0]); + if (attribute == null) + return Guid.Empty; - var attribute = ((UmbracoObjectTypeAttribute)attributes[0]); - if (attribute == null) - return Guid.Empty; - - UmbracoObjectTypeCache.Add(umbracoObjectType, attribute.ObjectId); - - return attribute.ObjectId; + return attribute.ObjectId; + }); } /// diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs index 522e0b4173..98d5d9cebc 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs @@ -16,7 +16,7 @@ namespace Umbraco.Core.Persistence /// can then override any additional execution (such as additional loggging, functionality, etc...) that we need to without breaking compatibility since we'll always be exposing /// this object instead of the base PetaPoco database object. /// - public class UmbracoDatabase : Database + public class UmbracoDatabase : Database, IDisposeOnRequestEnd { private readonly Guid _instanceId = Guid.NewGuid(); /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 2cc6a8fbd4..dc95d29ab4 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -166,6 +166,7 @@ + diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 17751b4077..2458dc62e0 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web /// /// Class that encapsulates Umbraco information of a specific HTTP request /// - public class UmbracoContext : DisposableObject + public class UmbracoContext : DisposableObject, IDisposeOnRequestEnd { private const string HttpContextItemName = "Umbraco.Web.UmbracoContext"; private static readonly object Locker = new object(); diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 79f029c4a7..5ba568c56e 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -467,11 +467,15 @@ namespace Umbraco.Web /// private static void DisposeHttpContextItems(HttpContext http) { + // do not process if client-side request + if (http.Request.Url.IsClientSideRequest()) + return; + //get a list of keys to dispose var keys = new HashSet(); foreach (DictionaryEntry i in http.Items) { - if (i.Value is IDisposable || i.Key is IDisposable) + if (i.Value is IDisposeOnRequestEnd || i.Key is IDisposeOnRequestEnd) { keys.Add(i.Key); } From a8f6b9eeb9ceebf6c2c85cbee4f526626beda8f6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 30 Sep 2013 13:31:48 +1000 Subject: [PATCH 5/7] Fixes: U4-2925 Tree performance is very poor in 6.1.x when having event subscriptions --- src/Umbraco.Core/TypeHelper.cs | 10 ++ .../Trees/BaseContentTreeTests.cs | 156 +++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../umbraco/Trees/BaseContentTree.cs | 129 +++++++++----- .../umbraco/Trees/BaseMediaTree.cs | 163 ++++++++++++------ .../umbraco/Trees/BaseTree.cs | 10 ++ 6 files changed, 367 insertions(+), 102 deletions(-) create mode 100644 src/Umbraco.Tests/Trees/BaseContentTreeTests.cs diff --git a/src/Umbraco.Core/TypeHelper.cs b/src/Umbraco.Core/TypeHelper.cs index e77f03ee78..adda1d3298 100644 --- a/src/Umbraco.Core/TypeHelper.cs +++ b/src/Umbraco.Core/TypeHelper.cs @@ -16,6 +16,16 @@ namespace Umbraco.Core private static readonly ConcurrentDictionary GetFieldsCache = new ConcurrentDictionary(); private static readonly ConcurrentDictionary, PropertyInfo[]> GetPropertiesCache = new ConcurrentDictionary, PropertyInfo[]>(); + /// + /// Checks if the method is actually overriding a base method + /// + /// + /// + public static bool IsOverride(MethodInfo m) + { + return m.GetBaseDefinition().DeclaringType != m.DeclaringType; + } + /// /// Find all assembly references that are referencing the assignTypeFrom Type's assembly found in the assemblyList /// diff --git a/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs b/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs new file mode 100644 index 0000000000..70c4ed9326 --- /dev/null +++ b/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using umbraco.cms.presentation.Trees; + +namespace Umbraco.Tests.Trees +{ + + [TestFixture] + public class BaseMediaTreeTests + { + + [TearDown] + public void TestTearDown() + { + BaseTree.AfterTreeRender -= EventHandler; + BaseTree.BeforeTreeRender -= EventHandler; + } + + [Test] + public void Run_Optimized() + { + var tree = new MyOptimizedMediaTree("media"); + + Assert.IsTrue(tree.UseOptimizedRendering); + } + + [Test] + public void Not_Optimized_Events_AfterRender() + { + var tree = new MyOptimizedMediaTree("media"); + + BaseTree.AfterTreeRender += EventHandler; + + Assert.IsFalse(tree.UseOptimizedRendering); + } + + [Test] + public void Not_Optimized_Events_BeforeRender() + { + var tree = new MyOptimizedMediaTree("media"); + + BaseTree.BeforeTreeRender += EventHandler; + + Assert.IsFalse(tree.UseOptimizedRendering); + } + + private void EventHandler(object sender, TreeEventArgs treeEventArgs) + { + + } + + public class MyOptimizedMediaTree : BaseMediaTree + { + public MyOptimizedMediaTree(string application) + : base(application) + { + } + + protected override void CreateRootNode(ref XmlTreeNode rootNode) + { + + } + } + + + } + + [TestFixture] + public class BaseContentTreeTests + { + + [TearDown] + public void TestTearDown() + { + BaseTree.AfterTreeRender -= EventHandler; + BaseTree.BeforeTreeRender -= EventHandler; + } + + [Test] + public void Run_Optimized() + { + var tree = new MyOptimizedContentTree("content"); + + Assert.IsTrue(tree.UseOptimizedRendering); + } + + [Test] + public void Not_Optimized_Events_AfterRender() + { + var tree = new MyOptimizedContentTree("content"); + + BaseTree.AfterTreeRender += EventHandler; + + Assert.IsFalse(tree.UseOptimizedRendering); + } + + [Test] + public void Not_Optimized_Events_BeforeRender() + { + var tree = new MyOptimizedContentTree("content"); + + BaseTree.BeforeTreeRender += EventHandler; + + Assert.IsFalse(tree.UseOptimizedRendering); + } + + [Test] + public void Not_Optimized_Overriden_Method() + { + var tree = new MyNotOptimizedContentTree("content"); + + Assert.IsFalse(tree.UseOptimizedRendering); + } + + private void EventHandler(object sender, TreeEventArgs treeEventArgs) + { + + } + + public class MyOptimizedContentTree : BaseContentTree + { + public MyOptimizedContentTree(string application) + : base(application) + { + } + + protected override void CreateRootNode(ref XmlTreeNode rootNode) + { + + } + } + + public class MyNotOptimizedContentTree : BaseContentTree + { + public MyNotOptimizedContentTree(string application) + : base(application) + { + } + + protected override void CreateRootNode(ref XmlTreeNode rootNode) + { + + } + + protected override void OnRenderNode(ref XmlTreeNode xNode, umbraco.cms.businesslogic.web.Document doc) + { + base.OnRenderNode(ref xNode, doc); + } + } + + + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 34c578d136..003b7611a3 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -362,6 +362,7 @@ + diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseContentTree.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseContentTree.cs index 39eb33e31f..bb1046985b 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseContentTree.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseContentTree.cs @@ -2,8 +2,10 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Reflection; using System.Text; using System.Web; +using Umbraco.Core; using Umbraco.Core.Models; using umbraco.BasePages; using umbraco.BusinessLogic; @@ -73,61 +75,64 @@ function openContent(id) { /// Renders the specified tree item. /// /// The tree. - /*public override void Render(ref XmlTree Tree) - { - //get documents to render - Document[] docs = Document.GetChildrenForTree(m_id); - - var args = new TreeEventArgs(Tree); - OnBeforeTreeRender(docs, args); - - foreach (Document dd in docs) - { - List allowedUserOptions = GetUserActionsForNode(dd); - if (CanUserAccessNode(dd, allowedUserOptions)) - { - - XmlTreeNode node = CreateNode(dd, allowedUserOptions); - - OnRenderNode(ref node, dd); - - OnBeforeNodeRender(ref Tree, ref node, EventArgs.Empty); - if (node != null) - { - Tree.Add(node); - OnAfterNodeRender(ref Tree, ref node, EventArgs.Empty); - } - } - } - OnAfterTreeRender(docs, args); - }*/ public override void Render(ref XmlTree Tree) { - //get documents to render - var entities = Services.EntityService.GetChildren(m_id, UmbracoObjectTypes.Document).ToArray(); - - var args = new TreeEventArgs(Tree); - OnBeforeTreeRender(entities, args, true); - - foreach (var entity in entities) + if (UseOptimizedRendering == false) { - var e = entity as UmbracoEntity; - List allowedUserOptions = GetUserActionsForNode(e); - if (CanUserAccessNode(e, allowedUserOptions)) + //We cannot run optimized mode since there are subscribers to events/methods that require document instances + // so we'll render the original way by looking up the docs. + + //get documents to render + var docs = Document.GetChildrenForTree(m_id); + + var args = new TreeEventArgs(Tree); + OnBeforeTreeRender(docs, args); + + foreach (var dd in docs) { - XmlTreeNode node = CreateNode(e, allowedUserOptions); - - OnRenderNode(ref node, new Document(entity, LoadMinimalDocument)); - - OnBeforeNodeRender(ref Tree, ref node, EventArgs.Empty); - if (node != null) + var allowedUserOptions = GetUserActionsForNode(dd); + if (CanUserAccessNode(dd, allowedUserOptions)) { - Tree.Add(node); - OnAfterNodeRender(ref Tree, ref node, EventArgs.Empty); + + var node = CreateNode(dd, allowedUserOptions); + + OnRenderNode(ref node, dd); + + OnBeforeNodeRender(ref Tree, ref node, EventArgs.Empty); + if (node != null) + { + Tree.Add(node); + OnAfterNodeRender(ref Tree, ref node, EventArgs.Empty); + } + } + } + OnAfterTreeRender(docs, args); + } + else + { + + //We ARE running in optmized mode, this means we will NOT be raising the BeforeTreeRender or AfterTreeRender + // events and NOT calling the OnRenderNode method - we've already detected that there are not subscribers or implementations + // to call so that is fine. + + var entities = Services.EntityService.GetChildren(m_id, UmbracoObjectTypes.Document).ToArray(); + foreach (var entity in entities) + { + var e = entity as UmbracoEntity; + var allowedUserOptions = GetUserActionsForNode(e); + if (CanUserAccessNode(e, allowedUserOptions)) + { + var node = CreateNode(e, allowedUserOptions); + + OnBeforeNodeRender(ref Tree, ref node, EventArgs.Empty); + if (node != null) + { + Tree.Add(node); + OnAfterNodeRender(ref Tree, ref node, EventArgs.Empty); + } } } } - OnAfterTreeRender(entities, args, true); } #region Tree Create-node-helper Methods - Legacy @@ -475,5 +480,35 @@ function openContent(id) { return umbraco.BusinessLogic.Actions.Action.FromString(fullMenu); } + /// + /// Returns true if we can use the EntityService to render the tree or revert to the original way + /// using normal documents + /// + /// + /// We determine this by: + /// * If there are any subscribers to the events: BeforeTreeRender or AfterTreeRender - then we cannot run optimized + /// * If there are any overrides of the method: OnRenderNode - then we cannot run optimized + /// + internal bool UseOptimizedRendering + { + get + { + if (HasEntityBasedEventSubscribers) + { + return false; + } + + //now we need to check if the current tree type has OnRenderNode overridden with a custom implementation + //Strangely - this even works in med trust! + var method = this.GetType().GetMethod("OnRenderNode", BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(XmlTreeNode).MakeByRefType(), typeof(Document) }, null); + if (TypeHelper.IsOverride(method)) + { + return false; + } + + return true; + } + } + } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseMediaTree.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseMediaTree.cs index f4e4684c72..e501489534 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseMediaTree.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseMediaTree.cs @@ -2,12 +2,14 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Reflection; using System.Text; using Umbraco.Core.Logging; using Umbraco.Core.Models; using umbraco.BasePages; using umbraco.BusinessLogic; using umbraco.BusinessLogic.Actions; +using umbraco.cms.businesslogic.web; using umbraco.interfaces; using Umbraco.Core; using Media = umbraco.cms.businesslogic.media.Media; @@ -17,7 +19,6 @@ namespace umbraco.cms.presentation.Trees { public abstract class BaseMediaTree : BaseTree { - private DisposableTimer _timer; private User _user; public BaseMediaTree(string application) @@ -57,78 +58,109 @@ function openMedia(id) { } } - //Updated Render method for improved performance, but currently not usable because of backwards compatibility - //with the OnBeforeTreeRender/OnAfterTreeRender events, which sends an array for legacy Media items. public override void Render(ref XmlTree tree) { - //_timer = DisposableTimer.Start(x => LogHelper.Debug("Media tree loaded" + " (took " + x + "ms)")); - var entities = Services.EntityService.GetChildren(m_id, UmbracoObjectTypes.Media).ToArray(); - - var args = new TreeEventArgs(tree); - OnBeforeTreeRender(entities, args, false); - - foreach (UmbracoEntity entity in entities) + if (UseOptimizedRendering == false) { - XmlTreeNode xNode = XmlTreeNode.Create(this); - xNode.NodeID = entity.Id.ToString(CultureInfo.InvariantCulture); - xNode.Text = entity.Name; + //We cannot run optimized mode since there are subscribers to events/methods that require document instances + // so we'll render the original way by looking up the docs. - xNode.HasChildren = entity.HasChildren; - xNode.Source = this.IsDialog ? GetTreeDialogUrl(entity.Id) : GetTreeServiceUrl(entity.Id); + var docs = new Media(m_id).Children; - xNode.Icon = entity.ContentTypeIcon; - xNode.OpenIcon = entity.ContentTypeIcon; - - if (IsDialog == false) + var args = new TreeEventArgs(tree); + OnBeforeTreeRender(docs, args); + + foreach (var dd in docs) { - if(this.ShowContextMenu == false) - xNode.Menu = null; - xNode.Action = "javascript:openMedia(" + entity.Id + ");"; - } - else - { - xNode.Menu = this.ShowContextMenu ? new List(new IAction[] { ActionRefresh.Instance }) : null; - if (this.DialogMode == TreeDialogModes.fulllink) + var e = dd; + var xNode = PerformNodeRender(e.Id, e.Text, e.HasChildren, e.ContentType.IconUrl, e.ContentType.Alias, () => GetLinkValue(e, e.Id.ToString(CultureInfo.InvariantCulture))); + + + OnBeforeNodeRender(ref tree, ref xNode, EventArgs.Empty); + if (xNode != null) { - string nodeLink = GetLinkValue(entity); - if (string.IsNullOrEmpty(nodeLink) == false) - { - xNode.Action = "javascript:openMedia('" + nodeLink + "');"; - } - else - { - if (string.Equals(entity.ContentTypeAlias, Constants.Conventions.MediaTypes.Folder, StringComparison.OrdinalIgnoreCase)) - { - //#U4-2254 - Inspiration to use void from here: http://stackoverflow.com/questions/4924383/jquery-object-object-error - xNode.Action = "javascript:void jQuery('.umbTree #" + entity.Id.ToString(CultureInfo.InvariantCulture) + "').click();"; - } - else - { - xNode.Action = null; - xNode.Style.DimNode(); - } - } + tree.Add(xNode); + OnAfterNodeRender(ref tree, ref xNode, EventArgs.Empty); + } + } + + OnAfterTreeRender(docs, args); + } + else + { + //We ARE running in optmized mode, this means we will NOT be raising the BeforeTreeRender or AfterTreeRender + // events - we've already detected that there are not subscribers or implementations + // to call so that is fine. + + var entities = Services.EntityService.GetChildren(m_id, UmbracoObjectTypes.Media).ToArray(); + + foreach (UmbracoEntity entity in entities) + { + var e = entity; + var xNode = PerformNodeRender(e.Id, entity.Name, e.HasChildren, e.ContentTypeIcon, e.ContentTypeAlias, () => GetLinkValue(e)); + + OnBeforeNodeRender(ref tree, ref xNode, EventArgs.Empty); + if (xNode != null) + { + tree.Add(xNode); + OnAfterNodeRender(ref tree, ref xNode, EventArgs.Empty); + } + } + } + } + + private XmlTreeNode PerformNodeRender(int nodeId, string nodeName, bool hasChildren, string icon, string contentTypeAlias, Func getLinkValue) + { + var xNode = XmlTreeNode.Create(this); + xNode.NodeID = nodeId.ToString(CultureInfo.InvariantCulture); + xNode.Text = nodeName; + + xNode.HasChildren = hasChildren; + xNode.Source = this.IsDialog ? GetTreeDialogUrl(nodeId) : GetTreeServiceUrl(nodeId); + + xNode.Icon = icon; + xNode.OpenIcon = icon; + + if (IsDialog == false) + { + if (this.ShowContextMenu == false) + xNode.Menu = null; + xNode.Action = "javascript:openMedia(" + nodeId + ");"; + } + else + { + xNode.Menu = this.ShowContextMenu ? new List(new IAction[] { ActionRefresh.Instance }) : null; + if (this.DialogMode == TreeDialogModes.fulllink) + { + string nodeLink = getLinkValue(); + if (string.IsNullOrEmpty(nodeLink) == false) + { + xNode.Action = "javascript:openMedia('" + nodeLink + "');"; } else { - xNode.Action = "javascript:openMedia('" + entity.Id.ToString(CultureInfo.InvariantCulture) + "');"; + if (string.Equals(contentTypeAlias, Constants.Conventions.MediaTypes.Folder, StringComparison.OrdinalIgnoreCase)) + { + //#U4-2254 - Inspiration to use void from here: http://stackoverflow.com/questions/4924383/jquery-object-object-error + xNode.Action = "javascript:void jQuery('.umbTree #" + nodeId.ToString(CultureInfo.InvariantCulture) + "').click();"; + } + else + { + xNode.Action = null; + xNode.Style.DimNode(); + } } } - - OnBeforeNodeRender(ref tree, ref xNode, EventArgs.Empty); - if (xNode != null) + else { - tree.Add(xNode); - OnAfterNodeRender(ref tree, ref xNode, EventArgs.Empty); + xNode.Action = "javascript:openMedia('" + nodeId.ToString(CultureInfo.InvariantCulture) + "');"; } } - //stop the timer and log the output - //_timer.Dispose(); - - OnAfterTreeRender(entities, args, false); + return xNode; } + /// /// Returns the value for a link in WYSIWYG mode, by default only media items that have a @@ -183,5 +215,26 @@ function openMedia(id) { /// public static List LinkableMediaDataTypes { get; protected set; } + /// + /// Returns true if we can use the EntityService to render the tree or revert to the original way + /// using normal documents + /// + /// + /// We determine this by: + /// * If there are any subscribers to the events: BeforeTreeRender or AfterTreeRender - then we cannot run optimized + /// + internal bool UseOptimizedRendering + { + get + { + if (HasEntityBasedEventSubscribers) + { + return false; + } + + return true; + } + } + } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseTree.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseTree.cs index 08038f0421..9c547edf04 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseTree.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseTree.cs @@ -527,6 +527,7 @@ namespace umbraco.cms.presentation.Trees AfterTreeRender(sender, e); } + [Obsolete("Do not use this method to raise events, it is no longer used and will cause very high performance spikes!")] protected internal virtual void OnBeforeTreeRender(IEnumerable sender, TreeEventArgs e, bool isContent) { if (BeforeTreeRender != null) @@ -542,6 +543,7 @@ namespace umbraco.cms.presentation.Trees } } + [Obsolete("Do not use this method to raise events, it is no longer used and will cause very high performance spikes!")] protected internal virtual void OnAfterTreeRender(IEnumerable sender, TreeEventArgs e, bool isContent) { if (AfterTreeRender != null) @@ -557,6 +559,14 @@ namespace umbraco.cms.presentation.Trees } } + /// + /// Returns true if there are subscribers to either BeforeTreeRender or AfterTreeRender + /// + internal bool HasEntityBasedEventSubscribers + { + get { return BeforeTreeRender != null || AfterTreeRender != null; } + } + /// /// Event that is raised once actions are assigned to nodes /// From 15d2021786988a541620780fdc30cef38ec2e789 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 30 Sep 2013 13:36:49 +1000 Subject: [PATCH 6/7] Obsoletes the LoadMinimalDocument flag since it no longer serves a purpose. --- .../umbraco/Trees/BaseContentTree.cs | 10 ++-------- .../umbraco/Trees/ContentRecycleBin.cs | 4 +++- .../umbraco.presentation/umbraco/Trees/loadContent.cs | 5 ++++- .../MultiNodeTreePicker/FilteredContentTree.cs | 1 + 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseContentTree.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseContentTree.cs index bb1046985b..31f65e3dde 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseContentTree.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseContentTree.cs @@ -26,14 +26,8 @@ namespace umbraco.cms.presentation.Trees public BaseContentTree(string application) : base(application) { } private User _user; - - /// - /// Determines whether the (legacy) Document object passed to the OnRenderNode-method - /// should be initialized with a full set of properties. - /// By default the Document will be initialized, so setting the boolean to True will - /// ensure that the Document object is loaded with a minimum set of properties to - /// improve performance. - /// + + [Obsolete("This is no longer used and has no effect")] protected virtual bool LoadMinimalDocument { get; set; } /// diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/ContentRecycleBin.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/ContentRecycleBin.cs index aff51792be..c8ef6f2b2f 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/ContentRecycleBin.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/ContentRecycleBin.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Text; using umbraco.businesslogic; using umbraco.cms.businesslogic; @@ -19,6 +20,7 @@ namespace umbraco.cms.presentation.Trees public ContentRecycleBin(string application) : base(application) { } + [Obsolete("This is no longer used and has no effect")] protected override bool LoadMinimalDocument { get diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadContent.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadContent.cs index dfbb7cd8e8..15b7a2dfad 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadContent.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadContent.cs @@ -1,10 +1,12 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using umbraco.BusinessLogic.Actions; using umbraco.businesslogic; using umbraco.cms.businesslogic.web; using umbraco.cms.presentation.Trees; using umbraco.interfaces; using Umbraco.Core; +using Action = umbraco.BusinessLogic.Actions.Action; namespace umbraco { @@ -54,6 +56,7 @@ namespace umbraco } } + [Obsolete("This is no longer used and has no effect")] protected override bool LoadMinimalDocument { get diff --git a/src/umbraco.editorControls/MultiNodeTreePicker/FilteredContentTree.cs b/src/umbraco.editorControls/MultiNodeTreePicker/FilteredContentTree.cs index 664282ec89..c05f9849d9 100644 --- a/src/umbraco.editorControls/MultiNodeTreePicker/FilteredContentTree.cs +++ b/src/umbraco.editorControls/MultiNodeTreePicker/FilteredContentTree.cs @@ -36,6 +36,7 @@ namespace umbraco.editorControls.MultiNodeTreePicker /// private int? m_DeterminedStartNodeId = null; + [Obsolete("This is no longer used and has no effect")] protected override bool LoadMinimalDocument { get From 4df79ce55b5bf3578aa3c01c1c2b4d7228fc2beb Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 30 Sep 2013 13:47:19 +1000 Subject: [PATCH 7/7] re-enables LoadMinimalDocument and adds this to the optimized check, even in optimized model the OnRenderNode can still execute with a minimal doc. --- .../Trees/BaseContentTreeTests.cs | 105 +++++++----------- src/Umbraco.Tests/Trees/BaseMediaTreeTests.cs | 65 +++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../umbraco/Trees/BaseContentTree.cs | 8 +- .../umbraco/Trees/ContentRecycleBin.cs | 1 - .../umbraco/Trees/loadContent.cs | 1 - .../FilteredContentTree.cs | 1 - 7 files changed, 110 insertions(+), 72 deletions(-) create mode 100644 src/Umbraco.Tests/Trees/BaseMediaTreeTests.cs diff --git a/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs b/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs index 70c4ed9326..4a2c856a15 100644 --- a/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs +++ b/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs @@ -7,67 +7,6 @@ using umbraco.cms.presentation.Trees; namespace Umbraco.Tests.Trees { - - [TestFixture] - public class BaseMediaTreeTests - { - - [TearDown] - public void TestTearDown() - { - BaseTree.AfterTreeRender -= EventHandler; - BaseTree.BeforeTreeRender -= EventHandler; - } - - [Test] - public void Run_Optimized() - { - var tree = new MyOptimizedMediaTree("media"); - - Assert.IsTrue(tree.UseOptimizedRendering); - } - - [Test] - public void Not_Optimized_Events_AfterRender() - { - var tree = new MyOptimizedMediaTree("media"); - - BaseTree.AfterTreeRender += EventHandler; - - Assert.IsFalse(tree.UseOptimizedRendering); - } - - [Test] - public void Not_Optimized_Events_BeforeRender() - { - var tree = new MyOptimizedMediaTree("media"); - - BaseTree.BeforeTreeRender += EventHandler; - - Assert.IsFalse(tree.UseOptimizedRendering); - } - - private void EventHandler(object sender, TreeEventArgs treeEventArgs) - { - - } - - public class MyOptimizedMediaTree : BaseMediaTree - { - public MyOptimizedMediaTree(string application) - : base(application) - { - } - - protected override void CreateRootNode(ref XmlTreeNode rootNode) - { - - } - } - - - } - [TestFixture] public class BaseContentTreeTests { @@ -82,15 +21,17 @@ namespace Umbraco.Tests.Trees [Test] public void Run_Optimized() { - var tree = new MyOptimizedContentTree("content"); + var tree1 = new MyOptimizedContentTree1("content"); + var tree2 = new MyOptimizedContentTree2("content"); - Assert.IsTrue(tree.UseOptimizedRendering); + Assert.IsTrue(tree1.UseOptimizedRendering); + Assert.IsTrue(tree2.UseOptimizedRendering); } [Test] public void Not_Optimized_Events_AfterRender() { - var tree = new MyOptimizedContentTree("content"); + var tree = new MyOptimizedContentTree1("content"); BaseTree.AfterTreeRender += EventHandler; @@ -100,7 +41,7 @@ namespace Umbraco.Tests.Trees [Test] public void Not_Optimized_Events_BeforeRender() { - var tree = new MyOptimizedContentTree("content"); + var tree = new MyOptimizedContentTree1("content"); BaseTree.BeforeTreeRender += EventHandler; @@ -120,17 +61,42 @@ namespace Umbraco.Tests.Trees } - public class MyOptimizedContentTree : BaseContentTree + //optimized because we are not overriding OnRenderNode + public class MyOptimizedContentTree1 : BaseContentTree { - public MyOptimizedContentTree(string application) + public MyOptimizedContentTree1(string application) : base(application) { } + protected override void CreateRootNode(ref XmlTreeNode rootNode) + { + + } + } + + public class MyOptimizedContentTree2 : BaseContentTree + { + public MyOptimizedContentTree2(string application) + : base(application) + { + } + + protected override bool LoadMinimalDocument + { + get { return true; } + } + protected override void CreateRootNode(ref XmlTreeNode rootNode) { } + + //even if we override it will still be optimized because of the LoadMinimalDocument flag + protected override void OnRenderNode(ref XmlTreeNode xNode, umbraco.cms.businesslogic.web.Document doc) + { + base.OnRenderNode(ref xNode, doc); + } } public class MyNotOptimizedContentTree : BaseContentTree @@ -145,6 +111,11 @@ namespace Umbraco.Tests.Trees } + protected override bool LoadMinimalDocument + { + get { return false; } + } + protected override void OnRenderNode(ref XmlTreeNode xNode, umbraco.cms.businesslogic.web.Document doc) { base.OnRenderNode(ref xNode, doc); diff --git a/src/Umbraco.Tests/Trees/BaseMediaTreeTests.cs b/src/Umbraco.Tests/Trees/BaseMediaTreeTests.cs new file mode 100644 index 0000000000..0e93b3db78 --- /dev/null +++ b/src/Umbraco.Tests/Trees/BaseMediaTreeTests.cs @@ -0,0 +1,65 @@ +using NUnit.Framework; +using umbraco.cms.presentation.Trees; + +namespace Umbraco.Tests.Trees +{ + [TestFixture] + public class BaseMediaTreeTests + { + + [TearDown] + public void TestTearDown() + { + BaseTree.AfterTreeRender -= EventHandler; + BaseTree.BeforeTreeRender -= EventHandler; + } + + [Test] + public void Run_Optimized() + { + var tree = new MyOptimizedMediaTree("media"); + + Assert.IsTrue(tree.UseOptimizedRendering); + } + + [Test] + public void Not_Optimized_Events_AfterRender() + { + var tree = new MyOptimizedMediaTree("media"); + + BaseTree.AfterTreeRender += EventHandler; + + Assert.IsFalse(tree.UseOptimizedRendering); + } + + [Test] + public void Not_Optimized_Events_BeforeRender() + { + var tree = new MyOptimizedMediaTree("media"); + + BaseTree.BeforeTreeRender += EventHandler; + + Assert.IsFalse(tree.UseOptimizedRendering); + } + + private void EventHandler(object sender, TreeEventArgs treeEventArgs) + { + + } + + public class MyOptimizedMediaTree : BaseMediaTree + { + public MyOptimizedMediaTree(string application) + : base(application) + { + } + + protected override void CreateRootNode(ref XmlTreeNode rootNode) + { + + } + } + + + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 003b7611a3..a37a253e9b 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -363,6 +363,7 @@ + diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseContentTree.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseContentTree.cs index 31f65e3dde..81a97ab593 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseContentTree.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseContentTree.cs @@ -27,7 +27,6 @@ namespace umbraco.cms.presentation.Trees private User _user; - [Obsolete("This is no longer used and has no effect")] protected virtual bool LoadMinimalDocument { get; set; } /// @@ -118,6 +117,11 @@ function openContent(id) { { var node = CreateNode(e, allowedUserOptions); + //in optimized mode the LoadMinimalDocument will ALWAYS be true, if it is not true then we will + // be rendering in non-optimized mode and this code will not get executed so we don't need to worry + // about performance here. + OnRenderNode(ref node, new Document(e, LoadMinimalDocument)); + OnBeforeNodeRender(ref Tree, ref node, EventArgs.Empty); if (node != null) { @@ -495,7 +499,7 @@ function openContent(id) { //now we need to check if the current tree type has OnRenderNode overridden with a custom implementation //Strangely - this even works in med trust! var method = this.GetType().GetMethod("OnRenderNode", BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(XmlTreeNode).MakeByRefType(), typeof(Document) }, null); - if (TypeHelper.IsOverride(method)) + if (TypeHelper.IsOverride(method) && LoadMinimalDocument == false) { return false; } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/ContentRecycleBin.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/ContentRecycleBin.cs index c8ef6f2b2f..31dc820127 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/ContentRecycleBin.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/ContentRecycleBin.cs @@ -20,7 +20,6 @@ namespace umbraco.cms.presentation.Trees public ContentRecycleBin(string application) : base(application) { } - [Obsolete("This is no longer used and has no effect")] protected override bool LoadMinimalDocument { get diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadContent.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadContent.cs index 15b7a2dfad..3eb086debc 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadContent.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadContent.cs @@ -56,7 +56,6 @@ namespace umbraco } } - [Obsolete("This is no longer used and has no effect")] protected override bool LoadMinimalDocument { get diff --git a/src/umbraco.editorControls/MultiNodeTreePicker/FilteredContentTree.cs b/src/umbraco.editorControls/MultiNodeTreePicker/FilteredContentTree.cs index c05f9849d9..664282ec89 100644 --- a/src/umbraco.editorControls/MultiNodeTreePicker/FilteredContentTree.cs +++ b/src/umbraco.editorControls/MultiNodeTreePicker/FilteredContentTree.cs @@ -36,7 +36,6 @@ namespace umbraco.editorControls.MultiNodeTreePicker /// private int? m_DeterminedStartNodeId = null; - [Obsolete("This is no longer used and has no effect")] protected override bool LoadMinimalDocument { get