From 54e5140d6a732c1390bbef3f1eab5af26ac926fa Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Thu, 4 Oct 2012 03:26:56 +0500 Subject: [PATCH] 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;