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