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;