// fixme - should #define #undef FIX_GET_PROPERTY_VALUE using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Dynamic; using System.Linq; using System.Runtime.CompilerServices; using System.Web; using Umbraco.Core.Dynamics; using Umbraco.Core.Models; using Umbraco.Core; using System.Reflection; using Umbraco.Core.Models.PublishedContent; using ContentType = umbraco.cms.businesslogic.ContentType; namespace Umbraco.Web.Models { /// /// The base dynamic model for views /// [DebuggerDisplay("Content Id: {Id}, Name: {Name}")] public class DynamicPublishedContent : DynamicObject, IPublishedContent { protected internal IPublishedContent PublishedContent { get; private set; } public IEnumerable ContentSet { get { // fixme - what's this? throw new NotImplementedException("What shall we do?"); /* if (_contentSet != null) return _contentSet; // siblings = parent.Children var parent = PublishedContent.Parent; var dynamicParent = parent == null ? null : parent.AsDynamicOrNull(); if (dynamicParent != null) return dynamicParent.Children; // silbings = content at root var atRoot = new DynamicPublishedContentList(UmbracoContext.Current.ContentCache.GetAtRoot()); return atRoot; */ } } public PublishedContentType ContentType { get { return PublishedContent.ContentType; } } #region Constructors public DynamicPublishedContent(IPublishedContent content) { if (content == null) throw new ArgumentNullException("content"); PublishedContent = content; } //internal DynamicPublishedContent(IPublishedContent content, IEnumerable contentSet) //{ // PublishedContent = content; // _contentSet = contentSet; //} #endregion // these two here have leaked in v6 and so we cannot remove them anymore // without breaking compatibility but... TODO: remove them in v7 public DynamicPublishedContentList ChildrenAsList { get { return Children; } } public int parentId { get { return PublishedContent.Parent.Id; } } #region DynamicObject // fixme - so everywhere else we use a basic dictionary but here we use a concurrent one? why? private readonly ConcurrentDictionary _cachedMemberOutput = new ConcurrentDictionary(); /// /// Attempts to call a method on the dynamic object /// /// /// /// /// public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { var attempt = DynamicInstanceHelper.TryInvokeMember(this, binder, args, new[] { typeof(DynamicPublishedContent) }); if (attempt.Success) { result = attempt.Result.ObjectResult; //need to check the return type and possibly cast if result is from an extension method found if (attempt.Result.Reason == DynamicInstanceHelper.TryInvokeMemberSuccessReason.FoundExtensionMethod) { //we don't need to cast if it is already DynamicPublishedContent if (attempt.Result.ObjectResult != null && (!(attempt.Result.ObjectResult is DynamicPublishedContent))) { if (attempt.Result.ObjectResult is IPublishedContent) { result = new DynamicPublishedContent((IPublishedContent)attempt.Result.ObjectResult); } else if (attempt.Result.ObjectResult is IEnumerable) { result = new DynamicPublishedContentList((IEnumerable)attempt.Result.ObjectResult); } else if (attempt.Result.ObjectResult is IEnumerable) { result = new DynamicPublishedContentList((IEnumerable)attempt.Result.ObjectResult); } } } return true; } //this is the result of an extension method execution gone wrong so we return dynamic null //fixme - throws a NullRef, wrong order of checks?! if (attempt.Result.Reason == DynamicInstanceHelper.TryInvokeMemberSuccessReason.FoundExtensionMethod && attempt.Exception != null && attempt.Exception is TargetInvocationException) { result = DynamicNull.Null; return true; } result = null; return false; } /// /// Attempts to return a custom member (generally based on a string match) /// /// /// protected virtual Attempt TryGetCustomMember(GetMemberBinder binder) { // FIXME that one makes NO SENSE // why not let the NORMAL BINDER do the work?! // see below, that should be enough! if (binder.Name.InvariantEquals("ChildrenAsList") || binder.Name.InvariantEquals("Children")) { return Attempt.Succeed(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 Attempt.Succeed(parent.Id); } return Attempt.Fail(); } /// /// 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 Attempt.Succeed( new DynamicPublishedContentList(filteredTypeChildren.Select(x => new DynamicPublishedContent(x)))); } return Attempt.Fail(); } /// /// 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.RawValue // fixme - why use the raw value? : null; return Attempt.If(result != null, result); } /// /// Attempts to return a member based on a user defined umbraco property /// /// /// protected virtual Attempt TryGetUserProperty(GetMemberBinder binder) { var name = binder.Name; var recurse = false; if (name.StartsWith("_")) { name = name.Substring(1, name.Length - 1); recurse = true; } var value = PublishedContent.GetPropertyValue(name, recurse); return Attempt.SucceedIf(value != null, value); } /// /// 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 = DynamicNull.Null; //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; // Update the context before attempting to retrieve the property again. context = parent.AsDynamicOrNull(); 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); // get wrap the result in a PropertyResult - just so it's an IHtmlString - ?! return prop == null ? null : new PropertyResult(prop, PropertyResultType.UserProperty); } //reflect // FIXME but if it exists here, then the original BINDER should have found it ALREADY? // and if it's just a casing issue then there's BindingFlags.IgnoreCase ?!! Func> getMember = memberAlias => { try { return Attempt.Succeed( content.GetType().InvokeMember(memberAlias, System.Reflection.BindingFlags.GetProperty | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public, null, content, null)); } catch (MissingMethodException ex) { return Attempt.Fail(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); } #endregion #region Explicit IPublishedContent implementation IPublishedContent IPublishedContent.Parent { get { return PublishedContent.Parent; } } int IPublishedContent.Id { get { return PublishedContent.Id; } } int IPublishedContent.TemplateId { get { return PublishedContent.TemplateId; } } int IPublishedContent.SortOrder { get { return PublishedContent.SortOrder; } } string IPublishedContent.Name { get { return PublishedContent.Name; } } string IPublishedContent.UrlName { get { return PublishedContent.UrlName; } } string IPublishedContent.DocumentTypeAlias { get { return PublishedContent.DocumentTypeAlias; } } int IPublishedContent.DocumentTypeId { get { return PublishedContent.DocumentTypeId; } } string IPublishedContent.WriterName { get { return PublishedContent.WriterName; } } string IPublishedContent.CreatorName { get { return PublishedContent.CreatorName; } } int IPublishedContent.WriterId { get { return PublishedContent.WriterId; } } int IPublishedContent.CreatorId { get { return PublishedContent.CreatorId; } } string IPublishedContent.Path { get { return PublishedContent.Path; } } DateTime IPublishedContent.CreateDate { get { return PublishedContent.CreateDate; } } DateTime IPublishedContent.UpdateDate { get { return PublishedContent.UpdateDate; } } Guid IPublishedContent.Version { get { return PublishedContent.Version; } } int IPublishedContent.Level { get { return PublishedContent.Level; } } bool IPublishedContent.IsDraft { get { return PublishedContent.IsDraft; } } int IPublishedContent.GetIndex() { return PublishedContent.GetIndex(); } ICollection IPublishedContent.Properties { get { return PublishedContent.Properties; } } IEnumerable IPublishedContent.Children { get { return PublishedContent.Children; } } IPublishedProperty IPublishedContent.GetProperty(string alias) { return PublishedContent.GetProperty(alias); } #endregion #region IPublishedContent implementation public int TemplateId { get { return PublishedContent.TemplateId; } } public int SortOrder { get { return PublishedContent.SortOrder; } } public string Name { get { return PublishedContent.Name; } } 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 string Url { get { return PublishedContent.Url; } } public PublishedItemType ItemType { get { return PublishedContent.ItemType; } } // see note in IPublishedContent //public bool Published //{ // get { return PublishedContent.Published; } //} public IEnumerable Properties { get { return PublishedContent.Properties; } } public object this[string propertyAlias] { get { return PublishedContent[propertyAlias]; } } #endregion #region GetProperty // enhanced versions of the extension methods that exist for IPublishedContent, // here we support the recursive (_) and reflected (@) syntax public IPublishedProperty GetProperty(string alias) { return alias.StartsWith("_") ? GetProperty(alias.Substring(1), true) : GetProperty(alias, false); } public IPublishedProperty GetProperty(string alias, bool recurse) { if (alias.StartsWith("@")) return GetReflectedProperty(alias.Substring(1)); // get wrap the result in a PropertyResult - just so it's an IHtmlString - ?! var property = PublishedContent.GetProperty(alias, recurse); return property == null ? null : new PropertyResult(property, PropertyResultType.UserProperty); } #endregion // IPublishedContent extension methods: // // all these methods are IPublishedContent extension methods so they should in // theory apply to DynamicPublishedContent since it is an IPublishedContent and // we look for extension methods. But that lookup has to be pretty slow. // Duplicating the methods here makes things much faster. #region IPublishedContent extension methods - Template public string GetTemplateAlias() { return PublishedContentExtensions.GetTemplateAlias(this); } #endregion #region IPublishedContent extension methods - HasProperty public bool HasProperty(string name) { return PublishedContent.HasProperty(name); } #endregion #region IPublishedContent extension methods - HasValue public bool HasValue(string alias) { return PublishedContent.HasValue(alias); } public bool HasValue(string alias, bool recursive) { return PublishedContent.HasValue(alias, recursive); } public IHtmlString HasValue(string alias, string valueIfTrue, string valueIfFalse) { return PublishedContent.HasValue(alias, valueIfTrue, valueIfFalse); } public IHtmlString HasValue(string alias, bool recursive, string valueIfTrue, string valueIfFalse) { return PublishedContent.HasValue(alias, recursive, valueIfTrue, valueIfFalse); } public IHtmlString HasValue(string alias, string valueIfTrue) { return PublishedContent.HasValue(alias, valueIfTrue); } public IHtmlString HasValue(string alias, bool recursive, string valueIfTrue) { return PublishedContent.HasValue(alias, recursive, valueIfTrue); } #endregion #region IPublishedContent extension methods - GetPropertyValue // for whatever reason, some methods returning strings were created in DynamicPublishedContent // and are now considered a "feature" as of v6. So we can't have the proper GetPropertyValue // methods returning objects, too. And we don't want to change it in v6 as that would be a // breaking change. #if FIX_GET_PROPERTY_VALUE public object GetPropertyValue(string alias) { return PublishedContent.GetPropertyValue(alias); } public object GetPropertyValue(string alias, string defaultValue) { return PublishedContent.GetPropertyValue(alias, defaultValue); } public object GetPropertyValue(string alias, object defaultValue) { return PublishedContent.GetPropertyValue(alias, defaultValue); } public object GetPropertyValue(string alias, bool recurse) { return PublishedContent.GetPropertyValue(alias, recurse); } public object GetPropertyValue(string alias, bool recurse, object defaultValue) { return PublishedContent.GetPropertyValue(alias, recurse, defaultValue); } #else public string GetPropertyValue(string alias) { return GetPropertyValue(alias, false); } public string GetPropertyValue(string alias, string defaultValue) { var value = GetPropertyValue(alias); return value.IsNullOrWhiteSpace() ? defaultValue : value; } public string GetPropertyValue(string alias, bool recurse, string defaultValue) { var value = GetPropertyValue(alias, recurse); return value.IsNullOrWhiteSpace() ? defaultValue : value; } public string GetPropertyValue(string alias, bool recursive) { var property = GetProperty(alias, recursive); if (property == null || property.Value == null) return null; return property.Value.ToString(); } #endif #endregion #region IPublishedContent extension methods - GetPropertyValue public T GetPropertyValue(string alias) { return PublishedContent.GetPropertyValue(alias); } public T GetPropertyValue(string alias, T defaultValue) { return PublishedContent.GetPropertyValue(alias, defaultValue); } public T GetPropertyValue(string alias, bool recurse) { return PublishedContent.GetPropertyValue(alias, recurse); } public T GetPropertyValue(string alias, bool recurse, T defaultValue) { return PublishedContent.GetPropertyValue(alias, recurse, defaultValue); } #endregion #region IPublishedContent extension methods - Search public DynamicPublishedContentList Search(string term, bool useWildCards = true, string searchProvider = null) { return new DynamicPublishedContentList(PublishedContent.Search(term, useWildCards, searchProvider)); } public DynamicPublishedContentList SearchDescendants(string term, bool useWildCards = true, string searchProvider = null) { return new DynamicPublishedContentList(PublishedContent.SearchDescendants(term, useWildCards, searchProvider)); } public DynamicPublishedContentList SearchChildren(string term, bool useWildCards = true, string searchProvider = null) { return new DynamicPublishedContentList(PublishedContent.SearchChildren(term, useWildCards, searchProvider)); } public DynamicPublishedContentList Search(Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) { return new DynamicPublishedContentList(PublishedContent.Search(criteria, searchProvider)); } #endregion // fixme #region IPublishedContent extension methods - ToContentSet #endregion // fixme #region IPublishedContent extension methods - AsDynamic public dynamic AsDynamic() { return this; } public dynamic AsDynamicOrNull() { return this; } #endregion #region IPublishedContente extension methods - ContentSet public int Position() { return Index(); } public int Index() { return PublishedContent.GetIndex(); } #endregion #region IPublishedContent extension methods - IsSomething: misc public bool Visible { get { return PublishedContent.IsVisible(); } } public bool IsVisible() { return PublishedContent.IsVisible(); } public bool IsDocumentType(string docTypeAlias) { return PublishedContent.IsDocumentType(docTypeAlias); } public bool IsNull(string alias, bool recursive) { return PublishedContent.IsNull(alias, recursive); } public bool IsNull(string alias) { return PublishedContent.IsNull(alias, false); } #endregion #region IPublishedContent extension methods - IsSomething: position in set public bool IsFirst() { return PublishedContent.IsFirst(); } public HtmlString IsFirst(string valueIfTrue) { return PublishedContent.IsFirst(valueIfTrue); } public HtmlString IsFirst(string valueIfTrue, string valueIfFalse) { return PublishedContent.IsFirst(valueIfTrue, valueIfFalse); } public bool IsNotFirst() { return PublishedContent.IsNotFirst(); } public HtmlString IsNotFirst(string valueIfTrue) { return PublishedContent.IsNotFirst(valueIfTrue); } public HtmlString IsNotFirst(string valueIfTrue, string valueIfFalse) { return PublishedContent.IsNotFirst(valueIfTrue, valueIfFalse); } public bool IsPosition(int index) { return PublishedContent.IsPosition(index); } public HtmlString IsPosition(int index, string valueIfTrue) { return PublishedContent.IsPosition(index, valueIfTrue); } public HtmlString IsPosition(int index, string valueIfTrue, string valueIfFalse) { return PublishedContent.IsPosition(index, valueIfTrue, valueIfFalse); } public bool IsModZero(int modulus) { return PublishedContent.IsModZero(modulus); } public HtmlString IsModZero(int modulus, string valueIfTrue) { return PublishedContent.IsModZero(modulus, valueIfTrue); } public HtmlString IsModZero(int modulus, string valueIfTrue, string valueIfFalse) { return PublishedContent.IsModZero(modulus, valueIfTrue, valueIfFalse); } public bool IsNotModZero(int modulus) { return PublishedContent.IsNotModZero(modulus); } public HtmlString IsNotModZero(int modulus, string valueIfTrue) { return PublishedContent.IsNotModZero(modulus, valueIfTrue); } public HtmlString IsNotModZero(int modulus, string valueIfTrue, string valueIfFalse) { return PublishedContent.IsNotModZero(modulus, valueIfTrue, valueIfFalse); } public bool IsNotPosition(int index) { return PublishedContent.IsNotPosition(index); } public HtmlString IsNotPosition(int index, string valueIfTrue) { return PublishedContent.IsNotPosition(index, valueIfTrue); } public HtmlString IsNotPosition(int index, string valueIfTrue, string valueIfFalse) { return PublishedContent.IsNotPosition(index, valueIfTrue, valueIfFalse); } public bool IsLast() { return PublishedContent.IsLast(); } public HtmlString IsLast(string valueIfTrue) { return PublishedContent.IsLast(valueIfTrue); } public HtmlString IsLast(string valueIfTrue, string valueIfFalse) { return PublishedContent.IsLast(valueIfTrue, valueIfFalse); } public bool IsNotLast() { return PublishedContent.IsNotLast(); } public HtmlString IsNotLast(string valueIfTrue) { return PublishedContent.IsNotLast(valueIfTrue); } public HtmlString IsNotLast(string valueIfTrue, string valueIfFalse) { return PublishedContent.IsNotLast(valueIfTrue, valueIfFalse); } public bool IsEven() { return PublishedContent.IsEven(); } public HtmlString IsEven(string valueIfTrue) { return PublishedContent.IsEven(valueIfTrue); } public HtmlString IsEven(string valueIfTrue, string valueIfFalse) { return PublishedContent.IsEven(valueIfTrue, valueIfFalse); } public bool IsOdd() { return PublishedContent.IsOdd(); } public HtmlString IsOdd(string valueIfTrue) { return PublishedContent.IsOdd(valueIfTrue); } public HtmlString IsOdd(string valueIfTrue, string valueIfFalse) { return PublishedContent.IsOdd(valueIfTrue, valueIfFalse); } #endregion #region IPublishedContent extension methods - IsSomething: equality public bool IsEqual(DynamicPublishedContent other) { return PublishedContent.IsEqual(other); } public HtmlString IsEqual(DynamicPublishedContent other, string valueIfTrue) { return PublishedContent.IsEqual(other, valueIfTrue); } public HtmlString IsEqual(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) { return PublishedContent.IsEqual(other, valueIfTrue, valueIfFalse); } public bool IsNotEqual(DynamicPublishedContent other) { return PublishedContent.IsNotEqual(other); } public HtmlString IsNotEqual(DynamicPublishedContent other, string valueIfTrue) { return PublishedContent.IsNotEqual(other, valueIfTrue); } public HtmlString IsNotEqual(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) { return PublishedContent.IsNotEqual(other, valueIfTrue, valueIfFalse); } #endregion #region IPublishedContent extension methods - IsSomething: ancestors and descendants public bool IsDescendant(DynamicPublishedContent other) { return PublishedContent.IsDescendant(other); } public HtmlString IsDescendant(DynamicPublishedContent other, string valueIfTrue) { return PublishedContent.IsDescendant(other, valueIfTrue); } public HtmlString IsDescendant(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) { return PublishedContent.IsDescendant(other, valueIfTrue, valueIfFalse); } public bool IsDescendantOrSelf(DynamicPublishedContent other) { return PublishedContent.IsDescendantOrSelf(other); } public HtmlString IsDescendantOrSelf(DynamicPublishedContent other, string valueIfTrue) { return PublishedContent.IsDescendantOrSelf(other, valueIfTrue); } public HtmlString IsDescendantOrSelf(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) { return PublishedContent.IsDescendantOrSelf(other, valueIfTrue, valueIfFalse); } public bool IsAncestor(DynamicPublishedContent other) { return PublishedContent.IsAncestor(other); } public HtmlString IsAncestor(DynamicPublishedContent other, string valueIfTrue) { return PublishedContent.IsAncestor(other, valueIfTrue); } public HtmlString IsAncestor(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) { return PublishedContent.IsAncestor(other, valueIfTrue, valueIfFalse); } public bool IsAncestorOrSelf(DynamicPublishedContent other) { return PublishedContent.IsAncestorOrSelf(other); } public HtmlString IsAncestorOrSelf(DynamicPublishedContent other, string valueIfTrue) { return PublishedContent.IsAncestorOrSelf(other, valueIfTrue); } public HtmlString IsAncestorOrSelf(DynamicPublishedContent other, string valueIfTrue, string valueIfFalse) { return PublishedContent.IsAncestorOrSelf(other, valueIfTrue, valueIfFalse); } #endregion // all these methods wrap whatever PublishedContent returns in a new // DynamicPublishedContentList, for dynamic usage. #region Ancestors public DynamicPublishedContentList Ancestors(int level) { return new DynamicPublishedContentList(PublishedContent.Ancestors(level)); } public DynamicPublishedContentList Ancestors(string contentTypeAlias) { return new DynamicPublishedContentList(PublishedContent.Ancestors(contentTypeAlias)); } public DynamicPublishedContentList Ancestors() { return new DynamicPublishedContentList(PublishedContent.Ancestors()); } public DynamicPublishedContentList Ancestors(Func func) { return new DynamicPublishedContentList(PublishedContent.AncestorsOrSelf(false, func)); } public DynamicPublishedContent AncestorOrSelf() { return PublishedContent.AncestorOrSelf().AsDynamicOrNull(); } public DynamicPublishedContent AncestorOrSelf(int level) { return PublishedContent.AncestorOrSelf(level).AsDynamicOrNull(); } public DynamicPublishedContent AncestorOrSelf(string contentTypeAlias) { return PublishedContent.AncestorOrSelf(contentTypeAlias).AsDynamicOrNull(); } public DynamicPublishedContent AncestorOrSelf(Func func) { return PublishedContent.AncestorsOrSelf(true, func).FirstOrDefault().AsDynamicOrNull(); } public DynamicPublishedContentList AncestorsOrSelf(Func func) { return new DynamicPublishedContentList(PublishedContent.AncestorsOrSelf(true, func)); } public DynamicPublishedContentList AncestorsOrSelf() { return new DynamicPublishedContentList(PublishedContent.AncestorsOrSelf()); } public DynamicPublishedContentList AncestorsOrSelf(string contentTypeAlias) { return new DynamicPublishedContentList(PublishedContent.AncestorsOrSelf(contentTypeAlias)); } public DynamicPublishedContentList AncestorsOrSelf(int level) { return new DynamicPublishedContentList(PublishedContent.AncestorsOrSelf(level)); } #endregion #region Descendants public DynamicPublishedContentList Descendants(string contentTypeAlias) { return new DynamicPublishedContentList(PublishedContent.Descendants(contentTypeAlias)); } public DynamicPublishedContentList Descendants(int level) { return new DynamicPublishedContentList(PublishedContent.Descendants(level)); } public DynamicPublishedContentList Descendants() { return new DynamicPublishedContentList(PublishedContent.Descendants()); } public DynamicPublishedContentList DescendantsOrSelf(int level) { return new DynamicPublishedContentList(PublishedContent.DescendantsOrSelf(level)); } public DynamicPublishedContentList DescendantsOrSelf(string contentTypeAlias) { return new DynamicPublishedContentList(PublishedContent.DescendantsOrSelf(contentTypeAlias)); } public DynamicPublishedContentList DescendantsOrSelf() { return new DynamicPublishedContentList(PublishedContent.DescendantsOrSelf()); } #endregion #region Traversal public DynamicPublishedContent Up() { return PublishedContent.Up().AsDynamicOrNull(); } public DynamicPublishedContent Up(int number) { return PublishedContent.Up(number).AsDynamicOrNull(); } public DynamicPublishedContent Up(string contentTypeAlias) { return PublishedContent.Up(contentTypeAlias).AsDynamicOrNull(); } public DynamicPublishedContent Down() { return PublishedContent.Down().AsDynamicOrNull(); } public DynamicPublishedContent Down(int number) { return PublishedContent.Down(number).AsDynamicOrNull(); } public DynamicPublishedContent Down(string contentTypeAlias) { return PublishedContent.Down(contentTypeAlias).AsDynamicOrNull(); } public DynamicPublishedContent Next() { return PublishedContent.Next().AsDynamicOrNull(); } public DynamicPublishedContent Next(int number) { return PublishedContent.Next(number).AsDynamicOrNull(); } public DynamicPublishedContent Next(string contentTypeAlias) { return PublishedContent.Next(contentTypeAlias).AsDynamicOrNull(); } public DynamicPublishedContent Previous() { return PublishedContent.Previous().AsDynamicOrNull(); } public DynamicPublishedContent Previous(int number) { return PublishedContent.Previous(number).AsDynamicOrNull(); } public DynamicPublishedContent Previous(string contentTypeAlias) { return PublishedContent.Previous(contentTypeAlias).AsDynamicOrNull(); } public DynamicPublishedContent Sibling(int number) { return PublishedContent.Previous(number).AsDynamicOrNull(); } public DynamicPublishedContent Sibling(string contentTypeAlias) { return PublishedContent.Previous(contentTypeAlias).AsDynamicOrNull(); } #endregion #region Parent public DynamicPublishedContent Parent { get { return PublishedContent.Parent != null ? PublishedContent.Parent.AsDynamicOrNull() : null; } } #endregion #region Children // we want to cache the dynamic list of children here // whether PublishedContent.Children itself is cached, is not our concern private DynamicPublishedContentList _children; public DynamicPublishedContentList Children { get { return _children ?? (_children = new DynamicPublishedContentList(PublishedContent.Children)); } } #endregion // fixme - cleanup #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 } }