PublishedContent - the big refactoring

This commit is contained in:
Stephan
2013-09-05 17:47:13 +02:00
parent 912716f889
commit 0415a31d0e
115 changed files with 6366 additions and 6233 deletions

View File

@@ -1,62 +1,36 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Web.Templates;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Web.Models
{
/// <summary>
/// An abstract base class to use for IPublishedContent which ensures that the Url and Indexed property return values
/// are consistently returned.
/// Provide an abstract base class for <c>IPublishedContent</c> implementations.
/// </summary>
/// <remarks>
/// This also ensures that we have an OwnersCollection property so that the IsFirst/IsLast/Index helper methods work
/// when referenced inside the result of a collection. http://issues.umbraco.org/issue/U4-1797
/// </remarks>
/// <remarks>This base class does which (a) consitently resolves and caches the Url, (b) provides an implementation
/// for this[alias], and (c) provides basic content set management.</remarks>
[DebuggerDisplay("Content Id: {Id}, Name: {Name}")]
public abstract class PublishedContentBase : IPublishedContent, IOwnerCollectionAware<IPublishedContent>
{
private string _url;
private readonly Dictionary<string, object> _resolvePropertyValues = new Dictionary<string, object>();
private IEnumerable<IPublishedContent> _ownersCollection;
public abstract class PublishedContentBase : IPublishedContent
{
#region Content
/// <summary>
/// Need to get/set the owner collection when an item is returned from the result set of a query
/// </summary>
/// <remarks>
/// Based on this issue here: http://issues.umbraco.org/issue/U4-1797
/// </remarks>
IEnumerable<IPublishedContent> IOwnerCollectionAware<IPublishedContent>.OwnersCollection
{
get
{
//if the owners collection is null, we'll default to it's siblings
if (_ownersCollection == null)
{
//get the root docs if parent is null
_ownersCollection = this.Siblings();
}
return _ownersCollection;
}
set { _ownersCollection = value; }
}
private string _url;
/// <summary>
/// Returns the Url for this content item
/// Gets the url of the content.
/// </summary>
/// <remarks>
/// If this item type is media, the Url that is returned is the Url computed by the NiceUrlProvider, otherwise if it is media
/// the Url returned is the value found in the 'umbracoFile' property.
/// If this content is Content, the url that is returned is the one computed by the NiceUrlProvider, otherwise if
/// this content is Media, the url returned is the value found in the 'umbracoFile' property.
/// </remarks>
public virtual string Url
{
get
{
// should be thread-safe although it won't prevent url from being resolved more than once
if (_url != null)
return _url;
@@ -64,20 +38,21 @@ namespace Umbraco.Web.Models
{
case PublishedItemType.Content:
if (UmbracoContext.Current == null)
throw new InvalidOperationException("Cannot resolve a Url for a content item with a null UmbracoContext.Current reference");
throw new InvalidOperationException("Cannot resolve a Url for a content item when UmbracoContext.Current is null.");
if (UmbracoContext.Current.UrlProvider == null)
throw new InvalidOperationException("Cannot resolve a Url for a content item with a null UmbracoContext.Current.NiceUrlProvider reference");
_url= UmbracoContext.Current.UrlProvider.GetUrl(this.Id);
throw new InvalidOperationException("Cannot resolve a Url for a content item when UmbracoContext.Current.UrlProvider is null.");
_url= UmbracoContext.Current.UrlProvider.GetUrl(Id);
break;
case PublishedItemType.Media:
var prop = GetProperty(Constants.Conventions.Media.File);
if (prop == null)
throw new NotSupportedException("Cannot retreive a Url for a media item if there is no 'umbracoFile' property defined");
throw new NotSupportedException("Cannot resolve a Url for a media item when there is no 'umbracoFile' property defined.");
_url = prop.Value.ToString();
break;
default:
throw new ArgumentOutOfRangeException();
}
return _url;
}
}
@@ -99,32 +74,126 @@ namespace Umbraco.Web.Models
public abstract DateTime UpdateDate { get; }
public abstract Guid Version { get; }
public abstract int Level { get; }
public abstract ICollection<IPublishedContentProperty> Properties { get; }
/// <summary>
/// Returns the property value for the property alias specified
/// </summary>
/// <param name="propertyAlias"></param>
/// <returns></returns>
/// <remarks>
/// Ensures that the value is executed through the IPropertyEditorValueConverters and that all internal links are are to date
/// </remarks>
public virtual object this[string propertyAlias]
public abstract bool IsDraft { get; }
public int GetIndex()
{
var index = this.Siblings().FindIndex(x => x.Id == Id);
if (index < 0)
throw new IndexOutOfRangeException("Could not find content in the content set.");
return index;
}
#endregion
#region Tree
/// <summary>
/// Gets the parent of the content.
/// </summary>
public abstract IPublishedContent Parent { get; }
/// <summary>
/// Gets the children of the content.
/// </summary>
/// <remarks>Children are sorted by their sortOrder.</remarks>
public abstract IEnumerable<IPublishedContent> Children { get; }
#endregion
#region ContentSet
public virtual IEnumerable<IPublishedContent> ContentSet
{
// the default content set of a content is its siblings
get { return this.Siblings(); }
}
#endregion
#region ContentType
public abstract PublishedContentType ContentType { get; }
#endregion
#region Properties
/// <summary>
/// Gets the properties of the content.
/// </summary>
public abstract ICollection<IPublishedProperty> Properties { get; }
/// <summary>
/// Gets the value of a property identified by its alias.
/// </summary>
/// <param name="alias">The property alias.</param>
/// <returns>The value of the property identified by the alias.</returns>
/// <remarks>
/// <para>If <c>GetProperty(alias)</c> is <c>null</c> then returns <c>null</c> else return <c>GetProperty(alias).Value</c>.</para>
/// <para>So if the property has no value, returns the default value for that property type.</para>
/// <para>This one is defined here really because we cannot define index extension methods, but all it should do is:
/// <code>var p = GetProperty(alias); return p == null ? null : p.Value;</code> and nothing else.</para>
/// <para>The recursive syntax (eg "_title") is _not_ supported here.</para>
/// <para>The alias is case-insensitive.</para>
/// </remarks>
public virtual object this[string alias]
{
get
{
//check this instance's cache, this is better for performance because resolving a value can
//have performance impacts since it has to resolve Urls and IPropertyEditorValueConverter's as well.
if (_resolvePropertyValues.ContainsKey(propertyAlias))
return _resolvePropertyValues[propertyAlias];
_resolvePropertyValues.Add(propertyAlias, this.GetPropertyValue(propertyAlias));
return _resolvePropertyValues[propertyAlias];
// no cache here: GetProperty should be fast, and .Value cache should be managed by the property.
var property = GetProperty(alias);
return property == null ? null : property.Value;
}
}
public abstract IPublishedContentProperty GetProperty(string alias);
public abstract IPublishedContent Parent { get; }
public abstract IEnumerable<IPublishedContent> Children { get; }
/// <summary>
/// Gets a property identified by its alias.
/// </summary>
/// <param name="alias">The property alias.</param>
/// <returns>The property identified by the alias.</returns>
/// <remarks>
/// <para>If no property with the specified alias exists, returns <c>null</c>.</para>
/// <para>The returned property may have no value (ie <c>HasValue</c> is <c>false</c>).</para>
/// <para>The alias is case-insensitive.</para>
/// </remarks>
public abstract IPublishedProperty GetProperty(string alias);
/// <summary>
/// Gets a property identified by its alias.
/// </summary>
/// <param name="alias">The property alias.</param>
/// <param name="recurse">A value indicating whether to navigate the tree upwards until a property with a value is found.</param>
/// <returns>The property identified by the alias.</returns>
/// <remarks>
/// <para>Navigate the tree upwards and look for a property with that alias and with a value (ie <c>HasValue</c> is <c>true</c>).
/// If found, return the property. If no property with that alias is found, having a value or not, return <c>null</c>. Otherwise
/// return the first property that was found with the alias but had no value (ie <c>HasValue</c> is <c>false</c>).</para>
/// <para>The alias is case-insensitive.</para>
/// </remarks>
public virtual IPublishedProperty GetProperty(string alias, bool recurse)
{
var property = GetProperty(alias);
if (recurse == false) return property;
IPublishedContent content = this;
var firstNonNullProperty = property;
while (content != null && (property == null || property.HasValue == false))
{
content = content.Parent;
property = content == null ? null : content.GetProperty(alias);
if (firstNonNullProperty == null && property != null) firstNonNullProperty = property;
}
// if we find a content with the property with a value, return that property
// if we find no content with the property, return null
// if we find a content with the property without a value, return that property
// have to save that first property while we look further up, hence firstNonNullProperty
return property != null && property.HasValue ? property : firstNonNullProperty;
}
#endregion
}
}