diff --git a/src/Umbraco.Core/Dynamics/DynamicGrouping.cs b/src/Umbraco.Core/Dynamics/DynamicGrouping.cs index 11c2b1df65..9acd8c54b7 100644 --- a/src/Umbraco.Core/Dynamics/DynamicGrouping.cs +++ b/src/Umbraco.Core/Dynamics/DynamicGrouping.cs @@ -1,12 +1,13 @@ using System.Collections.Generic; using System.Linq; using System.Collections; +using Umbraco.Core.Models; namespace Umbraco.Core.Dynamics { public class DynamicGrouping : IEnumerable { - internal IEnumerable> Inner; + internal IEnumerable> Inner; public DynamicGrouping OrderBy(string expression) { @@ -21,7 +22,7 @@ namespace Umbraco.Core.Dynamics .Select(node => { string predicate = groupBy; - var internalList = new DynamicPublishedContentList(new DynamicPublishedContent[] { node }); + var internalList = new DynamicPublishedContentList(new DynamicPublishedContentBase[] { node }); var query = (IQueryable)internalList.Select(predicate, new object[] { }); var key = query.FirstOrDefault(); return new @@ -32,13 +33,13 @@ namespace Umbraco.Core.Dynamics }) .Where(item => item.Key != null) .GroupBy(item => item.Key) - .Select(item => new Grouping() + .Select(item => new Grouping() { Key = item.Key, Elements = item.Select(inner => inner.Node) }); } - internal DynamicGrouping(IEnumerable> source) + internal DynamicGrouping(IEnumerable> source) { this.Inner = source; } diff --git a/src/Umbraco.Core/Dynamics/DynamicPublishedContent.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContent.cs deleted file mode 100644 index a747163293..0000000000 --- a/src/Umbraco.Core/Dynamics/DynamicPublishedContent.cs +++ /dev/null @@ -1,1516 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Dynamic; -using System.Linq; -using System.Web; -using Umbraco.Core.Configuration; -using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; -using umbraco.interfaces; -using System.Reflection; -using System.Xml.Linq; - -namespace Umbraco.Core.Dynamics -{ - - /// - /// The dynamic model for views - /// - public class DynamicPublishedContent : DynamicObject, IPublishedContent - { - private readonly IPublishedContent _publishedContent; - private DynamicPublishedContentList _cachedChildren; - private readonly ConcurrentDictionary _cachedMemberOutput = new ConcurrentDictionary(); - - internal DynamicPublishedContentList OwnerList { get; set; } - - #region Constructors - - public DynamicPublishedContent(IPublishedContent node) - { - if (node == null) throw new ArgumentNullException("node"); - _publishedContent = node; - } - - /// - /// Returns an empty/blank DynamicPublishedContent, this is used for special case scenarios - /// - /// - internal static DynamicPublishedContent Empty() - { - return new DynamicPublishedContent(); - } - - private DynamicPublishedContent() - { - } - - #endregion - - public dynamic AsDynamic() - { - return this; - } - - #region Traversal - public DynamicPublishedContent Up() - { - return DynamicPublishedContentWalker.Up(this); - } - public DynamicPublishedContent Up(int number) - { - return DynamicPublishedContentWalker.Up(this, number); - } - public DynamicPublishedContent Up(string nodeTypeAlias) - { - return DynamicPublishedContentWalker.Up(this, nodeTypeAlias); - } - public DynamicPublishedContent Down() - { - return DynamicPublishedContentWalker.Down(this); - } - public DynamicPublishedContent Down(int number) - { - return DynamicPublishedContentWalker.Down(this, number); - } - public DynamicPublishedContent Down(string nodeTypeAlias) - { - return DynamicPublishedContentWalker.Down(this, nodeTypeAlias); - } - public DynamicPublishedContent Next() - { - return DynamicPublishedContentWalker.Next(this); - } - public DynamicPublishedContent Next(int number) - { - return DynamicPublishedContentWalker.Next(this, number); - } - public DynamicPublishedContent Next(string nodeTypeAlias) - { - return DynamicPublishedContentWalker.Next(this, nodeTypeAlias); - } - - public DynamicPublishedContent Previous() - { - return DynamicPublishedContentWalker.Previous(this); - } - public DynamicPublishedContent Previous(int number) - { - return DynamicPublishedContentWalker.Previous(this, number); - } - public DynamicPublishedContent Previous(string nodeTypeAlias) - { - return DynamicPublishedContentWalker.Previous(this, nodeTypeAlias); - } - public DynamicPublishedContent Sibling(int number) - { - return DynamicPublishedContentWalker.Sibling(this, number); - } - public DynamicPublishedContent Sibling(string nodeTypeAlias) - { - return DynamicPublishedContentWalker.Sibling(this, nodeTypeAlias); - } - #endregion - - public bool HasProperty(string name) - { - if (_publishedContent != null) - { - try - { - var prop = GetUserProperty(name); - - return (prop != null); - } - catch (Exception) - { - return false; - } - } - return false; - } - - /// - /// Attempts to call a method on the dynamic object - /// - /// - /// - /// - /// - public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) - { - //TODO: We MUST cache the result here, it is very expensive to keep finding extension methods! - - try - { - //Property? - result = typeof(DynamicPublishedContent).InvokeMember(binder.Name, - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public | - System.Reflection.BindingFlags.GetProperty, - null, - this, - args); - return true; - } - catch (MissingMethodException) - { - try - { - //Static or Instance Method? - result = typeof(DynamicPublishedContent).InvokeMember(binder.Name, - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public | - System.Reflection.BindingFlags.Static | - System.Reflection.BindingFlags.InvokeMethod, - null, - this, - args); - return true; - } - catch (MissingMethodException) - { - try - { - result = ExecuteExtensionMethod(args, binder.Name); - return true; - } - catch (TargetInvocationException) - { - result = new DynamicNull(); - return true; - } - - catch - { - //TODO: LOg this! - - result = null; - return false; - } - - } - - - } - catch - { - result = null; - return false; - } - - } - - private object ExecuteExtensionMethod(object[] args, string name) - { - object result = null; - - var methodTypesToFind = new[] - { - typeof(DynamicPublishedContent) - }; - - //find known extension methods that match the first type in the list - MethodInfo toExecute = null; - foreach (var t in methodTypesToFind) - { - toExecute = ExtensionMethodFinder.FindExtensionMethod(t, args, name, false); - if (toExecute != null) - break; - } - - if (toExecute != null) - { - var genericArgs = (new[] { this }).Concat(args); - result = toExecute.Invoke(null, genericArgs.ToArray()); - } - else - { - throw new MissingMethodException(); - } - if (result != null) - { - if (result is IPublishedContent) - { - result = new DynamicPublishedContent((IPublishedContent)result); - } - if (result is IEnumerable) - { - result = new DynamicPublishedContentList((IEnumerable)result); - } - if (result is IEnumerable) - { - result = new DynamicPublishedContentList((IEnumerable)result); - } - } - return result; - } - - /// - /// Attempts to return a custom member (generally based on a string match) - /// - /// - /// - protected virtual Attempt TryGetCustomMember(GetMemberBinder binder) - { - if (binder.Name.InvariantEquals("ChildrenAsList") || binder.Name.InvariantEquals("Children")) - { - return new Attempt(true, Children); - } - - if (binder.Name.InvariantEquals("parentId")) - { - var parent = Parent; - if (parent == null) - { - throw new InvalidOperationException(string.Format("The node {0} does not have a parent", Id)); - } - return new Attempt(true, parent.Id); - } - return Attempt.False; - } - - /// - /// Attempts to return the children by the document type's alias (for example: CurrentPage.NewsItems where NewsItem is the - /// document type alias) - /// - /// - /// - /// - /// This method will work by both the plural and non-plural alias (i.e. NewsItem and NewsItems) - /// - protected virtual Attempt TryGetChildrenByAlias(GetMemberBinder binder) - { - - var filteredTypeChildren = _publishedContent.Children - .Where(x => x.DocumentTypeAlias.InvariantEquals(binder.Name) || x.DocumentTypeAlias.MakePluralName().InvariantEquals(binder.Name)) - .ToArray(); - if (filteredTypeChildren.Any()) - { - return new Attempt(true, - new DynamicPublishedContentList(filteredTypeChildren.Select(x => new DynamicPublishedContent(x)))); - } - return Attempt.False; - } - - /// - /// Attempts to return a member based on the reflected document property - /// - /// - /// - protected virtual Attempt TryGetDocumentProperty(GetMemberBinder binder) - { - - var reflectedProperty = GetReflectedProperty(binder.Name); - var result = reflectedProperty != null - ? reflectedProperty.Value - : null; - - return result == null - ? Attempt.False - : new Attempt(true, result); - } - - /// - /// Attempts to return a member based on a user defined umbraco property - /// - /// - /// - protected virtual Attempt TryGetUserProperty(GetMemberBinder binder) - { - var name = binder.Name; - var recursive = false; - if (name.StartsWith("_")) - { - name = name.Substring(1, name.Length - 1); - recursive = true; - } - - var userProperty = GetUserProperty(name, recursive); - - if (userProperty == null) - { - return Attempt.False; - } - - var result = userProperty.Value; - - if (_publishedContent.DocumentTypeAlias == null && userProperty.Alias == null) - { - throw new InvalidOperationException("No node alias or property alias available. Unable to look up the datatype of the property you are trying to fetch."); - } - - //get the data type id for the current property - var dataType = DynamicPublishedContentDataSourceResolver.Current.DataSource.GetDataType(userProperty.DocumentTypeAlias, userProperty.Alias); - - //convert the string value to a known type - var converted = ConvertPropertyValue(result, dataType, userProperty.DocumentTypeAlias, userProperty.Alias); - if (converted.Success) - { - result = converted.Result; - } - - return new Attempt(true, result); - } - - /// - /// Returns the member match methods in the correct order and is used in the TryGetMember method. - /// - /// - protected virtual IEnumerable>> GetMemberMatchMethods() - { - var memberMatchMethods = new List>> - { - b => TryGetCustomMember(b), //match custom members - b => TryGetUserProperty(b), //then match custom user defined umbraco properties - b => TryGetChildrenByAlias(b), //then try to match children based on doc type alias - b => TryGetDocumentProperty(b) //then try to match on a reflected document property - }; - return memberMatchMethods; - } - - /// - /// Try to return an object based on the dynamic member accessor - /// - /// - /// - /// - /// - /// TODO: SD: This will alwasy return true so that no exceptions are generated, this is only because this is how the - /// old DynamicNode worked, I'm not sure if this is the correct/expected functionality but I've left it like that. - /// IMO I think this is incorrect and it would be better to throw an exception for something that is not supported! - /// - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - if (binder == null) throw new ArgumentNullException("binder"); - - var name = binder.Name; - - //check the cache first! - if (_cachedMemberOutput.TryGetValue(name, out result)) - { - return true; - } - - //loop through each member match method and execute it. - //If it is successful, cache the result and return it. - foreach (var attempt in GetMemberMatchMethods() - .Select(m => m(binder)) - .Where(attempt => attempt.Success)) - { - result = attempt.Result; - //cache the result so we don't have to re-process the whole thing - _cachedMemberOutput.TryAdd(name, result); - return true; - } - - //if property access, type lookup and member invoke all failed - //at this point, we're going to return null - //instead, we return a DynamicNull - see comments in that file - //this will let things like Model.ChildItem work and return nothing instead of crashing - if (result == null) - { - //.Where explictly checks for this type - //and will make it false - //which means backwards equality (&& property != true) will pass - //forwwards equality (&& property or && property == true) will fail - result = new DynamicNull(); - } - - //alwasy return true if we haven't thrown an exception though I'm wondering if we return 'false' if .Net throws an exception for us?? - return true; - } - - /// - /// Returns a property defined on the document object as a member property using reflection - /// - /// - /// - private PropertyResult GetReflectedProperty(string alias) - { - return GetPropertyInternal(alias, _publishedContent, false); - } - - /// - /// Return a user defined property - /// - /// - /// - /// - private PropertyResult GetUserProperty(string alias, bool recursive = false) - { - if (!recursive) - { - return GetPropertyInternal(alias, _publishedContent); - } - var context = this; - var prop = GetPropertyInternal(alias, _publishedContent); - while (prop == null || !prop.HasValue()) - { - context = context.Parent; - if (context == null) break; - prop = context.GetPropertyInternal(alias, context._publishedContent); - } - return prop; - } - - - private PropertyResult GetPropertyInternal(string alias, IPublishedContent content, bool checkUserProperty = true) - { - if (alias.IsNullOrWhiteSpace()) throw new ArgumentNullException("alias"); - if (content == null) throw new ArgumentNullException("content"); - - //if we're looking for a user defined property - if (checkUserProperty) - { - var prop = content.GetProperty(alias); - - return prop == null - ? null - : new PropertyResult(prop, PropertyResultType.UserProperty) - { - DocumentTypeAlias = content.DocumentTypeAlias, - DocumentId = content.Id - }; - } - - //reflect - - Func> getMember = - memberAlias => - { - try - { - return new Attempt(true, - content.GetType().InvokeMember(memberAlias, - System.Reflection.BindingFlags.GetProperty | - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public, - null, - content, - null)); - } - catch (MissingMethodException ex) - { - return new Attempt(ex); - } - }; - - //try with the current casing - var attempt = getMember(alias); - if (!attempt.Success) - { - //if we cannot get with the current alias, try changing it's case - attempt = alias[0].IsUpperCase() - ? getMember(alias.ConvertCase(StringAliasCaseType.CamelCase)) - : getMember(alias.ConvertCase(StringAliasCaseType.PascalCase)); - } - - return !attempt.Success - ? null - : new PropertyResult(alias, attempt.Result, Guid.Empty, PropertyResultType.ReflectedProperty) - { - DocumentTypeAlias = content.DocumentTypeAlias, - DocumentId = content.Id - }; - } - - /// - /// Converts the currentValue to a correctly typed value based on known registered converters, then based on known standards. - /// - /// - /// - /// - /// - /// - private Attempt ConvertPropertyValue(object currentValue, Guid dataType, string docTypeAlias, string propertyTypeAlias) - { - if (currentValue == null) return Attempt.False; - - //First lets check all registered converters for this data type. - var converters = PropertyEditorValueConvertersResolver.Current.Converters - .Where(x => x.IsConverterFor(dataType, docTypeAlias, propertyTypeAlias)) - .ToArray(); - - //try to convert the value with any of the converters: - foreach (var converted in converters - .Select(p => p.ConvertPropertyValue(currentValue)) - .Where(converted => converted.Success)) - { - return new Attempt(true, converted.Result); - } - - //if none of the converters worked, then we'll process this from what we know - - var sResult = Convert.ToString(currentValue).Trim(); - - //this will eat csv strings, so only do it if the decimal also includes a decimal seperator (according to the current culture) - if (sResult.Contains(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator)) - { - decimal dResult; - if (decimal.TryParse(sResult, System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.CurrentCulture, out dResult)) - { - return new Attempt(true, dResult); - } - } - //process string booleans as booleans - if (sResult.InvariantEquals("true")) - { - return new Attempt(true, true); - } - if (sResult.InvariantEquals("false")) - { - return new Attempt(true, false); - } - - //a really rough check to see if this may be valid xml - //TODO: This is legacy code, I'm sure there's a better and nicer way - if (sResult.StartsWith("<") && sResult.EndsWith(">") && sResult.Contains("/")) - { - try - { - var e = XElement.Parse(DynamicXml.StripDashesInElementOrAttributeNames(sResult), LoadOptions.None); - - //check that the document element is not one of the disallowed elements - //allows RTE to still return as html if it's valid xhtml - var documentElement = e.Name.LocalName; - - //TODO: See note against this setting, pretty sure we don't need this - if (!UmbracoSettings.NotDynamicXmlDocumentElements.Any( - tag => string.Equals(tag, documentElement, StringComparison.CurrentCultureIgnoreCase))) - { - return new Attempt(true, new DynamicXml(e)); - } - return Attempt.False; - } - catch (Exception) - { - return Attempt.False; - } - } - return Attempt.False; - } - - //public DynamicNode Media(string propertyAlias) - //{ - // if (_n != null) - // { - // IProperty prop = _n.GetProperty(propertyAlias); - // if (prop != null) - // { - // int mediaNodeId; - // if (int.TryParse(prop.Value, out mediaNodeId)) - // { - // return _razorLibrary.Value.MediaById(mediaNodeId); - // } - // } - // return null; - // } - // return null; - //} - //public bool IsProtected - //{ - // get - // { - // if (_n != null) - // { - // return umbraco.library.IsProtected(_n.Id, _n.Path); - // } - // return false; - // } - //} - //public bool HasAccess - //{ - // get - // { - // if (_n != null) - // { - // return umbraco.library.HasAccess(_n.Id, _n.Path); - // } - // return true; - // } - //} - - //public string Media(string propertyAlias, string mediaPropertyAlias) - //{ - // if (_n != null) - // { - // IProperty prop = _n.GetProperty(propertyAlias); - // if (prop == null && propertyAlias.Substring(0, 1).ToUpper() == propertyAlias.Substring(0, 1)) - // { - // prop = _n.GetProperty(propertyAlias.Substring(0, 1).ToLower() + propertyAlias.Substring((1))); - // } - // if (prop != null) - // { - // int mediaNodeId; - // if (int.TryParse(prop.Value, out mediaNodeId)) - // { - // umbraco.cms.businesslogic.media.Media media = new cms.businesslogic.media.Media(mediaNodeId); - // if (media != null) - // { - // Property mprop = media.getProperty(mediaPropertyAlias); - // // check for nicer support of Pascal Casing EVEN if alias is camelCasing: - // if (prop == null && mediaPropertyAlias.Substring(0, 1).ToUpper() == mediaPropertyAlias.Substring(0, 1)) - // { - // mprop = media.getProperty(mediaPropertyAlias.Substring(0, 1).ToLower() + mediaPropertyAlias.Substring((1))); - // } - // if (mprop != null) - // { - // return string.Format("{0}", mprop.Value); - // } - // } - // } - // } - // } - // return null; - //} - - #region Ancestors, Descendants and Parent - public DynamicPublishedContent AncestorOrSelf() - { - //TODO: Why is this query like this?? - return AncestorOrSelf(node => node.Level == 1); - } - public DynamicPublishedContent AncestorOrSelf(int level) - { - return AncestorOrSelf(node => node.Level == level); - } - public DynamicPublishedContent AncestorOrSelf(string nodeTypeAlias) - { - return AncestorOrSelf(node => node.DocumentTypeAlias == nodeTypeAlias); - } - public DynamicPublishedContent AncestorOrSelf(Func func) - { - var node = this; - while (node != null) - { - if (func(node)) return node; - var parent = node.Parent; - if (parent != null) - { - if (this != parent) - { - node = parent; - } - else - { - return node; - } - } - else - { - return null; - } - } - return node; - } - public DynamicPublishedContentList AncestorsOrSelf(Func func) - { - var ancestorList = new List(); - var node = this; - ancestorList.Add(node); - while (node != null) - { - if (node.Level == 1) break; - var parent = node.Parent; - if (parent != null) - { - if (this != parent) - { - node = parent; - if (func(node)) - { - ancestorList.Add(node); - } - } - else - { - break; - } - } - else - { - break; - } - } - ancestorList.Reverse(); - return new DynamicPublishedContentList(ancestorList); - } - public DynamicPublishedContentList AncestorsOrSelf() - { - return AncestorsOrSelf(n => true); - } - public DynamicPublishedContentList AncestorsOrSelf(string nodeTypeAlias) - { - return AncestorsOrSelf(n => n.DocumentTypeAlias == nodeTypeAlias); - } - public DynamicPublishedContentList AncestorsOrSelf(int level) - { - return AncestorsOrSelf(n => n.Level <= level); - } - public DynamicPublishedContentList Descendants(string nodeTypeAlias) - { - return Descendants(p => p.DocumentTypeAlias == nodeTypeAlias); - } - public DynamicPublishedContentList Descendants(int level) - { - return Descendants(p => p.Level >= level); - } - public DynamicPublishedContentList Descendants() - { - return Descendants(n => true); - } - internal DynamicPublishedContentList Descendants(Func func) - { - var flattenedNodes = this._publishedContent.Children.Map(func, (IPublishedContent n) => n.Children); - return new DynamicPublishedContentList(flattenedNodes.ToList().ConvertAll(dynamicBackingItem => new DynamicPublishedContent(dynamicBackingItem))); - } - public DynamicPublishedContentList DescendantsOrSelf(int level) - { - return DescendantsOrSelf(p => p.Level >= level); - } - public DynamicPublishedContentList DescendantsOrSelf(string nodeTypeAlias) - { - return DescendantsOrSelf(p => p.DocumentTypeAlias == nodeTypeAlias); - } - public DynamicPublishedContentList DescendantsOrSelf() - { - return DescendantsOrSelf(p => true); - } - internal DynamicPublishedContentList DescendantsOrSelf(Func func) - { - if (this._publishedContent != null) - { - var thisNode = new List(); - if (func(this._publishedContent)) - { - thisNode.Add(this._publishedContent); - } - var flattenedNodes = this._publishedContent.Children.Map(func, (IPublishedContent n) => n.Children); - return new DynamicPublishedContentList(thisNode.Concat(flattenedNodes).ToList().ConvertAll(dynamicBackingItem => new DynamicPublishedContent(dynamicBackingItem))); - } - return new DynamicPublishedContentList(Enumerable.Empty()); - } - public DynamicPublishedContentList Ancestors(int level) - { - return Ancestors(n => n.Level <= level); - } - public DynamicPublishedContentList Ancestors(string nodeTypeAlias) - { - return Ancestors(n => n.DocumentTypeAlias == nodeTypeAlias); - } - public DynamicPublishedContentList Ancestors() - { - return Ancestors(n => true); - } - public DynamicPublishedContentList Ancestors(Func func) - { - var ancestorList = new List(); - var node = this; - while (node != null) - { - if (node.Level == 1) break; - var parent = node.Parent; - if (parent != null) - { - if (this != parent) - { - node = parent; - if (func(node)) - { - ancestorList.Add(node); - } - } - else - { - break; - } - } - else - { - break; - } - } - ancestorList.Reverse(); - return new DynamicPublishedContentList(ancestorList); - } - public DynamicPublishedContent Parent - { - get - { - if (_publishedContent.Parent != null) - { - return new DynamicPublishedContent(_publishedContent.Parent); - } - if (_publishedContent != null && _publishedContent.Id == 0) - { - return this; - } - return null; - } - } - #endregion - - public int TemplateId - { - get { return _publishedContent.TemplateId; } - } - - public int SortOrder - { - get { return _publishedContent.SortOrder; } - } - - public string Name - { - get { return _publishedContent.Name; } - } - - public bool Visible - { - get - { - - var umbracoNaviHide = GetUserProperty("umbracoNaviHide"); - if (umbracoNaviHide != null) - { - return umbracoNaviHide.Value.ToString().Trim() != "1"; - } - return true; - } - } - - public string UrlName - { - get { return _publishedContent.UrlName; } - } - - public string DocumentTypeAlias - { - get { return _publishedContent.DocumentTypeAlias; } - } - - public string WriterName - { - get { return _publishedContent.WriterName; } - } - - public string CreatorName - { - get { return _publishedContent.CreatorName; } - } - - public int WriterId - { - get { return _publishedContent.WriterId; } - } - - public int CreatorId - { - get { return _publishedContent.CreatorId; } - } - - public string Path - { - get { return _publishedContent.Path; } - } - - public DateTime CreateDate - { - get { return _publishedContent.CreateDate; } - } - - public int Id - { - get { return _publishedContent.Id; } - } - - public DateTime UpdateDate - { - get { return _publishedContent.UpdateDate; } - } - - public Guid Version - { - get { return _publishedContent.Version; } - } - - public int Level - { - get { return _publishedContent.Level; } - } - - public IEnumerable Properties - { - get { return _publishedContent.Properties; } - } - - public IEnumerable Children - { - get - { - if (_cachedChildren == null) - { - var children = _publishedContent.Children; - //testing, think this must be a special case for the root node ? - if (!children.Any() && _publishedContent.Id == 0) - { - _cachedChildren = new DynamicPublishedContentList(new List { new DynamicPublishedContent(this._publishedContent) }); - } - else - { - _cachedChildren = new DynamicPublishedContentList(_publishedContent.Children.Select(x => new DynamicPublishedContent(x))); - } - } - return _cachedChildren; - } - } - - #region GetProperty methods which can be used with the dynamic object - - public IDocumentProperty GetProperty(string alias) - { - return GetProperty(alias, false); - } - public IDocumentProperty GetProperty(string alias, bool recursive) - { - return alias.StartsWith("@") - ? GetReflectedProperty(alias.TrimStart('@')) - : GetUserProperty(alias, recursive); - } - public string GetPropertyValue(string alias) - { - return GetPropertyValue(alias, false); - } - public string GetPropertyValue(string alias, string fallback) - { - var prop = GetPropertyValue(alias); - return !prop.IsNullOrWhiteSpace() ? prop : fallback; - } - public string GetPropertyValue(string alias, bool recursive) - { - var p = alias.StartsWith("@") - ? GetReflectedProperty(alias.TrimStart('@')) - : GetUserProperty(alias, recursive); - return p == null ? null : p.ValueAsString; - } - public string GetPropertyValue(string alias, bool recursive, string fallback) - { - var prop = GetPropertyValue(alias, recursive); - return !prop.IsNullOrWhiteSpace() ? prop : fallback; - } - - #endregion - - #region HasValue - public bool HasValue(string alias) - { - return HasValue(alias, false); - } - public bool HasValue(string alias, bool recursive) - { - var prop = GetUserProperty(alias, recursive); - if (prop == null) return false; - return prop.HasValue(); - } - public IHtmlString HasValue(string alias, string valueIfTrue, string valueIfFalse) - { - return HasValue(alias, false) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); - } - public IHtmlString HasValue(string alias, bool recursive, string valueIfTrue, string valueIfFalse) - { - return HasValue(alias, recursive) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); - } - public IHtmlString HasValue(string alias, string valueIfTrue) - { - return HasValue(alias, false) ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); - } - public IHtmlString HasValue(string alias, bool recursive, string valueIfTrue) - { - return HasValue(alias, recursive) ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); - } - #endregion - - public int Position() - { - return this.Index(); - } - public int Index() - { - if (this.OwnerList == null && this.Parent != null) - { - //var list = this.Parent.Children.Select(n => new DynamicNode(n)); - var list = this.Parent.Children; - this.OwnerList = new DynamicPublishedContentList(list); - } - if (this.OwnerList != null) - { - List container = this.OwnerList.Items.ToList(); - int currentIndex = container.FindIndex(n => n.Id == this.Id); - if (currentIndex != -1) - { - return currentIndex; - } - else - { - throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicDocumentList but could not retrieve the index for it's position in the list", this.Id)); - } - } - else - { - throw new ArgumentNullException(string.Format("Node {0} has been orphaned and doesn't belong to a DynamicDocumentList", this.Id)); - } - } - - #region Is Helpers - public bool IsNull(string alias, bool recursive) - { - var prop = GetUserProperty(alias, recursive); - if (prop == null) return true; - return ((PropertyResult)prop).HasValue(); - } - public bool IsNull(string alias) - { - return IsNull(alias, false); - } - public bool IsFirst() - { - return IsHelper(n => n.Index() == 0); - } - public HtmlString IsFirst(string valueIfTrue) - { - return IsHelper(n => n.Index() == 0, valueIfTrue); - } - public HtmlString IsFirst(string valueIfTrue, string valueIfFalse) - { - return IsHelper(n => n.Index() == 0, valueIfTrue, valueIfFalse); - } - public bool IsNotFirst() - { - return !IsHelper(n => n.Index() == 0); - } - public HtmlString IsNotFirst(string valueIfTrue) - { - return IsHelper(n => n.Index() != 0, valueIfTrue); - } - public HtmlString IsNotFirst(string valueIfTrue, string valueIfFalse) - { - return IsHelper(n => n.Index() != 0, valueIfTrue, valueIfFalse); - } - public bool IsPosition(int index) - { - if (this.OwnerList == null) - { - return false; - } - return IsHelper(n => n.Index() == index); - } - public HtmlString IsPosition(int index, string valueIfTrue) - { - if (this.OwnerList == null) - { - return new HtmlString(string.Empty); - } - return IsHelper(n => n.Index() == index, valueIfTrue); - } - public HtmlString IsPosition(int index, string valueIfTrue, string valueIfFalse) - { - if (this.OwnerList == null) - { - return new HtmlString(valueIfFalse); - } - return IsHelper(n => n.Index() == index, valueIfTrue, valueIfFalse); - } - public bool IsModZero(int modulus) - { - if (this.OwnerList == null) - { - return false; - } - return IsHelper(n => n.Index() % modulus == 0); - } - public HtmlString IsModZero(int modulus, string valueIfTrue) - { - if (this.OwnerList == null) - { - return new HtmlString(string.Empty); - } - return IsHelper(n => n.Index() % modulus == 0, valueIfTrue); - } - public HtmlString IsModZero(int modulus, string valueIfTrue, string valueIfFalse) - { - if (this.OwnerList == null) - { - return new HtmlString(valueIfFalse); - } - return IsHelper(n => n.Index() % modulus == 0, valueIfTrue, valueIfFalse); - } - - public bool IsNotModZero(int modulus) - { - if (this.OwnerList == null) - { - return false; - } - return IsHelper(n => n.Index() % modulus != 0); - } - public HtmlString IsNotModZero(int modulus, string valueIfTrue) - { - if (this.OwnerList == null) - { - return new HtmlString(string.Empty); - } - return IsHelper(n => n.Index() % modulus != 0, valueIfTrue); - } - public HtmlString IsNotModZero(int modulus, string valueIfTrue, string valueIfFalse) - { - if (this.OwnerList == null) - { - return new HtmlString(valueIfFalse); - } - return IsHelper(n => n.Index() % modulus != 0, valueIfTrue, valueIfFalse); - } - public bool IsNotPosition(int index) - { - if (this.OwnerList == null) - { - return false; - } - return !IsHelper(n => n.Index() == index); - } - public HtmlString IsNotPosition(int index, string valueIfTrue) - { - if (this.OwnerList == null) - { - return new HtmlString(string.Empty); - } - return IsHelper(n => n.Index() != index, valueIfTrue); - } - public HtmlString IsNotPosition(int index, string valueIfTrue, string valueIfFalse) - { - if (this.OwnerList == null) - { - return new HtmlString(valueIfFalse); - } - return IsHelper(n => n.Index() != index, valueIfTrue, valueIfFalse); - } - public bool IsLast() - { - if (this.OwnerList == null) - { - return false; - } - int count = this.OwnerList.Count(); - return IsHelper(n => n.Index() == count - 1); - } - public HtmlString IsLast(string valueIfTrue) - { - if (this.OwnerList == null) - { - return new HtmlString(string.Empty); - } - int count = this.OwnerList.Count(); - return IsHelper(n => n.Index() == count - 1, valueIfTrue); - } - public HtmlString IsLast(string valueIfTrue, string valueIfFalse) - { - if (this.OwnerList == null) - { - return new HtmlString(valueIfFalse); - } - int count = this.OwnerList.Count(); - return IsHelper(n => n.Index() == count - 1, valueIfTrue, valueIfFalse); - } - public bool IsNotLast() - { - if (this.OwnerList == null) - { - return false; - } - int count = this.OwnerList.Count(); - return !IsHelper(n => n.Index() == count - 1); - } - public HtmlString IsNotLast(string valueIfTrue) - { - if (this.OwnerList == null) - { - return new HtmlString(string.Empty); - } - int count = this.OwnerList.Count(); - return IsHelper(n => n.Index() != count - 1, valueIfTrue); - } - public HtmlString IsNotLast(string valueIfTrue, string valueIfFalse) - { - if (this.OwnerList == null) - { - return new HtmlString(valueIfFalse); - } - int count = this.OwnerList.Count(); - return IsHelper(n => n.Index() != count - 1, valueIfTrue, valueIfFalse); - } - public bool IsEven() - { - return IsHelper(n => n.Index() % 2 == 0); - } - public HtmlString IsEven(string valueIfTrue) - { - return IsHelper(n => n.Index() % 2 == 0, valueIfTrue); - } - public HtmlString IsEven(string valueIfTrue, string valueIfFalse) - { - return IsHelper(n => n.Index() % 2 == 0, valueIfTrue, valueIfFalse); - } - public bool IsOdd() - { - return IsHelper(n => n.Index() % 2 == 1); - } - public HtmlString IsOdd(string valueIfTrue) - { - return IsHelper(n => n.Index() % 2 == 1, valueIfTrue); - } - public HtmlString IsOdd(string valueIfTrue, string valueIfFalse) - { - return IsHelper(n => n.Index() % 2 == 1, valueIfTrue, valueIfFalse); - } - public bool IsEqual(DynamicPublishedContent other) - { - return IsHelper(n => n.Id == other.Id); - } - public HtmlString IsEqual(DynamicPublishedContent other, string valueIfTrue) - { - return IsHelper(n => n.Id == other.Id, valueIfTrue); - } - public HtmlString IsEqual(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) - { - return IsHelper(n => n.Id == other.Id, valueIfTrue, valueIfFalse); - } - public bool IsNotEqual(DynamicPublishedContent other) - { - return IsHelper(n => n.Id != other.Id); - } - public HtmlString IsNotEqual(DynamicPublishedContent other, string valueIfTrue) - { - return IsHelper(n => n.Id != other.Id, valueIfTrue); - } - public HtmlString IsNotEqual(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) - { - return IsHelper(n => n.Id != other.Id, valueIfTrue, valueIfFalse); - } - public bool IsDescendant(DynamicPublishedContent other) - { - var ancestors = this.Ancestors(); - return IsHelper(n => ancestors.Items.Find(ancestor => ancestor.Id == other.Id) != null); - } - public HtmlString IsDescendant(DynamicPublishedContent other, string valueIfTrue) - { - var ancestors = this.Ancestors(); - return IsHelper(n => ancestors.Items.Find(ancestor => ancestor.Id == other.Id) != null, valueIfTrue); - } - public HtmlString IsDescendant(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) - { - var ancestors = this.Ancestors(); - return IsHelper(n => ancestors.Items.Find(ancestor => ancestor.Id == other.Id) != null, valueIfTrue, valueIfFalse); - } - public bool IsDescendantOrSelf(DynamicPublishedContent other) - { - var ancestors = this.AncestorsOrSelf(); - return IsHelper(n => ancestors.Items.Find(ancestor => ancestor.Id == other.Id) != null); - } - public HtmlString IsDescendantOrSelf(DynamicPublishedContent other, string valueIfTrue) - { - var ancestors = this.AncestorsOrSelf(); - return IsHelper(n => ancestors.Items.Find(ancestor => ancestor.Id == other.Id) != null, valueIfTrue); - } - public HtmlString IsDescendantOrSelf(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) - { - var ancestors = this.AncestorsOrSelf(); - return IsHelper(n => ancestors.Items.Find(ancestor => ancestor.Id == other.Id) != null, valueIfTrue, valueIfFalse); - } - public bool IsAncestor(DynamicPublishedContent other) - { - var descendants = this.Descendants(); - return IsHelper(n => descendants.Items.Find(descendant => descendant.Id == other.Id) != null); - } - public HtmlString IsAncestor(DynamicPublishedContent other, string valueIfTrue) - { - var descendants = this.Descendants(); - return IsHelper(n => descendants.Items.Find(descendant => descendant.Id == other.Id) != null, valueIfTrue); - } - public HtmlString IsAncestor(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) - { - var descendants = this.Descendants(); - return IsHelper(n => descendants.Items.Find(descendant => descendant.Id == other.Id) != null, valueIfTrue, valueIfFalse); - } - public bool IsAncestorOrSelf(DynamicPublishedContent other) - { - var descendants = this.DescendantsOrSelf(); - return IsHelper(n => descendants.Items.Find(descendant => descendant.Id == other.Id) != null); - } - public HtmlString IsAncestorOrSelf(DynamicPublishedContent other, string valueIfTrue) - { - var descendants = this.DescendantsOrSelf(); - return IsHelper(n => descendants.Items.Find(descendant => descendant.Id == other.Id) != null, valueIfTrue); - } - public HtmlString IsAncestorOrSelf(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) - { - var descendants = this.DescendantsOrSelf(); - return IsHelper(n => descendants.Items.Find(descendant => descendant.Id == other.Id) != null, valueIfTrue, valueIfFalse); - } - private bool IsHelper(Func test) - { - return test(this); - } - private HtmlString IsHelper(Func test, string valueIfTrue) - { - return IsHelper(test, valueIfTrue, string.Empty); - } - private HtmlString IsHelper(Func test, string valueIfTrue, string valueIfFalse) - { - return test(this) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); - } - #endregion - - #region Where - - public HtmlString Where(string predicate, string valueIfTrue) - { - return Where(predicate, valueIfTrue, string.Empty); - } - public HtmlString Where(string predicate, string valueIfTrue, string valueIfFalse) - { - if (Where(predicate)) - { - return new HtmlString(valueIfTrue); - } - return new HtmlString(valueIfFalse); - } - public bool Where(string predicate) - { - //Totally gonna cheat here - var dynamicDocumentList = new DynamicPublishedContentList(); - dynamicDocumentList.Add(this); - var filtered = dynamicDocumentList.Where(predicate); - if (Queryable.Count(filtered) == 1) - { - //this node matches the predicate - return true; - } - return false; - } - - #endregion - - //TODO: need a method to return a string value for a user property regardless of xml content or data type thus bypassing all of the PropertyEditorValueConverters - ///// - ///// Returns the value as as string regardless of xml content or data type - ///// - ///// - //public override string ToString() - //{ - // return base.ToString(); - //} - - #region Explicit IPublishedContent implementation - IPublishedContent IPublishedContent.Parent - { - get { return _publishedContent.Parent; } - } - - int IPublishedContent.Id - { - get { return _publishedContent.Id; } - } - - int IPublishedContent.TemplateId - { - get { return _publishedContent.TemplateId; } - } - - int IPublishedContent.SortOrder - { - get { return _publishedContent.SortOrder; } - } - - string IPublishedContent.Name - { - get { return _publishedContent.Name; } - } - - string IPublishedContent.UrlName - { - get { return _publishedContent.UrlName; } - } - - string IPublishedContent.DocumentTypeAlias - { - get { return _publishedContent.DocumentTypeAlias; } - } - - int IPublishedContent.DocumentTypeId - { - get { return _publishedContent.DocumentTypeId; } - } - - string IPublishedContent.WriterName - { - get { return _publishedContent.WriterName; } - } - - string IPublishedContent.CreatorName - { - get { return _publishedContent.CreatorName; } - } - - int IPublishedContent.WriterId - { - get { return _publishedContent.WriterId; } - } - - int IPublishedContent.CreatorId - { - get { return _publishedContent.CreatorId; } - } - - string IPublishedContent.Path - { - get { return _publishedContent.Path; } - } - - DateTime IPublishedContent.CreateDate - { - get { return _publishedContent.CreateDate; } - } - - DateTime IPublishedContent.UpdateDate - { - get { return _publishedContent.UpdateDate; } - } - - Guid IPublishedContent.Version - { - get { return _publishedContent.Version; } - } - - int IPublishedContent.Level - { - get { return _publishedContent.Level; } - } - - System.Collections.ObjectModel.Collection IPublishedContent.Properties - { - get { return _publishedContent.Properties; } - } - - IEnumerable IPublishedContent.Children - { - get { return _publishedContent.Children; } - } - - IDocumentProperty IPublishedContent.GetProperty(string alias) - { - return _publishedContent.GetProperty(alias); - } - #endregion - } -} diff --git a/src/Umbraco.Core/Dynamics/DynamicPublishedContentDataSourceResolver.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContentDataSourceResolver.cs deleted file mode 100644 index 79aa4a5ed8..0000000000 --- a/src/Umbraco.Core/Dynamics/DynamicPublishedContentDataSourceResolver.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Umbraco.Core.ObjectResolution; - -namespace Umbraco.Core.Dynamics -{ - /// - /// This exists only because we want Dynamics in the Core project but DynamicNode has references to ContentType to run some queries - /// and currently the business logic part of Umbraco is still in the legacy project and we don't want to move that to the core so in the - /// meantime until the new APIs are made, we need to have this data source in place with a resolver which is set in the web project. - /// - internal class DynamicPublishedContentDataSourceResolver : SingleObjectResolverBase - { - public IDynamicPublishedContentDataSource DataSource { get; private set; } - - public DynamicPublishedContentDataSourceResolver(IDynamicPublishedContentDataSource dataSource) - { - DataSource = dataSource; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Dynamics/DynamicPublishedContentIdEqualityComparer.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContentIdEqualityComparer.cs index 54cea8d0cf..663684b9e4 100644 --- a/src/Umbraco.Core/Dynamics/DynamicPublishedContentIdEqualityComparer.cs +++ b/src/Umbraco.Core/Dynamics/DynamicPublishedContentIdEqualityComparer.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; +using Umbraco.Core.Models; namespace Umbraco.Core.Dynamics { - internal class DynamicPublishedContentIdEqualityComparer : EqualityComparer + internal class DynamicPublishedContentIdEqualityComparer : EqualityComparer { - public override bool Equals(DynamicPublishedContent x, DynamicPublishedContent y) + public override bool Equals(DynamicPublishedContentBase x, DynamicPublishedContentBase y) { //Check whether the compared objects reference the same data. if (Object.ReferenceEquals(x, y)) return true; @@ -20,7 +21,7 @@ namespace Umbraco.Core.Dynamics } - public override int GetHashCode(DynamicPublishedContent obj) + public override int GetHashCode(DynamicPublishedContentBase obj) { if (Object.ReferenceEquals(obj, null)) return 0; diff --git a/src/Umbraco.Core/Dynamics/DynamicPublishedContentListOrdering.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContentListOrdering.cs index c56a3b271b..872c393f5b 100644 --- a/src/Umbraco.Core/Dynamics/DynamicPublishedContentListOrdering.cs +++ b/src/Umbraco.Core/Dynamics/DynamicPublishedContentListOrdering.cs @@ -2,18 +2,19 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using Umbraco.Core.Models; namespace Umbraco.Core.Dynamics { internal static class DynamicPublishedContentListOrdering { - private static TOut Reduce(Func func, DynamicPublishedContent publishedContent) + private static TOut Reduce(Func func, DynamicPublishedContentBase publishedContent) { var value = func(publishedContent); - while (value is Func) + while (value is Func) { - value = (value as Func)(publishedContent); + value = (value as Func)(publishedContent); } //when you're sorting a list of properties //and one of those properties doesn't exist, it will come back as DynamicNull @@ -37,209 +38,209 @@ namespace Umbraco.Core.Dynamics } return (TOut)value; } - public static IOrderedQueryable OrderBy(object source, object key) + internal static IOrderedQueryable OrderBy(object source, object key) { - IEnumerable typedSource = source as IEnumerable; + IEnumerable typedSource = source as IEnumerable; LambdaExpression lambda = key as LambdaExpression; //if the lambda we have returns an actual property, not a dynamic one, //then the TOut of the func will be the actual type, not object //Func func = (Func)lambda.Compile(); var func = lambda.Compile(); var TOut = func.GetType().GetGenericArguments()[1]; - IOrderedQueryable result = null; - if (TOut == typeof(Func)) + IOrderedQueryable result = null; + if (TOut == typeof(Func)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(object)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(bool)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(decimal)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(int)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(string)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(DateTime)) { - result = (IOrderedQueryable)typedSource - .OrderBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderBy(x => Reduce(func as Func, x)) .AsQueryable(); } return result; } - public static IOrderedQueryable ThenBy(object source, object key) + internal static IOrderedQueryable ThenBy(object source, object key) { - IOrderedQueryable typedSource = source as IOrderedQueryable; + IOrderedQueryable typedSource = source as IOrderedQueryable; LambdaExpression lambda = key as LambdaExpression; var func = lambda.Compile(); var TOut = func.GetType().GetGenericArguments()[1]; - IOrderedQueryable result = null; - if (TOut == typeof(Func)) + IOrderedQueryable result = null; + if (TOut == typeof(Func)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(object)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(bool)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(decimal)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(int)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(string)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(DateTime)) { - result = (IOrderedQueryable)typedSource - .ThenBy(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenBy(x => Reduce(func as Func, x)) .AsQueryable(); } return result; } - public static IOrderedQueryable OrderByDescending(object source, object key) + internal static IOrderedQueryable OrderByDescending(object source, object key) { - IEnumerable typedSource = source as IEnumerable; + IEnumerable typedSource = source as IEnumerable; LambdaExpression lambda = key as LambdaExpression; var func = lambda.Compile(); var TOut = func.GetType().GetGenericArguments()[1]; - IOrderedQueryable result = null; - if (TOut == typeof(Func)) + IOrderedQueryable result = null; + if (TOut == typeof(Func)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(object)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(bool)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(decimal)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(int)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(string)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(DateTime)) { - result = (IOrderedQueryable)typedSource - .OrderByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .OrderByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } return result; } - public static IOrderedQueryable ThenByDescending(object source, object key) + internal static IOrderedQueryable ThenByDescending(object source, object key) { - IOrderedQueryable typedSource = source as IOrderedQueryable; + IOrderedQueryable typedSource = source as IOrderedQueryable; LambdaExpression lambda = key as LambdaExpression; var func = lambda.Compile(); var TOut = func.GetType().GetGenericArguments()[1]; - IOrderedQueryable result = null; - if (TOut == typeof(Func)) + IOrderedQueryable result = null; + if (TOut == typeof(Func)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(object)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(bool)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(decimal)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(int)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(string)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } if (TOut == typeof(DateTime)) { - result = (IOrderedQueryable)typedSource - .ThenByDescending(x => Reduce(func as Func, x)) + result = (IOrderedQueryable)typedSource + .ThenByDescending(x => Reduce(func as Func, x)) .AsQueryable(); } return result; diff --git a/src/Umbraco.Core/Dynamics/DynamicPublishedContentWalker.cs b/src/Umbraco.Core/Dynamics/DynamicPublishedContentWalker.cs deleted file mode 100644 index 26c3a8dee8..0000000000 --- a/src/Umbraco.Core/Dynamics/DynamicPublishedContentWalker.cs +++ /dev/null @@ -1,263 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Umbraco.Core.Dynamics -{ - internal static class DynamicPublishedContentWalker - { - public static DynamicPublishedContent Up(this DynamicPublishedContent context) - { - return context.Up(0); - } - public static DynamicPublishedContent Up(this DynamicPublishedContent context, int number) - { - if (number == 0) - { - return context.Parent; - } - else - { - while ((context = context.Parent) != null && --number >= 0) ; - return context; - } - } - public static DynamicPublishedContent Up(this DynamicPublishedContent context, string nodeTypeAlias) - { - if (string.IsNullOrEmpty(nodeTypeAlias)) - { - return context.Parent; - } - else - { - while ((context = context.Parent) != null && context.DocumentTypeAlias != nodeTypeAlias) ; - return context; - } - } - - public static DynamicPublishedContent Down(this DynamicPublishedContent context) - { - return context.Down(0); - } - public static DynamicPublishedContent Down(this DynamicPublishedContent context, int number) - { - var children = new DynamicPublishedContentList(context.Children); - if (number == 0) - { - return children.Items.First(); - } - else - { - DynamicPublishedContent working = context; - while (number-- >= 0) - { - working = children.Items.First(); - children = new DynamicPublishedContentList(working.Children); - } - return working; - } - } - public static DynamicPublishedContent Down(this DynamicPublishedContent context, string nodeTypeAlias) - { - - if (string.IsNullOrEmpty(nodeTypeAlias)) - { - var children = new DynamicPublishedContentList(context.Children); - return children.Items.First(); - } - else - { - return context.Descendants(nodeTypeAlias).Items.FirstOrDefault(); - } - } - public static DynamicPublishedContent Next(this DynamicPublishedContent context) - { - return context.Next(0); - } - public static DynamicPublishedContent Next(this DynamicPublishedContent context, int number) - { - if (context.OwnerList == null && context.Parent != null) - { - //var list = context.Parent.Children.Select(n => new DynamicNode(n)); - var list = context.Parent.Children; - context.OwnerList = new DynamicPublishedContentList(list); - } - if (context.OwnerList != null) - { - var container = context.OwnerList.Items.ToList(); - var currentIndex = container.FindIndex(n => n.Id == context.Id); - if (currentIndex != -1) - { - return container.ElementAtOrDefault(currentIndex + (number + 1)); - } - else - { - throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", context.Id)); - } - } - else - { - throw new ArgumentNullException(string.Format("Node {0} has been orphaned and doesn't belong to a DynamicNodeList", context.Id)); - } - } - public static DynamicPublishedContent Sibling(this DynamicPublishedContent context, int number) - { - if (context.OwnerList == null && context.Parent != null) - { - //var list = context.Parent.Children.Select(n => new DynamicNode(n)); - var list = context.Parent.Children; - context.OwnerList = new DynamicPublishedContentList(list); - } - if (context.OwnerList != null) - { - var container = context.OwnerList.Items.ToList(); - var currentIndex = container.FindIndex(n => n.Id == context.Id); - if (currentIndex != -1) - { - return container.ElementAtOrDefault(currentIndex + number); - } - else - { - throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", context.Id)); - } - } - else - { - throw new ArgumentNullException(string.Format("Node {0} has been orphaned and doesn't belong to a DynamicNodeList", context.Id)); - } - } - public static DynamicPublishedContent Sibling(this DynamicPublishedContent context, string nodeTypeAlias) - { - if (context.OwnerList == null && context.Parent != null) - { - //var list = context.Parent.Children.Select(n => new DynamicNode(n)); - var list = context.Parent.Children; - context.OwnerList = new DynamicPublishedContentList(list); - } - if (context.OwnerList != null) - { - var container = context.OwnerList.Items.ToList(); - var currentIndex = container.FindIndex(n => n.Id == context.Id); - if (currentIndex != -1) - { - var workingIndex = currentIndex + 1; - while (workingIndex != currentIndex) - { - var working = container.ElementAtOrDefault(workingIndex); - if (working != null && working.DocumentTypeAlias == nodeTypeAlias) - { - return working; - } - workingIndex++; - if (workingIndex > container.Count) - { - workingIndex = 0; - } - } - return null; - } - else - { - throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", context.Id)); - } - } - else - { - throw new ArgumentNullException(string.Format("Node {0} has been orphaned and doesn't belong to a DynamicNodeList", context.Id)); - } - } - public static DynamicPublishedContent Next(this DynamicPublishedContent context, string nodeTypeAlias) - { - if (context.OwnerList == null && context.Parent != null) - { - //var list = context.Parent.Children.Select(n => new DynamicNode(n)); - var list = context.Parent.Children; - context.OwnerList = new DynamicPublishedContentList(list); - } - if (context.OwnerList != null) - { - var container = context.OwnerList.Items.ToList(); - var currentIndex = container.FindIndex(n => n.Id == context.Id); - if (currentIndex != -1) - { - var newIndex = container.FindIndex(currentIndex, n => n.DocumentTypeAlias == nodeTypeAlias); - if (newIndex != -1) - { - return container.ElementAt(newIndex); - } - return null; - } - else - { - throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", context.Id)); - } - } - else - { - throw new ArgumentNullException(string.Format("Node {0} has been orphaned and doesn't belong to a DynamicNodeList", context.Id)); - } - } - public static DynamicPublishedContent Previous(this DynamicPublishedContent context) - { - return context.Previous(0); - } - public static DynamicPublishedContent Previous(this DynamicPublishedContent context, int number) - { - if (context.OwnerList == null && context.Parent != null) - { - //var list = context.Parent.Children.Select(n => new DynamicNode(n)); - var list = context.Parent.Children; - context.OwnerList = new DynamicPublishedContentList(list); - } - if (context.OwnerList != null) - { - List container = context.OwnerList.Items.ToList(); - int currentIndex = container.FindIndex(n => n.Id == context.Id); - if (currentIndex != -1) - { - return container.ElementAtOrDefault(currentIndex + (number - 1)); - } - else - { - throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", context.Id)); - } - } - else - { - throw new ArgumentNullException(string.Format("Node {0} has been orphaned and doesn't belong to a DynamicNodeList", context.Id)); - } - } - public static DynamicPublishedContent Previous(this DynamicPublishedContent context, string nodeTypeAlias) - { - if (context.OwnerList == null && context.Parent != null) - { - //var list = context.Parent.Children.Select(n => new DynamicNode(n)); - var list = context.Parent.Children; - context.OwnerList = new DynamicPublishedContentList(list); - } - if (context.OwnerList != null) - { - List container = context.OwnerList.Items.ToList(); - int currentIndex = container.FindIndex(n => n.Id == context.Id); - if (currentIndex != -1) - { - var previousNodes = container.Take(currentIndex).ToList(); - int newIndex = previousNodes.FindIndex(n => n.DocumentTypeAlias == nodeTypeAlias); - if (newIndex != -1) - { - return container.ElementAt(newIndex); - } - return null; - } - else - { - throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", context.Id)); - } - } - else - { - throw new ArgumentNullException(string.Format("Node {0} has been orphaned and doesn't belong to a DynamicNodeList", context.Id)); - } - } - } -} diff --git a/src/Umbraco.Core/Dynamics/DynamicQueryable.cs b/src/Umbraco.Core/Dynamics/DynamicQueryable.cs index 0f31abacff..1552ce98f5 100644 --- a/src/Umbraco.Core/Dynamics/DynamicQueryable.cs +++ b/src/Umbraco.Core/Dynamics/DynamicQueryable.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Diagnostics; +using Umbraco.Core.Models; namespace Umbraco.Core.Dynamics { @@ -21,22 +22,22 @@ namespace Umbraco.Core.Dynamics if (source == null) throw new ArgumentNullException("source"); if (predicate == null) throw new ArgumentNullException("predicate"); LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, true, values); - if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContent)) + if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContentBase)) { //source list is DynamicNode and the lambda returns a Func - IQueryable typedSource = source as IQueryable; + IQueryable typedSource = source as IQueryable; var compiledFunc = lambda.Compile(); - Func func = null; - Func boolFunc = null; - if (compiledFunc is Func) + Func func = null; + Func boolFunc = null; + if (compiledFunc is Func) { - func = (Func)compiledFunc; + func = (Func)compiledFunc; } - if (compiledFunc is Func) + if (compiledFunc is Func) { - boolFunc = (Func)compiledFunc; + boolFunc = (Func)compiledFunc; } - return typedSource.Where(delegate(DynamicPublishedContent node) + return typedSource.Where(delegate(DynamicPublishedContentBase node) { object value = -1; //value = func(node); @@ -46,13 +47,13 @@ namespace Umbraco.Core.Dynamics if (func != null) { var firstFuncResult = func(node); - if (firstFuncResult is Func) + if (firstFuncResult is Func) { - value = (firstFuncResult as Func)(node); + value = (firstFuncResult as Func)(node); } - if (firstFuncResult is Func) + if (firstFuncResult is Func) { - value = (firstFuncResult as Func)(node); + value = (firstFuncResult as Func)(node); } if (firstFuncResult is bool) { @@ -86,28 +87,28 @@ namespace Umbraco.Core.Dynamics } } - public static IQueryable Select(this IQueryable source, string selector, params object[] values) + public static IQueryable Select(this IQueryable source, string selector, params object[] values) { if (source == null) throw new ArgumentNullException("source"); if (selector == null) throw new ArgumentNullException("selector"); LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(object), selector, false, values); - if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContent)) + if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContentBase)) { //source list is DynamicNode and the lambda returns a Func - IQueryable typedSource = source as IQueryable; + IQueryable typedSource = source as IQueryable; var compiledFunc = lambda.Compile(); - Func func = null; - if (compiledFunc is Func) + Func func = null; + if (compiledFunc is Func) { - func = (Func)compiledFunc; + func = (Func)compiledFunc; } - return typedSource.Select(delegate(DynamicPublishedContent node) + return typedSource.Select(delegate(DynamicPublishedContentBase node) { object value = null; value = func(node); - if (value is Func) + if (value is Func) { - var innerValue = (value as Func)(node); + var innerValue = (value as Func)(node); return innerValue; } return value; @@ -133,7 +134,7 @@ namespace Umbraco.Core.Dynamics if (source == null) throw new ArgumentNullException("source"); if (ordering == null) throw new ArgumentNullException("ordering"); - IQueryable typedSource = source as IQueryable; + IQueryable typedSource = source as IQueryable; if (!ordering.Contains(",")) { bool descending = false; @@ -149,10 +150,10 @@ namespace Umbraco.Core.Dynamics } LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(object), ordering, false, values); - if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContent)) + if (lambda.Parameters.Count > 0 && lambda.Parameters[0].Type == typeof(DynamicPublishedContentBase)) { //source list is DynamicNode and the lambda returns a Func - Func func = (Func)lambda.Compile(); + Func func = (Func)lambda.Compile(); //get the values out var query = typedSource.ToList().ConvertAll(item => new { node = item, key = EvaluateDynamicNodeFunc(item, func) }); if (query.Count == 0) @@ -246,13 +247,13 @@ namespace Umbraco.Core.Dynamics return null; } } - private static object EvaluateDynamicNodeFunc(DynamicPublishedContent publishedContent, Func func) + private static object EvaluateDynamicNodeFunc(DynamicPublishedContentBase publishedContent, Func func) { object value = -1; var firstFuncResult = func(publishedContent); - if (firstFuncResult is Func) + if (firstFuncResult is Func) { - value = (firstFuncResult as Func)(publishedContent); + value = (firstFuncResult as Func)(publishedContent); } if (firstFuncResult.GetType().IsValueType || firstFuncResult is string) { diff --git a/src/Umbraco.Core/Dynamics/ExpressionParser.cs b/src/Umbraco.Core/Dynamics/ExpressionParser.cs index b8dff83b78..78b2e0fafd 100644 --- a/src/Umbraco.Core/Dynamics/ExpressionParser.cs +++ b/src/Umbraco.Core/Dynamics/ExpressionParser.cs @@ -4,6 +4,7 @@ using System.Dynamic; using System.Linq; using System.Linq.Expressions; using System.Reflection; +using Umbraco.Core.Models; namespace Umbraco.Core.Dynamics { @@ -507,7 +508,7 @@ namespace Umbraco.Core.Dynamics (expr as LambdaExpression).Parameters.CopyTo(parameters, 0); var invokedExpr = Expression.Invoke(expr, parameters); var not = Expression.Not(Expression.TypeAs(invokedExpr, typeof(Nullable))); - expr = Expression.Lambda>( + expr = Expression.Lambda>( Expression.Condition( Expression.Property(not, "HasValue"), Expression.Property(not, "Value"), @@ -854,11 +855,11 @@ namespace Umbraco.Core.Dynamics Expression[] args = ParseArgumentList(); MethodBase mb; LambdaExpression instanceAsString = null; - ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicPublishedContent), "instance"); + ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicPublishedContentBase), "instance"); if (type.IsGenericType && type != typeof(string)) { var typeArgs = type.GetGenericArguments(); - if (typeArgs[0] == typeof(DynamicPublishedContent)) + if (typeArgs[0] == typeof(DynamicPublishedContentBase)) { if (instance != null && instance is LambdaExpression) { @@ -929,14 +930,14 @@ namespace Umbraco.Core.Dynamics //this will invoke TryGetMember (but wrapped in an expression tree) //so that when it's evaluated, DynamicNode should be supported - ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicPublishedContent), "instance"); + ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicPublishedContentBase), "instance"); ParameterExpression convertDynamicNullToBooleanFalse = Expression.Parameter(typeof(bool), "convertDynamicNullToBooleanFalse"); ParameterExpression result = Expression.Parameter(typeof(object), "result"); ParameterExpression binder = Expression.Variable(typeof(DynamicQueryableGetMemberBinder), "binder"); ParameterExpression ignoreCase = Expression.Variable(typeof(bool), "ignoreCase"); ConstructorInfo getMemberBinderConstructor = typeof(DynamicQueryableGetMemberBinder).GetConstructor(new Type[] { typeof(string), typeof(bool) }); LabelTarget blockReturnLabel = Expression.Label(typeof(object)); - MethodInfo method = typeof(DynamicPublishedContent).GetMethod("TryGetMember"); + MethodInfo method = typeof(DynamicPublishedContentBase).GetMethod("TryGetMember"); BlockExpression block = Expression.Block( typeof(object), @@ -957,10 +958,10 @@ namespace Umbraco.Core.Dynamics Expression.Return(blockReturnLabel, result), Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object))) ); - LambdaExpression lax = Expression.Lambda>(block, instanceExpression); + LambdaExpression lax = Expression.Lambda>(block, instanceExpression); return lax; } - if (typeof(Func).IsAssignableFrom(type)) + if (typeof(Func).IsAssignableFrom(type)) { //accessing a property off an already resolved DynamicNode TryGetMember call //e.g. uBlogsyPostDate.Date @@ -971,8 +972,8 @@ namespace Umbraco.Core.Dynamics ParameterExpression result = Expression.Parameter(typeof(object), "result"); ParameterExpression idParam = Expression.Parameter(typeof(string), "id"); ParameterExpression lambdaResult = Expression.Parameter(typeof(object), "lambdaResult"); - ParameterExpression lambdaInstanceExpression = Expression.Parameter(typeof(DynamicPublishedContent), "lambdaInstanceExpression"); - ParameterExpression instanceExpression = Expression.Parameter(typeof(Func), "instance"); + ParameterExpression lambdaInstanceExpression = Expression.Parameter(typeof(DynamicPublishedContentBase), "lambdaInstanceExpression"); + ParameterExpression instanceExpression = Expression.Parameter(typeof(Func), "instance"); LabelTarget blockReturnLabel = Expression.Label(typeof(object)); BlockExpression block = Expression.Block( @@ -991,7 +992,7 @@ namespace Umbraco.Core.Dynamics Expression.Return(blockReturnLabel, result), Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object))) ); - LambdaExpression lax = Expression.Lambda>(block, lambdaInstanceExpression); + LambdaExpression lax = Expression.Lambda>(block, lambdaInstanceExpression); return lax; } } @@ -1050,11 +1051,11 @@ namespace Umbraco.Core.Dynamics switch (methodReturnType.Name) { case "String": - return Expression.Lambda>(block, instanceExpression); + return Expression.Lambda>(block, instanceExpression); case "Int32": - return Expression.Lambda>(block, instanceExpression); + return Expression.Lambda>(block, instanceExpression); case "Boolean": - return Expression.Lambda>(block, instanceExpression); + return Expression.Lambda>(block, instanceExpression); } return Expression.Call(instance, (MethodInfo)method, args); } @@ -1092,8 +1093,8 @@ namespace Umbraco.Core.Dynamics Expression.Return(cblockReturnLabel, cresult), Expression.Label(cblockReturnLabel, Expression.Constant(null, typeof(string)))); - LambdaExpression lax2 = Expression.Lambda>(cblock, instanceExpression); - var expression = Expression.Lambda>(cblock, instanceExpression); + LambdaExpression lax2 = Expression.Lambda>(cblock, instanceExpression); + var expression = Expression.Lambda>(cblock, instanceExpression); return expression; } @@ -1410,7 +1411,7 @@ namespace Umbraco.Core.Dynamics //if the type of the expression is a func - invokable returning object, //we are going to return it here, because we can get the real value when we actually have the instance //if (typeof(Func).IsAssignableFrom(expr.Type)) return expr; - if (expr is LambdaExpression && ((LambdaExpression)expr).Parameters.Count > 0 && ((LambdaExpression)expr).Parameters[0].Type == typeof(DynamicPublishedContent)) + if (expr is LambdaExpression && ((LambdaExpression)expr).Parameters.Count > 0 && ((LambdaExpression)expr).Parameters[0].Type == typeof(DynamicPublishedContentBase)) { return expr; } @@ -1689,12 +1690,12 @@ namespace Umbraco.Core.Dynamics UnaryExpression unboxedLeft = null, unboxedRight = null; ParameterExpression[] parameters = null; - if (left is LambdaExpression && (left as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicPublishedContent)) + if (left is LambdaExpression && (left as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicPublishedContentBase)) { leftIsLambda = true; } - if (right is LambdaExpression && (right as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicPublishedContent)) + if (right is LambdaExpression && (right as LambdaExpression).Type.GetGenericArguments().First() == typeof(DynamicPublishedContentBase)) { rightIsLambda = true; } @@ -1748,11 +1749,11 @@ namespace Umbraco.Core.Dynamics if (expressionType == ExpressionType.AndAlso) { - return ExpressionExtensions.And(left as Expression>, right as Expression>); + return ExpressionExtensions.And(left as Expression>, right as Expression>); } if (expressionType == ExpressionType.OrElse) { - return ExpressionExtensions.Or(left as Expression>, right as Expression>); + return ExpressionExtensions.Or(left as Expression>, right as Expression>); } } @@ -1783,11 +1784,11 @@ namespace Umbraco.Core.Dynamics //left is invoked and unboxed to right's TOut, right was not boxed if (expressionType == ExpressionType.AndAlso) { - return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); + return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); } if (expressionType == ExpressionType.OrElse) { - return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); + return ExpressionExtensions.And(right as Expression>, Expression.Lambda>(unboxedLeft, parameters) as Expression>); } } else @@ -1804,11 +1805,11 @@ namespace Umbraco.Core.Dynamics //right is invoked and unboxed to left's TOut, left was not boxed if (expressionType == ExpressionType.AndAlso) { - return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); + return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); } if (expressionType == ExpressionType.OrElse) { - return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); + return ExpressionExtensions.And(left as Expression>, Expression.Lambda>(unboxedRight, parameters) as Expression>); } } @@ -1879,7 +1880,7 @@ namespace Umbraco.Core.Dynamics break; case ExpressionType.Modulo: binaryExpression = Expression.Modulo(finalLeft, finalRight); - return (Expression.Lambda>(binaryExpression, parameters)); + return (Expression.Lambda>(binaryExpression, parameters)); case ExpressionType.AndAlso: if ((leftIsLambda && rightIsLambda && sequenceEqual) || (!leftIsLambda && !rightIsLambda)) { @@ -1887,7 +1888,7 @@ namespace Umbraco.Core.Dynamics } else { - return (Expression.Lambda>(Expression.AndAlso(finalLeft, finalRight), parameters)); + return (Expression.Lambda>(Expression.AndAlso(finalLeft, finalRight), parameters)); } case ExpressionType.OrElse: if (leftIsLambda && rightIsLambda && sequenceEqual || (!leftIsLambda && !rightIsLambda)) @@ -1896,7 +1897,7 @@ namespace Umbraco.Core.Dynamics } else { - return (Expression.Lambda>(Expression.OrElse(finalLeft, finalRight), parameters)); + return (Expression.Lambda>(Expression.OrElse(finalLeft, finalRight), parameters)); } default: return Expression.Equal(left, right); @@ -1904,7 +1905,7 @@ namespace Umbraco.Core.Dynamics if (leftIsLambda || rightIsLambda) { var body = Expression.Condition(Expression.TypeEqual(innerLeft, right.Type), binaryExpression, Expression.Constant(false)); - return Expression.Lambda>(body, parameters); + return Expression.Lambda>(body, parameters); } else { diff --git a/src/Umbraco.Core/Dynamics/ExtensionMethods.cs b/src/Umbraco.Core/Dynamics/ExtensionMethods.cs index 69689bf204..90c8828584 100644 --- a/src/Umbraco.Core/Dynamics/ExtensionMethods.cs +++ b/src/Umbraco.Core/Dynamics/ExtensionMethods.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Models; namespace Umbraco.Core.Dynamics { @@ -47,7 +48,7 @@ namespace Umbraco.Core.Dynamics return new DynamicPublishedContentList(all.Items.OrderBy(x => Guid.NewGuid()).Take(max)); } - public static DynamicPublishedContent Random(this DynamicPublishedContentList all) + public static DynamicPublishedContentBase Random(this DynamicPublishedContentList all) { return all.Items.OrderBy(x => Guid.NewGuid()).First(); } diff --git a/src/Umbraco.Core/Dynamics/Grouping.cs b/src/Umbraco.Core/Dynamics/Grouping.cs index d1122a6395..c3b5a4dcca 100644 --- a/src/Umbraco.Core/Dynamics/Grouping.cs +++ b/src/Umbraco.Core/Dynamics/Grouping.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Collections; using System.Dynamic; +using Umbraco.Core.Models; namespace Umbraco.Core.Dynamics { @@ -13,7 +14,7 @@ namespace Umbraco.Core.Dynamics public IEnumerator GetEnumerator() { - var temp = new DynamicPublishedContentList(Elements.Cast()); + var temp = new DynamicPublishedContentList(Elements.Cast()); return (IEnumerator)temp.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() @@ -42,7 +43,7 @@ namespace Umbraco.Core.Dynamics object key = null; (item as DynamicObject).TryGetMember(new DynamicQueryableGetMemberBinder(ordering, false), out key); return key; - }).Cast()); + }).Cast()); } else { @@ -51,7 +52,7 @@ namespace Umbraco.Core.Dynamics object key = null; (item as DynamicObject).TryGetMember(new DynamicQueryableGetMemberBinder(ordering, false), out key); return key; - }).Cast()); + }).Cast()); } } } diff --git a/src/Umbraco.Core/Dynamics/IDynamicPublishedContentDataSource.cs b/src/Umbraco.Core/Dynamics/IDynamicPublishedContentDataSource.cs deleted file mode 100644 index 50f7eba1c2..0000000000 --- a/src/Umbraco.Core/Dynamics/IDynamicPublishedContentDataSource.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Umbraco.Core.Dynamics -{ - /// - /// This exists only because we want Dynamics in the Core project but DynamicNode has references to ContentType to run some queries - /// and currently the business logic part of Umbraco is still in the legacy project and we don't want to move that to the core so in the - /// meantime until the new APIs are made, we need to have this data source in place with a resolver which is set in the web project. - /// - internal interface IDynamicPublishedContentDataSource - { - Guid GetDataType(string docTypeAlias, string propertyAlias); - - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/DynamicPublishedContentBase.cs b/src/Umbraco.Core/Models/DynamicPublishedContentBase.cs new file mode 100644 index 0000000000..b7c0c277ec --- /dev/null +++ b/src/Umbraco.Core/Models/DynamicPublishedContentBase.cs @@ -0,0 +1,843 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Web; +using Umbraco.Core.Configuration; +using Umbraco.Core.Dynamics; +using Umbraco.Core.PropertyEditors; +using System.Reflection; +using System.Xml.Linq; + +namespace Umbraco.Core.Models +{ + + /// + /// The base dynamic model for views + /// + public class DynamicPublishedContentBase : DynamicObject, IPublishedContent + { + protected IPublishedContent PublishedContent { get; private set; } + private DynamicPublishedContentList _cachedChildren; + private readonly ConcurrentDictionary _cachedMemberOutput = new ConcurrentDictionary(); + + #region Constructors + + public DynamicPublishedContentBase(IPublishedContent node) + { + if (node == null) throw new ArgumentNullException("node"); + PublishedContent = node; + } + + /// + /// Returns an empty/blank DynamicPublishedContent, this is used for special case scenarios + /// + /// + internal static DynamicPublishedContentBase Empty() + { + return new DynamicPublishedContentBase(); + } + + private DynamicPublishedContentBase() + { + } + + #endregion + + public dynamic AsDynamic() + { + return this; + } + + public bool HasProperty(string name) + { + if (PublishedContent != null) + { + try + { + var prop = GetUserProperty(name); + + return (prop != null); + } + catch (Exception) + { + return false; + } + } + return false; + } + + /// + /// Attempts to call a method on the dynamic object + /// + /// + /// + /// + /// + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) + { + //TODO: We MUST cache the result here, it is very expensive to keep finding extension methods! + + try + { + //Property? + result = typeof(DynamicPublishedContentBase).InvokeMember(binder.Name, + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.GetProperty, + null, + this, + args); + return true; + } + catch (MissingMethodException) + { + try + { + //Static or Instance Method? + result = typeof(DynamicPublishedContentBase).InvokeMember(binder.Name, + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.Static | + System.Reflection.BindingFlags.InvokeMethod, + null, + this, + args); + return true; + } + catch (MissingMethodException) + { + try + { + result = ExecuteExtensionMethod(args, binder.Name); + return true; + } + catch (TargetInvocationException) + { + result = new DynamicNull(); + return true; + } + + catch + { + //TODO: LOg this! + + result = null; + return false; + } + + } + + + } + catch + { + result = null; + return false; + } + + } + + private object ExecuteExtensionMethod(object[] args, string name) + { + object result = null; + + var methodTypesToFind = new[] + { + typeof(DynamicPublishedContentBase) + }; + + //find known extension methods that match the first type in the list + MethodInfo toExecute = null; + foreach (var t in methodTypesToFind) + { + toExecute = ExtensionMethodFinder.FindExtensionMethod(t, args, name, false); + if (toExecute != null) + break; + } + + if (toExecute != null) + { + var genericArgs = (new[] { this }).Concat(args); + result = toExecute.Invoke(null, genericArgs.ToArray()); + } + else + { + throw new MissingMethodException(); + } + if (result != null) + { + if (result is IPublishedContent) + { + result = new DynamicPublishedContentBase((IPublishedContent)result); + } + if (result is IEnumerable) + { + result = new DynamicPublishedContentList((IEnumerable)result); + } + if (result is IEnumerable) + { + result = new DynamicPublishedContentList((IEnumerable)result); + } + } + return result; + } + + /// + /// Attempts to return a custom member (generally based on a string match) + /// + /// + /// + protected virtual Attempt TryGetCustomMember(GetMemberBinder binder) + { + if (binder.Name.InvariantEquals("ChildrenAsList") || binder.Name.InvariantEquals("Children")) + { + return new Attempt(true, Children); + } + + if (binder.Name.InvariantEquals("parentId")) + { + var parent = ((IPublishedContent) this).Parent; + if (parent == null) + { + throw new InvalidOperationException(string.Format("The node {0} does not have a parent", Id)); + } + return new Attempt(true, parent.Id); + } + return Attempt.False; + } + + /// + /// Attempts to return the children by the document type's alias (for example: CurrentPage.NewsItems where NewsItem is the + /// document type alias) + /// + /// + /// + /// + /// This method will work by both the plural and non-plural alias (i.e. NewsItem and NewsItems) + /// + protected virtual Attempt TryGetChildrenByAlias(GetMemberBinder binder) + { + + var filteredTypeChildren = PublishedContent.Children + .Where(x => x.DocumentTypeAlias.InvariantEquals(binder.Name) || x.DocumentTypeAlias.MakePluralName().InvariantEquals(binder.Name)) + .ToArray(); + if (filteredTypeChildren.Any()) + { + return new Attempt(true, + new DynamicPublishedContentList(filteredTypeChildren.Select(x => new DynamicPublishedContentBase(x)))); + } + return Attempt.False; + } + + /// + /// Attempts to return a member based on the reflected document property + /// + /// + /// + protected virtual Attempt TryGetDocumentProperty(GetMemberBinder binder) + { + + var reflectedProperty = GetReflectedProperty(binder.Name); + var result = reflectedProperty != null + ? reflectedProperty.Value + : null; + + return result == null + ? Attempt.False + : new Attempt(true, result); + } + + /// + /// Attempts to return a member based on a user defined umbraco property + /// + /// + /// + protected virtual Attempt TryGetUserProperty(GetMemberBinder binder) + { + var name = binder.Name; + var recursive = false; + if (name.StartsWith("_")) + { + name = name.Substring(1, name.Length - 1); + recursive = true; + } + + var userProperty = GetUserProperty(name, recursive); + + if (userProperty == null) + { + return Attempt.False; + } + + var result = userProperty.Value; + + if (PublishedContent.DocumentTypeAlias == null && userProperty.Alias == null) + { + throw new InvalidOperationException("No node alias or property alias available. Unable to look up the datatype of the property you are trying to fetch."); + } + + return new Attempt(true, result); + + } + + /// + /// Returns the member match methods in the correct order and is used in the TryGetMember method. + /// + /// + protected virtual IEnumerable>> GetMemberMatchMethods() + { + var memberMatchMethods = new List>> + { + TryGetCustomMember, //match custom members + TryGetUserProperty, //then match custom user defined umbraco properties + TryGetChildrenByAlias, //then try to match children based on doc type alias + TryGetDocumentProperty //then try to match on a reflected document property + }; + return memberMatchMethods; + } + + /// + /// Try to return an object based on the dynamic member accessor + /// + /// + /// + /// + /// + /// TODO: SD: This will alwasy return true so that no exceptions are generated, this is only because this is how the + /// old DynamicNode worked, I'm not sure if this is the correct/expected functionality but I've left it like that. + /// IMO I think this is incorrect and it would be better to throw an exception for something that is not supported! + /// + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + if (binder == null) throw new ArgumentNullException("binder"); + + var name = binder.Name; + + //check the cache first! + if (_cachedMemberOutput.TryGetValue(name, out result)) + { + return true; + } + + //loop through each member match method and execute it. + //If it is successful, cache the result and return it. + foreach (var attempt in GetMemberMatchMethods() + .Select(m => m(binder)) + .Where(attempt => attempt.Success)) + { + result = attempt.Result; + //cache the result so we don't have to re-process the whole thing + _cachedMemberOutput.TryAdd(name, result); + return true; + } + + //if property access, type lookup and member invoke all failed + //at this point, we're going to return null + //instead, we return a DynamicNull - see comments in that file + //this will let things like Model.ChildItem work and return nothing instead of crashing + + //.Where explictly checks for this type + //and will make it false + //which means backwards equality (&& property != true) will pass + //forwwards equality (&& property or && property == true) will fail + result = new DynamicNull(); + + //alwasy return true if we haven't thrown an exception though I'm wondering if we return 'false' if .Net throws an exception for us?? + return true; + } + + /// + /// Returns a property defined on the document object as a member property using reflection + /// + /// + /// + private PropertyResult GetReflectedProperty(string alias) + { + return GetPropertyInternal(alias, PublishedContent, false); + } + + /// + /// Return a user defined property + /// + /// + /// + /// + internal PropertyResult GetUserProperty(string alias, bool recursive = false) + { + if (!recursive) + { + return GetPropertyInternal(alias, PublishedContent); + } + var context = this; + var prop = GetPropertyInternal(alias, PublishedContent); + while (prop == null || !prop.HasValue()) + { + var parent = ((IPublishedContent) context).Parent; + if (parent == null) break; + prop = context.GetPropertyInternal(alias, context.PublishedContent); + } + return prop; + } + + + private PropertyResult GetPropertyInternal(string alias, IPublishedContent content, bool checkUserProperty = true) + { + if (alias.IsNullOrWhiteSpace()) throw new ArgumentNullException("alias"); + if (content == null) throw new ArgumentNullException("content"); + + //if we're looking for a user defined property + if (checkUserProperty) + { + var prop = content.GetProperty(alias); + + return prop == null + ? null + : new PropertyResult(prop, PropertyResultType.UserProperty) + { + DocumentTypeAlias = content.DocumentTypeAlias, + DocumentId = content.Id + }; + } + + //reflect + + Func> getMember = + memberAlias => + { + try + { + return new Attempt(true, + content.GetType().InvokeMember(memberAlias, + System.Reflection.BindingFlags.GetProperty | + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public, + null, + content, + null)); + } + catch (MissingMethodException ex) + { + return new Attempt(ex); + } + }; + + //try with the current casing + var attempt = getMember(alias); + if (!attempt.Success) + { + //if we cannot get with the current alias, try changing it's case + attempt = alias[0].IsUpperCase() + ? getMember(alias.ConvertCase(StringAliasCaseType.CamelCase)) + : getMember(alias.ConvertCase(StringAliasCaseType.PascalCase)); + } + + return !attempt.Success + ? null + : new PropertyResult(alias, attempt.Result, Guid.Empty, PropertyResultType.ReflectedProperty) + { + DocumentTypeAlias = content.DocumentTypeAlias, + DocumentId = content.Id + }; + } + + + + //public DynamicNode Media(string propertyAlias) + //{ + // if (_n != null) + // { + // IProperty prop = _n.GetProperty(propertyAlias); + // if (prop != null) + // { + // int mediaNodeId; + // if (int.TryParse(prop.Value, out mediaNodeId)) + // { + // return _razorLibrary.Value.MediaById(mediaNodeId); + // } + // } + // return null; + // } + // return null; + //} + //public bool IsProtected + //{ + // get + // { + // if (_n != null) + // { + // return umbraco.library.IsProtected(_n.Id, _n.Path); + // } + // return false; + // } + //} + //public bool HasAccess + //{ + // get + // { + // if (_n != null) + // { + // return umbraco.library.HasAccess(_n.Id, _n.Path); + // } + // return true; + // } + //} + + //public string Media(string propertyAlias, string mediaPropertyAlias) + //{ + // if (_n != null) + // { + // IProperty prop = _n.GetProperty(propertyAlias); + // if (prop == null && propertyAlias.Substring(0, 1).ToUpper() == propertyAlias.Substring(0, 1)) + // { + // prop = _n.GetProperty(propertyAlias.Substring(0, 1).ToLower() + propertyAlias.Substring((1))); + // } + // if (prop != null) + // { + // int mediaNodeId; + // if (int.TryParse(prop.Value, out mediaNodeId)) + // { + // umbraco.cms.businesslogic.media.Media media = new cms.businesslogic.media.Media(mediaNodeId); + // if (media != null) + // { + // Property mprop = media.getProperty(mediaPropertyAlias); + // // check for nicer support of Pascal Casing EVEN if alias is camelCasing: + // if (prop == null && mediaPropertyAlias.Substring(0, 1).ToUpper() == mediaPropertyAlias.Substring(0, 1)) + // { + // mprop = media.getProperty(mediaPropertyAlias.Substring(0, 1).ToLower() + mediaPropertyAlias.Substring((1))); + // } + // if (mprop != null) + // { + // return string.Format("{0}", mprop.Value); + // } + // } + // } + // } + // } + // return null; + //} + + public int TemplateId + { + get { return PublishedContent.TemplateId; } + } + + public int SortOrder + { + get { return PublishedContent.SortOrder; } + } + + public string Name + { + get { return PublishedContent.Name; } + } + + public bool Visible + { + get + { + + var umbracoNaviHide = GetUserProperty("umbracoNaviHide"); + if (umbracoNaviHide != null) + { + return umbracoNaviHide.Value.ToString().Trim() != "1"; + } + return true; + } + } + + public string UrlName + { + get { return PublishedContent.UrlName; } + } + + public string DocumentTypeAlias + { + get { return PublishedContent.DocumentTypeAlias; } + } + + public string WriterName + { + get { return PublishedContent.WriterName; } + } + + public string CreatorName + { + get { return PublishedContent.CreatorName; } + } + + public int WriterId + { + get { return PublishedContent.WriterId; } + } + + public int CreatorId + { + get { return PublishedContent.CreatorId; } + } + + public string Path + { + get { return PublishedContent.Path; } + } + + public DateTime CreateDate + { + get { return PublishedContent.CreateDate; } + } + + public int Id + { + get { return PublishedContent.Id; } + } + + public DateTime UpdateDate + { + get { return PublishedContent.UpdateDate; } + } + + public Guid Version + { + get { return PublishedContent.Version; } + } + + public int Level + { + get { return PublishedContent.Level; } + } + + public IEnumerable Properties + { + get { return PublishedContent.Properties; } + } + + public DynamicPublishedContentList Children + { + get + { + if (_cachedChildren == null) + { + var children = PublishedContent.Children; + //testing, think this must be a special case for the root node ? + if (!children.Any() && PublishedContent.Id == 0) + { + _cachedChildren = new DynamicPublishedContentList(new List { new DynamicPublishedContentBase(this.PublishedContent) }); + } + else + { + _cachedChildren = new DynamicPublishedContentList(PublishedContent.Children.Select(x => new DynamicPublishedContentBase(x))); + } + } + return _cachedChildren; + } + } + + #region GetProperty methods which can be used with the dynamic object + + public IDocumentProperty GetProperty(string alias) + { + return GetProperty(alias, false); + } + public IDocumentProperty GetProperty(string alias, bool recursive) + { + return alias.StartsWith("@") + ? GetReflectedProperty(alias.TrimStart('@')) + : GetUserProperty(alias, recursive); + } + public string GetPropertyValue(string alias) + { + return GetPropertyValue(alias, false); + } + public string GetPropertyValue(string alias, string fallback) + { + var prop = GetPropertyValue(alias); + return !prop.IsNullOrWhiteSpace() ? prop : fallback; + } + public string GetPropertyValue(string alias, bool recursive) + { + var p = alias.StartsWith("@") + ? GetReflectedProperty(alias.TrimStart('@')) + : GetUserProperty(alias, recursive); + return p == null ? null : p.ValueAsString; + } + public string GetPropertyValue(string alias, bool recursive, string fallback) + { + var prop = GetPropertyValue(alias, recursive); + return !prop.IsNullOrWhiteSpace() ? prop : fallback; + } + + #endregion + + #region HasValue + public bool HasValue(string alias) + { + return HasValue(alias, false); + } + public bool HasValue(string alias, bool recursive) + { + var prop = GetUserProperty(alias, recursive); + if (prop == null) return false; + return prop.HasValue(); + } + public IHtmlString HasValue(string alias, string valueIfTrue, string valueIfFalse) + { + return HasValue(alias, false) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); + } + public IHtmlString HasValue(string alias, bool recursive, string valueIfTrue, string valueIfFalse) + { + return HasValue(alias, recursive) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); + } + public IHtmlString HasValue(string alias, string valueIfTrue) + { + return HasValue(alias, false) ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); + } + public IHtmlString HasValue(string alias, bool recursive, string valueIfTrue) + { + return HasValue(alias, recursive) ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); + } + #endregion + + #region Where + + public HtmlString Where(string predicate, string valueIfTrue) + { + return Where(predicate, valueIfTrue, string.Empty); + } + public HtmlString Where(string predicate, string valueIfTrue, string valueIfFalse) + { + if (Where(predicate)) + { + return new HtmlString(valueIfTrue); + } + return new HtmlString(valueIfFalse); + } + public bool Where(string predicate) + { + //Totally gonna cheat here + var dynamicDocumentList = new DynamicPublishedContentList(); + dynamicDocumentList.Add(this); + var filtered = dynamicDocumentList.Where(predicate); + if (Queryable.Count(filtered) == 1) + { + //this node matches the predicate + return true; + } + return false; + } + + #endregion + + //TODO: need a method to return a string value for a user property regardless of xml content or data type thus bypassing all of the PropertyEditorValueConverters + ///// + ///// Returns the value as as string regardless of xml content or data type + ///// + ///// + //public override string ToString() + //{ + // return base.ToString(); + //} + + #region Explicit IPublishedContent implementation + IPublishedContent IPublishedContent.Parent + { + get { return PublishedContent.Parent; } + } + + int IPublishedContent.Id + { + get { return PublishedContent.Id; } + } + + int IPublishedContent.TemplateId + { + get { return PublishedContent.TemplateId; } + } + + int IPublishedContent.SortOrder + { + get { return PublishedContent.SortOrder; } + } + + string IPublishedContent.Name + { + get { return PublishedContent.Name; } + } + + string IPublishedContent.UrlName + { + get { return PublishedContent.UrlName; } + } + + string IPublishedContent.DocumentTypeAlias + { + get { return PublishedContent.DocumentTypeAlias; } + } + + int IPublishedContent.DocumentTypeId + { + get { return PublishedContent.DocumentTypeId; } + } + + string IPublishedContent.WriterName + { + get { return PublishedContent.WriterName; } + } + + string IPublishedContent.CreatorName + { + get { return PublishedContent.CreatorName; } + } + + int IPublishedContent.WriterId + { + get { return PublishedContent.WriterId; } + } + + int IPublishedContent.CreatorId + { + get { return PublishedContent.CreatorId; } + } + + string IPublishedContent.Path + { + get { return PublishedContent.Path; } + } + + DateTime IPublishedContent.CreateDate + { + get { return PublishedContent.CreateDate; } + } + + DateTime IPublishedContent.UpdateDate + { + get { return PublishedContent.UpdateDate; } + } + + Guid IPublishedContent.Version + { + get { return PublishedContent.Version; } + } + + int IPublishedContent.Level + { + get { return PublishedContent.Level; } + } + + System.Collections.ObjectModel.Collection IPublishedContent.Properties + { + get { return PublishedContent.Properties; } + } + + IEnumerable IPublishedContent.Children + { + get { return PublishedContent.Children; } + } + + IDocumentProperty IPublishedContent.GetProperty(string alias) + { + return PublishedContent.GetProperty(alias); + } + #endregion + } +} diff --git a/src/Umbraco.Core/Dynamics/DynamicPublishedContentList.cs b/src/Umbraco.Core/Models/DynamicPublishedContentList.cs similarity index 88% rename from src/Umbraco.Core/Dynamics/DynamicPublishedContentList.cs rename to src/Umbraco.Core/Models/DynamicPublishedContentList.cs index 35658b46af..c3b47f2571 100644 --- a/src/Umbraco.Core/Dynamics/DynamicPublishedContentList.cs +++ b/src/Umbraco.Core/Models/DynamicPublishedContentList.cs @@ -2,32 +2,29 @@ using System.Collections.Generic; using System.Linq; using System.Dynamic; -using Umbraco.Core.Models; -using umbraco.interfaces; +using Umbraco.Core.Dynamics; using System.Collections; using System.Reflection; -namespace Umbraco.Core.Dynamics +namespace Umbraco.Core.Models { - public class DynamicPublishedContentList : DynamicObject, IEnumerable + public class DynamicPublishedContentList : DynamicObject, IEnumerable { - internal List Items { get; set; } + internal List Items { get; set; } public DynamicPublishedContentList() { - Items = new List(); + Items = new List(); } - public DynamicPublishedContentList(IEnumerable items) + public DynamicPublishedContentList(IEnumerable items) { - List list = items.ToList(); - list.ForEach(node => node.OwnerList = this); + var list = items.ToList(); Items = list; } public DynamicPublishedContentList(IEnumerable items) { - List list = items.Select(x => new DynamicPublishedContent(x)).ToList(); - list.ForEach(node => node.OwnerList = this); + var list = items.Select(x => new DynamicPublishedContentBase(x)).ToList(); Items = list; } public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) @@ -61,14 +58,14 @@ namespace Umbraco.Core.Dynamics var values = args.Skip(1).ToArray(); //TODO: We are pre-resolving the where into a ToList() here which will have performance impacts if there where clauses // are nested! We should somehow support an QueryableDocumentList! - result = new DynamicPublishedContentList(this.Where(predicate, values).ToList()); + result = new DynamicPublishedContentList(this.Where(predicate, values).ToList()); return true; } if (name == "OrderBy") { //TODO: We are pre-resolving the where into a ToList() here which will have performance impacts if there where clauses // are nested! We should somehow support an QueryableDocumentList! - result = new DynamicPublishedContentList(this.OrderBy(args.First().ToString()).ToList()); + result = new DynamicPublishedContentList(this.OrderBy(args.First().ToString()).ToList()); return true; } if (name == "Take") @@ -86,7 +83,7 @@ namespace Umbraco.Core.Dynamics int groupSize = 0; if (int.TryParse(args.First().ToString(), out groupSize)) { - result = this.InGroupsOf(groupSize); + result = this.InGroupsOf(groupSize); return true; } result = new DynamicNull(); @@ -97,7 +94,7 @@ namespace Umbraco.Core.Dynamics int groupCount = 0; if (int.TryParse(args.First().ToString(), out groupCount)) { - result = this.GroupedInto(groupCount); + result = this.GroupedInto(groupCount); return true; } result = new DynamicNull(); @@ -105,7 +102,7 @@ namespace Umbraco.Core.Dynamics } if (name == "GroupBy") { - result = this.GroupBy(args.First().ToString()); + result = this.GroupBy(args.First().ToString()); return true; } if (name == "Average" || name == "Min" || name == "Max" || name == "Sum") @@ -115,9 +112,9 @@ namespace Umbraco.Core.Dynamics } if (name == "Union") { - if ((args.First() as IEnumerable) != null) + if ((args.First() as IEnumerable) != null) { - result = new DynamicPublishedContentList(this.Items.Union(args.First() as IEnumerable)); + result = new DynamicPublishedContentList(this.Items.Union(args.First() as IEnumerable)); return true; } if ((args.First() as DynamicPublishedContentList) != null) @@ -128,9 +125,9 @@ namespace Umbraco.Core.Dynamics } if (name == "Except") { - if ((args.First() as IEnumerable) != null) + if ((args.First() as IEnumerable) != null) { - result = new DynamicPublishedContentList(this.Items.Except(args.First() as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); + result = new DynamicPublishedContentList(this.Items.Except(args.First() as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); return true; } if ((args.First() as DynamicPublishedContentList) != null) @@ -141,9 +138,9 @@ namespace Umbraco.Core.Dynamics } if (name == "Intersect") { - if ((args.First() as IEnumerable) != null) + if ((args.First() as IEnumerable) != null) { - result = new DynamicPublishedContentList(this.Items.Intersect(args.First() as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); + result = new DynamicPublishedContentList(this.Items.Intersect(args.First() as IEnumerable, new DynamicPublishedContentIdEqualityComparer())); return true; } if ((args.First() as DynamicPublishedContentList) != null) @@ -202,7 +199,7 @@ namespace Umbraco.Core.Dynamics //We do this to enable error checking of Razor Syntax when a method e.g. ElementAt(2) is used. //When the Script is tested, there's no Children which means ElementAt(2) is invalid (IndexOutOfRange) //Instead, we are going to return an empty DynamicNode. - result = DynamicPublishedContent.Empty(); + result = DynamicPublishedContentBase.Empty(); return true; } @@ -368,7 +365,7 @@ namespace Umbraco.Core.Dynamics var methodTypesToFind = new[] { - typeof(IEnumerable), + typeof(IEnumerable), typeof(DynamicPublishedContentList) }; @@ -408,15 +405,15 @@ namespace Umbraco.Core.Dynamics { if (result is IPublishedContent) { - result = new DynamicPublishedContent((IPublishedContent)result); + result = new DynamicPublishedContentBase((IPublishedContent)result); } if (result is IEnumerable) { result = new DynamicPublishedContentList((IEnumerable)result); } - if (result is IEnumerable) + if (result is IEnumerable) { - result = new DynamicPublishedContentList((IEnumerable)result); + result = new DynamicPublishedContentList((IEnumerable)result); } } return result; @@ -441,9 +438,9 @@ namespace Umbraco.Core.Dynamics return new DynamicGrouping( this .Items - .Select((node, index) => new KeyValuePair(index, node)) + .Select((node, index) => new KeyValuePair(index, node)) .GroupBy(kv => (object)(kv.Key / groupSize)) - .Select(item => new Grouping() + .Select(item => new Grouping() { Key = item.Key, Elements = item.Select(inner => inner.Value) @@ -454,9 +451,9 @@ namespace Umbraco.Core.Dynamics return new DynamicGrouping( this .Items - .Select((node, index) => new KeyValuePair(index, node)) + .Select((node, index) => new KeyValuePair(index, node)) .GroupBy(kv => (object)(kv.Key / groupSize)) - .Select(item => new Grouping() + .Select(item => new Grouping() { Key = item.Key, Elements = item.Select(inner => inner.Value) @@ -469,16 +466,14 @@ namespace Umbraco.Core.Dynamics return Items.AsQueryable().Select(predicate, values); } - public void Add(DynamicPublishedContent publishedContent) + public void Add(DynamicPublishedContentBase publishedContent) { - publishedContent.OwnerList = this; this.Items.Add(publishedContent); } - public void Remove(DynamicPublishedContent publishedContent) + public void Remove(DynamicPublishedContentBase publishedContent) { if (this.Items.Contains(publishedContent)) { - publishedContent.OwnerList = null; this.Items.Remove(publishedContent); } } @@ -491,7 +486,7 @@ namespace Umbraco.Core.Dynamics return true; } - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { return Items.GetEnumerator(); } diff --git a/src/Umbraco.Core/DocumentExtensions.cs b/src/Umbraco.Core/PublishedContentExtensions.cs similarity index 82% rename from src/Umbraco.Core/DocumentExtensions.cs rename to src/Umbraco.Core/PublishedContentExtensions.cs index ae6b8febe5..b49548a9b3 100644 --- a/src/Umbraco.Core/DocumentExtensions.cs +++ b/src/Umbraco.Core/PublishedContentExtensions.cs @@ -8,12 +8,15 @@ using umbraco.interfaces; namespace Umbraco.Core { - public static class DocumentExtensions + /// + /// Extension methods for IPublishedContent + /// + public static class PublishedContentExtensions { public static dynamic AsDynamic(this IPublishedContent doc) { - var dd = new DynamicPublishedContent(doc); + var dd = new DynamicPublishedContentBase(doc); return dd.AsDynamic(); } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index beedce14c7..81e334d42d 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -61,19 +61,17 @@ - + - - + - + - @@ -95,7 +93,6 @@ - diff --git a/src/Umbraco.Tests/ContentStores/PublishContentStoreTests.cs b/src/Umbraco.Tests/ContentStores/PublishContentStoreTests.cs index e686582e9a..7207c8b880 100644 --- a/src/Umbraco.Tests/ContentStores/PublishContentStoreTests.cs +++ b/src/Umbraco.Tests/ContentStores/PublishContentStoreTests.cs @@ -1,3 +1,4 @@ +using System.Linq; using System.Xml; using NUnit.Framework; using Umbraco.Core; @@ -66,6 +67,15 @@ namespace Umbraco.Tests } + [Test] + public void Get_Root_Docs() + { + var result = _publishedContentStore.GetRootDocuments(_umbracoContext); + Assert.AreEqual(2, result.Count()); + Assert.AreEqual(1046, result.ElementAt(0).Id); + Assert.AreEqual(1172, result.ElementAt(1).Id); + } + [TestCase("/", 1046)] [TestCase("/home", 1046)] [TestCase("/Home", 1046)] //test different cases diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs index 8722117bc1..a7172e8002 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs @@ -17,14 +17,14 @@ namespace Umbraco.Tests.DynamicDocument { base.Initialize(); - DynamicPublishedContentDataSourceResolver.Current = new DynamicPublishedContentDataSourceResolver( - new TestDynamicPublishedContentDataSource()); + + } public override void TearDown() { base.TearDown(); - DynamicPublishedContentDataSourceResolver.Reset(); + } protected override bool RequiresDbSetup @@ -32,20 +32,6 @@ namespace Umbraco.Tests.DynamicDocument get { return false; } } - private class TestDynamicPublishedContentDataSource : IDynamicPublishedContentDataSource - { - public Guid GetDataType(string docTypeAlias, string propertyAlias) - { - if (propertyAlias == "content") - { - //return the rte type id - return Guid.Parse("5e9b75ae-face-41c8-b47e-5f4b0fd82f83"); - } - - - return Guid.Empty; - } - } /// /// Returns the dynamic node/document to run tests against diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicNodeTests.cs b/src/Umbraco.Tests/DynamicDocument/DynamicNodeTests.cs index f04152a73f..360af99c14 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicNodeTests.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicNodeTests.cs @@ -1,9 +1,11 @@ +using System; using System.IO; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Tests.TestHelpers; using Umbraco.Web; +using Umbraco.Web.Models; using umbraco.BusinessLogic; using umbraco.IO; using umbraco.MacroEngines; @@ -44,6 +46,17 @@ namespace Umbraco.Tests.DynamicDocument typeof(DynamicNode).Assembly }; + //need to specify a custom callback for unit tests + DynamicNode.GetDataTypeCallback = (docTypeAlias, propertyAlias) => + { + if (propertyAlias == "content") + { + //return the rte type id + return Guid.Parse("5e9b75ae-face-41c8-b47e-5f4b0fd82f83"); + } + return Guid.Empty; + }; + } public override void TearDown() diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs index 115497b366..ba637192b8 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs @@ -1,22 +1,23 @@ using System.Collections.Generic; using Umbraco.Core.Dynamics; +using Umbraco.Core.Models; namespace Umbraco.Tests.DynamicDocument { public static class DynamicPublishedContentCustomExtensionMethods { - public static string DynamicDocumentNoParameters(this Core.Dynamics.DynamicPublishedContent doc) + public static string DynamicDocumentNoParameters(this DynamicPublishedContentBase doc) { return "Hello world"; } - public static string DynamicDocumentCustomString(this Core.Dynamics.DynamicPublishedContent doc, string custom) + public static string DynamicDocumentCustomString(this DynamicPublishedContentBase doc, string custom) { return custom; } - public static string DynamicDocumentMultiParam(this Core.Dynamics.DynamicPublishedContent doc, string custom, int i, bool b) + public static string DynamicDocumentMultiParam(this DynamicPublishedContentBase doc, string custom, int i, bool b) { return custom + i + b; } @@ -26,7 +27,7 @@ namespace Umbraco.Tests.DynamicDocument return custom + i + b; } - public static string DynamicDocumentEnumerableMultiParam(this IEnumerable doc, string custom, int i, bool b) + public static string DynamicDocumentEnumerableMultiParam(this IEnumerable doc, string custom, int i, bool b) { return custom + i + b; } diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs index 837920d1c0..fbf802c2ea 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs +++ b/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using NUnit.Framework; using Umbraco.Core.Dynamics; @@ -5,6 +6,7 @@ using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.Routing; using Umbraco.Web; +using Umbraco.Web.Models; using Umbraco.Web.Routing; using umbraco.BusinessLogic; using umbraco.cms.businesslogic; @@ -14,7 +16,7 @@ using umbraco.cms.businesslogic.web; namespace Umbraco.Tests.DynamicDocument { [TestFixture] - public class DynamicPublishedContentTests : DynamicDocumentTestsBase + public class DynamicPublishedContentTests : DynamicDocumentTestsBase { public override void Initialize() { @@ -27,6 +29,17 @@ namespace Umbraco.Tests.DynamicDocument typeof(TinyMcePropertyEditorValueConverter), typeof(YesNoPropertyEditorValueConverter) }); + + //need to specify a custom callback for unit tests + DynamicPublishedContent.GetDataTypeCallback = (docTypeAlias, propertyAlias) => + { + if (propertyAlias == "content") + { + //return the rte type id + return Guid.Parse("5e9b75ae-face-41c8-b47e-5f4b0fd82f83"); + } + return Guid.Empty; + }; } public override void TearDown() @@ -36,7 +49,7 @@ namespace Umbraco.Tests.DynamicDocument PropertyEditorValueConvertersResolver.Reset(); } - internal Core.Dynamics.DynamicPublishedContent GetNode(int id) + internal DynamicPublishedContent GetNode(int id) { //var template = Template.MakeNew("test", new User(0)); //var ctx = GetUmbracoContext("/test", template.Id); @@ -44,7 +57,7 @@ namespace Umbraco.Tests.DynamicDocument var contentStore = new DefaultPublishedContentStore(); var doc = contentStore.GetDocumentById(ctx, id); Assert.IsNotNull(doc); - var dynamicNode = new Core.Dynamics.DynamicPublishedContent(doc); + var dynamicNode = new DynamicPublishedContent(doc); Assert.IsNotNull(dynamicNode); return dynamicNode; } @@ -99,9 +112,9 @@ namespace Umbraco.Tests.DynamicDocument /// public class TestHelper { - private readonly Core.Dynamics.DynamicPublishedContent _doc; + private readonly DynamicPublishedContentBase _doc; - public TestHelper(Core.Dynamics.DynamicPublishedContent doc) + public TestHelper(DynamicPublishedContentBase doc) { _doc = doc; } diff --git a/src/Umbraco.Tests/Routing/LookupByNiceUrlWithDomainsTests.cs b/src/Umbraco.Tests/Routing/LookupByNiceUrlWithDomainsTests.cs index c64247585c..0ab44db50b 100644 --- a/src/Umbraco.Tests/Routing/LookupByNiceUrlWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/LookupByNiceUrlWithDomainsTests.cs @@ -14,22 +14,18 @@ namespace Umbraco.Tests.Routing [TestFixture] public class LookupByNiceUrlWithDomainsTests : BaseRoutingTest { + public override void Initialize() + { + base.Initialize(); + InitializeLanguagesAndDomains(); + } + public override void TearDown() { base.TearDown(); - ClearLanguagesAndDomains(); + } - void ClearLanguagesAndDomains() - { - var domains = Domain.GetDomains(); - foreach (var d in domains) - d.Delete(); - - var langs = Language.GetAllAsList(); - foreach (var l in langs.Skip(1)) - l.Delete(); - } void InitializeLanguagesAndDomains() { @@ -155,7 +151,6 @@ namespace Umbraco.Tests.Routing public void Lookup_SingleDomain(string url, int expectedId) { - InitializeLanguagesAndDomains(); SetDomains3(); ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", "true"); @@ -195,7 +190,6 @@ namespace Umbraco.Tests.Routing public void Lookup_NestedDomains(string url, int expectedId, string expectedCulture) { - InitializeLanguagesAndDomains(); SetDomains4(); ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", "true"); diff --git a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs index 50434a1ce8..74bb9954fa 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs @@ -20,8 +20,6 @@ namespace Umbraco.Tests.Routing ConfigurationManager.AppSettings.Set("umbracoUseDirectoryUrls", ""); ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", ""); - - ClearLanguagesAndDomains(); } internal override IRoutesCache GetRoutesCache() @@ -29,16 +27,6 @@ namespace Umbraco.Tests.Routing return new DefaultRoutesCache(false); } - void ClearLanguagesAndDomains() - { - var domains = Domain.GetDomains(); - foreach (var d in domains) - d.Delete(); - - var langs = Language.GetAllAsList(); - foreach (var l in langs.Skip(1)) - l.Delete(); - } void InitializeLanguagesAndDomains() { diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index 1b2bf2d8ad..7f4b68d2bd 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -71,7 +71,6 @@ namespace Umbraco.Tests.Routing ConfigurationManager.AppSettings.Set("umbracoUseDirectoryUrls", ""); ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", ""); - ClearLanguagesAndDomains(); } internal override IRoutesCache GetRoutesCache() @@ -79,16 +78,6 @@ namespace Umbraco.Tests.Routing return new DefaultRoutesCache(false); } - void ClearLanguagesAndDomains() - { - var domains = Domain.GetDomains(); - foreach (var d in domains) - d.Delete(); - - var langs = Language.GetAllAsList(); - foreach (var l in langs.Skip(1)) - l.Delete(); - } void InitializeLanguagesAndDomains() { diff --git a/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs b/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs index 2cf43ac056..008c73e4c7 100644 --- a/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs +++ b/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs @@ -3,39 +3,62 @@ using System.Collections.Generic; using System.Linq; using System.Text; using NUnit.Framework; +using Umbraco.Tests.Stubs; using Umbraco.Tests.TestHelpers; using System.Configuration; +using Umbraco.Web; using Umbraco.Web.Routing; +using umbraco.BusinessLogic; +using umbraco.cms.businesslogic.template; namespace Umbraco.Tests.Routing { [TestFixture] public class uQueryGetNodeIdByUrlTests : BaseRoutingTest { - protected RoutingContext GetRoutingContext() + public override void TearDown() { - var url = "/test"; - var templateId = 1111; + base.TearDown(); + ConfigurationManager.AppSettings.Set("umbracoUseDirectoryUrls", ""); + ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", ""); + } + + internal override IRoutesCache GetRoutesCache() + { + return new DefaultRoutesCache(false); + } + + public override void Initialize() + { + base.Initialize(); + + var url = "/test"; + var lookup = new Umbraco.Web.Routing.LookupByNiceUrl(); var lookups = new Umbraco.Web.Routing.IPublishedContentLookup[] { lookup }; - var umbracoContext = GetUmbracoContext(url, templateId, null); - var contentStore = new Umbraco.Web.DefaultPublishedContentStore(); + var t = Template.MakeNew("test", new User(0)); + + var umbracoContext = GetUmbracoContext(url, t.Id); + var contentStore = new DefaultPublishedContentStore(); var niceUrls = new NiceUrlProvider(contentStore, umbracoContext); var routingContext = new RoutingContext( umbracoContext, lookups, - new Umbraco.Tests.Stubs.FakeLastChanceLookup(), + new FakeLastChanceLookup(), contentStore, niceUrls); //assign the routing context back to the umbraco context umbracoContext.RoutingContext = routingContext; - return routingContext; + ////assign the routing context back to the umbraco context + //umbracoContext.RoutingContext = routingContext; + Umbraco.Web.UmbracoContext.Current = routingContext.UmbracoContext; } + [TestCase(1046, "/home")] [TestCase(1173, "/home/sub1")] [TestCase(1174, "/home/sub1/sub2")] @@ -47,9 +70,7 @@ namespace Umbraco.Tests.Routing public void GetNodeIdByUrl_Not_Hiding_Top_Level_Absolute(int nodeId, string url) { - var routingContext = GetRoutingContext(); - Umbraco.Web.UmbracoContext.Current = routingContext.UmbracoContext; - + ConfigurationManager.AppSettings.Set("umbracoUseDirectoryUrls", "true"); ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", "false"); Umbraco.Core.Configuration.UmbracoSettings.UseDomainPrefixes = false; @@ -68,9 +89,6 @@ namespace Umbraco.Tests.Routing public void GetNodeIdByUrl_Not_Hiding_Top_Level_Relative(int nodeId, string url) { - var routingContext = GetRoutingContext(); - Umbraco.Web.UmbracoContext.Current = routingContext.UmbracoContext; - ConfigurationManager.AppSettings.Set("umbracoUseDirectoryUrls", "true"); ConfigurationManager.AppSettings.Set("umbracoHideTopLevelNodeFromPath", "false"); Umbraco.Core.Configuration.UmbracoSettings.UseDomainPrefixes = false; diff --git a/src/Umbraco.Web/DefaultDynamicPublishedContentDataSource.cs b/src/Umbraco.Web/DefaultDynamicPublishedContentDataSource.cs deleted file mode 100644 index 61300cac77..0000000000 --- a/src/Umbraco.Web/DefaultDynamicPublishedContentDataSource.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core.Dynamics; -using umbraco.cms.businesslogic; - -namespace Umbraco.Web -{ - - /// - /// This exists only because we want Dynamics in the Core project but DynamicNode has references to ContentType to run some queries - /// and currently the business logic part of Umbraco is still in the legacy project and we don't want to move that to the core so in the - /// meantime until the new APIs are made, we need to have this data source in place with a resolver which is set in the web project. - /// - internal class DefaultDynamicPublishedContentDataSource : IDynamicPublishedContentDataSource - { - public Guid GetDataType(string docTypeAlias, string propertyAlias) - { - return ContentType.GetDataType(docTypeAlias, propertyAlias); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/DefaultPublishedContentStore.cs b/src/Umbraco.Web/DefaultPublishedContentStore.cs index d5c0c1cd31..b5ce8ac2ff 100644 --- a/src/Umbraco.Web/DefaultPublishedContentStore.cs +++ b/src/Umbraco.Web/DefaultPublishedContentStore.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Text; using System.Xml; using System.Xml.Linq; @@ -32,6 +33,11 @@ namespace Umbraco.Web return ConvertToDocument(GetXml(umbracoContext).GetElementById(nodeId.ToString())); } + public IEnumerable GetRootDocuments(UmbracoContext umbracoContext) + { + return (from XmlNode x in GetXml(umbracoContext).SelectNodes("/root/*[@isDoc]") select ConvertToDocument(x)).ToList(); + } + public IPublishedContent GetDocumentByRoute(UmbracoContext umbracoContext, string route, bool? hideTopLevelNode = null) { if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); diff --git a/src/Umbraco.Web/DefaultPublishedMediaStore.cs b/src/Umbraco.Web/DefaultPublishedMediaStore.cs index 344964da6e..ab43387283 100644 --- a/src/Umbraco.Web/DefaultPublishedMediaStore.cs +++ b/src/Umbraco.Web/DefaultPublishedMediaStore.cs @@ -26,6 +26,21 @@ namespace Umbraco.Web return GetUmbracoMedia(nodeId); } + public IEnumerable GetRootDocuments(UmbracoContext umbracoContext) + { + var rootMedia = global::umbraco.cms.businesslogic.media.Media.GetRootMedias(); + var result = new List(); + //TODO: need to get a ConvertFromMedia method but we'll just use this for now. + foreach (var media in rootMedia + .Select(m => global::umbraco.library.GetMedia(m.Id, true)) + .Where(media => media != null && media.Current != null)) + { + media.MoveNext(); + result.Add(ConvertFromXPathNavigator(media.Current)); + } + return result; + } + private IPublishedContent GetUmbracoMedia(int id) { diff --git a/src/Umbraco.Web/DynamicPublishedContentSearchExtensions.cs b/src/Umbraco.Web/DynamicPublishedContentSearchExtensions.cs index 169a666e37..173c5aab76 100644 --- a/src/Umbraco.Web/DynamicPublishedContentSearchExtensions.cs +++ b/src/Umbraco.Web/DynamicPublishedContentSearchExtensions.cs @@ -1,5 +1,6 @@ using Examine.LuceneEngine.SearchCriteria; using Umbraco.Core.Dynamics; +using Umbraco.Core.Models; namespace Umbraco.Web { @@ -8,7 +9,7 @@ namespace Umbraco.Web /// public static class DynamicPublishedContentSearchExtensions { - public static DynamicPublishedContentList Search(this DynamicPublishedContent d, string term, bool useWildCards = true, string searchProvider = null) + public static DynamicPublishedContentList Search(this DynamicPublishedContentBase d, string term, bool useWildCards = true, string searchProvider = null) { var searcher = Examine.ExamineManager.Instance.DefaultSearchProvider; if (!string.IsNullOrEmpty(searchProvider)) @@ -24,12 +25,12 @@ namespace Umbraco.Web return d.Search(crit, searcher); } - public static DynamicPublishedContentList SearchDescendants(this DynamicPublishedContent d, string term, bool useWildCards = true, string searchProvider = null) + public static DynamicPublishedContentList SearchDescendants(this DynamicPublishedContentBase d, string term, bool useWildCards = true, string searchProvider = null) { return d.Search(term, useWildCards, searchProvider); } - public static DynamicPublishedContentList SearchChildren(this DynamicPublishedContent d, string term, bool useWildCards = true, string searchProvider = null) + public static DynamicPublishedContentList SearchChildren(this DynamicPublishedContentBase d, string term, bool useWildCards = true, string searchProvider = null) { var searcher = Examine.ExamineManager.Instance.DefaultSearchProvider; if (!string.IsNullOrEmpty(searchProvider)) @@ -45,7 +46,7 @@ namespace Umbraco.Web return d.Search(crit, searcher); } - public static DynamicPublishedContentList Search(this DynamicPublishedContent d, Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) + public static DynamicPublishedContentList Search(this DynamicPublishedContentBase d, Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) { var s = Examine.ExamineManager.Instance.DefaultSearchProvider; if (searchProvider != null) diff --git a/src/Umbraco.Web/ExamineExtensions.cs b/src/Umbraco.Web/ExamineExtensions.cs index 8898b491ff..3f63345116 100644 --- a/src/Umbraco.Web/ExamineExtensions.cs +++ b/src/Umbraco.Web/ExamineExtensions.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Xml; using Examine; using Umbraco.Core.Dynamics; +using Umbraco.Core.Models; namespace Umbraco.Web { @@ -31,7 +32,7 @@ namespace Umbraco.Web if (doc == null) continue; //skip if this doesn't exist in the cache doc.Properties.Add( new PropertyResult("examineScore", result.Score.ToString(), Guid.Empty, PropertyResultType.CustomProperty)); - var dynamicDoc = new DynamicPublishedContent(doc); + var dynamicDoc = new DynamicPublishedContentBase(doc); list.Add(dynamicDoc); } return list; diff --git a/src/Umbraco.Web/IPublishedStore.cs b/src/Umbraco.Web/IPublishedStore.cs index dd2cc1031a..0eae294911 100644 --- a/src/Umbraco.Web/IPublishedStore.cs +++ b/src/Umbraco.Web/IPublishedStore.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Umbraco.Core.Models; namespace Umbraco.Web @@ -8,5 +9,6 @@ namespace Umbraco.Web public interface IPublishedStore { IPublishedContent GetDocumentById(UmbracoContext umbracoContext, int nodeId); + IEnumerable GetRootDocuments(UmbracoContext umbracoContext); } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/DynamicPublishedContent.cs b/src/Umbraco.Web/Models/DynamicPublishedContent.cs new file mode 100644 index 0000000000..c0065823d8 --- /dev/null +++ b/src/Umbraco.Web/Models/DynamicPublishedContent.cs @@ -0,0 +1,545 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Web; +using Umbraco.Core.Configuration; +using Umbraco.Core.Dynamics; +using Umbraco.Core.Models; +using Umbraco.Core; +using Umbraco.Core.PropertyEditors; +using System.Reflection; +using System.Xml.Linq; +using umbraco.cms.businesslogic; + +namespace Umbraco.Web.Models +{ + + /// + /// The base dynamic model for views + /// + public class DynamicPublishedContent : DynamicPublishedContentBase + { + /// + /// This callback is used only so we can set it dynamically for use in unit tests + /// + internal static Func GetDataTypeCallback = (docTypeAlias, propertyAlias) => + ContentType.GetDataType(docTypeAlias, propertyAlias); + + public DynamicPublishedContent(IPublishedContent node) + : base(node) + { + } + + /// + /// overriden method which uses PropertyEditorValueConverters to convert the resulting value + /// + /// + /// + protected override Attempt TryGetUserProperty(GetMemberBinder binder) + { + var name = binder.Name; + var recursive = false; + if (name.StartsWith("_")) + { + name = name.Substring(1, name.Length - 1); + recursive = true; + } + + var userProperty = GetUserProperty(name, recursive); + + if (userProperty == null) + { + return Attempt.False; + } + + var result = userProperty.Value; + + if (PublishedContent.DocumentTypeAlias == null && userProperty.Alias == null) + { + throw new InvalidOperationException("No node alias or property alias available. Unable to look up the datatype of the property you are trying to fetch."); + } + + //get the data type id for the current property + var dataType = GetDataType(userProperty.DocumentTypeAlias, userProperty.Alias); + + //convert the string value to a known type + var converted = ConvertPropertyValue(result, dataType, userProperty.DocumentTypeAlias, userProperty.Alias); + if (converted.Success) + { + result = converted.Result; + } + + return new Attempt(true, result); + } + + private static Guid GetDataType(string docTypeAlias, string propertyAlias) + { + return GetDataTypeCallback(docTypeAlias, propertyAlias); + } + + /// + /// Converts the currentValue to a correctly typed value based on known registered converters, then based on known standards. + /// + /// + /// + /// + /// + /// + private Attempt ConvertPropertyValue(object currentValue, Guid dataType, string docTypeAlias, string propertyTypeAlias) + { + if (currentValue == null) return Attempt.False; + + //First lets check all registered converters for this data type. + var converters = PropertyEditorValueConvertersResolver.Current.Converters + .Where(x => x.IsConverterFor(dataType, docTypeAlias, propertyTypeAlias)) + .ToArray(); + + //try to convert the value with any of the converters: + foreach (var converted in converters + .Select(p => p.ConvertPropertyValue(currentValue)) + .Where(converted => converted.Success)) + { + return new Attempt(true, converted.Result); + } + + //if none of the converters worked, then we'll process this from what we know + + var sResult = Convert.ToString(currentValue).Trim(); + + //this will eat csv strings, so only do it if the decimal also includes a decimal seperator (according to the current culture) + if (sResult.Contains(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator)) + { + decimal dResult; + if (decimal.TryParse(sResult, System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.CurrentCulture, out dResult)) + { + return new Attempt(true, dResult); + } + } + //process string booleans as booleans + if (sResult.InvariantEquals("true")) + { + return new Attempt(true, true); + } + if (sResult.InvariantEquals("false")) + { + return new Attempt(true, false); + } + + //a really rough check to see if this may be valid xml + //TODO: This is legacy code, I'm sure there's a better and nicer way + if (sResult.StartsWith("<") && sResult.EndsWith(">") && sResult.Contains("/")) + { + try + { + var e = XElement.Parse(DynamicXml.StripDashesInElementOrAttributeNames(sResult), LoadOptions.None); + + //check that the document element is not one of the disallowed elements + //allows RTE to still return as html if it's valid xhtml + var documentElement = e.Name.LocalName; + + //TODO: See note against this setting, pretty sure we don't need this + if (!UmbracoSettings.NotDynamicXmlDocumentElements.Any( + tag => string.Equals(tag, documentElement, StringComparison.CurrentCultureIgnoreCase))) + { + return new Attempt(true, new DynamicXml(e)); + } + return Attempt.False; + } + catch (Exception) + { + return Attempt.False; + } + } + return Attempt.False; + } + + #region Index/Position + public int Position() + { + return Umbraco.Web.PublishedContentExtensions.Position(this); + } + public int Index() + { + return Umbraco.Web.PublishedContentExtensions.Index(this); + } + #endregion + + #region Is Helpers + public bool IsNull(string alias, bool recursive) + { + return this.PublishedContent.IsNull(alias, recursive); + } + public bool IsNull(string alias) + { + return this.PublishedContent.IsNull(alias, false); + } + public bool IsFirst() + { + return this.PublishedContent.IsFirst(); + } + public HtmlString IsFirst(string valueIfTrue) + { + return this.PublishedContent.IsFirst(valueIfTrue); + } + public HtmlString IsFirst(string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsFirst(valueIfTrue, valueIfFalse); + } + public bool IsNotFirst() + { + return this.PublishedContent.IsNotFirst(); + } + public HtmlString IsNotFirst(string valueIfTrue) + { + return this.PublishedContent.IsNotFirst(valueIfTrue); + } + public HtmlString IsNotFirst(string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsNotFirst(valueIfTrue, valueIfFalse); + } + public bool IsPosition(int index) + { + return this.PublishedContent.IsPosition(index); + } + public HtmlString IsPosition(int index, string valueIfTrue) + { + return this.PublishedContent.IsPosition(index, valueIfTrue); + } + public HtmlString IsPosition(int index, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsPosition(index, valueIfTrue, valueIfFalse); + } + public bool IsModZero(int modulus) + { + return this.PublishedContent.IsModZero(modulus); + } + public HtmlString IsModZero(int modulus, string valueIfTrue) + { + return this.PublishedContent.IsModZero(modulus, valueIfTrue); + } + public HtmlString IsModZero(int modulus, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsModZero(modulus, valueIfTrue, valueIfFalse); + } + + public bool IsNotModZero(int modulus) + { + return this.PublishedContent.IsNotModZero(modulus); + } + public HtmlString IsNotModZero(int modulus, string valueIfTrue) + { + return this.PublishedContent.IsNotModZero(modulus, valueIfTrue); + } + public HtmlString IsNotModZero(int modulus, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsNotModZero(modulus, valueIfTrue, valueIfFalse); + } + public bool IsNotPosition(int index) + { + return this.PublishedContent.IsNotPosition(index); + } + public HtmlString IsNotPosition(int index, string valueIfTrue) + { + return this.PublishedContent.IsNotPosition(index, valueIfTrue); + } + public HtmlString IsNotPosition(int index, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsNotPosition(index, valueIfTrue, valueIfFalse); + } + public bool IsLast() + { + return this.PublishedContent.IsLast(); + } + public HtmlString IsLast(string valueIfTrue) + { + return this.PublishedContent.IsLast(valueIfTrue); + } + public HtmlString IsLast(string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsLast(valueIfTrue, valueIfFalse); + } + public bool IsNotLast() + { + return this.PublishedContent.IsNotLast(); + } + public HtmlString IsNotLast(string valueIfTrue) + { + return this.PublishedContent.IsNotLast(valueIfTrue); + } + public HtmlString IsNotLast(string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsNotLast(valueIfTrue, valueIfFalse); + } + public bool IsEven() + { + return this.PublishedContent.IsEven(); + } + public HtmlString IsEven(string valueIfTrue) + { + return this.PublishedContent.IsEven(valueIfTrue); + } + public HtmlString IsEven(string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsEven(valueIfTrue, valueIfFalse); + } + public bool IsOdd() + { + return this.PublishedContent.IsOdd(); + } + public HtmlString IsOdd(string valueIfTrue) + { + return this.PublishedContent.IsOdd(valueIfTrue); + } + public HtmlString IsOdd(string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsOdd(valueIfTrue, valueIfFalse); + } + public bool IsEqual(DynamicPublishedContentBase other) + { + return this.PublishedContent.IsEqual(other); + } + public HtmlString IsEqual(DynamicPublishedContentBase other, string valueIfTrue) + { + return this.PublishedContent.IsEqual(other, valueIfTrue); + } + public HtmlString IsEqual(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsEqual(other, valueIfTrue, valueIfFalse); + } + public bool IsNotEqual(DynamicPublishedContentBase other) + { + return this.PublishedContent.IsNotEqual(other); + } + public HtmlString IsNotEqual(DynamicPublishedContentBase other, string valueIfTrue) + { + return this.PublishedContent.IsNotEqual(other, valueIfTrue); + } + public HtmlString IsNotEqual(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsNotEqual(other, valueIfTrue, valueIfFalse); + } + public bool IsDescendant(DynamicPublishedContentBase other) + { + return this.PublishedContent.IsDescendant(other); + } + public HtmlString IsDescendant(DynamicPublishedContentBase other, string valueIfTrue) + { + return this.PublishedContent.IsDescendant(other, valueIfTrue); + } + public HtmlString IsDescendant(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsDescendant(other, valueIfTrue, valueIfFalse); + } + public bool IsDescendantOrSelf(DynamicPublishedContentBase other) + { + return this.PublishedContent.IsDescendantOrSelf(other); + } + public HtmlString IsDescendantOrSelf(DynamicPublishedContentBase other, string valueIfTrue) + { + return this.PublishedContent.IsDescendantOrSelf(other, valueIfTrue); + } + public HtmlString IsDescendantOrSelf(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsDescendantOrSelf(other, valueIfTrue, valueIfFalse); + } + public bool IsAncestor(DynamicPublishedContentBase other) + { + return this.PublishedContent.IsAncestor(other); + } + public HtmlString IsAncestor(DynamicPublishedContentBase other, string valueIfTrue) + { + return this.PublishedContent.IsAncestor(other, valueIfTrue); + } + public HtmlString IsAncestor(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsAncestor(other, valueIfTrue, valueIfFalse); + } + public bool IsAncestorOrSelf(DynamicPublishedContentBase other) + { + return this.PublishedContent.IsAncestorOrSelf(other); + } + public HtmlString IsAncestorOrSelf(DynamicPublishedContentBase other, string valueIfTrue) + { + return this.PublishedContent.IsAncestorOrSelf(other, valueIfTrue); + } + public HtmlString IsAncestorOrSelf(DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + return this.PublishedContent.IsAncestorOrSelf(other, valueIfTrue, valueIfFalse); + } + #endregion + + #region Traversal + public DynamicPublishedContent Up() + { + return Umbraco.Web.PublishedContentExtensions.Up(this).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Up(int number) + { + return Umbraco.Web.PublishedContentExtensions.Up(this, number).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Up(string nodeTypeAlias) + { + return Umbraco.Web.PublishedContentExtensions.Up(this, nodeTypeAlias).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Down() + { + return Umbraco.Web.PublishedContentExtensions.Down(this).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Down(int number) + { + return Umbraco.Web.PublishedContentExtensions.Down(this, number).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Down(string nodeTypeAlias) + { + return Umbraco.Web.PublishedContentExtensions.Down(this, nodeTypeAlias).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Next() + { + return Umbraco.Web.PublishedContentExtensions.Next(this).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Next(int number) + { + return Umbraco.Web.PublishedContentExtensions.Next(this, number).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Next(string nodeTypeAlias) + { + return Umbraco.Web.PublishedContentExtensions.Next(this, nodeTypeAlias).AsDynamicPublishedContent(); + } + + public DynamicPublishedContent Previous() + { + return Umbraco.Web.PublishedContentExtensions.Previous(this).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Previous(int number) + { + return Umbraco.Web.PublishedContentExtensions.Previous(this, number).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Previous(string nodeTypeAlias) + { + return Umbraco.Web.PublishedContentExtensions.Previous(this, nodeTypeAlias).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Sibling(int number) + { + return Umbraco.Web.PublishedContentExtensions.Previous(this, number).AsDynamicPublishedContent(); + } + public DynamicPublishedContent Sibling(string nodeTypeAlias) + { + return Umbraco.Web.PublishedContentExtensions.Previous(this, nodeTypeAlias).AsDynamicPublishedContent(); + } + #endregion + + #region Ancestors, Descendants and Parent + #region Ancestors + public DynamicPublishedContentList Ancestors(int level) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.Ancestors(this, level)); + } + public DynamicPublishedContentList Ancestors(string nodeTypeAlias) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.Ancestors(this, nodeTypeAlias)); + } + public DynamicPublishedContentList Ancestors() + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.Ancestors(this)); + } + public DynamicPublishedContentList Ancestors(Func func) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.Ancestors(this, func)); + } + public DynamicPublishedContent AncestorOrSelf() + { + return Umbraco.Web.PublishedContentExtensions.AncestorOrSelf(this).AsDynamicPublishedContent(); + } + public DynamicPublishedContent AncestorOrSelf(int level) + { + return Umbraco.Web.PublishedContentExtensions.AncestorOrSelf(this, level).AsDynamicPublishedContent(); + } + public DynamicPublishedContent AncestorOrSelf(string nodeTypeAlias) + { + return Umbraco.Web.PublishedContentExtensions.AncestorOrSelf(this, nodeTypeAlias).AsDynamicPublishedContent(); + } + public DynamicPublishedContent AncestorOrSelf(Func func) + { + return Umbraco.Web.PublishedContentExtensions.AncestorOrSelf(this, func).AsDynamicPublishedContent(); + } + public DynamicPublishedContentList AncestorsOrSelf(Func func) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.AncestorsOrSelf(this, func)); + } + public DynamicPublishedContentList AncestorsOrSelf() + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.AncestorsOrSelf(this)); + } + public DynamicPublishedContentList AncestorsOrSelf(string nodeTypeAlias) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.AncestorsOrSelf(this, nodeTypeAlias)); + } + public DynamicPublishedContentList AncestorsOrSelf(int level) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.AncestorsOrSelf(this, level)); + } + #endregion + #region Descendants + public DynamicPublishedContentList Descendants(string nodeTypeAlias) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.Descendants(this, nodeTypeAlias)); + } + public DynamicPublishedContentList Descendants(int level) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.Descendants(this, level)); + } + public DynamicPublishedContentList Descendants() + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.Descendants(this)); + } + public DynamicPublishedContentList DescendantsOrSelf(int level) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.DescendantsOrSelf(this, level)); + } + public DynamicPublishedContentList DescendantsOrSelf(string nodeTypeAlias) + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.DescendantsOrSelf(this, nodeTypeAlias)); + } + public DynamicPublishedContentList DescendantsOrSelf() + { + return new DynamicPublishedContentList( + Umbraco.Web.PublishedContentExtensions.DescendantsOrSelf(this)); + } + #endregion + + public DynamicPublishedContent Parent + { + get + { + if (PublishedContent.Parent != null) + { + return PublishedContent.Parent.AsDynamicPublishedContent(); + } + if (PublishedContent != null && PublishedContent.Id == 0) + { + return this; + } + return null; + } + } + + #endregion + + + } +} diff --git a/src/Umbraco.Web/Mvc/RenderViewPage.cs b/src/Umbraco.Web/Mvc/RenderViewPage.cs index 0128220a7b..00bb93fb9a 100644 --- a/src/Umbraco.Web/Mvc/RenderViewPage.cs +++ b/src/Umbraco.Web/Mvc/RenderViewPage.cs @@ -2,6 +2,7 @@ using System.Web.Mvc; using Umbraco.Core; using Umbraco.Core.Dictionary; using Umbraco.Core.Dynamics; +using Umbraco.Core.Models; using Umbraco.Web.Routing; namespace Umbraco.Web.Mvc @@ -23,7 +24,7 @@ namespace Umbraco.Web.Mvc { ////this.ViewData.Model = Model; //var backingItem = new DynamicBackingItem(Model.CurrentNode); - var dynamicNode = new DynamicPublishedContent(Model.CurrentContent); + var dynamicNode = new DynamicPublishedContentBase(Model.CurrentContent); CurrentPage = dynamicNode.AsDynamic(); } PublishedContentRequest = (PublishedContentRequest)ViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-doc-request"); diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index bf05cde8a0..1626346c13 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -2,7 +2,10 @@ using System; using System.Collections.Generic; using System.Data; using System.Linq; +using System.Web; +using Umbraco.Core.Dynamics; using Umbraco.Core.Models; +using Umbraco.Web.Models; using Umbraco.Web.Routing; using umbraco.cms.businesslogic; using Umbraco.Core; @@ -17,7 +20,672 @@ namespace Umbraco.Web /// which is why they cannot exist in the Core project. /// public static class PublishedContentExtensions - { + { + + /// + /// Converts a IPublishedContent to a DynamicPublishedContent and tests for null + /// + /// + /// + internal static DynamicPublishedContent AsDynamicPublishedContent(this IPublishedContent content) + { + if (content == null) + return null; + return new DynamicPublishedContent(content); + } + + public static IDocumentProperty GetProperty(this IPublishedContent content, string alias, bool recursive) + { + return content.GetUserRecursive(alias, recursive); + } + + private static IDocumentProperty GetUserRecursive(this IPublishedContent content, string alias, bool recursive = false) + { + if (!recursive) + { + return content.GetProperty(alias); + } + var context = content; + var prop = content.GetUserRecursive(alias); + while (prop == null || prop.Value == null || prop.Value.ToString().IsNullOrWhiteSpace()) + { + var parent = context.Parent; + if (parent == null) break; + prop = context.GetUserRecursive(alias); + } + return prop; + } + + #region Position/Index + public static int Position(this IPublishedContent content) + { + return content.Index(); + } + public static int Index(this IPublishedContent content) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + + var container = ownersList.ToList(); + int currentIndex = container.FindIndex(n => n.Id == content.Id); + if (currentIndex != -1) + { + return currentIndex; + } + else + { + throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicDocumentList but could not retrieve the index for it's position in the list", content.Id)); + } + } + #endregion + + #region Is Helpers + + public static bool IsNull(this IPublishedContent content, string alias, bool recursive) + { + var prop = content.GetProperty(alias, recursive); + if (prop == null) return true; + return ((PropertyResult)prop).HasValue(); + } + public static bool IsNull(this IPublishedContent content, string alias) + { + return content.IsNull(alias, false); + } + public static bool IsFirst(this IPublishedContent content) + { + return content.IsHelper(n => n.Index() == 0); + } + public static HtmlString IsFirst(this IPublishedContent content, string valueIfTrue) + { + return content.IsHelper(n => n.Index() == 0, valueIfTrue); + } + public static HtmlString IsFirst(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() == 0, valueIfTrue, valueIfFalse); + } + public static bool IsNotFirst(this IPublishedContent content) + { + return !content.IsHelper(n => n.Index() == 0); + } + public static HtmlString IsNotFirst(this IPublishedContent content, string valueIfTrue) + { + return content.IsHelper(n => n.Index() != 0, valueIfTrue); + } + public static HtmlString IsNotFirst(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() != 0, valueIfTrue, valueIfFalse); + } + public static bool IsPosition(this IPublishedContent content, int index) + { + return content.IsHelper(n => n.Index() == index); + } + public static HtmlString IsPosition(this IPublishedContent content, int index, string valueIfTrue) + { + return content.IsHelper(n => n.Index() == index, valueIfTrue); + } + public static HtmlString IsPosition(this IPublishedContent content, int index, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() == index, valueIfTrue, valueIfFalse); + } + public static bool IsModZero(this IPublishedContent content, int modulus) + { + return content.IsHelper(n => n.Index() % modulus == 0); + } + public static HtmlString IsModZero(this IPublishedContent content, int modulus, string valueIfTrue) + { + return content.IsHelper(n => n.Index() % modulus == 0, valueIfTrue); + } + public static HtmlString IsModZero(this IPublishedContent content, int modulus, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() % modulus == 0, valueIfTrue, valueIfFalse); + } + + public static bool IsNotModZero(this IPublishedContent content, int modulus) + { + return content.IsHelper(n => n.Index() % modulus != 0); + } + public static HtmlString IsNotModZero(this IPublishedContent content, int modulus, string valueIfTrue) + { + return content.IsHelper(n => n.Index() % modulus != 0, valueIfTrue); + } + public static HtmlString IsNotModZero(this IPublishedContent content, int modulus, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() % modulus != 0, valueIfTrue, valueIfFalse); + } + public static bool IsNotPosition(this IPublishedContent content, int index) + { + return !content.IsHelper(n => n.Index() == index); + } + public static HtmlString IsNotPosition(this IPublishedContent content, int index, string valueIfTrue) + { + return content.IsHelper(n => n.Index() != index, valueIfTrue); + } + public static HtmlString IsNotPosition(this IPublishedContent content, int index, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() != index, valueIfTrue, valueIfFalse); + } + public static bool IsLast(this IPublishedContent content) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + var count = ownersList.Count(); + return content.IsHelper(n => n.Index() == count - 1); + } + public static HtmlString IsLast(this IPublishedContent content, string valueIfTrue) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + var count = ownersList.Count(); + return content.IsHelper(n => n.Index() == count - 1, valueIfTrue); + } + public static HtmlString IsLast(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + var count = ownersList.Count(); + return content.IsHelper(n => n.Index() == count - 1, valueIfTrue, valueIfFalse); + } + public static bool IsNotLast(this IPublishedContent content) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + var count = ownersList.Count(); + return !content.IsHelper(n => n.Index() == count - 1); + } + public static HtmlString IsNotLast(this IPublishedContent content, string valueIfTrue) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + var count = ownersList.Count(); + return content.IsHelper(n => n.Index() != count - 1, valueIfTrue); + } + public static HtmlString IsNotLast(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + var count = ownersList.Count(); + return content.IsHelper(n => n.Index() != count - 1, valueIfTrue, valueIfFalse); + } + public static bool IsEven(this IPublishedContent content) + { + return content.IsHelper(n => n.Index() % 2 == 0); + } + public static HtmlString IsEven(this IPublishedContent content, string valueIfTrue) + { + return content.IsHelper(n => n.Index() % 2 == 0, valueIfTrue); + } + public static HtmlString IsEven(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() % 2 == 0, valueIfTrue, valueIfFalse); + } + public static bool IsOdd(this IPublishedContent content) + { + return content.IsHelper(n => n.Index() % 2 == 1); + } + public static HtmlString IsOdd(this IPublishedContent content, string valueIfTrue) + { + return content.IsHelper(n => n.Index() % 2 == 1, valueIfTrue); + } + public static HtmlString IsOdd(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() % 2 == 1, valueIfTrue, valueIfFalse); + } + public static bool IsEqual(this IPublishedContent content, IPublishedContent other) + { + return content.IsHelper(n => n.Id == other.Id); + } + public static HtmlString IsEqual(this IPublishedContent content, IPublishedContent other, string valueIfTrue) + { + return content.IsHelper(n => n.Id == other.Id, valueIfTrue); + } + public static HtmlString IsEqual(this IPublishedContent content, IPublishedContent other, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Id == other.Id, valueIfTrue, valueIfFalse); + } + public static bool IsNotEqual(this IPublishedContent content, IPublishedContent other) + { + return content.IsHelper(n => n.Id != other.Id); + } + public static HtmlString IsNotEqual(this IPublishedContent content, IPublishedContent other, string valueIfTrue) + { + return content.IsHelper(n => n.Id != other.Id, valueIfTrue); + } + public static HtmlString IsNotEqual(this IPublishedContent content, IPublishedContent other, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Id != other.Id, valueIfTrue, valueIfFalse); + } + public static bool IsDescendant(this IPublishedContent content, IPublishedContent other) + { + var ancestors = content.Ancestors(); + return content.IsHelper(n => ancestors.FirstOrDefault(ancestor => ancestor.Id == other.Id) != null); + } + public static HtmlString IsDescendant(this IPublishedContent content, IPublishedContent other, string valueIfTrue) + { + var ancestors = content.Ancestors(); + return content.IsHelper(n => ancestors.FirstOrDefault(ancestor => ancestor.Id == other.Id) != null, valueIfTrue); + } + public static HtmlString IsDescendant(this IPublishedContent content, IPublishedContent other, string valueIfTrue, string valueIfFalse) + { + var ancestors = content.Ancestors(); + return content.IsHelper(n => ancestors.FirstOrDefault(ancestor => ancestor.Id == other.Id) != null, valueIfTrue, valueIfFalse); + } + public static bool IsDescendantOrSelf(this IPublishedContent content, IPublishedContent other) + { + var ancestors = content.AncestorsOrSelf(); + return content.IsHelper(n => ancestors.FirstOrDefault(ancestor => ancestor.Id == other.Id) != null); + } + public static HtmlString IsDescendantOrSelf(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue) + { + var ancestors = content.AncestorsOrSelf(); + return content.IsHelper(n => ancestors.FirstOrDefault(ancestor => ancestor.Id == other.Id) != null, valueIfTrue); + } + public static HtmlString IsDescendantOrSelf(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + var ancestors = content.AncestorsOrSelf(); + return content.IsHelper(n => ancestors.FirstOrDefault(ancestor => ancestor.Id == other.Id) != null, valueIfTrue, valueIfFalse); + } + public static bool IsAncestor(this IPublishedContent content, DynamicPublishedContentBase other) + { + var descendants = content.Descendants(); + return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null); + } + public static HtmlString IsAncestor(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue) + { + var descendants = content.Descendants(); + return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null, valueIfTrue); + } + public static HtmlString IsAncestor(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + var descendants = content.Descendants(); + return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null, valueIfTrue, valueIfFalse); + } + public static bool IsAncestorOrSelf(this IPublishedContent content, DynamicPublishedContentBase other) + { + var descendants = content.DescendantsOrSelf(); + return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null); + } + public static HtmlString IsAncestorOrSelf(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue) + { + var descendants = content.DescendantsOrSelf(); + return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null, valueIfTrue); + } + public static HtmlString IsAncestorOrSelf(this IPublishedContent content, DynamicPublishedContentBase other, string valueIfTrue, string valueIfFalse) + { + var descendants = content.DescendantsOrSelf(); + return content.IsHelper(n => descendants.FirstOrDefault(descendant => descendant.Id == other.Id) != null, valueIfTrue, valueIfFalse); + } + private static bool IsHelper(this IPublishedContent content, Func test) + { + return test(content); + } + private static HtmlString IsHelper(this IPublishedContent content, Func test, string valueIfTrue) + { + return content.IsHelper(test, valueIfTrue, string.Empty); + } + private static HtmlString IsHelper(this IPublishedContent content, Func test, string valueIfTrue, string valueIfFalse) + { + return test(content) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); + } + #endregion + + #region Ancestors + + public static IEnumerable Ancestors(this IPublishedContent content, int level) + { + return content.Ancestors(n => n.Level <= level); + } + public static IEnumerable Ancestors(this IPublishedContent content, string nodeTypeAlias) + { + return content.Ancestors(n => n.DocumentTypeAlias == nodeTypeAlias); + } + public static IEnumerable Ancestors(this IPublishedContent content) + { + return content.Ancestors(n => true); + } + public static IEnumerable Ancestors(this IPublishedContent content, Func func) + { + var ancestorList = new List(); + var node = content; + while (node != null) + { + if (node.Level == 1) break; + var parent = node.Parent; + if (parent == null) + { + break; + } + if (content != parent) + { + node = parent; + if (func(node)) + { + ancestorList.Add(node); + } + } + else + { + break; + } + } + ancestorList.Reverse(); + return ancestorList; + } + + public static IPublishedContent AncestorOrSelf(this IPublishedContent content) + { + //TODO: Why is this query like this?? + return content.AncestorOrSelf(node => node.Level == 1); + } + public static IPublishedContent AncestorOrSelf(this IPublishedContent content, int level) + { + return content.AncestorOrSelf(node => node.Level == level); + } + public static IPublishedContent AncestorOrSelf(this IPublishedContent content, string nodeTypeAlias) + { + return content.AncestorOrSelf(node => node.DocumentTypeAlias == nodeTypeAlias); + } + public static IPublishedContent AncestorOrSelf(this IPublishedContent content, Func func) + { + var node = content; + while (node != null) + { + if (func(node)) return node; + var parent = node.Parent; + if (parent == null) + { + return null; + } + if (content != parent) + { + node = parent; + } + else + { + return node; + } + } + return null; + } + + public static IEnumerable AncestorsOrSelf(this IPublishedContent content, Func func) + { + var ancestorList = new List(); + var node = content; + ancestorList.Add(node); + while (node != null) + { + if (node.Level == 1) break; + var parent = node.Parent; + if (parent == null) + { + break; + } + if (content != parent) + { + node = parent; + if (func(node)) + { + ancestorList.Add(node); + } + } + else + { + break; + } + } + ancestorList.Reverse(); + return ancestorList; + } + public static IEnumerable AncestorsOrSelf(this IPublishedContent content) + { + return content.AncestorsOrSelf(n => true); + } + public static IEnumerable AncestorsOrSelf(this IPublishedContent content, string nodeTypeAlias) + { + return content.AncestorsOrSelf(n => n.DocumentTypeAlias == nodeTypeAlias); + } + public static IEnumerable AncestorsOrSelf(this IPublishedContent content, int level) + { + return content.AncestorsOrSelf(n => n.Level <= level); + } + + #endregion + + #region Descendants + public static IEnumerable Descendants(this IPublishedContent content, string nodeTypeAlias) + { + return content.Descendants(p => p.DocumentTypeAlias == nodeTypeAlias); + } + public static IEnumerable Descendants(this IPublishedContent content, int level) + { + return content.Descendants(p => p.Level >= level); + } + public static IEnumerable Descendants(this IPublishedContent content) + { + return content.Descendants(n => true); + } + internal static IEnumerable Descendants(this IPublishedContent content, Func func) + { + return content.Children.Map(func, (IPublishedContent n) => n.Children); + } + public static IEnumerable DescendantsOrSelf(this IPublishedContent content, int level) + { + return content.DescendantsOrSelf(p => p.Level >= level); + } + public static IEnumerable DescendantsOrSelf(this IPublishedContent content, string nodeTypeAlias) + { + return content.DescendantsOrSelf(p => p.DocumentTypeAlias == nodeTypeAlias); + } + public static IEnumerable DescendantsOrSelf(this IPublishedContent content) + { + return content.DescendantsOrSelf(p => true); + } + internal static IEnumerable DescendantsOrSelf(this IPublishedContent content, Func func) + { + if (content != null) + { + var thisNode = new List(); + if (func(content)) + { + thisNode.Add(content); + } + var flattenedNodes = content.Children.Map(func, (IPublishedContent n) => n.Children); + return thisNode.Concat(flattenedNodes).ToList().ConvertAll(dynamicBackingItem => new DynamicPublishedContentBase(dynamicBackingItem)); + } + return Enumerable.Empty(); + } + #endregion + + #region Traversal + + public static IPublishedContent Up(this IPublishedContent content) + { + return content.Up(0); + } + public static IPublishedContent Up(this IPublishedContent content, int number) + { + if (number == 0) + { + return content.Parent; + } + while ((content = content.Parent) != null && --number >= 0) ; + return content; + } + public static IPublishedContent Up(this IPublishedContent content, string nodeTypeAlias) + { + if (string.IsNullOrEmpty(nodeTypeAlias)) + { + return content.Parent; + } + while ((content = content.Parent) != null && content.DocumentTypeAlias != nodeTypeAlias) ; + return content; + } + public static IPublishedContent Down(this IPublishedContent content) + { + return content.Down(0); + } + public static IPublishedContent Down(this IPublishedContent content, int number) + { + var children = content.Children; + if (number == 0) + { + return children.First(); + } + var working = content; + while (number-- >= 0) + { + working = children.First(); + children = new DynamicPublishedContentList(working.Children); + } + return working; + } + public static IPublishedContent Down(this IPublishedContent content, string nodeTypeAlias) + { + if (string.IsNullOrEmpty(nodeTypeAlias)) + { + var children = content.Children; + return children.First(); + } + return content.Descendants(nodeTypeAlias).FirstOrDefault(); + } + + public static IPublishedContent Next(this IPublishedContent content) + { + return content.Next(0); + } + public static IPublishedContent Next(this IPublishedContent content, int number) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + + var container = ownersList.ToList(); + var currentIndex = container.FindIndex(n => n.Id == content.Id); + if (currentIndex != -1) + { + return container.ElementAtOrDefault(currentIndex + (number + 1)); + } + throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", content.Id)); + } + + public static IPublishedContent Next(this IPublishedContent content, string nodeTypeAlias) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + + var container = ownersList.ToList(); + var currentIndex = container.FindIndex(n => n.Id == content.Id); + if (currentIndex != -1) + { + var newIndex = container.FindIndex(currentIndex, n => n.DocumentTypeAlias == nodeTypeAlias); + return newIndex != -1 + ? container.ElementAt(newIndex) + : null; + } + throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", content.Id)); + } + public static IPublishedContent Previous(this IPublishedContent content) + { + return content.Previous(0); + } + public static IPublishedContent Previous(this IPublishedContent content, int number) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + + var container = ownersList.ToList(); + var currentIndex = container.FindIndex(n => n.Id == content.Id); + if (currentIndex != -1) + { + return container.ElementAtOrDefault(currentIndex + (number - 1)); + } + throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", content.Id)); + } + public static IPublishedContent Previous(this IPublishedContent content, string nodeTypeAlias) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + + var container = ownersList.ToList(); + int currentIndex = container.FindIndex(n => n.Id == content.Id); + if (currentIndex != -1) + { + var previousNodes = container.Take(currentIndex).ToList(); + int newIndex = previousNodes.FindIndex(n => n.DocumentTypeAlias == nodeTypeAlias); + if (newIndex != -1) + { + return container.ElementAt(newIndex); + } + return null; + } + throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", content.Id)); + } + public static IPublishedContent Sibling(this IPublishedContent content, int number) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + + var container = ownersList.ToList(); + var currentIndex = container.FindIndex(n => n.Id == content.Id); + if (currentIndex != -1) + { + return container.ElementAtOrDefault(currentIndex + number); + } + throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", content.Id)); + } + public static IPublishedContent Sibling(this IPublishedContent content, string nodeTypeAlias) + { + //get the root docs if parent is null + var ownersList = content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + + var container = ownersList.ToList(); + var currentIndex = container.FindIndex(n => n.Id == content.Id); + if (currentIndex != -1) + { + var workingIndex = currentIndex + 1; + while (workingIndex != currentIndex) + { + var working = container.ElementAtOrDefault(workingIndex); + if (working != null && working.DocumentTypeAlias == nodeTypeAlias) + { + return working; + } + workingIndex++; + if (workingIndex > container.Count) + { + workingIndex = 0; + } + } + return null; + } + throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", content.Id)); + } + #endregion /// /// Returns a DataTable object for the IPublishedContent diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index cc3faaf33b..50017de4e1 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -240,6 +240,7 @@ Properties\SolutionInfo.cs + @@ -250,7 +251,6 @@ - diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index d660808d90..3c65470112 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -487,7 +487,7 @@ namespace Umbraco.Web var doc = store.GetDocumentById(UmbracoContext.Current, id); return doc == null ? new DynamicNull() - : new DynamicPublishedContent(doc).AsDynamic(); + : new DynamicPublishedContentBase(doc).AsDynamic(); } private dynamic DocumentById(string id, IPublishedStore store) @@ -502,7 +502,7 @@ namespace Umbraco.Web { var nodes = ids.Select(eachId => DocumentById(eachId, store)) .Where(x => !TypeHelper.IsTypeAssignableFrom(x)) - .Cast(); + .Cast(); return new DynamicPublishedContentList(nodes); } @@ -510,7 +510,7 @@ namespace Umbraco.Web { var nodes = ids.Select(eachId => DocumentById(eachId, store)) .Where(x => !TypeHelper.IsTypeAssignableFrom(x)) - .Cast(); + .Cast(); return new DynamicPublishedContentList(nodes); } diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index a58b362f4b..2f86ed3407 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -226,9 +226,6 @@ namespace Umbraco.Web CultureDictionaryFactoryResolver.Current = new CultureDictionaryFactoryResolver( new DefaultCultureDictionaryFactory()); - //This exists only because the new business logic classes aren't created yet and we want Dynamics in the Core project, - //see the note in the DynamicNodeDataSourceResolver.cs class - DynamicPublishedContentDataSourceResolver.Current = new DynamicPublishedContentDataSourceResolver(new DefaultDynamicPublishedContentDataSource()); } } diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs index 52fb4f53df..9725390d3e 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs @@ -30,6 +30,12 @@ namespace umbraco.MacroEngines { public class DynamicNode : DynamicObject { + /// + /// This callback is used only so we can set it dynamically for use in unit tests + /// + internal static Func GetDataTypeCallback = (docTypeAlias, propertyAlias) => + ContentType.GetDataType(docTypeAlias, propertyAlias); + #region consts // these are private readonlys as const can't be Guids private readonly Guid DATATYPE_YESNO_GUID = new Guid("38b352c1-e9f8-4fd8-9324-9a2eab06d97a"); @@ -503,6 +509,11 @@ namespace umbraco.MacroEngines } } + private static Guid GetDataType(string docTypeAlias, string propertyAlias) + { + return GetDataTypeCallback(docTypeAlias, propertyAlias); + } + public override bool TryGetMember(GetMemberBinder binder, out object result) { @@ -542,9 +553,8 @@ namespace umbraco.MacroEngines } //contextAlias is the node which the property data was returned from - //Guid dataType = ContentType.GetDataType(data.ContextAlias, data.Alias); - //SD: replaced with our temporary resolver so that we can unit test this properly, this is what DynamicPublishedContent uses until we create our new data access layer. - var dataType = DynamicPublishedContentDataSourceResolver.Current.DataSource.GetDataType(data.ContextAlias, data.Alias); + //Guid dataType = ContentType.GetDataType(data.ContextAlias, data.Alias); + var dataType = GetDataType(data.ContextAlias, data.Alias); var staticMapping = UmbracoSettings.RazorDataTypeModelStaticMapping.FirstOrDefault(mapping => { diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicPublishedContentExtensions.cs b/src/umbraco.MacroEngines/RazorDynamicNode/PublishedContentExtensions.cs similarity index 86% rename from src/umbraco.MacroEngines/RazorDynamicNode/DynamicPublishedContentExtensions.cs rename to src/umbraco.MacroEngines/RazorDynamicNode/PublishedContentExtensions.cs index 1221331f08..6116f44fbf 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicPublishedContentExtensions.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/PublishedContentExtensions.cs @@ -2,8 +2,10 @@ using System; using System.Collections.Generic; using System.Data; using System.Linq; +using Umbraco.Core; using Umbraco.Core.Dynamics; using Umbraco.Core.Models; +using Umbraco.Web; using umbraco.NodeFactory; using umbraco.interfaces; @@ -12,7 +14,7 @@ namespace umbraco.MacroEngines.Library /// /// Extension methods for converting DynamicPublishedContent to INode /// - internal static class DynamicPublishedContentExtensions + internal static class PublishedContentExtensions { internal static IProperty ConvertToNodeProperty(this IDocumentProperty prop) @@ -20,7 +22,7 @@ namespace umbraco.MacroEngines.Library return new PropertyResult(prop.Alias, prop.Value.ToString(), prop.Version); } - internal static INode ConvertToNode(this DynamicPublishedContent doc) + internal static INode ConvertToNode(this IPublishedContent doc) { var node = new ConvertedNode(doc); return node; @@ -31,9 +33,9 @@ namespace umbraco.MacroEngines.Library /// private class ConvertedNode : INode { - private readonly DynamicPublishedContent _doc; + private readonly IPublishedContent _doc; - public ConvertedNode(DynamicPublishedContent doc) + public ConvertedNode(IPublishedContent doc) { _doc = doc; template = doc.TemplateId; @@ -109,12 +111,12 @@ namespace umbraco.MacroEngines.Library public DataTable ChildrenAsTable() { - throw new NotImplementedException(); + return _doc.ChildrenAsTable(); } public DataTable ChildrenAsTable(string nodeTypeAliasFilter) { - throw new NotImplementedException(); + return _doc.ChildrenAsTable(nodeTypeAliasFilter); } } } diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs b/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs index 0a01b47cb1..e756fdf534 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Web.Mvc; using Umbraco.Core.Dynamics; +using Umbraco.Core.Models; using Umbraco.Web; using umbraco.interfaces; using System.Xml.Linq; diff --git a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj index f8b48e64ca..ee3903ac40 100644 --- a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj +++ b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj @@ -86,7 +86,7 @@ - +