From ef5525e67fd950f6ad06ec37c2fc8a90bf30f63a Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Tue, 2 Oct 2012 22:51:53 +0500 Subject: [PATCH 1/4] renamed DynamicDocumentList to DynamicPublishedContentList simplified some of the DynamicPublishedContent methods to accept Func instead of Func, made the IsHelper method private as this shouldn't be exposed. renamed other objects starting with DynamicDocument to DynamicPublishedContent --- src/Umbraco.Core/Dynamics/DynamicGrouping.cs | 4 +- .../Dynamics/DynamicPublishedContent.cs | 108 +++++++++--------- ...amicPublishedContentDataSourceResolver.cs} | 6 +- ...amicPublishedContentIdEqualityComparer.cs} | 2 +- ...List.cs => DynamicPublishedContentList.cs} | 44 +++---- ...=> DynamicPublishedContentListOrdering.cs} | 2 +- ...er.cs => DynamicPublishedContentWalker.cs} | 20 ++-- src/Umbraco.Core/Dynamics/DynamicQueryable.cs | 2 +- src/Umbraco.Core/Dynamics/ExtensionMethods.cs | 8 +- src/Umbraco.Core/Dynamics/Grouping.cs | 8 +- ... => IDynamicPublishedContentDataSource.cs} | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 12 +- .../DynamicDocument/DocumentTests.cs | 4 +- .../DynamicDocumentTestsBase.cs | 8 +- ...PublishedContentCustomExtensionMethods.cs} | 4 +- ...sts.cs => DynamicPublishedContentTests.cs} | 2 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 4 +- ...faultDynamicPublishedContentDataSource.cs} | 2 +- ...ynamicPublishedContentSearchExtensions.cs} | 10 +- src/Umbraco.Web/ExamineExtensions.cs | 4 +- ...sions.cs => PublishedContentExtensions.cs} | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 6 +- src/Umbraco.Web/UmbracoHelper.cs | 4 +- src/Umbraco.Web/WebBootManager.cs | 2 +- .../RazorDynamicNode/DynamicNode.cs | 2 +- ...s => DynamicPublishedContentExtensions.cs} | 2 +- .../RazorDynamicNode/RazorLibraryCore.cs | 4 +- .../umbraco.MacroEngines.csproj | 2 +- 28 files changed, 140 insertions(+), 140 deletions(-) rename src/Umbraco.Core/Dynamics/{DynamicDocumentDataSourceResolver.cs => DynamicPublishedContentDataSourceResolver.cs} (60%) rename src/Umbraco.Core/Dynamics/{DynamicDocumentIdEqualityComparer.cs => DynamicPublishedContentIdEqualityComparer.cs} (86%) rename src/Umbraco.Core/Dynamics/{DynamicDocumentList.cs => DynamicPublishedContentList.cs} (86%) rename src/Umbraco.Core/Dynamics/{DynamicDocumentListOrdering.cs => DynamicPublishedContentListOrdering.cs} (97%) rename src/Umbraco.Core/Dynamics/{DynamicDocumentWalker.cs => DynamicPublishedContentWalker.cs} (89%) rename src/Umbraco.Core/Dynamics/{IDynamicDocumentDataSource.cs => IDynamicPublishedContentDataSource.cs} (89%) rename src/Umbraco.Tests/DynamicDocument/{DynamicDocumentCustomExtensionMethods.cs => DynamicPublishedContentCustomExtensionMethods.cs} (84%) rename src/Umbraco.Tests/DynamicDocument/{DynamicDocumentTests.cs => DynamicPublishedContentTests.cs} (92%) rename src/Umbraco.Web/{DefaultDynamicDocumentDataSource.cs => DefaultDynamicPublishedContentDataSource.cs} (86%) rename src/Umbraco.Web/{DynamicDocumentSearchExtensions.cs => DynamicPublishedContentSearchExtensions.cs} (70%) rename src/Umbraco.Web/{DocumentExtensions.cs => PublishedContentExtensions.cs} (96%) rename src/umbraco.MacroEngines/RazorDynamicNode/{DynamicDocumentExtensions.cs => DynamicPublishedContentExtensions.cs} (94%) diff --git a/src/Umbraco.Core/Dynamics/DynamicGrouping.cs b/src/Umbraco.Core/Dynamics/DynamicGrouping.cs index 6a0dca0609..11c2b1df65 100644 --- a/src/Umbraco.Core/Dynamics/DynamicGrouping.cs +++ b/src/Umbraco.Core/Dynamics/DynamicGrouping.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Dynamics return this; } - public DynamicGrouping(DynamicDocumentList list, string groupBy) + public DynamicGrouping(DynamicPublishedContentList list, string groupBy) { Inner = list @@ -21,7 +21,7 @@ namespace Umbraco.Core.Dynamics .Select(node => { string predicate = groupBy; - var internalList = new DynamicDocumentList(new DynamicPublishedContent[] { node }); + var internalList = new DynamicPublishedContentList(new DynamicPublishedContent[] { node }); var query = (IQueryable)internalList.Select(predicate, new object[] { }); var key = query.FirstOrDefault(); return new diff --git a/src/Umbraco.Core/Dynamics/DynamicPublishedContent.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContent.cs index efc97dba7a..a747163293 100644 --- a/src/Umbraco.Core/Dynamics/DynamicPublishedContent.cs +++ b/src/Umbraco.Core/Dynamics/DynamicPublishedContent.cs @@ -20,10 +20,10 @@ namespace Umbraco.Core.Dynamics public class DynamicPublishedContent : DynamicObject, IPublishedContent { private readonly IPublishedContent _publishedContent; - private DynamicDocumentList _cachedChildren; + private DynamicPublishedContentList _cachedChildren; private readonly ConcurrentDictionary _cachedMemberOutput = new ConcurrentDictionary(); - internal DynamicDocumentList OwnerList { get; set; } + internal DynamicPublishedContentList OwnerList { get; set; } #region Constructors @@ -56,60 +56,60 @@ namespace Umbraco.Core.Dynamics #region Traversal public DynamicPublishedContent Up() { - return DynamicDocumentWalker.Up(this); + return DynamicPublishedContentWalker.Up(this); } public DynamicPublishedContent Up(int number) { - return DynamicDocumentWalker.Up(this, number); + return DynamicPublishedContentWalker.Up(this, number); } public DynamicPublishedContent Up(string nodeTypeAlias) { - return DynamicDocumentWalker.Up(this, nodeTypeAlias); + return DynamicPublishedContentWalker.Up(this, nodeTypeAlias); } public DynamicPublishedContent Down() { - return DynamicDocumentWalker.Down(this); + return DynamicPublishedContentWalker.Down(this); } public DynamicPublishedContent Down(int number) { - return DynamicDocumentWalker.Down(this, number); + return DynamicPublishedContentWalker.Down(this, number); } public DynamicPublishedContent Down(string nodeTypeAlias) { - return DynamicDocumentWalker.Down(this, nodeTypeAlias); + return DynamicPublishedContentWalker.Down(this, nodeTypeAlias); } public DynamicPublishedContent Next() { - return DynamicDocumentWalker.Next(this); + return DynamicPublishedContentWalker.Next(this); } public DynamicPublishedContent Next(int number) { - return DynamicDocumentWalker.Next(this, number); + return DynamicPublishedContentWalker.Next(this, number); } public DynamicPublishedContent Next(string nodeTypeAlias) { - return DynamicDocumentWalker.Next(this, nodeTypeAlias); + return DynamicPublishedContentWalker.Next(this, nodeTypeAlias); } public DynamicPublishedContent Previous() { - return DynamicDocumentWalker.Previous(this); + return DynamicPublishedContentWalker.Previous(this); } public DynamicPublishedContent Previous(int number) { - return DynamicDocumentWalker.Previous(this, number); + return DynamicPublishedContentWalker.Previous(this, number); } public DynamicPublishedContent Previous(string nodeTypeAlias) { - return DynamicDocumentWalker.Previous(this, nodeTypeAlias); + return DynamicPublishedContentWalker.Previous(this, nodeTypeAlias); } public DynamicPublishedContent Sibling(int number) { - return DynamicDocumentWalker.Sibling(this, number); + return DynamicPublishedContentWalker.Sibling(this, number); } public DynamicPublishedContent Sibling(string nodeTypeAlias) { - return DynamicDocumentWalker.Sibling(this, nodeTypeAlias); + return DynamicPublishedContentWalker.Sibling(this, nodeTypeAlias); } #endregion @@ -237,11 +237,11 @@ namespace Umbraco.Core.Dynamics } if (result is IEnumerable) { - result = new DynamicDocumentList((IEnumerable)result); + result = new DynamicPublishedContentList((IEnumerable)result); } if (result is IEnumerable) { - result = new DynamicDocumentList((IEnumerable)result); + result = new DynamicPublishedContentList((IEnumerable)result); } } return result; @@ -289,7 +289,7 @@ namespace Umbraco.Core.Dynamics if (filteredTypeChildren.Any()) { return new Attempt(true, - new DynamicDocumentList(filteredTypeChildren.Select(x => new DynamicPublishedContent(x)))); + new DynamicPublishedContentList(filteredTypeChildren.Select(x => new DynamicPublishedContent(x)))); } return Attempt.False; } @@ -342,7 +342,7 @@ namespace Umbraco.Core.Dynamics } //get the data type id for the current property - var dataType = DynamicDocumentDataSourceResolver.Current.DataSource.GetDataType(userProperty.DocumentTypeAlias, userProperty.Alias); + var dataType = DynamicPublishedContentDataSourceResolver.Current.DataSource.GetDataType(userProperty.DocumentTypeAlias, userProperty.Alias); //convert the string value to a known type var converted = ConvertPropertyValue(result, dataType, userProperty.DocumentTypeAlias, userProperty.Alias); @@ -680,13 +680,13 @@ namespace Umbraco.Core.Dynamics { return AncestorOrSelf(node => node.DocumentTypeAlias == nodeTypeAlias); } - public DynamicPublishedContent AncestorOrSelf(Func func) + public DynamicPublishedContent AncestorOrSelf(Func func) { var node = this; while (node != null) { if (func(node)) return node; - DynamicPublishedContent parent = node.Parent; + var parent = node.Parent; if (parent != null) { if (this != parent) @@ -705,15 +705,15 @@ namespace Umbraco.Core.Dynamics } return node; } - public DynamicDocumentList AncestorsOrSelf(Func func) + public DynamicPublishedContentList AncestorsOrSelf(Func func) { - var ancestorList = new List(); + var ancestorList = new List(); var node = this; ancestorList.Add(node); while (node != null) { if (node.Level == 1) break; - DynamicPublishedContent parent = node.Parent; + var parent = node.Parent; if (parent != null) { if (this != parent) @@ -735,50 +735,50 @@ namespace Umbraco.Core.Dynamics } } ancestorList.Reverse(); - return new DynamicDocumentList(ancestorList); + return new DynamicPublishedContentList(ancestorList); } - public DynamicDocumentList AncestorsOrSelf() + public DynamicPublishedContentList AncestorsOrSelf() { return AncestorsOrSelf(n => true); } - public DynamicDocumentList AncestorsOrSelf(string nodeTypeAlias) + public DynamicPublishedContentList AncestorsOrSelf(string nodeTypeAlias) { return AncestorsOrSelf(n => n.DocumentTypeAlias == nodeTypeAlias); } - public DynamicDocumentList AncestorsOrSelf(int level) + public DynamicPublishedContentList AncestorsOrSelf(int level) { return AncestorsOrSelf(n => n.Level <= level); } - public DynamicDocumentList Descendants(string nodeTypeAlias) + public DynamicPublishedContentList Descendants(string nodeTypeAlias) { return Descendants(p => p.DocumentTypeAlias == nodeTypeAlias); } - public DynamicDocumentList Descendants(int level) + public DynamicPublishedContentList Descendants(int level) { return Descendants(p => p.Level >= level); } - public DynamicDocumentList Descendants() + public DynamicPublishedContentList Descendants() { return Descendants(n => true); } - internal DynamicDocumentList Descendants(Func func) + internal DynamicPublishedContentList Descendants(Func func) { var flattenedNodes = this._publishedContent.Children.Map(func, (IPublishedContent n) => n.Children); - return new DynamicDocumentList(flattenedNodes.ToList().ConvertAll(dynamicBackingItem => new DynamicPublishedContent(dynamicBackingItem))); + return new DynamicPublishedContentList(flattenedNodes.ToList().ConvertAll(dynamicBackingItem => new DynamicPublishedContent(dynamicBackingItem))); } - public DynamicDocumentList DescendantsOrSelf(int level) + public DynamicPublishedContentList DescendantsOrSelf(int level) { return DescendantsOrSelf(p => p.Level >= level); } - public DynamicDocumentList DescendantsOrSelf(string nodeTypeAlias) + public DynamicPublishedContentList DescendantsOrSelf(string nodeTypeAlias) { return DescendantsOrSelf(p => p.DocumentTypeAlias == nodeTypeAlias); } - public DynamicDocumentList DescendantsOrSelf() + public DynamicPublishedContentList DescendantsOrSelf() { return DescendantsOrSelf(p => true); } - internal DynamicDocumentList DescendantsOrSelf(Func func) + internal DynamicPublishedContentList DescendantsOrSelf(Func func) { if (this._publishedContent != null) { @@ -788,30 +788,30 @@ namespace Umbraco.Core.Dynamics thisNode.Add(this._publishedContent); } var flattenedNodes = this._publishedContent.Children.Map(func, (IPublishedContent n) => n.Children); - return new DynamicDocumentList(thisNode.Concat(flattenedNodes).ToList().ConvertAll(dynamicBackingItem => new DynamicPublishedContent(dynamicBackingItem))); + return new DynamicPublishedContentList(thisNode.Concat(flattenedNodes).ToList().ConvertAll(dynamicBackingItem => new DynamicPublishedContent(dynamicBackingItem))); } - return new DynamicDocumentList(Enumerable.Empty()); + return new DynamicPublishedContentList(Enumerable.Empty()); } - public DynamicDocumentList Ancestors(int level) + public DynamicPublishedContentList Ancestors(int level) { return Ancestors(n => n.Level <= level); } - public DynamicDocumentList Ancestors(string nodeTypeAlias) + public DynamicPublishedContentList Ancestors(string nodeTypeAlias) { return Ancestors(n => n.DocumentTypeAlias == nodeTypeAlias); } - public DynamicDocumentList Ancestors() + public DynamicPublishedContentList Ancestors() { return Ancestors(n => true); } - public DynamicDocumentList Ancestors(Func func) + public DynamicPublishedContentList Ancestors(Func func) { - var ancestorList = new List(); + var ancestorList = new List(); var node = this; while (node != null) { if (node.Level == 1) break; - DynamicPublishedContent parent = node.Parent; + var parent = node.Parent; if (parent != null) { if (this != parent) @@ -833,7 +833,7 @@ namespace Umbraco.Core.Dynamics } } ancestorList.Reverse(); - return new DynamicDocumentList(ancestorList); + return new DynamicPublishedContentList(ancestorList); } public DynamicPublishedContent Parent { @@ -956,11 +956,11 @@ namespace Umbraco.Core.Dynamics //testing, think this must be a special case for the root node ? if (!children.Any() && _publishedContent.Id == 0) { - _cachedChildren = new DynamicDocumentList(new List { new DynamicPublishedContent(this._publishedContent) }); + _cachedChildren = new DynamicPublishedContentList(new List { new DynamicPublishedContent(this._publishedContent) }); } else { - _cachedChildren = new DynamicDocumentList(_publishedContent.Children.Select(x => new DynamicPublishedContent(x))); + _cachedChildren = new DynamicPublishedContentList(_publishedContent.Children.Select(x => new DynamicPublishedContent(x))); } } return _cachedChildren; @@ -1042,7 +1042,7 @@ namespace Umbraco.Core.Dynamics { //var list = this.Parent.Children.Select(n => new DynamicNode(n)); var list = this.Parent.Children; - this.OwnerList = new DynamicDocumentList(list); + this.OwnerList = new DynamicPublishedContentList(list); } if (this.OwnerList != null) { @@ -1357,15 +1357,15 @@ namespace Umbraco.Core.Dynamics var descendants = this.DescendantsOrSelf(); return IsHelper(n => descendants.Items.Find(descendant => descendant.Id == other.Id) != null, valueIfTrue, valueIfFalse); } - public bool IsHelper(Func test) + private bool IsHelper(Func test) { return test(this); } - public HtmlString IsHelper(Func test, string valueIfTrue) + private HtmlString IsHelper(Func test, string valueIfTrue) { return IsHelper(test, valueIfTrue, string.Empty); } - public HtmlString IsHelper(Func test, string valueIfTrue, string valueIfFalse) + private HtmlString IsHelper(Func test, string valueIfTrue, string valueIfFalse) { return test(this) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); } @@ -1388,7 +1388,7 @@ namespace Umbraco.Core.Dynamics public bool Where(string predicate) { //Totally gonna cheat here - var dynamicDocumentList = new DynamicDocumentList(); + var dynamicDocumentList = new DynamicPublishedContentList(); dynamicDocumentList.Add(this); var filtered = dynamicDocumentList.Where(predicate); if (Queryable.Count(filtered) == 1) diff --git a/src/Umbraco.Core/Dynamics/DynamicDocumentDataSourceResolver.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContentDataSourceResolver.cs similarity index 60% rename from src/Umbraco.Core/Dynamics/DynamicDocumentDataSourceResolver.cs rename to src/Umbraco.Core/Dynamics/DynamicPublishedContentDataSourceResolver.cs index 654af1e4c0..79aa4a5ed8 100644 --- a/src/Umbraco.Core/Dynamics/DynamicDocumentDataSourceResolver.cs +++ b/src/Umbraco.Core/Dynamics/DynamicPublishedContentDataSourceResolver.cs @@ -7,11 +7,11 @@ namespace Umbraco.Core.Dynamics /// and currently the business logic part of Umbraco is still in the legacy project and we don't want to move that to the core so in the /// meantime until the new APIs are made, we need to have this data source in place with a resolver which is set in the web project. /// - internal class DynamicDocumentDataSourceResolver : SingleObjectResolverBase + internal class DynamicPublishedContentDataSourceResolver : SingleObjectResolverBase { - public IDynamicDocumentDataSource DataSource { get; private set; } + public IDynamicPublishedContentDataSource DataSource { get; private set; } - public DynamicDocumentDataSourceResolver(IDynamicDocumentDataSource dataSource) + public DynamicPublishedContentDataSourceResolver(IDynamicPublishedContentDataSource dataSource) { DataSource = dataSource; } diff --git a/src/Umbraco.Core/Dynamics/DynamicDocumentIdEqualityComparer.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContentIdEqualityComparer.cs similarity index 86% rename from src/Umbraco.Core/Dynamics/DynamicDocumentIdEqualityComparer.cs rename to src/Umbraco.Core/Dynamics/DynamicPublishedContentIdEqualityComparer.cs index 62243570a1..54cea8d0cf 100644 --- a/src/Umbraco.Core/Dynamics/DynamicDocumentIdEqualityComparer.cs +++ b/src/Umbraco.Core/Dynamics/DynamicPublishedContentIdEqualityComparer.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; namespace Umbraco.Core.Dynamics { - internal class DynamicDocumentIdEqualityComparer : EqualityComparer + internal class DynamicPublishedContentIdEqualityComparer : EqualityComparer { public override bool Equals(DynamicPublishedContent x, DynamicPublishedContent y) diff --git a/src/Umbraco.Core/Dynamics/DynamicDocumentList.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContentList.cs similarity index 86% rename from src/Umbraco.Core/Dynamics/DynamicDocumentList.cs rename to src/Umbraco.Core/Dynamics/DynamicPublishedContentList.cs index 0a491a1a16..35658b46af 100644 --- a/src/Umbraco.Core/Dynamics/DynamicDocumentList.cs +++ b/src/Umbraco.Core/Dynamics/DynamicPublishedContentList.cs @@ -9,22 +9,22 @@ using System.Reflection; namespace Umbraco.Core.Dynamics { - public class DynamicDocumentList : DynamicObject, IEnumerable + public class DynamicPublishedContentList : DynamicObject, IEnumerable { internal List Items { get; set; } - public DynamicDocumentList() + public DynamicPublishedContentList() { Items = new List(); } - public DynamicDocumentList(IEnumerable items) + public DynamicPublishedContentList(IEnumerable items) { List list = items.ToList(); list.ForEach(node => node.OwnerList = this); Items = list; } - public DynamicDocumentList(IEnumerable items) + public DynamicPublishedContentList(IEnumerable items) { List list = items.Select(x => new DynamicPublishedContent(x)).ToList(); list.ForEach(node => node.OwnerList = this); @@ -61,24 +61,24 @@ namespace Umbraco.Core.Dynamics var values = args.Skip(1).ToArray(); //TODO: We are pre-resolving the where into a ToList() here which will have performance impacts if there where clauses // are nested! We should somehow support an QueryableDocumentList! - result = new DynamicDocumentList(this.Where(predicate, values).ToList()); + result = new DynamicPublishedContentList(this.Where(predicate, values).ToList()); return true; } if (name == "OrderBy") { //TODO: We are pre-resolving the where into a ToList() here which will have performance impacts if there where clauses // are nested! We should somehow support an QueryableDocumentList! - result = new DynamicDocumentList(this.OrderBy(args.First().ToString()).ToList()); + result = new DynamicPublishedContentList(this.OrderBy(args.First().ToString()).ToList()); return true; } if (name == "Take") { - result = new DynamicDocumentList(this.Take((int)args.First())); + result = new DynamicPublishedContentList(this.Take((int)args.First())); return true; } if (name == "Skip") { - result = new DynamicDocumentList(this.Skip((int)args.First())); + result = new DynamicPublishedContentList(this.Skip((int)args.First())); return true; } if (name == "InGroupsOf") @@ -117,12 +117,12 @@ namespace Umbraco.Core.Dynamics { if ((args.First() as IEnumerable) != null) { - result = new DynamicDocumentList(this.Items.Union(args.First() as IEnumerable)); + result = new DynamicPublishedContentList(this.Items.Union(args.First() as IEnumerable)); return true; } - if ((args.First() as DynamicDocumentList) != null) + if ((args.First() as DynamicPublishedContentList) != null) { - result = new DynamicDocumentList(this.Items.Union((args.First() as DynamicDocumentList).Items)); + result = new DynamicPublishedContentList(this.Items.Union((args.First() as DynamicPublishedContentList).Items)); return true; } } @@ -130,12 +130,12 @@ namespace Umbraco.Core.Dynamics { if ((args.First() as IEnumerable) != null) { - result = new DynamicDocumentList(this.Items.Except(args.First() as IEnumerable, new DynamicDocumentIdEqualityComparer())); + result = new DynamicPublishedContentList(this.Items.Except(args.First() as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); return true; } - if ((args.First() as DynamicDocumentList) != null) + if ((args.First() as DynamicPublishedContentList) != null) { - result = new DynamicDocumentList(this.Items.Except((args.First() as DynamicDocumentList).Items, new DynamicDocumentIdEqualityComparer())); + result = new DynamicPublishedContentList(this.Items.Except((args.First() as DynamicPublishedContentList).Items, new DynamicPublishedContentIdEqualityComparer())); return true; } } @@ -143,18 +143,18 @@ namespace Umbraco.Core.Dynamics { if ((args.First() as IEnumerable) != null) { - result = new DynamicDocumentList(this.Items.Intersect(args.First() as IEnumerable, new DynamicDocumentIdEqualityComparer())); + result = new DynamicPublishedContentList(this.Items.Intersect(args.First() as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); return true; } - if ((args.First() as DynamicDocumentList) != null) + if ((args.First() as DynamicPublishedContentList) != null) { - result = new DynamicDocumentList(this.Items.Intersect((args.First() as DynamicDocumentList).Items, new DynamicDocumentIdEqualityComparer())); + result = new DynamicPublishedContentList(this.Items.Intersect((args.First() as DynamicPublishedContentList).Items, new DynamicPublishedContentIdEqualityComparer())); return true; } } if (name == "Distinct") { - result = new DynamicDocumentList(this.Items.Distinct(new DynamicDocumentIdEqualityComparer())); + result = new DynamicPublishedContentList(this.Items.Distinct(new DynamicPublishedContentIdEqualityComparer())); return true; } if (name == "Pluck" || name == "Select") @@ -369,7 +369,7 @@ namespace Umbraco.Core.Dynamics var methodTypesToFind = new[] { typeof(IEnumerable), - typeof(DynamicDocumentList) + typeof(DynamicPublishedContentList) }; //find known extension methods that match the first type in the list @@ -383,7 +383,7 @@ namespace Umbraco.Core.Dynamics if (toExecute != null) { - if (toExecute.GetParameters().First().ParameterType == typeof(DynamicDocumentList)) + if (toExecute.GetParameters().First().ParameterType == typeof(DynamicPublishedContentList)) { var genericArgs = (new[] { this }).Concat(args); result = toExecute.Invoke(null, genericArgs.ToArray()); @@ -412,11 +412,11 @@ namespace Umbraco.Core.Dynamics } if (result is IEnumerable) { - result = new DynamicDocumentList((IEnumerable)result); + result = new DynamicPublishedContentList((IEnumerable)result); } if (result is IEnumerable) { - result = new DynamicDocumentList((IEnumerable)result); + result = new DynamicPublishedContentList((IEnumerable)result); } } return result; diff --git a/src/Umbraco.Core/Dynamics/DynamicDocumentListOrdering.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContentListOrdering.cs similarity index 97% rename from src/Umbraco.Core/Dynamics/DynamicDocumentListOrdering.cs rename to src/Umbraco.Core/Dynamics/DynamicPublishedContentListOrdering.cs index 39d987f509..c56a3b271b 100644 --- a/src/Umbraco.Core/Dynamics/DynamicDocumentListOrdering.cs +++ b/src/Umbraco.Core/Dynamics/DynamicPublishedContentListOrdering.cs @@ -5,7 +5,7 @@ using System.Linq.Expressions; namespace Umbraco.Core.Dynamics { - internal static class DynamicDocumentListOrdering + internal static class DynamicPublishedContentListOrdering { private static TOut Reduce(Func func, DynamicPublishedContent publishedContent) diff --git a/src/Umbraco.Core/Dynamics/DynamicDocumentWalker.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContentWalker.cs similarity index 89% rename from src/Umbraco.Core/Dynamics/DynamicDocumentWalker.cs rename to src/Umbraco.Core/Dynamics/DynamicPublishedContentWalker.cs index 0fa83c4c63..26c3a8dee8 100644 --- a/src/Umbraco.Core/Dynamics/DynamicDocumentWalker.cs +++ b/src/Umbraco.Core/Dynamics/DynamicPublishedContentWalker.cs @@ -4,7 +4,7 @@ using System.Linq; namespace Umbraco.Core.Dynamics { - internal static class DynamicDocumentWalker + internal static class DynamicPublishedContentWalker { public static DynamicPublishedContent Up(this DynamicPublishedContent context) { @@ -41,7 +41,7 @@ namespace Umbraco.Core.Dynamics } public static DynamicPublishedContent Down(this DynamicPublishedContent context, int number) { - var children = new DynamicDocumentList(context.Children); + var children = new DynamicPublishedContentList(context.Children); if (number == 0) { return children.Items.First(); @@ -52,7 +52,7 @@ namespace Umbraco.Core.Dynamics while (number-- >= 0) { working = children.Items.First(); - children = new DynamicDocumentList(working.Children); + children = new DynamicPublishedContentList(working.Children); } return working; } @@ -62,7 +62,7 @@ namespace Umbraco.Core.Dynamics if (string.IsNullOrEmpty(nodeTypeAlias)) { - var children = new DynamicDocumentList(context.Children); + var children = new DynamicPublishedContentList(context.Children); return children.Items.First(); } else @@ -80,7 +80,7 @@ namespace Umbraco.Core.Dynamics { //var list = context.Parent.Children.Select(n => new DynamicNode(n)); var list = context.Parent.Children; - context.OwnerList = new DynamicDocumentList(list); + context.OwnerList = new DynamicPublishedContentList(list); } if (context.OwnerList != null) { @@ -106,7 +106,7 @@ namespace Umbraco.Core.Dynamics { //var list = context.Parent.Children.Select(n => new DynamicNode(n)); var list = context.Parent.Children; - context.OwnerList = new DynamicDocumentList(list); + context.OwnerList = new DynamicPublishedContentList(list); } if (context.OwnerList != null) { @@ -132,7 +132,7 @@ namespace Umbraco.Core.Dynamics { //var list = context.Parent.Children.Select(n => new DynamicNode(n)); var list = context.Parent.Children; - context.OwnerList = new DynamicDocumentList(list); + context.OwnerList = new DynamicPublishedContentList(list); } if (context.OwnerList != null) { @@ -172,7 +172,7 @@ namespace Umbraco.Core.Dynamics { //var list = context.Parent.Children.Select(n => new DynamicNode(n)); var list = context.Parent.Children; - context.OwnerList = new DynamicDocumentList(list); + context.OwnerList = new DynamicPublishedContentList(list); } if (context.OwnerList != null) { @@ -207,7 +207,7 @@ namespace Umbraco.Core.Dynamics { //var list = context.Parent.Children.Select(n => new DynamicNode(n)); var list = context.Parent.Children; - context.OwnerList = new DynamicDocumentList(list); + context.OwnerList = new DynamicPublishedContentList(list); } if (context.OwnerList != null) { @@ -233,7 +233,7 @@ namespace Umbraco.Core.Dynamics { //var list = context.Parent.Children.Select(n => new DynamicNode(n)); var list = context.Parent.Children; - context.OwnerList = new DynamicDocumentList(list); + context.OwnerList = new DynamicPublishedContentList(list); } if (context.OwnerList != null) { diff --git a/src/Umbraco.Core/Dynamics/DynamicQueryable.cs b/src/Umbraco.Core/Dynamics/DynamicQueryable.cs index 1f82b55d2b..0f31abacff 100644 --- a/src/Umbraco.Core/Dynamics/DynamicQueryable.cs +++ b/src/Umbraco.Core/Dynamics/DynamicQueryable.cs @@ -214,7 +214,7 @@ namespace Umbraco.Core.Dynamics //reroute each stacked Expression.Call into our own methods that know how to deal //with DynamicNode queryExpr = Expression.Call( - typeof(DynamicDocumentListOrdering), + typeof(DynamicPublishedContentListOrdering), o.Ascending ? methodAsc : methodDesc, null, queryExpr, diff --git a/src/Umbraco.Core/Dynamics/ExtensionMethods.cs b/src/Umbraco.Core/Dynamics/ExtensionMethods.cs index 69e1d877da..69689bf204 100644 --- a/src/Umbraco.Core/Dynamics/ExtensionMethods.cs +++ b/src/Umbraco.Core/Dynamics/ExtensionMethods.cs @@ -32,7 +32,7 @@ namespace Umbraco.Core.Dynamics } - public static DynamicDocumentList Random(this DynamicDocumentList all, int min, int max) + public static DynamicPublishedContentList Random(this DynamicPublishedContentList all, int min, int max) { //get a random number generator Random r = new Random(); @@ -41,13 +41,13 @@ namespace Umbraco.Core.Dynamics //Call the other method return Random(all, Number); } - public static DynamicDocumentList Random(this DynamicDocumentList all, int max) + public static DynamicPublishedContentList Random(this DynamicPublishedContentList all, int max) { //Randomly order the items in the set by a Guid, Take the correct number, and return this wrapped in a new DynamicNodeList - return new DynamicDocumentList(all.Items.OrderBy(x => Guid.NewGuid()).Take(max)); + return new DynamicPublishedContentList(all.Items.OrderBy(x => Guid.NewGuid()).Take(max)); } - public static DynamicPublishedContent Random(this DynamicDocumentList all) + public static DynamicPublishedContent Random(this DynamicPublishedContentList all) { return all.Items.OrderBy(x => Guid.NewGuid()).First(); } diff --git a/src/Umbraco.Core/Dynamics/Grouping.cs b/src/Umbraco.Core/Dynamics/Grouping.cs index ff7d83549f..d1122a6395 100644 --- a/src/Umbraco.Core/Dynamics/Grouping.cs +++ b/src/Umbraco.Core/Dynamics/Grouping.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Dynamics public IEnumerator GetEnumerator() { - var temp = new DynamicDocumentList(Elements.Cast()); + var temp = new DynamicPublishedContentList(Elements.Cast()); return (IEnumerator)temp.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() @@ -21,7 +21,7 @@ namespace Umbraco.Core.Dynamics return (IEnumerator)GetEnumerator(); } - public DynamicDocumentList OrderBy(string ordering) + public DynamicPublishedContentList OrderBy(string ordering) { bool descending = false; if (ordering.IndexOf(" descending", StringComparison.CurrentCultureIgnoreCase) >= 0) @@ -37,7 +37,7 @@ namespace Umbraco.Core.Dynamics if (!descending) { - return new DynamicDocumentList(Elements.OrderBy(item => + return new DynamicPublishedContentList(Elements.OrderBy(item => { object key = null; (item as DynamicObject).TryGetMember(new DynamicQueryableGetMemberBinder(ordering, false), out key); @@ -46,7 +46,7 @@ namespace Umbraco.Core.Dynamics } else { - return new DynamicDocumentList(Elements.OrderByDescending(item => + return new DynamicPublishedContentList(Elements.OrderByDescending(item => { object key = null; (item as DynamicObject).TryGetMember(new DynamicQueryableGetMemberBinder(ordering, false), out key); diff --git a/src/Umbraco.Core/Dynamics/IDynamicDocumentDataSource.cs b/src/Umbraco.Core/Dynamics/IDynamicPublishedContentDataSource.cs similarity index 89% rename from src/Umbraco.Core/Dynamics/IDynamicDocumentDataSource.cs rename to src/Umbraco.Core/Dynamics/IDynamicPublishedContentDataSource.cs index c84e1565ac..50f7eba1c2 100644 --- a/src/Umbraco.Core/Dynamics/IDynamicDocumentDataSource.cs +++ b/src/Umbraco.Core/Dynamics/IDynamicPublishedContentDataSource.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Dynamics /// and currently the business logic part of Umbraco is still in the legacy project and we don't want to move that to the core so in the /// meantime until the new APIs are made, we need to have this data source in place with a resolver which is set in the web project. /// - internal interface IDynamicDocumentDataSource + internal interface IDynamicPublishedContentDataSource { Guid GetDataType(string docTypeAlias, string propertyAlias); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index c19c511dd0..beedce14c7 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -69,11 +69,11 @@ - - - - - + + + + + @@ -95,7 +95,7 @@ - + diff --git a/src/Umbraco.Tests/DynamicDocument/DocumentTests.cs b/src/Umbraco.Tests/DynamicDocument/DocumentTests.cs index edf68d6b71..54c8f473ed 100644 --- a/src/Umbraco.Tests/DynamicDocument/DocumentTests.cs +++ b/src/Umbraco.Tests/DynamicDocument/DocumentTests.cs @@ -21,7 +21,7 @@ namespace Umbraco.Tests.DynamicDocument { base.Initialize(); //need to specify a different callback for testing - Umbraco.Web.DocumentExtensions.GetPropertyAliasesAndNames = s => + Umbraco.Web.PublishedContentExtensions.GetPropertyAliasesAndNames = s => { var userFields = new Dictionary() { @@ -64,7 +64,7 @@ namespace Umbraco.Tests.DynamicDocument public override void TearDown() { base.TearDown(); - Umbraco.Web.DocumentExtensions.GetPropertyAliasesAndNames = null; + Umbraco.Web.PublishedContentExtensions.GetPropertyAliasesAndNames = null; UmbracoContext.Current = null; } diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs index c1d3deb4b7..8722117bc1 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs @@ -17,14 +17,14 @@ namespace Umbraco.Tests.DynamicDocument { base.Initialize(); - DynamicDocumentDataSourceResolver.Current = new DynamicDocumentDataSourceResolver( - new TestDynamicDocumentDataSource()); + DynamicPublishedContentDataSourceResolver.Current = new DynamicPublishedContentDataSourceResolver( + new TestDynamicPublishedContentDataSource()); } public override void TearDown() { base.TearDown(); - DynamicDocumentDataSourceResolver.Reset(); + DynamicPublishedContentDataSourceResolver.Reset(); } protected override bool RequiresDbSetup @@ -32,7 +32,7 @@ namespace Umbraco.Tests.DynamicDocument get { return false; } } - private class TestDynamicDocumentDataSource : IDynamicDocumentDataSource + private class TestDynamicPublishedContentDataSource : IDynamicPublishedContentDataSource { public Guid GetDataType(string docTypeAlias, string propertyAlias) { diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentCustomExtensionMethods.cs b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs similarity index 84% rename from src/Umbraco.Tests/DynamicDocument/DynamicDocumentCustomExtensionMethods.cs rename to src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs index a1d55025dd..115497b366 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentCustomExtensionMethods.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs @@ -3,7 +3,7 @@ using Umbraco.Core.Dynamics; namespace Umbraco.Tests.DynamicDocument { - public static class DynamicDocumentCustomExtensionMethods + public static class DynamicPublishedContentCustomExtensionMethods { public static string DynamicDocumentNoParameters(this Core.Dynamics.DynamicPublishedContent doc) @@ -21,7 +21,7 @@ namespace Umbraco.Tests.DynamicDocument return custom + i + b; } - public static string DynamicDocumentListMultiParam(this DynamicDocumentList doc, string custom, int i, bool b) + public static string DynamicDocumentListMultiParam(this DynamicPublishedContentList doc, string custom, int i, bool b) { return custom + i + b; } diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTests.cs b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs similarity index 92% rename from src/Umbraco.Tests/DynamicDocument/DynamicDocumentTests.cs rename to src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs index 176b7a3b9d..837920d1c0 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTests.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs @@ -14,7 +14,7 @@ using umbraco.cms.businesslogic.web; namespace Umbraco.Tests.DynamicDocument { [TestFixture] - public class DynamicDocumentTests : DynamicDocumentTestsBase + public class DynamicPublishedContentTests : DynamicDocumentTestsBase { public override void Initialize() { diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index a55a13e47c..4d1b329bb3 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -63,8 +63,8 @@ - - + + diff --git a/src/Umbraco.Web/DefaultDynamicDocumentDataSource.cs b/src/Umbraco.Web/DefaultDynamicPublishedContentDataSource.cs similarity index 86% rename from src/Umbraco.Web/DefaultDynamicDocumentDataSource.cs rename to src/Umbraco.Web/DefaultDynamicPublishedContentDataSource.cs index 12e2e2e2ec..61300cac77 100644 --- a/src/Umbraco.Web/DefaultDynamicDocumentDataSource.cs +++ b/src/Umbraco.Web/DefaultDynamicPublishedContentDataSource.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web /// and currently the business logic part of Umbraco is still in the legacy project and we don't want to move that to the core so in the /// meantime until the new APIs are made, we need to have this data source in place with a resolver which is set in the web project. /// - internal class DefaultDynamicDocumentDataSource : IDynamicDocumentDataSource + internal class DefaultDynamicPublishedContentDataSource : IDynamicPublishedContentDataSource { public Guid GetDataType(string docTypeAlias, string propertyAlias) { diff --git a/src/Umbraco.Web/DynamicDocumentSearchExtensions.cs b/src/Umbraco.Web/DynamicPublishedContentSearchExtensions.cs similarity index 70% rename from src/Umbraco.Web/DynamicDocumentSearchExtensions.cs rename to src/Umbraco.Web/DynamicPublishedContentSearchExtensions.cs index 5babdca24b..169a666e37 100644 --- a/src/Umbraco.Web/DynamicDocumentSearchExtensions.cs +++ b/src/Umbraco.Web/DynamicPublishedContentSearchExtensions.cs @@ -6,9 +6,9 @@ namespace Umbraco.Web /// /// DynamicPublishedContent extension methods for searching using Examine /// - public static class DynamicDocumentSearchExtensions + public static class DynamicPublishedContentSearchExtensions { - public static DynamicDocumentList Search(this DynamicPublishedContent d, string term, bool useWildCards = true, string searchProvider = null) + public static DynamicPublishedContentList Search(this DynamicPublishedContent d, string term, bool useWildCards = true, string searchProvider = null) { var searcher = Examine.ExamineManager.Instance.DefaultSearchProvider; if (!string.IsNullOrEmpty(searchProvider)) @@ -24,12 +24,12 @@ namespace Umbraco.Web return d.Search(crit, searcher); } - public static DynamicDocumentList SearchDescendants(this DynamicPublishedContent d, string term, bool useWildCards = true, string searchProvider = null) + public static DynamicPublishedContentList SearchDescendants(this DynamicPublishedContent d, string term, bool useWildCards = true, string searchProvider = null) { return d.Search(term, useWildCards, searchProvider); } - public static DynamicDocumentList SearchChildren(this DynamicPublishedContent d, string term, bool useWildCards = true, string searchProvider = null) + public static DynamicPublishedContentList SearchChildren(this DynamicPublishedContent d, string term, bool useWildCards = true, string searchProvider = null) { var searcher = Examine.ExamineManager.Instance.DefaultSearchProvider; if (!string.IsNullOrEmpty(searchProvider)) @@ -45,7 +45,7 @@ namespace Umbraco.Web return d.Search(crit, searcher); } - public static DynamicDocumentList Search(this DynamicPublishedContent d, Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) + public static DynamicPublishedContentList Search(this DynamicPublishedContent d, Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) { var s = Examine.ExamineManager.Instance.DefaultSearchProvider; if (searchProvider != null) diff --git a/src/Umbraco.Web/ExamineExtensions.cs b/src/Umbraco.Web/ExamineExtensions.cs index 243230e03a..8898b491ff 100644 --- a/src/Umbraco.Web/ExamineExtensions.cs +++ b/src/Umbraco.Web/ExamineExtensions.cs @@ -12,7 +12,7 @@ namespace Umbraco.Web /// internal static class ExamineExtensions { - internal static DynamicDocumentList ConvertSearchResultToDynamicDocument( + internal static DynamicPublishedContentList ConvertSearchResultToDynamicDocument( this IEnumerable results, IPublishedStore store) { @@ -20,7 +20,7 @@ namespace Umbraco.Web // however thsi is currently not the case: // http://examine.codeplex.com/workitem/10350 - var list = new DynamicDocumentList(); + var list = new DynamicPublishedContentList(); var xd = new XmlDocument(); foreach (var result in results.OrderByDescending(x => x.Score)) diff --git a/src/Umbraco.Web/DocumentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs similarity index 96% rename from src/Umbraco.Web/DocumentExtensions.cs rename to src/Umbraco.Web/PublishedContentExtensions.cs index a2e783e1ec..bf05cde8a0 100644 --- a/src/Umbraco.Web/DocumentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -16,7 +16,7 @@ namespace Umbraco.Web /// These methods exist in the web project as we need access to web based classes like NiceUrl provider /// which is why they cannot exist in the Core project. /// - public static class DocumentExtensions + public static class PublishedContentExtensions { /// diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 1c6acc0197..cc3faaf33b 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -250,12 +250,12 @@ - + - - + + ASPXCodeBehind diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 8d4e5abdb1..348f954d93 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -463,7 +463,7 @@ namespace Umbraco.Web var nodes = ids.Select(eachId => DocumentById(eachId, store)) .Where(x => !TypeHelper.IsTypeAssignableFrom(x)) .Cast(); - return new DynamicDocumentList(nodes); + return new DynamicPublishedContentList(nodes); } private dynamic DocumentByIds(IPublishedStore store, params string[] ids) @@ -471,7 +471,7 @@ namespace Umbraco.Web var nodes = ids.Select(eachId => DocumentById(eachId, store)) .Where(x => !TypeHelper.IsTypeAssignableFrom(x)) .Cast(); - return new DynamicDocumentList(nodes); + return new DynamicPublishedContentList(nodes); } #endregion diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index 50654ba39c..a58b362f4b 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -228,7 +228,7 @@ namespace Umbraco.Web //This exists only because the new business logic classes aren't created yet and we want Dynamics in the Core project, //see the note in the DynamicNodeDataSourceResolver.cs class - DynamicDocumentDataSourceResolver.Current = new DynamicDocumentDataSourceResolver(new DefaultDynamicDocumentDataSource()); + DynamicPublishedContentDataSourceResolver.Current = new DynamicPublishedContentDataSourceResolver(new DefaultDynamicPublishedContentDataSource()); } } diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs index 0b8446739c..52fb4f53df 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs @@ -544,7 +544,7 @@ namespace umbraco.MacroEngines //contextAlias is the node which the property data was returned from //Guid dataType = ContentType.GetDataType(data.ContextAlias, data.Alias); //SD: replaced with our temporary resolver so that we can unit test this properly, this is what DynamicPublishedContent uses until we create our new data access layer. - var dataType = DynamicDocumentDataSourceResolver.Current.DataSource.GetDataType(data.ContextAlias, data.Alias); + var dataType = DynamicPublishedContentDataSourceResolver.Current.DataSource.GetDataType(data.ContextAlias, data.Alias); var staticMapping = UmbracoSettings.RazorDataTypeModelStaticMapping.FirstOrDefault(mapping => { diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicDocumentExtensions.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicPublishedContentExtensions.cs similarity index 94% rename from src/umbraco.MacroEngines/RazorDynamicNode/DynamicDocumentExtensions.cs rename to src/umbraco.MacroEngines/RazorDynamicNode/DynamicPublishedContentExtensions.cs index f53758e677..1221331f08 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicDocumentExtensions.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicPublishedContentExtensions.cs @@ -12,7 +12,7 @@ namespace umbraco.MacroEngines.Library /// /// Extension methods for converting DynamicPublishedContent to INode /// - internal static class DynamicDocumentExtensions + internal static class DynamicPublishedContentExtensions { internal static IProperty ConvertToNodeProperty(this IDocumentProperty prop) diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs b/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs index 7239330fb9..0a01b47cb1 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs @@ -144,7 +144,7 @@ namespace umbraco.MacroEngines.Library public dynamic Search(string term, bool useWildCards = true, string searchProvider = null) { //wraps the functionality in UmbracoHelper but still returns the legacy DynamicNodeList - var nodes = ((DynamicDocumentList)_umbracoHelper.Search(term, useWildCards, searchProvider)) + var nodes = ((DynamicPublishedContentList)_umbracoHelper.Search(term, useWildCards, searchProvider)) .Select(x => x.ConvertToNode()); return new DynamicNodeList(nodes); } @@ -152,7 +152,7 @@ namespace umbraco.MacroEngines.Library public dynamic Search(Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) { //wraps the functionality in UmbracoHelper but still returns the legacy DynamicNodeList - var nodes = ((DynamicDocumentList) _umbracoHelper.Search(criteria, searchProvider)) + var nodes = ((DynamicPublishedContentList) _umbracoHelper.Search(criteria, searchProvider)) .Select(x => x.ConvertToNode()); return new DynamicNodeList(nodes); } diff --git a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj index 38b25e1993..f8b48e64ca 100644 --- a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj +++ b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj @@ -86,7 +86,7 @@ - + From 831d1966dcfe1f1236673b586ca4978d5c125de2 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Tue, 2 Oct 2012 22:55:36 +0500 Subject: [PATCH 2/4] Added IEnumerable extensions to TypedContent, TypedMedia, Content, and Media methods --- src/Umbraco.Web/UmbracoHelper.cs | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 348f954d93..d660808d90 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -350,6 +350,16 @@ namespace Umbraco.Web return TypedDocumentsbyIds(PublishedContentStoreResolver.Current.PublishedContentStore, ids); } + public IEnumerable TypedContent(IEnumerable ids) + { + return TypedContent(ids.ToArray()); + } + + public IEnumerable TypedContent(IEnumerable ids) + { + return TypedContent(ids.ToArray()); + } + public dynamic Content(int id) { return DocumentById(id, PublishedContentStoreResolver.Current.PublishedContentStore); @@ -370,6 +380,16 @@ namespace Umbraco.Web return DocumentByIds(PublishedContentStoreResolver.Current.PublishedContentStore, ids); } + public dynamic Content(IEnumerable ids) + { + return Content(ids.ToArray()); + } + + public dynamic Content(IEnumerable ids) + { + return Content(ids.ToArray()); + } + #endregion #region Media @@ -394,6 +414,16 @@ namespace Umbraco.Web return TypedDocumentsbyIds(PublishedMediaStoreResolver.Current.PublishedMediaStore, ids); } + public IEnumerable TypedMedia(IEnumerable ids) + { + return TypedMedia(ids.ToArray()); + } + + public IEnumerable TypedMedia(IEnumerable ids) + { + return TypedMedia(ids.ToArray()); + } + public dynamic Media(int id) { return DocumentById(id, PublishedMediaStoreResolver.Current.PublishedMediaStore); @@ -414,6 +444,16 @@ namespace Umbraco.Web return DocumentByIds(PublishedMediaStoreResolver.Current.PublishedMediaStore, ids); } + public dynamic Media(IEnumerable ids) + { + return Media(ids.ToArray()); + } + + public dynamic Media(IEnumerable ids) + { + return Media(ids.ToArray()); + } + #endregion #region Used by Content/Media From c0102f1c71cbae77daa650d459f4b64e75eda8ca Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Thu, 4 Oct 2012 01:31:08 +0500 Subject: [PATCH 3/4] Refactored the traversal, ishelper, etc... methods to be extension methods on IPublishedContent so now all of these methods are available on the Typed object not just the dynamic object which makes a whole lot more sense... and you can have intellisense. Updated DynamicPublishedContent's methods to just proxy calls to the new extension methods so that all of the logic is contained in one place. Added new GetRootDocuments to the IPublishedContentStore since we need this in order to get the root list of documents for many of these methods. Fixed an issue with the DynamicNode to IPublishedContent converter. Fixed many of the failing unit tests. --- src/Umbraco.Core/Dynamics/DynamicGrouping.cs | 9 +- .../Dynamics/DynamicPublishedContent.cs | 1516 ----------------- ...namicPublishedContentDataSourceResolver.cs | 19 - ...namicPublishedContentIdEqualityComparer.cs | 7 +- .../DynamicPublishedContentListOrdering.cs | 151 +- .../Dynamics/DynamicPublishedContentWalker.cs | 263 --- src/Umbraco.Core/Dynamics/DynamicQueryable.cs | 57 +- src/Umbraco.Core/Dynamics/ExpressionParser.cs | 57 +- src/Umbraco.Core/Dynamics/ExtensionMethods.cs | 3 +- src/Umbraco.Core/Dynamics/Grouping.cs | 7 +- .../IDynamicPublishedContentDataSource.cs | 16 - .../Models/DynamicPublishedContentBase.cs | 843 +++++++++ .../DynamicPublishedContentList.cs | 67 +- ...sions.cs => PublishedContentExtensions.cs} | 7 +- src/Umbraco.Core/Umbraco.Core.csproj | 9 +- .../ContentStores/PublishContentStoreTests.cs | 10 + .../DynamicDocumentTestsBase.cs | 20 +- .../DynamicDocument/DynamicNodeTests.cs | 13 + ...cPublishedContentCustomExtensionMethods.cs | 9 +- .../DynamicPublishedContentTests.cs | 23 +- .../LookupByNiceUrlWithDomainsTests.cs | 20 +- .../NiceUrlsProviderWithDomainsTests.cs | 12 - .../Routing/UrlsWithNestedDomains.cs | 11 - .../Routing/uQueryGetNodeIdByUrlTests.cs | 44 +- ...efaultDynamicPublishedContentDataSource.cs | 21 - .../DefaultPublishedContentStore.cs | 6 + src/Umbraco.Web/DefaultPublishedMediaStore.cs | 15 + ...DynamicPublishedContentSearchExtensions.cs | 9 +- src/Umbraco.Web/ExamineExtensions.cs | 3 +- src/Umbraco.Web/IPublishedStore.cs | 2 + .../Models/DynamicPublishedContent.cs | 545 ++++++ src/Umbraco.Web/Mvc/RenderViewPage.cs | 3 +- src/Umbraco.Web/PublishedContentExtensions.cs | 670 +++++++- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- src/Umbraco.Web/UmbracoHelper.cs | 6 +- src/Umbraco.Web/WebBootManager.cs | 3 - .../RazorDynamicNode/DynamicNode.cs | 16 +- ...sions.cs => PublishedContentExtensions.cs} | 14 +- .../RazorDynamicNode/RazorLibraryCore.cs | 1 + .../umbraco.MacroEngines.csproj | 2 +- 40 files changed, 2391 insertions(+), 2120 deletions(-) delete mode 100644 src/Umbraco.Core/Dynamics/DynamicPublishedContent.cs delete mode 100644 src/Umbraco.Core/Dynamics/DynamicPublishedContentDataSourceResolver.cs delete mode 100644 src/Umbraco.Core/Dynamics/DynamicPublishedContentWalker.cs delete mode 100644 src/Umbraco.Core/Dynamics/IDynamicPublishedContentDataSource.cs create mode 100644 src/Umbraco.Core/Models/DynamicPublishedContentBase.cs rename src/Umbraco.Core/{Dynamics => Models}/DynamicPublishedContentList.cs (88%) rename src/Umbraco.Core/{DocumentExtensions.cs => PublishedContentExtensions.cs} (82%) delete mode 100644 src/Umbraco.Web/DefaultDynamicPublishedContentDataSource.cs create mode 100644 src/Umbraco.Web/Models/DynamicPublishedContent.cs rename src/umbraco.MacroEngines/RazorDynamicNode/{DynamicPublishedContentExtensions.cs => PublishedContentExtensions.cs} (86%) diff --git a/src/Umbraco.Core/Dynamics/DynamicGrouping.cs b/src/Umbraco.Core/Dynamics/DynamicGrouping.cs index 11c2b1df65..9acd8c54b7 100644 --- a/src/Umbraco.Core/Dynamics/DynamicGrouping.cs +++ b/src/Umbraco.Core/Dynamics/DynamicGrouping.cs @@ -1,12 +1,13 @@ using System.Collections.Generic; using System.Linq; using System.Collections; +using Umbraco.Core.Models; namespace Umbraco.Core.Dynamics { public class DynamicGrouping : IEnumerable { - internal IEnumerable> Inner; + internal IEnumerable> Inner; public DynamicGrouping OrderBy(string expression) { @@ -21,7 +22,7 @@ namespace Umbraco.Core.Dynamics .Select(node => { string predicate = groupBy; - var internalList = new DynamicPublishedContentList(new DynamicPublishedContent[] { node }); + var internalList = new DynamicPublishedContentList(new DynamicPublishedContentBase[] { node }); var query = (IQueryable)internalList.Select(predicate, new object[] { }); var key = query.FirstOrDefault(); return new @@ -32,13 +33,13 @@ namespace Umbraco.Core.Dynamics }) .Where(item => item.Key != null) .GroupBy(item => item.Key) - .Select(item => new Grouping() + .Select(item => new Grouping() { Key = item.Key, Elements = item.Select(inner => inner.Node) }); } - internal DynamicGrouping(IEnumerable> source) + internal DynamicGrouping(IEnumerable> source) { this.Inner = source; } diff --git a/src/Umbraco.Core/Dynamics/DynamicPublishedContent.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContent.cs deleted file mode 100644 index a747163293..0000000000 --- a/src/Umbraco.Core/Dynamics/DynamicPublishedContent.cs +++ /dev/null @@ -1,1516 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Dynamic; -using System.Linq; -using System.Web; -using Umbraco.Core.Configuration; -using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; -using umbraco.interfaces; -using System.Reflection; -using System.Xml.Linq; - -namespace Umbraco.Core.Dynamics -{ - - /// - /// The dynamic model for views - /// - public class DynamicPublishedContent : DynamicObject, IPublishedContent - { - private readonly IPublishedContent _publishedContent; - private DynamicPublishedContentList _cachedChildren; - private readonly ConcurrentDictionary _cachedMemberOutput = new ConcurrentDictionary(); - - internal DynamicPublishedContentList OwnerList { get; set; } - - #region Constructors - - public DynamicPublishedContent(IPublishedContent node) - { - if (node == null) throw new ArgumentNullException("node"); - _publishedContent = node; - } - - /// - /// Returns an empty/blank DynamicPublishedContent, this is used for special case scenarios - /// - /// - internal static DynamicPublishedContent Empty() - { - return new DynamicPublishedContent(); - } - - private DynamicPublishedContent() - { - } - - #endregion - - public dynamic AsDynamic() - { - return this; - } - - #region Traversal - public DynamicPublishedContent Up() - { - return DynamicPublishedContentWalker.Up(this); - } - public DynamicPublishedContent Up(int number) - { - return DynamicPublishedContentWalker.Up(this, number); - } - public DynamicPublishedContent Up(string nodeTypeAlias) - { - return DynamicPublishedContentWalker.Up(this, nodeTypeAlias); - } - public DynamicPublishedContent Down() - { - return DynamicPublishedContentWalker.Down(this); - } - public DynamicPublishedContent Down(int number) - { - return DynamicPublishedContentWalker.Down(this, number); - } - public DynamicPublishedContent Down(string nodeTypeAlias) - { - return DynamicPublishedContentWalker.Down(this, nodeTypeAlias); - } - public DynamicPublishedContent Next() - { - return DynamicPublishedContentWalker.Next(this); - } - public DynamicPublishedContent Next(int number) - { - return DynamicPublishedContentWalker.Next(this, number); - } - public DynamicPublishedContent Next(string nodeTypeAlias) - { - return DynamicPublishedContentWalker.Next(this, nodeTypeAlias); - } - - public DynamicPublishedContent Previous() - { - return DynamicPublishedContentWalker.Previous(this); - } - public DynamicPublishedContent Previous(int number) - { - return DynamicPublishedContentWalker.Previous(this, number); - } - public DynamicPublishedContent Previous(string nodeTypeAlias) - { - return DynamicPublishedContentWalker.Previous(this, nodeTypeAlias); - } - public DynamicPublishedContent Sibling(int number) - { - return DynamicPublishedContentWalker.Sibling(this, number); - } - public DynamicPublishedContent Sibling(string nodeTypeAlias) - { - return DynamicPublishedContentWalker.Sibling(this, nodeTypeAlias); - } - #endregion - - public bool HasProperty(string name) - { - if (_publishedContent != null) - { - try - { - var prop = GetUserProperty(name); - - return (prop != null); - } - catch (Exception) - { - return false; - } - } - return false; - } - - /// - /// Attempts to call a method on the dynamic object - /// - /// - /// - /// - /// - public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) - { - //TODO: We MUST cache the result here, it is very expensive to keep finding extension methods! - - try - { - //Property? - result = typeof(DynamicPublishedContent).InvokeMember(binder.Name, - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public | - System.Reflection.BindingFlags.GetProperty, - null, - this, - args); - return true; - } - catch (MissingMethodException) - { - try - { - //Static or Instance Method? - result = typeof(DynamicPublishedContent).InvokeMember(binder.Name, - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public | - System.Reflection.BindingFlags.Static | - System.Reflection.BindingFlags.InvokeMethod, - null, - this, - args); - return true; - } - catch (MissingMethodException) - { - try - { - result = ExecuteExtensionMethod(args, binder.Name); - return true; - } - catch (TargetInvocationException) - { - result = new DynamicNull(); - return true; - } - - catch - { - //TODO: LOg this! - - result = null; - return false; - } - - } - - - } - catch - { - result = null; - return false; - } - - } - - private object ExecuteExtensionMethod(object[] args, string name) - { - object result = null; - - var methodTypesToFind = new[] - { - typeof(DynamicPublishedContent) - }; - - //find known extension methods that match the first type in the list - MethodInfo toExecute = null; - foreach (var t in methodTypesToFind) - { - toExecute = ExtensionMethodFinder.FindExtensionMethod(t, args, name, false); - if (toExecute != null) - break; - } - - if (toExecute != null) - { - var genericArgs = (new[] { this }).Concat(args); - result = toExecute.Invoke(null, genericArgs.ToArray()); - } - else - { - throw new MissingMethodException(); - } - if (result != null) - { - if (result is IPublishedContent) - { - result = new DynamicPublishedContent((IPublishedContent)result); - } - if (result is IEnumerable) - { - result = new DynamicPublishedContentList((IEnumerable)result); - } - if (result is IEnumerable) - { - result = new DynamicPublishedContentList((IEnumerable)result); - } - } - return result; - } - - /// - /// Attempts to return a custom member (generally based on a string match) - /// - /// - /// - protected virtual Attempt TryGetCustomMember(GetMemberBinder binder) - { - if (binder.Name.InvariantEquals("ChildrenAsList") || binder.Name.InvariantEquals("Children")) - { - return new Attempt(true, Children); - } - - if (binder.Name.InvariantEquals("parentId")) - { - var parent = Parent; - if (parent == null) - { - throw new InvalidOperationException(string.Format("The node {0} does not have a parent", Id)); - } - return new Attempt(true, parent.Id); - } - return Attempt.False; - } - - /// - /// Attempts to return the children by the document type's alias (for example: CurrentPage.NewsItems where NewsItem is the - /// document type alias) - /// - /// - /// - /// - /// This method will work by both the plural and non-plural alias (i.e. NewsItem and NewsItems) - /// - protected virtual Attempt TryGetChildrenByAlias(GetMemberBinder binder) - { - - var filteredTypeChildren = _publishedContent.Children - .Where(x => x.DocumentTypeAlias.InvariantEquals(binder.Name) || x.DocumentTypeAlias.MakePluralName().InvariantEquals(binder.Name)) - .ToArray(); - if (filteredTypeChildren.Any()) - { - return new Attempt(true, - new DynamicPublishedContentList(filteredTypeChildren.Select(x => new DynamicPublishedContent(x)))); - } - return Attempt.False; - } - - /// - /// Attempts to return a member based on the reflected document property - /// - /// - /// - protected virtual Attempt TryGetDocumentProperty(GetMemberBinder binder) - { - - var reflectedProperty = GetReflectedProperty(binder.Name); - var result = reflectedProperty != null - ? reflectedProperty.Value - : null; - - return result == null - ? Attempt.False - : new Attempt(true, result); - } - - /// - /// Attempts to return a member based on a user defined umbraco property - /// - /// - /// - protected virtual Attempt TryGetUserProperty(GetMemberBinder binder) - { - var name = binder.Name; - var recursive = false; - if (name.StartsWith("_")) - { - name = name.Substring(1, name.Length - 1); - recursive = true; - } - - var userProperty = GetUserProperty(name, recursive); - - if (userProperty == null) - { - return Attempt.False; - } - - var result = userProperty.Value; - - if (_publishedContent.DocumentTypeAlias == null && userProperty.Alias == null) - { - throw new InvalidOperationException("No node alias or property alias available. Unable to look up the datatype of the property you are trying to fetch."); - } - - //get the data type id for the current property - var dataType = DynamicPublishedContentDataSourceResolver.Current.DataSource.GetDataType(userProperty.DocumentTypeAlias, userProperty.Alias); - - //convert the string value to a known type - var converted = ConvertPropertyValue(result, dataType, userProperty.DocumentTypeAlias, userProperty.Alias); - if (converted.Success) - { - result = converted.Result; - } - - return new Attempt(true, result); - } - - /// - /// Returns the member match methods in the correct order and is used in the TryGetMember method. - /// - /// - protected virtual IEnumerable>> GetMemberMatchMethods() - { - var memberMatchMethods = new List>> - { - b => TryGetCustomMember(b), //match custom members - b => TryGetUserProperty(b), //then match custom user defined umbraco properties - b => TryGetChildrenByAlias(b), //then try to match children based on doc type alias - b => TryGetDocumentProperty(b) //then try to match on a reflected document property - }; - return memberMatchMethods; - } - - /// - /// Try to return an object based on the dynamic member accessor - /// - /// - /// - /// - /// - /// TODO: SD: This will alwasy return true so that no exceptions are generated, this is only because this is how the - /// old DynamicNode worked, I'm not sure if this is the correct/expected functionality but I've left it like that. - /// IMO I think this is incorrect and it would be better to throw an exception for something that is not supported! - /// - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - if (binder == null) throw new ArgumentNullException("binder"); - - var name = binder.Name; - - //check the cache first! - if (_cachedMemberOutput.TryGetValue(name, out result)) - { - return true; - } - - //loop through each member match method and execute it. - //If it is successful, cache the result and return it. - foreach (var attempt in GetMemberMatchMethods() - .Select(m => m(binder)) - .Where(attempt => attempt.Success)) - { - result = attempt.Result; - //cache the result so we don't have to re-process the whole thing - _cachedMemberOutput.TryAdd(name, result); - return true; - } - - //if property access, type lookup and member invoke all failed - //at this point, we're going to return null - //instead, we return a DynamicNull - see comments in that file - //this will let things like Model.ChildItem work and return nothing instead of crashing - if (result == null) - { - //.Where explictly checks for this type - //and will make it false - //which means backwards equality (&& property != true) will pass - //forwwards equality (&& property or && property == true) will fail - result = new DynamicNull(); - } - - //alwasy return true if we haven't thrown an exception though I'm wondering if we return 'false' if .Net throws an exception for us?? - return true; - } - - /// - /// Returns a property defined on the document object as a member property using reflection - /// - /// - /// - private PropertyResult GetReflectedProperty(string alias) - { - return GetPropertyInternal(alias, _publishedContent, false); - } - - /// - /// Return a user defined property - /// - /// - /// - /// - private PropertyResult GetUserProperty(string alias, bool recursive = false) - { - if (!recursive) - { - return GetPropertyInternal(alias, _publishedContent); - } - var context = this; - var prop = GetPropertyInternal(alias, _publishedContent); - while (prop == null || !prop.HasValue()) - { - context = context.Parent; - if (context == null) break; - prop = context.GetPropertyInternal(alias, context._publishedContent); - } - return prop; - } - - - private PropertyResult GetPropertyInternal(string alias, IPublishedContent content, bool checkUserProperty = true) - { - if (alias.IsNullOrWhiteSpace()) throw new ArgumentNullException("alias"); - if (content == null) throw new ArgumentNullException("content"); - - //if we're looking for a user defined property - if (checkUserProperty) - { - var prop = content.GetProperty(alias); - - return prop == null - ? null - : new PropertyResult(prop, PropertyResultType.UserProperty) - { - DocumentTypeAlias = content.DocumentTypeAlias, - DocumentId = content.Id - }; - } - - //reflect - - Func> getMember = - memberAlias => - { - try - { - return new Attempt(true, - content.GetType().InvokeMember(memberAlias, - System.Reflection.BindingFlags.GetProperty | - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public, - null, - content, - null)); - } - catch (MissingMethodException ex) - { - return new Attempt(ex); - } - }; - - //try with the current casing - var attempt = getMember(alias); - if (!attempt.Success) - { - //if we cannot get with the current alias, try changing it's case - attempt = alias[0].IsUpperCase() - ? getMember(alias.ConvertCase(StringAliasCaseType.CamelCase)) - : getMember(alias.ConvertCase(StringAliasCaseType.PascalCase)); - } - - return !attempt.Success - ? null - : new PropertyResult(alias, attempt.Result, Guid.Empty, PropertyResultType.ReflectedProperty) - { - DocumentTypeAlias = content.DocumentTypeAlias, - DocumentId = content.Id - }; - } - - /// - /// Converts the currentValue to a correctly typed value based on known registered converters, then based on known standards. - /// - /// - /// - /// - /// - /// - private Attempt ConvertPropertyValue(object currentValue, Guid dataType, string docTypeAlias, string propertyTypeAlias) - { - if (currentValue == null) return Attempt.False; - - //First lets check all registered converters for this data type. - var converters = PropertyEditorValueConvertersResolver.Current.Converters - .Where(x => x.IsConverterFor(dataType, docTypeAlias, propertyTypeAlias)) - .ToArray(); - - //try to convert the value with any of the converters: - foreach (var converted in converters - .Select(p => p.ConvertPropertyValue(currentValue)) - .Where(converted => converted.Success)) - { - return new Attempt(true, converted.Result); - } - - //if none of the converters worked, then we'll process this from what we know - - var sResult = Convert.ToString(currentValue).Trim(); - - //this will eat csv strings, so only do it if the decimal also includes a decimal seperator (according to the current culture) - if (sResult.Contains(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator)) - { - decimal dResult; - if (decimal.TryParse(sResult, System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.CurrentCulture, out dResult)) - { - return new Attempt(true, dResult); - } - } - //process string booleans as booleans - if (sResult.InvariantEquals("true")) - { - return new Attempt(true, true); - } - if (sResult.InvariantEquals("false")) - { - return new Attempt(true, false); - } - - //a really rough check to see if this may be valid xml - //TODO: This is legacy code, I'm sure there's a better and nicer way - if (sResult.StartsWith("<") && sResult.EndsWith(">") && sResult.Contains("/")) - { - try - { - var e = XElement.Parse(DynamicXml.StripDashesInElementOrAttributeNames(sResult), LoadOptions.None); - - //check that the document element is not one of the disallowed elements - //allows RTE to still return as html if it's valid xhtml - var documentElement = e.Name.LocalName; - - //TODO: See note against this setting, pretty sure we don't need this - if (!UmbracoSettings.NotDynamicXmlDocumentElements.Any( - tag => string.Equals(tag, documentElement, StringComparison.CurrentCultureIgnoreCase))) - { - return new Attempt(true, new DynamicXml(e)); - } - return Attempt.False; - } - catch (Exception) - { - return Attempt.False; - } - } - return Attempt.False; - } - - //public DynamicNode Media(string propertyAlias) - //{ - // if (_n != null) - // { - // IProperty prop = _n.GetProperty(propertyAlias); - // if (prop != null) - // { - // int mediaNodeId; - // if (int.TryParse(prop.Value, out mediaNodeId)) - // { - // return _razorLibrary.Value.MediaById(mediaNodeId); - // } - // } - // return null; - // } - // return null; - //} - //public bool IsProtected - //{ - // get - // { - // if (_n != null) - // { - // return umbraco.library.IsProtected(_n.Id, _n.Path); - // } - // return false; - // } - //} - //public bool HasAccess - //{ - // get - // { - // if (_n != null) - // { - // return umbraco.library.HasAccess(_n.Id, _n.Path); - // } - // return true; - // } - //} - - //public string Media(string propertyAlias, string mediaPropertyAlias) - //{ - // if (_n != null) - // { - // IProperty prop = _n.GetProperty(propertyAlias); - // if (prop == null && propertyAlias.Substring(0, 1).ToUpper() == propertyAlias.Substring(0, 1)) - // { - // prop = _n.GetProperty(propertyAlias.Substring(0, 1).ToLower() + propertyAlias.Substring((1))); - // } - // if (prop != null) - // { - // int mediaNodeId; - // if (int.TryParse(prop.Value, out mediaNodeId)) - // { - // umbraco.cms.businesslogic.media.Media media = new cms.businesslogic.media.Media(mediaNodeId); - // if (media != null) - // { - // Property mprop = media.getProperty(mediaPropertyAlias); - // // check for nicer support of Pascal Casing EVEN if alias is camelCasing: - // if (prop == null && mediaPropertyAlias.Substring(0, 1).ToUpper() == mediaPropertyAlias.Substring(0, 1)) - // { - // mprop = media.getProperty(mediaPropertyAlias.Substring(0, 1).ToLower() + mediaPropertyAlias.Substring((1))); - // } - // if (mprop != null) - // { - // return string.Format("{0}", mprop.Value); - // } - // } - // } - // } - // } - // return null; - //} - - #region Ancestors, Descendants and Parent - public DynamicPublishedContent AncestorOrSelf() - { - //TODO: Why is this query like this?? - return AncestorOrSelf(node => node.Level == 1); - } - public DynamicPublishedContent AncestorOrSelf(int level) - { - return AncestorOrSelf(node => node.Level == level); - } - public DynamicPublishedContent AncestorOrSelf(string nodeTypeAlias) - { - return AncestorOrSelf(node => node.DocumentTypeAlias == nodeTypeAlias); - } - public DynamicPublishedContent AncestorOrSelf(Func func) - { - var node = this; - while (node != null) - { - if (func(node)) return node; - var parent = node.Parent; - if (parent != null) - { - if (this != parent) - { - node = parent; - } - else - { - return node; - } - } - else - { - return null; - } - } - return node; - } - public DynamicPublishedContentList AncestorsOrSelf(Func func) - { - var ancestorList = new List(); - var node = this; - ancestorList.Add(node); - while (node != null) - { - if (node.Level == 1) break; - var parent = node.Parent; - if (parent != null) - { - if (this != parent) - { - node = parent; - if (func(node)) - { - ancestorList.Add(node); - } - } - else - { - break; - } - } - else - { - break; - } - } - ancestorList.Reverse(); - return new DynamicPublishedContentList(ancestorList); - } - public DynamicPublishedContentList AncestorsOrSelf() - { - return AncestorsOrSelf(n => true); - } - public DynamicPublishedContentList AncestorsOrSelf(string nodeTypeAlias) - { - return AncestorsOrSelf(n => n.DocumentTypeAlias == nodeTypeAlias); - } - public DynamicPublishedContentList AncestorsOrSelf(int level) - { - return AncestorsOrSelf(n => n.Level <= level); - } - public DynamicPublishedContentList Descendants(string nodeTypeAlias) - { - return Descendants(p => p.DocumentTypeAlias == nodeTypeAlias); - } - public DynamicPublishedContentList Descendants(int level) - { - return Descendants(p => p.Level >= level); - } - public DynamicPublishedContentList Descendants() - { - return Descendants(n => true); - } - internal DynamicPublishedContentList Descendants(Func func) - { - var flattenedNodes = this._publishedContent.Children.Map(func, (IPublishedContent n) => n.Children); - return new DynamicPublishedContentList(flattenedNodes.ToList().ConvertAll(dynamicBackingItem => new DynamicPublishedContent(dynamicBackingItem))); - } - public DynamicPublishedContentList DescendantsOrSelf(int level) - { - return DescendantsOrSelf(p => p.Level >= level); - } - public DynamicPublishedContentList DescendantsOrSelf(string nodeTypeAlias) - { - return DescendantsOrSelf(p => p.DocumentTypeAlias == nodeTypeAlias); - } - public DynamicPublishedContentList DescendantsOrSelf() - { - return DescendantsOrSelf(p => true); - } - internal DynamicPublishedContentList DescendantsOrSelf(Func func) - { - if (this._publishedContent != null) - { - var thisNode = new List(); - if (func(this._publishedContent)) - { - thisNode.Add(this._publishedContent); - } - var flattenedNodes = this._publishedContent.Children.Map(func, (IPublishedContent n) => n.Children); - return new DynamicPublishedContentList(thisNode.Concat(flattenedNodes).ToList().ConvertAll(dynamicBackingItem => new DynamicPublishedContent(dynamicBackingItem))); - } - return new DynamicPublishedContentList(Enumerable.Empty()); - } - public DynamicPublishedContentList Ancestors(int level) - { - return Ancestors(n => n.Level <= level); - } - public DynamicPublishedContentList Ancestors(string nodeTypeAlias) - { - return Ancestors(n => n.DocumentTypeAlias == nodeTypeAlias); - } - public DynamicPublishedContentList Ancestors() - { - return Ancestors(n => true); - } - public DynamicPublishedContentList Ancestors(Func func) - { - var ancestorList = new List(); - var node = this; - while (node != null) - { - if (node.Level == 1) break; - var parent = node.Parent; - if (parent != null) - { - if (this != parent) - { - node = parent; - if (func(node)) - { - ancestorList.Add(node); - } - } - else - { - break; - } - } - else - { - break; - } - } - ancestorList.Reverse(); - return new DynamicPublishedContentList(ancestorList); - } - public DynamicPublishedContent Parent - { - get - { - if (_publishedContent.Parent != null) - { - return new DynamicPublishedContent(_publishedContent.Parent); - } - if (_publishedContent != null && _publishedContent.Id == 0) - { - return this; - } - return null; - } - } - #endregion - - public int TemplateId - { - get { return _publishedContent.TemplateId; } - } - - public int SortOrder - { - get { return _publishedContent.SortOrder; } - } - - public string Name - { - get { return _publishedContent.Name; } - } - - public bool Visible - { - get - { - - var umbracoNaviHide = GetUserProperty("umbracoNaviHide"); - if (umbracoNaviHide != null) - { - return umbracoNaviHide.Value.ToString().Trim() != "1"; - } - return true; - } - } - - public string UrlName - { - get { return _publishedContent.UrlName; } - } - - public string DocumentTypeAlias - { - get { return _publishedContent.DocumentTypeAlias; } - } - - public string WriterName - { - get { return _publishedContent.WriterName; } - } - - public string CreatorName - { - get { return _publishedContent.CreatorName; } - } - - public int WriterId - { - get { return _publishedContent.WriterId; } - } - - public int CreatorId - { - get { return _publishedContent.CreatorId; } - } - - public string Path - { - get { return _publishedContent.Path; } - } - - public DateTime CreateDate - { - get { return _publishedContent.CreateDate; } - } - - public int Id - { - get { return _publishedContent.Id; } - } - - public DateTime UpdateDate - { - get { return _publishedContent.UpdateDate; } - } - - public Guid Version - { - get { return _publishedContent.Version; } - } - - public int Level - { - get { return _publishedContent.Level; } - } - - public IEnumerable Properties - { - get { return _publishedContent.Properties; } - } - - public IEnumerable Children - { - get - { - if (_cachedChildren == null) - { - var children = _publishedContent.Children; - //testing, think this must be a special case for the root node ? - if (!children.Any() && _publishedContent.Id == 0) - { - _cachedChildren = new DynamicPublishedContentList(new List { new DynamicPublishedContent(this._publishedContent) }); - } - else - { - _cachedChildren = new DynamicPublishedContentList(_publishedContent.Children.Select(x => new DynamicPublishedContent(x))); - } - } - return _cachedChildren; - } - } - - #region GetProperty methods which can be used with the dynamic object - - public IDocumentProperty GetProperty(string alias) - { - return GetProperty(alias, false); - } - public IDocumentProperty GetProperty(string alias, bool recursive) - { - return alias.StartsWith("@") - ? GetReflectedProperty(alias.TrimStart('@')) - : GetUserProperty(alias, recursive); - } - public string GetPropertyValue(string alias) - { - return GetPropertyValue(alias, false); - } - public string GetPropertyValue(string alias, string fallback) - { - var prop = GetPropertyValue(alias); - return !prop.IsNullOrWhiteSpace() ? prop : fallback; - } - public string GetPropertyValue(string alias, bool recursive) - { - var p = alias.StartsWith("@") - ? GetReflectedProperty(alias.TrimStart('@')) - : GetUserProperty(alias, recursive); - return p == null ? null : p.ValueAsString; - } - public string GetPropertyValue(string alias, bool recursive, string fallback) - { - var prop = GetPropertyValue(alias, recursive); - return !prop.IsNullOrWhiteSpace() ? prop : fallback; - } - - #endregion - - #region HasValue - public bool HasValue(string alias) - { - return HasValue(alias, false); - } - public bool HasValue(string alias, bool recursive) - { - var prop = GetUserProperty(alias, recursive); - if (prop == null) return false; - return prop.HasValue(); - } - public IHtmlString HasValue(string alias, string valueIfTrue, string valueIfFalse) - { - return HasValue(alias, false) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); - } - public IHtmlString HasValue(string alias, bool recursive, string valueIfTrue, string valueIfFalse) - { - return HasValue(alias, recursive) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); - } - public IHtmlString HasValue(string alias, string valueIfTrue) - { - return HasValue(alias, false) ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); - } - public IHtmlString HasValue(string alias, bool recursive, string valueIfTrue) - { - return HasValue(alias, recursive) ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); - } - #endregion - - public int Position() - { - return this.Index(); - } - public int Index() - { - if (this.OwnerList == null && this.Parent != null) - { - //var list = this.Parent.Children.Select(n => new DynamicNode(n)); - var list = this.Parent.Children; - this.OwnerList = new DynamicPublishedContentList(list); - } - if (this.OwnerList != null) - { - List container = this.OwnerList.Items.ToList(); - int currentIndex = container.FindIndex(n => n.Id == this.Id); - if (currentIndex != -1) - { - return currentIndex; - } - else - { - throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicDocumentList but could not retrieve the index for it's position in the list", this.Id)); - } - } - else - { - throw new ArgumentNullException(string.Format("Node {0} has been orphaned and doesn't belong to a DynamicDocumentList", this.Id)); - } - } - - #region Is Helpers - public bool IsNull(string alias, bool recursive) - { - var prop = GetUserProperty(alias, recursive); - if (prop == null) return true; - return ((PropertyResult)prop).HasValue(); - } - public bool IsNull(string alias) - { - return IsNull(alias, false); - } - public bool IsFirst() - { - return IsHelper(n => n.Index() == 0); - } - public HtmlString IsFirst(string valueIfTrue) - { - return IsHelper(n => n.Index() == 0, valueIfTrue); - } - public HtmlString IsFirst(string valueIfTrue, string valueIfFalse) - { - return IsHelper(n => n.Index() == 0, valueIfTrue, valueIfFalse); - } - public bool IsNotFirst() - { - return !IsHelper(n => n.Index() == 0); - } - public HtmlString IsNotFirst(string valueIfTrue) - { - return IsHelper(n => n.Index() != 0, valueIfTrue); - } - public HtmlString IsNotFirst(string valueIfTrue, string valueIfFalse) - { - return IsHelper(n => n.Index() != 0, valueIfTrue, valueIfFalse); - } - public bool IsPosition(int index) - { - if (this.OwnerList == null) - { - return false; - } - return IsHelper(n => n.Index() == index); - } - public HtmlString IsPosition(int index, string valueIfTrue) - { - if (this.OwnerList == null) - { - return new HtmlString(string.Empty); - } - return IsHelper(n => n.Index() == index, valueIfTrue); - } - public HtmlString IsPosition(int index, string valueIfTrue, string valueIfFalse) - { - if (this.OwnerList == null) - { - return new HtmlString(valueIfFalse); - } - return IsHelper(n => n.Index() == index, valueIfTrue, valueIfFalse); - } - public bool IsModZero(int modulus) - { - if (this.OwnerList == null) - { - return false; - } - return IsHelper(n => n.Index() % modulus == 0); - } - public HtmlString IsModZero(int modulus, string valueIfTrue) - { - if (this.OwnerList == null) - { - return new HtmlString(string.Empty); - } - return IsHelper(n => n.Index() % modulus == 0, valueIfTrue); - } - public HtmlString IsModZero(int modulus, string valueIfTrue, string valueIfFalse) - { - if (this.OwnerList == null) - { - return new HtmlString(valueIfFalse); - } - return IsHelper(n => n.Index() % modulus == 0, valueIfTrue, valueIfFalse); - } - - public bool IsNotModZero(int modulus) - { - if (this.OwnerList == null) - { - return false; - } - return IsHelper(n => n.Index() % modulus != 0); - } - public HtmlString IsNotModZero(int modulus, string valueIfTrue) - { - if (this.OwnerList == null) - { - return new HtmlString(string.Empty); - } - return IsHelper(n => n.Index() % modulus != 0, valueIfTrue); - } - public HtmlString IsNotModZero(int modulus, string valueIfTrue, string valueIfFalse) - { - if (this.OwnerList == null) - { - return new HtmlString(valueIfFalse); - } - return IsHelper(n => n.Index() % modulus != 0, valueIfTrue, valueIfFalse); - } - public bool IsNotPosition(int index) - { - if (this.OwnerList == null) - { - return false; - } - return !IsHelper(n => n.Index() == index); - } - public HtmlString IsNotPosition(int index, string valueIfTrue) - { - if (this.OwnerList == null) - { - return new HtmlString(string.Empty); - } - return IsHelper(n => n.Index() != index, valueIfTrue); - } - public HtmlString IsNotPosition(int index, string valueIfTrue, string valueIfFalse) - { - if (this.OwnerList == null) - { - return new HtmlString(valueIfFalse); - } - return IsHelper(n => n.Index() != index, valueIfTrue, valueIfFalse); - } - public bool IsLast() - { - if (this.OwnerList == null) - { - return false; - } - int count = this.OwnerList.Count(); - return IsHelper(n => n.Index() == count - 1); - } - public HtmlString IsLast(string valueIfTrue) - { - if (this.OwnerList == null) - { - return new HtmlString(string.Empty); - } - int count = this.OwnerList.Count(); - return IsHelper(n => n.Index() == count - 1, valueIfTrue); - } - public HtmlString IsLast(string valueIfTrue, string valueIfFalse) - { - if (this.OwnerList == null) - { - return new HtmlString(valueIfFalse); - } - int count = this.OwnerList.Count(); - return IsHelper(n => n.Index() == count - 1, valueIfTrue, valueIfFalse); - } - public bool IsNotLast() - { - if (this.OwnerList == null) - { - return false; - } - int count = this.OwnerList.Count(); - return !IsHelper(n => n.Index() == count - 1); - } - public HtmlString IsNotLast(string valueIfTrue) - { - if (this.OwnerList == null) - { - return new HtmlString(string.Empty); - } - int count = this.OwnerList.Count(); - return IsHelper(n => n.Index() != count - 1, valueIfTrue); - } - public HtmlString IsNotLast(string valueIfTrue, string valueIfFalse) - { - if (this.OwnerList == null) - { - return new HtmlString(valueIfFalse); - } - int count = this.OwnerList.Count(); - return IsHelper(n => n.Index() != count - 1, valueIfTrue, valueIfFalse); - } - public bool IsEven() - { - return IsHelper(n => n.Index() % 2 == 0); - } - public HtmlString IsEven(string valueIfTrue) - { - return IsHelper(n => n.Index() % 2 == 0, valueIfTrue); - } - public HtmlString IsEven(string valueIfTrue, string valueIfFalse) - { - return IsHelper(n => n.Index() % 2 == 0, valueIfTrue, valueIfFalse); - } - public bool IsOdd() - { - return IsHelper(n => n.Index() % 2 == 1); - } - public HtmlString IsOdd(string valueIfTrue) - { - return IsHelper(n => n.Index() % 2 == 1, valueIfTrue); - } - public HtmlString IsOdd(string valueIfTrue, string valueIfFalse) - { - return IsHelper(n => n.Index() % 2 == 1, valueIfTrue, valueIfFalse); - } - public bool IsEqual(DynamicPublishedContent other) - { - return IsHelper(n => n.Id == other.Id); - } - public HtmlString IsEqual(DynamicPublishedContent other, string valueIfTrue) - { - return IsHelper(n => n.Id == other.Id, valueIfTrue); - } - public HtmlString IsEqual(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) - { - return IsHelper(n => n.Id == other.Id, valueIfTrue, valueIfFalse); - } - public bool IsNotEqual(DynamicPublishedContent other) - { - return IsHelper(n => n.Id != other.Id); - } - public HtmlString IsNotEqual(DynamicPublishedContent other, string valueIfTrue) - { - return IsHelper(n => n.Id != other.Id, valueIfTrue); - } - public HtmlString IsNotEqual(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) - { - return IsHelper(n => n.Id != other.Id, valueIfTrue, valueIfFalse); - } - public bool IsDescendant(DynamicPublishedContent other) - { - var ancestors = this.Ancestors(); - return IsHelper(n => ancestors.Items.Find(ancestor => ancestor.Id == other.Id) != null); - } - public HtmlString IsDescendant(DynamicPublishedContent other, string valueIfTrue) - { - var ancestors = this.Ancestors(); - return IsHelper(n => ancestors.Items.Find(ancestor => ancestor.Id == other.Id) != null, valueIfTrue); - } - public HtmlString IsDescendant(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) - { - var ancestors = this.Ancestors(); - return IsHelper(n => ancestors.Items.Find(ancestor => ancestor.Id == other.Id) != null, valueIfTrue, valueIfFalse); - } - public bool IsDescendantOrSelf(DynamicPublishedContent other) - { - var ancestors = this.AncestorsOrSelf(); - return IsHelper(n => ancestors.Items.Find(ancestor => ancestor.Id == other.Id) != null); - } - public HtmlString IsDescendantOrSelf(DynamicPublishedContent other, string valueIfTrue) - { - var ancestors = this.AncestorsOrSelf(); - return IsHelper(n => ancestors.Items.Find(ancestor => ancestor.Id == other.Id) != null, valueIfTrue); - } - public HtmlString IsDescendantOrSelf(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) - { - var ancestors = this.AncestorsOrSelf(); - return IsHelper(n => ancestors.Items.Find(ancestor => ancestor.Id == other.Id) != null, valueIfTrue, valueIfFalse); - } - public bool IsAncestor(DynamicPublishedContent other) - { - var descendants = this.Descendants(); - return IsHelper(n => descendants.Items.Find(descendant => descendant.Id == other.Id) != null); - } - public HtmlString IsAncestor(DynamicPublishedContent other, string valueIfTrue) - { - var descendants = this.Descendants(); - return IsHelper(n => descendants.Items.Find(descendant => descendant.Id == other.Id) != null, valueIfTrue); - } - public HtmlString IsAncestor(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) - { - var descendants = this.Descendants(); - return IsHelper(n => descendants.Items.Find(descendant => descendant.Id == other.Id) != null, valueIfTrue, valueIfFalse); - } - public bool IsAncestorOrSelf(DynamicPublishedContent other) - { - var descendants = this.DescendantsOrSelf(); - return IsHelper(n => descendants.Items.Find(descendant => descendant.Id == other.Id) != null); - } - public HtmlString IsAncestorOrSelf(DynamicPublishedContent other, string valueIfTrue) - { - var descendants = this.DescendantsOrSelf(); - return IsHelper(n => descendants.Items.Find(descendant => descendant.Id == other.Id) != null, valueIfTrue); - } - public HtmlString IsAncestorOrSelf(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) - { - var descendants = this.DescendantsOrSelf(); - return IsHelper(n => descendants.Items.Find(descendant => descendant.Id == other.Id) != null, valueIfTrue, valueIfFalse); - } - private bool IsHelper(Func test) - { - return test(this); - } - private HtmlString IsHelper(Func test, string valueIfTrue) - { - return IsHelper(test, valueIfTrue, string.Empty); - } - private HtmlString IsHelper(Func test, string valueIfTrue, string valueIfFalse) - { - return test(this) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); - } - #endregion - - #region Where - - public HtmlString Where(string predicate, string valueIfTrue) - { - return Where(predicate, valueIfTrue, string.Empty); - } - public HtmlString Where(string predicate, string valueIfTrue, string valueIfFalse) - { - if (Where(predicate)) - { - return new HtmlString(valueIfTrue); - } - return new HtmlString(valueIfFalse); - } - public bool Where(string predicate) - { - //Totally gonna cheat here - var dynamicDocumentList = new DynamicPublishedContentList(); - dynamicDocumentList.Add(this); - var filtered = dynamicDocumentList.Where(predicate); - if (Queryable.Count(filtered) == 1) - { - //this node matches the predicate - return true; - } - return false; - } - - #endregion - - //TODO: need a method to return a string value for a user property regardless of xml content or data type thus bypassing all of the PropertyEditorValueConverters - ///// - ///// Returns the value as as string regardless of xml content or data type - ///// - ///// - //public override string ToString() - //{ - // return base.ToString(); - //} - - #region Explicit IPublishedContent implementation - IPublishedContent IPublishedContent.Parent - { - get { return _publishedContent.Parent; } - } - - int IPublishedContent.Id - { - get { return _publishedContent.Id; } - } - - int IPublishedContent.TemplateId - { - get { return _publishedContent.TemplateId; } - } - - int IPublishedContent.SortOrder - { - get { return _publishedContent.SortOrder; } - } - - string IPublishedContent.Name - { - get { return _publishedContent.Name; } - } - - string IPublishedContent.UrlName - { - get { return _publishedContent.UrlName; } - } - - string IPublishedContent.DocumentTypeAlias - { - get { return _publishedContent.DocumentTypeAlias; } - } - - int IPublishedContent.DocumentTypeId - { - get { return _publishedContent.DocumentTypeId; } - } - - string IPublishedContent.WriterName - { - get { return _publishedContent.WriterName; } - } - - string IPublishedContent.CreatorName - { - get { return _publishedContent.CreatorName; } - } - - int IPublishedContent.WriterId - { - get { return _publishedContent.WriterId; } - } - - int IPublishedContent.CreatorId - { - get { return _publishedContent.CreatorId; } - } - - string IPublishedContent.Path - { - get { return _publishedContent.Path; } - } - - DateTime IPublishedContent.CreateDate - { - get { return _publishedContent.CreateDate; } - } - - DateTime IPublishedContent.UpdateDate - { - get { return _publishedContent.UpdateDate; } - } - - Guid IPublishedContent.Version - { - get { return _publishedContent.Version; } - } - - int IPublishedContent.Level - { - get { return _publishedContent.Level; } - } - - System.Collections.ObjectModel.Collection IPublishedContent.Properties - { - get { return _publishedContent.Properties; } - } - - IEnumerable IPublishedContent.Children - { - get { return _publishedContent.Children; } - } - - IDocumentProperty IPublishedContent.GetProperty(string alias) - { - return _publishedContent.GetProperty(alias); - } - #endregion - } -} diff --git a/src/Umbraco.Core/Dynamics/DynamicPublishedContentDataSourceResolver.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContentDataSourceResolver.cs deleted file mode 100644 index 79aa4a5ed8..0000000000 --- a/src/Umbraco.Core/Dynamics/DynamicPublishedContentDataSourceResolver.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Umbraco.Core.ObjectResolution; - -namespace Umbraco.Core.Dynamics -{ - /// - /// This exists only because we want Dynamics in the Core project but DynamicNode has references to ContentType to run some queries - /// and currently the business logic part of Umbraco is still in the legacy project and we don't want to move that to the core so in the - /// meantime until the new APIs are made, we need to have this data source in place with a resolver which is set in the web project. - /// - internal class DynamicPublishedContentDataSourceResolver : SingleObjectResolverBase - { - public IDynamicPublishedContentDataSource DataSource { get; private set; } - - public DynamicPublishedContentDataSourceResolver(IDynamicPublishedContentDataSource dataSource) - { - DataSource = dataSource; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Dynamics/DynamicPublishedContentIdEqualityComparer.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContentIdEqualityComparer.cs index 54cea8d0cf..663684b9e4 100644 --- a/src/Umbraco.Core/Dynamics/DynamicPublishedContentIdEqualityComparer.cs +++ b/src/Umbraco.Core/Dynamics/DynamicPublishedContentIdEqualityComparer.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; +using Umbraco.Core.Models; namespace Umbraco.Core.Dynamics { - internal class DynamicPublishedContentIdEqualityComparer : EqualityComparer + internal class DynamicPublishedContentIdEqualityComparer : EqualityComparer { - public override bool Equals(DynamicPublishedContent x, DynamicPublishedContent y) + public override bool Equals(DynamicPublishedContentBase x, DynamicPublishedContentBase y) { //Check whether the compared objects reference the same data. if (Object.ReferenceEquals(x, y)) return true; @@ -20,7 +21,7 @@ namespace Umbraco.Core.Dynamics } - public override int GetHashCode(DynamicPublishedContent obj) + public override int GetHashCode(DynamicPublishedContentBase obj) { if (Object.ReferenceEquals(obj, null)) return 0; diff --git a/src/Umbraco.Core/Dynamics/DynamicPublishedContentListOrdering.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContentListOrdering.cs index c56a3b271b..872c393f5b 100644 --- a/src/Umbraco.Core/Dynamics/DynamicPublishedContentListOrdering.cs +++ b/src/Umbraco.Core/Dynamics/DynamicPublishedContentListOrdering.cs @@ -2,18 +2,19 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using Umbraco.Core.Models; namespace Umbraco.Core.Dynamics { internal static class DynamicPublishedContentListOrdering { - private static TOut Reduce(Func func, DynamicPublishedContent publishedContent) + private static TOut Reduce(Func func, DynamicPublishedContentBase publishedContent) { var value = func(publishedContent); - while (value is Func) + while (value is Func) { - value = (value as Func)(publishedContent); + value = (value as Func)(publishedContent); } //when you're sorting a list of properties //and one of those properties doesn't exist, it will come back as DynamicNull @@ -37,209 +38,209 @@ namespace Umbraco.Core.Dynamics } return (TOut)value; } - public static IOrderedQueryable OrderBy(object source, object key) + internal static IOrderedQueryable OrderBy(object source, object key) { - IEnumerable typedSource = source as IEnumerable; + IEnumerable typedSource = source as IEnumerable; LambdaExpression lambda = key as LambdaExpression; //if the lambda we have returns an actual property, not a dynamic one, //then the TOut of the func will be the actual type, not object //Func func = (Func)lambda.Compile(); var func = lambda.Compile(); var TOut = func.GetType().GetGenericArguments()[1]; - IOrderedQueryable result = null; - if (TOut == typeof(Func)) + IOrderedQueryable result = null; + if (TOut == typeof(Func)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(object)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(bool)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(decimal)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(int)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(string)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(DateTime)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } return result; } - public static IOrderedQueryable ThenBy(object source, object key) + internal static IOrderedQueryable ThenBy(object source, object key) { - IOrderedQueryable typedSource = source as IOrderedQueryable; + IOrderedQueryable typedSource = source as IOrderedQueryable; LambdaExpression lambda = key as LambdaExpression; var func = lambda.Compile(); var TOut = func.GetType().GetGenericArguments()[1]; - IOrderedQueryable result = null; - if (TOut == typeof(Func)) + IOrderedQueryable result = null; + if (TOut == typeof(Func)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(object)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(bool)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(decimal)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(int)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(string)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(DateTime)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } return result; } - public static IOrderedQueryable OrderByDescending(object source, object key) + internal static IOrderedQueryable OrderByDescending(object source, object key) { - IEnumerable typedSource = source as IEnumerable; + IEnumerable typedSource = source as IEnumerable; LambdaExpression lambda = key as LambdaExpression; var func = lambda.Compile(); var TOut = func.GetType().GetGenericArguments()[1]; - IOrderedQueryable result = null; - if (TOut == typeof(Func)) + IOrderedQueryable result = null; + if (TOut == typeof(Func)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(object)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(bool)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(decimal)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(int)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(string)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(DateTime)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } return result; } - public static IOrderedQueryable ThenByDescending(object source, object key) + internal static IOrderedQueryable ThenByDescending(object source, object key) { - IOrderedQueryable typedSource = source as IOrderedQueryable; + IOrderedQueryable typedSource = source as IOrderedQueryable; LambdaExpression lambda = key as LambdaExpression; var func = lambda.Compile(); var TOut = func.GetType().GetGenericArguments()[1]; - IOrderedQueryable result = null; - if (TOut == typeof(Func)) + IOrderedQueryable result = null; + if (TOut == typeof(Func)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(object)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(bool)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(decimal)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(int)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(string)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(DateTime)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } return result; diff --git a/src/Umbraco.Core/Dynamics/DynamicPublishedContentWalker.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContentWalker.cs deleted file mode 100644 index 26c3a8dee8..0000000000 --- a/src/Umbraco.Core/Dynamics/DynamicPublishedContentWalker.cs +++ /dev/null @@ -1,263 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Umbraco.Core.Dynamics -{ - internal static class DynamicPublishedContentWalker - { - public static DynamicPublishedContent Up(this DynamicPublishedContent context) - { - return context.Up(0); - } - public static DynamicPublishedContent Up(this DynamicPublishedContent context, int number) - { - if (number == 0) - { - return context.Parent; - } - else - { - while ((context = context.Parent) != null && --number >= 0) ; - return context; - } - } - public static DynamicPublishedContent Up(this DynamicPublishedContent context, string nodeTypeAlias) - { - if (string.IsNullOrEmpty(nodeTypeAlias)) - { - return context.Parent; - } - else - { - while ((context = context.Parent) != null && context.DocumentTypeAlias != nodeTypeAlias) ; - return context; - } - } - - public static DynamicPublishedContent Down(this DynamicPublishedContent context) - { - return context.Down(0); - } - public static DynamicPublishedContent Down(this DynamicPublishedContent context, int number) - { - var children = new DynamicPublishedContentList(context.Children); - if (number == 0) - { - return children.Items.First(); - } - else - { - DynamicPublishedContent working = context; - while (number-- >= 0) - { - working = children.Items.First(); - children = new DynamicPublishedContentList(working.Children); - } - return working; - } - } - public static DynamicPublishedContent Down(this DynamicPublishedContent context, string nodeTypeAlias) - { - - if (string.IsNullOrEmpty(nodeTypeAlias)) - { - var children = new DynamicPublishedContentList(context.Children); - return children.Items.First(); - } - else - { - return context.Descendants(nodeTypeAlias).Items.FirstOrDefault(); - } - } - public static DynamicPublishedContent Next(this DynamicPublishedContent context) - { - return context.Next(0); - } - public static DynamicPublishedContent Next(this DynamicPublishedContent context, int number) - { - if (context.OwnerList == null && context.Parent != null) - { - //var list = context.Parent.Children.Select(n => new DynamicNode(n)); - var list = context.Parent.Children; - context.OwnerList = new DynamicPublishedContentList(list); - } - if (context.OwnerList != null) - { - var container = context.OwnerList.Items.ToList(); - var currentIndex = container.FindIndex(n => n.Id == context.Id); - if (currentIndex != -1) - { - return container.ElementAtOrDefault(currentIndex + (number + 1)); - } - else - { - throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", context.Id)); - } - } - else - { - throw new ArgumentNullException(string.Format("Node {0} has been orphaned and doesn't belong to a DynamicNodeList", context.Id)); - } - } - public static DynamicPublishedContent Sibling(this DynamicPublishedContent context, int number) - { - if (context.OwnerList == null && context.Parent != null) - { - //var list = context.Parent.Children.Select(n => new DynamicNode(n)); - var list = context.Parent.Children; - context.OwnerList = new DynamicPublishedContentList(list); - } - if (context.OwnerList != null) - { - var container = context.OwnerList.Items.ToList(); - var currentIndex = container.FindIndex(n => n.Id == context.Id); - if (currentIndex != -1) - { - return container.ElementAtOrDefault(currentIndex + number); - } - else - { - throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", context.Id)); - } - } - else - { - throw new ArgumentNullException(string.Format("Node {0} has been orphaned and doesn't belong to a DynamicNodeList", context.Id)); - } - } - public static DynamicPublishedContent Sibling(this DynamicPublishedContent context, string nodeTypeAlias) - { - if (context.OwnerList == null && context.Parent != null) - { - //var list = context.Parent.Children.Select(n => new DynamicNode(n)); - var list = context.Parent.Children; - context.OwnerList = new DynamicPublishedContentList(list); - } - if (context.OwnerList != null) - { - var container = context.OwnerList.Items.ToList(); - var currentIndex = container.FindIndex(n => n.Id == context.Id); - if (currentIndex != -1) - { - var workingIndex = currentIndex + 1; - while (workingIndex != currentIndex) - { - var working = container.ElementAtOrDefault(workingIndex); - if (working != null && working.DocumentTypeAlias == nodeTypeAlias) - { - return working; - } - workingIndex++; - if (workingIndex > container.Count) - { - workingIndex = 0; - } - } - return null; - } - else - { - throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", context.Id)); - } - } - else - { - throw new ArgumentNullException(string.Format("Node {0} has been orphaned and doesn't belong to a DynamicNodeList", context.Id)); - } - } - public static DynamicPublishedContent Next(this DynamicPublishedContent context, string nodeTypeAlias) - { - if (context.OwnerList == null && context.Parent != null) - { - //var list = context.Parent.Children.Select(n => new DynamicNode(n)); - var list = context.Parent.Children; - context.OwnerList = new DynamicPublishedContentList(list); - } - if (context.OwnerList != null) - { - var container = context.OwnerList.Items.ToList(); - var currentIndex = container.FindIndex(n => n.Id == context.Id); - if (currentIndex != -1) - { - var newIndex = container.FindIndex(currentIndex, n => n.DocumentTypeAlias == nodeTypeAlias); - if (newIndex != -1) - { - return container.ElementAt(newIndex); - } - return null; - } - else - { - throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", context.Id)); - } - } - else - { - throw new ArgumentNullException(string.Format("Node {0} has been orphaned and doesn't belong to a DynamicNodeList", context.Id)); - } - } - public static DynamicPublishedContent Previous(this DynamicPublishedContent context) - { - return context.Previous(0); - } - public static DynamicPublishedContent Previous(this DynamicPublishedContent context, int number) - { - if (context.OwnerList == null && context.Parent != null) - { - //var list = context.Parent.Children.Select(n => new DynamicNode(n)); - var list = context.Parent.Children; - context.OwnerList = new DynamicPublishedContentList(list); - } - if (context.OwnerList != null) - { - List container = context.OwnerList.Items.ToList(); - int currentIndex = container.FindIndex(n => n.Id == context.Id); - if (currentIndex != -1) - { - return container.ElementAtOrDefault(currentIndex + (number - 1)); - } - else - { - throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", context.Id)); - } - } - else - { - throw new ArgumentNullException(string.Format("Node {0} has been orphaned and doesn't belong to a DynamicNodeList", context.Id)); - } - } - public static DynamicPublishedContent Previous(this DynamicPublishedContent context, string nodeTypeAlias) - { - if (context.OwnerList == null && context.Parent != null) - { - //var list = context.Parent.Children.Select(n => new DynamicNode(n)); - var list = context.Parent.Children; - context.OwnerList = new DynamicPublishedContentList(list); - } - if (context.OwnerList != null) - { - List container = context.OwnerList.Items.ToList(); - int currentIndex = container.FindIndex(n => n.Id == context.Id); - if (currentIndex != -1) - { - var previousNodes = container.Take(currentIndex).ToList(); - int newIndex = previousNodes.FindIndex(n => n.DocumentTypeAlias == nodeTypeAlias); - if (newIndex != -1) - { - return container.ElementAt(newIndex); - } - return null; - } - else - { - throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", context.Id)); - } - } - else - { - throw new ArgumentNullException(string.Format("Node {0} has been orphaned and doesn't belong to a DynamicNodeList", context.Id)); - } - } - } -} diff --git a/src/Umbraco.Core/Dynamics/DynamicQueryable.cs b/src/Umbraco.Core/Dynamics/DynamicQueryable.cs index 0f31abacff..1552ce98f5 100644 --- a/src/Umbraco.Core/Dynamics/DynamicQueryable.cs +++ b/src/Umbraco.Core/Dynamics/DynamicQueryable.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Diagnostics; +using Umbraco.Core.Models; namespace Umbraco.Core.Dynamics { @@ -21,22 +22,22 @@ namespace Umbraco.Core.Dynamics if (source == null) throw new ArgumentNullException("source"); if (predicate == null) throw new ArgumentNullException("predicate"); LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, true, values); - if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContent)) + if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContentBase)) { //source list is DynamicNode and the lambda returns a Func - IQueryable typedSource = source as IQueryable; + IQueryable typedSource = source as IQueryable; var compiledFunc = lambda.Compile(); - Func func = null; - Func boolFunc = null; - if (compiledFunc is Func) + Func func = null; + Func boolFunc = null; + if (compiledFunc is Func) { - func = (Func)compiledFunc; + func = (Func)compiledFunc; } - if (compiledFunc is Func) + if (compiledFunc is Func) { - boolFunc = (Func)compiledFunc; + boolFunc = (Func)compiledFunc; } - return typedSource.Where(delegate(DynamicPublishedContent node) + return typedSource.Where(delegate(DynamicPublishedContentBase node) { object value = -1; //value = func(node); @@ -46,13 +47,13 @@ namespace Umbraco.Core.Dynamics if (func != null) { var firstFuncResult = func(node); - if (firstFuncResult is Func) + if (firstFuncResult is Func) { - value = (firstFuncResult as Func)(node); + value = (firstFuncResult as Func)(node); } - if (firstFuncResult is Func) + if (firstFuncResult is Func) { - value = (firstFuncResult as Func)(node); + value = (firstFuncResult as Func)(node); } if (firstFuncResult is bool) { @@ -86,28 +87,28 @@ namespace Umbraco.Core.Dynamics } } - public static IQueryable Select(this IQueryable source, string selector, params object[] values) + public static IQueryable Select(this IQueryable source, string selector, params object[] values) { if (source == null) throw new ArgumentNullException("source"); if (selector == null) throw new ArgumentNullException("selector"); LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(object), selector, false, values); - if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContent)) + if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContentBase)) { //source list is DynamicNode and the lambda returns a Func - IQueryable typedSource = source as IQueryable; + IQueryable typedSource = source as IQueryable; var compiledFunc = lambda.Compile(); - Func func = null; - if (compiledFunc is Func) + Func func = null; + if (compiledFunc is Func) { - func = (Func)compiledFunc; + func = (Func)compiledFunc; } - return typedSource.Select(delegate(DynamicPublishedContent node) + return typedSource.Select(delegate(DynamicPublishedContentBase node) { object value = null; value = func(node); - if (value is Func) + if (value is Func) { - var innerValue = (value as Func)(node); + var innerValue = (value as Func)(node); return innerValue; } return value; @@ -133,7 +134,7 @@ namespace Umbraco.Core.Dynamics if (source == null) throw new ArgumentNullException("source"); if (ordering == null) throw new ArgumentNullException("ordering"); - IQueryable typedSource = source as IQueryable; + IQueryable typedSource = source as IQueryable; if (!ordering.Contains(",")) { bool descending = false; @@ -149,10 +150,10 @@ namespace Umbraco.Core.Dynamics } LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(object), ordering, false, values); - if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContent)) + if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContentBase)) { //source list is DynamicNode and the lambda returns a Func - Func func = (Func)lambda.Compile(); + Func func = (Func)lambda.Compile(); //get the values out var query = typedSource.ToList().ConvertAll(item => new { node = item, key = EvaluateDynamicNodeFunc(item, func) }); if (query.Count == 0) @@ -246,13 +247,13 @@ namespace Umbraco.Core.Dynamics return null; } } - private static object EvaluateDynamicNodeFunc(DynamicPublishedContent publishedContent, Func func) + private static object EvaluateDynamicNodeFunc(DynamicPublishedContentBase publishedContent, Func func) { object value = -1; var firstFuncResult = func(publishedContent); - if (firstFuncResult is Func) + if (firstFuncResult is Func) { - value = (firstFuncResult as Func)(publishedContent); + value = (firstFuncResult as Func)(publishedContent); } if (firstFuncResult.GetType().IsValueType || firstFuncResult is string) { diff --git a/src/Umbraco.Core/Dynamics/ExpressionParser.cs b/src/Umbraco.Core/Dynamics/ExpressionParser.cs index b8dff83b78..78b2e0fafd 100644 --- a/src/Umbraco.Core/Dynamics/ExpressionParser.cs +++ b/src/Umbraco.Core/Dynamics/ExpressionParser.cs @@ -4,6 +4,7 @@ using System.Dynamic; using System.Linq; using System.Linq.Expressions; using System.Reflection; +using Umbraco.Core.Models; namespace Umbraco.Core.Dynamics { @@ -507,7 +508,7 @@ namespace Umbraco.Core.Dynamics (expr as LambdaExpression).Parameters.CopyTo(parameters, 0); var invokedExpr = Expression.Invoke(expr, parameters); var not = Expression.Not(Expression.TypeAs(invokedExpr, typeof(Nullable))); - expr = Expression.Lambda>( + expr = Expression.Lambda>( Expression.Condition( Expression.Property(not, "HasValue"), Expression.Property(not, "Value"), @@ -854,11 +855,11 @@ namespace Umbraco.Core.Dynamics Expression[] args = ParseArgumentList(); MethodBase mb; LambdaExpression instanceAsString = null; - ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicPublishedContent), "instance"); + ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicPublishedContentBase), "instance"); if (type.IsGenericType && type != typeof(string)) { var typeArgs = type.GetGenericArguments(); - if (typeArgs[0] == typeof(DynamicPublishedContent)) + if (typeArgs[0] == typeof(DynamicPublishedContentBase)) { if (instance != null && instance is LambdaExpression) { @@ -929,14 +930,14 @@ namespace Umbraco.Core.Dynamics //this will invoke TryGetMember (but wrapped in an expression tree) //so that when it's evaluated, DynamicNode should be supported - ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicPublishedContent), "instance"); + ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicPublishedContentBase), "instance"); ParameterExpression convertDynamicNullToBooleanFalse = Expression.Parameter(typeof(bool), "convertDynamicNullToBooleanFalse"); ParameterExpression result = Expression.Parameter(typeof(object), "result"); ParameterExpression binder = Expression.Variable(typeof(DynamicQueryableGetMemberBinder), "binder"); ParameterExpression ignoreCase = Expression.Variable(typeof(bool), "ignoreCase"); ConstructorInfo getMemberBinderConstructor = typeof(DynamicQueryableGetMemberBinder).GetConstructor(new Type[] { typeof(string), typeof(bool) }); LabelTarget blockReturnLabel = Expression.Label(typeof(object)); - MethodInfo method = typeof(DynamicPublishedContent).GetMethod("TryGetMember"); + MethodInfo method = typeof(DynamicPublishedContentBase).GetMethod("TryGetMember"); BlockExpression block = Expression.Block( typeof(object), @@ -957,10 +958,10 @@ namespace Umbraco.Core.Dynamics Expression.Return(blockReturnLabel, result), Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object))) ); - LambdaExpression lax = Expression.Lambda>(block, instanceExpression); + LambdaExpression lax = Expression.Lambda>(block, instanceExpression); return lax; } - if (typeof(Func).IsAssignableFrom(type)) + if (typeof(Func).IsAssignableFrom(type)) { //accessing a property off an already resolved DynamicNode TryGetMember call //e.g. uBlogsyPostDate.Date @@ -971,8 +972,8 @@ namespace Umbraco.Core.Dynamics ParameterExpression result = Expression.Parameter(typeof(object), "result"); ParameterExpression idParam = Expression.Parameter(typeof(string), "id"); ParameterExpression lambdaResult = Expression.Parameter(typeof(object), "lambdaResult"); - ParameterExpression lambdaInstanceExpression = Expression.Parameter(typeof(DynamicPublishedContent), "lambdaInstanceExpression"); - ParameterExpression instanceExpression = Expression.Parameter(typeof(Func), "instance"); + ParameterExpression lambdaInstanceExpression = Expression.Parameter(typeof(DynamicPublishedContentBase), "lambdaInstanceExpression"); + ParameterExpression instanceExpression = Expression.Parameter(typeof(Func), "instance"); LabelTarget blockReturnLabel = Expression.Label(typeof(object)); BlockExpression block = Expression.Block( @@ -991,7 +992,7 @@ namespace Umbraco.Core.Dynamics Expression.Return(blockReturnLabel, result), Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object))) ); - LambdaExpression lax = Expression.Lambda>(block, lambdaInstanceExpression); + LambdaExpression lax = Expression.Lambda>(block, lambdaInstanceExpression); return lax; } } @@ -1050,11 +1051,11 @@ namespace Umbraco.Core.Dynamics switch (methodReturnType.Name) { case "String": - return Expression.Lambda>(block, instanceExpression); + return Expression.Lambda>(block, instanceExpression); case "Int32": - return Expression.Lambda>(block, instanceExpression); + return Expression.Lambda>(block, instanceExpression); case "Boolean": - return Expression.Lambda>(block, instanceExpression); + return Expression.Lambda>(block, instanceExpression); } return Expression.Call(instance, (MethodInfo)method, args); } @@ -1092,8 +1093,8 @@ namespace Umbraco.Core.Dynamics Expression.Return(cblockReturnLabel, cresult), Expression.Label(cblockReturnLabel, Expression.Constant(null, typeof(string)))); - LambdaExpression lax2 = Expression.Lambda>(cblock, instanceExpression); - var expression = Expression.Lambda>(cblock, instanceExpression); + LambdaExpression lax2 = Expression.Lambda>(cblock, instanceExpression); + var expression = Expression.Lambda>(cblock, instanceExpression); return expression; } @@ -1410,7 +1411,7 @@ namespace Umbraco.Core.Dynamics //if the type of the expression is a func - invokable returning object, //we are going to return it here, because we can get the real value when we actually have the instance //if (typeof(Func).IsAssignableFrom(expr.Type)) return expr; - if (expr is LambdaExpression && ((LambdaExpression)expr).Parameters.Count > 0 && ((LambdaExpression)expr).Parameters[0].Type == typeof(DynamicPublishedContent)) + if (expr is LambdaExpression && ((LambdaExpression)expr).Parameters.Count > 0 && ((LambdaExpression)expr).Parameters[0].Type == typeof(DynamicPublishedContentBase)) { return expr; } @@ -1689,12 +1690,12 @@ namespace Umbraco.Core.Dynamics UnaryExpression unboxedLeft = null, unboxedRight = null; ParameterExpression[] parameters = null; - if (left is LambdaExpression && (left as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicPublishedContent)) + if (left is LambdaExpression && (left as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicPublishedContentBase)) { leftIsLambda = true; } - if (right is LambdaExpression && (right as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicPublishedContent)) + if (right is LambdaExpression && (right as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicPublishedContentBase)) { rightIsLambda = true; } @@ -1748,11 +1749,11 @@ namespace Umbraco.Core.Dynamics if (expressionType == ExpressionType.AndAlso) { - return ExpressionExtensions.And(left as Expression>, right as Expression>); + return ExpressionExtensions.And(left as Expression>, right as Expression>); } if (expressionType == ExpressionType.OrElse) { - return ExpressionExtensions.Or(left as Expression>, right as Expression>); + return ExpressionExtensions.Or(left as Expression>, right as Expression>); } } @@ -1783,11 +1784,11 @@ namespace Umbraco.Core.Dynamics //left is invoked and unboxed to right's TOut, right was not boxed if (expressionType == ExpressionType.AndAlso) { - return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); + return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); } if (expressionType == ExpressionType.OrElse) { - return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); + return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); } } else @@ -1804,11 +1805,11 @@ namespace Umbraco.Core.Dynamics //right is invoked and unboxed to left's TOut, left was not boxed if (expressionType == ExpressionType.AndAlso) { - return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); + return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); } if (expressionType == ExpressionType.OrElse) { - return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); + return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); } } @@ -1879,7 +1880,7 @@ namespace Umbraco.Core.Dynamics break; case ExpressionType.Modulo: binaryExpression = Expression.Modulo(finalLeft, finalRight); - return (Expression.Lambda>(binaryExpression, parameters)); + return (Expression.Lambda>(binaryExpression, parameters)); case ExpressionType.AndAlso: if ((leftIsLambda && rightIsLambda && sequenceEqual) || (!leftIsLambda && !rightIsLambda)) { @@ -1887,7 +1888,7 @@ namespace Umbraco.Core.Dynamics } else { - return (Expression.Lambda>(Expression.AndAlso(finalLeft, finalRight), parameters)); + return (Expression.Lambda>(Expression.AndAlso(finalLeft, finalRight), parameters)); } case ExpressionType.OrElse: if (leftIsLambda && rightIsLambda && sequenceEqual || (!leftIsLambda && !rightIsLambda)) @@ -1896,7 +1897,7 @@ namespace Umbraco.Core.Dynamics } else { - return (Expression.Lambda>(Expression.OrElse(finalLeft, finalRight), parameters)); + return (Expression.Lambda>(Expression.OrElse(finalLeft, finalRight), parameters)); } default: return Expression.Equal(left, right); @@ -1904,7 +1905,7 @@ namespace Umbraco.Core.Dynamics if (leftIsLambda || rightIsLambda) { var body = Expression.Condition(Expression.TypeEqual(innerLeft, right.Type), binaryExpression, Expression.Constant(false)); - return Expression.Lambda>(body, parameters); + return Expression.Lambda>(body, parameters); } else { diff --git a/src/Umbraco.Core/Dynamics/ExtensionMethods.cs b/src/Umbraco.Core/Dynamics/ExtensionMethods.cs index 69689bf204..90c8828584 100644 --- a/src/Umbraco.Core/Dynamics/ExtensionMethods.cs +++ b/src/Umbraco.Core/Dynamics/ExtensionMethods.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Models; namespace Umbraco.Core.Dynamics { @@ -47,7 +48,7 @@ namespace Umbraco.Core.Dynamics return new DynamicPublishedContentList(all.Items.OrderBy(x => Guid.NewGuid()).Take(max)); } - public static DynamicPublishedContent Random(this DynamicPublishedContentList all) + public static DynamicPublishedContentBase Random(this DynamicPublishedContentList all) { return all.Items.OrderBy(x => Guid.NewGuid()).First(); } diff --git a/src/Umbraco.Core/Dynamics/Grouping.cs b/src/Umbraco.Core/Dynamics/Grouping.cs index d1122a6395..c3b5a4dcca 100644 --- a/src/Umbraco.Core/Dynamics/Grouping.cs +++ b/src/Umbraco.Core/Dynamics/Grouping.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Collections; using System.Dynamic; +using Umbraco.Core.Models; namespace Umbraco.Core.Dynamics { @@ -13,7 +14,7 @@ namespace Umbraco.Core.Dynamics public IEnumerator GetEnumerator() { - var temp = new DynamicPublishedContentList(Elements.Cast()); + var temp = new DynamicPublishedContentList(Elements.Cast()); return (IEnumerator)temp.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() @@ -42,7 +43,7 @@ namespace Umbraco.Core.Dynamics object key = null; (item as DynamicObject).TryGetMember(new DynamicQueryableGetMemberBinder(ordering, false), out key); return key; - }).Cast()); + }).Cast()); } else { @@ -51,7 +52,7 @@ namespace Umbraco.Core.Dynamics object key = null; (item as DynamicObject).TryGetMember(new DynamicQueryableGetMemberBinder(ordering, false), out key); return key; - }).Cast()); + }).Cast()); } } } diff --git a/src/Umbraco.Core/Dynamics/IDynamicPublishedContentDataSource.cs b/src/Umbraco.Core/Dynamics/IDynamicPublishedContentDataSource.cs deleted file mode 100644 index 50f7eba1c2..0000000000 --- a/src/Umbraco.Core/Dynamics/IDynamicPublishedContentDataSource.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Umbraco.Core.Dynamics -{ - /// - /// This exists only because we want Dynamics in the Core project but DynamicNode has references to ContentType to run some queries - /// and currently the business logic part of Umbraco is still in the legacy project and we don't want to move that to the core so in the - /// meantime until the new APIs are made, we need to have this data source in place with a resolver which is set in the web project. - /// - internal interface IDynamicPublishedContentDataSource - { - Guid GetDataType(string docTypeAlias, string propertyAlias); - - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/DynamicPublishedContentBase.cs b/src/Umbraco.Core/Models/DynamicPublishedContentBase.cs new file mode 100644 index 0000000000..b7c0c277ec --- /dev/null +++ b/src/Umbraco.Core/Models/DynamicPublishedContentBase.cs @@ -0,0 +1,843 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Web; +using Umbraco.Core.Configuration; +using Umbraco.Core.Dynamics; +using Umbraco.Core.PropertyEditors; +using System.Reflection; +using System.Xml.Linq; + +namespace Umbraco.Core.Models +{ + + /// + /// The base dynamic model for views + /// + public class DynamicPublishedContentBase : DynamicObject, IPublishedContent + { + protected IPublishedContent PublishedContent { get; private set; } + private DynamicPublishedContentList _cachedChildren; + private readonly ConcurrentDictionary _cachedMemberOutput = new ConcurrentDictionary(); + + #region Constructors + + public DynamicPublishedContentBase(IPublishedContent node) + { + if (node == null) throw new ArgumentNullException("node"); + PublishedContent = node; + } + + /// + /// Returns an empty/blank DynamicPublishedContent, this is used for special case scenarios + /// + /// + internal static DynamicPublishedContentBase Empty() + { + return new DynamicPublishedContentBase(); + } + + private DynamicPublishedContentBase() + { + } + + #endregion + + public dynamic AsDynamic() + { + return this; + } + + public bool HasProperty(string name) + { + if (PublishedContent != null) + { + try + { + var prop = GetUserProperty(name); + + return (prop != null); + } + catch (Exception) + { + return false; + } + } + return false; + } + + /// + /// Attempts to call a method on the dynamic object + /// + /// + /// + /// + /// + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) + { + //TODO: We MUST cache the result here, it is very expensive to keep finding extension methods! + + try + { + //Property? + result = typeof(DynamicPublishedContentBase).InvokeMember(binder.Name, + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.GetProperty, + null, + this, + args); + return true; + } + catch (MissingMethodException) + { + try + { + //Static or Instance Method? + result = typeof(DynamicPublishedContentBase).InvokeMember(binder.Name, + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.Static | + System.Reflection.BindingFlags.InvokeMethod, + null, + this, + args); + return true; + } + catch (MissingMethodException) + { + try + { + result = ExecuteExtensionMethod(args, binder.Name); + return true; + } + catch (TargetInvocationException) + { + result = new DynamicNull(); + return true; + } + + catch + { + //TODO: LOg this! + + result = null; + return false; + } + + } + + + } + catch + { + result = null; + return false; + } + + } + + private object ExecuteExtensionMethod(object[] args, string name) + { + object result = null; + + var methodTypesToFind = new[] + { + typeof(DynamicPublishedContentBase) + }; + + //find known extension methods that match the first type in the list + MethodInfo toExecute = null; + foreach (var t in methodTypesToFind) + { + toExecute = ExtensionMethodFinder.FindExtensionMethod(t, args, name, false); + if (toExecute != null) + break; + } + + if (toExecute != null) + { + var genericArgs = (new[] { this }).Concat(args); + result = toExecute.Invoke(null, genericArgs.ToArray()); + } + else + { + throw new MissingMethodException(); + } + if (result != null) + { + if (result is IPublishedContent) + { + result = new DynamicPublishedContentBase((IPublishedContent)result); + } + if (result is IEnumerable) + { + result = new DynamicPublishedContentList((IEnumerable)result); + } + if (result is IEnumerable) + { + result = new DynamicPublishedContentList((IEnumerable)result); + } + } + return result; + } + + /// + /// Attempts to return a custom member (generally based on a string match) + /// + /// + /// + protected virtual Attempt TryGetCustomMember(GetMemberBinder binder) + { + if (binder.Name.InvariantEquals("ChildrenAsList") || binder.Name.InvariantEquals("Children")) + { + return new Attempt(true, Children); + } + + if (binder.Name.InvariantEquals("parentId")) + { + var parent = ((IPublishedContent) this).Parent; + if (parent == null) + { + throw new InvalidOperationException(string.Format("The node {0} does not have a parent", Id)); + } + return new Attempt(true, parent.Id); + } + return Attempt.False; + } + + /// + /// Attempts to return the children by the document type's alias (for example: CurrentPage.NewsItems where NewsItem is the + /// document type alias) + /// + /// + /// + /// + /// This method will work by both the plural and non-plural alias (i.e. NewsItem and NewsItems) + /// + protected virtual Attempt TryGetChildrenByAlias(GetMemberBinder binder) + { + + var filteredTypeChildren = PublishedContent.Children + .Where(x => x.DocumentTypeAlias.InvariantEquals(binder.Name) || x.DocumentTypeAlias.MakePluralName().InvariantEquals(binder.Name)) + .ToArray(); + if (filteredTypeChildren.Any()) + { + return new Attempt(true, + new DynamicPublishedContentList(filteredTypeChildren.Select(x => new DynamicPublishedContentBase(x)))); + } + return Attempt.False; + } + + /// + /// Attempts to return a member based on the reflected document property + /// + /// + /// + protected virtual Attempt TryGetDocumentProperty(GetMemberBinder binder) + { + + var reflectedProperty = GetReflectedProperty(binder.Name); + var result = reflectedProperty != null + ? reflectedProperty.Value + : null; + + return result == null + ? Attempt.False + : new Attempt(true, result); + } + + /// + /// Attempts to return a member based on a user defined umbraco property + /// + /// + /// + protected virtual Attempt TryGetUserProperty(GetMemberBinder binder) + { + var name = binder.Name; + var recursive = false; + if (name.StartsWith("_")) + { + name = name.Substring(1, name.Length - 1); + recursive = true; + } + + var userProperty = GetUserProperty(name, recursive); + + if (userProperty == null) + { + return Attempt.False; + } + + var result = userProperty.Value; + + if (PublishedContent.DocumentTypeAlias == null && userProperty.Alias == null) + { + throw new InvalidOperationException("No node alias or property alias available. Unable to look up the datatype of the property you are trying to fetch."); + } + + return new Attempt(true, result); + + } + + /// + /// Returns the member match methods in the correct order and is used in the TryGetMember method. + /// + /// + protected virtual IEnumerable>> GetMemberMatchMethods() + { + var memberMatchMethods = new List>> + { + TryGetCustomMember, //match custom members + TryGetUserProperty, //then match custom user defined umbraco properties + TryGetChildrenByAlias, //then try to match children based on doc type alias + TryGetDocumentProperty //then try to match on a reflected document property + }; + return memberMatchMethods; + } + + /// + /// Try to return an object based on the dynamic member accessor + /// + /// + /// + /// + /// + /// TODO: SD: This will alwasy return true so that no exceptions are generated, this is only because this is how the + /// old DynamicNode worked, I'm not sure if this is the correct/expected functionality but I've left it like that. + /// IMO I think this is incorrect and it would be better to throw an exception for something that is not supported! + /// + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + if (binder == null) throw new ArgumentNullException("binder"); + + var name = binder.Name; + + //check the cache first! + if (_cachedMemberOutput.TryGetValue(name, out result)) + { + return true; + } + + //loop through each member match method and execute it. + //If it is successful, cache the result and return it. + foreach (var attempt in GetMemberMatchMethods() + .Select(m => m(binder)) + .Where(attempt => attempt.Success)) + { + result = attempt.Result; + //cache the result so we don't have to re-process the whole thing + _cachedMemberOutput.TryAdd(name, result); + return true; + } + + //if property access, type lookup and member invoke all failed + //at this point, we're going to return null + //instead, we return a DynamicNull - see comments in that file + //this will let things like Model.ChildItem work and return nothing instead of crashing + + //.Where explictly checks for this type + //and will make it false + //which means backwards equality (&& property != true) will pass + //forwwards equality (&& property or && property == true) will fail + result = new DynamicNull(); + + //alwasy return true if we haven't thrown an exception though I'm wondering if we return 'false' if .Net throws an exception for us?? + return true; + } + + /// + /// Returns a property defined on the document object as a member property using reflection + /// + /// + /// + private PropertyResult GetReflectedProperty(string alias) + { + return GetPropertyInternal(alias, PublishedContent, false); + } + + /// + /// Return a user defined property + /// + /// + /// + /// + internal PropertyResult GetUserProperty(string alias, bool recursive = false) + { + if (!recursive) + { + return GetPropertyInternal(alias, PublishedContent); + } + var context = this; + var prop = GetPropertyInternal(alias, PublishedContent); + while (prop == null || !prop.HasValue()) + { + var parent = ((IPublishedContent) context).Parent; + if (parent == null) break; + prop = context.GetPropertyInternal(alias, context.PublishedContent); + } + return prop; + } + + + private PropertyResult GetPropertyInternal(string alias, IPublishedContent content, bool checkUserProperty = true) + { + if (alias.IsNullOrWhiteSpace()) throw new ArgumentNullException("alias"); + if (content == null) throw new ArgumentNullException("content"); + + //if we're looking for a user defined property + if (checkUserProperty) + { + var prop = content.GetProperty(alias); + + return prop == null + ? null + : new PropertyResult(prop, PropertyResultType.UserProperty) + { + DocumentTypeAlias = content.DocumentTypeAlias, + DocumentId = content.Id + }; + } + + //reflect + + Func> getMember = + memberAlias => + { + try + { + return new Attempt(true, + content.GetType().InvokeMember(memberAlias, + System.Reflection.BindingFlags.GetProperty | + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public, + null, + content, + null)); + } + catch (MissingMethodException ex) + { + return new Attempt(ex); + } + }; + + //try with the current casing + var attempt = getMember(alias); + if (!attempt.Success) + { + //if we cannot get with the current alias, try changing it's case + attempt = alias[0].IsUpperCase() + ? getMember(alias.ConvertCase(StringAliasCaseType.CamelCase)) + : getMember(alias.ConvertCase(StringAliasCaseType.PascalCase)); + } + + return !attempt.Success + ? null + : new PropertyResult(alias, attempt.Result, Guid.Empty, PropertyResultType.ReflectedProperty) + { + DocumentTypeAlias = content.DocumentTypeAlias, + DocumentId = content.Id + }; + } + + + + //public DynamicNode Media(string propertyAlias) + //{ + // if (_n != null) + // { + // IProperty prop = _n.GetProperty(propertyAlias); + // if (prop != null) + // { + // int mediaNodeId; + // if (int.TryParse(prop.Value, out mediaNodeId)) + // { + // return _razorLibrary.Value.MediaById(mediaNodeId); + // } + // } + // return null; + // } + // return null; + //} + //public bool IsProtected + //{ + // get + // { + // if (_n != null) + // { + // return umbraco.library.IsProtected(_n.Id, _n.Path); + // } + // return false; + // } + //} + //public bool HasAccess + //{ + // get + // { + // if (_n != null) + // { + // return umbraco.library.HasAccess(_n.Id, _n.Path); + // } + // return true; + // } + //} + + //public string Media(string propertyAlias, string mediaPropertyAlias) + //{ + // if (_n != null) + // { + // IProperty prop = _n.GetProperty(propertyAlias); + // if (prop == null && propertyAlias.Substring(0, 1).ToUpper() == propertyAlias.Substring(0, 1)) + // { + // prop = _n.GetProperty(propertyAlias.Substring(0, 1).ToLower() + propertyAlias.Substring((1))); + // } + // if (prop != null) + // { + // int mediaNodeId; + // if (int.TryParse(prop.Value, out mediaNodeId)) + // { + // umbraco.cms.businesslogic.media.Media media = new cms.businesslogic.media.Media(mediaNodeId); + // if (media != null) + // { + // Property mprop = media.getProperty(mediaPropertyAlias); + // // check for nicer support of Pascal Casing EVEN if alias is camelCasing: + // if (prop == null && mediaPropertyAlias.Substring(0, 1).ToUpper() == mediaPropertyAlias.Substring(0, 1)) + // { + // mprop = media.getProperty(mediaPropertyAlias.Substring(0, 1).ToLower() + mediaPropertyAlias.Substring((1))); + // } + // if (mprop != null) + // { + // return string.Format("{0}", mprop.Value); + // } + // } + // } + // } + // } + // return null; + //} + + public int TemplateId + { + get { return PublishedContent.TemplateId; } + } + + public int SortOrder + { + get { return PublishedContent.SortOrder; } + } + + public string Name + { + get { return PublishedContent.Name; } + } + + public bool Visible + { + get + { + + var umbracoNaviHide = GetUserProperty("umbracoNaviHide"); + if (umbracoNaviHide != null) + { + return umbracoNaviHide.Value.ToString().Trim() != "1"; + } + return true; + } + } + + public string UrlName + { + get { return PublishedContent.UrlName; } + } + + public string DocumentTypeAlias + { + get { return PublishedContent.DocumentTypeAlias; } + } + + public string WriterName + { + get { return PublishedContent.WriterName; } + } + + public string CreatorName + { + get { return PublishedContent.CreatorName; } + } + + public int WriterId + { + get { return PublishedContent.WriterId; } + } + + public int CreatorId + { + get { return PublishedContent.CreatorId; } + } + + public string Path + { + get { return PublishedContent.Path; } + } + + public DateTime CreateDate + { + get { return PublishedContent.CreateDate; } + } + + public int Id + { + get { return PublishedContent.Id; } + } + + public DateTime UpdateDate + { + get { return PublishedContent.UpdateDate; } + } + + public Guid Version + { + get { return PublishedContent.Version; } + } + + public int Level + { + get { return PublishedContent.Level; } + } + + public IEnumerable Properties + { + get { return PublishedContent.Properties; } + } + + public DynamicPublishedContentList Children + { + get + { + if (_cachedChildren == null) + { + var children = PublishedContent.Children; + //testing, think this must be a special case for the root node ? + if (!children.Any() && PublishedContent.Id == 0) + { + _cachedChildren = new DynamicPublishedContentList(new List { new DynamicPublishedContentBase(this.PublishedContent) }); + } + else + { + _cachedChildren = new DynamicPublishedContentList(PublishedContent.Children.Select(x => new DynamicPublishedContentBase(x))); + } + } + return _cachedChildren; + } + } + + #region GetProperty methods which can be used with the dynamic object + + public IDocumentProperty GetProperty(string alias) + { + return GetProperty(alias, false); + } + public IDocumentProperty GetProperty(string alias, bool recursive) + { + return alias.StartsWith("@") + ? GetReflectedProperty(alias.TrimStart('@')) + : GetUserProperty(alias, recursive); + } + public string GetPropertyValue(string alias) + { + return GetPropertyValue(alias, false); + } + public string GetPropertyValue(string alias, string fallback) + { + var prop = GetPropertyValue(alias); + return !prop.IsNullOrWhiteSpace() ? prop : fallback; + } + public string GetPropertyValue(string alias, bool recursive) + { + var p = alias.StartsWith("@") + ? GetReflectedProperty(alias.TrimStart('@')) + : GetUserProperty(alias, recursive); + return p == null ? null : p.ValueAsString; + } + public string GetPropertyValue(string alias, bool recursive, string fallback) + { + var prop = GetPropertyValue(alias, recursive); + return !prop.IsNullOrWhiteSpace() ? prop : fallback; + } + + #endregion + + #region HasValue + public bool HasValue(string alias) + { + return HasValue(alias, false); + } + public bool HasValue(string alias, bool recursive) + { + var prop = GetUserProperty(alias, recursive); + if (prop == null) return false; + return prop.HasValue(); + } + public IHtmlString HasValue(string alias, string valueIfTrue, string valueIfFalse) + { + return HasValue(alias, false) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); + } + public IHtmlString HasValue(string alias, bool recursive, string valueIfTrue, string valueIfFalse) + { + return HasValue(alias, recursive) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); + } + public IHtmlString HasValue(string alias, string valueIfTrue) + { + return HasValue(alias, false) ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); + } + public IHtmlString HasValue(string alias, bool recursive, string valueIfTrue) + { + return HasValue(alias, recursive) ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); + } + #endregion + + #region Where + + public HtmlString Where(string predicate, string valueIfTrue) + { + return Where(predicate, valueIfTrue, string.Empty); + } + public HtmlString Where(string predicate, string valueIfTrue, string valueIfFalse) + { + if (Where(predicate)) + { + return new HtmlString(valueIfTrue); + } + return new HtmlString(valueIfFalse); + } + public bool Where(string predicate) + { + //Totally gonna cheat here + var dynamicDocumentList = new DynamicPublishedContentList(); + dynamicDocumentList.Add(this); + var filtered = dynamicDocumentList.Where(predicate); + if (Queryable.Count(filtered) == 1) + { + //this node matches the predicate + return true; + } + return false; + } + + #endregion + + //TODO: need a method to return a string value for a user property regardless of xml content or data type thus bypassing all of the PropertyEditorValueConverters + ///// + ///// Returns the value as as string regardless of xml content or data type + ///// + ///// + //public override string ToString() + //{ + // return base.ToString(); + //} + + #region Explicit IPublishedContent implementation + IPublishedContent IPublishedContent.Parent + { + get { return PublishedContent.Parent; } + } + + int IPublishedContent.Id + { + get { return PublishedContent.Id; } + } + + int IPublishedContent.TemplateId + { + get { return PublishedContent.TemplateId; } + } + + int IPublishedContent.SortOrder + { + get { return PublishedContent.SortOrder; } + } + + string IPublishedContent.Name + { + get { return PublishedContent.Name; } + } + + string IPublishedContent.UrlName + { + get { return PublishedContent.UrlName; } + } + + string IPublishedContent.DocumentTypeAlias + { + get { return PublishedContent.DocumentTypeAlias; } + } + + int IPublishedContent.DocumentTypeId + { + get { return PublishedContent.DocumentTypeId; } + } + + string IPublishedContent.WriterName + { + get { return PublishedContent.WriterName; } + } + + string IPublishedContent.CreatorName + { + get { return PublishedContent.CreatorName; } + } + + int IPublishedContent.WriterId + { + get { return PublishedContent.WriterId; } + } + + int IPublishedContent.CreatorId + { + get { return PublishedContent.CreatorId; } + } + + string IPublishedContent.Path + { + get { return PublishedContent.Path; } + } + + DateTime IPublishedContent.CreateDate + { + get { return PublishedContent.CreateDate; } + } + + DateTime IPublishedContent.UpdateDate + { + get { return PublishedContent.UpdateDate; } + } + + Guid IPublishedContent.Version + { + get { return PublishedContent.Version; } + } + + int IPublishedContent.Level + { + get { return PublishedContent.Level; } + } + + System.Collections.ObjectModel.Collection IPublishedContent.Properties + { + get { return PublishedContent.Properties; } + } + + IEnumerable IPublishedContent.Children + { + get { return PublishedContent.Children; } + } + + IDocumentProperty IPublishedContent.GetProperty(string alias) + { + return PublishedContent.GetProperty(alias); + } + #endregion + } +} diff --git a/src/Umbraco.Core/Dynamics/DynamicPublishedContentList.cs b/src/Umbraco.Core/Models/DynamicPublishedContentList.cs similarity index 88% rename from src/Umbraco.Core/Dynamics/DynamicPublishedContentList.cs rename to src/Umbraco.Core/Models/DynamicPublishedContentList.cs index 35658b46af..c3b47f2571 100644 --- a/src/Umbraco.Core/Dynamics/DynamicPublishedContentList.cs +++ b/src/Umbraco.Core/Models/DynamicPublishedContentList.cs @@ -2,32 +2,29 @@ using System.Collections.Generic; using System.Linq; using System.Dynamic; -using Umbraco.Core.Models; -using umbraco.interfaces; +using Umbraco.Core.Dynamics; using System.Collections; using System.Reflection; -namespace Umbraco.Core.Dynamics +namespace Umbraco.Core.Models { - public class DynamicPublishedContentList : DynamicObject, IEnumerable + public class DynamicPublishedContentList : DynamicObject, IEnumerable { - internal List Items { get; set; } + internal List Items { get; set; } public DynamicPublishedContentList() { - Items = new List(); + Items = new List(); } - public DynamicPublishedContentList(IEnumerable items) + public DynamicPublishedContentList(IEnumerable items) { - List list = items.ToList(); - list.ForEach(node => node.OwnerList = this); + var list = items.ToList(); Items = list; } public DynamicPublishedContentList(IEnumerable items) { - List list = items.Select(x => new DynamicPublishedContent(x)).ToList(); - list.ForEach(node => node.OwnerList = this); + var list = items.Select(x => new DynamicPublishedContentBase(x)).ToList(); Items = list; } public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) @@ -61,14 +58,14 @@ namespace Umbraco.Core.Dynamics var values = args.Skip(1).ToArray(); //TODO: We are pre-resolving the where into a ToList() here which will have performance impacts if there where clauses // are nested! We should somehow support an QueryableDocumentList! - result = new DynamicPublishedContentList(this.Where(predicate, values).ToList()); + result = new DynamicPublishedContentList(this.Where(predicate, values).ToList()); return true; } if (name == "OrderBy") { //TODO: We are pre-resolving the where into a ToList() here which will have performance impacts if there where clauses // are nested! We should somehow support an QueryableDocumentList! - result = new DynamicPublishedContentList(this.OrderBy(args.First().ToString()).ToList()); + result = new DynamicPublishedContentList(this.OrderBy(args.First().ToString()).ToList()); return true; } if (name == "Take") @@ -86,7 +83,7 @@ namespace Umbraco.Core.Dynamics int groupSize = 0; if (int.TryParse(args.First().ToString(), out groupSize)) { - result = this.InGroupsOf(groupSize); + result = this.InGroupsOf(groupSize); return true; } result = new DynamicNull(); @@ -97,7 +94,7 @@ namespace Umbraco.Core.Dynamics int groupCount = 0; if (int.TryParse(args.First().ToString(), out groupCount)) { - result = this.GroupedInto(groupCount); + result = this.GroupedInto(groupCount); return true; } result = new DynamicNull(); @@ -105,7 +102,7 @@ namespace Umbraco.Core.Dynamics } if (name == "GroupBy") { - result = this.GroupBy(args.First().ToString()); + result = this.GroupBy(args.First().ToString()); return true; } if (name == "Average" || name == "Min" || name == "Max" || name == "Sum") @@ -115,9 +112,9 @@ namespace Umbraco.Core.Dynamics } if (name == "Union") { - if ((args.First() as IEnumerable) != null) + if ((args.First() as IEnumerable) != null) { - result = new DynamicPublishedContentList(this.Items.Union(args.First() as IEnumerable)); + result = new DynamicPublishedContentList(this.Items.Union(args.First() as IEnumerable)); return true; } if ((args.First() as DynamicPublishedContentList) != null) @@ -128,9 +125,9 @@ namespace Umbraco.Core.Dynamics } if (name == "Except") { - if ((args.First() as IEnumerable) != null) + if ((args.First() as IEnumerable) != null) { - result = new DynamicPublishedContentList(this.Items.Except(args.First() as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); + result = new DynamicPublishedContentList(this.Items.Except(args.First() as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); return true; } if ((args.First() as DynamicPublishedContentList) != null) @@ -141,9 +138,9 @@ namespace Umbraco.Core.Dynamics } if (name == "Intersect") { - if ((args.First() as IEnumerable) != null) + if ((args.First() as IEnumerable) != null) { - result = new DynamicPublishedContentList(this.Items.Intersect(args.First() as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); + result = new DynamicPublishedContentList(this.Items.Intersect(args.First() as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); return true; } if ((args.First() as DynamicPublishedContentList) != null) @@ -202,7 +199,7 @@ namespace Umbraco.Core.Dynamics //We do this to enable error checking of Razor Syntax when a method e.g. ElementAt(2) is used. //When the Script is tested, there's no Children which means ElementAt(2) is invalid (IndexOutOfRange) //Instead, we are going to return an empty DynamicNode. - result = DynamicPublishedContent.Empty(); + result = DynamicPublishedContentBase.Empty(); return true; } @@ -368,7 +365,7 @@ namespace Umbraco.Core.Dynamics var methodTypesToFind = new[] { - typeof(IEnumerable), + typeof(IEnumerable), typeof(DynamicPublishedContentList) }; @@ -408,15 +405,15 @@ namespace Umbraco.Core.Dynamics { if (result is IPublishedContent) { - result = new DynamicPublishedContent((IPublishedContent)result); + result = new DynamicPublishedContentBase((IPublishedContent)result); } if (result is IEnumerable) { result = new DynamicPublishedContentList((IEnumerable)result); } - if (result is IEnumerable) + if (result is IEnumerable) { - result = new DynamicPublishedContentList((IEnumerable)result); + result = new DynamicPublishedContentList((IEnumerable)result); } } return result; @@ -441,9 +438,9 @@ namespace Umbraco.Core.Dynamics return new DynamicGrouping( this .Items - .Select((node, index) => new KeyValuePair(index, node)) + .Select((node, index) => new KeyValuePair(index, node)) .GroupBy(kv => (object)(kv.Key / groupSize)) - .Select(item => new Grouping() + .Select(item => new Grouping() { Key = item.Key, Elements = item.Select(inner => inner.Value) @@ -454,9 +451,9 @@ namespace Umbraco.Core.Dynamics return new DynamicGrouping( this .Items - .Select((node, index) => new KeyValuePair(index, node)) + .Select((node, index) => new KeyValuePair(index, node)) .GroupBy(kv => (object)(kv.Key / groupSize)) - .Select(item => new Grouping() + .Select(item => new Grouping() { Key = item.Key, Elements = item.Select(inner => inner.Value) @@ -469,16 +466,14 @@ namespace Umbraco.Core.Dynamics return Items.AsQueryable().Select(predicate, values); } - public void Add(DynamicPublishedContent publishedContent) + public void Add(DynamicPublishedContentBase publishedContent) { - publishedContent.OwnerList = this; this.Items.Add(publishedContent); } - public void Remove(DynamicPublishedContent publishedContent) + public void Remove(DynamicPublishedContentBase publishedContent) { if (this.Items.Contains(publishedContent)) { - publishedContent.OwnerList = null; this.Items.Remove(publishedContent); } } @@ -491,7 +486,7 @@ namespace Umbraco.Core.Dynamics return true; } - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { return Items.GetEnumerator(); } diff --git a/src/Umbraco.Core/DocumentExtensions.cs b/src/Umbraco.Core/PublishedContentExtensions.cs similarity index 82% rename from src/Umbraco.Core/DocumentExtensions.cs rename to src/Umbraco.Core/PublishedContentExtensions.cs index ae6b8febe5..b49548a9b3 100644 --- a/src/Umbraco.Core/DocumentExtensions.cs +++ b/src/Umbraco.Core/PublishedContentExtensions.cs @@ -8,12 +8,15 @@ using umbraco.interfaces; namespace Umbraco.Core { - public static class DocumentExtensions + /// + /// Extension methods for IPublishedContent + /// + public static class PublishedContentExtensions { public static dynamic AsDynamic(this IPublishedContent doc) { - var dd = new DynamicPublishedContent(doc); + var dd = new DynamicPublishedContentBase(doc); return dd.AsDynamic(); } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index beedce14c7..81e334d42d 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -61,19 +61,17 @@ - + - - + - + - @@ -95,7 +93,6 @@ - diff --git a/src/Umbraco.Tests/ContentStores/PublishContentStoreTests.cs b/src/Umbraco.Tests/ContentStores/PublishContentStoreTests.cs index e686582e9a..7207c8b880 100644 --- a/src/Umbraco.Tests/ContentStores/PublishContentStoreTests.cs +++ b/src/Umbraco.Tests/ContentStores/PublishContentStoreTests.cs @@ -1,3 +1,4 @@ +using System.Linq; using System.Xml; using NUnit.Framework; using Umbraco.Core; @@ -66,6 +67,15 @@ namespace Umbraco.Tests } + [Test] + public void Get_Root_Docs() + { + var result = _publishedContentStore.GetRootDocuments(_umbracoContext); + Assert.AreEqual(2, result.Count()); + Assert.AreEqual(1046, result.ElementAt(0).Id); + Assert.AreEqual(1172, result.ElementAt(1).Id); + } + [TestCase("/", 1046)] [TestCase("/home", 1046)] [TestCase("/Home", 1046)] //test different cases diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs index 8722117bc1..a7172e8002 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs @@ -17,14 +17,14 @@ namespace Umbraco.Tests.DynamicDocument { base.Initialize(); - DynamicPublishedContentDataSourceResolver.Current = new DynamicPublishedContentDataSourceResolver( - new TestDynamicPublishedContentDataSource()); + + } public override void TearDown() { base.TearDown(); - DynamicPublishedContentDataSourceResolver.Reset(); + } protected override bool RequiresDbSetup @@ -32,20 +32,6 @@ namespace Umbraco.Tests.DynamicDocument get { return false; } } - private class TestDynamicPublishedContentDataSource : IDynamicPublishedContentDataSource - { - public Guid GetDataType(string docTypeAlias, string propertyAlias) - { - if (propertyAlias == "content") - { - //return the rte type id - return Guid.Parse("5e9b75ae-face-41c8-b47e-5f4b0fd82f83"); - } - - - return Guid.Empty; - } - } /// /// Returns the dynamic node/document to run tests against diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicNodeTests.cs b/src/Umbraco.Tests/DynamicDocument/DynamicNodeTests.cs index f04152a73f..360af99c14 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicNodeTests.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicNodeTests.cs @@ -1,9 +1,11 @@ +using System; using System.IO; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Tests.TestHelpers; using Umbraco.Web; +using Umbraco.Web.Models; using umbraco.BusinessLogic; using umbraco.IO; using umbraco.MacroEngines; @@ -44,6 +46,17 @@ namespace Umbraco.Tests.DynamicDocument typeof(DynamicNode).Assembly }; + //need to specify a custom callback for unit tests + DynamicNode.GetDataTypeCallback = (docTypeAlias, propertyAlias) => + { + if (propertyAlias == "content") + { + //return the rte type id + return Guid.Parse("5e9b75ae-face-41c8-b47e-5f4b0fd82f83"); + } + return Guid.Empty; + }; + } public override void TearDown() diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs index 115497b366..ba637192b8 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs @@ -1,22 +1,23 @@ using System.Collections.Generic; using Umbraco.Core.Dynamics; +using Umbraco.Core.Models; namespace Umbraco.Tests.DynamicDocument { public static class DynamicPublishedContentCustomExtensionMethods { - public static string DynamicDocumentNoParameters(this Core.Dynamics.DynamicPublishedContent doc) + public static string DynamicDocumentNoParameters(this DynamicPublishedContentBase doc) { return "Hello world"; } - public static string DynamicDocumentCustomString(this Core.Dynamics.DynamicPublishedContent doc, string custom) + public static string DynamicDocumentCustomString(this DynamicPublishedContentBase doc, string custom) { return custom; } - public static string DynamicDocumentMultiParam(this Core.Dynamics.DynamicPublishedContent doc, string custom, int i, bool b) + public static string DynamicDocumentMultiParam(this DynamicPublishedContentBase doc, string custom, int i, bool b) { return custom + i + b; } @@ -26,7 +27,7 @@ namespace Umbraco.Tests.DynamicDocument return custom + i + b; } - public static string DynamicDocumentEnumerableMultiParam(this IEnumerable doc, string custom, int i, bool b) + public static string DynamicDocumentEnumerableMultiParam(this IEnumerable doc, string custom, int i, bool b) { return custom + i + b; } diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs index 837920d1c0..fbf802c2ea 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using NUnit.Framework; using Umbraco.Core.Dynamics; @@ -5,6 +6,7 @@ using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.Routing; using Umbraco.Web; +using Umbraco.Web.Models; using Umbraco.Web.Routing; using umbraco.BusinessLogic; using umbraco.cms.businesslogic; @@ -14,7 +16,7 @@ using umbraco.cms.businesslogic.web; namespace Umbraco.Tests.DynamicDocument { [TestFixture] - public class DynamicPublishedContentTests : DynamicDocumentTestsBase + public class DynamicPublishedContentTests : DynamicDocumentTestsBase { public override void Initialize() { @@ -27,6 +29,17 @@ namespace Umbraco.Tests.DynamicDocument typeof(TinyMcePropertyEditorValueConverter), typeof(YesNoPropertyEditorValueConverter) }); + + //need to specify a custom callback for unit tests + DynamicPublishedContent.GetDataTypeCallback = (docTypeAlias, propertyAlias) => + { + if (propertyAlias == "content") + { + //return the rte type id + return Guid.Parse("5e9b75ae-face-41c8-b47e-5f4b0fd82f83"); + } + return Guid.Empty; + }; } public override void TearDown() @@ -36,7 +49,7 @@ namespace Umbraco.Tests.DynamicDocument PropertyEditorValueConvertersResolver.Reset(); } - internal Core.Dynamics.DynamicPublishedContent GetNode(int id) + internal DynamicPublishedContent GetNode(int id) { //var template = Template.MakeNew("test", new User(0)); //var ctx = GetUmbracoContext("/test", template.Id); @@ -44,7 +57,7 @@ namespace Umbraco.Tests.DynamicDocument var contentStore = new DefaultPublishedContentStore(); var doc = contentStore.GetDocumentById(ctx, id); Assert.IsNotNull(doc); - var dynamicNode = new Core.Dynamics.DynamicPublishedContent(doc); + var dynamicNode = new DynamicPublishedContent(doc); Assert.IsNotNull(dynamicNode); return dynamicNode; } @@ -99,9 +112,9 @@ namespace Umbraco.Tests.DynamicDocument /// public class TestHelper { - private readonly Core.Dynamics.DynamicPublishedContent _doc; + private readonly DynamicPublishedContentBase _doc; - public TestHelper(Core.Dynamics.DynamicPublishedContent doc) + public TestHelper(DynamicPublishedContentBase doc) { _doc = doc; } diff --git a/src/Umbraco.Tests/Routing/LookupByNiceUrlWithDomainsTests.cs b/src/Umbraco.Tests/Routing/LookupByNiceUrlWithDomainsTests.cs index c64247585c..0ab44db50b 100644 --- a/src/Umbraco.Tests/Routing/LookupByNiceUrlWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/LookupByNiceUrlWithDomainsTests.cs @@ -14,22 +14,18 @@ namespace Umbraco.Tests.Routing [TestFixture] public class LookupByNiceUrlWithDomainsTests : BaseRoutingTest { + public override void Initialize() + { + base.Initialize(); + InitializeLanguagesAndDomains(); + } + public override void TearDown() { base.TearDown(); - ClearLanguagesAndDomains(); + } - void ClearLanguagesAndDomains() - { - var domains = Domain.GetDomains(); - foreach (var d in domains) - d.Delete(); - - var langs = Language.GetAllAsList(); - foreach (var l in langs.Skip(1)) - l.Delete(); - } void InitializeLanguagesAndDomains() { @@ -155,7 +151,6 @@ namespace Umbraco.Tests.Routing public void Lookup_SingleDomain(string url, int expectedId) { - InitializeLanguagesAndDomains(); SetDomains3(); ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", "true"); @@ -195,7 +190,6 @@ namespace Umbraco.Tests.Routing public void Lookup_NestedDomains(string url, int expectedId, string expectedCulture) { - InitializeLanguagesAndDomains(); SetDomains4(); ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", "true"); diff --git a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs index 50434a1ce8..74bb9954fa 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs @@ -20,8 +20,6 @@ namespace Umbraco.Tests.Routing ConfigurationManager.AppSettings.Set("umbracoUseDirectoryUrls", ""); ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", ""); - - ClearLanguagesAndDomains(); } internal override IRoutesCache GetRoutesCache() @@ -29,16 +27,6 @@ namespace Umbraco.Tests.Routing return new DefaultRoutesCache(false); } - void ClearLanguagesAndDomains() - { - var domains = Domain.GetDomains(); - foreach (var d in domains) - d.Delete(); - - var langs = Language.GetAllAsList(); - foreach (var l in langs.Skip(1)) - l.Delete(); - } void InitializeLanguagesAndDomains() { diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index 1b2bf2d8ad..7f4b68d2bd 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -71,7 +71,6 @@ namespace Umbraco.Tests.Routing ConfigurationManager.AppSettings.Set("umbracoUseDirectoryUrls", ""); ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", ""); - ClearLanguagesAndDomains(); } internal override IRoutesCache GetRoutesCache() @@ -79,16 +78,6 @@ namespace Umbraco.Tests.Routing return new DefaultRoutesCache(false); } - void ClearLanguagesAndDomains() - { - var domains = Domain.GetDomains(); - foreach (var d in domains) - d.Delete(); - - var langs = Language.GetAllAsList(); - foreach (var l in langs.Skip(1)) - l.Delete(); - } void InitializeLanguagesAndDomains() { diff --git a/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs b/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs index 2cf43ac056..008c73e4c7 100644 --- a/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs +++ b/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs @@ -3,39 +3,62 @@ using System.Collections.Generic; using System.Linq; using System.Text; using NUnit.Framework; +using Umbraco.Tests.Stubs; using Umbraco.Tests.TestHelpers; using System.Configuration; +using Umbraco.Web; using Umbraco.Web.Routing; +using umbraco.BusinessLogic; +using umbraco.cms.businesslogic.template; namespace Umbraco.Tests.Routing { [TestFixture] public class uQueryGetNodeIdByUrlTests : BaseRoutingTest { - protected RoutingContext GetRoutingContext() + public override void TearDown() { - var url = "/test"; - var templateId = 1111; + base.TearDown(); + ConfigurationManager.AppSettings.Set("umbracoUseDirectoryUrls", ""); + ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", ""); + } + + internal override IRoutesCache GetRoutesCache() + { + return new DefaultRoutesCache(false); + } + + public override void Initialize() + { + base.Initialize(); + + var url = "/test"; + var lookup = new Umbraco.Web.Routing.LookupByNiceUrl(); var lookups = new Umbraco.Web.Routing.IPublishedContentLookup[] { lookup }; - var umbracoContext = GetUmbracoContext(url, templateId, null); - var contentStore = new Umbraco.Web.DefaultPublishedContentStore(); + var t = Template.MakeNew("test", new User(0)); + + var umbracoContext = GetUmbracoContext(url, t.Id); + var contentStore = new DefaultPublishedContentStore(); var niceUrls = new NiceUrlProvider(contentStore, umbracoContext); var routingContext = new RoutingContext( umbracoContext, lookups, - new Umbraco.Tests.Stubs.FakeLastChanceLookup(), + new FakeLastChanceLookup(), contentStore, niceUrls); //assign the routing context back to the umbraco context umbracoContext.RoutingContext = routingContext; - return routingContext; + ////assign the routing context back to the umbraco context + //umbracoContext.RoutingContext = routingContext; + Umbraco.Web.UmbracoContext.Current = routingContext.UmbracoContext; } + [TestCase(1046, "/home")] [TestCase(1173, "/home/sub1")] [TestCase(1174, "/home/sub1/sub2")] @@ -47,9 +70,7 @@ namespace Umbraco.Tests.Routing public void GetNodeIdByUrl_Not_Hiding_Top_Level_Absolute(int nodeId, string url) { - var routingContext = GetRoutingContext(); - Umbraco.Web.UmbracoContext.Current = routingContext.UmbracoContext; - + ConfigurationManager.AppSettings.Set("umbracoUseDirectoryUrls", "true"); ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", "false"); Umbraco.Core.Configuration.UmbracoSettings.UseDomainPrefixes = false; @@ -68,9 +89,6 @@ namespace Umbraco.Tests.Routing public void GetNodeIdByUrl_Not_Hiding_Top_Level_Relative(int nodeId, string url) { - var routingContext = GetRoutingContext(); - Umbraco.Web.UmbracoContext.Current = routingContext.UmbracoContext; - ConfigurationManager.AppSettings.Set("umbracoUseDirectoryUrls", "true"); ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", "false"); Umbraco.Core.Configuration.UmbracoSettings.UseDomainPrefixes = false; diff --git a/src/Umbraco.Web/DefaultDynamicPublishedContentDataSource.cs b/src/Umbraco.Web/DefaultDynamicPublishedContentDataSource.cs deleted file mode 100644 index 61300cac77..0000000000 --- a/src/Umbraco.Web/DefaultDynamicPublishedContentDataSource.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core.Dynamics; -using umbraco.cms.businesslogic; - -namespace Umbraco.Web -{ - - /// - /// This exists only because we want Dynamics in the Core project but DynamicNode has references to ContentType to run some queries - /// and currently the business logic part of Umbraco is still in the legacy project and we don't want to move that to the core so in the - /// meantime until the new APIs are made, we need to have this data source in place with a resolver which is set in the web project. - /// - internal class DefaultDynamicPublishedContentDataSource : IDynamicPublishedContentDataSource - { - public Guid GetDataType(string docTypeAlias, string propertyAlias) - { - return ContentType.GetDataType(docTypeAlias, propertyAlias); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/DefaultPublishedContentStore.cs b/src/Umbraco.Web/DefaultPublishedContentStore.cs index d5c0c1cd31..b5ce8ac2ff 100644 --- a/src/Umbraco.Web/DefaultPublishedContentStore.cs +++ b/src/Umbraco.Web/DefaultPublishedContentStore.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Text; using System.Xml; using System.Xml.Linq; @@ -32,6 +33,11 @@ namespace Umbraco.Web return ConvertToDocument(GetXml(umbracoContext).GetElementById(nodeId.ToString())); } + public IEnumerable GetRootDocuments(UmbracoContext umbracoContext) + { + return (from XmlNode x in GetXml(umbracoContext).SelectNodes("/root/*[@isDoc]") select ConvertToDocument(x)).ToList(); + } + public IPublishedContent GetDocumentByRoute(UmbracoContext umbracoContext, string route, bool? hideTopLevelNode = null) { if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); diff --git a/src/Umbraco.Web/DefaultPublishedMediaStore.cs b/src/Umbraco.Web/DefaultPublishedMediaStore.cs index 344964da6e..ab43387283 100644 --- a/src/Umbraco.Web/DefaultPublishedMediaStore.cs +++ b/src/Umbraco.Web/DefaultPublishedMediaStore.cs @@ -26,6 +26,21 @@ namespace Umbraco.Web return GetUmbracoMedia(nodeId); } + public IEnumerable GetRootDocuments(UmbracoContext umbracoContext) + { + var rootMedia = global::umbraco.cms.businesslogic.media.Media.GetRootMedias(); + var result = new List(); + //TODO: need to get a ConvertFromMedia method but we'll just use this for now. + foreach (var media in rootMedia + .Select(m => global::umbraco.library.GetMedia(m.Id, true)) + .Where(media => media != null && media.Current != null)) + { + media.MoveNext(); + result.Add(ConvertFromXPathNavigator(media.Current)); + } + return result; + } + private IPublishedContent GetUmbracoMedia(int id) { diff --git a/src/Umbraco.Web/DynamicPublishedContentSearchExtensions.cs b/src/Umbraco.Web/DynamicPublishedContentSearchExtensions.cs index 169a666e37..173c5aab76 100644 --- a/src/Umbraco.Web/DynamicPublishedContentSearchExtensions.cs +++ b/src/Umbraco.Web/DynamicPublishedContentSearchExtensions.cs @@ -1,5 +1,6 @@ using Examine.LuceneEngine.SearchCriteria; using Umbraco.Core.Dynamics; +using Umbraco.Core.Models; namespace Umbraco.Web { @@ -8,7 +9,7 @@ namespace Umbraco.Web /// public static class DynamicPublishedContentSearchExtensions { - public static DynamicPublishedContentList Search(this DynamicPublishedContent d, string term, bool useWildCards = true, string searchProvider = null) + public static DynamicPublishedContentList Search(this DynamicPublishedContentBase d, string term, bool useWildCards = true, string searchProvider = null) { var searcher = Examine.ExamineManager.Instance.DefaultSearchProvider; if (!string.IsNullOrEmpty(searchProvider)) @@ -24,12 +25,12 @@ namespace Umbraco.Web return d.Search(crit, searcher); } - public static DynamicPublishedContentList SearchDescendants(this DynamicPublishedContent d, string term, bool useWildCards = true, string searchProvider = null) + public static DynamicPublishedContentList SearchDescendants(this DynamicPublishedContentBase d, string term, bool useWildCards = true, string searchProvider = null) { return d.Search(term, useWildCards, searchProvider); } - public static DynamicPublishedContentList SearchChildren(this DynamicPublishedContent d, string term, bool useWildCards = true, string searchProvider = null) + public static DynamicPublishedContentList SearchChildren(this DynamicPublishedContentBase d, string term, bool useWildCards = true, string searchProvider = null) { var searcher = Examine.ExamineManager.Instance.DefaultSearchProvider; if (!string.IsNullOrEmpty(searchProvider)) @@ -45,7 +46,7 @@ namespace Umbraco.Web return d.Search(crit, searcher); } - public static DynamicPublishedContentList Search(this DynamicPublishedContent d, Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) + public static DynamicPublishedContentList Search(this DynamicPublishedContentBase d, Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) { var s = Examine.ExamineManager.Instance.DefaultSearchProvider; if (searchProvider != null) diff --git a/src/Umbraco.Web/ExamineExtensions.cs b/src/Umbraco.Web/ExamineExtensions.cs index 8898b491ff..3f63345116 100644 --- a/src/Umbraco.Web/ExamineExtensions.cs +++ b/src/Umbraco.Web/ExamineExtensions.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Xml; using Examine; using Umbraco.Core.Dynamics; +using Umbraco.Core.Models; namespace Umbraco.Web { @@ -31,7 +32,7 @@ namespace Umbraco.Web if (doc == null) continue; //skip if this doesn't exist in the cache doc.Properties.Add( new PropertyResult("examineScore", result.Score.ToString(), Guid.Empty, PropertyResultType.CustomProperty)); - var dynamicDoc = new DynamicPublishedContent(doc); + var dynamicDoc = new DynamicPublishedContentBase(doc); list.Add(dynamicDoc); } return list; diff --git a/src/Umbraco.Web/IPublishedStore.cs b/src/Umbraco.Web/IPublishedStore.cs index dd2cc1031a..0eae294911 100644 --- a/src/Umbraco.Web/IPublishedStore.cs +++ b/src/Umbraco.Web/IPublishedStore.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Umbraco.Core.Models; namespace Umbraco.Web @@ -8,5 +9,6 @@ namespace Umbraco.Web public interface IPublishedStore { IPublishedContent GetDocumentById(UmbracoContext umbracoContext, int nodeId); + IEnumerable GetRootDocuments(UmbracoContext umbracoContext); } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/DynamicPublishedContent.cs b/src/Umbraco.Web/Models/DynamicPublishedContent.cs new file mode 100644 index 0000000000..c0065823d8 --- /dev/null +++ b/src/Umbraco.Web/Models/DynamicPublishedContent.cs @@ -0,0 +1,545 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Web; +using Umbraco.Core.Configuration; +using Umbraco.Core.Dynamics; +using Umbraco.Core.Models; +using Umbraco.Core; +using Umbraco.Core.PropertyEditors; +using System.Reflection; +using System.Xml.Linq; +using umbraco.cms.businesslogic; + +namespace Umbraco.Web.Models +{ + + /// + /// The base dynamic model for views + /// + public class DynamicPublishedContent : DynamicPublishedContentBase + { + /// + /// This callback is used only so we can set it dynamically for use in unit tests + /// + internal static Func GetDataTypeCallback = (docTypeAlias, propertyAlias) => + ContentType.GetDataType(docTypeAlias, propertyAlias); + + public DynamicPublishedContent(IPublishedContent node) + : base(node) + { + } + + /// + /// overriden method which uses PropertyEditorValueConverters to convert the resulting value + /// + /// + /// + protected override Attempt TryGetUserProperty(GetMemberBinder binder) + { + var name = binder.Name; + var recursive = false; + if (name.StartsWith("_")) + { + name = name.Substring(1, name.Length - 1); + recursive = true; + } + + var userProperty = GetUserProperty(name, recursive); + + if (userProperty == null) + { + return Attempt.False; + } + + var result = userProperty.Value; + + if (PublishedContent.DocumentTypeAlias == null && userProperty.Alias == null) + { + throw new InvalidOperationException("No node alias or property alias available. Unable to look up the datatype of the property you are trying to fetch."); + } + + //get the data type id for the current property + var dataType = GetDataType(userProperty.DocumentTypeAlias, userProperty.Alias); + + //convert the string value to a known type + var converted = ConvertPropertyValue(result, dataType, userProperty.DocumentTypeAlias, userProperty.Alias); + if (converted.Success) + { + result = converted.Result; + } + + return new Attempt(true, result); + } + + private static Guid GetDataType(string docTypeAlias, string propertyAlias) + { + return GetDataTypeCallback(docTypeAlias, propertyAlias); + } + + /// + /// Converts the currentValue to a correctly typed value based on known registered converters, then based on known standards. + /// + /// + /// + /// + /// + /// + private Attempt ConvertPropertyValue(object currentValue, Guid dataType, string docTypeAlias, string propertyTypeAlias) + { + if (currentValue == null) return Attempt.False; + + //First lets check all registered converters for this data type. + var converters = PropertyEditorValueConvertersResolver.Current.Converters + .Where(x => x.IsConverterFor(dataType, docTypeAlias, propertyTypeAlias)) + .ToArray(); + + //try to convert the value with any of the converters: + foreach (var converted in converters + .Select(p => p.ConvertPropertyValue(currentValue)) + .Where(converted => converted.Success)) + { + return new Attempt(true, converted.Result); + } + + //if none of the converters worked, then we'll process this from what we know + + var sResult = Convert.ToString(currentValue).Trim(); + + //this will eat csv strings, so only do it if the decimal also includes a decimal seperator (according to the current culture) + if (sResult.Contains(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator)) + { + decimal dResult; + if (decimal.TryParse(sResult, System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.CurrentCulture, out dResult)) + { + return new Attempt(true, dResult); + } + } + //process string booleans as booleans + if (sResult.InvariantEquals("true")) + { + return new Attempt(true, true); + } + if (sResult.InvariantEquals("false")) + { + return new Attempt(true, false); + } + + //a really rough check to see if this may be valid xml + //TODO: This is legacy code, I'm sure there's a better and nicer way + if (sResult.StartsWith("<") && sResult.EndsWith(">") && sResult.Contains("/")) + { + try + { + var e = XElement.Parse(DynamicXml.StripDashesInElementOrAttributeNames(sResult), LoadOptions.None); + + //check that the document element is not one of the disallowed elements + //allows RTE to still return as html if it's valid xhtml + var documentElement = e.Name.LocalName; + + //TODO: See note against this setting, pretty sure we don't need this + if (!UmbracoSettings.NotDynamicXmlDocumentElements.Any( + tag => string.Equals(tag, documentElement, StringComparison.CurrentCultureIgnoreCase))) + { + return new Attempt(true, new DynamicXml(e)); + } + return Attempt.False; + } + catch (Exception) + { + return Attempt.False; + } + } + return Attempt.False; + } + + #region Index/Position + public int Position() + { + return Umbraco.Web.PublishedContentExtensions.Position(this); + } + public int Index() + { + return Umbraco.Web.PublishedContentExtensions.Index(this); + } + #endregion + + #region Is Helpers + public bool IsNull(string alias, bool recursive) + { + return this.PublishedContent.IsNull(alias, recursive); + } + public bool IsNull(string alias) + { + return this.PublishedContent.IsNull(alias, false); + } + public bool IsFirst() + { + return this.PublishedContent.IsFirst(); + } + public HtmlString IsFirst(string valueIfTrue) + { + return this.PublishedContent.IsFirst(valueIfTrue); + } + public HtmlString IsFirst(string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsFirst(valueIfTrue, valueIfFalse); + } + public bool IsNotFirst() + { + return this.PublishedContent.IsNotFirst(); + } + public HtmlString IsNotFirst(string valueIfTrue) + { + return this.PublishedContent.IsNotFirst(valueIfTrue); + } + public HtmlString IsNotFirst(string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsNotFirst(valueIfTrue, valueIfFalse); + } + public bool IsPosition(int index) + { + return this.PublishedContent.IsPosition(index); + } + public HtmlString IsPosition(int index, string valueIfTrue) + { + return this.PublishedContent.IsPosition(index, valueIfTrue); + } + public HtmlString IsPosition(int index, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsPosition(index, valueIfTrue, valueIfFalse); + } + public bool IsModZero(int modulus) + { + return this.PublishedContent.IsModZero(modulus); + } + public HtmlString IsModZero(int modulus, string valueIfTrue) + { + return this.PublishedContent.IsModZero(modulus, valueIfTrue); + } + public HtmlString IsModZero(int modulus, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsModZero(modulus, valueIfTrue, valueIfFalse); + } + + public bool IsNotModZero(int modulus) + { + return this.PublishedContent.IsNotModZero(modulus); + } + public HtmlString IsNotModZero(int modulus, string valueIfTrue) + { + return this.PublishedContent.IsNotModZero(modulus, valueIfTrue); + } + public HtmlString IsNotModZero(int modulus, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsNotModZero(modulus, valueIfTrue, valueIfFalse); + } + public bool IsNotPosition(int index) + { + return this.PublishedContent.IsNotPosition(index); + } + public HtmlString IsNotPosition(int index, string valueIfTrue) + { + return this.PublishedContent.IsNotPosition(index, valueIfTrue); + } + public HtmlString IsNotPosition(int index, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsNotPosition(index, valueIfTrue, valueIfFalse); + } + public bool IsLast() + { + return this.PublishedContent.IsLast(); + } + public HtmlString IsLast(string valueIfTrue) + { + return this.PublishedContent.IsLast(valueIfTrue); + } + public HtmlString IsLast(string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsLast(valueIfTrue, valueIfFalse); + } + public bool IsNotLast() + { + return this.PublishedContent.IsNotLast(); + } + public HtmlString IsNotLast(string valueIfTrue) + { + return this.PublishedContent.IsNotLast(valueIfTrue); + } + public HtmlString IsNotLast(string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsNotLast(valueIfTrue, valueIfFalse); + } + public bool IsEven() + { + return this.PublishedContent.IsEven(); + } + public HtmlString IsEven(string valueIfTrue) + { + return this.PublishedContent.IsEven(valueIfTrue); + } + public HtmlString IsEven(string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsEven(valueIfTrue, valueIfFalse); + } + public bool IsOdd() + { + return this.PublishedContent.IsOdd(); + } + public HtmlString IsOdd(string valueIfTrue) + { + return this.PublishedContent.IsOdd(valueIfTrue); + } + public HtmlString IsOdd(string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsOdd(valueIfTrue, valueIfFalse); + } + public bool IsEqual(DynamicPublishedContentBase other) + { + return this.PublishedContent.IsEqual(other); + } + public HtmlString IsEqual(DynamicPublishedContentBase other, string valueIfTrue) + { + return this.PublishedContent.IsEqual(other, valueIfTrue); + } + public HtmlString IsEqual(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsEqual(other, valueIfTrue, valueIfFalse); + } + public bool IsNotEqual(DynamicPublishedContentBase other) + { + return this.PublishedContent.IsNotEqual(other); + } + public HtmlString IsNotEqual(DynamicPublishedContentBase other, string valueIfTrue) + { + return this.PublishedContent.IsNotEqual(other, valueIfTrue); + } + public HtmlString IsNotEqual(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsNotEqual(other, valueIfTrue, valueIfFalse); + } + public bool IsDescendant(DynamicPublishedContentBase other) + { + return this.PublishedContent.IsDescendant(other); + } + public HtmlString IsDescendant(DynamicPublishedContentBase other, string valueIfTrue) + { + return this.PublishedContent.IsDescendant(other, valueIfTrue); + } + public HtmlString IsDescendant(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsDescendant(other, valueIfTrue, valueIfFalse); + } + public bool IsDescendantOrSelf(DynamicPublishedContentBase other) + { + return this.PublishedContent.IsDescendantOrSelf(other); + } + public HtmlString IsDescendantOrSelf(DynamicPublishedContentBase other, string valueIfTrue) + { + return this.PublishedContent.IsDescendantOrSelf(other, valueIfTrue); + } + public HtmlString IsDescendantOrSelf(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsDescendantOrSelf(other, valueIfTrue, valueIfFalse); + } + public bool IsAncestor(DynamicPublishedContentBase other) + { + return this.PublishedContent.IsAncestor(other); + } + public HtmlString IsAncestor(DynamicPublishedContentBase other, string valueIfTrue) + { + return this.PublishedContent.IsAncestor(other, valueIfTrue); + } + public HtmlString IsAncestor(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsAncestor(other, valueIfTrue, valueIfFalse); + } + public bool IsAncestorOrSelf(DynamicPublishedContentBase other) + { + return this.PublishedContent.IsAncestorOrSelf(other); + } + public HtmlString IsAncestorOrSelf(DynamicPublishedContentBase other, string valueIfTrue) + { + return this.PublishedContent.IsAncestorOrSelf(other, valueIfTrue); + } + public HtmlString IsAncestorOrSelf(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsAncestorOrSelf(other, valueIfTrue, valueIfFalse); + } + #endregion + + #region Traversal + public DynamicPublishedContent Up() + { + return Umbraco.Web.PublishedContentExtensions.Up(this).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Up(int number) + { + return Umbraco.Web.PublishedContentExtensions.Up(this, number).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Up(string nodeTypeAlias) + { + return Umbraco.Web.PublishedContentExtensions.Up(this, nodeTypeAlias).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Down() + { + return Umbraco.Web.PublishedContentExtensions.Down(this).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Down(int number) + { + return Umbraco.Web.PublishedContentExtensions.Down(this, number).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Down(string nodeTypeAlias) + { + return Umbraco.Web.PublishedContentExtensions.Down(this, nodeTypeAlias).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Next() + { + return Umbraco.Web.PublishedContentExtensions.Next(this).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Next(int number) + { + return Umbraco.Web.PublishedContentExtensions.Next(this, number).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Next(string nodeTypeAlias) + { + return Umbraco.Web.PublishedContentExtensions.Next(this, nodeTypeAlias).AsDynamicPublishedContent(); + } + + public DynamicPublishedContent Previous() + { + return Umbraco.Web.PublishedContentExtensions.Previous(this).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Previous(int number) + { + return Umbraco.Web.PublishedContentExtensions.Previous(this, number).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Previous(string nodeTypeAlias) + { + return Umbraco.Web.PublishedContentExtensions.Previous(this, nodeTypeAlias).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Sibling(int number) + { + return Umbraco.Web.PublishedContentExtensions.Previous(this, number).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Sibling(string nodeTypeAlias) + { + return Umbraco.Web.PublishedContentExtensions.Previous(this, nodeTypeAlias).AsDynamicPublishedContent(); + } + #endregion + + #region Ancestors, Descendants and Parent + #region Ancestors + public DynamicPublishedContentList Ancestors(int level) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.Ancestors(this, level)); + } + public DynamicPublishedContentList Ancestors(string nodeTypeAlias) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.Ancestors(this, nodeTypeAlias)); + } + public DynamicPublishedContentList Ancestors() + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.Ancestors(this)); + } + public DynamicPublishedContentList Ancestors(Func func) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.Ancestors(this, func)); + } + public DynamicPublishedContent AncestorOrSelf() + { + return Umbraco.Web.PublishedContentExtensions.AncestorOrSelf(this).AsDynamicPublishedContent(); + } + public DynamicPublishedContent AncestorOrSelf(int level) + { + return Umbraco.Web.PublishedContentExtensions.AncestorOrSelf(this, level).AsDynamicPublishedContent(); + } + public DynamicPublishedContent AncestorOrSelf(string nodeTypeAlias) + { + return Umbraco.Web.PublishedContentExtensions.AncestorOrSelf(this, nodeTypeAlias).AsDynamicPublishedContent(); + } + public DynamicPublishedContent AncestorOrSelf(Func func) + { + return Umbraco.Web.PublishedContentExtensions.AncestorOrSelf(this, func).AsDynamicPublishedContent(); + } + public DynamicPublishedContentList AncestorsOrSelf(Func func) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.AncestorsOrSelf(this, func)); + } + public DynamicPublishedContentList AncestorsOrSelf() + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.AncestorsOrSelf(this)); + } + public DynamicPublishedContentList AncestorsOrSelf(string nodeTypeAlias) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.AncestorsOrSelf(this, nodeTypeAlias)); + } + public DynamicPublishedContentList AncestorsOrSelf(int level) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.AncestorsOrSelf(this, level)); + } + #endregion + #region Descendants + public DynamicPublishedContentList Descendants(string nodeTypeAlias) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.Descendants(this, nodeTypeAlias)); + } + public DynamicPublishedContentList Descendants(int level) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.Descendants(this, level)); + } + public DynamicPublishedContentList Descendants() + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.Descendants(this)); + } + public DynamicPublishedContentList DescendantsOrSelf(int level) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.DescendantsOrSelf(this, level)); + } + public DynamicPublishedContentList DescendantsOrSelf(string nodeTypeAlias) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.DescendantsOrSelf(this, nodeTypeAlias)); + } + public DynamicPublishedContentList DescendantsOrSelf() + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.DescendantsOrSelf(this)); + } + #endregion + + public DynamicPublishedContent Parent + { + get + { + if (PublishedContent.Parent != null) + { + return PublishedContent.Parent.AsDynamicPublishedContent(); + } + if (PublishedContent != null && PublishedContent.Id == 0) + { + return this; + } + return null; + } + } + + #endregion + + + } +} diff --git a/src/Umbraco.Web/Mvc/RenderViewPage.cs b/src/Umbraco.Web/Mvc/RenderViewPage.cs index 0128220a7b..00bb93fb9a 100644 --- a/src/Umbraco.Web/Mvc/RenderViewPage.cs +++ b/src/Umbraco.Web/Mvc/RenderViewPage.cs @@ -2,6 +2,7 @@ using System.Web.Mvc; using Umbraco.Core; using Umbraco.Core.Dictionary; using Umbraco.Core.Dynamics; +using Umbraco.Core.Models; using Umbraco.Web.Routing; namespace Umbraco.Web.Mvc @@ -23,7 +24,7 @@ namespace Umbraco.Web.Mvc { ////this.ViewData.Model = Model; //var backingItem = new DynamicBackingItem(Model.CurrentNode); - var dynamicNode = new DynamicPublishedContent(Model.CurrentContent); + var dynamicNode = new DynamicPublishedContentBase(Model.CurrentContent); CurrentPage = dynamicNode.AsDynamic(); } PublishedContentRequest = (PublishedContentRequest)ViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-doc-request"); diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index bf05cde8a0..1626346c13 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -2,7 +2,10 @@ using System; using System.Collections.Generic; using System.Data; using System.Linq; +using System.Web; +using Umbraco.Core.Dynamics; using Umbraco.Core.Models; +using Umbraco.Web.Models; using Umbraco.Web.Routing; using umbraco.cms.businesslogic; using Umbraco.Core; @@ -17,7 +20,672 @@ namespace Umbraco.Web /// which is why they cannot exist in the Core project. /// public static class PublishedContentExtensions - { + { + + /// + /// Converts a IPublishedContent to a DynamicPublishedContent and tests for null + /// + /// + /// + internal static DynamicPublishedContent AsDynamicPublishedContent(this IPublishedContent content) + { + if (content == null) + return null; + return new DynamicPublishedContent(content); + } + + public static IDocumentProperty GetProperty(this IPublishedContent content, string alias, bool recursive) + { + return content.GetUserRecursive(alias, recursive); + } + + private static IDocumentProperty GetUserRecursive(this IPublishedContent content, string alias, bool recursive = false) + { + if (!recursive) + { + return content.GetProperty(alias); + } + var context = content; + var prop = content.GetUserRecursive(alias); + while (prop == null || prop.Value == null || prop.Value.ToString().IsNullOrWhiteSpace()) + { + var parent = context.Parent; + if (parent == null) break; + prop = context.GetUserRecursive(alias); + } + return prop; + } + + #region Position/Index + public static int Position(this IPublishedContent content) + { + return content.Index(); + } + public static int Index(this IPublishedContent content) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + + var container = ownersList.ToList(); + int currentIndex = container.FindIndex(n => n.Id == content.Id); + if (currentIndex != -1) + { + return currentIndex; + } + else + { + throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicDocumentList but could not retrieve the index for it's position in the list", content.Id)); + } + } + #endregion + + #region Is Helpers + + public static bool IsNull(this IPublishedContent content, string alias, bool recursive) + { + var prop = content.GetProperty(alias, recursive); + if (prop == null) return true; + return ((PropertyResult)prop).HasValue(); + } + public static bool IsNull(this IPublishedContent content, string alias) + { + return content.IsNull(alias, false); + } + public static bool IsFirst(this IPublishedContent content) + { + return content.IsHelper(n => n.Index() == 0); + } + public static HtmlString IsFirst(this IPublishedContent content, string valueIfTrue) + { + return content.IsHelper(n => n.Index() == 0, valueIfTrue); + } + public static HtmlString IsFirst(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() == 0, valueIfTrue, valueIfFalse); + } + public static bool IsNotFirst(this IPublishedContent content) + { + return !content.IsHelper(n => n.Index() == 0); + } + public static HtmlString IsNotFirst(this IPublishedContent content, string valueIfTrue) + { + return content.IsHelper(n => n.Index() != 0, valueIfTrue); + } + public static HtmlString IsNotFirst(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() != 0, valueIfTrue, valueIfFalse); + } + public static bool IsPosition(this IPublishedContent content, int index) + { + return content.IsHelper(n => n.Index() == index); + } + public static HtmlString IsPosition(this IPublishedContent content, int index, string valueIfTrue) + { + return content.IsHelper(n => n.Index() == index, valueIfTrue); + } + public static HtmlString IsPosition(this IPublishedContent content, int index, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() == index, valueIfTrue, valueIfFalse); + } + public static bool IsModZero(this IPublishedContent content, int modulus) + { + return content.IsHelper(n => n.Index() % modulus == 0); + } + public static HtmlString IsModZero(this IPublishedContent content, int modulus, string valueIfTrue) + { + return content.IsHelper(n => n.Index() % modulus == 0, valueIfTrue); + } + public static HtmlString IsModZero(this IPublishedContent content, int modulus, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() % modulus == 0, valueIfTrue, valueIfFalse); + } + + public static bool IsNotModZero(this IPublishedContent content, int modulus) + { + return content.IsHelper(n => n.Index() % modulus != 0); + } + public static HtmlString IsNotModZero(this IPublishedContent content, int modulus, string valueIfTrue) + { + return content.IsHelper(n => n.Index() % modulus != 0, valueIfTrue); + } + public static HtmlString IsNotModZero(this IPublishedContent content, int modulus, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() % modulus != 0, valueIfTrue, valueIfFalse); + } + public static bool IsNotPosition(this IPublishedContent content, int index) + { + return !content.IsHelper(n => n.Index() == index); + } + public static HtmlString IsNotPosition(this IPublishedContent content, int index, string valueIfTrue) + { + return content.IsHelper(n => n.Index() != index, valueIfTrue); + } + public static HtmlString IsNotPosition(this IPublishedContent content, int index, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() != index, valueIfTrue, valueIfFalse); + } + public static bool IsLast(this IPublishedContent content) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + var count = ownersList.Count(); + return content.IsHelper(n => n.Index() == count - 1); + } + public static HtmlString IsLast(this IPublishedContent content, string valueIfTrue) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + var count = ownersList.Count(); + return content.IsHelper(n => n.Index() == count - 1, valueIfTrue); + } + public static HtmlString IsLast(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + var count = ownersList.Count(); + return content.IsHelper(n => n.Index() == count - 1, valueIfTrue, valueIfFalse); + } + public static bool IsNotLast(this IPublishedContent content) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + var count = ownersList.Count(); + return !content.IsHelper(n => n.Index() == count - 1); + } + public static HtmlString IsNotLast(this IPublishedContent content, string valueIfTrue) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + var count = ownersList.Count(); + return content.IsHelper(n => n.Index() != count - 1, valueIfTrue); + } + public static HtmlString IsNotLast(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + var count = ownersList.Count(); + return content.IsHelper(n => n.Index() != count - 1, valueIfTrue, valueIfFalse); + } + public static bool IsEven(this IPublishedContent content) + { + return content.IsHelper(n => n.Index() % 2 == 0); + } + public static HtmlString IsEven(this IPublishedContent content, string valueIfTrue) + { + return content.IsHelper(n => n.Index() % 2 == 0, valueIfTrue); + } + public static HtmlString IsEven(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() % 2 == 0, valueIfTrue, valueIfFalse); + } + public static bool IsOdd(this IPublishedContent content) + { + return content.IsHelper(n => n.Index() % 2 == 1); + } + public static HtmlString IsOdd(this IPublishedContent content, string valueIfTrue) + { + return content.IsHelper(n => n.Index() % 2 == 1, valueIfTrue); + } + public static HtmlString IsOdd(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() % 2 == 1, valueIfTrue, valueIfFalse); + } + public static bool IsEqual(this IPublishedContent content, IPublishedContent other) + { + return content.IsHelper(n => n.Id == other.Id); + } + public static HtmlString IsEqual(this IPublishedContent content, IPublishedContent other, string valueIfTrue) + { + return content.IsHelper(n => n.Id == other.Id, valueIfTrue); + } + public static HtmlString IsEqual(this IPublishedContent content, IPublishedContent other, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Id == other.Id, valueIfTrue, valueIfFalse); + } + public static bool IsNotEqual(this IPublishedContent content, IPublishedContent other) + { + return content.IsHelper(n => n.Id != other.Id); + } + public static HtmlString IsNotEqual(this IPublishedContent content, IPublishedContent other, string valueIfTrue) + { + return content.IsHelper(n => n.Id != other.Id, valueIfTrue); + } + public static HtmlString IsNotEqual(this IPublishedContent content, IPublishedContent other, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Id != other.Id, valueIfTrue, valueIfFalse); + } + public static bool IsDescendant(this IPublishedContent content, IPublishedContent other) + { + var ancestors = content.Ancestors(); + return content.IsHelper(n => ancestors.FirstOrDefault(ancestor => ancestor.Id == other.Id) != null); + } + public static HtmlString IsDescendant(this IPublishedContent content, IPublishedContent other, string valueIfTrue) + { + var ancestors = content.Ancestors(); + return content.IsHelper(n => ancestors.FirstOrDefault(ancestor => ancestor.Id == other.Id) != null, valueIfTrue); + } + public static HtmlString IsDescendant(this IPublishedContent content, IPublishedContent other, string valueIfTrue, string valueIfFalse) + { + var ancestors = content.Ancestors(); + return content.IsHelper(n => ancestors.FirstOrDefault(ancestor => ancestor.Id == other.Id) != null, valueIfTrue, valueIfFalse); + } + public static bool IsDescendantOrSelf(this IPublishedContent content, IPublishedContent other) + { + var ancestors = content.AncestorsOrSelf(); + return content.IsHelper(n => ancestors.FirstOrDefault(ancestor => ancestor.Id == other.Id) != null); + } + public static HtmlString IsDescendantOrSelf(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue) + { + var ancestors = content.AncestorsOrSelf(); + return content.IsHelper(n => ancestors.FirstOrDefault(ancestor => ancestor.Id == other.Id) != null, valueIfTrue); + } + public static HtmlString IsDescendantOrSelf(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + var ancestors = content.AncestorsOrSelf(); + return content.IsHelper(n => ancestors.FirstOrDefault(ancestor => ancestor.Id == other.Id) != null, valueIfTrue, valueIfFalse); + } + public static bool IsAncestor(this IPublishedContent content, DynamicPublishedContentBase other) + { + var descendants = content.Descendants(); + return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null); + } + public static HtmlString IsAncestor(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue) + { + var descendants = content.Descendants(); + return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null, valueIfTrue); + } + public static HtmlString IsAncestor(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + var descendants = content.Descendants(); + return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null, valueIfTrue, valueIfFalse); + } + public static bool IsAncestorOrSelf(this IPublishedContent content, DynamicPublishedContentBase other) + { + var descendants = content.DescendantsOrSelf(); + return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null); + } + public static HtmlString IsAncestorOrSelf(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue) + { + var descendants = content.DescendantsOrSelf(); + return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null, valueIfTrue); + } + public static HtmlString IsAncestorOrSelf(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + var descendants = content.DescendantsOrSelf(); + return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null, valueIfTrue, valueIfFalse); + } + private static bool IsHelper(this IPublishedContent content, Func test) + { + return test(content); + } + private static HtmlString IsHelper(this IPublishedContent content, Func test, string valueIfTrue) + { + return content.IsHelper(test, valueIfTrue, string.Empty); + } + private static HtmlString IsHelper(this IPublishedContent content, Func test, string valueIfTrue, string valueIfFalse) + { + return test(content) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); + } + #endregion + + #region Ancestors + + public static IEnumerable Ancestors(this IPublishedContent content, int level) + { + return content.Ancestors(n => n.Level <= level); + } + public static IEnumerable Ancestors(this IPublishedContent content, string nodeTypeAlias) + { + return content.Ancestors(n => n.DocumentTypeAlias == nodeTypeAlias); + } + public static IEnumerable Ancestors(this IPublishedContent content) + { + return content.Ancestors(n => true); + } + public static IEnumerable Ancestors(this IPublishedContent content, Func func) + { + var ancestorList = new List(); + var node = content; + while (node != null) + { + if (node.Level == 1) break; + var parent = node.Parent; + if (parent == null) + { + break; + } + if (content != parent) + { + node = parent; + if (func(node)) + { + ancestorList.Add(node); + } + } + else + { + break; + } + } + ancestorList.Reverse(); + return ancestorList; + } + + public static IPublishedContent AncestorOrSelf(this IPublishedContent content) + { + //TODO: Why is this query like this?? + return content.AncestorOrSelf(node => node.Level == 1); + } + public static IPublishedContent AncestorOrSelf(this IPublishedContent content, int level) + { + return content.AncestorOrSelf(node => node.Level == level); + } + public static IPublishedContent AncestorOrSelf(this IPublishedContent content, string nodeTypeAlias) + { + return content.AncestorOrSelf(node => node.DocumentTypeAlias == nodeTypeAlias); + } + public static IPublishedContent AncestorOrSelf(this IPublishedContent content, Func func) + { + var node = content; + while (node != null) + { + if (func(node)) return node; + var parent = node.Parent; + if (parent == null) + { + return null; + } + if (content != parent) + { + node = parent; + } + else + { + return node; + } + } + return null; + } + + public static IEnumerable AncestorsOrSelf(this IPublishedContent content, Func func) + { + var ancestorList = new List(); + var node = content; + ancestorList.Add(node); + while (node != null) + { + if (node.Level == 1) break; + var parent = node.Parent; + if (parent == null) + { + break; + } + if (content != parent) + { + node = parent; + if (func(node)) + { + ancestorList.Add(node); + } + } + else + { + break; + } + } + ancestorList.Reverse(); + return ancestorList; + } + public static IEnumerable AncestorsOrSelf(this IPublishedContent content) + { + return content.AncestorsOrSelf(n => true); + } + public static IEnumerable AncestorsOrSelf(this IPublishedContent content, string nodeTypeAlias) + { + return content.AncestorsOrSelf(n => n.DocumentTypeAlias == nodeTypeAlias); + } + public static IEnumerable AncestorsOrSelf(this IPublishedContent content, int level) + { + return content.AncestorsOrSelf(n => n.Level <= level); + } + + #endregion + + #region Descendants + public static IEnumerable Descendants(this IPublishedContent content, string nodeTypeAlias) + { + return content.Descendants(p => p.DocumentTypeAlias == nodeTypeAlias); + } + public static IEnumerable Descendants(this IPublishedContent content, int level) + { + return content.Descendants(p => p.Level >= level); + } + public static IEnumerable Descendants(this IPublishedContent content) + { + return content.Descendants(n => true); + } + internal static IEnumerable Descendants(this IPublishedContent content, Func func) + { + return content.Children.Map(func, (IPublishedContent n) => n.Children); + } + public static IEnumerable DescendantsOrSelf(this IPublishedContent content, int level) + { + return content.DescendantsOrSelf(p => p.Level >= level); + } + public static IEnumerable DescendantsOrSelf(this IPublishedContent content, string nodeTypeAlias) + { + return content.DescendantsOrSelf(p => p.DocumentTypeAlias == nodeTypeAlias); + } + public static IEnumerable DescendantsOrSelf(this IPublishedContent content) + { + return content.DescendantsOrSelf(p => true); + } + internal static IEnumerable DescendantsOrSelf(this IPublishedContent content, Func func) + { + if (content != null) + { + var thisNode = new List(); + if (func(content)) + { + thisNode.Add(content); + } + var flattenedNodes = content.Children.Map(func, (IPublishedContent n) => n.Children); + return thisNode.Concat(flattenedNodes).ToList().ConvertAll(dynamicBackingItem => new DynamicPublishedContentBase(dynamicBackingItem)); + } + return Enumerable.Empty(); + } + #endregion + + #region Traversal + + public static IPublishedContent Up(this IPublishedContent content) + { + return content.Up(0); + } + public static IPublishedContent Up(this IPublishedContent content, int number) + { + if (number == 0) + { + return content.Parent; + } + while ((content = content.Parent) != null && --number >= 0) ; + return content; + } + public static IPublishedContent Up(this IPublishedContent content, string nodeTypeAlias) + { + if (string.IsNullOrEmpty(nodeTypeAlias)) + { + return content.Parent; + } + while ((content = content.Parent) != null && content.DocumentTypeAlias != nodeTypeAlias) ; + return content; + } + public static IPublishedContent Down(this IPublishedContent content) + { + return content.Down(0); + } + public static IPublishedContent Down(this IPublishedContent content, int number) + { + var children = content.Children; + if (number == 0) + { + return children.First(); + } + var working = content; + while (number-- >= 0) + { + working = children.First(); + children = new DynamicPublishedContentList(working.Children); + } + return working; + } + public static IPublishedContent Down(this IPublishedContent content, string nodeTypeAlias) + { + if (string.IsNullOrEmpty(nodeTypeAlias)) + { + var children = content.Children; + return children.First(); + } + return content.Descendants(nodeTypeAlias).FirstOrDefault(); + } + + public static IPublishedContent Next(this IPublishedContent content) + { + return content.Next(0); + } + public static IPublishedContent Next(this IPublishedContent content, int number) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + + var container = ownersList.ToList(); + var currentIndex = container.FindIndex(n => n.Id == content.Id); + if (currentIndex != -1) + { + return container.ElementAtOrDefault(currentIndex + (number + 1)); + } + throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", content.Id)); + } + + public static IPublishedContent Next(this IPublishedContent content, string nodeTypeAlias) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + + var container = ownersList.ToList(); + var currentIndex = container.FindIndex(n => n.Id == content.Id); + if (currentIndex != -1) + { + var newIndex = container.FindIndex(currentIndex, n => n.DocumentTypeAlias == nodeTypeAlias); + return newIndex != -1 + ? container.ElementAt(newIndex) + : null; + } + throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", content.Id)); + } + public static IPublishedContent Previous(this IPublishedContent content) + { + return content.Previous(0); + } + public static IPublishedContent Previous(this IPublishedContent content, int number) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + + var container = ownersList.ToList(); + var currentIndex = container.FindIndex(n => n.Id == content.Id); + if (currentIndex != -1) + { + return container.ElementAtOrDefault(currentIndex + (number - 1)); + } + throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", content.Id)); + } + public static IPublishedContent Previous(this IPublishedContent content, string nodeTypeAlias) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + + var container = ownersList.ToList(); + int currentIndex = container.FindIndex(n => n.Id == content.Id); + if (currentIndex != -1) + { + var previousNodes = container.Take(currentIndex).ToList(); + int newIndex = previousNodes.FindIndex(n => n.DocumentTypeAlias == nodeTypeAlias); + if (newIndex != -1) + { + return container.ElementAt(newIndex); + } + return null; + } + throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", content.Id)); + } + public static IPublishedContent Sibling(this IPublishedContent content, int number) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + + var container = ownersList.ToList(); + var currentIndex = container.FindIndex(n => n.Id == content.Id); + if (currentIndex != -1) + { + return container.ElementAtOrDefault(currentIndex + number); + } + throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", content.Id)); + } + public static IPublishedContent Sibling(this IPublishedContent content, string nodeTypeAlias) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + + var container = ownersList.ToList(); + var currentIndex = container.FindIndex(n => n.Id == content.Id); + if (currentIndex != -1) + { + var workingIndex = currentIndex + 1; + while (workingIndex != currentIndex) + { + var working = container.ElementAtOrDefault(workingIndex); + if (working != null && working.DocumentTypeAlias == nodeTypeAlias) + { + return working; + } + workingIndex++; + if (workingIndex > container.Count) + { + workingIndex = 0; + } + } + return null; + } + throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", content.Id)); + } + #endregion /// /// Returns a DataTable object for the IPublishedContent diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index cc3faaf33b..50017de4e1 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -240,6 +240,7 @@ Properties\SolutionInfo.cs + @@ -250,7 +251,6 @@ - diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index d660808d90..3c65470112 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -487,7 +487,7 @@ namespace Umbraco.Web var doc = store.GetDocumentById(UmbracoContext.Current, id); return doc == null ? new DynamicNull() - : new DynamicPublishedContent(doc).AsDynamic(); + : new DynamicPublishedContentBase(doc).AsDynamic(); } private dynamic DocumentById(string id, IPublishedStore store) @@ -502,7 +502,7 @@ namespace Umbraco.Web { var nodes = ids.Select(eachId => DocumentById(eachId, store)) .Where(x => !TypeHelper.IsTypeAssignableFrom(x)) - .Cast(); + .Cast(); return new DynamicPublishedContentList(nodes); } @@ -510,7 +510,7 @@ namespace Umbraco.Web { var nodes = ids.Select(eachId => DocumentById(eachId, store)) .Where(x => !TypeHelper.IsTypeAssignableFrom(x)) - .Cast(); + .Cast(); return new DynamicPublishedContentList(nodes); } diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index a58b362f4b..2f86ed3407 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -226,9 +226,6 @@ namespace Umbraco.Web CultureDictionaryFactoryResolver.Current = new CultureDictionaryFactoryResolver( new DefaultCultureDictionaryFactory()); - //This exists only because the new business logic classes aren't created yet and we want Dynamics in the Core project, - //see the note in the DynamicNodeDataSourceResolver.cs class - DynamicPublishedContentDataSourceResolver.Current = new DynamicPublishedContentDataSourceResolver(new DefaultDynamicPublishedContentDataSource()); } } diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs index 52fb4f53df..9725390d3e 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs @@ -30,6 +30,12 @@ namespace umbraco.MacroEngines { public class DynamicNode : DynamicObject { + /// + /// This callback is used only so we can set it dynamically for use in unit tests + /// + internal static Func GetDataTypeCallback = (docTypeAlias, propertyAlias) => + ContentType.GetDataType(docTypeAlias, propertyAlias); + #region consts // these are private readonlys as const can't be Guids private readonly Guid DATATYPE_YESNO_GUID = new Guid("38b352c1-e9f8-4fd8-9324-9a2eab06d97a"); @@ -503,6 +509,11 @@ namespace umbraco.MacroEngines } } + private static Guid GetDataType(string docTypeAlias, string propertyAlias) + { + return GetDataTypeCallback(docTypeAlias, propertyAlias); + } + public override bool TryGetMember(GetMemberBinder binder, out object result) { @@ -542,9 +553,8 @@ namespace umbraco.MacroEngines } //contextAlias is the node which the property data was returned from - //Guid dataType = ContentType.GetDataType(data.ContextAlias, data.Alias); - //SD: replaced with our temporary resolver so that we can unit test this properly, this is what DynamicPublishedContent uses until we create our new data access layer. - var dataType = DynamicPublishedContentDataSourceResolver.Current.DataSource.GetDataType(data.ContextAlias, data.Alias); + //Guid dataType = ContentType.GetDataType(data.ContextAlias, data.Alias); + var dataType = GetDataType(data.ContextAlias, data.Alias); var staticMapping = UmbracoSettings.RazorDataTypeModelStaticMapping.FirstOrDefault(mapping => { diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicPublishedContentExtensions.cs b/src/umbraco.MacroEngines/RazorDynamicNode/PublishedContentExtensions.cs similarity index 86% rename from src/umbraco.MacroEngines/RazorDynamicNode/DynamicPublishedContentExtensions.cs rename to src/umbraco.MacroEngines/RazorDynamicNode/PublishedContentExtensions.cs index 1221331f08..6116f44fbf 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicPublishedContentExtensions.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/PublishedContentExtensions.cs @@ -2,8 +2,10 @@ using System; using System.Collections.Generic; using System.Data; using System.Linq; +using Umbraco.Core; using Umbraco.Core.Dynamics; using Umbraco.Core.Models; +using Umbraco.Web; using umbraco.NodeFactory; using umbraco.interfaces; @@ -12,7 +14,7 @@ namespace umbraco.MacroEngines.Library /// /// Extension methods for converting DynamicPublishedContent to INode /// - internal static class DynamicPublishedContentExtensions + internal static class PublishedContentExtensions { internal static IProperty ConvertToNodeProperty(this IDocumentProperty prop) @@ -20,7 +22,7 @@ namespace umbraco.MacroEngines.Library return new PropertyResult(prop.Alias, prop.Value.ToString(), prop.Version); } - internal static INode ConvertToNode(this DynamicPublishedContent doc) + internal static INode ConvertToNode(this IPublishedContent doc) { var node = new ConvertedNode(doc); return node; @@ -31,9 +33,9 @@ namespace umbraco.MacroEngines.Library /// private class ConvertedNode : INode { - private readonly DynamicPublishedContent _doc; + private readonly IPublishedContent _doc; - public ConvertedNode(DynamicPublishedContent doc) + public ConvertedNode(IPublishedContent doc) { _doc = doc; template = doc.TemplateId; @@ -109,12 +111,12 @@ namespace umbraco.MacroEngines.Library public DataTable ChildrenAsTable() { - throw new NotImplementedException(); + return _doc.ChildrenAsTable(); } public DataTable ChildrenAsTable(string nodeTypeAliasFilter) { - throw new NotImplementedException(); + return _doc.ChildrenAsTable(nodeTypeAliasFilter); } } } diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs b/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs index 0a01b47cb1..e756fdf534 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Web.Mvc; using Umbraco.Core.Dynamics; +using Umbraco.Core.Models; using Umbraco.Web; using umbraco.interfaces; using System.Xml.Linq; diff --git a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj index f8b48e64ca..ee3903ac40 100644 --- a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj +++ b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj @@ -86,7 +86,7 @@ - + From 54e5140d6a732c1390bbef3f1eab5af26ac926fa Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Thu, 4 Oct 2012 03:26:56 +0500 Subject: [PATCH 4/4] Added more extension methods to our strongly typed IPublishedContent object and IEnumerable to match all of the available methods that are on the DynamicPublishedContent object. Added more unit tests for all of these classes. Moved some of the Dynamic objects into the web project which simplifies things quite a bit as some of these classes require access to the biz logic layer. Now we have intellisense for all of the nice magical methods that were on DynamicPublishedContent on our strongly typed object! --- src/Umbraco.Core/Dynamics/ExtensionMethods.cs | 20 +- src/Umbraco.Core/Dynamics/PropertyResult.cs | 9 +- .../Models/DynamicPublishedContentBase.cs | 843 ------------------ src/Umbraco.Core/Models/IPublishedContent.cs | 4 +- ...operty.cs => IPublishedContentProperty.cs} | 2 +- .../PublishedContentExtensions.cs | 106 ++- src/Umbraco.Core/Umbraco.Core.csproj | 11 +- .../DynamicDocument/DocumentTests.cs | 8 +- .../DynamicDocumentTestsBase.cs | 72 +- ...cPublishedContentCustomExtensionMethods.cs | 9 +- .../DynamicPublishedContentTests.cs | 9 +- .../DynamicDocument/PublishedContentTests.cs | 324 +++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + src/Umbraco.Web/DefaultPublishedMediaStore.cs | 12 +- ...DynamicPublishedContentSearchExtensions.cs | 9 +- .../Dynamics/DynamicExpression.cs | 3 +- .../Dynamics/DynamicGrouping.cs | 25 +- ...namicPublishedContentIdEqualityComparer.cs | 10 +- .../DynamicPublishedContentListOrdering.cs | 154 ++-- .../Dynamics/DynamicQueryable.cs | 61 +- .../Dynamics/ExpressionParser.cs | 62 +- src/Umbraco.Web/Dynamics/ExtensionMethods.cs | 32 + .../Dynamics/Grouping.cs | 11 +- src/Umbraco.Web/ExamineExtensions.cs | 3 +- .../Models/DynamicPublishedContent.cs | 793 +++++++++++++++- .../Models/DynamicPublishedContentList.cs | 73 +- src/Umbraco.Web/Models/XmlPublishedContent.cs | 8 +- ...erty.cs => XmlPublishedContentProperty.cs} | 6 +- src/Umbraco.Web/Mvc/RenderViewPage.cs | 3 +- src/Umbraco.Web/PublishedContentExtensions.cs | 95 +- src/Umbraco.Web/Umbraco.Web.csproj | 11 +- src/Umbraco.Web/UmbracoHelper.cs | 7 +- .../RazorDynamicNode/DynamicExpression.cs | 16 +- .../RazorDynamicNode/DynamicQueryable.cs | 10 +- .../RazorDynamicNode/ExpressionParser.cs | 2 +- .../PublishedContentExtensions.cs | 2 +- .../RazorDynamicNode/RazorLibraryCore.cs | 1 + 37 files changed, 1648 insertions(+), 1179 deletions(-) delete mode 100644 src/Umbraco.Core/Models/DynamicPublishedContentBase.cs rename src/Umbraco.Core/Models/{IDocumentProperty.cs => IPublishedContentProperty.cs} (70%) create mode 100644 src/Umbraco.Tests/DynamicDocument/PublishedContentTests.cs rename src/{Umbraco.Core => Umbraco.Web}/Dynamics/DynamicExpression.cs (94%) rename src/{Umbraco.Core => Umbraco.Web}/Dynamics/DynamicGrouping.cs (65%) rename src/{Umbraco.Core => Umbraco.Web}/Dynamics/DynamicPublishedContentIdEqualityComparer.cs (71%) rename src/{Umbraco.Core => Umbraco.Web}/Dynamics/DynamicPublishedContentListOrdering.cs (69%) rename src/{Umbraco.Core => Umbraco.Web}/Dynamics/DynamicQueryable.cs (86%) rename src/{Umbraco.Core => Umbraco.Web}/Dynamics/ExpressionParser.cs (92%) create mode 100644 src/Umbraco.Web/Dynamics/ExtensionMethods.cs rename src/{Umbraco.Core => Umbraco.Web}/Dynamics/Grouping.cs (86%) rename src/{Umbraco.Core => Umbraco.Web}/Models/DynamicPublishedContentList.cs (87%) rename src/Umbraco.Web/Models/{XmlDocumentProperty.cs => XmlPublishedContentProperty.cs} (88%) diff --git a/src/Umbraco.Core/Dynamics/ExtensionMethods.cs b/src/Umbraco.Core/Dynamics/ExtensionMethods.cs index 90c8828584..f36d4f255d 100644 --- a/src/Umbraco.Core/Dynamics/ExtensionMethods.cs +++ b/src/Umbraco.Core/Dynamics/ExtensionMethods.cs @@ -33,25 +33,7 @@ namespace Umbraco.Core.Dynamics } - public static DynamicPublishedContentList Random(this DynamicPublishedContentList all, int min, int max) - { - //get a random number generator - Random r = new Random(); - //choose the number of elements to be returned between Min and Max - int Number = r.Next(min, max); - //Call the other method - return Random(all, Number); - } - public static DynamicPublishedContentList Random(this DynamicPublishedContentList all, int max) - { - //Randomly order the items in the set by a Guid, Take the correct number, and return this wrapped in a new DynamicNodeList - return new DynamicPublishedContentList(all.Items.OrderBy(x => Guid.NewGuid()).Take(max)); - } - - public static DynamicPublishedContentBase Random(this DynamicPublishedContentList all) - { - return all.Items.OrderBy(x => Guid.NewGuid()).First(); - } + public static bool ContainsAny(this string haystack, IEnumerable needles) { diff --git a/src/Umbraco.Core/Dynamics/PropertyResult.cs b/src/Umbraco.Core/Dynamics/PropertyResult.cs index bcabfd05e4..bb2165d184 100644 --- a/src/Umbraco.Core/Dynamics/PropertyResult.cs +++ b/src/Umbraco.Core/Dynamics/PropertyResult.cs @@ -5,9 +5,9 @@ using System.Web; namespace Umbraco.Core.Dynamics { - internal class PropertyResult : IDocumentProperty, IHtmlString + internal class PropertyResult : IPublishedContentProperty, IHtmlString { - internal PropertyResult(IDocumentProperty source, PropertyResultType type) + internal PropertyResult(IPublishedContentProperty source, PropertyResultType type) { if (source == null) throw new ArgumentNullException("source"); @@ -46,11 +46,6 @@ namespace Umbraco.Core.Dynamics public Guid Version { get; private set; } - - public bool HasValue() - { - return !ValueAsString.IsNullOrWhiteSpace(); - } /// /// The Id of the document for which this property belongs to diff --git a/src/Umbraco.Core/Models/DynamicPublishedContentBase.cs b/src/Umbraco.Core/Models/DynamicPublishedContentBase.cs deleted file mode 100644 index b7c0c277ec..0000000000 --- a/src/Umbraco.Core/Models/DynamicPublishedContentBase.cs +++ /dev/null @@ -1,843 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Dynamic; -using System.Linq; -using System.Web; -using Umbraco.Core.Configuration; -using Umbraco.Core.Dynamics; -using Umbraco.Core.PropertyEditors; -using System.Reflection; -using System.Xml.Linq; - -namespace Umbraco.Core.Models -{ - - /// - /// The base dynamic model for views - /// - public class DynamicPublishedContentBase : DynamicObject, IPublishedContent - { - protected IPublishedContent PublishedContent { get; private set; } - private DynamicPublishedContentList _cachedChildren; - private readonly ConcurrentDictionary _cachedMemberOutput = new ConcurrentDictionary(); - - #region Constructors - - public DynamicPublishedContentBase(IPublishedContent node) - { - if (node == null) throw new ArgumentNullException("node"); - PublishedContent = node; - } - - /// - /// Returns an empty/blank DynamicPublishedContent, this is used for special case scenarios - /// - /// - internal static DynamicPublishedContentBase Empty() - { - return new DynamicPublishedContentBase(); - } - - private DynamicPublishedContentBase() - { - } - - #endregion - - public dynamic AsDynamic() - { - return this; - } - - public bool HasProperty(string name) - { - if (PublishedContent != null) - { - try - { - var prop = GetUserProperty(name); - - return (prop != null); - } - catch (Exception) - { - return false; - } - } - return false; - } - - /// - /// Attempts to call a method on the dynamic object - /// - /// - /// - /// - /// - public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) - { - //TODO: We MUST cache the result here, it is very expensive to keep finding extension methods! - - try - { - //Property? - result = typeof(DynamicPublishedContentBase).InvokeMember(binder.Name, - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public | - System.Reflection.BindingFlags.GetProperty, - null, - this, - args); - return true; - } - catch (MissingMethodException) - { - try - { - //Static or Instance Method? - result = typeof(DynamicPublishedContentBase).InvokeMember(binder.Name, - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public | - System.Reflection.BindingFlags.Static | - System.Reflection.BindingFlags.InvokeMethod, - null, - this, - args); - return true; - } - catch (MissingMethodException) - { - try - { - result = ExecuteExtensionMethod(args, binder.Name); - return true; - } - catch (TargetInvocationException) - { - result = new DynamicNull(); - return true; - } - - catch - { - //TODO: LOg this! - - result = null; - return false; - } - - } - - - } - catch - { - result = null; - return false; - } - - } - - private object ExecuteExtensionMethod(object[] args, string name) - { - object result = null; - - var methodTypesToFind = new[] - { - typeof(DynamicPublishedContentBase) - }; - - //find known extension methods that match the first type in the list - MethodInfo toExecute = null; - foreach (var t in methodTypesToFind) - { - toExecute = ExtensionMethodFinder.FindExtensionMethod(t, args, name, false); - if (toExecute != null) - break; - } - - if (toExecute != null) - { - var genericArgs = (new[] { this }).Concat(args); - result = toExecute.Invoke(null, genericArgs.ToArray()); - } - else - { - throw new MissingMethodException(); - } - if (result != null) - { - if (result is IPublishedContent) - { - result = new DynamicPublishedContentBase((IPublishedContent)result); - } - if (result is IEnumerable) - { - result = new DynamicPublishedContentList((IEnumerable)result); - } - if (result is IEnumerable) - { - result = new DynamicPublishedContentList((IEnumerable)result); - } - } - return result; - } - - /// - /// Attempts to return a custom member (generally based on a string match) - /// - /// - /// - protected virtual Attempt TryGetCustomMember(GetMemberBinder binder) - { - if (binder.Name.InvariantEquals("ChildrenAsList") || binder.Name.InvariantEquals("Children")) - { - return new Attempt(true, Children); - } - - if (binder.Name.InvariantEquals("parentId")) - { - var parent = ((IPublishedContent) this).Parent; - if (parent == null) - { - throw new InvalidOperationException(string.Format("The node {0} does not have a parent", Id)); - } - return new Attempt(true, parent.Id); - } - return Attempt.False; - } - - /// - /// Attempts to return the children by the document type's alias (for example: CurrentPage.NewsItems where NewsItem is the - /// document type alias) - /// - /// - /// - /// - /// This method will work by both the plural and non-plural alias (i.e. NewsItem and NewsItems) - /// - protected virtual Attempt TryGetChildrenByAlias(GetMemberBinder binder) - { - - var filteredTypeChildren = PublishedContent.Children - .Where(x => x.DocumentTypeAlias.InvariantEquals(binder.Name) || x.DocumentTypeAlias.MakePluralName().InvariantEquals(binder.Name)) - .ToArray(); - if (filteredTypeChildren.Any()) - { - return new Attempt(true, - new DynamicPublishedContentList(filteredTypeChildren.Select(x => new DynamicPublishedContentBase(x)))); - } - return Attempt.False; - } - - /// - /// Attempts to return a member based on the reflected document property - /// - /// - /// - protected virtual Attempt TryGetDocumentProperty(GetMemberBinder binder) - { - - var reflectedProperty = GetReflectedProperty(binder.Name); - var result = reflectedProperty != null - ? reflectedProperty.Value - : null; - - return result == null - ? Attempt.False - : new Attempt(true, result); - } - - /// - /// Attempts to return a member based on a user defined umbraco property - /// - /// - /// - protected virtual Attempt TryGetUserProperty(GetMemberBinder binder) - { - var name = binder.Name; - var recursive = false; - if (name.StartsWith("_")) - { - name = name.Substring(1, name.Length - 1); - recursive = true; - } - - var userProperty = GetUserProperty(name, recursive); - - if (userProperty == null) - { - return Attempt.False; - } - - var result = userProperty.Value; - - if (PublishedContent.DocumentTypeAlias == null && userProperty.Alias == null) - { - throw new InvalidOperationException("No node alias or property alias available. Unable to look up the datatype of the property you are trying to fetch."); - } - - return new Attempt(true, result); - - } - - /// - /// Returns the member match methods in the correct order and is used in the TryGetMember method. - /// - /// - protected virtual IEnumerable>> GetMemberMatchMethods() - { - var memberMatchMethods = new List>> - { - TryGetCustomMember, //match custom members - TryGetUserProperty, //then match custom user defined umbraco properties - TryGetChildrenByAlias, //then try to match children based on doc type alias - TryGetDocumentProperty //then try to match on a reflected document property - }; - return memberMatchMethods; - } - - /// - /// Try to return an object based on the dynamic member accessor - /// - /// - /// - /// - /// - /// TODO: SD: This will alwasy return true so that no exceptions are generated, this is only because this is how the - /// old DynamicNode worked, I'm not sure if this is the correct/expected functionality but I've left it like that. - /// IMO I think this is incorrect and it would be better to throw an exception for something that is not supported! - /// - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - if (binder == null) throw new ArgumentNullException("binder"); - - var name = binder.Name; - - //check the cache first! - if (_cachedMemberOutput.TryGetValue(name, out result)) - { - return true; - } - - //loop through each member match method and execute it. - //If it is successful, cache the result and return it. - foreach (var attempt in GetMemberMatchMethods() - .Select(m => m(binder)) - .Where(attempt => attempt.Success)) - { - result = attempt.Result; - //cache the result so we don't have to re-process the whole thing - _cachedMemberOutput.TryAdd(name, result); - return true; - } - - //if property access, type lookup and member invoke all failed - //at this point, we're going to return null - //instead, we return a DynamicNull - see comments in that file - //this will let things like Model.ChildItem work and return nothing instead of crashing - - //.Where explictly checks for this type - //and will make it false - //which means backwards equality (&& property != true) will pass - //forwwards equality (&& property or && property == true) will fail - result = new DynamicNull(); - - //alwasy return true if we haven't thrown an exception though I'm wondering if we return 'false' if .Net throws an exception for us?? - return true; - } - - /// - /// Returns a property defined on the document object as a member property using reflection - /// - /// - /// - private PropertyResult GetReflectedProperty(string alias) - { - return GetPropertyInternal(alias, PublishedContent, false); - } - - /// - /// Return a user defined property - /// - /// - /// - /// - internal PropertyResult GetUserProperty(string alias, bool recursive = false) - { - if (!recursive) - { - return GetPropertyInternal(alias, PublishedContent); - } - var context = this; - var prop = GetPropertyInternal(alias, PublishedContent); - while (prop == null || !prop.HasValue()) - { - var parent = ((IPublishedContent) context).Parent; - if (parent == null) break; - prop = context.GetPropertyInternal(alias, context.PublishedContent); - } - return prop; - } - - - private PropertyResult GetPropertyInternal(string alias, IPublishedContent content, bool checkUserProperty = true) - { - if (alias.IsNullOrWhiteSpace()) throw new ArgumentNullException("alias"); - if (content == null) throw new ArgumentNullException("content"); - - //if we're looking for a user defined property - if (checkUserProperty) - { - var prop = content.GetProperty(alias); - - return prop == null - ? null - : new PropertyResult(prop, PropertyResultType.UserProperty) - { - DocumentTypeAlias = content.DocumentTypeAlias, - DocumentId = content.Id - }; - } - - //reflect - - Func> getMember = - memberAlias => - { - try - { - return new Attempt(true, - content.GetType().InvokeMember(memberAlias, - System.Reflection.BindingFlags.GetProperty | - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public, - null, - content, - null)); - } - catch (MissingMethodException ex) - { - return new Attempt(ex); - } - }; - - //try with the current casing - var attempt = getMember(alias); - if (!attempt.Success) - { - //if we cannot get with the current alias, try changing it's case - attempt = alias[0].IsUpperCase() - ? getMember(alias.ConvertCase(StringAliasCaseType.CamelCase)) - : getMember(alias.ConvertCase(StringAliasCaseType.PascalCase)); - } - - return !attempt.Success - ? null - : new PropertyResult(alias, attempt.Result, Guid.Empty, PropertyResultType.ReflectedProperty) - { - DocumentTypeAlias = content.DocumentTypeAlias, - DocumentId = content.Id - }; - } - - - - //public DynamicNode Media(string propertyAlias) - //{ - // if (_n != null) - // { - // IProperty prop = _n.GetProperty(propertyAlias); - // if (prop != null) - // { - // int mediaNodeId; - // if (int.TryParse(prop.Value, out mediaNodeId)) - // { - // return _razorLibrary.Value.MediaById(mediaNodeId); - // } - // } - // return null; - // } - // return null; - //} - //public bool IsProtected - //{ - // get - // { - // if (_n != null) - // { - // return umbraco.library.IsProtected(_n.Id, _n.Path); - // } - // return false; - // } - //} - //public bool HasAccess - //{ - // get - // { - // if (_n != null) - // { - // return umbraco.library.HasAccess(_n.Id, _n.Path); - // } - // return true; - // } - //} - - //public string Media(string propertyAlias, string mediaPropertyAlias) - //{ - // if (_n != null) - // { - // IProperty prop = _n.GetProperty(propertyAlias); - // if (prop == null && propertyAlias.Substring(0, 1).ToUpper() == propertyAlias.Substring(0, 1)) - // { - // prop = _n.GetProperty(propertyAlias.Substring(0, 1).ToLower() + propertyAlias.Substring((1))); - // } - // if (prop != null) - // { - // int mediaNodeId; - // if (int.TryParse(prop.Value, out mediaNodeId)) - // { - // umbraco.cms.businesslogic.media.Media media = new cms.businesslogic.media.Media(mediaNodeId); - // if (media != null) - // { - // Property mprop = media.getProperty(mediaPropertyAlias); - // // check for nicer support of Pascal Casing EVEN if alias is camelCasing: - // if (prop == null && mediaPropertyAlias.Substring(0, 1).ToUpper() == mediaPropertyAlias.Substring(0, 1)) - // { - // mprop = media.getProperty(mediaPropertyAlias.Substring(0, 1).ToLower() + mediaPropertyAlias.Substring((1))); - // } - // if (mprop != null) - // { - // return string.Format("{0}", mprop.Value); - // } - // } - // } - // } - // } - // return null; - //} - - public int TemplateId - { - get { return PublishedContent.TemplateId; } - } - - public int SortOrder - { - get { return PublishedContent.SortOrder; } - } - - public string Name - { - get { return PublishedContent.Name; } - } - - public bool Visible - { - get - { - - var umbracoNaviHide = GetUserProperty("umbracoNaviHide"); - if (umbracoNaviHide != null) - { - return umbracoNaviHide.Value.ToString().Trim() != "1"; - } - return true; - } - } - - public string UrlName - { - get { return PublishedContent.UrlName; } - } - - public string DocumentTypeAlias - { - get { return PublishedContent.DocumentTypeAlias; } - } - - public string WriterName - { - get { return PublishedContent.WriterName; } - } - - public string CreatorName - { - get { return PublishedContent.CreatorName; } - } - - public int WriterId - { - get { return PublishedContent.WriterId; } - } - - public int CreatorId - { - get { return PublishedContent.CreatorId; } - } - - public string Path - { - get { return PublishedContent.Path; } - } - - public DateTime CreateDate - { - get { return PublishedContent.CreateDate; } - } - - public int Id - { - get { return PublishedContent.Id; } - } - - public DateTime UpdateDate - { - get { return PublishedContent.UpdateDate; } - } - - public Guid Version - { - get { return PublishedContent.Version; } - } - - public int Level - { - get { return PublishedContent.Level; } - } - - public IEnumerable Properties - { - get { return PublishedContent.Properties; } - } - - public DynamicPublishedContentList Children - { - get - { - if (_cachedChildren == null) - { - var children = PublishedContent.Children; - //testing, think this must be a special case for the root node ? - if (!children.Any() && PublishedContent.Id == 0) - { - _cachedChildren = new DynamicPublishedContentList(new List { new DynamicPublishedContentBase(this.PublishedContent) }); - } - else - { - _cachedChildren = new DynamicPublishedContentList(PublishedContent.Children.Select(x => new DynamicPublishedContentBase(x))); - } - } - return _cachedChildren; - } - } - - #region GetProperty methods which can be used with the dynamic object - - public IDocumentProperty GetProperty(string alias) - { - return GetProperty(alias, false); - } - public IDocumentProperty GetProperty(string alias, bool recursive) - { - return alias.StartsWith("@") - ? GetReflectedProperty(alias.TrimStart('@')) - : GetUserProperty(alias, recursive); - } - public string GetPropertyValue(string alias) - { - return GetPropertyValue(alias, false); - } - public string GetPropertyValue(string alias, string fallback) - { - var prop = GetPropertyValue(alias); - return !prop.IsNullOrWhiteSpace() ? prop : fallback; - } - public string GetPropertyValue(string alias, bool recursive) - { - var p = alias.StartsWith("@") - ? GetReflectedProperty(alias.TrimStart('@')) - : GetUserProperty(alias, recursive); - return p == null ? null : p.ValueAsString; - } - public string GetPropertyValue(string alias, bool recursive, string fallback) - { - var prop = GetPropertyValue(alias, recursive); - return !prop.IsNullOrWhiteSpace() ? prop : fallback; - } - - #endregion - - #region HasValue - public bool HasValue(string alias) - { - return HasValue(alias, false); - } - public bool HasValue(string alias, bool recursive) - { - var prop = GetUserProperty(alias, recursive); - if (prop == null) return false; - return prop.HasValue(); - } - public IHtmlString HasValue(string alias, string valueIfTrue, string valueIfFalse) - { - return HasValue(alias, false) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); - } - public IHtmlString HasValue(string alias, bool recursive, string valueIfTrue, string valueIfFalse) - { - return HasValue(alias, recursive) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); - } - public IHtmlString HasValue(string alias, string valueIfTrue) - { - return HasValue(alias, false) ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); - } - public IHtmlString HasValue(string alias, bool recursive, string valueIfTrue) - { - return HasValue(alias, recursive) ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); - } - #endregion - - #region Where - - public HtmlString Where(string predicate, string valueIfTrue) - { - return Where(predicate, valueIfTrue, string.Empty); - } - public HtmlString Where(string predicate, string valueIfTrue, string valueIfFalse) - { - if (Where(predicate)) - { - return new HtmlString(valueIfTrue); - } - return new HtmlString(valueIfFalse); - } - public bool Where(string predicate) - { - //Totally gonna cheat here - var dynamicDocumentList = new DynamicPublishedContentList(); - dynamicDocumentList.Add(this); - var filtered = dynamicDocumentList.Where(predicate); - if (Queryable.Count(filtered) == 1) - { - //this node matches the predicate - return true; - } - return false; - } - - #endregion - - //TODO: need a method to return a string value for a user property regardless of xml content or data type thus bypassing all of the PropertyEditorValueConverters - ///// - ///// Returns the value as as string regardless of xml content or data type - ///// - ///// - //public override string ToString() - //{ - // return base.ToString(); - //} - - #region Explicit IPublishedContent implementation - IPublishedContent IPublishedContent.Parent - { - get { return PublishedContent.Parent; } - } - - int IPublishedContent.Id - { - get { return PublishedContent.Id; } - } - - int IPublishedContent.TemplateId - { - get { return PublishedContent.TemplateId; } - } - - int IPublishedContent.SortOrder - { - get { return PublishedContent.SortOrder; } - } - - string IPublishedContent.Name - { - get { return PublishedContent.Name; } - } - - string IPublishedContent.UrlName - { - get { return PublishedContent.UrlName; } - } - - string IPublishedContent.DocumentTypeAlias - { - get { return PublishedContent.DocumentTypeAlias; } - } - - int IPublishedContent.DocumentTypeId - { - get { return PublishedContent.DocumentTypeId; } - } - - string IPublishedContent.WriterName - { - get { return PublishedContent.WriterName; } - } - - string IPublishedContent.CreatorName - { - get { return PublishedContent.CreatorName; } - } - - int IPublishedContent.WriterId - { - get { return PublishedContent.WriterId; } - } - - int IPublishedContent.CreatorId - { - get { return PublishedContent.CreatorId; } - } - - string IPublishedContent.Path - { - get { return PublishedContent.Path; } - } - - DateTime IPublishedContent.CreateDate - { - get { return PublishedContent.CreateDate; } - } - - DateTime IPublishedContent.UpdateDate - { - get { return PublishedContent.UpdateDate; } - } - - Guid IPublishedContent.Version - { - get { return PublishedContent.Version; } - } - - int IPublishedContent.Level - { - get { return PublishedContent.Level; } - } - - System.Collections.ObjectModel.Collection IPublishedContent.Properties - { - get { return PublishedContent.Properties; } - } - - IEnumerable IPublishedContent.Children - { - get { return PublishedContent.Children; } - } - - IDocumentProperty IPublishedContent.GetProperty(string alias) - { - return PublishedContent.GetProperty(alias); - } - #endregion - } -} diff --git a/src/Umbraco.Core/Models/IPublishedContent.cs b/src/Umbraco.Core/Models/IPublishedContent.cs index c9695ad52a..869a6ae696 100644 --- a/src/Umbraco.Core/Models/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/IPublishedContent.cs @@ -30,7 +30,7 @@ namespace Umbraco.Core.Models DateTime UpdateDate { get; } Guid Version { get; } int Level { get; } - Collection Properties { get; } + Collection Properties { get; } IEnumerable Children { get; } /// @@ -46,6 +46,6 @@ namespace Umbraco.Core.Models /// In some cases Pulish Stores, a property value may exist in multiple places and we need to fallback to different cached locations /// therefore sometimes the 'Properties' collection may not be sufficient. /// - IDocumentProperty GetProperty(string alias); + IPublishedContentProperty GetProperty(string alias); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/IDocumentProperty.cs b/src/Umbraco.Core/Models/IPublishedContentProperty.cs similarity index 70% rename from src/Umbraco.Core/Models/IDocumentProperty.cs rename to src/Umbraco.Core/Models/IPublishedContentProperty.cs index 67494c92e6..6361c29683 100644 --- a/src/Umbraco.Core/Models/IDocumentProperty.cs +++ b/src/Umbraco.Core/Models/IPublishedContentProperty.cs @@ -2,7 +2,7 @@ using System; namespace Umbraco.Core.Models { - public interface IDocumentProperty + public interface IPublishedContentProperty { string Alias { get; } object Value { get; } diff --git a/src/Umbraco.Core/PublishedContentExtensions.cs b/src/Umbraco.Core/PublishedContentExtensions.cs index b49548a9b3..1523e86d12 100644 --- a/src/Umbraco.Core/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/PublishedContentExtensions.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Data; using System.Linq; +using System.Web; using Umbraco.Core.Dynamics; using Umbraco.Core.Models; using umbraco.interfaces; @@ -13,11 +14,110 @@ namespace Umbraco.Core /// public static class PublishedContentExtensions { + - public static dynamic AsDynamic(this IPublishedContent doc) + #region GetProperty + public static IPublishedContentProperty GetProperty(this IPublishedContent content, string alias, bool recursive) { - var dd = new DynamicPublishedContentBase(doc); - return dd.AsDynamic(); + return content.GetPropertyRecursive(alias, recursive); + } + + private static IPublishedContentProperty GetPropertyRecursive(this IPublishedContent content, string alias, bool recursive = false) + { + if (!recursive) + { + return content.GetProperty(alias); + } + var context = content; + var prop = content.GetPropertyRecursive(alias); + while (prop == null || prop.Value == null || prop.Value.ToString().IsNullOrWhiteSpace()) + { + var parent = context.Parent; + if (parent == null) break; + prop = context.GetPropertyRecursive(alias); + } + return prop; + } + #endregion + + #region GetPropertyValue + public static string GetPropertyValue(this IPublishedContent doc, string alias) + { + return doc.GetPropertyValue(alias, false); + } + public static string GetPropertyValue(this IPublishedContent doc, string alias, string fallback) + { + var prop = doc.GetPropertyValue(alias); + return !prop.IsNullOrWhiteSpace() ? prop : fallback; + } + public static string GetPropertyValue(this IPublishedContent doc, string alias, bool recursive) + { + var p = doc.GetProperty(alias, recursive); + return p == null ? null : Convert.ToString(p.Value); + } + public static string GetPropertyValue(this IPublishedContent doc, string alias, bool recursive, string fallback) + { + var prop = doc.GetPropertyValue(alias, recursive); + return !prop.IsNullOrWhiteSpace() ? prop : fallback; + } + #endregion + + #region HasValue + + public static bool HasValue(this IPublishedContentProperty prop) + { + if (prop == null) return false; + if (prop.Value == null) return false; + return !prop.Value.ToString().IsNullOrWhiteSpace(); + } + + public static bool HasValue(this IPublishedContent doc, string alias) + { + return doc.HasValue(alias, false); + } + public static bool HasValue(this IPublishedContent doc, string alias, bool recursive) + { + var prop = doc.GetProperty(alias, recursive); + if (prop == null) return false; + return prop.HasValue(); + } + public static IHtmlString HasValue(this IPublishedContent doc, string alias, string valueIfTrue, string valueIfFalse) + { + return doc.HasValue(alias, false) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); + } + public static IHtmlString HasValue(this IPublishedContent doc, string alias, bool recursive, string valueIfTrue, string valueIfFalse) + { + return doc.HasValue(alias, recursive) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); + } + public static IHtmlString HasValue(this IPublishedContent doc, string alias, string valueIfTrue) + { + return doc.HasValue(alias, false) ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); + } + public static IHtmlString HasValue(this IPublishedContent doc, string alias, bool recursive, string valueIfTrue) + { + return doc.HasValue(alias, recursive) ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); + } + #endregion + + public static bool IsVisible(this IPublishedContent doc) + { + var umbracoNaviHide = doc.GetProperty("umbracoNaviHide"); + if (umbracoNaviHide != null) + { + return umbracoNaviHide.Value.ToString().Trim() != "1"; + } + return true; + } + + public static bool HasProperty(this IPublishedContent doc, string name) + { + if (doc != null) + { + var prop = doc.GetProperty(name); + + return (prop != null); + } + return false; } /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 81e334d42d..10f23b9c08 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -66,22 +66,13 @@ - - - - - - - - - @@ -99,7 +90,7 @@ - + diff --git a/src/Umbraco.Tests/DynamicDocument/DocumentTests.cs b/src/Umbraco.Tests/DynamicDocument/DocumentTests.cs index 54c8f473ed..177ae515c5 100644 --- a/src/Umbraco.Tests/DynamicDocument/DocumentTests.cs +++ b/src/Umbraco.Tests/DynamicDocument/DocumentTests.cs @@ -137,8 +137,8 @@ namespace Umbraco.Tests.DynamicDocument WriterName = "Shannon", Parent = null, Level = 1, - Properties = new Collection( - new List() + Properties = new Collection( + new List() { new PropertyResult("property1", "value" + indexVals, Guid.NewGuid(), PropertyResultType.UserProperty), new PropertyResult("property2", "value" + (indexVals + 1), Guid.NewGuid(), PropertyResultType.UserProperty) @@ -186,9 +186,9 @@ namespace Umbraco.Tests.DynamicDocument public DateTime UpdateDate { get; set; } public Guid Version { get; set; } public int Level { get; set; } - public Collection Properties { get; set; } + public Collection Properties { get; set; } public IEnumerable Children { get; set; } - public IDocumentProperty GetProperty(string alias) + public IPublishedContentProperty GetProperty(string alias) { return Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); } diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs index a7172e8002..425807cf6f 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs @@ -6,6 +6,7 @@ using System.Web; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Dynamics; +using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.DynamicDocument @@ -16,9 +17,6 @@ namespace Umbraco.Tests.DynamicDocument public override void Initialize() { base.Initialize(); - - - } public override void TearDown() @@ -32,6 +30,41 @@ namespace Umbraco.Tests.DynamicDocument get { return false; } } + protected override string GetXmlContent(int templateId) + { + return @" + + + + +]> + + + + + 1 + + This is some content]]> + + + + + + + + + + + + + + + + + +"; + } /// /// Returns the dynamic node/document to run tests against @@ -40,6 +73,31 @@ namespace Umbraco.Tests.DynamicDocument /// protected abstract dynamic GetDynamicNode(int id); + [Test] + public void Children_GroupBy_DocumentTypeAlias() + { + var doc = GetDynamicNode(1046); + + var found1 = doc.Children.GroupBy("DocumentTypeAlias"); + + var casted = (IEnumerable>)(found1); + Assert.AreEqual(2, casted.Count()); + Assert.AreEqual(2, casted.Single(x => x.Key.ToString() == "Home").Count()); + Assert.AreEqual(1, casted.Single(x => x.Key.ToString() == "CustomDocument").Count()); + } + + [Test] + public void Children_Where_DocumentTypeAlias() + { + var doc = GetDynamicNode(1046); + + var found1 = doc.Children.Where("DocumentTypeAlias == \"CustomDocument\""); + var found2 = doc.Children.Where("DocumentTypeAlias == \"Home\""); + + Assert.AreEqual(1, found1.Count()); + Assert.AreEqual(2, found2.Count()); + } + [Test] public void Children_Order_By_Update_Date() { @@ -262,8 +320,8 @@ namespace Umbraco.Tests.DynamicDocument Assert.IsNotNull(result); var list = (IEnumerable)result; - Assert.AreEqual(7, list.Count()); - Assert.IsTrue(list.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1046, 1173, 1174, 1176, 1175 })); + Assert.AreEqual(8, list.Count()); + Assert.IsTrue(list.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1046, 1173, 1174, 1176, 1175, 4444 })); } [Test] @@ -276,8 +334,8 @@ namespace Umbraco.Tests.DynamicDocument Assert.IsNotNull(result); var list = (IEnumerable)result; - Assert.AreEqual(6, list.Count()); - Assert.IsTrue(list.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1174, 1176, 1175 })); + Assert.AreEqual(7, list.Count()); + Assert.IsTrue(list.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1174, 1176, 1175, 4444 })); } [Test] diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs index ba637192b8..92a47a6260 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs @@ -1,23 +1,24 @@ using System.Collections.Generic; using Umbraco.Core.Dynamics; using Umbraco.Core.Models; +using Umbraco.Web.Models; namespace Umbraco.Tests.DynamicDocument { public static class DynamicPublishedContentCustomExtensionMethods { - public static string DynamicDocumentNoParameters(this DynamicPublishedContentBase doc) + public static string DynamicDocumentNoParameters(this DynamicPublishedContent doc) { return "Hello world"; } - public static string DynamicDocumentCustomString(this DynamicPublishedContentBase doc, string custom) + public static string DynamicDocumentCustomString(this DynamicPublishedContent doc, string custom) { return custom; } - public static string DynamicDocumentMultiParam(this DynamicPublishedContentBase doc, string custom, int i, bool b) + public static string DynamicDocumentMultiParam(this DynamicPublishedContent doc, string custom, int i, bool b) { return custom + i + b; } @@ -27,7 +28,7 @@ namespace Umbraco.Tests.DynamicDocument return custom + i + b; } - public static string DynamicDocumentEnumerableMultiParam(this IEnumerable doc, string custom, int i, bool b) + public static string DynamicDocumentEnumerableMultiParam(this IEnumerable doc, string custom, int i, bool b) { return custom + i + b; } diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs index fbf802c2ea..29aae92e53 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs @@ -1,5 +1,8 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Linq; +using System.Web; using NUnit.Framework; using Umbraco.Core.Dynamics; using Umbraco.Core.Models; @@ -16,7 +19,7 @@ using umbraco.cms.businesslogic.web; namespace Umbraco.Tests.DynamicDocument { [TestFixture] - public class DynamicPublishedContentTests : DynamicDocumentTestsBase + public class DynamicPublishedContentTests : DynamicDocumentTestsBase { public override void Initialize() { @@ -112,9 +115,9 @@ namespace Umbraco.Tests.DynamicDocument /// public class TestHelper { - private readonly DynamicPublishedContentBase _doc; + private readonly DynamicPublishedContent _doc; - public TestHelper(DynamicPublishedContentBase doc) + public TestHelper(DynamicPublishedContent doc) { _doc = doc; } diff --git a/src/Umbraco.Tests/DynamicDocument/PublishedContentTests.cs b/src/Umbraco.Tests/DynamicDocument/PublishedContentTests.cs new file mode 100644 index 0000000000..66a898e2f4 --- /dev/null +++ b/src/Umbraco.Tests/DynamicDocument/PublishedContentTests.cs @@ -0,0 +1,324 @@ +using System; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web; +using Umbraco.Web.Models; + +namespace Umbraco.Tests.DynamicDocument +{ + /// + /// Tests the typed extension methods on IPublishedContent the same way we test the dynamic ones + /// + [TestFixture] + public class PublishedContentTests : BaseWebTest + { + protected override bool RequiresDbSetup + { + get { return false; } + } + + protected override string GetXmlContent(int templateId) + { + return @" + + + + +]> + + + + + 1 + + This is some content]]> + + + + + + + + + + + + + + + + + +"; + } + + public override void Initialize() + { + base.Initialize(); + + PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver( + new[] + { + typeof(DatePickerPropertyEditorValueConverter), + typeof(TinyMcePropertyEditorValueConverter), + typeof(YesNoPropertyEditorValueConverter) + }); + + //need to specify a custom callback for unit tests + DynamicPublishedContent.GetDataTypeCallback = (docTypeAlias, propertyAlias) => + { + if (propertyAlias == "content") + { + //return the rte type id + return Guid.Parse("5e9b75ae-face-41c8-b47e-5f4b0fd82f83"); + } + return Guid.Empty; + }; + } + + public override void TearDown() + { + base.TearDown(); + + PropertyEditorValueConvertersResolver.Reset(); + } + + internal IPublishedContent GetNode(int id) + { + var ctx = GetUmbracoContext("/test", 1234); + var contentStore = new DefaultPublishedContentStore(); + var doc = contentStore.GetDocumentById(ctx, id); + Assert.IsNotNull(doc); + return doc; + } + + [Test] + public void Children_GroupBy_DocumentTypeAlias() + { + var doc = GetNode(1046); + + var found1 = doc.Children.GroupBy("DocumentTypeAlias"); + + Assert.AreEqual(2, found1.Count()); + Assert.AreEqual(2, found1.Single(x => x.Key.ToString() == "Home").Count()); + Assert.AreEqual(1, found1.Single(x => x.Key.ToString() == "CustomDocument").Count()); + } + + [Test] + public void Children_Where_DocumentTypeAlias() + { + var doc = GetNode(1046); + + var found1 = doc.Children.Where("DocumentTypeAlias == \"CustomDocument\""); + var found2 = doc.Children.Where("DocumentTypeAlias == \"Home\""); + + Assert.AreEqual(1, found1.Count()); + Assert.AreEqual(2, found2.Count()); + } + + [Test] + public void Children_Order_By_Update_Date() + { + var doc = GetNode(1173); + + var ordered = doc.Children.OrderBy("UpdateDate"); + + var correctOrder = new[] { 1178, 1177, 1174, 1176 }; + for (var i = 0; i < correctOrder.Length; i++) + { + Assert.AreEqual(correctOrder[i], ordered.ElementAt(i).Id); + } + + } + + [Test] + public void HasProperty() + { + var doc = GetNode(1173); + + var hasProp = doc.HasProperty("umbracoUrlAlias"); + + Assert.AreEqual(true, (bool)hasProp); + + } + + + [Test] + public void HasValue() + { + var doc = GetNode(1173); + + var hasValue = doc.HasValue("umbracoUrlAlias"); + var noValue = doc.HasValue("blahblahblah"); + + Assert.IsTrue(hasValue); + Assert.IsFalse(noValue); + } + + + [Test] + public void Ancestors_Where_Visible() + { + var doc = GetNode(1174); + + var whereVisible = doc.Ancestors().Where("Visible"); + + Assert.AreEqual(1, whereVisible.Count()); + + } + + [Test] + public void Visible() + { + var hidden = GetNode(1046); + var visible = GetNode(1173); + + Assert.IsFalse(hidden.IsVisible()); + Assert.IsTrue(visible.IsVisible()); + } + + [Test] + public void Ensure_TinyMCE_Converted_Type_User_Property() + { + var doc = GetNode(1173); + + throw new NotImplementedException("We currently don't have an extension method to return the formatted value using IPropertyValueConverter! This currently only works in the dynamic implementation"); + + //Assert.IsTrue(TypeHelper.IsTypeAssignableFrom(doc.GetPropertyValue().Content.GetType())); + //Assert.AreEqual("
This is some content
", doc.Content.ToString()); + } + + [Test] + public void Ancestor_Or_Self() + { + var doc = GetNode(1173); + + var result = doc.AncestorOrSelf(); + + Assert.IsNotNull(result); + + Assert.AreEqual((int)1046, (int)result.Id); + } + + [Test] + public void Ancestors_Or_Self() + { + var doc = GetNode(1174); + + var result = doc.AncestorsOrSelf(); + + Assert.IsNotNull(result); + + Assert.AreEqual(3, result.Count()); + Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1174, 1173, 1046 })); + } + + [Test] + public void Ancestors() + { + var doc = GetNode(1174); + + var result = doc.Ancestors(); + + Assert.IsNotNull(result); + + Assert.AreEqual(2, result.Count()); + Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1046 })); + } + + [Test] + public void Descendants_Or_Self() + { + var doc = GetNode(1046); + + var result = doc.DescendantsOrSelf(); + + Assert.IsNotNull(result); + + Assert.AreEqual(7, result.Count()); + Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1046, 1173, 1174, 1176, 1175 })); + } + + [Test] + public void Descendants() + { + var doc = GetNode(1046); + + var result = doc.Descendants(); + + Assert.IsNotNull(result); + + Assert.AreEqual(6, result.Count()); + Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1174, 1176, 1175 })); + } + + [Test] + public void Up() + { + var doc = GetNode(1173); + + var result = doc.Up(); + + Assert.IsNotNull(result); + + Assert.AreEqual((int)1046, (int)result.Id); + } + + [Test] + public void Down() + { + var doc = GetNode(1173); + + var result = doc.Down(); + + Assert.IsNotNull(result); + + Assert.AreEqual((int)1174, (int)result.Id); + } + + [Test] + public void Next() + { + var doc = GetNode(1173); + + var result = doc.Next(); + + Assert.IsNotNull(result); + + Assert.AreEqual((int)1175, (int)result.Id); + } + + [Test] + public void Next_Without_Sibling() + { + var doc = GetNode(1178); + + Assert.IsNull(doc.Next()); + } + + [Test] + public void Previous_Without_Sibling() + { + var doc = GetNode(1173); + + Assert.IsNull(doc.Previous()); + } + + [Test] + public void Previous() + { + var doc = GetNode(1176); + + var result = doc.Previous(); + + Assert.IsNotNull(result); + + Assert.AreEqual((int)1174, (int)result.Id); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 4d1b329bb3..ef4036ce16 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -55,6 +55,7 @@ + diff --git a/src/Umbraco.Web/DefaultPublishedMediaStore.cs b/src/Umbraco.Web/DefaultPublishedMediaStore.cs index ab43387283..7a3e9a66f8 100644 --- a/src/Umbraco.Web/DefaultPublishedMediaStore.cs +++ b/src/Umbraco.Web/DefaultPublishedMediaStore.cs @@ -182,7 +182,7 @@ namespace Umbraco.Web /// /// /// - private IDocumentProperty GetProperty(DictionaryPublishedContent dd, string alias) + private IPublishedContentProperty GetProperty(DictionaryPublishedContent dd, string alias) { if (dd.LoadedFromExamine) { @@ -300,7 +300,7 @@ namespace Umbraco.Web IDictionary valueDictionary, Func getParent, Func> getChildren, - Func getProperty) + Func getProperty) { if (valueDictionary == null) throw new ArgumentNullException("valueDictionary"); if (getParent == null) throw new ArgumentNullException("getParent"); @@ -337,7 +337,7 @@ namespace Umbraco.Web } }, "parentID"); - Properties = new Collection(); + Properties = new Collection(); //loop through remaining values that haven't been applied foreach (var i in valueDictionary.Where(x => !_keysAdded.Contains(x.Key))) @@ -356,7 +356,7 @@ namespace Umbraco.Web private readonly Func _getParent; private readonly Func> _getChildren; - private readonly Func _getProperty; + private readonly Func _getProperty; public IPublishedContent Parent { @@ -380,13 +380,13 @@ namespace Umbraco.Web public DateTime UpdateDate { get; private set; } public Guid Version { get; private set; } public int Level { get; private set; } - public Collection Properties { get; private set; } + public Collection Properties { get; private set; } public IEnumerable Children { get { return _getChildren(this); } } - public IDocumentProperty GetProperty(string alias) + public IPublishedContentProperty GetProperty(string alias) { return _getProperty(this, alias); } diff --git a/src/Umbraco.Web/DynamicPublishedContentSearchExtensions.cs b/src/Umbraco.Web/DynamicPublishedContentSearchExtensions.cs index 173c5aab76..5b48aa2573 100644 --- a/src/Umbraco.Web/DynamicPublishedContentSearchExtensions.cs +++ b/src/Umbraco.Web/DynamicPublishedContentSearchExtensions.cs @@ -1,6 +1,7 @@ using Examine.LuceneEngine.SearchCriteria; using Umbraco.Core.Dynamics; using Umbraco.Core.Models; +using Umbraco.Web.Models; namespace Umbraco.Web { @@ -9,7 +10,7 @@ namespace Umbraco.Web ///
public static class DynamicPublishedContentSearchExtensions { - public static DynamicPublishedContentList Search(this DynamicPublishedContentBase d, string term, bool useWildCards = true, string searchProvider = null) + public static DynamicPublishedContentList Search(this DynamicPublishedContent d, string term, bool useWildCards = true, string searchProvider = null) { var searcher = Examine.ExamineManager.Instance.DefaultSearchProvider; if (!string.IsNullOrEmpty(searchProvider)) @@ -25,12 +26,12 @@ namespace Umbraco.Web return d.Search(crit, searcher); } - public static DynamicPublishedContentList SearchDescendants(this DynamicPublishedContentBase d, string term, bool useWildCards = true, string searchProvider = null) + public static DynamicPublishedContentList SearchDescendants(this DynamicPublishedContent d, string term, bool useWildCards = true, string searchProvider = null) { return d.Search(term, useWildCards, searchProvider); } - public static DynamicPublishedContentList SearchChildren(this DynamicPublishedContentBase d, string term, bool useWildCards = true, string searchProvider = null) + public static DynamicPublishedContentList SearchChildren(this DynamicPublishedContent d, string term, bool useWildCards = true, string searchProvider = null) { var searcher = Examine.ExamineManager.Instance.DefaultSearchProvider; if (!string.IsNullOrEmpty(searchProvider)) @@ -46,7 +47,7 @@ namespace Umbraco.Web return d.Search(crit, searcher); } - public static DynamicPublishedContentList Search(this DynamicPublishedContentBase d, Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) + public static DynamicPublishedContentList Search(this DynamicPublishedContent d, Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) { var s = Examine.ExamineManager.Instance.DefaultSearchProvider; if (searchProvider != null) diff --git a/src/Umbraco.Core/Dynamics/DynamicExpression.cs b/src/Umbraco.Web/Dynamics/DynamicExpression.cs similarity index 94% rename from src/Umbraco.Core/Dynamics/DynamicExpression.cs rename to src/Umbraco.Web/Dynamics/DynamicExpression.cs index defb58caed..848947b458 100644 --- a/src/Umbraco.Core/Dynamics/DynamicExpression.cs +++ b/src/Umbraco.Web/Dynamics/DynamicExpression.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; using System.Linq.Expressions; +using Umbraco.Core.Dynamics; -namespace Umbraco.Core.Dynamics +namespace Umbraco.Web.Dynamics { internal static class DynamicExpression { diff --git a/src/Umbraco.Core/Dynamics/DynamicGrouping.cs b/src/Umbraco.Web/Dynamics/DynamicGrouping.cs similarity index 65% rename from src/Umbraco.Core/Dynamics/DynamicGrouping.cs rename to src/Umbraco.Web/Dynamics/DynamicGrouping.cs index 9acd8c54b7..07ec14ecd3 100644 --- a/src/Umbraco.Core/Dynamics/DynamicGrouping.cs +++ b/src/Umbraco.Web/Dynamics/DynamicGrouping.cs @@ -2,12 +2,13 @@ using System.Linq; using System.Collections; using Umbraco.Core.Models; +using Umbraco.Web.Models; -namespace Umbraco.Core.Dynamics +namespace Umbraco.Web.Dynamics { - public class DynamicGrouping : IEnumerable + public class DynamicGrouping : IEnumerable>, IEnumerable> { - internal IEnumerable> Inner; + internal IEnumerable> Inner; public DynamicGrouping OrderBy(string expression) { @@ -22,7 +23,7 @@ namespace Umbraco.Core.Dynamics .Select(node => { string predicate = groupBy; - var internalList = new DynamicPublishedContentList(new DynamicPublishedContentBase[] { node }); + var internalList = new DynamicPublishedContentList(new DynamicPublishedContent[] { node }); var query = (IQueryable)internalList.Select(predicate, new object[] { }); var key = query.FirstOrDefault(); return new @@ -33,18 +34,28 @@ namespace Umbraco.Core.Dynamics }) .Where(item => item.Key != null) .GroupBy(item => item.Key) - .Select(item => new Grouping() + .Select(item => new Grouping() { Key = item.Key, Elements = item.Select(inner => inner.Node) }); } - internal DynamicGrouping(IEnumerable> source) + internal DynamicGrouping(IEnumerable> source) { this.Inner = source; } - public IEnumerator GetEnumerator() + IEnumerator> IEnumerable>.GetEnumerator() + { + return Inner.GetEnumerator(); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return Inner.GetEnumerator(); + } + + public IEnumerator GetEnumerator() { return Inner.GetEnumerator(); } diff --git a/src/Umbraco.Core/Dynamics/DynamicPublishedContentIdEqualityComparer.cs b/src/Umbraco.Web/Dynamics/DynamicPublishedContentIdEqualityComparer.cs similarity index 71% rename from src/Umbraco.Core/Dynamics/DynamicPublishedContentIdEqualityComparer.cs rename to src/Umbraco.Web/Dynamics/DynamicPublishedContentIdEqualityComparer.cs index 663684b9e4..27bb327bf4 100644 --- a/src/Umbraco.Core/Dynamics/DynamicPublishedContentIdEqualityComparer.cs +++ b/src/Umbraco.Web/Dynamics/DynamicPublishedContentIdEqualityComparer.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; -using Umbraco.Core.Models; +using Umbraco.Web.Models; -namespace Umbraco.Core.Dynamics +namespace Umbraco.Web.Dynamics { - internal class DynamicPublishedContentIdEqualityComparer : EqualityComparer + internal class DynamicPublishedContentIdEqualityComparer : EqualityComparer { - public override bool Equals(DynamicPublishedContentBase x, DynamicPublishedContentBase y) + public override bool Equals(DynamicPublishedContent x, DynamicPublishedContent y) { //Check whether the compared objects reference the same data. if (Object.ReferenceEquals(x, y)) return true; @@ -21,7 +21,7 @@ namespace Umbraco.Core.Dynamics } - public override int GetHashCode(DynamicPublishedContentBase obj) + public override int GetHashCode(DynamicPublishedContent obj) { if (Object.ReferenceEquals(obj, null)) return 0; diff --git a/src/Umbraco.Core/Dynamics/DynamicPublishedContentListOrdering.cs b/src/Umbraco.Web/Dynamics/DynamicPublishedContentListOrdering.cs similarity index 69% rename from src/Umbraco.Core/Dynamics/DynamicPublishedContentListOrdering.cs rename to src/Umbraco.Web/Dynamics/DynamicPublishedContentListOrdering.cs index 872c393f5b..abf8c210b6 100644 --- a/src/Umbraco.Core/Dynamics/DynamicPublishedContentListOrdering.cs +++ b/src/Umbraco.Web/Dynamics/DynamicPublishedContentListOrdering.cs @@ -2,19 +2,19 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using Umbraco.Core.Models; +using Umbraco.Web.Models; -namespace Umbraco.Core.Dynamics +namespace Umbraco.Web.Dynamics { internal static class DynamicPublishedContentListOrdering { - private static TOut Reduce(Func func, DynamicPublishedContentBase publishedContent) + private static TOut Reduce(Func func, DynamicPublishedContent publishedContent) { var value = func(publishedContent); - while (value is Func) + while (value is Func) { - value = (value as Func)(publishedContent); + value = (value as Func)(publishedContent); } //when you're sorting a list of properties //and one of those properties doesn't exist, it will come back as DynamicNull @@ -38,209 +38,209 @@ namespace Umbraco.Core.Dynamics } return (TOut)value; } - internal static IOrderedQueryable OrderBy(object source, object key) + internal static IOrderedQueryable OrderBy(object source, object key) { - IEnumerable typedSource = source as IEnumerable; + IEnumerable typedSource = source as IEnumerable; LambdaExpression lambda = key as LambdaExpression; //if the lambda we have returns an actual property, not a dynamic one, //then the TOut of the func will be the actual type, not object //Func func = (Func)lambda.Compile(); var func = lambda.Compile(); var TOut = func.GetType().GetGenericArguments()[1]; - IOrderedQueryable result = null; - if (TOut == typeof(Func)) + IOrderedQueryable result = null; + if (TOut == typeof(Func)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(object)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(bool)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(decimal)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(int)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(string)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(DateTime)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } return result; } - internal static IOrderedQueryable ThenBy(object source, object key) + internal static IOrderedQueryable ThenBy(object source, object key) { - IOrderedQueryable typedSource = source as IOrderedQueryable; + IOrderedQueryable typedSource = source as IOrderedQueryable; LambdaExpression lambda = key as LambdaExpression; var func = lambda.Compile(); var TOut = func.GetType().GetGenericArguments()[1]; - IOrderedQueryable result = null; - if (TOut == typeof(Func)) + IOrderedQueryable result = null; + if (TOut == typeof(Func)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(object)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(bool)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(decimal)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(int)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(string)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(DateTime)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } return result; } - internal static IOrderedQueryable OrderByDescending(object source, object key) + internal static IOrderedQueryable OrderByDescending(object source, object key) { - IEnumerable typedSource = source as IEnumerable; + IEnumerable typedSource = source as IEnumerable; LambdaExpression lambda = key as LambdaExpression; var func = lambda.Compile(); var TOut = func.GetType().GetGenericArguments()[1]; - IOrderedQueryable result = null; - if (TOut == typeof(Func)) + IOrderedQueryable result = null; + if (TOut == typeof(Func)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(object)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(bool)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(decimal)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(int)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(string)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(DateTime)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } return result; } - internal static IOrderedQueryable ThenByDescending(object source, object key) + internal static IOrderedQueryable ThenByDescending(object source, object key) { - IOrderedQueryable typedSource = source as IOrderedQueryable; + IOrderedQueryable typedSource = source as IOrderedQueryable; LambdaExpression lambda = key as LambdaExpression; var func = lambda.Compile(); var TOut = func.GetType().GetGenericArguments()[1]; - IOrderedQueryable result = null; - if (TOut == typeof(Func)) + IOrderedQueryable result = null; + if (TOut == typeof(Func)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(object)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(bool)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(decimal)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(int)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(string)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(DateTime)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } return result; diff --git a/src/Umbraco.Core/Dynamics/DynamicQueryable.cs b/src/Umbraco.Web/Dynamics/DynamicQueryable.cs similarity index 86% rename from src/Umbraco.Core/Dynamics/DynamicQueryable.cs rename to src/Umbraco.Web/Dynamics/DynamicQueryable.cs index 1552ce98f5..b7958944e0 100644 --- a/src/Umbraco.Core/Dynamics/DynamicQueryable.cs +++ b/src/Umbraco.Web/Dynamics/DynamicQueryable.cs @@ -6,9 +6,10 @@ using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Diagnostics; -using Umbraco.Core.Models; +using Umbraco.Core.Dynamics; +using Umbraco.Web.Models; -namespace Umbraco.Core.Dynamics +namespace Umbraco.Web.Dynamics { internal static class DynamicQueryable { @@ -22,22 +23,22 @@ namespace Umbraco.Core.Dynamics if (source == null) throw new ArgumentNullException("source"); if (predicate == null) throw new ArgumentNullException("predicate"); LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, true, values); - if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContentBase)) + if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContent)) { //source list is DynamicNode and the lambda returns a Func - IQueryable typedSource = source as IQueryable; + IQueryable typedSource = source as IQueryable; var compiledFunc = lambda.Compile(); - Func func = null; - Func boolFunc = null; - if (compiledFunc is Func) + Func func = null; + Func boolFunc = null; + if (compiledFunc is Func) { - func = (Func)compiledFunc; + func = (Func)compiledFunc; } - if (compiledFunc is Func) + if (compiledFunc is Func) { - boolFunc = (Func)compiledFunc; + boolFunc = (Func)compiledFunc; } - return typedSource.Where(delegate(DynamicPublishedContentBase node) + return typedSource.Where(delegate(DynamicPublishedContent node) { object value = -1; //value = func(node); @@ -47,13 +48,13 @@ namespace Umbraco.Core.Dynamics if (func != null) { var firstFuncResult = func(node); - if (firstFuncResult is Func) + if (firstFuncResult is Func) { - value = (firstFuncResult as Func)(node); + value = (firstFuncResult as Func)(node); } - if (firstFuncResult is Func) + if (firstFuncResult is Func) { - value = (firstFuncResult as Func)(node); + value = (firstFuncResult as Func)(node); } if (firstFuncResult is bool) { @@ -87,28 +88,28 @@ namespace Umbraco.Core.Dynamics } } - public static IQueryable Select(this IQueryable source, string selector, params object[] values) + public static IQueryable Select(this IQueryable source, string selector, params object[] values) { if (source == null) throw new ArgumentNullException("source"); if (selector == null) throw new ArgumentNullException("selector"); LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(object), selector, false, values); - if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContentBase)) + if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContent)) { //source list is DynamicNode and the lambda returns a Func - IQueryable typedSource = source as IQueryable; + IQueryable typedSource = source as IQueryable; var compiledFunc = lambda.Compile(); - Func func = null; - if (compiledFunc is Func) + Func func = null; + if (compiledFunc is Func) { - func = (Func)compiledFunc; + func = (Func)compiledFunc; } - return typedSource.Select(delegate(DynamicPublishedContentBase node) + return typedSource.Select(delegate(DynamicPublishedContent node) { object value = null; value = func(node); - if (value is Func) + if (value is Func) { - var innerValue = (value as Func)(node); + var innerValue = (value as Func)(node); return innerValue; } return value; @@ -134,7 +135,7 @@ namespace Umbraco.Core.Dynamics if (source == null) throw new ArgumentNullException("source"); if (ordering == null) throw new ArgumentNullException("ordering"); - IQueryable typedSource = source as IQueryable; + IQueryable typedSource = source as IQueryable; if (!ordering.Contains(",")) { bool descending = false; @@ -150,10 +151,10 @@ namespace Umbraco.Core.Dynamics } LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(object), ordering, false, values); - if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContentBase)) + if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContent)) { //source list is DynamicNode and the lambda returns a Func - Func func = (Func)lambda.Compile(); + Func func = (Func)lambda.Compile(); //get the values out var query = typedSource.ToList().ConvertAll(item => new { node = item, key = EvaluateDynamicNodeFunc(item, func) }); if (query.Count == 0) @@ -247,13 +248,13 @@ namespace Umbraco.Core.Dynamics return null; } } - private static object EvaluateDynamicNodeFunc(DynamicPublishedContentBase publishedContent, Func func) + private static object EvaluateDynamicNodeFunc(DynamicPublishedContent publishedContent, Func func) { object value = -1; var firstFuncResult = func(publishedContent); - if (firstFuncResult is Func) + if (firstFuncResult is Func) { - value = (firstFuncResult as Func)(publishedContent); + value = (firstFuncResult as Func)(publishedContent); } if (firstFuncResult.GetType().IsValueType || firstFuncResult is string) { diff --git a/src/Umbraco.Core/Dynamics/ExpressionParser.cs b/src/Umbraco.Web/Dynamics/ExpressionParser.cs similarity index 92% rename from src/Umbraco.Core/Dynamics/ExpressionParser.cs rename to src/Umbraco.Web/Dynamics/ExpressionParser.cs index 78b2e0fafd..2808a8f0db 100644 --- a/src/Umbraco.Core/Dynamics/ExpressionParser.cs +++ b/src/Umbraco.Web/Dynamics/ExpressionParser.cs @@ -4,9 +4,11 @@ using System.Dynamic; using System.Linq; using System.Linq.Expressions; using System.Reflection; -using Umbraco.Core.Models; +using Umbraco.Core; +using Umbraco.Core.Dynamics; +using Umbraco.Web.Models; -namespace Umbraco.Core.Dynamics +namespace Umbraco.Web.Dynamics { internal class ExpressionParser { @@ -508,7 +510,7 @@ namespace Umbraco.Core.Dynamics (expr as LambdaExpression).Parameters.CopyTo(parameters, 0); var invokedExpr = Expression.Invoke(expr, parameters); var not = Expression.Not(Expression.TypeAs(invokedExpr, typeof(Nullable))); - expr = Expression.Lambda>( + expr = Expression.Lambda>( Expression.Condition( Expression.Property(not, "HasValue"), Expression.Property(not, "Value"), @@ -855,11 +857,11 @@ namespace Umbraco.Core.Dynamics Expression[] args = ParseArgumentList(); MethodBase mb; LambdaExpression instanceAsString = null; - ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicPublishedContentBase), "instance"); + ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicPublishedContent), "instance"); if (type.IsGenericType && type != typeof(string)) { var typeArgs = type.GetGenericArguments(); - if (typeArgs[0] == typeof(DynamicPublishedContentBase)) + if (typeArgs[0] == typeof(DynamicPublishedContent)) { if (instance != null && instance is LambdaExpression) { @@ -930,14 +932,14 @@ namespace Umbraco.Core.Dynamics //this will invoke TryGetMember (but wrapped in an expression tree) //so that when it's evaluated, DynamicNode should be supported - ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicPublishedContentBase), "instance"); + ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicPublishedContent), "instance"); ParameterExpression convertDynamicNullToBooleanFalse = Expression.Parameter(typeof(bool), "convertDynamicNullToBooleanFalse"); ParameterExpression result = Expression.Parameter(typeof(object), "result"); ParameterExpression binder = Expression.Variable(typeof(DynamicQueryableGetMemberBinder), "binder"); ParameterExpression ignoreCase = Expression.Variable(typeof(bool), "ignoreCase"); ConstructorInfo getMemberBinderConstructor = typeof(DynamicQueryableGetMemberBinder).GetConstructor(new Type[] { typeof(string), typeof(bool) }); LabelTarget blockReturnLabel = Expression.Label(typeof(object)); - MethodInfo method = typeof(DynamicPublishedContentBase).GetMethod("TryGetMember"); + MethodInfo method = typeof(DynamicPublishedContent).GetMethod("TryGetMember"); BlockExpression block = Expression.Block( typeof(object), @@ -958,10 +960,10 @@ namespace Umbraco.Core.Dynamics Expression.Return(blockReturnLabel, result), Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object))) ); - LambdaExpression lax = Expression.Lambda>(block, instanceExpression); + LambdaExpression lax = Expression.Lambda>(block, instanceExpression); return lax; } - if (typeof(Func).IsAssignableFrom(type)) + if (typeof(Func).IsAssignableFrom(type)) { //accessing a property off an already resolved DynamicNode TryGetMember call //e.g. uBlogsyPostDate.Date @@ -972,8 +974,8 @@ namespace Umbraco.Core.Dynamics ParameterExpression result = Expression.Parameter(typeof(object), "result"); ParameterExpression idParam = Expression.Parameter(typeof(string), "id"); ParameterExpression lambdaResult = Expression.Parameter(typeof(object), "lambdaResult"); - ParameterExpression lambdaInstanceExpression = Expression.Parameter(typeof(DynamicPublishedContentBase), "lambdaInstanceExpression"); - ParameterExpression instanceExpression = Expression.Parameter(typeof(Func), "instance"); + ParameterExpression lambdaInstanceExpression = Expression.Parameter(typeof(DynamicPublishedContent), "lambdaInstanceExpression"); + ParameterExpression instanceExpression = Expression.Parameter(typeof(Func), "instance"); LabelTarget blockReturnLabel = Expression.Label(typeof(object)); BlockExpression block = Expression.Block( @@ -992,7 +994,7 @@ namespace Umbraco.Core.Dynamics Expression.Return(blockReturnLabel, result), Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object))) ); - LambdaExpression lax = Expression.Lambda>(block, lambdaInstanceExpression); + LambdaExpression lax = Expression.Lambda>(block, lambdaInstanceExpression); return lax; } } @@ -1051,11 +1053,11 @@ namespace Umbraco.Core.Dynamics switch (methodReturnType.Name) { case "String": - return Expression.Lambda>(block, instanceExpression); + return Expression.Lambda>(block, instanceExpression); case "Int32": - return Expression.Lambda>(block, instanceExpression); + return Expression.Lambda>(block, instanceExpression); case "Boolean": - return Expression.Lambda>(block, instanceExpression); + return Expression.Lambda>(block, instanceExpression); } return Expression.Call(instance, (MethodInfo)method, args); } @@ -1093,8 +1095,8 @@ namespace Umbraco.Core.Dynamics Expression.Return(cblockReturnLabel, cresult), Expression.Label(cblockReturnLabel, Expression.Constant(null, typeof(string)))); - LambdaExpression lax2 = Expression.Lambda>(cblock, instanceExpression); - var expression = Expression.Lambda>(cblock, instanceExpression); + LambdaExpression lax2 = Expression.Lambda>(cblock, instanceExpression); + var expression = Expression.Lambda>(cblock, instanceExpression); return expression; } @@ -1411,7 +1413,7 @@ namespace Umbraco.Core.Dynamics //if the type of the expression is a func - invokable returning object, //we are going to return it here, because we can get the real value when we actually have the instance //if (typeof(Func).IsAssignableFrom(expr.Type)) return expr; - if (expr is LambdaExpression && ((LambdaExpression)expr).Parameters.Count > 0 && ((LambdaExpression)expr).Parameters[0].Type == typeof(DynamicPublishedContentBase)) + if (expr is LambdaExpression && ((LambdaExpression)expr).Parameters.Count > 0 && ((LambdaExpression)expr).Parameters[0].Type == typeof(DynamicPublishedContent)) { return expr; } @@ -1690,12 +1692,12 @@ namespace Umbraco.Core.Dynamics UnaryExpression unboxedLeft = null, unboxedRight = null; ParameterExpression[] parameters = null; - if (left is LambdaExpression && (left as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicPublishedContentBase)) + if (left is LambdaExpression && (left as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicPublishedContent)) { leftIsLambda = true; } - if (right is LambdaExpression && (right as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicPublishedContentBase)) + if (right is LambdaExpression && (right as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicPublishedContent)) { rightIsLambda = true; } @@ -1749,11 +1751,11 @@ namespace Umbraco.Core.Dynamics if (expressionType == ExpressionType.AndAlso) { - return ExpressionExtensions.And(left as Expression>, right as Expression>); + return ExpressionExtensions.And(left as Expression>, right as Expression>); } if (expressionType == ExpressionType.OrElse) { - return ExpressionExtensions.Or(left as Expression>, right as Expression>); + return ExpressionExtensions.Or(left as Expression>, right as Expression>); } } @@ -1784,11 +1786,11 @@ namespace Umbraco.Core.Dynamics //left is invoked and unboxed to right's TOut, right was not boxed if (expressionType == ExpressionType.AndAlso) { - return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); + return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); } if (expressionType == ExpressionType.OrElse) { - return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); + return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); } } else @@ -1805,11 +1807,11 @@ namespace Umbraco.Core.Dynamics //right is invoked and unboxed to left's TOut, left was not boxed if (expressionType == ExpressionType.AndAlso) { - return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); + return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); } if (expressionType == ExpressionType.OrElse) { - return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); + return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); } } @@ -1880,7 +1882,7 @@ namespace Umbraco.Core.Dynamics break; case ExpressionType.Modulo: binaryExpression = Expression.Modulo(finalLeft, finalRight); - return (Expression.Lambda>(binaryExpression, parameters)); + return (Expression.Lambda>(binaryExpression, parameters)); case ExpressionType.AndAlso: if ((leftIsLambda && rightIsLambda && sequenceEqual) || (!leftIsLambda && !rightIsLambda)) { @@ -1888,7 +1890,7 @@ namespace Umbraco.Core.Dynamics } else { - return (Expression.Lambda>(Expression.AndAlso(finalLeft, finalRight), parameters)); + return (Expression.Lambda>(Expression.AndAlso(finalLeft, finalRight), parameters)); } case ExpressionType.OrElse: if (leftIsLambda && rightIsLambda && sequenceEqual || (!leftIsLambda && !rightIsLambda)) @@ -1897,7 +1899,7 @@ namespace Umbraco.Core.Dynamics } else { - return (Expression.Lambda>(Expression.OrElse(finalLeft, finalRight), parameters)); + return (Expression.Lambda>(Expression.OrElse(finalLeft, finalRight), parameters)); } default: return Expression.Equal(left, right); @@ -1905,7 +1907,7 @@ namespace Umbraco.Core.Dynamics if (leftIsLambda || rightIsLambda) { var body = Expression.Condition(Expression.TypeEqual(innerLeft, right.Type), binaryExpression, Expression.Constant(false)); - return Expression.Lambda>(body, parameters); + return Expression.Lambda>(body, parameters); } else { diff --git a/src/Umbraco.Web/Dynamics/ExtensionMethods.cs b/src/Umbraco.Web/Dynamics/ExtensionMethods.cs new file mode 100644 index 0000000000..316b4951bf --- /dev/null +++ b/src/Umbraco.Web/Dynamics/ExtensionMethods.cs @@ -0,0 +1,32 @@ +using System; +using System.Linq; +using Umbraco.Web.Models; + +namespace Umbraco.Web.Dynamics +{ + internal static class ExtensionMethods + { + + + public static DynamicPublishedContentList Random(this DynamicPublishedContentList all, int min, int max) + { + //get a random number generator + Random r = new Random(); + //choose the number of elements to be returned between Min and Max + int Number = r.Next(min, max); + //Call the other method + return Random(all, Number); + } + public static DynamicPublishedContentList Random(this DynamicPublishedContentList all, int max) + { + //Randomly order the items in the set by a Guid, Take the correct number, and return this wrapped in a new DynamicNodeList + return new DynamicPublishedContentList(all.Items.OrderBy(x => Guid.NewGuid()).Take(max)); + } + + public static DynamicPublishedContent Random(this DynamicPublishedContentList all) + { + return all.Items.OrderBy(x => Guid.NewGuid()).First(); + } + + } +} diff --git a/src/Umbraco.Core/Dynamics/Grouping.cs b/src/Umbraco.Web/Dynamics/Grouping.cs similarity index 86% rename from src/Umbraco.Core/Dynamics/Grouping.cs rename to src/Umbraco.Web/Dynamics/Grouping.cs index c3b5a4dcca..e36ce9167f 100644 --- a/src/Umbraco.Core/Dynamics/Grouping.cs +++ b/src/Umbraco.Web/Dynamics/Grouping.cs @@ -3,9 +3,10 @@ using System.Collections.Generic; using System.Linq; using System.Collections; using System.Dynamic; -using Umbraco.Core.Models; +using Umbraco.Core.Dynamics; +using Umbraco.Web.Models; -namespace Umbraco.Core.Dynamics +namespace Umbraco.Web.Dynamics { internal class Grouping : IGrouping where T : DynamicObject { @@ -14,7 +15,7 @@ namespace Umbraco.Core.Dynamics public IEnumerator GetEnumerator() { - var temp = new DynamicPublishedContentList(Elements.Cast()); + var temp = new DynamicPublishedContentList(Elements.Cast()); return (IEnumerator)temp.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() @@ -43,7 +44,7 @@ namespace Umbraco.Core.Dynamics object key = null; (item as DynamicObject).TryGetMember(new DynamicQueryableGetMemberBinder(ordering, false), out key); return key; - }).Cast()); + }).Cast()); } else { @@ -52,7 +53,7 @@ namespace Umbraco.Core.Dynamics object key = null; (item as DynamicObject).TryGetMember(new DynamicQueryableGetMemberBinder(ordering, false), out key); return key; - }).Cast()); + }).Cast()); } } } diff --git a/src/Umbraco.Web/ExamineExtensions.cs b/src/Umbraco.Web/ExamineExtensions.cs index 3f63345116..7ae2911f43 100644 --- a/src/Umbraco.Web/ExamineExtensions.cs +++ b/src/Umbraco.Web/ExamineExtensions.cs @@ -5,6 +5,7 @@ using System.Xml; using Examine; using Umbraco.Core.Dynamics; using Umbraco.Core.Models; +using Umbraco.Web.Models; namespace Umbraco.Web { @@ -32,7 +33,7 @@ namespace Umbraco.Web if (doc == null) continue; //skip if this doesn't exist in the cache doc.Properties.Add( new PropertyResult("examineScore", result.Score.ToString(), Guid.Empty, PropertyResultType.CustomProperty)); - var dynamicDoc = new DynamicPublishedContentBase(doc); + var dynamicDoc = new DynamicPublishedContent(doc); list.Add(dynamicDoc); } return list; diff --git a/src/Umbraco.Web/Models/DynamicPublishedContent.cs b/src/Umbraco.Web/Models/DynamicPublishedContent.cs index c0065823d8..d3487f0e77 100644 --- a/src/Umbraco.Web/Models/DynamicPublishedContent.cs +++ b/src/Umbraco.Web/Models/DynamicPublishedContent.cs @@ -19,25 +19,225 @@ namespace Umbraco.Web.Models /// /// The base dynamic model for views /// - public class DynamicPublishedContent : DynamicPublishedContentBase + public class DynamicPublishedContent : DynamicObject, IPublishedContent { /// /// This callback is used only so we can set it dynamically for use in unit tests /// internal static Func GetDataTypeCallback = (docTypeAlias, propertyAlias) => ContentType.GetDataType(docTypeAlias, propertyAlias); + + protected IPublishedContent PublishedContent { get; private set; } + private DynamicPublishedContentList _cachedChildren; + private readonly ConcurrentDictionary _cachedMemberOutput = new ConcurrentDictionary(); + + #region Constructors - public DynamicPublishedContent(IPublishedContent node) - : base(node) + public DynamicPublishedContent(IPublishedContent content) { + if (content == null) throw new ArgumentNullException("content"); + PublishedContent = content; + } + + #endregion + + public dynamic AsDynamic() + { + return this; + } + + public bool HasProperty(string name) + { + return PublishedContent.HasProperty(name); } /// - /// overriden method which uses PropertyEditorValueConverters to convert the resulting value + /// Attempts to call a method on the dynamic object + /// + /// + /// + /// + /// + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) + { + //TODO: We MUST cache the result here, it is very expensive to keep finding extension methods! + + try + { + //Property? + result = typeof(DynamicPublishedContent).InvokeMember(binder.Name, + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.GetProperty, + null, + this, + args); + return true; + } + catch (MissingMethodException) + { + try + { + //Static or Instance Method? + result = typeof(DynamicPublishedContent).InvokeMember(binder.Name, + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.Static | + System.Reflection.BindingFlags.InvokeMethod, + null, + this, + args); + return true; + } + catch (MissingMethodException) + { + try + { + result = ExecuteExtensionMethod(args, binder.Name); + return true; + } + catch (TargetInvocationException) + { + result = new DynamicNull(); + return true; + } + + catch + { + //TODO: LOg this! + + result = null; + return false; + } + + } + + + } + catch + { + result = null; + return false; + } + + } + + private object ExecuteExtensionMethod(object[] args, string name) + { + object result = null; + + var methodTypesToFind = new[] + { + typeof(DynamicPublishedContent) + }; + + //find known extension methods that match the first type in the list + MethodInfo toExecute = null; + foreach (var t in methodTypesToFind) + { + toExecute = ExtensionMethodFinder.FindExtensionMethod(t, args, name, false); + if (toExecute != null) + break; + } + + if (toExecute != null) + { + var genericArgs = (new[] { this }).Concat(args); + result = toExecute.Invoke(null, genericArgs.ToArray()); + } + else + { + throw new MissingMethodException(); + } + if (result != null) + { + if (result is IPublishedContent) + { + result = new DynamicPublishedContent((IPublishedContent)result); + } + if (result is IEnumerable) + { + result = new DynamicPublishedContentList((IEnumerable)result); + } + if (result is IEnumerable) + { + result = new DynamicPublishedContentList((IEnumerable)result); + } + } + return result; + } + + /// + /// Attempts to return a custom member (generally based on a string match) /// /// /// - protected override Attempt TryGetUserProperty(GetMemberBinder binder) + protected virtual Attempt TryGetCustomMember(GetMemberBinder binder) + { + if (binder.Name.InvariantEquals("ChildrenAsList") || binder.Name.InvariantEquals("Children")) + { + return new Attempt(true, Children); + } + + if (binder.Name.InvariantEquals("parentId")) + { + var parent = ((IPublishedContent) this).Parent; + if (parent == null) + { + throw new InvalidOperationException(string.Format("The node {0} does not have a parent", Id)); + } + return new Attempt(true, parent.Id); + } + return Attempt.False; + } + + /// + /// Attempts to return the children by the document type's alias (for example: CurrentPage.NewsItems where NewsItem is the + /// document type alias) + /// + /// + /// + /// + /// This method will work by both the plural and non-plural alias (i.e. NewsItem and NewsItems) + /// + protected virtual Attempt TryGetChildrenByAlias(GetMemberBinder binder) + { + + var filteredTypeChildren = PublishedContent.Children + .Where(x => x.DocumentTypeAlias.InvariantEquals(binder.Name) || x.DocumentTypeAlias.MakePluralName().InvariantEquals(binder.Name)) + .ToArray(); + if (filteredTypeChildren.Any()) + { + return new Attempt(true, + new DynamicPublishedContentList(filteredTypeChildren.Select(x => new DynamicPublishedContent(x)))); + } + return Attempt.False; + } + + /// + /// Attempts to return a member based on the reflected document property + /// + /// + /// + protected virtual Attempt TryGetDocumentProperty(GetMemberBinder binder) + { + + var reflectedProperty = GetReflectedProperty(binder.Name); + var result = reflectedProperty != null + ? reflectedProperty.Value + : null; + + return result == null + ? Attempt.False + : new Attempt(true, result); + } + + /// + /// Attempts to return a member based on a user defined umbraco property + /// + /// + /// + protected virtual Attempt TryGetUserProperty(GetMemberBinder binder) { var name = binder.Name; var recursive = false; @@ -72,8 +272,524 @@ namespace Umbraco.Web.Models } return new Attempt(true, result); + } + /// + /// Returns the member match methods in the correct order and is used in the TryGetMember method. + /// + /// + protected virtual IEnumerable>> GetMemberMatchMethods() + { + var memberMatchMethods = new List>> + { + TryGetCustomMember, //match custom members + TryGetUserProperty, //then match custom user defined umbraco properties + TryGetChildrenByAlias, //then try to match children based on doc type alias + TryGetDocumentProperty //then try to match on a reflected document property + }; + return memberMatchMethods; + } + + /// + /// Try to return an object based on the dynamic member accessor + /// + /// + /// + /// + /// + /// TODO: SD: This will alwasy return true so that no exceptions are generated, this is only because this is how the + /// old DynamicNode worked, I'm not sure if this is the correct/expected functionality but I've left it like that. + /// IMO I think this is incorrect and it would be better to throw an exception for something that is not supported! + /// + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + if (binder == null) throw new ArgumentNullException("binder"); + + var name = binder.Name; + + //check the cache first! + if (_cachedMemberOutput.TryGetValue(name, out result)) + { + return true; + } + + //loop through each member match method and execute it. + //If it is successful, cache the result and return it. + foreach (var attempt in GetMemberMatchMethods() + .Select(m => m(binder)) + .Where(attempt => attempt.Success)) + { + result = attempt.Result; + //cache the result so we don't have to re-process the whole thing + _cachedMemberOutput.TryAdd(name, result); + return true; + } + + //if property access, type lookup and member invoke all failed + //at this point, we're going to return null + //instead, we return a DynamicNull - see comments in that file + //this will let things like Model.ChildItem work and return nothing instead of crashing + + //.Where explictly checks for this type + //and will make it false + //which means backwards equality (&& property != true) will pass + //forwwards equality (&& property or && property == true) will fail + result = new DynamicNull(); + + //alwasy return true if we haven't thrown an exception though I'm wondering if we return 'false' if .Net throws an exception for us?? + return true; + } + + /// + /// Returns a property defined on the document object as a member property using reflection + /// + /// + /// + private PropertyResult GetReflectedProperty(string alias) + { + return GetPropertyInternal(alias, PublishedContent, false); + } + + /// + /// Return a user defined property + /// + /// + /// + /// + internal PropertyResult GetUserProperty(string alias, bool recursive = false) + { + if (!recursive) + { + return GetPropertyInternal(alias, PublishedContent); + } + var context = this; + var prop = GetPropertyInternal(alias, PublishedContent); + while (prop == null || !prop.HasValue()) + { + var parent = ((IPublishedContent) context).Parent; + if (parent == null) break; + prop = context.GetPropertyInternal(alias, context.PublishedContent); + } + return prop; + } + + + private PropertyResult GetPropertyInternal(string alias, IPublishedContent content, bool checkUserProperty = true) + { + if (alias.IsNullOrWhiteSpace()) throw new ArgumentNullException("alias"); + if (content == null) throw new ArgumentNullException("content"); + + //if we're looking for a user defined property + if (checkUserProperty) + { + var prop = content.GetProperty(alias); + + return prop == null + ? null + : new PropertyResult(prop, PropertyResultType.UserProperty) + { + DocumentTypeAlias = content.DocumentTypeAlias, + DocumentId = content.Id + }; + } + + //reflect + + Func> getMember = + memberAlias => + { + try + { + return new Attempt(true, + content.GetType().InvokeMember(memberAlias, + System.Reflection.BindingFlags.GetProperty | + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public, + null, + content, + null)); + } + catch (MissingMethodException ex) + { + return new Attempt(ex); + } + }; + + //try with the current casing + var attempt = getMember(alias); + if (!attempt.Success) + { + //if we cannot get with the current alias, try changing it's case + attempt = alias[0].IsUpperCase() + ? getMember(alias.ConvertCase(StringAliasCaseType.CamelCase)) + : getMember(alias.ConvertCase(StringAliasCaseType.PascalCase)); + } + + return !attempt.Success + ? null + : new PropertyResult(alias, attempt.Result, Guid.Empty, PropertyResultType.ReflectedProperty) + { + DocumentTypeAlias = content.DocumentTypeAlias, + DocumentId = content.Id + }; + } + + + + //public DynamicNode Media(string propertyAlias) + //{ + // if (_n != null) + // { + // IProperty prop = _n.GetProperty(propertyAlias); + // if (prop != null) + // { + // int mediaNodeId; + // if (int.TryParse(prop.Value, out mediaNodeId)) + // { + // return _razorLibrary.Value.MediaById(mediaNodeId); + // } + // } + // return null; + // } + // return null; + //} + //public bool IsProtected + //{ + // get + // { + // if (_n != null) + // { + // return umbraco.library.IsProtected(_n.Id, _n.Path); + // } + // return false; + // } + //} + //public bool HasAccess + //{ + // get + // { + // if (_n != null) + // { + // return umbraco.library.HasAccess(_n.Id, _n.Path); + // } + // return true; + // } + //} + + //public string Media(string propertyAlias, string mediaPropertyAlias) + //{ + // if (_n != null) + // { + // IProperty prop = _n.GetProperty(propertyAlias); + // if (prop == null && propertyAlias.Substring(0, 1).ToUpper() == propertyAlias.Substring(0, 1)) + // { + // prop = _n.GetProperty(propertyAlias.Substring(0, 1).ToLower() + propertyAlias.Substring((1))); + // } + // if (prop != null) + // { + // int mediaNodeId; + // if (int.TryParse(prop.Value, out mediaNodeId)) + // { + // umbraco.cms.businesslogic.media.Media media = new cms.businesslogic.media.Media(mediaNodeId); + // if (media != null) + // { + // Property mprop = media.getProperty(mediaPropertyAlias); + // // check for nicer support of Pascal Casing EVEN if alias is camelCasing: + // if (prop == null && mediaPropertyAlias.Substring(0, 1).ToUpper() == mediaPropertyAlias.Substring(0, 1)) + // { + // mprop = media.getProperty(mediaPropertyAlias.Substring(0, 1).ToLower() + mediaPropertyAlias.Substring((1))); + // } + // if (mprop != null) + // { + // return string.Format("{0}", mprop.Value); + // } + // } + // } + // } + // } + // return null; + //} + + #region Standard Properties + public int TemplateId + { + get { return PublishedContent.TemplateId; } + } + + public int SortOrder + { + get { return PublishedContent.SortOrder; } + } + + public string Name + { + get { return PublishedContent.Name; } + } + + public bool Visible + { + get { return PublishedContent.IsVisible(); } + } + + public bool IsVisible() + { + return PublishedContent.IsVisible(); + } + + public string UrlName + { + get { return PublishedContent.UrlName; } + } + + public string DocumentTypeAlias + { + get { return PublishedContent.DocumentTypeAlias; } + } + + public string WriterName + { + get { return PublishedContent.WriterName; } + } + + public string CreatorName + { + get { return PublishedContent.CreatorName; } + } + + public int WriterId + { + get { return PublishedContent.WriterId; } + } + + public int CreatorId + { + get { return PublishedContent.CreatorId; } + } + + public string Path + { + get { return PublishedContent.Path; } + } + + public DateTime CreateDate + { + get { return PublishedContent.CreateDate; } + } + + public int Id + { + get { return PublishedContent.Id; } + } + + public DateTime UpdateDate + { + get { return PublishedContent.UpdateDate; } + } + + public Guid Version + { + get { return PublishedContent.Version; } + } + + public int Level + { + get { return PublishedContent.Level; } + } + + public IEnumerable Properties + { + get { return PublishedContent.Properties; } + } + + public DynamicPublishedContentList Children + { + get + { + if (_cachedChildren == null) + { + var children = PublishedContent.Children; + //testing, think this must be a special case for the root node ? + if (!children.Any() && PublishedContent.Id == 0) + { + _cachedChildren = new DynamicPublishedContentList(new List { new DynamicPublishedContent(this.PublishedContent) }); + } + else + { + _cachedChildren = new DynamicPublishedContentList(PublishedContent.Children.Select(x => new DynamicPublishedContent(x))); + } + } + return _cachedChildren; + } + } + #endregion + + #region GetProperty methods which can be used with the dynamic object + + public IPublishedContentProperty GetProperty(string alias) + { + return GetProperty(alias, false); + } + public IPublishedContentProperty GetProperty(string alias, bool recursive) + { + return alias.StartsWith("@") + ? GetReflectedProperty(alias.TrimStart('@')) + : GetUserProperty(alias, recursive); + } + public string GetPropertyValue(string alias) + { + return GetPropertyValue(alias, false); + } + public string GetPropertyValue(string alias, string fallback) + { + var prop = GetPropertyValue(alias); + return !prop.IsNullOrWhiteSpace() ? prop : fallback; + } + public string GetPropertyValue(string alias, bool recursive) + { + var p = alias.StartsWith("@") + ? GetReflectedProperty(alias.TrimStart('@')) + : GetUserProperty(alias, recursive); + return p == null ? null : p.ValueAsString; + } + public string GetPropertyValue(string alias, bool recursive, string fallback) + { + var prop = GetPropertyValue(alias, recursive); + return !prop.IsNullOrWhiteSpace() ? prop : fallback; + } + + #endregion + + #region HasValue + public bool HasValue(string alias) + { + return this.PublishedContent.HasValue(alias); + } + public bool HasValue(string alias, bool recursive) + { + return this.PublishedContent.HasValue(alias, recursive); + } + public IHtmlString HasValue(string alias, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.HasValue(alias, valueIfTrue, valueIfFalse); + } + public IHtmlString HasValue(string alias, bool recursive, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.HasValue(alias, recursive, valueIfTrue, valueIfFalse); + } + public IHtmlString HasValue(string alias, string valueIfTrue) + { + return this.PublishedContent.HasValue(alias, valueIfTrue); + } + public IHtmlString HasValue(string alias, bool recursive, string valueIfTrue) + { + return this.PublishedContent.HasValue(alias, recursive, valueIfTrue); + } + #endregion + + #region Explicit IPublishedContent implementation + + IPublishedContent IPublishedContent.Parent + { + get { return PublishedContent.Parent; } + } + + int IPublishedContent.Id + { + get { return PublishedContent.Id; } + } + + int IPublishedContent.TemplateId + { + get { return PublishedContent.TemplateId; } + } + + int IPublishedContent.SortOrder + { + get { return PublishedContent.SortOrder; } + } + + string IPublishedContent.Name + { + get { return PublishedContent.Name; } + } + + string IPublishedContent.UrlName + { + get { return PublishedContent.UrlName; } + } + + string IPublishedContent.DocumentTypeAlias + { + get { return PublishedContent.DocumentTypeAlias; } + } + + int IPublishedContent.DocumentTypeId + { + get { return PublishedContent.DocumentTypeId; } + } + + string IPublishedContent.WriterName + { + get { return PublishedContent.WriterName; } + } + + string IPublishedContent.CreatorName + { + get { return PublishedContent.CreatorName; } + } + + int IPublishedContent.WriterId + { + get { return PublishedContent.WriterId; } + } + + int IPublishedContent.CreatorId + { + get { return PublishedContent.CreatorId; } + } + + string IPublishedContent.Path + { + get { return PublishedContent.Path; } + } + + DateTime IPublishedContent.CreateDate + { + get { return PublishedContent.CreateDate; } + } + + DateTime IPublishedContent.UpdateDate + { + get { return PublishedContent.UpdateDate; } + } + + Guid IPublishedContent.Version + { + get { return PublishedContent.Version; } + } + + int IPublishedContent.Level + { + get { return PublishedContent.Level; } + } + + System.Collections.ObjectModel.Collection IPublishedContent.Properties + { + get { return PublishedContent.Properties; } + } + + IEnumerable IPublishedContent.Children + { + get { return PublishedContent.Children; } + } + + IPublishedContentProperty IPublishedContent.GetProperty(string alias) + { + return PublishedContent.GetProperty(alias); + } + #endregion + private static Guid GetDataType(string docTypeAlias, string propertyAlias) { return GetDataTypeCallback(docTypeAlias, propertyAlias); @@ -296,75 +1012,75 @@ namespace Umbraco.Web.Models { return this.PublishedContent.IsOdd(valueIfTrue, valueIfFalse); } - public bool IsEqual(DynamicPublishedContentBase other) + public bool IsEqual(DynamicPublishedContent other) { return this.PublishedContent.IsEqual(other); } - public HtmlString IsEqual(DynamicPublishedContentBase other, string valueIfTrue) + public HtmlString IsEqual(DynamicPublishedContent other, string valueIfTrue) { return this.PublishedContent.IsEqual(other, valueIfTrue); } - public HtmlString IsEqual(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + public HtmlString IsEqual(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) { return this.PublishedContent.IsEqual(other, valueIfTrue, valueIfFalse); } - public bool IsNotEqual(DynamicPublishedContentBase other) + public bool IsNotEqual(DynamicPublishedContent other) { return this.PublishedContent.IsNotEqual(other); } - public HtmlString IsNotEqual(DynamicPublishedContentBase other, string valueIfTrue) + public HtmlString IsNotEqual(DynamicPublishedContent other, string valueIfTrue) { return this.PublishedContent.IsNotEqual(other, valueIfTrue); } - public HtmlString IsNotEqual(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + public HtmlString IsNotEqual(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) { return this.PublishedContent.IsNotEqual(other, valueIfTrue, valueIfFalse); } - public bool IsDescendant(DynamicPublishedContentBase other) + public bool IsDescendant(DynamicPublishedContent other) { return this.PublishedContent.IsDescendant(other); } - public HtmlString IsDescendant(DynamicPublishedContentBase other, string valueIfTrue) + public HtmlString IsDescendant(DynamicPublishedContent other, string valueIfTrue) { return this.PublishedContent.IsDescendant(other, valueIfTrue); } - public HtmlString IsDescendant(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + public HtmlString IsDescendant(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) { return this.PublishedContent.IsDescendant(other, valueIfTrue, valueIfFalse); } - public bool IsDescendantOrSelf(DynamicPublishedContentBase other) + public bool IsDescendantOrSelf(DynamicPublishedContent other) { return this.PublishedContent.IsDescendantOrSelf(other); } - public HtmlString IsDescendantOrSelf(DynamicPublishedContentBase other, string valueIfTrue) + public HtmlString IsDescendantOrSelf(DynamicPublishedContent other, string valueIfTrue) { return this.PublishedContent.IsDescendantOrSelf(other, valueIfTrue); } - public HtmlString IsDescendantOrSelf(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + public HtmlString IsDescendantOrSelf(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) { return this.PublishedContent.IsDescendantOrSelf(other, valueIfTrue, valueIfFalse); } - public bool IsAncestor(DynamicPublishedContentBase other) + public bool IsAncestor(DynamicPublishedContent other) { return this.PublishedContent.IsAncestor(other); } - public HtmlString IsAncestor(DynamicPublishedContentBase other, string valueIfTrue) + public HtmlString IsAncestor(DynamicPublishedContent other, string valueIfTrue) { return this.PublishedContent.IsAncestor(other, valueIfTrue); } - public HtmlString IsAncestor(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + public HtmlString IsAncestor(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) { return this.PublishedContent.IsAncestor(other, valueIfTrue, valueIfFalse); } - public bool IsAncestorOrSelf(DynamicPublishedContentBase other) + public bool IsAncestorOrSelf(DynamicPublishedContent other) { return this.PublishedContent.IsAncestorOrSelf(other); } - public HtmlString IsAncestorOrSelf(DynamicPublishedContentBase other, string valueIfTrue) + public HtmlString IsAncestorOrSelf(DynamicPublishedContent other, string valueIfTrue) { return this.PublishedContent.IsAncestorOrSelf(other, valueIfTrue); } - public HtmlString IsAncestorOrSelf(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + public HtmlString IsAncestorOrSelf(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) { return this.PublishedContent.IsAncestorOrSelf(other, valueIfTrue, valueIfFalse); } @@ -539,7 +1255,36 @@ namespace Umbraco.Web.Models } #endregion - + + #region Where + + public HtmlString Where(string predicate, string valueIfTrue) + { + return Where(predicate, valueIfTrue, string.Empty); + } + public HtmlString Where(string predicate, string valueIfTrue, string valueIfFalse) + { + if (Where(predicate)) + { + return new HtmlString(valueIfTrue); + } + return new HtmlString(valueIfFalse); + } + public bool Where(string predicate) + { + //Totally gonna cheat here + var dynamicDocumentList = new DynamicPublishedContentList(); + dynamicDocumentList.Add(this); + var filtered = dynamicDocumentList.Where(predicate); + if (Queryable.Count(filtered) == 1) + { + //this node matches the predicate + return true; + } + return false; + } + + #endregion } } diff --git a/src/Umbraco.Core/Models/DynamicPublishedContentList.cs b/src/Umbraco.Web/Models/DynamicPublishedContentList.cs similarity index 87% rename from src/Umbraco.Core/Models/DynamicPublishedContentList.cs rename to src/Umbraco.Web/Models/DynamicPublishedContentList.cs index c3b47f2571..4f52fe66eb 100644 --- a/src/Umbraco.Core/Models/DynamicPublishedContentList.cs +++ b/src/Umbraco.Web/Models/DynamicPublishedContentList.cs @@ -2,21 +2,24 @@ using System.Collections.Generic; using System.Linq; using System.Dynamic; +using Umbraco.Core; using Umbraco.Core.Dynamics; using System.Collections; using System.Reflection; +using Umbraco.Core.Models; +using Umbraco.Web.Dynamics; -namespace Umbraco.Core.Models +namespace Umbraco.Web.Models { - public class DynamicPublishedContentList : DynamicObject, IEnumerable + public class DynamicPublishedContentList : DynamicObject, IEnumerable { - internal List Items { get; set; } + internal List Items { get; set; } public DynamicPublishedContentList() { - Items = new List(); + Items = new List(); } - public DynamicPublishedContentList(IEnumerable items) + public DynamicPublishedContentList(IEnumerable items) { var list = items.ToList(); Items = list; @@ -24,7 +27,7 @@ namespace Umbraco.Core.Models public DynamicPublishedContentList(IEnumerable items) { - var list = items.Select(x => new DynamicPublishedContentBase(x)).ToList(); + var list = items.Select(x => new DynamicPublishedContent(x)).ToList(); Items = list; } public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) @@ -58,14 +61,14 @@ namespace Umbraco.Core.Models var values = args.Skip(1).ToArray(); //TODO: We are pre-resolving the where into a ToList() here which will have performance impacts if there where clauses // are nested! We should somehow support an QueryableDocumentList! - result = new DynamicPublishedContentList(this.Where(predicate, values).ToList()); + result = new DynamicPublishedContentList(this.Where(predicate, values).ToList()); return true; } if (name == "OrderBy") { //TODO: We are pre-resolving the where into a ToList() here which will have performance impacts if there where clauses // are nested! We should somehow support an QueryableDocumentList! - result = new DynamicPublishedContentList(this.OrderBy(args.First().ToString()).ToList()); + result = new DynamicPublishedContentList(this.OrderBy(args.First().ToString()).ToList()); return true; } if (name == "Take") @@ -83,7 +86,7 @@ namespace Umbraco.Core.Models int groupSize = 0; if (int.TryParse(args.First().ToString(), out groupSize)) { - result = this.InGroupsOf(groupSize); + result = InGroupsOf(groupSize); return true; } result = new DynamicNull(); @@ -94,7 +97,7 @@ namespace Umbraco.Core.Models int groupCount = 0; if (int.TryParse(args.First().ToString(), out groupCount)) { - result = this.GroupedInto(groupCount); + result = GroupedInto(groupCount); return true; } result = new DynamicNull(); @@ -102,7 +105,7 @@ namespace Umbraco.Core.Models } if (name == "GroupBy") { - result = this.GroupBy(args.First().ToString()); + result = GroupBy(args.First().ToString()); return true; } if (name == "Average" || name == "Min" || name == "Max" || name == "Sum") @@ -112,9 +115,9 @@ namespace Umbraco.Core.Models } if (name == "Union") { - if ((args.First() as IEnumerable) != null) + if ((args.First() as IEnumerable) != null) { - result = new DynamicPublishedContentList(this.Items.Union(args.First() as IEnumerable)); + result = new DynamicPublishedContentList(this.Items.Union(args.First() as IEnumerable)); return true; } if ((args.First() as DynamicPublishedContentList) != null) @@ -125,9 +128,9 @@ namespace Umbraco.Core.Models } if (name == "Except") { - if ((args.First() as IEnumerable) != null) + if ((args.First() as IEnumerable) != null) { - result = new DynamicPublishedContentList(this.Items.Except(args.First() as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); + result = new DynamicPublishedContentList(this.Items.Except(args.First() as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); return true; } if ((args.First() as DynamicPublishedContentList) != null) @@ -138,9 +141,9 @@ namespace Umbraco.Core.Models } if (name == "Intersect") { - if ((args.First() as IEnumerable) != null) + if ((args.First() as IEnumerable) != null) { - result = new DynamicPublishedContentList(this.Items.Intersect(args.First() as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); + result = new DynamicPublishedContentList(this.Items.Intersect(args.First() as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); return true; } if ((args.First() as DynamicPublishedContentList) != null) @@ -198,8 +201,8 @@ namespace Umbraco.Core.Models { //We do this to enable error checking of Razor Syntax when a method e.g. ElementAt(2) is used. //When the Script is tested, there's no Children which means ElementAt(2) is invalid (IndexOutOfRange) - //Instead, we are going to return an empty DynamicNode. - result = DynamicPublishedContentBase.Empty(); + //Instead, we are going to return DynamicNull; + result = new DynamicNull(); return true; } @@ -220,7 +223,7 @@ namespace Umbraco.Core.Models } } - private T Aggregate(List data, string name) where T : struct + private T Aggregate(IEnumerable data, string name) where T : struct { switch (name) { @@ -365,7 +368,7 @@ namespace Umbraco.Core.Models var methodTypesToFind = new[] { - typeof(IEnumerable), + typeof(IEnumerable), typeof(DynamicPublishedContentList) }; @@ -405,15 +408,15 @@ namespace Umbraco.Core.Models { if (result is IPublishedContent) { - result = new DynamicPublishedContentBase((IPublishedContent)result); + result = new DynamicPublishedContent((IPublishedContent)result); } if (result is IEnumerable) { result = new DynamicPublishedContentList((IEnumerable)result); } - if (result is IEnumerable) + if (result is IEnumerable) { - result = new DynamicPublishedContentList((IEnumerable)result); + result = new DynamicPublishedContentList((IEnumerable)result); } } return result; @@ -427,33 +430,33 @@ namespace Umbraco.Core.Models { return ((IQueryable)Items.AsQueryable()).OrderBy(key); } - public DynamicGrouping GroupBy(string key) + public DynamicGrouping GroupBy(string key) { var group = new DynamicGrouping(this, key); return group; } - public DynamicGrouping GroupedInto(int groupCount) + public DynamicGrouping GroupedInto(int groupCount) { int groupSize = (int)Math.Ceiling(((decimal)Items.Count() / groupCount)); return new DynamicGrouping( this .Items - .Select((node, index) => new KeyValuePair(index, node)) + .Select((node, index) => new KeyValuePair(index, node)) .GroupBy(kv => (object)(kv.Key / groupSize)) - .Select(item => new Grouping() + .Select(item => new Grouping() { Key = item.Key, Elements = item.Select(inner => inner.Value) })); } - public DynamicGrouping InGroupsOf(int groupSize) + public DynamicGrouping InGroupsOf(int groupSize) { return new DynamicGrouping( this .Items - .Select((node, index) => new KeyValuePair(index, node)) + .Select((node, index) => new KeyValuePair(index, node)) .GroupBy(kv => (object)(kv.Key / groupSize)) - .Select(item => new Grouping() + .Select(item => new Grouping() { Key = item.Key, Elements = item.Select(inner => inner.Value) @@ -463,14 +466,14 @@ namespace Umbraco.Core.Models public IQueryable Select(string predicate, params object[] values) { - return Items.AsQueryable().Select(predicate, values); + return DynamicQueryable.Select(Items.AsQueryable(), predicate, values); } - public void Add(DynamicPublishedContentBase publishedContent) + public void Add(DynamicPublishedContent publishedContent) { this.Items.Add(publishedContent); } - public void Remove(DynamicPublishedContentBase publishedContent) + public void Remove(DynamicPublishedContent publishedContent) { if (this.Items.Contains(publishedContent)) { @@ -486,7 +489,7 @@ namespace Umbraco.Core.Models return true; } - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { return Items.GetEnumerator(); } diff --git a/src/Umbraco.Web/Models/XmlPublishedContent.cs b/src/Umbraco.Web/Models/XmlPublishedContent.cs index d603e2ef5a..41e134bee2 100644 --- a/src/Umbraco.Web/Models/XmlPublishedContent.cs +++ b/src/Umbraco.Web/Models/XmlPublishedContent.cs @@ -60,7 +60,7 @@ namespace Umbraco.Web.Models private DateTime _createDate; private DateTime _updateDate; private Guid _version; - private readonly Collection _properties = new Collection(); + private readonly Collection _properties = new Collection(); private readonly XmlNode _pageXmlNode; private int _sortOrder; private int _level; @@ -75,7 +75,7 @@ namespace Umbraco.Web.Models } } - public IDocumentProperty GetProperty(string alias) + public IPublishedContentProperty GetProperty(string alias) { return Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); } @@ -251,7 +251,7 @@ namespace Umbraco.Web.Models } } - public Collection Properties + public Collection Properties { get { @@ -336,7 +336,7 @@ namespace Umbraco.Web.Models // load data var dataXPath = UmbracoSettings.UseLegacyXmlSchema ? "data" : "* [not(@isDoc)]"; foreach (XmlNode n in _pageXmlNode.SelectNodes(dataXPath)) - _properties.Add(new XmlDocumentProperty(n)); + _properties.Add(new XmlPublishedContentProperty(n)); // load children var childXPath = UmbracoSettings.UseLegacyXmlSchema ? "node" : "* [@isDoc]"; diff --git a/src/Umbraco.Web/Models/XmlDocumentProperty.cs b/src/Umbraco.Web/Models/XmlPublishedContentProperty.cs similarity index 88% rename from src/Umbraco.Web/Models/XmlDocumentProperty.cs rename to src/Umbraco.Web/Models/XmlPublishedContentProperty.cs index 7affc17852..386c75ec39 100644 --- a/src/Umbraco.Web/Models/XmlDocumentProperty.cs +++ b/src/Umbraco.Web/Models/XmlPublishedContentProperty.cs @@ -15,7 +15,7 @@ namespace Umbraco.Web.Models /// [Serializable] [XmlType(Namespace = "http://umbraco.org/webservices/")] - public class XmlDocumentProperty : IDocumentProperty + public class XmlPublishedContentProperty : IPublishedContentProperty { private readonly Guid _version; private readonly string _alias; @@ -54,12 +54,12 @@ namespace Umbraco.Web.Models get { return _version; } } - public XmlDocumentProperty() + public XmlPublishedContentProperty() { } - public XmlDocumentProperty(XmlNode propertyXmlData) + public XmlPublishedContentProperty(XmlNode propertyXmlData) { if (propertyXmlData != null) { diff --git a/src/Umbraco.Web/Mvc/RenderViewPage.cs b/src/Umbraco.Web/Mvc/RenderViewPage.cs index 00bb93fb9a..d161da0d23 100644 --- a/src/Umbraco.Web/Mvc/RenderViewPage.cs +++ b/src/Umbraco.Web/Mvc/RenderViewPage.cs @@ -3,6 +3,7 @@ using Umbraco.Core; using Umbraco.Core.Dictionary; using Umbraco.Core.Dynamics; using Umbraco.Core.Models; +using Umbraco.Web.Models; using Umbraco.Web.Routing; namespace Umbraco.Web.Mvc @@ -24,7 +25,7 @@ namespace Umbraco.Web.Mvc { ////this.ViewData.Model = Model; //var backingItem = new DynamicBackingItem(Model.CurrentNode); - var dynamicNode = new DynamicPublishedContentBase(Model.CurrentContent); + var dynamicNode = new DynamicPublishedContent(Model.CurrentContent); CurrentPage = dynamicNode.AsDynamic(); } PublishedContentRequest = (PublishedContentRequest)ViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-doc-request"); diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 1626346c13..5ca1d4f364 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -22,6 +22,41 @@ namespace Umbraco.Web public static class PublishedContentExtensions { + #region List Extensions + + public static IQueryable OrderBy(this IEnumerable list, string predicate) + { + var dList = new DynamicPublishedContentList(list); + return dList.OrderBy(predicate); + } + + public static IQueryable Where(this IEnumerable list, string predicate) + { + var dList = new DynamicPublishedContentList(list); + return dList.Where(predicate); + } + + public static IEnumerable> GroupBy(this IEnumerable list, string predicate) + { + var dList = new DynamicPublishedContentList(list); + return dList.GroupBy(predicate); + } + + public static IQueryable Select(this IEnumerable list, string predicate, params object[] values) + { + var dList = new DynamicPublishedContentList(list); + return dList.Select(predicate); + } + + #endregion + + public static dynamic AsDynamic(this IPublishedContent doc) + { + if (doc == null) throw new ArgumentNullException("doc"); + var dd = new DynamicPublishedContent(doc); + return dd.AsDynamic(); + } + /// /// Converts a IPublishedContent to a DynamicPublishedContent and tests for null /// @@ -34,27 +69,39 @@ namespace Umbraco.Web return new DynamicPublishedContent(content); } - public static IDocumentProperty GetProperty(this IPublishedContent content, string alias, bool recursive) + #region Where + + public static HtmlString Where(this IPublishedContent doc, string predicate, string valueIfTrue) { - return content.GetUserRecursive(alias, recursive); + if (doc == null) throw new ArgumentNullException("doc"); + return doc.Where(predicate, valueIfTrue, string.Empty); } - private static IDocumentProperty GetUserRecursive(this IPublishedContent content, string alias, bool recursive = false) + public static HtmlString Where(this IPublishedContent doc, string predicate, string valueIfTrue, string valueIfFalse) { - if (!recursive) + if (doc == null) throw new ArgumentNullException("doc"); + if (doc.Where(predicate)) { - return content.GetProperty(alias); + return new HtmlString(valueIfTrue); } - var context = content; - var prop = content.GetUserRecursive(alias); - while (prop == null || prop.Value == null || prop.Value.ToString().IsNullOrWhiteSpace()) - { - var parent = context.Parent; - if (parent == null) break; - prop = context.GetUserRecursive(alias); - } - return prop; + return new HtmlString(valueIfFalse); } + public static bool Where(this IPublishedContent doc, string predicate) + { + if (doc == null) throw new ArgumentNullException("doc"); + //Totally gonna cheat here + var dynamicDocumentList = new DynamicPublishedContentList(); + dynamicDocumentList.Add(doc.AsDynamicPublishedContent()); + var filtered = dynamicDocumentList.Where(predicate); + if (Queryable.Count(filtered) == 1) + { + //this node matches the predicate + return true; + } + return false; + } + + #endregion #region Position/Index public static int Position(this IPublishedContent content) @@ -288,42 +335,42 @@ namespace Umbraco.Web var ancestors = content.AncestorsOrSelf(); return content.IsHelper(n => ancestors.FirstOrDefault(ancestor => ancestor.Id == other.Id) != null); } - public static HtmlString IsDescendantOrSelf(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue) + public static HtmlString IsDescendantOrSelf(this IPublishedContent content, DynamicPublishedContent other, string valueIfTrue) { var ancestors = content.AncestorsOrSelf(); return content.IsHelper(n => ancestors.FirstOrDefault(ancestor => ancestor.Id == other.Id) != null, valueIfTrue); } - public static HtmlString IsDescendantOrSelf(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + public static HtmlString IsDescendantOrSelf(this IPublishedContent content, DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) { var ancestors = content.AncestorsOrSelf(); return content.IsHelper(n => ancestors.FirstOrDefault(ancestor => ancestor.Id == other.Id) != null, valueIfTrue, valueIfFalse); } - public static bool IsAncestor(this IPublishedContent content, DynamicPublishedContentBase other) + public static bool IsAncestor(this IPublishedContent content, DynamicPublishedContent other) { var descendants = content.Descendants(); return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null); } - public static HtmlString IsAncestor(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue) + public static HtmlString IsAncestor(this IPublishedContent content, DynamicPublishedContent other, string valueIfTrue) { var descendants = content.Descendants(); return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null, valueIfTrue); } - public static HtmlString IsAncestor(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + public static HtmlString IsAncestor(this IPublishedContent content, DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) { var descendants = content.Descendants(); return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null, valueIfTrue, valueIfFalse); } - public static bool IsAncestorOrSelf(this IPublishedContent content, DynamicPublishedContentBase other) + public static bool IsAncestorOrSelf(this IPublishedContent content, DynamicPublishedContent other) { var descendants = content.DescendantsOrSelf(); return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null); } - public static HtmlString IsAncestorOrSelf(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue) + public static HtmlString IsAncestorOrSelf(this IPublishedContent content, DynamicPublishedContent other, string valueIfTrue) { var descendants = content.DescendantsOrSelf(); return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null, valueIfTrue); } - public static HtmlString IsAncestorOrSelf(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + public static HtmlString IsAncestorOrSelf(this IPublishedContent content, DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) { var descendants = content.DescendantsOrSelf(); return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null, valueIfTrue, valueIfFalse); @@ -504,7 +551,7 @@ namespace Umbraco.Web thisNode.Add(content); } var flattenedNodes = content.Children.Map(func, (IPublishedContent n) => n.Children); - return thisNode.Concat(flattenedNodes).ToList().ConvertAll(dynamicBackingItem => new DynamicPublishedContentBase(dynamicBackingItem)); + return thisNode.Concat(flattenedNodes).ToList().ConvertAll(dynamicBackingItem => new DynamicPublishedContent(dynamicBackingItem)); } return Enumerable.Empty(); } @@ -748,7 +795,7 @@ namespace Umbraco.Web {"Url", urlProvider.GetNiceUrl(n.Id)} }; var userVals = new Dictionary(); - foreach (var p in from IDocumentProperty p in n.Properties where p.Value != null select p) + foreach (var p in from IPublishedContentProperty p in n.Properties where p.Value != null select p) { userVals[p.Alias] = p.Value; } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 50017de4e1..1465cdcb82 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -240,7 +240,16 @@ Properties\SolutionInfo.cs + + + + + + + + + @@ -326,7 +335,7 @@ - + diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 3c65470112..dd8636c8cc 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -15,6 +15,7 @@ using Umbraco.Core.Dictionary; using Umbraco.Core.Dynamics; using Umbraco.Core.IO; using Umbraco.Core.Models; +using Umbraco.Web.Models; using Umbraco.Web.Mvc; using Umbraco.Web.Templates; using umbraco; @@ -487,7 +488,7 @@ namespace Umbraco.Web var doc = store.GetDocumentById(UmbracoContext.Current, id); return doc == null ? new DynamicNull() - : new DynamicPublishedContentBase(doc).AsDynamic(); + : new DynamicPublishedContent(doc).AsDynamic(); } private dynamic DocumentById(string id, IPublishedStore store) @@ -502,7 +503,7 @@ namespace Umbraco.Web { var nodes = ids.Select(eachId => DocumentById(eachId, store)) .Where(x => !TypeHelper.IsTypeAssignableFrom(x)) - .Cast(); + .Cast(); return new DynamicPublishedContentList(nodes); } @@ -510,7 +511,7 @@ namespace Umbraco.Web { var nodes = ids.Select(eachId => DocumentById(eachId, store)) .Where(x => !TypeHelper.IsTypeAssignableFrom(x)) - .Cast(); + .Cast(); return new DynamicPublishedContentList(nodes); } diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicExpression.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicExpression.cs index 9720fe69dd..f872950344 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicExpression.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicExpression.cs @@ -9,37 +9,37 @@ namespace System.Linq.Dynamic { public static bool ConvertDynamicNullToBooleanFalse { - get { return Umbraco.Core.Dynamics.DynamicExpression.ConvertDynamicNullToBooleanFalse; } - set { Umbraco.Core.Dynamics.DynamicExpression.ConvertDynamicNullToBooleanFalse = value; } + get { return Umbraco.Web.Dynamics.DynamicExpression.ConvertDynamicNullToBooleanFalse; } + set { Umbraco.Web.Dynamics.DynamicExpression.ConvertDynamicNullToBooleanFalse = value; } } public static Expression Parse(Type resultType, string expression, bool convertDynamicNullToBooleanFalse, params object[] values) { - return Umbraco.Core.Dynamics.DynamicExpression.Parse(resultType, expression, convertDynamicNullToBooleanFalse, values); + return Umbraco.Web.Dynamics.DynamicExpression.Parse(resultType, expression, convertDynamicNullToBooleanFalse, values); } public static LambdaExpression ParseLambda(Type itType, Type resultType, string expression, bool convertDynamicNullToBooleanFalse, params object[] values) { - return Umbraco.Core.Dynamics.DynamicExpression.ParseLambda(itType, resultType, expression, convertDynamicNullToBooleanFalse, values); + return Umbraco.Web.Dynamics.DynamicExpression.ParseLambda(itType, resultType, expression, convertDynamicNullToBooleanFalse, values); } public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, bool convertDynamicNullToBooleanFalse, params object[] values) { - return Umbraco.Core.Dynamics.DynamicExpression.ParseLambda(parameters, resultType, expression, convertDynamicNullToBooleanFalse, values); + return Umbraco.Web.Dynamics.DynamicExpression.ParseLambda(parameters, resultType, expression, convertDynamicNullToBooleanFalse, values); } public static Expression> ParseLambda(string expression, bool convertDynamicNullToBooleanFalse, params object[] values) { - return Umbraco.Core.Dynamics.DynamicExpression.ParseLambda(expression, convertDynamicNullToBooleanFalse, values); + return Umbraco.Web.Dynamics.DynamicExpression.ParseLambda(expression, convertDynamicNullToBooleanFalse, values); } public static Type CreateClass(params DynamicProperty[] properties) { - return Umbraco.Core.Dynamics.DynamicExpression.CreateClass(properties.Select(x => new Umbraco.Core.Dynamics.DynamicProperty(x.Name, x.Type))); + return Umbraco.Web.Dynamics.DynamicExpression.CreateClass(properties.Select(x => new Umbraco.Core.Dynamics.DynamicProperty(x.Name, x.Type))); } public static Type CreateClass(IEnumerable properties) { - return Umbraco.Core.Dynamics.DynamicExpression.CreateClass(properties.Select(x => new Umbraco.Core.Dynamics.DynamicProperty(x.Name, x.Type))); + return Umbraco.Web.Dynamics.DynamicExpression.CreateClass(properties.Select(x => new Umbraco.Core.Dynamics.DynamicProperty(x.Name, x.Type))); } } } \ No newline at end of file diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicQueryable.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicQueryable.cs index 178ec05132..3be3e33b56 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicQueryable.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicQueryable.cs @@ -23,7 +23,7 @@ namespace System.Linq.Dynamic { if (source == null) throw new ArgumentNullException("source"); if (predicate == null) throw new ArgumentNullException("predicate"); - LambdaExpression lambda = Umbraco.Core.Dynamics.DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, true, values); + LambdaExpression lambda = Umbraco.Web.Dynamics.DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, true, values); if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicNode)) { //source list is DynamicNode and the lambda returns a Func @@ -93,7 +93,7 @@ namespace System.Linq.Dynamic { if (source == null) throw new ArgumentNullException("source"); if (selector == null) throw new ArgumentNullException("selector"); - LambdaExpression lambda = Umbraco.Core.Dynamics.DynamicExpression.ParseLambda(source.ElementType, typeof(object), selector, false, values); + LambdaExpression lambda = Umbraco.Web.Dynamics.DynamicExpression.ParseLambda(source.ElementType, typeof(object), selector, false, values); if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicNode)) { //source list is DynamicNode and the lambda returns a Func @@ -151,7 +151,7 @@ namespace System.Linq.Dynamic descending = true; } - LambdaExpression lambda = Umbraco.Core.Dynamics.DynamicExpression.ParseLambda(source.ElementType, typeof(object), ordering, false, values); + LambdaExpression lambda = Umbraco.Web.Dynamics.DynamicExpression.ParseLambda(source.ElementType, typeof(object), ordering, false, values); if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicNode)) { //source list is DynamicNode and the lambda returns a Func @@ -288,8 +288,8 @@ namespace System.Linq.Dynamic if (source == null) throw new ArgumentNullException("source"); if (keySelector == null) throw new ArgumentNullException("keySelector"); if (elementSelector == null) throw new ArgumentNullException("elementSelector"); - LambdaExpression keyLambda = Umbraco.Core.Dynamics.DynamicExpression.ParseLambda(source.ElementType, null, keySelector, true, values); - LambdaExpression elementLambda = Umbraco.Core.Dynamics.DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, true, values); + LambdaExpression keyLambda = Umbraco.Web.Dynamics.DynamicExpression.ParseLambda(source.ElementType, null, keySelector, true, values); + LambdaExpression elementLambda = Umbraco.Web.Dynamics.DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, true, values); return source.Provider.CreateQuery( Expression.Call( typeof(Queryable), "GroupBy", diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/ExpressionParser.cs b/src/umbraco.MacroEngines/RazorDynamicNode/ExpressionParser.cs index b737b8eeff..a0673b9425 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/ExpressionParser.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/ExpressionParser.cs @@ -6,7 +6,7 @@ using umbraco.MacroEngines; namespace System.Linq.Dynamic { - internal class ExpressionParser : Umbraco.Core.Dynamics.ExpressionParser + internal class ExpressionParser : Umbraco.Web.Dynamics.ExpressionParser { public ExpressionParser(ParameterExpression[] parameters, string expression, object[] values) : base(parameters, expression, values) diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/PublishedContentExtensions.cs b/src/umbraco.MacroEngines/RazorDynamicNode/PublishedContentExtensions.cs index 6116f44fbf..d6cefb5191 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/PublishedContentExtensions.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/PublishedContentExtensions.cs @@ -17,7 +17,7 @@ namespace umbraco.MacroEngines.Library internal static class PublishedContentExtensions { - internal static IProperty ConvertToNodeProperty(this IDocumentProperty prop) + internal static IProperty ConvertToNodeProperty(this IPublishedContentProperty prop) { return new PropertyResult(prop.Alias, prop.Value.ToString(), prop.Version); } diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs b/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs index e756fdf534..c583894aa9 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs @@ -6,6 +6,7 @@ using System.Web.Mvc; using Umbraco.Core.Dynamics; using Umbraco.Core.Models; using Umbraco.Web; +using Umbraco.Web.Models; using umbraco.interfaces; using System.Xml.Linq; using System.Xml.XPath;