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/TypeHelper.cs b/src/Umbraco.Core/TypeHelper.cs index 9e19b46c31..7d0810cf13 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.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 1aae6f7b51..f07ffcf758 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -274,6 +274,7 @@ + diff --git a/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs b/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs new file mode 100644 index 0000000000..4a2c856a15 --- /dev/null +++ b/src/Umbraco.Tests/Trees/BaseContentTreeTests.cs @@ -0,0 +1,127 @@ +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 BaseContentTreeTests + { + + [TearDown] + public void TestTearDown() + { + BaseTree.AfterTreeRender -= EventHandler; + BaseTree.BeforeTreeRender -= EventHandler; + } + + [Test] + public void Run_Optimized() + { + var tree1 = new MyOptimizedContentTree1("content"); + var tree2 = new MyOptimizedContentTree2("content"); + + Assert.IsTrue(tree1.UseOptimizedRendering); + Assert.IsTrue(tree2.UseOptimizedRendering); + } + + [Test] + public void Not_Optimized_Events_AfterRender() + { + var tree = new MyOptimizedContentTree1("content"); + + BaseTree.AfterTreeRender += EventHandler; + + Assert.IsFalse(tree.UseOptimizedRendering); + } + + [Test] + public void Not_Optimized_Events_BeforeRender() + { + var tree = new MyOptimizedContentTree1("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) + { + + } + + //optimized because we are not overriding OnRenderNode + public class MyOptimizedContentTree1 : BaseContentTree + { + 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 + { + public MyNotOptimizedContentTree(string application) + : base(application) + { + } + + protected override void CreateRootNode(ref XmlTreeNode rootNode) + { + + } + + 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 49f8d6e6b3..dd84e8706c 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -427,6 +427,8 @@ + + diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs index fb6aca88b3..27ab92eadd 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. diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 35896c9358..756b1abfcd 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -16,7 +16,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 ea0f861921..8a43d34dc3 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -527,11 +527,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); } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseContentTree.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseContentTree.cs index 986ea82796..1eacc072bd 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; @@ -25,14 +27,7 @@ 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. - /// + protected virtual bool LoadMinimalDocument { get; set; } /// @@ -74,61 +69,69 @@ 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); + + //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) + { + Tree.Add(node); + OnAfterNodeRender(ref Tree, ref node, EventArgs.Empty); + } } } } - OnAfterTreeRender(entities, args, true); } #region Tree Create-node-helper Methods - Legacy @@ -476,5 +479,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) && LoadMinimalDocument == false) + { + 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 5a033ace09..0b48f7a2e1 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseMediaTree.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseMediaTree.cs @@ -2,6 +2,7 @@ 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; @@ -10,6 +11,7 @@ using Umbraco.Core.PropertyEditors; 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; @@ -21,7 +23,6 @@ namespace umbraco.cms.presentation.Trees [Obsolete("This is no longer used and will be removed from the codebase in the future")] public abstract class BaseMediaTree : BaseTree { - private DisposableTimer _timer; private User _user; public BaseMediaTree(string application) @@ -61,78 +62,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 @@ -192,5 +224,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 a06adb1f2d..74c040aec5 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseTree.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseTree.cs @@ -528,6 +528,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) @@ -543,6 +544,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) @@ -558,6 +560,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 ///