diff --git a/src/Umbraco.Core/Cache/CacheKeys.cs b/src/Umbraco.Core/Cache/CacheKeys.cs
index 54f9ab4832..fc17bd7245 100644
--- a/src/Umbraco.Core/Cache/CacheKeys.cs
+++ b/src/Umbraco.Core/Cache/CacheKeys.cs
@@ -47,6 +47,5 @@ namespace Umbraco.Core.Cache
public const string StylesheetPropertyCacheKey = "UmbracoStylesheetProperty";
public const string DataTypeCacheKey = "UmbracoDataTypeDefinition";
-
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings.cs b/src/Umbraco.Core/Configuration/UmbracoSettings.cs
index 22b5e4e3b6..6b53540238 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings.cs
@@ -502,8 +502,12 @@ namespace Umbraco.Core.Configuration
}
}
- //TODO: I"m not sure why we need this, need to ask Gareth what the deal is, pretty sure we can remove it or change it, seems like
- // massive overkill.
+ // we have that one because we auto-discover when a property is "xml" and should be returned by Razor as
+ // dynamic xml. But, stuff such as "
hello
" can be parsed into xml and would be returned as xml,
+ // unless has been defined in the exclusion list... this is dirty and not-efficient, we should at least
+ // cache the list somewhere!
+ //
+ // TODO get rid of that whole dynamic xml mess
///
/// razor DynamicNode typecasting detects XML and returns DynamicXml - Root elements that won't convert to DynamicXml
diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs
index 57d31d7e00..76045b011e 100644
--- a/src/Umbraco.Core/CoreBootManager.cs
+++ b/src/Umbraco.Core/CoreBootManager.cs
@@ -125,8 +125,9 @@ namespace Umbraco.Core
{
CanResolveBeforeFrozen = true
};
- //add custom types here that are internal
- ApplicationEventsResolver.Current.AddType();
+
+ // add custom types here that are internal, if needed
+ //ApplicationEventsResolver.Current.AddType<>();
}
///
@@ -254,15 +255,21 @@ namespace Umbraco.Core
//the database migration objects
MigrationResolver.Current = new MigrationResolver(
() => PluginManager.Current.ResolveMigrationTypes());
-
-
- PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver(
+ // fixme - remove that one eventually
+ PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver(
PluginManager.Current.ResolvePropertyEditorValueConverters());
- //add the internal ones, these are not public currently so need to add them manually
- PropertyEditorValueConvertersResolver.Current.AddType();
- PropertyEditorValueConvertersResolver.Current.AddType();
- PropertyEditorValueConvertersResolver.Current.AddType();
+
+ // initialize the new property value converters
+ // fixme - discuss: explicit registration vs. discovery?
+ PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver(
+ PluginManager.Current.ResolveTypes());
+
+ // add the internal ones
+ // fixme - should be public not internal?
+ PropertyValueConvertersResolver.Current.AddType();
+ PropertyValueConvertersResolver.Current.AddType();
+ PropertyValueConvertersResolver.Current.AddType();
// this is how we'd switch over to DefaultShortStringHelper _and_ still use
// UmbracoSettings UrlReplaceCharacters...
diff --git a/src/Umbraco.Core/Dynamics/DynamicNull.cs b/src/Umbraco.Core/Dynamics/DynamicNull.cs
index 0788e68923..473df3d203 100644
--- a/src/Umbraco.Core/Dynamics/DynamicNull.cs
+++ b/src/Umbraco.Core/Dynamics/DynamicNull.cs
@@ -11,78 +11,96 @@ namespace Umbraco.Core.Dynamics
//Because it's IEnumerable, if the user is actually trying @Model.TextPages or similar
//it will still return an enumerable object (assuming the call actually failed because there were no children of that type)
//but in .Where, if they use a property that doesn't exist, the lambda will bypass this and return false
+
+ // returned when TryGetMember fails on a DynamicPublishedContent
+ //
+ // so if user does @CurrentPage.TextPages it will get something that is enumerable (but empty)
+ // fixme - not sure I understand the stuff about .Where, though
+
public class DynamicNull : DynamicObject, IEnumerable, IHtmlString
{
+ public static readonly DynamicNull Null = new DynamicNull();
+
+ private DynamicNull() {}
+
public IEnumerator GetEnumerator()
{
return (new List()).GetEnumerator();
}
+
public DynamicNull Where(string predicate, params object[] values)
{
return this;
}
+
public DynamicNull OrderBy(string orderBy)
{
return this;
}
+
public int Count()
{
return 0;
}
+
public override string ToString()
{
return string.Empty;
}
+
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = this;
return true;
}
+
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
result = this;
return true;
}
+
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
{
result = this;
return true;
}
+
public bool IsNull()
{
return true;
}
+
public bool HasValue()
{
return false;
}
+
public string Name
{
- get
- {
- return string.Empty;
- }
+ get { return string.Empty; }
}
+
public int Id
{
- get
- {
- return 0;
- }
+ get { return 0; }
}
public static implicit operator bool(DynamicNull n)
{
return false;
}
+
public static implicit operator DateTime(DynamicNull n)
{
return DateTime.MinValue;
}
+
public static implicit operator int(DynamicNull n)
{
return 0;
}
+
public static implicit operator string(DynamicNull n)
{
return string.Empty;
@@ -92,6 +110,5 @@ namespace Umbraco.Core.Dynamics
{
return string.Empty;
}
-
}
}
diff --git a/src/Umbraco.Core/Dynamics/DynamicXml.cs b/src/Umbraco.Core/Dynamics/DynamicXml.cs
index a84389d2e5..7a4d714fbe 100644
--- a/src/Umbraco.Core/Dynamics/DynamicXml.cs
+++ b/src/Umbraco.Core/Dynamics/DynamicXml.cs
@@ -203,7 +203,7 @@ namespace Umbraco.Core.Dynamics
if (attempt.Result.Reason == DynamicInstanceHelper.TryInvokeMemberSuccessReason.FoundExtensionMethod
&& attempt.Exception != null && attempt.Exception is TargetInvocationException)
{
- result = new DynamicNull();
+ result = DynamicNull.Null;
return true;
}
diff --git a/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs b/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs
index 2fe86ddeec..215d94e566 100644
--- a/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs
+++ b/src/Umbraco.Core/Dynamics/ExtensionMethodFinder.cs
@@ -13,7 +13,86 @@ namespace Umbraco.Core.Dynamics
/// Utility class for finding extension methods on a type to execute
///
internal static class ExtensionMethodFinder
- {
+ {
+ private static readonly MethodInfo[] AllExtensionMethods;
+
+ static ExtensionMethodFinder()
+ {
+ AllExtensionMethods = TypeFinder.GetAssembliesWithKnownExclusions()
+ // assemblies that contain extension methods
+ .Where(a => a.IsDefined(typeof(ExtensionAttribute), false))
+ // types that contain extension methods
+ .SelectMany(a => a.GetTypes()
+ .Where(t => t.IsDefined(typeof(ExtensionAttribute), false) && t.IsSealed && t.IsGenericType == false && t.IsNested == false))
+ // actual extension methods
+ .SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public)
+ .Where(m => m.IsDefined(typeof(ExtensionAttribute), false)))
+ // and also IEnumerable extension methods - because the assembly is excluded
+ .Concat(typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public))
+ .ToArray();
+ }
+
+ // get all extension methods for type thisType, with name name,
+ // accepting argsCount arguments (not counting the instance of thisType).
+ private static IEnumerable GetExtensionMethods(Type thisType, string name, int argsCount)
+ {
+ var key = string.Format("{0}.{1}::{2}", thisType.FullName, name, argsCount);
+
+ var types = thisType.GetBaseTypes(true); // either do this OR have MatchFirstParameter handle the stuff... FIXME?
+
+ var methods = AllExtensionMethods
+ .Where(m => m.Name == name)
+ .Where(m => m.GetParameters().Length == argsCount)
+ .Where(m => MatchFirstParameter(thisType, m.GetParameters()[0].ParameterType));
+
+ // fixme - is this what we should cache?
+ return methods;
+ }
+
+ // find out whether the first parameter is a match for thisType
+ private static bool MatchFirstParameter(Type thisType, Type firstParameterType)
+ {
+ return MethodArgZeroHasCorrectTargetType(null, firstParameterType, thisType);
+ }
+
+ // get the single extension method for type thisType, with name name,
+ // that accepts the arguments in args (which does not contain the instance of thisType).
+ public static MethodInfo GetExtensionMethod(Type thisType, string name, object[] args)
+ {
+ MethodInfo result = null;
+ foreach (var method in GetExtensionMethods(thisType, name, args.Length).Where(m => MatchParameters(m, args)))
+ {
+ if (result == null)
+ result = method;
+ else
+ throw new AmbiguousMatchException("More than one matching extension method was found.");
+ }
+ return result;
+ }
+
+ // find out whether the method can accept the arguments
+ private static bool MatchParameters(MethodInfo method, IList args)
+ {
+ var parameters = method.GetParameters();
+
+ var i = 0;
+ for (; i < parameters.Length; ++i)
+ {
+ if (MatchParameter(parameters[i].ParameterType, args[i].GetType()) == false)
+ break;
+ }
+ return (i == parameters.Length);
+ }
+
+ internal static bool MatchParameter(Type parameterType, Type argumentType)
+ {
+ // public static int DoSomething(Foo foo, T t1, T t2)
+ // DoSomething(foo, t1, t2) => how can we match?!
+ return parameterType == argumentType; // fixme of course!
+ }
+
+ // BELOW IS THE ORIGINAL CODE...
+
///
/// Returns all extension methods found matching the definition
///
diff --git a/src/Umbraco.Core/Dynamics/PropertyResult.cs b/src/Umbraco.Core/Dynamics/PropertyResult.cs
index bb2165d184..26bc475b6c 100644
--- a/src/Umbraco.Core/Dynamics/PropertyResult.cs
+++ b/src/Umbraco.Core/Dynamics/PropertyResult.cs
@@ -1,65 +1,50 @@
using System;
using Umbraco.Core.Models;
-using umbraco.interfaces;
using System.Web;
namespace Umbraco.Core.Dynamics
{
- internal class PropertyResult : IPublishedContentProperty, IHtmlString
- {
- internal PropertyResult(IPublishedContentProperty source, PropertyResultType type)
+ internal class PropertyResult : IPublishedProperty, IHtmlString
+ {
+ private readonly IPublishedProperty _source;
+ private readonly string _alias;
+ private readonly object _value;
+ private readonly PropertyResultType _type;
+
+ internal PropertyResult(IPublishedProperty source, PropertyResultType type)
{
if (source == null) throw new ArgumentNullException("source");
-
- Alias = source.Alias;
- Value = source.Value;
- Version = source.Version;
- PropertyType = type;
+
+ _type = type;
+ _source = source;
}
- internal PropertyResult(string alias, object value, Guid version, PropertyResultType type)
+
+ internal PropertyResult(string alias, object value, Guid version, PropertyResultType type)
{
if (alias == null) throw new ArgumentNullException("alias");
if (value == null) throw new ArgumentNullException("value");
- Alias = alias;
- Value = value;
- Version = version;
- PropertyType = type;
+ _type = type;
+ _alias = alias;
+ _value = value;
}
- internal PropertyResultType PropertyType { get; private set; }
-
- public string Alias { get; private set; }
+ internal PropertyResultType PropertyType { get { return _type; } }
- public object Value { get; private set; }
-
- ///
- /// Returns the value as a string output, this is used in the final rendering process of a property
- ///
- internal string ValueAsString
- {
- get
- {
- return Value == null ? "" : Convert.ToString(Value);
- }
- }
-
- public Guid Version { get; private set; }
-
-
- ///
- /// The Id of the document for which this property belongs to
- ///
- public int DocumentId { get; set; }
-
- ///
- /// The alias of the document type alias for which this property belongs to
- ///
- public string DocumentTypeAlias { get; set; }
+ public string Alias { get { return _source == null ? _alias : _source.Alias; } }
+ public object RawValue { get { return _source == null ? _value : _source.RawValue; } }
+ public bool HasValue { get { return _source == null || _source.HasValue; } }
+ public object Value { get { return _source == null ? _value : _source.Value; } }
+ public object XPathValue { get { return Value == null ? null : Value.ToString(); } }
+ // implements IHtmlString.ToHtmlString
public string ToHtmlString()
{
- return ValueAsString;
+ // note - use RawValue here, because that's what the original
+ // Razor macro engine seems to do...
+
+ var value = RawValue;
+ return value == null ? string.Empty : value.ToString();
}
}
}
diff --git a/src/Umbraco.Core/Models/IPublishedContent.cs b/src/Umbraco.Core/Models/IPublishedContent.cs
index 6f55bdb413..5c93b32c16 100644
--- a/src/Umbraco.Core/Models/IPublishedContent.cs
+++ b/src/Umbraco.Core/Models/IPublishedContent.cs
@@ -1,20 +1,48 @@
using System;
using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Diagnostics;
+using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.Models
{
///
- /// Defines a published item in Umbraco
+ /// Represents a cached content.
///
///
- /// A replacement for INode which needs to occur since INode doesn't contain the document type alias
- /// and INode is poorly formatted with mutable properties (i.e. Lists instead of IEnumerable)
+ /// SD: A replacement for INode which needs to occur since INode doesn't contain the document type alias
+ /// and INode is poorly formatted with mutable properties (i.e. Lists instead of IEnumerable).
+ /// Stephan: initially, that was for cached published content only. Now, we're using it also for
+ /// cached preview (so, maybe unpublished) content. A better name would therefore be ICachedContent, as
+ /// has been suggested. However, can't change now. Maybe in v7?
///
- public interface IPublishedContent
- {
- int Id { get; }
+ public interface IPublishedContent
+ {
+ #region ContentSet
+
+ // Because of http://issues.umbraco.org/issue/U4-1797 and in order to implement
+ // Index() and methods that derive from it such as IsFirst(), IsLast(), etc... all
+ // content items must know about their containing content set.
+
+ ///
+ /// Gets the content set to which the content belongs.
+ ///
+ /// The default set consists in the siblings of the content (including the content
+ /// itself) ordered by sortOrder .
+ IEnumerable ContentSet { get; }
+
+ #endregion
+
+ #region ContentType
+
+ ///
+ /// Gets the content type.
+ ///
+ PublishedContentType ContentType { get; }
+
+ #endregion
+
+ #region Content
+
+ int Id { get; }
int TemplateId { get; }
int SortOrder { get; }
string Name { get; }
@@ -31,32 +59,97 @@ namespace Umbraco.Core.Models
Guid Version { get; }
int Level { get; }
string Url { get; }
+
+ ///
+ /// Gets a value indicating whether the content is a content (aka a document) or a media.
+ ///
PublishedItemType ItemType { get; }
+
+ ///
+ /// Gets a value indicating whether the content is draft.
+ ///
+ /// A content is draft when it is the unpublished version of a content, which may
+ /// have a published version, or not.
+ bool IsDraft { get; }
+
+ ///
+ /// Gets the index of the published content within its current owning content set.
+ ///
+ /// The index of the published content within its current owning content set.
+ int GetIndex();
+
+ #endregion
+
+ #region Tree
+
+ ///
+ /// Gets the parent of the content.
+ ///
+ /// The parent of root content is null .
IPublishedContent Parent { get; }
+
+ ///
+ /// Gets the children of the content.
+ ///
+ /// Children are sorted by their sortOrder.
IEnumerable Children { get; }
- ICollection Properties { get; }
+ #endregion
- ///
- /// Returns the property value for the property alias specified
- ///
- ///
- ///
- object this[string propertyAlias] { get; }
+ #region Properties
- ///
- /// Returns a property on the object based on an alias
+ ///
+ /// Gets the properties of the content.
+ ///
+ ///
+ /// Contains one IPublishedProperty for each property defined for the content type, including
+ /// inherited properties. Some properties may have no value.
+ /// The properties collection of an IPublishedContent instance should be read-only ie it is illegal
+ /// to add properties to the collection.
+ ///
+ ICollection Properties { get; }
+
+ ///
+ /// Gets a property identified by its alias.
+ ///
+ /// The property alias.
+ /// The property identified by the alias.
+ ///
+ /// If the content type has no property with that alias, including inherited properties, returns null ,
+ /// otherwise return a property -- that may have no value (ie HasValue is false ).
+ /// The alias is case-insensitive.
+ ///
+ IPublishedProperty GetProperty(string alias);
+
+ ///
+ /// Gets a property identified by its alias.
+ ///
+ /// The property alias.
+ /// A value indicating whether to navigate the tree upwards until a property with a value is found.
+ /// The property identified by the alias.
+ ///
+ /// Navigate the tree upwards and look for a property with that alias and with a value (ie HasValue is true ).
+ /// If found, return the property. If no property with that alias is found, having a value or not, return null . Otherwise
+ /// return the first property that was found with the alias but had no value (ie HasValue is false ).
+ /// The alias is case-insensitive.
+ ///
+ IPublishedProperty GetProperty(string alias, bool recurse);
+
+ ///
+ /// Gets the value of a property identified by its alias.
///
- ///
- ///
- ///
- /// Although we do have a a property to return Properties of the object, in some cases a custom implementation may not know
- /// about all properties until specifically asked for one by alias.
- ///
- /// This method is mostly used in places such as DynamicPublishedContent when trying to resolve a property based on an alias.
- /// In some cases Pulish Stores, a property value may exist in multiple places and we need to fallback to different cached locations
- /// therefore sometimes the 'Properties' collection may not be sufficient.
- ///
- IPublishedContentProperty GetProperty(string alias);
- }
+ /// The property alias.
+ /// The value of the property identified by the alias.
+ ///
+ /// If GetProperty(alias) is null then returns null else return GetProperty(alias).Value .
+ /// So if the property has no value, returns the default value for that property type.
+ /// This one is defined here really because we cannot define index extension methods, but all it should do is:
+ /// var p = GetProperty(alias); return p == null ? null : p.Value; and nothing else.
+ /// The recursive syntax (eg "_title") is _not_ supported here.
+ /// The alias is case-insensitive.
+ ///
+ object this[string alias] { get; } // fixme - kill in v7
+
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/IPublishedContentProperty.cs b/src/Umbraco.Core/Models/IPublishedContentProperty.cs
deleted file mode 100644
index 6361c29683..0000000000
--- a/src/Umbraco.Core/Models/IPublishedContentProperty.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-
-namespace Umbraco.Core.Models
-{
- public interface IPublishedContentProperty
- {
- string Alias { get; }
- object Value { get; }
- Guid Version { get; }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/IPublishedProperty.cs b/src/Umbraco.Core/Models/IPublishedProperty.cs
new file mode 100644
index 0000000000..1b5813874f
--- /dev/null
+++ b/src/Umbraco.Core/Models/IPublishedProperty.cs
@@ -0,0 +1,60 @@
+using System;
+
+namespace Umbraco.Core.Models
+{
+ ///
+ /// Represents a property of an IPublishedContent .
+ ///
+ public interface IPublishedProperty
+ {
+ ///
+ /// Gets the alias of the property.
+ ///
+ string Alias { get; }
+
+ ///
+ /// Gets a value indicating whether the property has a value.
+ ///
+ ///
+ /// This is somewhat implementation-dependent -- depending on whatever IPublishedCache considers
+ /// a missing value.
+ /// The XmlPublishedCache raw values are strings, and it will consider missing, null or empty (and
+ /// that includes whitespace-only) strings as "no value".
+ /// Other caches that get their raw value from the database would consider that a property has "no
+ /// value" if it is missing, null, or an empty string (including whitespace-only).
+ ///
+ bool HasValue { get; }
+
+ ///
+ /// Gets the raw value of the property.
+ ///
+ ///
+ /// The raw value is whatever was passed to the property when it was instanciated, and it is
+ /// somewhat implementation-dependent -- depending on how the IPublishedCache is implemented.
+ /// The XmlPublishedCache raw values are strings exclusively since they come from the Xml cache.
+ /// For other cachesthat get their raw value from the database, it would be either a string,
+ /// an integer (Int32), or a date and time (DateTime).
+ ///
+ object RawValue { get; }
+
+ ///
+ /// Gets the value of the property.
+ ///
+ ///
+ /// The value is what you want to use when rendering content in an MVC view ie in C#.
+ /// It can be null, or any type of CLR object.
+ /// It has been fully prepared and processed by the appropriate converters.
+ ///
+ object Value { get; }
+
+ ///
+ /// Gets the XPath value of the property.
+ ///
+ ///
+ /// The XPath value is what you want to use when navigating content via XPath eg in the XSLT engine.
+ /// It must be either null, or a non-empty string, or an XPathNavigator.
+ /// It has been fully prepared and processed by the appropriate converters.
+ ///
+ object XPathValue { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentExtended.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentExtended.cs
new file mode 100644
index 0000000000..1a05c2e07a
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentExtended.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Umbraco.Core.Models.PublishedContent
+{
+ ///
+ /// Provides methods to handle extended content.
+ ///
+ internal interface IPublishedContentExtended : IPublishedContent
+ {
+ ///
+ /// Adds a property to the extended content.
+ ///
+ /// The property to add.
+ void AddProperty(IPublishedProperty property);
+
+ ///
+ /// Gets a value indicating whether properties were added to the extended content.
+ ///
+ bool HasAddedProperties { get; }
+
+ ///
+ /// Sets the content set of the extended content.
+ ///
+ ///
+ void SetContentSet(IEnumerable contentSet);
+
+ ///
+ /// Resets the content set of the extended content.
+ ///
+ void ClearContentSet();
+
+ ///
+ /// Sets the index of the extended content.
+ ///
+ /// The index value.
+ void SetIndex(int value);
+
+ ///
+ /// Resets the index of the extended content.
+ ///
+ void ClearIndex();
+ }
+}
diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentModelFactory.cs
new file mode 100644
index 0000000000..cd58a43b54
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentModelFactory.cs
@@ -0,0 +1,16 @@
+namespace Umbraco.Core.Models.PublishedContent
+{
+ ///
+ /// Provides the model creation service.
+ ///
+ internal interface IPublishedContentModelFactory
+ {
+ ///
+ /// Creates a strongly-typed model representing a published content.
+ ///
+ /// The original published content.
+ /// The strongly-typed model representing the published content, or the published content
+ /// itself it the factory has no model for that content type.
+ IPublishedContent CreateModel(IPublishedContent content);
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs
new file mode 100644
index 0000000000..eee3a194dd
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+
+namespace Umbraco.Core.Models.PublishedContent
+{
+
+ public class PublishedContentExtended : PublishedContentWrapped, IPublishedContentExtended
+ {
+ #region Constructor
+
+ // protected for models, private for our static Extend method
+ protected PublishedContentExtended(IPublishedContent content)
+ : base(content)
+ { }
+
+ #endregion
+
+ #region Index
+
+ private int? _index;
+
+ public override int GetIndex()
+ {
+ // fast
+ if (_index.HasValue) return _index.Value;
+
+ // slow -- and don't cache, not in a set
+ if (_contentSet == null) return Content.GetIndex();
+
+ // slow -- but cache for next time
+ var index = _contentSet.FindIndex(x => x.Id == Id);
+ if (index < 0)
+ throw new IndexOutOfRangeException("Could not find content in the content set.");
+ _index = index;
+ return index;
+ }
+
+ #endregion
+
+ #region Extend
+
+ internal static IPublishedContentExtended Extend(IPublishedContent content, IEnumerable contentSet)
+ {
+ var wrapped = content as PublishedContentExtended;
+ while (wrapped != null && ((IPublishedContentExtended)wrapped).HasAddedProperties == false)
+ wrapped = (content = wrapped.Unwrap()) as PublishedContentExtended;
+
+ // if the factory returns something else than content it means it has created
+ // a model, and then that model has to inherit from PublishedContentExtended,
+ // => implements the internal IPublishedContentExtended.
+
+ var model = PublishedContentModelFactory.CreateModel(content);
+ var extended = model == content // == means the factory did not create a model
+ ? new PublishedContentExtended(content) // so we have to extend
+ : model; // else we can use what the factory returned
+
+ var extended2 = extended as IPublishedContentExtended;
+ if (extended2 != null) // always true, but keeps Resharper happy
+ extended2.SetContentSet(contentSet);
+ return extended2;
+ }
+
+ #endregion
+
+ #region IPublishedContentExtended
+
+ void IPublishedContentExtended.AddProperty(IPublishedProperty property)
+ {
+ if (_properties == null)
+ _properties = new Collection();
+ _properties.Add(property);
+ }
+
+ bool IPublishedContentExtended.HasAddedProperties
+ {
+ get { return _properties != null; }
+ }
+
+ void IPublishedContentExtended.SetContentSet(IEnumerable contentSet)
+ {
+ _contentSet = contentSet;
+ }
+
+ void IPublishedContentExtended.ClearContentSet()
+ {
+ _contentSet = null;
+ }
+
+ void IPublishedContentExtended.SetIndex(int value)
+ {
+ _index = value;
+ }
+
+ void IPublishedContentExtended.ClearIndex()
+ {
+ _index = null;
+ }
+
+ #endregion
+
+ #region Content set
+
+ private IEnumerable _contentSet;
+
+ public override IEnumerable ContentSet
+ {
+ get { return _contentSet ?? Content.ContentSet; }
+ }
+
+ #endregion
+
+ #region Properties
+
+ private ICollection _properties;
+
+ public override ICollection Properties
+ {
+ get
+ {
+ return _properties == null
+ ? Content.Properties
+ : Content.Properties.Union(_properties).ToList();
+ }
+ }
+
+ public override object this[string alias]
+ {
+ get
+ {
+ if (_properties != null)
+ {
+ var property = _properties.FirstOrDefault(prop => prop.Alias.InvariantEquals(alias));
+ if (property != null) return property.HasValue ? property.Value : null;
+ }
+ return Content[alias];
+ }
+ }
+
+ public override IPublishedProperty GetProperty(string alias)
+ {
+ return _properties == null
+ ? Content.GetProperty(alias)
+ : _properties.FirstOrDefault(prop => prop.Alias.InvariantEquals(alias)) ?? Content.GetProperty(alias);
+ }
+
+ #endregion
+ }
+
+}
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactory.cs
new file mode 100644
index 0000000000..ce63a640e6
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactory.cs
@@ -0,0 +1,20 @@
+namespace Umbraco.Core.Models.PublishedContent
+{
+ ///
+ /// Provides strongly typed published content models services.
+ ///
+ internal static class PublishedContentModelFactory
+ {
+ ///
+ /// Creates a strongly typed published content model for an internal published content.
+ ///
+ /// The internal published content.
+ /// The strongly typed published content model.
+ public static IPublishedContent CreateModel(IPublishedContent content)
+ {
+ return PublishedContentModelFactoryResolver.Current.HasValue
+ ? PublishedContentModelFactoryResolver.Current.Factory.CreateModel(content)
+ : content;
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactoryResolver.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactoryResolver.cs
new file mode 100644
index 0000000000..6995cafc7f
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModelFactoryResolver.cs
@@ -0,0 +1,45 @@
+using Umbraco.Core.ObjectResolution;
+
+namespace Umbraco.Core.Models.PublishedContent
+{
+ ///
+ /// Resolves the IPublishedContentModelFactory object.
+ ///
+ internal class PublishedContentModelFactoryResolver : SingleObjectResolverBase
+ {
+ ///
+ /// Initializes a new instance of the .
+ ///
+ /// The resolver is created by the WebBootManager and thus the constructor remains internal.
+ internal PublishedContentModelFactoryResolver()
+ : base()
+ { }
+
+ ///
+ /// Initializes a new instance of the with a factory.
+ ///
+ /// The factory.
+ /// The resolver is created by the WebBootManager and thus the constructor remains internal.
+ internal PublishedContentModelFactoryResolver(IPublishedContentModelFactory factory)
+ : base(factory)
+ { }
+
+ ///
+ /// Sets the factory.
+ ///
+ /// The factory.
+ /// For developers, at application startup.
+ public void SetFactory(IPublishedContentModelFactory factory)
+ {
+ Value = factory;
+ }
+
+ ///
+ /// Gets the factory.
+ ///
+ public IPublishedContentModelFactory Factory
+ {
+ get { return Value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentOrderedSet.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentOrderedSet.cs
new file mode 100644
index 0000000000..4770051649
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentOrderedSet.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Umbraco.Core.Models.PublishedContent
+{
+ ///
+ /// Represents an ordered set of .
+ ///
+ /// The type of content.
+ public class PublishedContentOrderedSet : PublishedContentSet, IOrderedEnumerable
+ where T : class, IPublishedContent
+ {
+// ReSharper disable ParameterTypeCanBeEnumerable.Local
+ internal PublishedContentOrderedSet(IOrderedEnumerable content)
+// ReSharper restore ParameterTypeCanBeEnumerable.Local
+ : base(content)
+ { }
+
+ #region IOrderedEnumerable
+
+ public IOrderedEnumerable CreateOrderedEnumerable(Func keySelector, IComparer comparer, bool descending)
+ {
+ return new PublishedContentOrderedSet(((IOrderedEnumerable)Source).CreateOrderedEnumerable(keySelector, comparer, descending));
+ }
+
+ #endregion
+
+ // fixme wtf?!
+#if IMPLEMENT_LINQ_EXTENSIONS
+
+ // BEWARE!
+ // here, Source.Whatever() will invoke the System.Linq.Enumerable extension method
+ // and not the extension methods that we may have defined on IEnumerable or
+ // IOrderedEnumerable, provided that they are NOT within the scope at compile time.
+
+ #region Wrap methods returning IOrderedEnumerable
+
+ public PublishedContentOrderedSet ThenBy(Func keySelector)
+ {
+ return new PublishedContentOrderedSet(((IOrderedEnumerable)Source).ThenBy(keySelector));
+ }
+
+ public PublishedContentOrderedSet ThenBy(Func keySelector, IComparer comparer)
+ {
+ return new PublishedContentOrderedSet(((IOrderedEnumerable)Source).ThenBy(keySelector, comparer));
+ }
+
+ public PublishedContentOrderedSet ThenByDescending(Func keySelector)
+ {
+ return new PublishedContentOrderedSet(((IOrderedEnumerable)Source).ThenByDescending(keySelector));
+ }
+
+ public PublishedContentOrderedSet ThenByDescending(Func keySelector, IComparer comparer)
+ {
+ return new PublishedContentOrderedSet(((IOrderedEnumerable)Source).ThenByDescending(keySelector, comparer));
+ }
+
+ #endregion
+
+#endif
+ }
+}
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentSet.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentSet.cs
new file mode 100644
index 0000000000..94f54d03aa
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentSet.cs
@@ -0,0 +1,239 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Umbraco.Core.Models.PublishedContent
+{
+ ///
+ /// Represents a set of .
+ ///
+ /// The type of content.
+ ///
+ /// A ContentSet{T} is created from an IEnumerable{T} using the ToContentSet
+ /// extension method.
+ /// The content set source is enumerated only once. Same as what you get
+ /// when you call ToList on an IEnumerable. Only, ToList enumerates its source when
+ /// created, whereas a content set enumerates its source only when the content set itself
+ /// is enumerated.
+ ///
+ public class PublishedContentSet : IEnumerable
+ where T : class, IPublishedContent
+ {
+ // used by ToContentSet extension method to initialize a new set from an IEnumerable.
+ internal PublishedContentSet(IEnumerable source)
+ {
+ if (source == null)
+ throw new ArgumentNullException("source");
+ Source = source;
+ }
+
+ #region Source
+
+ protected readonly IEnumerable Source;
+
+ #endregion
+
+ #region Enumerated
+
+ // cache the enumeration so we don't enumerate more than once. Same as what you get
+ // when you call ToList on an IEnumerable. Only, ToList enumerates its source when
+ // created, whereas a content set enumerates its source only when the content set itself
+ // is enumerated.
+
+ // cache the wrapped items so if we reset the enumeration, we do not re-wrap everything (only new items).
+
+ private T[] _enumerated;
+ private readonly Dictionary _xContent = new Dictionary();
+
+ // wrap an item, ie create the actual clone for this set
+ private T MapContentAsT(T t)
+ {
+ // fixme - cleanup
+ return MapContent(t) /*.Content*/ as T;
+ }
+
+ // fixme - cleanup
+ internal IPublishedContentExtended /*Handle*/ MapContent(T t)
+ {
+ IPublishedContentExtended extend;
+ if (_xContent.TryGetValue(t, out extend) == false)
+ {
+ // fixme - cleanup
+ extend = PublishedContentExtended.Extend(t, this);
+ //extend = t.Extend(this);
+ var asT = extend as T;
+ //var asT = extend.Content as T;
+ if (asT == null)
+ throw new InvalidOperationException(string.Format("Failed extend a published content of type {0}."
+ + "Got {1} when expecting {2}.", t.GetType().FullName, extend /*.Content*/ .GetType().FullName, typeof(T).FullName));
+ _xContent[t] = extend;
+ }
+ return extend;
+ }
+
+ private T[] Enumerated
+ {
+ get
+ {
+ // enumerate the source and cache the result
+ // tell clones about their index within the set (for perfs purposes)
+ var index = 0;
+ return _enumerated ?? (_enumerated = Source.Select(t =>
+ {
+ var extend = MapContent(t);
+ extend.SetIndex(index++);
+ return extend /*.Content*/ as T; // fixme - cleanup
+ }).ToArray());
+ }
+ }
+
+ // indicates that the source has changed
+ // so the set can clear its inner caches
+ public void SourceChanged()
+ {
+ // reset the cached enumeration so it's enumerated again
+ if (_enumerated == null) return;
+ _enumerated = null;
+
+ foreach (var item in _xContent.Values)
+ item.ClearIndex();
+
+ var removed = _xContent.Keys.Except(Source);
+ foreach (var content in removed)
+ {
+ _xContent[content].ClearContentSet();
+ _xContent.Remove(content);
+ }
+ }
+
+ ///
+ /// Gets the number of items in the set.
+ ///
+ /// The number of items in the set.
+ /// Will cause the set to be enumerated if it hasn't been already.
+ public virtual int Count
+ {
+ get { return Enumerated.Length; }
+ }
+ #endregion
+
+ #region IEnumerable
+
+ public IEnumerator GetEnumerator()
+ {
+ return ((IEnumerable)Enumerated).GetEnumerator();
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ #endregion
+
+ #region Wrap methods returning T
+
+ public T ElementAt(int index)
+ {
+ return MapContentAsT(Source.ElementAt(index));
+ }
+
+ public T ElementAtOrDefault(int index)
+ {
+ var element = Source.ElementAtOrDefault(index);
+ return element == null ? null : MapContentAsT(element);
+ }
+
+ public T First()
+ {
+ return MapContentAsT(Source.First());
+ }
+
+ public T First(Func predicate)
+ {
+ return MapContentAsT(Source.First(predicate));
+ }
+
+ public T FirstOrDefault()
+ {
+ var first = Source.FirstOrDefault();
+ return first == null ? null : MapContentAsT(first);
+ }
+
+ public T FirstOrDefault(Func predicate)
+ {
+ var first = Source.FirstOrDefault(predicate);
+ return first == null ? null : MapContentAsT(first);
+ }
+
+ public T Last()
+ {
+ return MapContentAsT(Source.Last());
+ }
+
+ public T Last(Func predicate)
+ {
+ return MapContentAsT(Source.Last(predicate));
+ }
+
+ public T LastOrDefault()
+ {
+ var last = Source.LastOrDefault();
+ return last == null ? null : MapContentAsT(last);
+ }
+
+ public T LastOrDefault(Func predicate)
+ {
+ var last = Source.LastOrDefault(predicate);
+ return last == null ? null : MapContentAsT(last);
+ }
+
+ public T Single()
+ {
+ return MapContentAsT(Source.Single());
+ }
+
+ public T Single(Func predicate)
+ {
+ return MapContentAsT(Source.Single(predicate));
+ }
+
+ public T SingleOrDefault()
+ {
+ var single = Source.SingleOrDefault();
+ return single == null ? null : MapContentAsT(single);
+ }
+
+ public T SingleOrDefault(Func predicate)
+ {
+ var single = Source.SingleOrDefault(predicate);
+ return single == null ? null : MapContentAsT(single);
+ }
+
+ #endregion
+
+ #region Wrap methods returning IOrderedEnumerable
+
+ public PublishedContentOrderedSet OrderBy(Func keySelector)
+ {
+ return new PublishedContentOrderedSet(Source.OrderBy(keySelector));
+ }
+
+ public PublishedContentOrderedSet OrderBy(Func keySelector, IComparer comparer)
+ {
+ return new PublishedContentOrderedSet(Source.OrderBy(keySelector, comparer));
+ }
+
+ public PublishedContentOrderedSet OrderByDescending(Func keySelector)
+ {
+ return new PublishedContentOrderedSet(Source.OrderByDescending(keySelector));
+ }
+
+ public PublishedContentOrderedSet OrderByDescending(Func keySelector, IComparer comparer)
+ {
+ return new PublishedContentOrderedSet(Source.OrderByDescending(keySelector, comparer));
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs
new file mode 100644
index 0000000000..779fc267e9
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs
@@ -0,0 +1,168 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web.Caching;
+using Umbraco.Core.Cache;
+
+namespace Umbraco.Core.Models.PublishedContent
+{
+ ///
+ /// Represents an type.
+ ///
+ /// Instances of the class are immutable, ie
+ /// if the content type changes, then a new class needs to be created.
+ public class PublishedContentType
+ {
+ private readonly PublishedPropertyType[] _propertyTypes;
+
+ // fast alias-to-index xref containing both the raw alias and its lowercase version
+ private readonly Dictionary _indexes = new Dictionary();
+
+ // internal so it can be used by PublishedNoCache which does _not_ want to cache anything and so will never
+ // use the static cache getter PublishedContentType.GetPublishedContentType(alias) below - anything else
+ // should use it.
+ internal PublishedContentType(IContentTypeComposition contentType)
+ {
+ Id = contentType.Id;
+ Alias = contentType.Alias;
+ _propertyTypes = contentType.CompositionPropertyTypes
+ .Select(x => new PublishedPropertyType(this, x))
+ .ToArray();
+ InitializeIndexes();
+ }
+
+ // internal so it can be used for unit tests
+ internal PublishedContentType(int id, string alias, IEnumerable propertyTypes)
+ {
+ Id = id;
+ Alias = alias;
+ _propertyTypes = propertyTypes.ToArray();
+ foreach (var propertyType in _propertyTypes)
+ propertyType.ContentType = this;
+ InitializeIndexes();
+ }
+
+ private void InitializeIndexes()
+ {
+ for (var i = 0; i < _propertyTypes.Length; i++)
+ {
+ var propertyType = _propertyTypes[i];
+ _indexes[propertyType.Alias] = i;
+ _indexes[propertyType.Alias.ToLowerInvariant()] = i;
+ }
+ }
+
+ #region Content type
+
+ public int Id { get; private set; }
+ public string Alias { get; private set; }
+
+ #endregion
+
+ #region Properties
+
+ public IEnumerable PropertyTypes
+ {
+ get { return _propertyTypes; }
+ }
+
+ // alias is case-insensitive
+ // this is the ONLY place where we compare ALIASES!
+ public int GetPropertyIndex(string alias)
+ {
+ int index;
+ if (_indexes.TryGetValue(alias, out index)) return index; // fastest
+ if (_indexes.TryGetValue(alias.ToLowerInvariant(), out index)) return index; // slower
+ return -1;
+ }
+
+ // virtual for unit tests
+ public virtual PublishedPropertyType GetPropertyType(string alias)
+ {
+ var index = GetPropertyIndex(alias);
+ return GetPropertyType(index);
+ }
+
+ // virtual for unit tests
+ public virtual PublishedPropertyType GetPropertyType(int index)
+ {
+ return index >= 0 && index < _propertyTypes.Length ? _propertyTypes[index] : null;
+ }
+
+ #endregion
+
+ #region Cache
+
+ // note
+ // default cache refresher events will contain the ID of the refreshed / removed IContentType
+ // and not the alias. Also, we cannot hook into the cache refresher event here, because it belongs
+ // to Umbraco.Web, so we do it in Umbraco.Web.Models.PublishedContentTypeCaching.
+
+ // fixme
+ // how do we know a content type has changed? if just the property has changed, do we trigger an event?
+ // must run in debug mode to figure out... what happens when a DATATYPE changes? how do I get the type
+ // of a content right with the content and be sure it's OK?
+ // ******** HERE IS THE REAL ISSUE *******
+
+ static readonly ConcurrentDictionary ContentTypes = new ConcurrentDictionary();
+
+ // fixme - should not be public
+ internal static void ClearAll()
+ {
+ Logging.LogHelper.Debug("Clear all.");
+ ContentTypes.Clear();
+ }
+
+ // fixme - should not be public
+ internal static void ClearContentType(int id)
+ {
+ Logging.LogHelper.Debug("Clear content type w/id {0}.", () => id);
+
+ // see http://blogs.msdn.com/b/pfxteam/archive/2011/04/02/10149222.aspx
+ // that should be race-cond safe
+ ContentTypes.RemoveAll(kvp => kvp.Value.Id == id);
+ }
+
+ // fixme
+ internal static void ClearDataType(int id)
+ {
+ Logging.LogHelper.Debug("Clear data type w/id {0}.", () => id);
+
+ // see note in ClearContentType()
+ ContentTypes.RemoveAll(kvp => kvp.Value.PropertyTypes.Any(x => x.DataTypeId == id));
+ }
+
+ public static PublishedContentType Get(PublishedItemType itemType, string alias)
+ {
+ var key = (itemType == PublishedItemType.Content ? "content" : "media") + "::" + alias.ToLowerInvariant();
+ return ContentTypes.GetOrAdd(key, k => CreatePublishedContentType(itemType, alias));
+ }
+
+ private static PublishedContentType CreatePublishedContentType(PublishedItemType itemType, string alias)
+ {
+ if (GetPublishedContentTypeCallback != null)
+ return GetPublishedContentTypeCallback(alias);
+
+ var contentType = itemType == PublishedItemType.Content
+ ? (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias)
+ : (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias);
+
+ return new PublishedContentType(contentType);
+ }
+
+ // for unit tests - changing the callback must reset the cache obviously
+ private static Func _getPublishedContentTypeCallBack;
+ internal static Func GetPublishedContentTypeCallback
+ {
+ get { return _getPublishedContentTypeCallBack; }
+ set
+ {
+ ClearAll();
+ _getPublishedContentTypeCallBack = value;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs
new file mode 100644
index 0000000000..38e2537b40
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs
@@ -0,0 +1,208 @@
+using System;
+using System.Collections.Generic;
+
+namespace Umbraco.Core.Models.PublishedContent
+{
+ //
+ // This class has two purposes.
+ //
+ // - First, we cannot implement strongly-typed content by inheriting from some sort
+ // of "master content" because that master content depends on the actual content cache
+ // that is being used. It can be an XmlPublishedContent with the XmlPublishedCache,
+ // or just anything else.
+ //
+ // So we implement strongly-typed content by encapsulating whatever content is
+ // returned by the content cache, and providing extra properties (mostly) or
+ // methods or whatever. This class provides the base for such encapsulation.
+ //
+ // - Second, any time a content is used in a content set obtained from
+ // IEnumerable.ToContentSet(), it needs to be cloned and extended
+ // in order to know about its position in the set. This class provides the base
+ // for implementing such extension.
+ //
+
+ ///
+ /// Provides an abstract base class for IPublishedContent implementations that
+ /// wrap and extend another IPublishedContent .
+ ///
+ public abstract class PublishedContentWrapped : IPublishedContent
+ {
+ protected readonly IPublishedContent Content;
+
+ ///
+ /// Initialize a new instance of the class
+ /// with an IPublishedContent instance to wrap and extend.
+ ///
+ /// The content to wrap and extend.
+ protected PublishedContentWrapped(IPublishedContent content)
+ {
+ Content = content;
+ }
+
+ ///
+ /// Gets the wrapped content.
+ ///
+ /// The wrapped content, that was passed as an argument to the constructor.
+ public IPublishedContent Unwrap()
+ {
+ return Content;
+ }
+
+ #region ContentSet
+
+ public virtual IEnumerable ContentSet
+ {
+ get { return Content.ContentSet; }
+ }
+
+ #endregion
+
+ #region ContentType
+
+ public virtual PublishedContentType ContentType { get { return Content.ContentType; } }
+
+ #endregion
+
+ #region Content
+
+ public virtual int Id
+ {
+ get { return Content.Id; }
+ }
+
+ public virtual int TemplateId
+ {
+ get { return Content.TemplateId; }
+ }
+
+ public virtual int SortOrder
+ {
+ get { return Content.SortOrder; }
+ }
+
+ public virtual string Name
+ {
+ get { return Content.Name; }
+ }
+
+ public virtual string UrlName
+ {
+ get { return Content.UrlName; }
+ }
+
+ public virtual string DocumentTypeAlias
+ {
+ get { return Content.DocumentTypeAlias; }
+ }
+
+ public virtual int DocumentTypeId
+ {
+ get { return Content.DocumentTypeId; }
+ }
+
+ public virtual string WriterName
+ {
+ get { return Content.WriterName; }
+ }
+
+ public virtual string CreatorName
+ {
+ get { return Content.CreatorName; }
+ }
+
+ public virtual int WriterId
+ {
+ get { return Content.WriterId; }
+ }
+
+ public virtual int CreatorId
+ {
+ get { return Content.CreatorId; }
+ }
+
+ public virtual string Path
+ {
+ get { return Content.Path; }
+ }
+
+ public virtual DateTime CreateDate
+ {
+ get { return Content.CreateDate; }
+ }
+
+ public virtual DateTime UpdateDate
+ {
+ get { return Content.UpdateDate; }
+ }
+
+ public virtual Guid Version
+ {
+ get { return Content.Version; }
+ }
+
+ public virtual int Level
+ {
+ get { return Content.Level; }
+ }
+
+ public virtual string Url
+ {
+ get { return Content.Url; }
+ }
+
+ public virtual PublishedItemType ItemType
+ {
+ get { return Content.ItemType; }
+ }
+
+ public virtual bool IsDraft
+ {
+ get { return Content.IsDraft; }
+ }
+
+ public virtual int GetIndex()
+ {
+ return Content.GetIndex();
+ }
+
+ #endregion
+
+ #region Tree
+
+ public virtual IPublishedContent Parent
+ {
+ get { return Content.Parent; }
+ }
+
+ public virtual IEnumerable Children
+ {
+ get { return Content.Children; }
+ }
+
+ #endregion
+
+ #region Properties
+
+ public virtual ICollection Properties
+ {
+ get { return Content.Properties; }
+ }
+
+ public virtual object this[string alias]
+ {
+ get { return Content[alias]; }
+ }
+
+ public virtual IPublishedProperty GetProperty(string alias)
+ {
+ return Content.GetProperty(alias);
+ }
+
+ public virtual IPublishedProperty GetProperty(string alias, bool recurse)
+ {
+ return Content.GetProperty(alias, recurse);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs
new file mode 100644
index 0000000000..872efb0d46
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace Umbraco.Core.Models.PublishedContent
+{
+ ///
+ /// Provides a base class for IPublishedProperty implementations which converts and caches
+ /// the value source to the actual value to use when rendering content.
+ ///
+ internal abstract class PublishedPropertyBase : IPublishedProperty
+ {
+ public readonly PublishedPropertyType PropertyType;
+
+ protected PublishedPropertyBase(PublishedPropertyType propertyType)
+ {
+ if (propertyType == null)
+ throw new ArgumentNullException("propertyType");
+ PropertyType = propertyType;
+ }
+
+ public string Alias
+ {
+ get { return PropertyType.Alias; }
+ }
+
+ // these have to be provided by the actual implementation
+ public abstract bool HasValue { get; }
+ public abstract object RawValue { get; }
+ public abstract object Value { get; }
+ public abstract object XPathValue { get; }
+ }
+}
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs
new file mode 100644
index 0000000000..35ff64cd3e
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs
@@ -0,0 +1,238 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Xml.Linq;
+using System.Xml.XPath;
+using Umbraco.Core.Dynamics;
+using Umbraco.Core.PropertyEditors;
+
+namespace Umbraco.Core.Models.PublishedContent
+{
+ ///
+ /// Represents an type.
+ ///
+ /// Instances of the class are immutable, ie
+ /// if the property type changes, then a new class needs to be created.
+ public class PublishedPropertyType
+ {
+ public PublishedPropertyType(PublishedContentType contentType, PropertyType propertyType)
+ {
+ // one control identified by its DataTypeGuid
+ // can be used to create several datatypes, identified by their DataTypeDefinitionId and supporting prevalues
+ // which can be used to create several property types, identified by their Id
+
+ ContentType = contentType;
+ Id = propertyType.Id;
+ Alias = propertyType.Alias;
+
+ DataTypeId = propertyType.DataTypeDefinitionId;
+ EditorGuid = propertyType.DataTypeId;
+
+ InitializeConverters();
+ }
+
+ // for unit tests
+ internal PublishedPropertyType(string alias, Guid dataTypeGuid, int propertyTypeId, int dataTypeDefinitionId)
+ {
+ // ContentType to be set by PublishedContentType when creating it
+ Id = propertyTypeId;
+ Alias = alias;
+
+ DataTypeId = dataTypeDefinitionId;
+ EditorGuid = dataTypeGuid;
+
+ InitializeConverters();
+ }
+
+ #region Property type
+
+ // gets the content type
+ // internally set by PublishedContentType constructor
+ public PublishedContentType ContentType { get; internal set; }
+
+ // gets the property type id
+ public int Id { get; private set; }
+
+ // gets the property alias
+ public string Alias { get; private set; }
+
+ public int DataTypeId { get; private set; }
+
+ public Guid EditorGuid { get; private set; }
+
+ #endregion
+
+ #region Converters
+
+ private IPropertyValueConverter _sourceConverter;
+ private IPropertyValueConverter _objectConverter;
+ private IPropertyValueConverter _xpathConverter;
+
+ private PropertyCacheLevel _sourceCacheLevel;
+ private PropertyCacheLevel _objectCacheLevel;
+ private PropertyCacheLevel _xpathCacheLevel;
+
+ private void InitializeConverters()
+ {
+ var converters = PropertyValueConvertersResolver.Current.Converters.ToArray();
+
+ // fixme - get rid of the IPropertyValueEditorConverter support eventually
+ _sourceConverter = GetSingleConverterOrDefault(converters.Union(GetCompatConverters()), x => x.IsDataToSourceConverter(this), "data-to-source");
+ _sourceCacheLevel = GetCacheLevel(_sourceConverter, PropertyCacheValue.Source);
+
+ _objectConverter = GetSingleConverterOrDefault(converters, x => x.IsSourceToObjectConverter(this), "source-to-object");
+ _objectCacheLevel = GetCacheLevel(_objectConverter, PropertyCacheValue.Object);
+ if (_objectCacheLevel < _sourceCacheLevel)
+ _objectCacheLevel = _sourceCacheLevel; // quietely fix the inconsistency, no need to throw
+
+ _xpathConverter = GetSingleConverterOrDefault(converters, x => x.IsSourceToXPathConverter(this), "source-to-xpath");
+ _objectCacheLevel = GetCacheLevel(_objectConverter, PropertyCacheValue.XPath);
+ if (_xpathCacheLevel < _sourceCacheLevel)
+ _xpathCacheLevel = _sourceCacheLevel; // quietely fix the inconsistency, no need to throw
+ }
+
+ static IPropertyValueConverter GetSingleConverterOrDefault(IEnumerable converters,
+ Func predicate, string name)
+ {
+ IPropertyValueConverter result = null;
+ foreach (var converter in converters.Where(predicate))
+ {
+ if (result == null) result = converter;
+ else throw new InvalidOperationException("More than one " + name + " converter.");
+ }
+ return result;
+ }
+
+ static PropertyCacheLevel GetCacheLevel(IPropertyValueConverter converter, PropertyCacheValue value)
+ {
+ if (converter == null)
+ return PropertyCacheLevel.Request;
+
+ var attr = converter.GetType().GetCustomAttributes(false)
+ .FirstOrDefault(x => x.Value == value || x.Value == PropertyCacheValue.All);
+
+ return attr == null ? PropertyCacheLevel.Request : attr.Level;
+ }
+
+ // converts the raw value into the source value
+ // uses converters, else falls back to dark (& performance-wise expensive) magic
+ // source: the property raw value
+ // preview: whether we are previewing or not
+ public object ConvertDataToSource(object source, bool preview)
+ {
+ // use the converter else use dark (& performance-wise expensive) magic
+ return _sourceConverter != null
+ ? _sourceConverter.ConvertDataToSource(this, source, preview)
+ : ConvertSourceUsingDarkMagic(source);
+ }
+
+ // gets the source cache level
+ public PropertyCacheLevel SourceCacheLevel { get { return _sourceCacheLevel; } }
+
+ // converts the source value into the clr value
+ // uses converters, else returns the source value
+ // source: the property source value
+ // preview: whether we are previewing or not
+ public object ConvertSourceToObject(object source, bool preview)
+ {
+ // use the converter if any
+ // else just return the source value
+ return _objectConverter != null
+ ? _objectConverter.ConvertSourceToObject(this, source, preview)
+ : source;
+ }
+
+ // gets the value cache level
+ public PropertyCacheLevel ObjectCacheLevel { get { return _objectCacheLevel; } }
+
+ // converts the source value into the xpath value
+ // uses the converter else returns the source value as a string
+ // if successful, returns either a string or an XPathNavigator
+ // source: the property source value
+ // preview: whether we are previewing or not
+ public object ConvertSourceToXPath(object source, bool preview)
+ {
+ // use the converter if any
+ if (_xpathConverter != null)
+ return _xpathConverter.ConvertSourceToXPath(this, source, preview);
+
+ // else just return the source value as a string or an XPathNavigator
+ if (source == null) return null;
+ var xElement = source as XElement;
+ if (xElement != null)
+ return xElement.CreateNavigator();
+ return source.ToString().Trim();
+ }
+
+ // gets the xpath cache level
+ public PropertyCacheLevel XPathCacheLevel { get { return _xpathCacheLevel; } }
+
+ private static object ConvertSourceUsingDarkMagic(object source)
+ {
+ // convert to string
+ var stringSource = source as string;
+ if (stringSource == null) return source; // not a string => return the object
+ stringSource = stringSource.Trim();
+ if (stringSource.Length == 0) return null; // empty string => return null
+
+ // try numbers and booleans
+ // make sure we use the invariant culture ie a dot decimal point, comma is for csv
+ // NOTE far from perfect: "01a" is returned as a string but "012" is returned as an integer...
+ int i;
+ if (int.TryParse(stringSource, NumberStyles.Integer, CultureInfo.InvariantCulture, out i))
+ return i;
+ float f;
+ if (float.TryParse(stringSource, NumberStyles.Float, CultureInfo.InvariantCulture, out f))
+ return f;
+ bool b;
+ if (bool.TryParse(stringSource, out b))
+ return b;
+
+ // try xml - that is expensive, performance-wise
+ XElement elt;
+ if (XmlHelper.TryCreateXElementFromPropertyValue(stringSource, out elt))
+ return Attempt.Succeed(new DynamicXml(elt)); // xml => return DynamicXml for compatiblity's sake
+
+ return source;
+ }
+
+ #endregion
+
+ #region Compat
+
+ // fixme - remove in v7
+ // backward-compatibility: support IPropertyEditorValueConverter while we have to
+ IEnumerable GetCompatConverters()
+ {
+ return PropertyEditorValueConvertersResolver.HasCurrent
+ ? PropertyEditorValueConvertersResolver.Current.Converters
+ .Where(x => x.IsConverterFor(EditorGuid, ContentType.Alias, Alias))
+ .Select(x => new CompatConverter(x))
+ : Enumerable.Empty();
+ }
+
+ class CompatConverter : PropertyValueConverterBase
+ {
+ private readonly IPropertyEditorValueConverter _converter;
+
+ public CompatConverter(IPropertyEditorValueConverter converter)
+ {
+ _converter = converter;
+ }
+
+ public override bool IsDataToSourceConverter(PublishedPropertyType propertyType)
+ {
+ return true;
+ }
+
+ public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ // NOTE: ignore preview, because IPropertyEditorValueConverter does not support it
+ return _converter.ConvertPropertyValue(source).Result;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Umbraco.Core/Models/PublishedItemType.cs b/src/Umbraco.Core/Models/PublishedItemType.cs
index b9fb603735..a98a3c2a75 100644
--- a/src/Umbraco.Core/Models/PublishedItemType.cs
+++ b/src/Umbraco.Core/Models/PublishedItemType.cs
@@ -1,11 +1,18 @@
namespace Umbraco.Core.Models
{
///
- /// The type of published item
+ /// The type of published content, ie whether it is a content or a media.
///
public enum PublishedItemType
{
+ ///
+ /// A content, ie what was formerly known as a document.
+ ///
Content,
+
+ ///
+ /// A media.
+ ///
Media
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/PropertyEditors/DatePickerPropertyEditorValueConverter.cs b/src/Umbraco.Core/PropertyEditors/DatePickerPropertyEditorValueConverter.cs
deleted file mode 100644
index 658de7e399..0000000000
--- a/src/Umbraco.Core/PropertyEditors/DatePickerPropertyEditorValueConverter.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-using System.Linq;
-
-namespace Umbraco.Core.PropertyEditors
-{
- internal class DatePickerPropertyEditorValueConverter : IPropertyEditorValueConverter
- {
- public bool IsConverterFor(Guid propertyEditorId, string docTypeAlias, string propertyTypeAlias)
- {
- return (new[]
- {
- Guid.Parse(Constants.PropertyEditors.DateTime),
- Guid.Parse(Constants.PropertyEditors.Date)
- }).Contains(propertyEditorId);
- }
-
- ///
- /// return a DateTime object even if the value is a string
- ///
- ///
- ///
- public Attempt ConvertPropertyValue(object value)
- {
- return value.TryConvertTo(typeof(DateTime));
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/PropertyEditors/DatePickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/DatePickerValueConverter.cs
new file mode 100644
index 0000000000..1ce4e74dd0
--- /dev/null
+++ b/src/Umbraco.Core/PropertyEditors/DatePickerValueConverter.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Globalization;
+using System.Linq;
+using System.Xml;
+using Umbraco.Core.Models.PublishedContent;
+
+namespace Umbraco.Core.PropertyEditors
+{
+ [PropertyValueType(typeof(DateTime))]
+ [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)]
+ internal class DatePickerValueConverter : IPropertyValueConverter
+ {
+ private static readonly Guid[] DataTypeGuids = new[]
+ {
+ Guid.Parse(Constants.PropertyEditors.DateTime),
+ Guid.Parse(Constants.PropertyEditors.Date)
+ };
+
+ public bool IsDataToSourceConverter(PublishedPropertyType propertyType)
+ {
+ return DataTypeGuids.Contains(propertyType.EditorGuid);
+ }
+
+ public object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ if (source == null) return DateTime.MinValue;
+
+ // in XML a DateTime is: string - format "yyyy-MM-ddTHH:mm:ss"
+ var sourceString = source as string;
+ if (sourceString != null)
+ {
+ DateTime value;
+ return DateTime.TryParseExact(sourceString, "yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out value)
+ ? value
+ : DateTime.MinValue;
+ }
+
+ // in the database a DateTime is: DateTime
+ // default value is: DateTime.MinValue
+ return (source is DateTime)
+ ? source
+ : DateTime.MinValue;
+ }
+
+ public bool IsSourceToObjectConverter(PublishedPropertyType propertyType)
+ {
+ return IsDataToSourceConverter(propertyType);
+ }
+
+ public object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ // source should come from ConvertSource and be a DateTime already
+ return source;
+ }
+
+ public bool IsSourceToXPathConverter(PublishedPropertyType propertyType)
+ {
+ return IsDataToSourceConverter(propertyType);
+ }
+
+ public object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ // source should come from ConvertSource and be a DateTime already
+ return XmlConvert.ToString((DateTime) source, "yyyy-MM-ddTHH:mm:ss");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyEditorValueConverter.cs b/src/Umbraco.Core/PropertyEditors/IPropertyEditorValueConverter.cs
index 386a350e29..1fd767da53 100644
--- a/src/Umbraco.Core/PropertyEditors/IPropertyEditorValueConverter.cs
+++ b/src/Umbraco.Core/PropertyEditors/IPropertyEditorValueConverter.cs
@@ -1,31 +1,30 @@
using System;
-using Umbraco.Core.Dynamics;
namespace Umbraco.Core.PropertyEditors
{
+ ///
+ /// Maps a property source value to a data object.
+ ///
+ // fixme - should obsolete, use IPropertyValueConverter instead
public interface IPropertyEditorValueConverter
{
+ ///
+ /// Returns a value indicating whether this provider applies to the specified property.
+ ///
+ /// A Guid identifying the property datatype.
+ /// The content type alias.
+ /// The property alias.
+ /// True if this provider applies to the specified property.
+ bool IsConverterFor(Guid datatypeGuid, string contentTypeAlias, string propertyTypeAlias);
///
- /// Returns true if this converter can perform the value conversion for the specified property editor id
+ /// Attempts to convert a source value specified into a property model.
///
- ///
- ///
- ///
- ///
- bool IsConverterFor(Guid propertyEditorId, string docTypeAlias, string propertyTypeAlias);
-
- ///
- /// Attempts to convert the value specified into a useable value on the front-end
- ///
- ///
- ///
- ///
- /// This is used to convert the value stored in the repository into a usable value on the front-end.
- /// For example, if a 0 or 1 is stored for a boolean, we'd want to convert this to a real boolean.
- ///
- /// Also note that the value might not come in as a 0 or 1 but as a "0" or "1"
- ///
- Attempt ConvertPropertyValue(object value);
+ /// The source value.
+ /// An Attempt representing the result of the conversion.
+ /// The source value is dependent on the content cache. With the Xml content cache it
+ /// is always a string, but with other caches it may be an object (numeric, time...) matching
+ /// what is in the database. Be prepared.
+ Attempt ConvertPropertyValue(object sourceValue);
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs b/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs
new file mode 100644
index 0000000000..c69ad86efa
--- /dev/null
+++ b/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs
@@ -0,0 +1,82 @@
+using Umbraco.Core.Models.PublishedContent;
+
+namespace Umbraco.Core.PropertyEditors
+{
+ ///
+ /// Provides published content properties conversion service.
+ ///
+ public interface IPropertyValueConverter
+ {
+ #region Data to Source
+
+ ///
+ /// Gets a value indicating whether the converter can convert from Data value to Source value.
+ ///
+ /// The property type.
+ /// A value indicating whether the converter can convert from Data value to Source value.
+ bool IsDataToSourceConverter(PublishedPropertyType propertyType);
+
+ ///
+ /// Converts a property Data value to a Source value.
+ ///
+ /// The property type.
+ /// The data value.
+ /// A value indicating whether conversion should take place in preview mode.
+ /// The result of the conversion.
+ ///
+ /// fixme
+ /// The converter should know how to convert a null raw value into the default value for the property type.
+ /// Raw values may come from the database or from the XML cache (thus being strings).
+ ///
+ object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview);
+
+ #endregion
+
+ #region Source to Object
+
+ ///
+ /// Gets a value indicating whether the converter can convert from Source value to Object value.
+ ///
+ /// The property type.
+ /// A value indicating whether the converter can convert from Source value to Object value.
+ bool IsSourceToObjectConverter(PublishedPropertyType propertyType);
+
+ ///
+ /// Converts a property Source value to an Object value.
+ ///
+ /// The property type.
+ /// The source value.
+ /// A value indicating whether conversion should take place in preview mode.
+ /// The result of the conversion.
+ /// fixme
+ /// The converter should know how to convert a null source value into the default value for the property type.
+ object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview);
+
+ #endregion
+
+ #region Source to XPath
+
+ ///
+ /// Gets a value indicating whether the converter can convert from Source value to XPath value.
+ ///
+ /// The property type.
+ /// A value indicating whether the converter can convert from Source value to XPath value.
+ bool IsSourceToXPathConverter(PublishedPropertyType propertyType);
+
+ ///
+ /// Converts a property Source value to an XPath value.
+ ///
+ /// The property type.
+ /// The source value.
+ /// A value indicating whether conversion should take place in preview mode.
+ /// The result of the conversion.
+ ///
+ /// fixme
+ /// The converter should know how to convert a null source value into the default value for the property type.
+ /// If successful, the result should be either null , a non-empty string, or an XPathNavigator instance.
+ ///
+ object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview);
+
+ #endregion
+ }
+}
diff --git a/src/Umbraco.Core/PropertyEditors/PropertyCacheLevel.cs b/src/Umbraco.Core/PropertyEditors/PropertyCacheLevel.cs
new file mode 100644
index 0000000000..1e365d1ac4
--- /dev/null
+++ b/src/Umbraco.Core/PropertyEditors/PropertyCacheLevel.cs
@@ -0,0 +1,33 @@
+namespace Umbraco.Core.PropertyEditors
+{
+ ///
+ /// Specifies the acceptable level of cache for a property value.
+ ///
+ /// By default, Request is assumed.
+ public enum PropertyCacheLevel
+ {
+ ///
+ /// Indicates that the property value can be cached at the content level, ie it can be
+ /// cached until the content itself is modified.
+ ///
+ Content = 1,
+
+ ///
+ /// Indicates that the property value can be cached at the content cache level, ie it can
+ /// be cached until any content in the cache is modified.
+ ///
+ ContentCache = 2,
+
+ ///
+ /// Indicates that the property value can be cached at the request level, ie it can be
+ /// cached for the duration of the current request.
+ ///
+ Request = 3,
+
+ ///
+ /// Indicates that the property value cannot be cached and has to be converted any time
+ /// it is requested.
+ ///
+ None = 4
+ }
+}
diff --git a/src/Umbraco.Core/PropertyEditors/PropertyCacheValue.cs b/src/Umbraco.Core/PropertyEditors/PropertyCacheValue.cs
new file mode 100644
index 0000000000..c4f438fb5e
--- /dev/null
+++ b/src/Umbraco.Core/PropertyEditors/PropertyCacheValue.cs
@@ -0,0 +1,29 @@
+namespace Umbraco.Core.PropertyEditors
+{
+ ///
+ /// Specifies the different types of property cacheable values.
+ ///
+ public enum PropertyCacheValue
+ {
+ ///
+ /// All of them.
+ ///
+ All,
+
+ ///
+ /// The source value ie the internal value that can be used to create both the
+ /// object value and the xpath value.
+ ///
+ Source,
+
+ ///
+ /// The object value ie the strongly typed value of the property as seen when accessing content via C#.
+ ///
+ Object,
+
+ ///
+ /// The XPath value ie the value of the property as seen when accessing content via XPath.
+ ///
+ XPath
+ }
+}
diff --git a/src/Umbraco.Core/PropertyEditors/PropertyEditorValueConvertersResolver.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditorValueConvertersResolver.cs
index f804f424a6..17bb95fd7e 100644
--- a/src/Umbraco.Core/PropertyEditors/PropertyEditorValueConvertersResolver.cs
+++ b/src/Umbraco.Core/PropertyEditors/PropertyEditorValueConvertersResolver.cs
@@ -4,17 +4,35 @@ using Umbraco.Core.ObjectResolution;
namespace Umbraco.Core.PropertyEditors
{
- ///
- /// Manages the list of IPropertyEditorValueConverter's
- ///
- internal sealed class PropertyEditorValueConvertersResolver : ManyObjectsResolverBase
+ ///
+ /// Resolves the IPropertyEditorValueConverter objects.
+ ///
+ internal sealed class PropertyEditorValueConvertersResolver : ManyObjectsResolverBase
{
- public PropertyEditorValueConvertersResolver(IEnumerable converters)
+ ///
+ /// Initializes a new instance of the class with
+ /// an initial list of converter types.
+ ///
+ /// The list of converter types
+ /// The resolver is created by the WebBootManager and thus the constructor remains internal.
+ internal PropertyEditorValueConvertersResolver(IEnumerable converters)
: base(converters)
- {
- }
-
- public IEnumerable Converters
+ { }
+
+ ///
+ /// Initializes a new instance of the class with
+ /// an initial list of converter types.
+ ///
+ /// The list of converter types
+ /// The resolver is created by the WebBootManager and thus the constructor remains internal.
+ internal PropertyEditorValueConvertersResolver(params Type[] converters)
+ : base(converters)
+ { }
+
+ ///
+ /// Gets the converteres.
+ ///
+ public IEnumerable Converters
{
get { return Values; }
}
diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueCacheAttribute.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueCacheAttribute.cs
new file mode 100644
index 0000000000..76d16b79c6
--- /dev/null
+++ b/src/Umbraco.Core/PropertyEditors/PropertyValueCacheAttribute.cs
@@ -0,0 +1,34 @@
+using System;
+using log4net.Core;
+
+namespace Umbraco.Core.PropertyEditors
+{
+ ///
+ /// Indicates the cache level for a property cacheable value.
+ ///
+ /// Use this attribute to mark property values converters.
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
+ public class PropertyValueCacheAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the class with a cacheable value and a cache level.
+ ///
+ /// The cacheable value.
+ /// The cache level.
+ public PropertyValueCacheAttribute(PropertyCacheValue value, PropertyCacheLevel level)
+ {
+ Value = value;
+ Level = level;
+ }
+
+ ///
+ /// Gets or sets the cacheable value.
+ ///
+ public PropertyCacheValue Value { get; private set; }
+
+ ///
+ /// Gets or sets the cache level;
+ ///
+ public PropertyCacheLevel Level { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs
new file mode 100644
index 0000000000..e807c9de43
--- /dev/null
+++ b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Umbraco.Core.PropertyEditors
+{
+ ///
+ /// Provides a default overridable implementation for that does nothing.
+ ///
+ class PropertyValueConverterBase : IPropertyValueConverter
+ {
+ public virtual bool IsSourceToObjectConverter(Models.PublishedContent.PublishedPropertyType propertyType)
+ {
+ return false;
+ }
+
+ public virtual object ConvertSourceToObject(Models.PublishedContent.PublishedPropertyType propertyType, object source, bool preview)
+ {
+ return null;
+ }
+
+ public virtual bool IsDataToSourceConverter(Models.PublishedContent.PublishedPropertyType propertyType)
+ {
+ return false;
+ }
+
+ public virtual object ConvertDataToSource(Models.PublishedContent.PublishedPropertyType propertyType, object source, bool preview)
+ {
+ return null;
+ }
+
+ public virtual bool IsSourceToXPathConverter(Models.PublishedContent.PublishedPropertyType propertyType)
+ {
+ return false;
+ }
+
+ public virtual object ConvertSourceToXPath(Models.PublishedContent.PublishedPropertyType propertyType, object source, bool preview)
+ {
+ return null;
+ }
+ }
+}
diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs
new file mode 100644
index 0000000000..7af9927978
--- /dev/null
+++ b/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using Umbraco.Core.ObjectResolution;
+
+namespace Umbraco.Core.PropertyEditors
+{
+ ///
+ /// Resolves the IPropertyValueConverter objects.
+ ///
+ public sealed class PropertyValueConvertersResolver : ManyObjectsResolverBase
+ {
+ ///
+ /// Initializes a new instance of the class with
+ /// an initial list of converter types.
+ ///
+ /// The list of converter types
+ /// The resolver is created by the WebBootManager and thus the constructor remains internal.
+ internal PropertyValueConvertersResolver(IEnumerable converters)
+ : base(converters)
+ { }
+
+ ///
+ /// Initializes a new instance of the class with
+ /// an initial list of converter types.
+ ///
+ /// The list of converter types
+ /// The resolver is created by the WebBootManager and thus the constructor remains internal.
+ internal PropertyValueConvertersResolver(params Type[] converters)
+ : base(converters)
+ { }
+
+ ///
+ /// Gets the converters.
+ ///
+ public IEnumerable Converters
+ {
+ get { return Values; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueTypeAttribute.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueTypeAttribute.cs
new file mode 100644
index 0000000000..5d41b7f184
--- /dev/null
+++ b/src/Umbraco.Core/PropertyEditors/PropertyValueTypeAttribute.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Umbraco.Core.PropertyEditors
+{
+ ///
+ /// Indicates the CLR type of property object values returned by a converter.
+ ///
+ /// Use this attribute to mark property values converters.
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
+ public class PropertyValueTypeAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the class with a type.
+ ///
+ /// The type.
+ public PropertyValueTypeAttribute(Type type)
+ {
+ Type = type;
+ }
+
+ ///
+ /// Gets or sets the type.
+ ///
+ public Type Type { get; private set; }
+ }
+}
diff --git a/src/Umbraco.Core/PropertyEditors/TinyMcePropertyEditorValueConverter.cs b/src/Umbraco.Core/PropertyEditors/TinyMcePropertyEditorValueConverter.cs
deleted file mode 100644
index 8480d91260..0000000000
--- a/src/Umbraco.Core/PropertyEditors/TinyMcePropertyEditorValueConverter.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System;
-using System.Web;
-
-namespace Umbraco.Core.PropertyEditors
-{
- ///
- /// Value converter for the RTE so that it always returns IHtmlString so that Html.Raw doesn't have to be used.
- ///
- internal class TinyMcePropertyEditorValueConverter : IPropertyEditorValueConverter
- {
- public bool IsConverterFor(Guid propertyEditorId, string docTypeAlias, string propertyTypeAlias)
- {
- return Guid.Parse(Constants.PropertyEditors.TinyMCEv3).Equals(propertyEditorId);
- }
-
- ///
- /// Return IHtmlString so devs doesn't need to decode html
- ///
- ///
- ///
- public virtual Attempt ConvertPropertyValue(object value)
- {
- return Attempt.Succeed(new HtmlString(value.ToString()));
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/PropertyEditors/TinyMceValueConverter.cs b/src/Umbraco.Core/PropertyEditors/TinyMceValueConverter.cs
new file mode 100644
index 0000000000..1e054c8733
--- /dev/null
+++ b/src/Umbraco.Core/PropertyEditors/TinyMceValueConverter.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Web;
+using Umbraco.Core.Models.PublishedContent;
+
+namespace Umbraco.Core.PropertyEditors
+{
+ ///
+ /// Value converter for the RTE so that it always returns IHtmlString so that Html.Raw doesn't have to be used.
+ ///
+ // PropertyCacheLevel.Content is ok here because that version of RTE converter does not parse {locallink} nor executes macros
+ [PropertyValueType(typeof(IHtmlString))]
+ [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)]
+ internal class TinyMceValueConverter : IPropertyValueConverter
+ {
+ public bool IsDataToSourceConverter(PublishedPropertyType propertyType)
+ {
+ return Guid.Parse(Constants.PropertyEditors.TinyMCEv3).Equals(propertyType.EditorGuid);
+ }
+
+ public virtual object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ // in xml a string is: string
+ // in the database a string is: string
+ // default value is: null
+ return source;
+ }
+
+ public bool IsSourceToObjectConverter(PublishedPropertyType propertyType)
+ {
+ return IsDataToSourceConverter(propertyType);
+ }
+
+ public virtual object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ // source should come from ConvertSource and be a string already
+ return new HtmlString((string)source);
+ }
+
+ public bool IsSourceToXPathConverter(PublishedPropertyType propertyType)
+ {
+ return IsDataToSourceConverter(propertyType);
+ }
+
+ public virtual object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ // source should come from ConvertSource and be a string already
+ return source;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/PropertyEditors/YesNoPropertyEditorValueConverter.cs b/src/Umbraco.Core/PropertyEditors/YesNoPropertyEditorValueConverter.cs
deleted file mode 100644
index 6e52ca295b..0000000000
--- a/src/Umbraco.Core/PropertyEditors/YesNoPropertyEditorValueConverter.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System;
-
-namespace Umbraco.Core.PropertyEditors
-{
- internal class YesNoPropertyEditorValueConverter : IPropertyEditorValueConverter
- {
- public bool IsConverterFor(Guid propertyEditorId, string docTypeAlias, string propertyTypeAlias)
- {
- return Guid.Parse(Constants.PropertyEditors.TrueFalse).Equals(propertyEditorId);
- }
-
- ///
- /// Convert from string boolean or 0 or 1 to real boolean
- ///
- ///
- ///
- public Attempt ConvertPropertyValue(object value)
- {
- return value.TryConvertTo(typeof(bool));
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/PropertyEditors/YesNoValueConverter.cs b/src/Umbraco.Core/PropertyEditors/YesNoValueConverter.cs
new file mode 100644
index 0000000000..9d7c35c856
--- /dev/null
+++ b/src/Umbraco.Core/PropertyEditors/YesNoValueConverter.cs
@@ -0,0 +1,49 @@
+using System;
+using Umbraco.Core.Models.PublishedContent;
+
+namespace Umbraco.Core.PropertyEditors
+{
+ [PropertyValueType(typeof(bool))]
+ [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)]
+ class YesNoValueConverter : IPropertyValueConverter
+ {
+ public bool IsDataToSourceConverter(PublishedPropertyType propertyType)
+ {
+ return Guid.Parse(Constants.PropertyEditors.TrueFalse).Equals(propertyType.EditorGuid);
+ }
+
+ public object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ // in xml a boolean is: string
+ // in the database a boolean is: string "1" or "0" or empty
+ // the converter does not need to handle anything else ("true"...)
+
+ // default value is: false
+ var sourceString = source as string;
+ if (sourceString == null) return false;
+ return sourceString == "1";
+ }
+
+ public bool IsSourceToObjectConverter(PublishedPropertyType propertyType)
+ {
+ return IsDataToSourceConverter(propertyType);
+ }
+
+ public object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ // source should come from ConvertSource and be a boolean already
+ return source;
+ }
+
+ public bool IsSourceToXPathConverter(PublishedPropertyType propertyType)
+ {
+ return IsDataToSourceConverter(propertyType);
+ }
+
+ public object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview)
+ {
+ // source should come from ConvertSource and be a boolean already
+ return (bool) source ? "1" : "0";
+ }
+ }
+}
diff --git a/src/Umbraco.Core/PublishedContentExtensions.cs b/src/Umbraco.Core/PublishedContentExtensions.cs
deleted file mode 100644
index 83e5c53402..0000000000
--- a/src/Umbraco.Core/PublishedContentExtensions.cs
+++ /dev/null
@@ -1,144 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Net.Mime;
-using System.Web;
-using Umbraco.Core.Dynamics;
-using Umbraco.Core.Models;
-using umbraco.interfaces;
-
-namespace Umbraco.Core
-{
- ///
- /// Extension methods for IPublishedContent
- ///
- public static class PublishedContentExtensions
- {
-
- #region GetProperty
- public static IPublishedContentProperty GetProperty(this IPublishedContent content, string alias, bool recursive)
- {
- return content.GetPropertyRecursive(alias, recursive);
- }
-
- private static IPublishedContentProperty GetPropertyRecursive(this IPublishedContent content, string alias, bool recursive = false)
- {
- if (!recursive)
- {
- return content.GetProperty(alias);
- }
- var context = content;
- var prop = content.GetPropertyRecursive(alias);
- while (prop == null || prop.Value == null || prop.Value.ToString().IsNullOrWhiteSpace())
- {
- if (context.Parent == null) break;
- context = context.Parent;
- prop = context.GetPropertyRecursive(alias);
- }
- return prop;
- }
- #endregion
-
- #region HasValue
-
- public static bool HasValue(this IPublishedContentProperty prop)
- {
- if (prop == null) return false;
- if (prop.Value == null) return false;
- return !prop.Value.ToString().IsNullOrWhiteSpace();
- }
-
- public static bool HasValue(this IPublishedContent doc, string alias)
- {
- return doc.HasValue(alias, false);
- }
- public static bool HasValue(this IPublishedContent doc, string alias, bool recursive)
- {
- var prop = doc.GetProperty(alias, recursive);
- if (prop == null) return false;
- return prop.HasValue();
- }
- public static IHtmlString HasValue(this IPublishedContent doc, string alias, string valueIfTrue, string valueIfFalse)
- {
- return doc.HasValue(alias, false) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse);
- }
- public static IHtmlString HasValue(this IPublishedContent doc, string alias, bool recursive, string valueIfTrue, string valueIfFalse)
- {
- return doc.HasValue(alias, recursive) ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse);
- }
- public static IHtmlString HasValue(this IPublishedContent doc, string alias, string valueIfTrue)
- {
- return doc.HasValue(alias, false) ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty);
- }
- public static IHtmlString HasValue(this IPublishedContent doc, string alias, bool recursive, string valueIfTrue)
- {
- return doc.HasValue(alias, recursive) ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty);
- }
- #endregion
-
- ///
- /// Returns the recursive value of a field by iterating up the parent chain but starting at the publishedContent passed in
- ///
- ///
- ///
- ///
- public static string GetRecursiveValue(this IPublishedContent publishedContent, string fieldname)
- {
- //check for the cached value in the objects properties first
- var cachedVal = publishedContent["__recursive__" + fieldname];
- if (cachedVal != null)
- {
- return cachedVal.ToString();
- }
-
- var contentValue = "";
- var currentContent = publishedContent;
-
- while (contentValue.IsNullOrWhiteSpace())
- {
- var val = currentContent[fieldname];
- if (val == null || val.ToString().IsNullOrWhiteSpace())
- {
- if (currentContent.Parent == null)
- {
- break; //we've reached the top
- }
- currentContent = currentContent.Parent;
- }
- else
- {
- contentValue = val.ToString(); //we've found a recursive val
- }
- }
-
- //cache this lookup in a new custom (hidden) property
- publishedContent.Properties.Add(new PropertyResult("__recursive__" + fieldname, contentValue, Guid.Empty, PropertyResultType.CustomProperty));
-
- return contentValue;
- }
-
- public static bool IsVisible(this IPublishedContent doc)
- {
- var umbracoNaviHide = doc.GetProperty(Constants.Conventions.Content.NaviHide);
- if (umbracoNaviHide != null)
- {
- return umbracoNaviHide.Value.ToString().Trim() != "1";
- }
- return true;
- }
-
- public static bool HasProperty(this IPublishedContent doc, string name)
- {
- if (doc != null)
- {
- var prop = doc.GetProperty(name);
-
- return (prop != null);
- }
- return false;
- }
-
-
-
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/PublishedContentHelper.cs b/src/Umbraco.Core/PublishedContentHelper.cs
deleted file mode 100644
index 6dffef290d..0000000000
--- a/src/Umbraco.Core/PublishedContentHelper.cs
+++ /dev/null
@@ -1,162 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Xml.Linq;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Dynamics;
-using Umbraco.Core.Models;
-using Umbraco.Core.PropertyEditors;
-using Umbraco.Core.Services;
-
-namespace Umbraco.Core
-{
-
- ///
- /// Utility class for dealing with data types and value conversions
- ///
- ///
- /// TODO: The logic for the GetDataType + cache should probably be moved to a service, no ?
- ///
- /// We inherit from ApplicationEventHandler so we can bind to the ContentTypeService events to ensure that our local cache
- /// object gets cleared when content types change.
- ///
- internal class PublishedContentHelper : ApplicationEventHandler
- {
- ///
- /// Used to invalidate the cache from the ICacherefresher
- ///
- internal static void ClearPropertyTypeCache()
- {
- PropertyTypeCache.Clear();
- }
-
- ///
- /// This callback is used only for unit tests which enables us to return any data we want and not rely on having the data in a database
- ///
- internal static Func GetDataTypeCallback = null;
-
- private static readonly ConcurrentDictionary, Guid> PropertyTypeCache = new ConcurrentDictionary, Guid>();
-
- ///
- /// Return the GUID Id for the data type assigned to the document type with the property alias
- ///
- ///
- ///
- ///
- ///
- ///
- internal static Guid GetDataType(ApplicationContext applicationContext, string docTypeAlias, string propertyAlias, PublishedItemType itemType)
- {
- if (GetDataTypeCallback != null)
- return GetDataTypeCallback(docTypeAlias, propertyAlias);
-
- var key = new Tuple(docTypeAlias, propertyAlias, itemType);
- return PropertyTypeCache.GetOrAdd(key, tuple =>
- {
- IContentTypeComposition result = null;
- switch (itemType)
- {
- case PublishedItemType.Content:
- result = applicationContext.Services.ContentTypeService.GetContentType(docTypeAlias);
- break;
- case PublishedItemType.Media:
- result = applicationContext.Services.ContentTypeService.GetMediaType(docTypeAlias);
- break;
- default:
- throw new ArgumentOutOfRangeException("itemType");
- }
-
- if (result == null) return Guid.Empty;
-
- //SD: we need to check for 'any' here because the collection is backed by KeyValuePair which is a struct
- // and can never be null so FirstOrDefault doesn't actually work. Have told Seb and Morten about thsi
- // issue.
- if (!result.CompositionPropertyTypes.Any(x => x.Alias.InvariantEquals(propertyAlias)))
- {
- return Guid.Empty;
- }
- var property = result.CompositionPropertyTypes.FirstOrDefault(x => x.Alias.InvariantEquals(propertyAlias));
- //as per above, this will never be null but we'll keep the check here anyways.
- if (property == null) return Guid.Empty;
- return property.DataTypeId;
- });
- }
-
- ///
- /// Converts the currentValue to a correctly typed value based on known registered converters, then based on known standards.
- ///
- ///
- ///
- ///
- ///
- ///
- internal static Attempt ConvertPropertyValue(object currentValue, Guid dataType, string docTypeAlias, string propertyTypeAlias)
- {
- if (currentValue == null) return Attempt.Fail();
-
- //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 Attempt.Succeed(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 Attempt.Succeed(dResult);
- }
- }
- //process string booleans as booleans
- if (sResult.InvariantEquals("true"))
- {
- return Attempt.Succeed(true);
- }
- if (sResult.InvariantEquals("false"))
- {
- return Attempt.Succeed(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(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 Attempt.Succeed(new DynamicXml(e));
- }
- return Attempt.Fail();
- }
- catch (Exception)
- {
- return Attempt.Fail();
- }
- }
- return Attempt.Fail();
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/TypeExtensions.cs b/src/Umbraco.Core/TypeExtensions.cs
index b7c079f384..c8da74f067 100644
--- a/src/Umbraco.Core/TypeExtensions.cs
+++ b/src/Umbraco.Core/TypeExtensions.cs
@@ -83,26 +83,35 @@ namespace Umbraco.Core
return true;
}
- public static IEnumerable AllInterfaces(this Type target)
+ // that method is broken (will return duplicates) and useless (GetInterfaces already does the job)
+ //public static IEnumerable AllInterfaces(this Type target)
+ //{
+ // foreach (var IF in target.GetInterfaces())
+ // {
+ // yield return IF;
+ // foreach (var childIF in IF.AllInterfaces())
+ // {
+ // yield return childIF;
+ // }
+ // }
+ //}
+
+ public static IEnumerable GetBaseTypes(this Type type, bool andSelf)
{
- foreach (var IF in target.GetInterfaces())
- {
- yield return IF;
- foreach (var childIF in IF.AllInterfaces())
- {
- yield return childIF;
- }
- }
+ if (andSelf)
+ yield return type;
+
+ while ((type = type.BaseType) != null)
+ yield return type;
}
public static IEnumerable AllMethods(this Type target)
{
- var allTypes = target.AllInterfaces().ToList();
+ //var allTypes = target.AllInterfaces().ToList();
+ var allTypes = target.GetInterfaces().ToList(); // GetInterfaces is ok here
allTypes.Add(target);
- return from type in allTypes
- from method in type.GetMethods()
- select method;
+ return allTypes.SelectMany(t => t.GetMethods());
}
///
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index b6e95a4c1b..a270070d9e 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -182,6 +182,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -201,6 +215,11 @@
+
+
+
+
+
@@ -562,7 +581,6 @@
-
@@ -647,7 +665,7 @@
-
+
@@ -655,7 +673,7 @@
-
+
@@ -677,14 +695,12 @@
-
-
-
+
@@ -831,7 +847,6 @@
-
diff --git a/src/Umbraco.Tests/CodeFirst/ContentTypeBase.cs b/src/Umbraco.Tests/CodeFirst/ContentTypeBase.cs
index 2de26d87f1..2f175ad668 100644
--- a/src/Umbraco.Tests/CodeFirst/ContentTypeBase.cs
+++ b/src/Umbraco.Tests/CodeFirst/ContentTypeBase.cs
@@ -93,7 +93,7 @@ namespace Umbraco.Tests.CodeFirst
//Using this attribute to hide Properties from Intellisense (when compiled?)
[EditorBrowsable(EditorBrowsableState.Never)]
- public ICollection Properties
+ public ICollection Properties
{
get { return _content.Properties; }
}
@@ -107,7 +107,7 @@ namespace Umbraco.Tests.CodeFirst
//Using this attribute to hide Properties from Intellisense (when compiled?)
[EditorBrowsable(EditorBrowsableState.Never)]
- public IPublishedContentProperty GetProperty(string alias)
+ public IPublishedProperty GetProperty(string alias)
{
return _content.GetProperty(alias);
}
diff --git a/src/Umbraco.Tests/CodeFirst/StronglyTypedMapperTest.cs b/src/Umbraco.Tests/CodeFirst/StronglyTypedMapperTest.cs
index 26054a6fdf..37a3d7c500 100644
--- a/src/Umbraco.Tests/CodeFirst/StronglyTypedMapperTest.cs
+++ b/src/Umbraco.Tests/CodeFirst/StronglyTypedMapperTest.cs
@@ -1,8 +1,12 @@
-using System.IO;
+using System;
+using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using NUnit.Framework;
+using Umbraco.Core;
using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.PropertyEditors;
using Umbraco.Tests.CodeFirst.TestModels;
using Umbraco.Tests.PublishedContent;
using Umbraco.Tests.TestHelpers;
@@ -58,9 +62,33 @@ namespace Umbraco.Tests.CodeFirst
#region Test setup
public override void Initialize()
{
+ // required so we can access property.Value
+ //PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver();
+
TestHelper.EnsureUmbracoSettingsConfig();
base.Initialize();
+
+ // need to specify a custom callback for unit tests
+ // AutoPublishedContentTypes generates properties automatically
+ // when they are requested, but we must declare those that we
+ // explicitely want to be here...
+
+ var propertyTypes = new[]
+ {
+ // AutoPublishedContentType will auto-generate other properties
+ new PublishedPropertyType("siteDescription", Guid.Empty, 0, 0),
+ new PublishedPropertyType("siteName", Guid.Empty, 0, 0),
+ new PublishedPropertyType("articleContent", Guid.Empty, 0, 0),
+ new PublishedPropertyType("articleAuthor", Guid.Empty, 0, 0),
+ new PublishedPropertyType("articleDate", Guid.Empty, 0, 0),
+ new PublishedPropertyType("pageTitle", Guid.Empty, 0, 0),
+ };
+ var type = new AutoPublishedContentType(0, "anything", propertyTypes);
+ PublishedContentType.GetPublishedContentTypeCallback = (alias) => type;
+ Console.WriteLine("INIT STRONG {0}",
+ PublishedContentType.Get(PublishedItemType.Content, "anything")
+ .PropertyTypes.Count());
}
public override void TearDown()
diff --git a/src/Umbraco.Tests/CodeFirst/TestModels/Home.cs b/src/Umbraco.Tests/CodeFirst/TestModels/Home.cs
index ffed8cf6c3..637400ef1e 100644
--- a/src/Umbraco.Tests/CodeFirst/TestModels/Home.cs
+++ b/src/Umbraco.Tests/CodeFirst/TestModels/Home.cs
@@ -10,6 +10,8 @@ namespace Umbraco.Tests.CodeFirst.TestModels
[PropertyType(typeof(TextFieldDataType))]
public string SiteName { get; set; }
+ // fixme - yet the property alias is "siteDescription"?
+
[Alias("umbSiteDescription", Name = "Site Description")]
[PropertyType(typeof(textfieldMultipleDataType))]
public string SiteDescription { get; set; }
diff --git a/src/Umbraco.Tests/CoreXml/NavigableNavigatorTests.cs b/src/Umbraco.Tests/CoreXml/NavigableNavigatorTests.cs
index a23a6f6435..2c12d420af 100644
--- a/src/Umbraco.Tests/CoreXml/NavigableNavigatorTests.cs
+++ b/src/Umbraco.Tests/CoreXml/NavigableNavigatorTests.cs
@@ -882,24 +882,37 @@ namespace Umbraco.Tests.CoreXml
public object Value(int id)
{
- var fieldType = _type.FieldTypes[id];
+ var fieldType = _type.FieldTypes[id] as TestPropertyType;
+ if (fieldType == null) throw new Exception("Oops");
+
var value = FieldValues[id];
var isAttr = id <= _type.Source.LastAttributeIndex;
+ // null => return null
if (value == null) return null;
+
+ // attribute => return string value
if (isAttr) return value.ToString();
+
+ // has a converter => use the converter
+ if (fieldType.XmlStringConverter != null)
+ return fieldType.XmlStringConverter(value);
- if (fieldType.XmlStringConverter != null) return fieldType.XmlStringConverter(value);
+ // not a string => return value as a string
+ var s = value as string;
+ if (s == null) return value.ToString();
- // though in reality we should use the converters, which should
- // know whether the property is XML or not, instead of guessing.
- XPathDocument doc;
- if (XmlHelper.TryCreateXPathDocumentFromPropertyValue(value, out doc))
- return doc.CreateNavigator();
+ // xml content... try xml
+ if (fieldType.IsXmlContent)
+ {
+ XPathDocument doc;
+ if (XmlHelper.TryCreateXPathDocumentFromPropertyValue(s, out doc))
+ return doc.CreateNavigator();
+ }
- //var s = value.ToString();
- //return XmlHelper.IsXmlWhitespace(s) ? null : s;
- return value.ToString();
+ // return the string
+ // even if it's xml that can't be parsed...
+ return s;
}
// locals
diff --git a/src/Umbraco.Tests/DynamicsAndReflection/ExtensionMethodFinderTests.cs b/src/Umbraco.Tests/DynamicsAndReflection/ExtensionMethodFinderTests.cs
new file mode 100644
index 0000000000..6736ab0289
--- /dev/null
+++ b/src/Umbraco.Tests/DynamicsAndReflection/ExtensionMethodFinderTests.cs
@@ -0,0 +1,198 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using NUnit.Framework;
+using Umbraco.Core;
+using Umbraco.Core.Dynamics;
+
+namespace Umbraco.Tests.DynamicsAndReflection
+{
+ [TestFixture]
+ public class ExtensionMethodFinderTests
+ {
+ // To expand on Jon's answer, the reason this doesn't work is because in regular,
+ // non-dynamic code extension methods work by doing a full search of all the
+ // classes known to the compiler for a static class that has an extension method
+ // that match. The search goes in order based on the namespace nesting and available
+ // "using" directives in each namespace.
+ //
+ // That means that in order to get a dynamic extension method invocation resolved
+ // correctly, somehow the DLR has to know at runtime what all the namespace nestings
+ // and "using" directives were in your source code. We do not have a mechanism handy
+ // for encoding all that information into the call site. We considered inventing
+ // such a mechanism, but decided that it was too high cost and produced too much
+ // schedule risk to be worth it.
+ //
+ // Eric Lippert, http://stackoverflow.com/questions/5311465/extension-method-and-dynamic-object-in-c-sharp
+
+ [Test]
+ [Ignore("fails")]
+ public void TypesTests()
+ {
+ Assert.IsTrue(typeof(int[]).Inherits());
+ Assert.IsFalse(typeof(int[]).Inherits());
+
+ var m1 = typeof (ExtensionMethodFinderTests).GetMethod("TestMethod1");
+
+ var a1A = new object[] {1};
+ var m1A = GetMethodForArguments(m1, a1A);
+ Assert.IsNotNull(m1A);
+ m1A.Invoke(this, a1A);
+
+ var a1B = new object[] {"foo"};
+ var m1B = GetMethodForArguments(m1, a1B);
+ Assert.IsNull(m1B);
+
+ var m2 = typeof(ExtensionMethodFinderTests).GetMethod("TestMethod2");
+
+ var m2A = GetMethodForArguments(m2, a1A);
+ Assert.IsNotNull(m2A);
+ m2A.Invoke(this, a1A);
+
+ var m2B = GetMethodForArguments(m2, a1B);
+ Assert.IsNotNull(m2B);
+ m2B.Invoke(this, a1B);
+
+ var m3 = typeof(ExtensionMethodFinderTests).GetMethod("TestMethod3");
+
+ var a3A = new object[] {1, 2};
+ var m3A = GetMethodForArguments(m3, a3A);
+ Assert.IsNotNull(m3A);
+ m3A.Invoke(this, a3A);
+
+ var a3B = new object[] {1, "foo"};
+ var m3B = GetMethodForArguments(m3, a3B);
+ Assert.IsNull(m3B);
+
+ var m4 = typeof(ExtensionMethodFinderTests).GetMethod("TestMethod4");
+
+ var m4A = GetMethodForArguments(m4, a3A);
+ Assert.IsNotNull(m4A);
+ m4A.Invoke(this, a3A);
+
+ var m4B = GetMethodForArguments(m4, a3B);
+ Assert.IsNotNull(m4B);
+ m4B.Invoke(this, a3B);
+
+ var m5 = typeof(ExtensionMethodFinderTests).GetMethod("TestMethod5");
+
+ // fixme - currently that fails because we can't match List with List
+ var a5 = new object[] {new List()};
+ var m5A = GetMethodForArguments(m5, a5);
+ Assert.IsNotNull(m5A);
+
+ // fixme - should we also handle "ref" and "out" parameters?
+ // fixme - should we pay attention to array types?
+ }
+
+ public void TestMethod1(int value) {}
+ public void TestMethod2(T value) {}
+ public void TestMethod3(T value1, T value2) { }
+ public void TestMethod4(T1 value1, T2 value2) { }
+ public void TestMethod5(List value) { }
+
+ // gets the method that can apply to the arguments
+ // either the method itself, or a generic one
+ // or null if it couldn't match
+ //
+ // this is a nightmare - if we want to do it right, then we have
+ // to re-do the whole compiler type inference stuff by ourselves?!
+ //
+ static MethodInfo GetMethodForArguments(MethodInfo method, IList arguments)
+ {
+ var parameters = method.GetParameters();
+ var genericArguments = method.GetGenericArguments();
+
+ if (parameters.Length != arguments.Count) return null;
+
+ var genericArgumentTypes = new Type[genericArguments.Length];
+ var i = 0;
+ for (; i < parameters.Length; i++)
+ {
+ var parameterType = parameters[i].ParameterType;
+ var argumentType = arguments[i].GetType();
+
+ Console.WriteLine("{0} / {1}", parameterType, argumentType);
+
+ if (parameterType == argumentType) continue; // match
+ if (parameterType.IsGenericParameter) // eg T
+ {
+ var pos = parameterType.GenericParameterPosition;
+ if (genericArgumentTypes[pos] != null)
+ {
+ // fixme - is this OK? what about variance and such?
+ // it is NOT ok, if the first pass is SomethingElse then next is Something
+ // it will fail... the specs prob. indicate how it works, trying to find a common
+ // type...
+ if (genericArgumentTypes[pos].IsAssignableFrom(argumentType) == false)
+ break;
+ }
+ else
+ {
+ genericArgumentTypes[pos] = argumentType;
+ }
+ }
+ else if (parameterType.IsGenericType) // eg List
+ {
+ if (argumentType.IsGenericType == false) break;
+
+ var pg = parameterType.GetGenericArguments();
+ var ag = argumentType.GetGenericArguments();
+
+ // then what ?!
+ // should _variance_ be of some importance?
+ Console.WriteLine("generic {0}", argumentType.IsGenericType);
+ }
+ else
+ {
+ if (parameterType.IsAssignableFrom(argumentType) == false)
+ break;
+ }
+ }
+ if (i != parameters.Length) return null;
+ return genericArguments.Length == 0
+ ? method
+ : method.MakeGenericMethod(genericArgumentTypes);
+ }
+
+ public class Class1
+ {}
+
+ [Test]
+ [Ignore("fails")]
+ public void FinderTests()
+ {
+ MethodInfo method;
+ var class1 = new Class1();
+
+ method = ExtensionMethodFinder.FindExtensionMethod(typeof (Class1), new object[] {1}, "TestMethod1", false);
+ Assert.IsNotNull(method);
+ method.Invoke(null, new object[] { class1, 1 });
+
+ method = ExtensionMethodFinder.FindExtensionMethod(typeof(Class1), new object[] { "x" }, "TestMethod1", false);
+ Assert.IsNull(method); // fixme - fails, return TestMethod1!
+
+ method = ExtensionMethodFinder.FindExtensionMethod(typeof(Class1), new object[] { 1 }, "TestMethod2", false);
+ Assert.IsNotNull(method);
+ method.Invoke(null, new object[] { class1, "1" });
+
+ method = ExtensionMethodFinder.FindExtensionMethod(typeof(Class1), new object[] { "x" }, "TestMethod2", false);
+ Assert.IsNotNull(method);
+ method.Invoke(null, new object[] { class1, "x" });
+ }
+ }
+
+ static class ExtensionMethodFinderTestsExtensions
+ {
+ public static void TestMethod1(this ExtensionMethodFinderTests.Class1 source, int value)
+ { }
+
+ public static void TestMethod2(this ExtensionMethodFinderTests.Class1 source, int value)
+ { }
+
+ public static void TestMethod2(this ExtensionMethodFinderTests.Class1 source, string value)
+ { }
+ }
+}
diff --git a/src/Umbraco.Tests/DynamicsAndReflection/ReflectionTests.cs b/src/Umbraco.Tests/DynamicsAndReflection/ReflectionTests.cs
new file mode 100644
index 0000000000..857517724f
--- /dev/null
+++ b/src/Umbraco.Tests/DynamicsAndReflection/ReflectionTests.cs
@@ -0,0 +1,73 @@
+using System.Linq;
+using NUnit.Framework;
+using Umbraco.Core;
+
+namespace Umbraco.Tests.DynamicsAndReflection
+{
+ [TestFixture]
+ public class ReflectionTests
+ {
+ [Test]
+ public void GetBaseTypesIsOk()
+ {
+ // tests that the GetBaseTypes extension method works.
+
+ var type = typeof(Class2);
+ var types = type.GetBaseTypes(true).ToArray();
+ Assert.AreEqual(3, types.Length);
+ Assert.Contains(typeof(Class2), types);
+ Assert.Contains(typeof(Class1), types);
+ Assert.Contains(typeof(object), types);
+
+ types = type.GetBaseTypes(false).ToArray();
+ Assert.AreEqual(2, types.Length);
+ Assert.Contains(typeof(Class1), types);
+ Assert.Contains(typeof(object), types);
+ }
+
+ [Test]
+ public void GetInterfacesIsOk()
+ {
+ // tests that GetInterfaces gets _all_ interfaces
+ // so the AllInterfaces extension method is useless
+
+ var type = typeof(Class2);
+ var interfaces = type.GetInterfaces();
+ Assert.AreEqual(2, interfaces.Length);
+ Assert.Contains(typeof(IInterface1), interfaces);
+ Assert.Contains(typeof(IInterface2), interfaces);
+ }
+
+ // TypeExtensions.AllInterfaces was broken an not used, has been commented out
+ //
+ //[Test]
+ //public void AllInterfacesIsBroken()
+ //{
+ // // tests that the AllInterfaces extension method is broken
+ //
+ // var type = typeof(Class2);
+ // var interfaces = type.AllInterfaces().ToArray();
+ // Assert.AreEqual(3, interfaces.Length); // should be 2!
+ // Assert.Contains(typeof(IInterface1), interfaces);
+ // Assert.Contains(typeof(IInterface2), interfaces);
+ // Assert.AreEqual(2, interfaces.Count(i => i == typeof(IInterface1))); // duplicate!
+ // Assert.AreEqual(1, interfaces.Count(i => i == typeof(IInterface2)));
+ //}
+
+ interface IInterface1
+ { }
+
+ interface IInterface2 : IInterface1
+ {
+ void Method();
+ }
+
+ class Class1 : IInterface2
+ {
+ public void Method() { }
+ }
+
+ class Class2 : Class1
+ { }
+ }
+}
diff --git a/src/Umbraco.Tests/LibraryTests.cs b/src/Umbraco.Tests/LibraryTests.cs
index 9cc6302761..a61d39049e 100644
--- a/src/Umbraco.Tests/LibraryTests.cs
+++ b/src/Umbraco.Tests/LibraryTests.cs
@@ -4,6 +4,10 @@ using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.PropertyEditors;
+using Umbraco.Tests.PublishedContent;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.PublishedCache;
@@ -19,11 +23,30 @@ namespace Umbraco.Tests
[TestFixture]
public class LibraryTests : BaseRoutingTest
{
- public override void Initialize()
- {
- base.Initialize();
+ public override void Initialize()
+ {
+ // required so we can access property.Value
+ PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver();
+
+ base.Initialize();
- var routingContext = GetRoutingContext("/test", 1234);
+ // need to specify a custom callback for unit tests
+ // AutoPublishedContentTypes generates properties automatically
+ // when they are requested, but we must declare those that we
+ // explicitely want to be here...
+
+ var propertyTypes = new[]
+ {
+ // AutoPublishedContentType will auto-generate other properties
+ new PublishedPropertyType("content", Guid.Empty, 0, 0),
+ };
+ var type = new AutoPublishedContentType(0, "anything", propertyTypes);
+ PublishedContentType.GetPublishedContentTypeCallback = (alias) => type;
+ Console.WriteLine("INIT LIB {0}",
+ PublishedContentType.Get(PublishedItemType.Content, "anything")
+ .PropertyTypes.Count());
+
+ var routingContext = GetRoutingContext("/test", 1234);
UmbracoContext.Current = routingContext.UmbracoContext;
}
diff --git a/src/Umbraco.Tests/Masterpages/dummy.txt b/src/Umbraco.Tests/Masterpages/dummy.txt
deleted file mode 100644
index 9c01dce8f4..0000000000
--- a/src/Umbraco.Tests/Masterpages/dummy.txt
+++ /dev/null
@@ -1 +0,0 @@
-This file is just here to make sure the directory gets created.
\ No newline at end of file
diff --git a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs
index 4e5cfe56dd..54c3f47129 100644
--- a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs
+++ b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs
@@ -10,42 +10,54 @@ namespace Umbraco.Tests.PropertyEditors
[TestFixture]
public class PropertyEditorValueConverterTests
{
- [TestCase("2012-11-10", true)]
- [TestCase("2012/11/10", true)]
- [TestCase("10/11/2012", true)]
- [TestCase("11/10/2012", false)]
- [TestCase("Sat 10, Nov 2012", true)]
- [TestCase("Saturday 10, Nov 2012", true)]
- [TestCase("Sat 10, November 2012", true)]
- [TestCase("Saturday 10, November 2012", true)]
- [TestCase("2012-11-10 13:14:15", true)]
- [TestCase("", false)]
+ // see notes in the converter
+ // only ONE date format is expected here
+
+ //[TestCase("2012-11-10", true)]
+ //[TestCase("2012/11/10", true)]
+ //[TestCase("10/11/2012", true)]
+ //[TestCase("11/10/2012", false)]
+ //[TestCase("Sat 10, Nov 2012", true)]
+ //[TestCase("Saturday 10, Nov 2012", true)]
+ //[TestCase("Sat 10, November 2012", true)]
+ //[TestCase("Saturday 10, November 2012", true)]
+ //[TestCase("2012-11-10 13:14:15", true)]
+ [TestCase("2012-11-10 13:14:15", false)]
+ [TestCase("2012-11-10T13:14:15", true)]
+ [TestCase("", false)]
public void CanConvertDatePickerPropertyEditor(string date, bool expected)
{
- var converter = new DatePickerPropertyEditorValueConverter();
+ var converter = new DatePickerValueConverter();
var dateTime = new DateTime(2012, 11, 10, 13, 14, 15);
- var result = converter.ConvertPropertyValue(date);
+ var result = converter.ConvertDataToSource(null, date, false); // does not use type for conversion
- Assert.IsTrue(result.Success);
- Assert.AreEqual(DateTime.Equals(dateTime.Date, ((DateTime) result.Result).Date), expected);
- }
+ if (expected)
+ Assert.AreEqual(dateTime.Date, ((DateTime) result).Date);
+ else
+ Assert.AreNotEqual(dateTime.Date, ((DateTime)result).Date);
+ }
- [TestCase("TRUE", true)]
- [TestCase("True", true)]
- [TestCase("true", true)]
+ // see the notes in the converter
+ // values such as "true" are NOT expected here
+
+ //[TestCase("TRUE", true)]
+ //[TestCase("True", true)]
+ //[TestCase("true", true)]
[TestCase("1", true)]
- [TestCase("FALSE", false)]
- [TestCase("False", false)]
- [TestCase("false", false)]
+ //[TestCase("FALSE", false)]
+ //[TestCase("False", false)]
+ //[TestCase("false", false)]
[TestCase("0", false)]
[TestCase("", false)]
- public void CanConvertYesNoPropertyEditor(string value, bool expected)
+ [TestCase("true", false)]
+ [TestCase("false", false)]
+ [TestCase("blah", false)]
+ public void CanConvertYesNoPropertyEditor(string value, bool expected)
{
- var converter = new YesNoPropertyEditorValueConverter();
- var result = converter.ConvertPropertyValue(value);
+ var converter = new YesNoValueConverter();
+ var result = converter.ConvertDataToSource(null, value, false); // does not use type for conversion
- Assert.IsTrue(result.Success);
- Assert.AreEqual(expected, result.Result);
+ Assert.AreEqual(expected, result);
}
}
}
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicDocumentTestsBase.cs b/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicDocumentTestsBase.cs
deleted file mode 100644
index e0321e08d5..0000000000
--- a/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicDocumentTestsBase.cs
+++ /dev/null
@@ -1,699 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Web;
-using NUnit.Framework;
-using Umbraco.Core;
-using Umbraco.Core.Dynamics;
-using Umbraco.Tests.TestHelpers;
-
-namespace Umbraco.Tests.PublishedContent
-{
- [TestFixture]
- public abstract class DynamicDocumentTestsBase : PublishedContentTestBase
- {
- protected override DatabaseBehavior DatabaseTestBehavior
- {
- get { return DatabaseBehavior.NoDatabasePerFixture; }
- }
-
- protected override string GetXmlContent(int templateId)
- {
- return @"
-
-
-
-
-]>
-
-
-
-
- 1
-
-
- This is some content]]>
-
-
-
-
-
-
-
-
-
-
-
- 1
-
-
-
-
-
-
-
-
-
-
-
- ";
- }
-
- ///
- /// Returns the dynamic node/document to run tests against
- ///
- ///
- ///
- protected abstract dynamic GetDynamicNode(int id);
-
- [Test]
- public void Recursive_Property()
- {
- var doc = GetDynamicNode(1174);
- var prop = doc.GetProperty("siteTitle", true);
- Assert.IsNotNull(prop);
- Assert.AreEqual("This is my site", prop.Value);
- prop = doc.GetProperty("_siteTitle"); //test with underscore prefix
- Assert.IsNotNull(prop);
- Assert.AreEqual("This is my site", prop.Value);
- Assert.AreEqual("This is my site", doc._siteTitle);
- }
-
- ///
- /// Tests the internal instance level caching of returning properties
- ///
- ///
- /// http://issues.umbraco.org/issue/U4-1824
- /// http://issues.umbraco.org/issue/U4-1825
- ///
- [Test]
- public void Can_Return_Property_And_Value()
- {
- var doc = GetDynamicNode(1173);
-
- Assert.IsTrue(doc.HasProperty(Constants.Conventions.Content.UrlAlias));
- var prop = doc.GetProperty(Constants.Conventions.Content.UrlAlias);
- Assert.IsNotNull(prop);
- Assert.AreEqual("page2/alias, 2ndpagealias", prop.Value);
- Assert.AreEqual("page2/alias, 2ndpagealias", doc.umbracoUrlAlias);
- }
-
- ///
- /// Tests the IsLast method with the result set from a Where statement
- ///
- [Test]
- public void Is_Last_From_Where_Filter()
- {
- var doc = GetDynamicNode(1173);
-
- foreach (var d in doc.Children.Where("Visible"))
- {
- if (d.Id != 1178)
- {
- Assert.IsFalse(d.IsLast());
- }
- else
- {
- Assert.IsTrue(d.IsLast());
- }
- }
-
- }
-
- [Test]
- public void Single()
- {
- var doc = GetDynamicNode(4444);
-
- var result = doc.Children().Single();
-
- Assert.IsNotNull(result);
- Assert.AreEqual(5555, result.Id);
- }
-
- [Test]
- public void Single_With_Query()
- {
- var doc = GetDynamicNode(1046);
-
- var result = doc.Children().Single("id==1175");
-
- Assert.IsNotNull(result);
- Assert.AreEqual(1175, result.Id);
- }
-
- [Test]
- public void First()
- {
- var doc = GetDynamicNode(1173);
-
- var result = doc.Children().First();
-
- Assert.IsNotNull(result);
- Assert.AreEqual(1174, result.Id);
- }
-
- [Test]
- public void First_With_Query()
- {
- var doc = GetDynamicNode(1173);
-
- var result = doc.Children().First("blah==\"some content\"");
-
- Assert.IsNotNull(result);
- Assert.AreEqual(1176, result.Id);
- }
-
- [Test]
- public void Where_User_Property_Value()
- {
- var doc = GetDynamicNode(1173);
-
- var result = (IEnumerable)doc.Children().Where("blah==\"some content\"");
-
- Assert.IsNotNull(result);
- Assert.AreEqual(1, result.Count());
- Assert.AreEqual(1176, result.Single().Id);
- }
-
- [Test]
- public void String_ContainsValue_Extension_Method()
- {
- var doc = GetDynamicNode(1046);
-
- var paramVals = new Dictionary { { "searchId", 1173 } }; //this is an integer value
- var result = doc.Children()
- .Where("selectedNodes.ContainsValue(searchId)", paramVals) //call an extension method
- .FirstOrDefault();
-
- Assert.IsNotNull(result);
- Assert.AreEqual(4444, result.Id);
-
- //don't find!
- paramVals = new Dictionary { { "searchId", 1111777 } };
- result = doc.Children()
- .Where("selectedNodes.ContainsValue(searchId)", paramVals)
- .FirstOrDefault();
-
- Assert.IsNotNull(result);
- Assert.IsTrue(result.GetType() == typeof(DynamicNull) || result.GetType() == typeof(umbraco.MacroEngines.DynamicNull));
- //Assert.AreEqual(typeof(DynamicNull), result.GetType());
- }
-
- [Test]
- public void String_Contains_Method()
- {
- var doc = GetDynamicNode(1046);
-
- var paramVals = new Dictionary { { "searchId", "1173" } };
- var result = doc.Children()
- .Where("selectedNodes.Contains(searchId)", paramVals)
- .FirstOrDefault();
-
- Assert.IsNotNull(result);
- Assert.AreEqual(4444, result.Id);
-
- //don't find!
- paramVals = new Dictionary { { "searchId", "1aaa173" } };
- result = doc.Children()
- .Where("selectedNodes.Contains(searchId)", paramVals)
- .FirstOrDefault();
-
- Assert.IsNotNull(result);
- Assert.IsTrue(result.GetType() == typeof (DynamicNull) || result.GetType() == typeof (umbraco.MacroEngines.DynamicNull));
- //Assert.AreEqual(typeof (DynamicNull), result.GetType());
- }
-
- [Test]
- public void String_Split_Method()
- {
- var doc = GetDynamicNode(1046);
-
- var paramVals = new Dictionary
- {
- { "splitTerm", new char[] { ',' } },
- { "splitOptions", StringSplitOptions.RemoveEmptyEntries }
- };
- var result = doc.Children()
- .Where("selectedNodes.Split(splitTerm, splitOptions).Length == 3", paramVals)
- .FirstOrDefault();
-
- Assert.IsNotNull(result);
- Assert.AreEqual(4444, result.Id);
- }
-
- [Ignore("We are ignoring this test because currently our ExpressionParser class cannot deal with this... it needs some serious TLC but it is very complex.")]
- [Test]
- public void Complex_Linq()
- {
- var doc = GetDynamicNode(1173);
-
- var paramVals = new Dictionary {{"splitTerm", new char[] {','}}, {"searchId", "1173"}};
- var result = doc.Ancestors().OrderBy("level")
- .Single()
- .Descendants()
- .Where("selectedNodes != null && selectedNodes != String.Empty && selectedNodes.Split(splitTerm).Contains(searchId)", paramVals)
- .FirstOrDefault();
-
- Assert.IsNotNull(result);
- Assert.AreEqual(4444, result.Id);
- }
-
- [Test]
- public void Index()
- {
- var doc = GetDynamicNode(1173);
- Assert.AreEqual(0, doc.Index());
- doc = GetDynamicNode(1176);
- Assert.AreEqual(3, doc.Index());
- doc = GetDynamicNode(1177);
- Assert.AreEqual(1, doc.Index());
- doc = GetDynamicNode(1178);
- Assert.AreEqual(2, doc.Index());
- }
-
- [Test]
- public virtual void Is_First_Root_Nodes()
- {
- var doc = GetDynamicNode(1046); //test root nodes
- Assert.IsTrue(doc.IsFirst());
- doc = GetDynamicNode(1172);
- Assert.IsFalse(doc.IsFirst());
- }
-
- [Test]
- public void Is_First()
- {
- var doc = GetDynamicNode(1173); //test normal nodes
- Assert.IsTrue(doc.IsFirst());
- doc = GetDynamicNode(1175);
- Assert.IsFalse(doc.IsFirst());
- }
-
- [Test]
- public virtual void Is_Not_First_Root_Nodes()
- {
- var doc = GetDynamicNode(1046); //test root nodes
- Assert.IsFalse(doc.IsNotFirst());
- doc = GetDynamicNode(1172);
- Assert.IsTrue(doc.IsNotFirst());
- }
-
- [Test]
- public void Is_Not_First()
- {
- var doc = GetDynamicNode(1173); //test normal nodes
- Assert.IsFalse(doc.IsNotFirst());
- doc = GetDynamicNode(1175);
- Assert.IsTrue(doc.IsNotFirst());
- }
-
- [Test]
- public virtual void Is_Position_Root_Nodes()
- {
- var doc = GetDynamicNode(1046); //test root nodes
- Assert.IsTrue(doc.IsPosition(0));
- doc = GetDynamicNode(1172);
- Assert.IsTrue(doc.IsPosition(1));
- }
-
- [Test]
- public void Is_Position()
- {
- var doc = GetDynamicNode(1173); //test normal nodes
- Assert.IsTrue(doc.IsPosition(0));
- doc = GetDynamicNode(1175);
- Assert.IsTrue(doc.IsPosition(1));
- }
-
- [Test]
- public void Children_GroupBy_DocumentTypeAlias()
- {
- var doc = GetDynamicNode(1046);
-
- var found1 = doc.Children.GroupBy("DocumentTypeAlias");
-
- var casted = (IEnumerable>)(found1);
- Assert.AreEqual(2, casted.Count());
- Assert.AreEqual(2, casted.Single(x => x.Key.ToString() == "Home").Count());
- Assert.AreEqual(1, casted.Single(x => x.Key.ToString() == "CustomDocument").Count());
- }
-
- [Test]
- public void Children_Where_DocumentTypeAlias()
- {
- var doc = GetDynamicNode(1046);
-
- var found1 = doc.Children.Where("DocumentTypeAlias == \"CustomDocument\"");
- var found2 = doc.Children.Where("DocumentTypeAlias == \"Home\"");
-
- Assert.AreEqual(1, found1.Count());
- Assert.AreEqual(2, found2.Count());
- }
-
- [Test]
- public void Children_Where_NodeTypeAlias()
- {
- var doc = GetDynamicNode(1046);
-
- var found1 = doc.Children.Where("NodeTypeAlias == \"CustomDocument\"");
- var found2 = doc.Children.Where("NodeTypeAlias == \"Home\"");
-
- Assert.AreEqual(1, found1.Count());
- Assert.AreEqual(2, found2.Count());
- }
-
- [Test]
- public void Children_Order_By_Update_Date()
- {
- var asDynamic = GetDynamicNode(1173);
-
- var ordered = asDynamic.Children.OrderBy("UpdateDate");
- var casted = (IEnumerable)ordered;
-
- var correctOrder = new[] { 1178, 1177, 1174, 1176 };
- for (var i = 0; i < correctOrder.Length ;i++)
- {
- Assert.AreEqual(correctOrder[i], ((dynamic)casted.ElementAt(i)).Id);
- }
-
- }
-
- [Test]
- public void Children_Order_By_Update_Date_Descending()
- {
- var asDynamic = GetDynamicNode(1173);
-
- var ordered = asDynamic.Children.OrderBy("UpdateDate desc");
- var casted = (IEnumerable)ordered;
-
- var correctOrder = new[] { 1176, 1174, 1177, 1178 };
- for (var i = 0; i < correctOrder.Length; i++)
- {
- Assert.AreEqual(correctOrder[i], ((dynamic)casted.ElementAt(i)).Id);
- }
-
- }
-
- [Test]
- public void HasProperty()
- {
- var asDynamic = GetDynamicNode(1173);
-
- var hasProp = asDynamic.HasProperty(Constants.Conventions.Content.UrlAlias);
-
- Assert.AreEqual(true, (bool)hasProp);
-
- }
-
- [Test]
- public void Skip()
- {
- var asDynamic = GetDynamicNode(1173);
-
- var skip = asDynamic.Children.Skip(2);
- var casted = (IEnumerable)skip;
-
- Assert.AreEqual(2, casted.Count());
- Assert.IsTrue(casted.Select(x => ((dynamic) x).Id).ContainsAll(new dynamic[] {1178, 1176}));
-
- }
-
- [Test]
- public void HasValue()
- {
- var asDynamic = GetDynamicNode(1173);
-
- var hasValue = asDynamic.HasValue(Constants.Conventions.Content.UrlAlias);
- var noValue = asDynamic.HasValue("blahblahblah");
-
- Assert.IsTrue(hasValue);
- Assert.IsFalse(noValue);
- }
-
- [Test]
- public void Take()
- {
- var asDynamic = GetDynamicNode(1173);
-
- var take = asDynamic.Children.Take(2);
- var casted = (IEnumerable)take;
-
- Assert.AreEqual(2, casted.Count());
- Assert.IsTrue(casted.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1174, 1177 }));
- }
-
- [Test]
- public void Ancestors_Where_Visible()
- {
- var asDynamic = GetDynamicNode(1174);
-
- var whereVisible = asDynamic.Ancestors().Where("Visible");
- var casted = (IEnumerable)whereVisible;
-
- Assert.AreEqual(1, casted.Count());
-
- }
-
- [Test]
- public void Visible()
- {
- var asDynamicHidden = GetDynamicNode(1046);
- var asDynamicVisible = GetDynamicNode(1173);
-
- Assert.IsFalse(asDynamicHidden.Visible);
- Assert.IsTrue(asDynamicVisible.Visible);
- }
-
- [Test]
- public void Ensure_TinyMCE_Converted_Type_User_Property()
- {
- var asDynamic = GetDynamicNode(1173);
-
- Assert.IsTrue(TypeHelper.IsTypeAssignableFrom(asDynamic.Content.GetType()));
- Assert.AreEqual("This is some content
", asDynamic.Content.ToString());
- }
-
- [Test]
- public void Get_Children_With_Pluralized_Alias()
- {
- var asDynamic = GetDynamicNode(1173);
-
- Action doAssert = d =>
- {
- Assert.IsTrue(TypeHelper.IsTypeAssignableFrom(d));
- var casted = (IEnumerable)d;
- Assert.AreEqual(2, casted.Count());
- };
-
- doAssert(asDynamic.Homes); //pluralized alias
- doAssert(asDynamic.homes); //pluralized alias
- doAssert(asDynamic.CustomDocuments); //pluralized alias
- doAssert(asDynamic.customDocuments); //pluralized alias
- }
-
- [Test]
- public void GetPropertyValue_Non_Reflected()
- {
- var asDynamic = GetDynamicNode(1174);
-
- Assert.AreEqual("Custom data with same property name as the member name", asDynamic.GetPropertyValue("creatorName"));
- Assert.AreEqual("Custom data with same property name as the member name", asDynamic.GetPropertyValue("CreatorName"));
- }
-
- [Test]
- public void GetPropertyValue_Reflected()
- {
- var asDynamic = GetDynamicNode(1174);
-
- Assert.AreEqual("admin", asDynamic.GetPropertyValue("@creatorName"));
- Assert.AreEqual("admin", asDynamic.GetPropertyValue("@CreatorName"));
- }
-
- [Test]
- public void Get_User_Property_With_Same_Name_As_Member_Property()
- {
- var asDynamic = GetDynamicNode(1174);
-
- Assert.AreEqual("Custom data with same property name as the member name", asDynamic.creatorName);
-
- //because CreatorName is defined on DynamicNode, it will not return the user defined property
- Assert.AreEqual("admin", asDynamic.CreatorName);
- }
-
- [Test]
- public void Get_Member_Property()
- {
- var asDynamic = GetDynamicNode(1173);
-
- Assert.AreEqual((int) 2, (int) asDynamic.Level);
- Assert.AreEqual((int) 2, (int) asDynamic.level);
-
- Assert.AreEqual((int) 1046, (int) asDynamic.ParentId);
- Assert.AreEqual((int) 1046, (int) asDynamic.parentId);
- }
-
- [Test]
- public void Get_Children()
- {
- var asDynamic = GetDynamicNode(1173);
-
- var children = asDynamic.Children;
- Assert.IsTrue(TypeHelper.IsTypeAssignableFrom(children));
-
- var childrenAsList = asDynamic.ChildrenAsList; //test ChildrenAsList too
- Assert.IsTrue(TypeHelper.IsTypeAssignableFrom(childrenAsList));
-
- var castChildren = (IEnumerable)children;
- Assert.AreEqual(4, castChildren.Count());
-
- var castChildrenAsList = (IEnumerable)childrenAsList;
- Assert.AreEqual(4, castChildrenAsList.Count());
- }
-
- [Test]
- public void Ancestor_Or_Self()
- {
- var asDynamic = GetDynamicNode(1173);
-
- var result = asDynamic.AncestorOrSelf();
-
- Assert.IsNotNull(result);
-
- Assert.AreEqual((int) 1046, (int) result.Id);
- }
-
- [Test]
- public void Ancestors_Or_Self()
- {
- var asDynamic = GetDynamicNode(1174);
-
- var result = asDynamic.AncestorsOrSelf();
-
- Assert.IsNotNull(result);
-
- var list = (IEnumerable)result;
- Assert.AreEqual(3, list.Count());
- Assert.IsTrue(list.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1174, 1173, 1046 }));
- }
-
- [Test]
- public void Ancestors()
- {
- var asDynamic = GetDynamicNode(1174);
-
- var result = asDynamic.Ancestors();
-
- Assert.IsNotNull(result);
-
- var list = (IEnumerable)result;
- Assert.AreEqual(2, list.Count());
- Assert.IsTrue(list.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1046 }));
- }
-
- [Test]
- public void Descendants_Or_Self()
- {
- var asDynamic = GetDynamicNode(1046);
-
- var result = asDynamic.DescendantsOrSelf();
-
- Assert.IsNotNull(result);
-
- var list = (IEnumerable)result;
- Assert.AreEqual(9, list.Count());
- Assert.IsTrue(list.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1046, 1173, 1174, 1176, 1175, 4444 }));
- }
-
- [Test]
- public void Descendants()
- {
- var asDynamic = GetDynamicNode(1046);
-
- var result = asDynamic.Descendants();
-
- Assert.IsNotNull(result);
-
- var list = (IEnumerable)result;
- Assert.AreEqual(8, list.Count());
- Assert.IsTrue(list.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1174, 1176, 1175, 4444 }));
- }
-
- [Test]
- public void Up()
- {
- var asDynamic = GetDynamicNode(1173);
-
- var result = asDynamic.Up();
-
- Assert.IsNotNull(result);
-
- Assert.AreEqual((int) 1046, (int) result.Id);
- }
-
- [Test]
- public void Down()
- {
- var asDynamic = GetDynamicNode(1173);
-
- var result = asDynamic.Down();
-
- Assert.IsNotNull(result);
-
- Assert.AreEqual((int) 1174, (int) result.Id);
- }
-
- [Test]
- public void Next()
- {
- var asDynamic = GetDynamicNode(1173);
-
- var result = asDynamic.Next();
-
- Assert.IsNotNull(result);
-
- Assert.AreEqual((int) 1175, (int) result.Id);
- }
-
- [Test]
- public void Next_Without_Sibling()
- {
- var asDynamic = GetDynamicNode(1176);
-
- Assert.IsNull(asDynamic.Next());
- }
-
- [Test]
- public void Previous_Without_Sibling()
- {
- var asDynamic = GetDynamicNode(1173);
-
- Assert.IsNull(asDynamic.Previous());
- }
-
- [Test]
- public void Previous()
- {
- var asDynamic = GetDynamicNode(1176);
-
- var result = asDynamic.Previous();
-
- Assert.IsNotNull(result);
-
- Assert.AreEqual((int)1178, (int)result.Id);
- }
- }
-
- ///
- /// Extension methods used in tests
- ///
- public static class TestExtensionMethods
- {
- public static bool ContainsValue(this string s, int val)
- {
- return s.Contains(val.ToString());
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicNodeTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicNodeTests.cs
deleted file mode 100644
index b000466a85..0000000000
--- a/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicNodeTests.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-using System;
-using System.IO;
-using NUnit.Framework;
-using Umbraco.Core;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.IO;
-using Umbraco.Tests.TestHelpers;
-using Umbraco.Web;
-using Umbraco.Web.PublishedCache;
-using Umbraco.Web.PublishedCache.XmlPublishedCache;
-using umbraco.MacroEngines;
-using umbraco.NodeFactory;
-using System.Linq;
-
-namespace Umbraco.Tests.PublishedContent
-{
- [TestFixture]
- public class DynamicNodeTests : DynamicDocumentTestsBase
- {
- ///
- /// We only need a new schema per fixture... speeds up testing
- ///
- protected override DatabaseBehavior DatabaseTestBehavior
- {
- get { return DatabaseBehavior.NewSchemaPerFixture; }
- }
-
- public override void Initialize()
- {
- base.Initialize();
- //copy the umbraco settings file over
- var currDir = new DirectoryInfo(TestHelper.CurrentAssemblyDirectory);
- File.Copy(
- currDir.Parent.Parent.Parent.GetDirectories("Umbraco.Web.UI")
- .First()
- .GetDirectories("config").First()
- .GetFiles("umbracoSettings.Release.config").First().FullName,
- Path.Combine(currDir.Parent.Parent.FullName, "config", "umbracoSettings.config"),
- true);
-
- UmbracoSettings.SettingsFilePath = IOHelper.MapPath(SystemDirectories.Config + Path.DirectorySeparatorChar, false);
-
- //need to specify a custom callback for unit tests
- DynamicNode.GetDataTypeCallback = (docTypeAlias, propertyAlias) =>
- {
- if (propertyAlias == "content")
- {
- //return the rte type id
- return Guid.Parse(Constants.PropertyEditors.TinyMCEv3);
- }
- return Guid.Empty;
- };
-
- }
-
- [Test]
- [Ignore("This test will never work unless DynamicNode is refactored a lot in order to get a list of root nodes since root nodes don't have a parent to look up")]
- public override void Is_First_Root_Nodes()
- {
- base.Is_First_Root_Nodes();
- }
-
- [Test]
- [Ignore("This test will never work unless DynamicNode is refactored a lot in order to get a list of root nodes since root nodes don't have a parent to look up")]
- public override void Is_Not_First_Root_Nodes()
- {
- base.Is_Not_First_Root_Nodes();
- }
-
- [Test]
- [Ignore("This test will never work unless DynamicNode is refactored a lot in order to get a list of root nodes since root nodes don't have a parent to look up")]
- public override void Is_Position_Root_Nodes()
- {
- base.Is_Position_Root_Nodes();
- }
-
- public override void TearDown()
- {
- base.TearDown();
- }
-
- protected override dynamic GetDynamicNode(int id)
- {
- //var template = Template.MakeNew("test", new User(0));
- //var ctx = GetUmbracoContext("/test", template.Id);
- var ctx = GetUmbracoContext("/test", 1234);
-
- var cache = ctx.ContentCache.InnerCache as PublishedContentCache;
- if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the legacy one is supported.");
-
- var node = new DynamicNode(
- new DynamicBackingItem(
- new Node(cache.GetXml(ctx).SelectSingleNode("//*[@id='" + id + "' and @isDoc]"))));
- Assert.IsNotNull(node);
- return (dynamic)node;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicPublishedContentCustomExtensionMethods.cs b/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicPublishedContentCustomExtensionMethods.cs
deleted file mode 100644
index b2cebf3ed0..0000000000
--- a/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicPublishedContentCustomExtensionMethods.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System.Collections.Generic;
-using Umbraco.Web.Models;
-
-namespace Umbraco.Tests.PublishedContent
-{
- public static class DynamicPublishedContentCustomExtensionMethods
- {
-
- public static string DynamicDocumentNoParameters(this DynamicPublishedContent doc)
- {
- return "Hello world";
- }
-
- public static string DynamicDocumentCustomString(this DynamicPublishedContent doc, string custom)
- {
- return custom;
- }
-
- public static string DynamicDocumentMultiParam(this DynamicPublishedContent doc, string custom, int i, bool b)
- {
- return custom + i + b;
- }
-
- public static string DynamicDocumentListMultiParam(this DynamicPublishedContentList doc, string custom, int i, bool b)
- {
- return custom + i + b;
- }
-
- public static string DynamicDocumentEnumerableMultiParam(this IEnumerable doc, string custom, int i, bool b)
- {
- return custom + i + b;
- }
-
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicPublishedContentTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicPublishedContentTests.cs
deleted file mode 100644
index fdf097472d..0000000000
--- a/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicPublishedContentTests.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-using System.Linq;
-using NUnit.Framework;
-using Umbraco.Core;
-using Umbraco.Core.Models;
-using Umbraco.Web;
-using Umbraco.Web.Models;
-using Umbraco.Web.PublishedCache;
-using Umbraco.Web.PublishedCache.XmlPublishedCache;
-
-namespace Umbraco.Tests.PublishedContent
-{
- [TestFixture]
- public class DynamicPublishedContentTests : DynamicDocumentTestsBase
- {
- public override void Initialize()
- {
- base.Initialize();
-
- }
-
- public override void TearDown()
- {
- base.TearDown();
- }
-
- internal DynamicPublishedContent GetNode(int id)
- {
- //var template = Template.MakeNew("test", new User(0));
- //var ctx = GetUmbracoContext("/test", template.Id);
- var ctx = GetUmbracoContext("/test", 1234);
- var doc = ctx.ContentCache.GetById(id);
- Assert.IsNotNull(doc);
- var dynamicNode = new DynamicPublishedContent(doc);
- Assert.IsNotNull(dynamicNode);
- return dynamicNode;
- }
-
- protected override dynamic GetDynamicNode(int id)
- {
- return GetNode(id).AsDynamic();
- }
-
- [Test]
- public void Custom_Extension_Methods()
- {
- var asDynamic = GetDynamicNode(1173);
-
- Assert.AreEqual("Hello world", asDynamic.DynamicDocumentNoParameters());
- Assert.AreEqual("Hello world!", asDynamic.DynamicDocumentCustomString("Hello world!"));
- Assert.AreEqual("Hello world!" + 123 + false, asDynamic.DynamicDocumentMultiParam("Hello world!", 123, false));
- Assert.AreEqual("Hello world!" + 123 + false, asDynamic.Children.DynamicDocumentListMultiParam("Hello world!", 123, false));
- Assert.AreEqual("Hello world!" + 123 + false, asDynamic.Children.DynamicDocumentEnumerableMultiParam("Hello world!", 123, false));
-
- }
-
- [Test]
- public void Returns_IDocument_Object()
- {
- var helper = new TestHelper(GetNode(1173));
- var doc = helper.GetDoc();
- //HasProperty is only a prop on DynamicPublishedContent, NOT IPublishedContent
- Assert.IsFalse(doc.GetType().GetProperties().Any(x => x.Name == "HasProperty"));
- }
-
- [Test]
- public void Returns_DynamicDocument_Object()
- {
- var helper = new TestHelper(GetNode(1173));
- var doc = helper.GetDocAsDynamic();
- //HasProperty is only a prop on DynamicPublishedContent, NOT IPublishedContent
- Assert.IsTrue(doc.HasProperty(Constants.Conventions.Content.UrlAlias));
- }
-
- [Test]
- public void Returns_DynamicDocument_Object_After_Casting()
- {
- var helper = new TestHelper(GetNode(1173));
- var doc = helper.GetDoc();
- var ddoc = (dynamic) doc;
- //HasProperty is only a prop on DynamicPublishedContent, NOT IPublishedContent
- Assert.IsTrue(ddoc.HasProperty(Constants.Conventions.Content.UrlAlias));
- }
-
- ///
- /// Test class to mimic UmbracoHelper when returning docs
- ///
- public class TestHelper
- {
- private readonly DynamicPublishedContent _doc;
-
- public TestHelper(DynamicPublishedContent doc)
- {
- _doc = doc;
- }
-
- public IPublishedContent GetDoc()
- {
- return _doc;
- }
-
- public dynamic GetDocAsDynamic()
- {
- return _doc.AsDynamic();
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicXmlConverterTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicXmlConverterTests.cs
deleted file mode 100644
index f0ad32a1bc..0000000000
--- a/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicXmlConverterTests.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using System.Xml;
-using System.Xml.Linq;
-using NUnit.Framework;
-using Umbraco.Core;
-using Umbraco.Core.Dynamics;
-using Umbraco.Tests.PartialTrust;
-
-namespace Umbraco.Tests.PublishedContent
-{
- [TestFixture]
- public class DynamicXmlConverterTests : AbstractPartialTrustFixture
- {
- [Test]
- public void Convert_To_Raw_String()
- {
- var xml = "/media/54/tulips.jpg 1024 768 620888 jpg /media/41/hydrangeas.jpg 1024 768 595284 jpg ";
- var dXml = new DynamicXml(
- XmlHelper.StripDashesInElementOrAttributeNames(xml),
- xml);
- var result = dXml.TryConvertTo();
- Assert.IsTrue(result.Success);
- Assert.AreEqual(xml, result.Result.Value);
- }
-
- [Test]
- public void Convert_To_Raw_XElement()
- {
- var xml = "/media/54/tulips.jpg 1024 768 620888 jpg /media/41/hydrangeas.jpg 1024 768 595284 jpg ";
- var dXml = new DynamicXml(
- XmlHelper.StripDashesInElementOrAttributeNames(xml),
- xml);
- var result = dXml.TryConvertTo();
- Assert.IsTrue(result.Success);
- Assert.AreEqual(xml, result.Result.Value.ToString(SaveOptions.DisableFormatting));
- }
-
- [Test]
- public void Convert_To_Raw_XmlElement()
- {
- var xml = "/media/54/tulips.jpg 1024 768 620888 jpg /media/41/hydrangeas.jpg 1024 768 595284 jpg ";
- var dXml = new DynamicXml(
- XmlHelper.StripDashesInElementOrAttributeNames(xml),
- xml);
- var result = dXml.TryConvertTo();
- Assert.IsTrue(result.Success);
- Assert.AreEqual(xml, result.Result.Value.OuterXml);
- }
-
- [Test]
- public void Convert_To_Raw_XmlDocument()
- {
- var xml = "/media/54/tulips.jpg 1024 768 620888 jpg /media/41/hydrangeas.jpg 1024 768 595284 jpg ";
- var dXml = new DynamicXml(
- XmlHelper.StripDashesInElementOrAttributeNames(xml),
- xml);
- var result = dXml.TryConvertTo();
- Assert.IsTrue(result.Success);
- Assert.AreEqual(xml, result.Result.Value.InnerXml);
- }
-
- [Test]
- public void Convert_To_String()
- {
- var xml = "/media/54/tulips.jpg 1024 768 620888 jpg /media/41/hydrangeas.jpg 1024 768 595284 jpg ";
- var dXml = new DynamicXml(xml);
- var result = dXml.TryConvertTo();
- Assert.IsTrue(result.Success);
- Assert.AreEqual(xml, result.Result);
- }
-
- [Test]
- public void Convert_To_XElement()
- {
- var xml = "/media/54/tulips.jpg 1024 768 620888 jpg /media/41/hydrangeas.jpg 1024 768 595284 jpg ";
- var dXml = new DynamicXml(xml);
- var result = dXml.TryConvertTo();
- Assert.IsTrue(result.Success);
- Assert.AreEqual(xml, result.Result.ToString(SaveOptions.DisableFormatting));
- }
-
- [Test]
- public void Convert_To_XmlElement()
- {
- var xml = "/media/54/tulips.jpg 1024 768 620888 jpg /media/41/hydrangeas.jpg 1024 768 595284 jpg ";
- var dXml = new DynamicXml(xml);
- var result = dXml.TryConvertTo();
- Assert.IsTrue(result.Success);
- Assert.AreEqual(xml, result.Result.OuterXml);
- }
-
- [Test]
- public void Convert_To_XmlDocument()
- {
- var xml = "/media/54/tulips.jpg 1024 768 620888 jpg /media/41/hydrangeas.jpg 1024 768 595284 jpg ";
- var dXml = new DynamicXml(xml);
- var result = dXml.TryConvertTo();
- Assert.IsTrue(result.Success);
- Assert.AreEqual(xml, result.Result.InnerXml);
- }
-
- public override void TestSetup()
- {
-
- }
-
- public override void TestTearDown()
- {
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicXmlTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicXmlTests.cs
deleted file mode 100644
index 642952e4aa..0000000000
--- a/src/Umbraco.Tests/PublishedCache/PublishedContent/DynamicXmlTests.cs
+++ /dev/null
@@ -1,178 +0,0 @@
-using System;
-using System.Diagnostics;
-using Microsoft.CSharp.RuntimeBinder;
-using NUnit.Framework;
-using Umbraco.Core;
-using Umbraco.Core.Dynamics;
-using System.Linq;
-
-namespace Umbraco.Tests.PublishedContent
-{
- [TestFixture]
- public class DynamicXmlTests
- {
- ///
- /// Ensures that when we return the xml structure we get the real structure, not the replaced hyphen structure
- /// see: http://issues.umbraco.org/issue/U4-1405#comment=67-5113
- /// http://issues.umbraco.org/issue/U4-1636
- ///
- [Test]
- public void Deals_With_Hyphenated_Values()
- {
- var xml = @"
-
- True
- 1161
- /content/
- 12 december Zorgbeurs Care
-
- ";
-
- var typedXml = new DynamicXml(
- XmlHelper.StripDashesInElementOrAttributeNames(xml),
- xml);
- dynamic dynamicXml = typedXml;
-
- var typedElement = typedXml.RawXmlElement.Element("url-picker");
- var dynamicElementByCleanedName = dynamicXml.urlpicker;
-
- Assert.IsNotNull(typedElement);
- Assert.IsNotNull(dynamicElementByCleanedName);
-
- Assert.AreEqual(
- typedElement.Attribute("some-attribute").Value,
- dynamicElementByCleanedName.someattribute);
- }
-
- [Test]
- public void Custom_Extension_Method_Legacy()
- {
- var xml = "/media/54/tulips.jpg 1024 768 620888 jpg /media/41/hydrangeas.jpg 1024 768 595284 jpg ";
- var typedXml = new global::umbraco.MacroEngines.DynamicXml(xml);
- dynamic dynamicXml = typedXml;
-
- //we haven't explicitly defined ElementAt so this will dynamically invoke this method
- var element = dynamicXml.ElementAt(0);
-
- Assert.AreEqual("1057", Enumerable.First(element.BaseElement.Elements()).Attribute("id").Value);
- }
-
- [Test]
- public void Custom_Extension_Method()
- {
- var xml = "/media/54/tulips.jpg 1024 768 620888 jpg /media/41/hydrangeas.jpg 1024 768 595284 jpg ";
- var typedXml = new DynamicXml(xml);
-
- dynamic dynamicXml = typedXml;
-
- //we haven't explicitly defined ElementAt so this will dynamically invoke this method
- var element = dynamicXml.ElementAt(0);
-
- Assert.AreEqual("1057", Enumerable.First(element.BaseElement.Elements()).Attribute("id").Value);
- }
-
- [Test]
- public void Take_Legacy()
- {
- var xml = "/media/54/tulips.jpg 1024 768 620888 jpg /media/41/hydrangeas.jpg 1024 768 595284 jpg ";
- var typedXml = new global::umbraco.MacroEngines.DynamicXml(xml);
- dynamic dynamicXml = typedXml;
- var typedTaken = typedXml.Take(1);
- var dynamicTaken = dynamicXml.Take(1);
-
- Assert.AreEqual(1, typedTaken.Count());
- Assert.AreEqual(1, Enumerable.Count(dynamicTaken));
-
- Assert.AreEqual("1057", typedTaken.ElementAt(0).BaseElement.Elements().First().Attribute("id").Value);
- Assert.AreEqual("1057", Enumerable.First(Enumerable.ElementAt(dynamicTaken, 0).BaseElement.Elements()).Attribute("id").Value);
- }
-
- [Test]
- public void Take()
- {
- var xml = "/media/54/tulips.jpg 1024 768 620888 jpg /media/41/hydrangeas.jpg 1024 768 595284 jpg ";
- var typedXml = new DynamicXml(xml);
- dynamic dynamicXml = typedXml;
- var typedTaken = typedXml.Take(1);
- var dynamicTaken = dynamicXml.Take(1);
-
- Assert.AreEqual(1, typedTaken.Count());
- Assert.AreEqual(1, Enumerable.Count(dynamicTaken));
-
- Assert.AreEqual("1057", typedTaken.ElementAt(0).BaseElement.Elements().First().Attribute("id").Value);
- Assert.AreEqual("1057", Enumerable.First(Enumerable.ElementAt(dynamicTaken, 0).BaseElement.Elements()).Attribute("id").Value);
- }
-
- [Test]
- public void Ensure_Legacy_Objects_Are_Returned()
- {
- var xml = "/media/54/tulips.jpg 1024 768 620888 jpg /media/41/hydrangeas.jpg 1024 768 595284 jpg ";
- var mediaItems = new global::umbraco.MacroEngines.DynamicXml(xml);
- //Debug.WriteLine("full xml = {0}", mediaItems.ToXml());
-
- if (mediaItems.Count() != 0)
- {
- foreach (dynamic item in mediaItems)
- {
- Type itemType = item.GetType();
- Debug.WriteLine("item type = {0}", itemType);
- dynamic image = item.Image;
-
- Type imageType = image.GetType();
- Debug.WriteLine("image type = {0}", imageType);
-
- //ensure they are the same
- Assert.AreEqual(itemType, imageType);
-
- //ensure they are legacy
- Assert.AreEqual(typeof(global::umbraco.MacroEngines.DynamicXml), itemType);
- Assert.AreEqual(typeof(global::umbraco.MacroEngines.DynamicXml), imageType);
- }
- }
- }
-
- ///
- /// Test the current Core class
- ///
- [Test]
- public void Find_Test_Core_Class()
- {
- RunFindTest(x => new DynamicXml(x));
- }
-
- ///
- /// Tests the macroEngines legacy class
- ///
- [Test]
- public void Find_Test_Legacy_Class()
- {
- RunFindTest(x => new global::umbraco.MacroEngines.DynamicXml(x));
- }
-
- private void RunFindTest(Func getDynamicXml)
- {
- var xmlstring = @"
-
-
-
- ";
-
- dynamic dXml = getDynamicXml(xmlstring);
-
- var result1 = dXml.Find("@name", "test 1");
- var result2 = dXml.Find("@name", "test 2");
- var result3 = dXml.Find("@name", "test 3");
- var result4 = dXml.Find("@name", "dont find");
-
- Assert.AreEqual("found 1", result1.value);
- Assert.AreEqual("found 2", result2.value);
- Assert.AreEqual("found 3", result3.value);
- Assert.Throws(() =>
- {
- //this will throw because result4 is not found
- var temp = result4.value;
- });
- }
-
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContent/LegacyExamineBackedMediaTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedContent/LegacyExamineBackedMediaTests.cs
deleted file mode 100644
index 19f3046211..0000000000
--- a/src/Umbraco.Tests/PublishedCache/PublishedContent/LegacyExamineBackedMediaTests.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using System;
-using System.IO;
-using System.Linq;
-using Lucene.Net.Documents;
-using Lucene.Net.Store;
-using NUnit.Framework;
-using Umbraco.Core.Configuration;
-using Umbraco.Tests.UmbracoExamine;
-using umbraco.MacroEngines;
-
-namespace Umbraco.Tests.PublishedContent
-{
- public class LegacyExamineBackedMediaTests : ExamineBaseTest
- {
- public override void TestSetup()
- {
- base.TestSetup();
- UmbracoSettings.ForceSafeAliases = true;
- UmbracoSettings.UmbracoLibraryCacheDuration = 1800;
- UmbracoSettings.ForceSafeAliases = true;
- }
-
- public override void TestTearDown()
- {
- base.TestTearDown();
- }
-
- [Test]
- public void Ensure_Children_Are_Sorted()
- {
- using (var luceneDir = new RAMDirectory())
- {
- var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
- indexer.RebuildIndex();
-
- var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
- var result = searcher.Search(searcher.CreateSearchCriteria().Id(1111).Compile());
- Assert.IsNotNull(result);
- Assert.AreEqual(1, result.TotalItemCount);
-
- var searchItem = result.First();
- var backedMedia = new ExamineBackedMedia(searchItem, indexer, searcher);
- var children = backedMedia.ChildrenAsList.Value;
-
- var currSort = 0;
- for (var i = 0; i < children.Count(); i++)
- {
- Assert.GreaterOrEqual(children[i].SortOrder, currSort);
- currSort = children[i].SortOrder;
- }
- }
-
- }
-
- [Test]
- public void Ensure_Result_Has_All_Values()
- {
- using (var luceneDir = new RAMDirectory())
- {
- var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
- indexer.RebuildIndex();
-
- var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
- var result = searcher.Search(searcher.CreateSearchCriteria().Id(1111).Compile());
- Assert.IsNotNull(result);
- Assert.AreEqual(1, result.TotalItemCount);
-
- var searchItem = result.First();
- var backedMedia = new ExamineBackedMedia(searchItem, indexer, searcher);
-
- Assert.AreEqual(searchItem.Id, backedMedia.Id);
- Assert.AreEqual(searchItem.Fields["sortOrder"], backedMedia.SortOrder.ToString());
- Assert.AreEqual(searchItem.Fields["urlName"], backedMedia.UrlName);
- Assert.AreEqual(DateTools.StringToDate(searchItem.Fields["createDate"]), backedMedia.CreateDate);
- Assert.AreEqual(DateTools.StringToDate(searchItem.Fields["updateDate"]), backedMedia.UpdateDate);
- Assert.AreEqual(Guid.Parse(searchItem.Fields["version"]), backedMedia.Version);
- Assert.AreEqual(searchItem.Fields["level"], backedMedia.Level.ToString());
- Assert.AreEqual(searchItem.Fields["writerID"], backedMedia.WriterID.ToString());
- Assert.AreEqual(searchItem.Fields["writerID"], backedMedia.CreatorID.ToString()); //there's only writerId in the xml
- Assert.AreEqual(searchItem.Fields["writerName"], backedMedia.CreatorName);
- Assert.AreEqual(searchItem.Fields["writerName"], backedMedia.WriterName); //tehre's only writer name in the xml
- }
-
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedContent/PublishedContentDataTableTests.cs
deleted file mode 100644
index c6ce668a40..0000000000
--- a/src/Umbraco.Tests/PublishedCache/PublishedContent/PublishedContentDataTableTests.cs
+++ /dev/null
@@ -1,215 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using NUnit.Framework;
-using Umbraco.Core;
-using Umbraco.Core.Dynamics;
-using Umbraco.Core.Models;
-using Umbraco.Tests.TestHelpers;
-using Umbraco.Web;
-
-namespace Umbraco.Tests.PublishedContent
-{
- ///
- /// Unit tests for IPublishedContent and extensions
- ///
- [TestFixture]
- public class PublishedContentDataTableTests : BaseRoutingTest
- {
- public override void Initialize()
- {
- base.Initialize();
- //need to specify a different callback for testing
- Umbraco.Web.PublishedContentExtensions.GetPropertyAliasesAndNames = s =>
- {
- var userFields = new Dictionary()
- {
- {"property1", "Property 1"},
- {"property2", "Property 2"}
- };
- if (s == "Child")
- {
- userFields.Add("property4", "Property 4");
- }
- else
- {
- userFields.Add("property3", "Property 3");
- }
-
- //ensure the standard fields are there
- var allFields = new Dictionary()
- {
- {"Id", "Id"},
- {"NodeName", "NodeName"},
- {"NodeTypeAlias", "NodeTypeAlias"},
- {"CreateDate", "CreateDate"},
- {"UpdateDate", "UpdateDate"},
- {"CreatorName", "CreatorName"},
- {"WriterName", "WriterName"},
- {"Url", "Url"}
- };
- foreach (var f in userFields.Where(f => !allFields.ContainsKey(f.Key)))
- {
- allFields.Add(f.Key, f.Value);
- }
- return allFields;
- };
- var routingContext = GetRoutingContext("/test");
-
- //set the UmbracoContext.Current since the extension methods rely on it
- UmbracoContext.Current = routingContext.UmbracoContext;
- }
-
- public override void TearDown()
- {
- base.TearDown();
- Umbraco.Web.PublishedContentExtensions.GetPropertyAliasesAndNames = null;
- UmbracoContext.Current = null;
- }
-
- [Test]
- public void To_DataTable()
- {
- var doc = GetContent(true, 1);
- var dt = doc.ChildrenAsTable();
-
- Assert.AreEqual(11, dt.Columns.Count);
- Assert.AreEqual(3, dt.Rows.Count);
- Assert.AreEqual("value4", dt.Rows[0]["Property 1"]);
- Assert.AreEqual("value5", dt.Rows[0]["Property 2"]);
- Assert.AreEqual("value6", dt.Rows[0]["Property 4"]);
- Assert.AreEqual("value7", dt.Rows[1]["Property 1"]);
- Assert.AreEqual("value8", dt.Rows[1]["Property 2"]);
- Assert.AreEqual("value9", dt.Rows[1]["Property 4"]);
- Assert.AreEqual("value10", dt.Rows[2]["Property 1"]);
- Assert.AreEqual("value11", dt.Rows[2]["Property 2"]);
- Assert.AreEqual("value12", dt.Rows[2]["Property 4"]);
- }
-
- [Test]
- public void To_DataTable_With_Filter()
- {
- var doc = GetContent(true, 1);
- //change a doc type alias
- ((TestPublishedContent) doc.Children.ElementAt(0)).DocumentTypeAlias = "DontMatch";
-
- var dt = doc.ChildrenAsTable("Child");
-
- Assert.AreEqual(11, dt.Columns.Count);
- Assert.AreEqual(2, dt.Rows.Count);
- Assert.AreEqual("value7", dt.Rows[0]["Property 1"]);
- Assert.AreEqual("value8", dt.Rows[0]["Property 2"]);
- Assert.AreEqual("value9", dt.Rows[0]["Property 4"]);
- Assert.AreEqual("value10", dt.Rows[1]["Property 1"]);
- Assert.AreEqual("value11", dt.Rows[1]["Property 2"]);
- Assert.AreEqual("value12", dt.Rows[1]["Property 4"]);
- }
-
- [Test]
- public void To_DataTable_No_Rows()
- {
- var doc = GetContent(false, 1);
- var dt = doc.ChildrenAsTable();
- //will return an empty data table
- Assert.AreEqual(0, dt.Columns.Count);
- Assert.AreEqual(0, dt.Rows.Count);
- }
-
- private IPublishedContent GetContent(bool createChildren, int indexVals)
- {
- var d = new TestPublishedContent
- {
- CreateDate = DateTime.Now,
- CreatorId = 1,
- CreatorName = "Shannon",
- DocumentTypeAlias = createChildren? "Parent" : "Child",
- DocumentTypeId = 2,
- Id = 3,
- SortOrder = 4,
- TemplateId = 5,
- UpdateDate = DateTime.Now,
- Path = "-1,3",
- UrlName = "home-page",
- Name = "Page" + Guid.NewGuid().ToString(),
- Version = Guid.NewGuid(),
- WriterId = 1,
- WriterName = "Shannon",
- Parent = null,
- Level = 1,
- Properties = new Collection(
- new List()
- {
- new PropertyResult("property1", "value" + indexVals, Guid.NewGuid(), PropertyResultType.UserProperty),
- new PropertyResult("property2", "value" + (indexVals + 1), Guid.NewGuid(), PropertyResultType.UserProperty)
- }),
- Children = new List()
- };
- if (createChildren)
- {
- d.Children = new List()
- {
- GetContent(false, indexVals + 3),
- GetContent(false, indexVals + 6),
- GetContent(false, indexVals + 9)
- };
- }
- if (!createChildren)
- {
- //create additional columns, used to test the different columns for child nodes
- d.Properties.Add(new PropertyResult("property4", "value" + (indexVals + 2), Guid.NewGuid(), PropertyResultType.UserProperty));
- }
- else
- {
- d.Properties.Add(new PropertyResult("property3", "value" + (indexVals + 2), Guid.NewGuid(), PropertyResultType.UserProperty));
- }
- return d;
- }
-
-
- private class TestPublishedContent : IPublishedContent
- {
- public string Url { get; set; }
- public PublishedItemType ItemType { get; set; }
-
- IPublishedContent IPublishedContent.Parent
- {
- get { return Parent; }
- }
- IEnumerable IPublishedContent.Children
- {
- get { return Children; }
- }
- public IPublishedContent Parent { get; set; }
- public int Id { get; set; }
- public int TemplateId { get; set; }
- public int SortOrder { get; set; }
- public string Name { get; set; }
- public string UrlName { get; set; }
- public string DocumentTypeAlias { get; set; }
- public int DocumentTypeId { get; set; }
- public string WriterName { get; set; }
- public string CreatorName { get; set; }
- public int WriterId { get; set; }
- public int CreatorId { get; set; }
- public string Path { get; set; }
- public DateTime CreateDate { get; set; }
- public DateTime UpdateDate { get; set; }
- public Guid Version { get; set; }
- public int Level { get; set; }
- public ICollection Properties { get; set; }
-
- public object this[string propertyAlias]
- {
- get { return GetProperty(propertyAlias).Value; }
- }
-
- public IEnumerable Children { get; set; }
- public IPublishedContentProperty GetProperty(string alias)
- {
- return Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
- }
- }
-
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedCache/PublishedContent/PublishedContentTestBase.cs
deleted file mode 100644
index 4db386e3a4..0000000000
--- a/src/Umbraco.Tests/PublishedCache/PublishedContent/PublishedContentTestBase.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using System;
-using System.IO;
-using Umbraco.Core;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.PropertyEditors;
-using Umbraco.Tests.TestHelpers;
-using Umbraco.Web;
-using Umbraco.Web.PublishedCache;
-using Umbraco.Web.PublishedCache.XmlPublishedCache;
-
-namespace Umbraco.Tests.PublishedContent
-{
- ///
- /// Abstract base class for tests for published content and published media
- ///
- public abstract class PublishedContentTestBase : BaseRoutingTest
- {
- public override void Initialize()
- {
- base.Initialize();
-
- UmbracoSettings.SettingsFilePath = Core.IO.IOHelper.MapPath(Core.IO.SystemDirectories.Config + Path.DirectorySeparatorChar, false);
-
- //need to specify a custom callback for unit tests
- PublishedContentHelper.GetDataTypeCallback = (docTypeAlias, propertyAlias) =>
- {
- if (propertyAlias == "content")
- {
- //return the rte type id
- return Guid.Parse(Constants.PropertyEditors.TinyMCEv3);
- }
- return Guid.Empty;
- };
-
- var rCtx = GetRoutingContext("/test", 1234);
- UmbracoContext.Current = rCtx.UmbracoContext;
-
- }
-
- protected override void FreezeResolution()
- {
- PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver(
- new[]
- {
- typeof(DatePickerPropertyEditorValueConverter),
- typeof(TinyMcePropertyEditorValueConverter),
- typeof(YesNoPropertyEditorValueConverter)
- });
-
- PublishedContentCacheResolver.Current = new PublishedContentCacheResolver(new PublishedContentCache());
- PublishedMediaCacheResolver.Current = new PublishedMediaCacheResolver(new PublishedMediaCache());
-
- base.FreezeResolution();
- }
-
- public override void TearDown()
- {
- base.TearDown();
-
- UmbracoContext.Current = null;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedContent/PublishedContentTests.cs
deleted file mode 100644
index 79940a82f2..0000000000
--- a/src/Umbraco.Tests/PublishedCache/PublishedContent/PublishedContentTests.cs
+++ /dev/null
@@ -1,491 +0,0 @@
-using System.Linq;
-using System.Web;
-using NUnit.Framework;
-using Umbraco.Core;
-using Umbraco.Core.Models;
-using Umbraco.Tests.TestHelpers;
-using Umbraco.Web;
-
-namespace Umbraco.Tests.PublishedContent
-{
- ///
- /// Tests the methods on IPublishedContent using the DefaultPublishedContentStore
- ///
- [TestFixture]
- public class PublishedContentTests : PublishedContentTestBase
- {
- protected override DatabaseBehavior DatabaseTestBehavior
- {
- get { return DatabaseBehavior.NoDatabasePerFixture; }
- }
-
- protected override string GetXmlContent(int templateId)
- {
- return @"
-
-
-
-
-]>
-
-
-
-
- 1
-
-
- This is some content]]>
-
-
-
-
-
-
-
-
-
-
-
-
- 1
-
-
-
-
-
-
-
-
-
- ";
- }
-
- internal IPublishedContent GetNode(int id)
- {
- var ctx = GetUmbracoContext("/test", 1234);
- var doc = ctx.ContentCache.GetById(id);
- Assert.IsNotNull(doc);
- return doc;
- }
-
- [Test]
- public void Is_Last_From_Where_Filter_Dynamic_Linq()
- {
- var doc = GetNode(1173);
-
- foreach (var d in doc.Children.Where("Visible"))
- {
- if (d.Id != 1178)
- {
- Assert.IsFalse(d.IsLast());
- }
- else
- {
- Assert.IsTrue(d.IsLast());
- }
- }
- }
-
- [Test]
- public void Is_Last_From_Where_Filter()
- {
- var doc = GetNode(1173);
-
- foreach (var d in doc.Children.Where(x => x.IsVisible()))
- {
- if (d.Id != 1178)
- {
- Assert.IsFalse(d.IsLast());
- }
- else
- {
- Assert.IsTrue(d.IsLast());
- }
- }
- }
-
- [Test]
- public void Is_Last_From_Take()
- {
- var doc = GetNode(1173);
-
- foreach (var d in doc.Children.Take(3))
- {
- if (d.Id != 1178)
- {
- Assert.IsFalse(d.IsLast());
- }
- else
- {
- Assert.IsTrue(d.IsLast());
- }
- }
- }
-
- [Test]
- public void Is_Last_From_Skip()
- {
- var doc = GetNode(1173);
-
- foreach (var d in doc.Children.Skip(1))
- {
- if (d.Id != 1176)
- {
- Assert.IsFalse(d.IsLast());
- }
- else
- {
- Assert.IsTrue(d.IsLast());
- }
- }
- }
-
- [Test]
- public void Is_Last_From_Concat()
- {
- var doc = GetNode(1173);
-
-
- foreach (var d in doc.Children.Concat(new[] { GetNode(1175), GetNode(4444) }))
- {
- if (d.Id != 4444)
- {
- Assert.IsFalse(d.IsLast());
- }
- else
- {
- Assert.IsTrue(d.IsLast());
- }
- }
- }
-
- [Test]
- public void Descendants_Ordered_Properly()
- {
- var doc = GetNode(1046);
-
- var currentLevel = 0;
- var lastSortOrder = 0;
- var levelChangesAt = new[] { 1046, 1173, 1174 };
-
- foreach (var d in doc.DescendantsOrSelf())
- {
- if (levelChangesAt.Contains(d.Id))
- {
- Assert.Greater(d.Level, currentLevel);
- currentLevel = d.Level;
- }
- else
- {
- Assert.AreEqual(currentLevel, d.Level);
- Assert.Greater(d.SortOrder, lastSortOrder);
- }
- lastSortOrder = d.SortOrder;
- }
- }
-
- [Test]
- public void Test_Get_Recursive_Val()
- {
- var doc = GetNode(1174);
- var rVal = doc.GetRecursiveValue("testRecursive");
- var nullVal = doc.GetRecursiveValue("DoNotFindThis");
- Assert.AreEqual("This is the recursive val", rVal);
- Assert.AreEqual("", nullVal);
- }
-
- [Test]
- public void Get_Property_Value_Uses_Converter()
- {
- var doc = GetNode(1173);
-
- var propVal = doc.GetPropertyValue("content");
- Assert.IsTrue(TypeHelper.IsTypeAssignableFrom(propVal.GetType()));
- Assert.AreEqual("This is some content
", propVal.ToString());
-
- var propVal2 = doc.GetPropertyValue("content");
- Assert.IsTrue(TypeHelper.IsTypeAssignableFrom(propVal2.GetType()));
- Assert.AreEqual("This is some content
", propVal2.ToString());
- }
-
- [Test]
- public void Complex_Linq()
- {
- var doc = GetNode(1173);
-
- var result = doc.Ancestors().OrderBy(x => x.Level)
- .Single()
- .Descendants()
- .FirstOrDefault(x => x.GetPropertyValue("selectedNodes", "").Split(',').Contains("1173"));
-
- Assert.IsNotNull(result);
- }
-
- [Test]
- public void Index()
- {
- var doc = GetNode(1173);
- Assert.AreEqual(0, doc.Index());
- doc = GetNode(1176);
- Assert.AreEqual(3, doc.Index());
- doc = GetNode(1177);
- Assert.AreEqual(1, doc.Index());
- doc = GetNode(1178);
- Assert.AreEqual(2, doc.Index());
- }
-
- [Test]
- public void Is_First()
- {
- var doc = GetNode(1046); //test root nodes
- Assert.IsTrue(doc.IsFirst());
- doc = GetNode(1172);
- Assert.IsFalse(doc.IsFirst());
- doc = GetNode(1173); //test normal nodes
- Assert.IsTrue(doc.IsFirst());
- doc = GetNode(1175);
- Assert.IsFalse(doc.IsFirst());
- }
-
- [Test]
- public void Is_Not_First()
- {
- var doc = GetNode(1046); //test root nodes
- Assert.IsFalse(doc.IsNotFirst());
- doc = GetNode(1172);
- Assert.IsTrue(doc.IsNotFirst());
- doc = GetNode(1173); //test normal nodes
- Assert.IsFalse(doc.IsNotFirst());
- doc = GetNode(1175);
- Assert.IsTrue(doc.IsNotFirst());
- }
-
- [Test]
- public void Is_Position()
- {
- var doc = GetNode(1046); //test root nodes
- Assert.IsTrue(doc.IsPosition(0));
- doc = GetNode(1172);
- Assert.IsTrue(doc.IsPosition(1));
- doc = GetNode(1173); //test normal nodes
- Assert.IsTrue(doc.IsPosition(0));
- doc = GetNode(1175);
- Assert.IsTrue(doc.IsPosition(1));
- }
-
- [Test]
- public void Children_GroupBy_DocumentTypeAlias()
- {
- var doc = GetNode(1046);
-
- var found1 = doc.Children.GroupBy("DocumentTypeAlias");
-
- Assert.AreEqual(2, found1.Count());
- Assert.AreEqual(2, found1.Single(x => x.Key.ToString() == "Home").Count());
- Assert.AreEqual(1, found1.Single(x => x.Key.ToString() == "CustomDocument").Count());
- }
-
- [Test]
- public void Children_Where_DocumentTypeAlias()
- {
- var doc = GetNode(1046);
-
- var found1 = doc.Children.Where("DocumentTypeAlias == \"CustomDocument\"");
- var found2 = doc.Children.Where("DocumentTypeAlias == \"Home\"");
-
- Assert.AreEqual(1, found1.Count());
- Assert.AreEqual(2, found2.Count());
- }
-
- [Test]
- public void Children_Order_By_Update_Date()
- {
- var doc = GetNode(1173);
-
- var ordered = doc.Children.OrderBy("UpdateDate");
-
- var correctOrder = new[] { 1178, 1177, 1174, 1176 };
- for (var i = 0; i < correctOrder.Length; i++)
- {
- Assert.AreEqual(correctOrder[i], ordered.ElementAt(i).Id);
- }
-
- }
-
- [Test]
- public void HasProperty()
- {
- var doc = GetNode(1173);
-
- var hasProp = doc.HasProperty(Constants.Conventions.Content.UrlAlias);
-
- Assert.AreEqual(true, (bool)hasProp);
-
- }
-
-
- [Test]
- public void HasValue()
- {
- var doc = GetNode(1173);
-
- var hasValue = doc.HasValue(Constants.Conventions.Content.UrlAlias);
- var noValue = doc.HasValue("blahblahblah");
-
- Assert.IsTrue(hasValue);
- Assert.IsFalse(noValue);
- }
-
-
- [Test]
- public void Ancestors_Where_Visible()
- {
- var doc = GetNode(1174);
-
- var whereVisible = doc.Ancestors().Where("Visible");
-
- Assert.AreEqual(1, whereVisible.Count());
-
- }
-
- [Test]
- public void Visible()
- {
- var hidden = GetNode(1046);
- var visible = GetNode(1173);
-
- Assert.IsFalse(hidden.IsVisible());
- Assert.IsTrue(visible.IsVisible());
- }
-
-
- [Test]
- public void Ancestor_Or_Self()
- {
- var doc = GetNode(1173);
-
- var result = doc.AncestorOrSelf();
-
- Assert.IsNotNull(result);
-
- Assert.AreEqual((int)1046, (int)result.Id);
- }
-
- [Test]
- public void Ancestors_Or_Self()
- {
- var doc = GetNode(1174);
-
- var result = doc.AncestorsOrSelf();
-
- Assert.IsNotNull(result);
-
- Assert.AreEqual(3, result.Count());
- Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1174, 1173, 1046 }));
- }
-
- [Test]
- public void Ancestors()
- {
- var doc = GetNode(1174);
-
- var result = doc.Ancestors();
-
- Assert.IsNotNull(result);
-
- Assert.AreEqual(2, result.Count());
- Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1046 }));
- }
-
- [Test]
- public void Descendants_Or_Self()
- {
- var doc = GetNode(1046);
-
- var result = doc.DescendantsOrSelf();
-
- Assert.IsNotNull(result);
-
- Assert.AreEqual(8, result.Count());
- Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1046, 1173, 1174, 1176, 1175 }));
- }
-
- [Test]
- public void Descendants()
- {
- var doc = GetNode(1046);
-
- var result = doc.Descendants();
-
- Assert.IsNotNull(result);
-
- Assert.AreEqual(7, result.Count());
- Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1174, 1176, 1175, 4444 }));
- }
-
- [Test]
- public void Up()
- {
- var doc = GetNode(1173);
-
- var result = doc.Up();
-
- Assert.IsNotNull(result);
-
- Assert.AreEqual((int)1046, (int)result.Id);
- }
-
- [Test]
- public void Down()
- {
- var doc = GetNode(1173);
-
- var result = doc.Down();
-
- Assert.IsNotNull(result);
-
- Assert.AreEqual((int)1174, (int)result.Id);
- }
-
- [Test]
- public void Next()
- {
- var doc = GetNode(1173);
-
- var result = doc.Next();
-
- Assert.IsNotNull(result);
-
- Assert.AreEqual((int)1175, (int)result.Id);
- }
-
- [Test]
- public void Next_Without_Sibling()
- {
- var doc = GetNode(1176);
-
- Assert.IsNull(doc.Next());
- }
-
- [Test]
- public void Previous_Without_Sibling()
- {
- var doc = GetNode(1173);
-
- Assert.IsNull(doc.Previous());
- }
-
- [Test]
- public void Previous()
- {
- var doc = GetNode(1176);
-
- var result = doc.Previous();
-
- Assert.IsNotNull(result);
-
- Assert.AreEqual((int)1178, (int)result.Id);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedContent/PublishedMediaTests.cs
deleted file mode 100644
index 36cba1ad3e..0000000000
--- a/src/Umbraco.Tests/PublishedCache/PublishedContent/PublishedMediaTests.cs
+++ /dev/null
@@ -1,387 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Text.RegularExpressions;
-using System.Xml.Linq;
-using System.Xml.XPath;
-using Examine;
-using Examine.LuceneEngine;
-using Examine.LuceneEngine.Providers;
-using Lucene.Net.Analysis.Standard;
-using Lucene.Net.Store;
-using NUnit.Framework;
-using Umbraco.Core;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Models;
-using Umbraco.Core.PropertyEditors;
-using Umbraco.Tests.TestHelpers;
-using Umbraco.Tests.UmbracoExamine;
-using Umbraco.Web;
-using Umbraco.Web.PublishedCache;
-using Umbraco.Web.PublishedCache.XmlPublishedCache;
-using UmbracoExamine;
-using UmbracoExamine.DataServices;
-using umbraco.BusinessLogic;
-using System.Linq;
-
-namespace Umbraco.Tests.PublishedContent
-{
- ///
- /// Tests the typed extension methods on IPublishedContent using the DefaultPublishedMediaStore
- ///
- [TestFixture, RequiresSTA]
- public class PublishedMediaTests : PublishedContentTestBase
- {
-
- public override void Initialize()
- {
- base.Initialize();
- UmbracoExamineSearcher.DisableInitializationCheck = true;
- BaseUmbracoIndexer.DisableInitializationCheck = true;
- UmbracoSettings.ForceSafeAliases = true;
- UmbracoSettings.UmbracoLibraryCacheDuration = 1800;
- UmbracoSettings.ForceSafeAliases = true;
- }
-
- public override void TearDown()
- {
- base.TearDown();
- UmbracoExamineSearcher.DisableInitializationCheck = null;
- BaseUmbracoIndexer.DisableInitializationCheck = null;
- }
-
- ///
- /// Shared with PublishMediaStoreTests
- ///
- ///
- ///
- ///
- internal static IPublishedContent GetNode(int id, UmbracoContext umbracoContext)
- {
- var ctx = umbracoContext;
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(), ctx);
- var doc = cache.GetById(id);
- Assert.IsNotNull(doc);
- return doc;
- }
-
- private IPublishedContent GetNode(int id)
- {
- return GetNode(id, GetUmbracoContext("/test", 1234));
- }
-
- [Test]
- public void Ensure_Children_Sorted_With_Examine()
- {
- using (var luceneDir = new RAMDirectory())
- {
- var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
- indexer.RebuildIndex();
- var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
- var ctx = GetUmbracoContext("/test", 1234);
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(searcher, indexer), ctx);
-
- //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
- var publishedMedia = cache.GetById(1111);
- var rootChildren = publishedMedia.Children().ToArray();
- var currSort = 0;
- for (var i = 0; i < rootChildren.Count(); i++)
- {
- Assert.GreaterOrEqual(rootChildren[i].SortOrder, currSort);
- currSort = rootChildren[i].SortOrder;
- }
- }
-
-
-
-
-
- }
-
-
- [Test]
- public void Do_Not_Find_In_Recycle_Bin()
- {
- using (var luceneDir = new RAMDirectory())
- {
- var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
- indexer.RebuildIndex();
- var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
- var ctx = GetUmbracoContext("/test", 1234);
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(searcher, indexer), ctx);
-
- //ensure it is found
- var publishedMedia = cache.GetById(3113);
- Assert.IsNotNull(publishedMedia);
-
- //move item to recycle bin
- var newXml = XElement.Parse(@"
-
- 115
- 268
- 10726
- jpg
- ");
- indexer.ReIndexNode(newXml, "media");
-
- //ensure it still exists in the index (raw examine search)
- var criteria = searcher.CreateSearchCriteria();
- var filter = criteria.Id(3113);
- var found = searcher.Search(filter.Compile());
- Assert.IsNotNull(found);
- Assert.AreEqual(1, found.TotalItemCount);
-
- //ensure it does not show up in the published media store
- var recycledMedia = cache.GetById(3113);
- Assert.IsNull(recycledMedia);
-
- }
-
- }
-
- [Test]
- public void Children_With_Examine()
- {
- using (var luceneDir = new RAMDirectory())
- {
- var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
- indexer.RebuildIndex();
- var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
- var ctx = GetUmbracoContext("/test", 1234);
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(searcher, indexer), ctx);
-
- //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
- var publishedMedia = cache.GetById(1111);
- var rootChildren = publishedMedia.Children();
- Assert.IsTrue(rootChildren.Select(x => x.Id).ContainsAll(new[] { 2222, 1113, 1114, 1115, 1116 }));
-
- var publishedChild1 = cache.GetById(2222);
- var subChildren = publishedChild1.Children();
- Assert.IsTrue(subChildren.Select(x => x.Id).ContainsAll(new[] { 2112 }));
- }
- }
-
- [Test]
- public void Descendants_With_Examine()
- {
- using (var luceneDir = new RAMDirectory())
- {
- var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
- indexer.RebuildIndex();
- var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
- var ctx = GetUmbracoContext("/test", 1234);
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(searcher, indexer), ctx);
-
- //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
- var publishedMedia = cache.GetById(1111);
- var rootDescendants = publishedMedia.Descendants();
- Assert.IsTrue(rootDescendants.Select(x => x.Id).ContainsAll(new[] { 2112, 2222, 1113, 1114, 1115, 1116 }));
-
- var publishedChild1 = cache.GetById(2222);
- var subDescendants = publishedChild1.Descendants();
- Assert.IsTrue(subDescendants.Select(x => x.Id).ContainsAll(new[] { 2112, 3113 }));
- }
- }
-
- [Test]
- public void DescendantsOrSelf_With_Examine()
- {
- using (var luceneDir = new RAMDirectory())
- {
- var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
- indexer.RebuildIndex();
- var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
- var ctx = GetUmbracoContext("/test", 1234);
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(searcher, indexer), ctx);
-
- //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
- var publishedMedia = cache.GetById(1111);
- var rootDescendants = publishedMedia.DescendantsOrSelf();
- Assert.IsTrue(rootDescendants.Select(x => x.Id).ContainsAll(new[] { 1111, 2112, 2222, 1113, 1114, 1115, 1116 }));
-
- var publishedChild1 = cache.GetById(2222);
- var subDescendants = publishedChild1.DescendantsOrSelf();
- Assert.IsTrue(subDescendants.Select(x => x.Id).ContainsAll(new[] { 2222, 2112, 3113 }));
- }
- }
-
- [Test]
- public void Ancestors_With_Examine()
- {
- using (var luceneDir = new RAMDirectory())
- {
- var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
- indexer.RebuildIndex();
- var ctx = GetUmbracoContext("/test", 1234);
- var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(searcher, indexer), ctx);
-
- //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
- var publishedMedia = cache.GetById(3113);
- var ancestors = publishedMedia.Ancestors();
- Assert.IsTrue(ancestors.Select(x => x.Id).ContainsAll(new[] { 2112, 2222, 1111 }));
- }
-
- }
-
- [Test]
- public void AncestorsOrSelf_With_Examine()
- {
- using (var luceneDir = new RAMDirectory())
- {
- var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir);
- indexer.RebuildIndex();
- var ctx = GetUmbracoContext("/test", 1234);
- var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir);
- var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(searcher, indexer), ctx);
-
- //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace
- var publishedMedia = cache.GetById(3113);
- var ancestors = publishedMedia.AncestorsOrSelf();
- Assert.IsTrue(ancestors.Select(x => x.Id).ContainsAll(new[] { 3113, 2112, 2222, 1111 }));
- }
- }
-
- [Test]
- public void Children_Without_Examine()
- {
- var user = new User(0);
- var mType = global::umbraco.cms.businesslogic.media.MediaType.MakeNew(user, "TestMediaType");
- var mRoot = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot", mType, user, -1);
-
- var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot.Id);
- var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot.Id);
- var mChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child3", mType, user, mRoot.Id);
-
- var mSubChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild1", mType, user, mChild1.Id);
- var mSubChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild2", mType, user, mChild1.Id);
- var mSubChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild3", mType, user, mChild1.Id);
-
- var publishedMedia = GetNode(mRoot.Id);
- var rootChildren = publishedMedia.Children();
- Assert.IsTrue(rootChildren.Select(x => x.Id).ContainsAll(new[] { mChild1.Id, mChild2.Id, mChild3.Id }));
-
- var publishedChild1 = GetNode(mChild1.Id);
- var subChildren = publishedChild1.Children();
- Assert.IsTrue(subChildren.Select(x => x.Id).ContainsAll(new[] { mSubChild1.Id, mSubChild2.Id, mSubChild3.Id }));
- }
-
- [Test]
- public void Descendants_Without_Examine()
- {
- var user = new User(0);
- var mType = global::umbraco.cms.businesslogic.media.MediaType.MakeNew(user, "TestMediaType");
- var mRoot = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot", mType, user, -1);
-
- var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot.Id);
- var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot.Id);
- var mChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child3", mType, user, mRoot.Id);
-
- var mSubChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild1", mType, user, mChild1.Id);
- var mSubChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild2", mType, user, mChild1.Id);
- var mSubChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild3", mType, user, mChild1.Id);
-
- var publishedMedia = GetNode(mRoot.Id);
- var rootDescendants = publishedMedia.Descendants();
- Assert.IsTrue(rootDescendants.Select(x => x.Id).ContainsAll(new[] { mChild1.Id, mChild2.Id, mChild3.Id, mSubChild1.Id, mSubChild2.Id, mSubChild3.Id }));
-
- var publishedChild1 = GetNode(mChild1.Id);
- var subDescendants = publishedChild1.Descendants();
- Assert.IsTrue(subDescendants.Select(x => x.Id).ContainsAll(new[] { mSubChild1.Id, mSubChild2.Id, mSubChild3.Id }));
- }
-
- [Test]
- public void DescendantsOrSelf_Without_Examine()
- {
- var user = new User(0);
- var mType = global::umbraco.cms.businesslogic.media.MediaType.MakeNew(user, "TestMediaType");
- var mRoot = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot", mType, user, -1);
-
- var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot.Id);
- var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot.Id);
- var mChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child3", mType, user, mRoot.Id);
-
- var mSubChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild1", mType, user, mChild1.Id);
- var mSubChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild2", mType, user, mChild1.Id);
- var mSubChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild3", mType, user, mChild1.Id);
-
- var publishedMedia = GetNode(mRoot.Id);
- var rootDescendantsOrSelf = publishedMedia.DescendantsOrSelf();
- Assert.IsTrue(rootDescendantsOrSelf.Select(x => x.Id).ContainsAll(
- new[] { mRoot.Id, mChild1.Id, mChild2.Id, mChild3.Id, mSubChild1.Id, mSubChild2.Id, mSubChild3.Id }));
-
- var publishedChild1 = GetNode(mChild1.Id);
- var subDescendantsOrSelf = publishedChild1.DescendantsOrSelf();
- Assert.IsTrue(subDescendantsOrSelf.Select(x => x.Id).ContainsAll(
- new[] { mChild1.Id, mSubChild1.Id, mSubChild2.Id, mSubChild3.Id }));
- }
-
- [Test]
- public void Parent_Without_Examine()
- {
- var user = new User(0);
- var mType = global::umbraco.cms.businesslogic.media.MediaType.MakeNew(user, "TestMediaType");
- var mRoot = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot", mType, user, -1);
-
- var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot.Id);
- var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot.Id);
- var mChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child3", mType, user, mRoot.Id);
-
- var mSubChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild1", mType, user, mChild1.Id);
- var mSubChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild2", mType, user, mChild1.Id);
- var mSubChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild3", mType, user, mChild1.Id);
-
- var publishedRoot = GetNode(mRoot.Id);
- Assert.AreEqual(null, publishedRoot.Parent);
-
- var publishedChild1 = GetNode(mChild1.Id);
- Assert.AreEqual(mRoot.Id, publishedChild1.Parent.Id);
-
- var publishedSubChild1 = GetNode(mSubChild1.Id);
- Assert.AreEqual(mChild1.Id, publishedSubChild1.Parent.Id);
- }
-
-
- [Test]
- public void Ancestors_Without_Examine()
- {
- var user = new User(0);
- var mType = global::umbraco.cms.businesslogic.media.MediaType.MakeNew(user, "TestMediaType");
- var mRoot = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot", mType, user, -1);
-
- var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot.Id);
- var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot.Id);
- var mChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child3", mType, user, mRoot.Id);
-
- var mSubChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild1", mType, user, mChild1.Id);
- var mSubChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild2", mType, user, mChild1.Id);
- var mSubChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild3", mType, user, mChild1.Id);
-
- var publishedSubChild1 = GetNode(mSubChild1.Id);
- Assert.IsTrue(publishedSubChild1.Ancestors().Select(x => x.Id).ContainsAll(new[] { mChild1.Id, mRoot.Id }));
- }
-
- [Test]
- public void AncestorsOrSelf_Without_Examine()
- {
- var user = new User(0);
- var mType = global::umbraco.cms.businesslogic.media.MediaType.MakeNew(user, "TestMediaType");
- var mRoot = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot", mType, user, -1);
-
- var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot.Id);
- var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot.Id);
- var mChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child3", mType, user, mRoot.Id);
-
- var mSubChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild1", mType, user, mChild1.Id);
- var mSubChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild2", mType, user, mChild1.Id);
- var mSubChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild3", mType, user, mChild1.Id);
-
- var publishedSubChild1 = GetNode(mSubChild1.Id);
- Assert.IsTrue(publishedSubChild1.AncestorsOrSelf().Select(x => x.Id).ContainsAll(
- new[] { mSubChild1.Id, mChild1.Id, mRoot.Id }));
- }
- }
-
-
-}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContent/StronglyTypedQueryTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedContent/StronglyTypedQueryTests.cs
deleted file mode 100644
index e437829956..0000000000
--- a/src/Umbraco.Tests/PublishedCache/PublishedContent/StronglyTypedQueryTests.cs
+++ /dev/null
@@ -1,432 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using NUnit.Framework;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Models;
-using Umbraco.Core.PropertyEditors;
-using Umbraco.Tests.TestHelpers;
-using Umbraco.Web;
-using Umbraco.Web.Models;
-using Umbraco.Web.PublishedCache;
-using Umbraco.Web.PublishedCache.XmlPublishedCache;
-
-namespace Umbraco.Tests.PublishedContent
-{
- [TestFixture]
- public class StronglyTypedQueryTests : PublishedContentTestBase
- {
- public override void Initialize()
- {
- base.Initialize();
- }
-
- public override void TearDown()
- {
- base.TearDown();
- }
-
- protected override DatabaseBehavior DatabaseTestBehavior
- {
- get { return DatabaseBehavior.NoDatabasePerFixture; }
- }
-
- protected override string GetXmlContent(int templateId)
- {
- return @"
-
-
-
-
-
-
-
-
-]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ";
- }
-
- internal IPublishedContent GetNode(int id)
- {
- var ctx = UmbracoContext.Current;
- var doc = ctx.ContentCache.GetById(id);
- Assert.IsNotNull(doc);
- return doc;
- }
-
-
- [Test]
- public void Type_Test()
- {
- var doc = GetNode(1);
- var result = doc.NewsArticles(TraversalType.Descendants).ToArray();
- Assert.AreEqual("John doe", result[0].ArticleAuthor);
- Assert.AreEqual("John Smith", result[1].ArticleAuthor);
- }
-
-
- [Test]
- public void As_Test()
- {
- var doc = GetNode(1);
- var result = doc.AsHome();
- Assert.AreEqual("Test site", result.SiteName);
-
- Assert.Throws(() => doc.AsContentPage());
- }
-
- }
-
- //NOTE: Some of these class will be moved in to the core once all this is working the way we want
-
- #region Gen classes & supporting classes
-
- //TOOD: SD: This class could be the way that the UmbracoHelper deals with looking things up in the background, we might not
- // even expose it publicly but it could handle any caching (per request) that might be required when looking up any objects...
- // though we might not need it at all, not sure yet.
- // However, what we need to do is implement the GetDocumentsByType method of the IPublishedStore, see the TODO there.
- // It might be nicer to have a QueryContext on the UmbracoHelper (we can still keep the Content and TypedContent, etc...
- // methods, but these would just wrap the QueryContext attached to it. Other methods on the QueryContext will be
- // ContentByType, TypedContentByType, etc... then we can also have extension methods like below for strongly typed
- // access like: GetAllHomes, GetAllNewsArticles, etc...
-
- //public class QueryDataContext
- //{
- // private readonly IPublishedContentStore _contentStore;
- // private readonly UmbracoContext _umbracoContext;
-
- // internal QueryDataContext(IPublishedContentStore contentStore, UmbracoContext umbracoContext)
- // {
- // _contentStore = contentStore;
- // _umbracoContext = umbracoContext;
- // }
-
- // public IPublishedContent GetDocumentById(int id)
- // {
- // return _contentStore.GetDocumentById(_umbracoContext, id);
- // }
-
- // public IEnumerable GetByDocumentType(string alias)
- // {
-
- // }
- //}
-
- public enum TraversalType
- {
- Children,
- Ancestors,
- AncestorsOrSelf,
- Descendants,
- DescendantsOrSelf
- }
-
- public static class StronglyTypedQueryExtensions
- {
- private static IEnumerable GetEnumerable(this IPublishedContent content, string docTypeAlias, TraversalType traversalType = TraversalType.Children)
- {
- switch (traversalType)
- {
- case TraversalType.Children:
- return content.Children.Where(x => x.DocumentTypeAlias == docTypeAlias);
- case TraversalType.Ancestors:
- return content.Ancestors().Where(x => x.DocumentTypeAlias == docTypeAlias);
- case TraversalType.AncestorsOrSelf:
- return content.AncestorsOrSelf().Where(x => x.DocumentTypeAlias == docTypeAlias);
- case TraversalType.Descendants:
- return content.Descendants().Where(x => x.DocumentTypeAlias == docTypeAlias);
- case TraversalType.DescendantsOrSelf:
- return content.DescendantsOrSelf().Where(x => x.DocumentTypeAlias == docTypeAlias);
- default:
- throw new ArgumentOutOfRangeException("traversalType");
- }
- }
-
- private static T AsDocumentType(this IPublishedContent content, string alias, Func creator)
- {
- if (content.DocumentTypeAlias == alias) return creator(content);
- throw new InvalidOperationException("The content type cannot be cast to " + typeof(T).FullName + " since it is type: " + content.DocumentTypeAlias);
- }
-
- public static HomeContentItem AsHome(this IPublishedContent content)
- {
- return content.AsDocumentType("Home", x => new HomeContentItem(x));
- }
-
- public static IEnumerable Homes(this IPublishedContent content, TraversalType traversalType = TraversalType.Children)
- {
- return content.GetEnumerable("Home", traversalType).Select(x => new HomeContentItem(x));
- }
-
- public static NewsArticleContentItem AsNewsArticle(this IPublishedContent content)
- {
- return content.AsDocumentType("NewsArticle", x => new NewsArticleContentItem(x));
- }
-
- public static IEnumerable NewsArticles(this IPublishedContent content, TraversalType traversalType = TraversalType.Children)
- {
- return content.GetEnumerable("NewsArticle", traversalType).Select(x => new NewsArticleContentItem(x));
- }
-
- public static NewsLandingPageContentItem AsNewsLandingPage(this IPublishedContent content)
- {
- return content.AsDocumentType("NewsLandingPage", x => new NewsLandingPageContentItem(x));
- }
-
- public static IEnumerable NewsLandingPages(this IPublishedContent content, TraversalType traversalType = TraversalType.Children)
- {
- return content.GetEnumerable("NewsLandingPage", traversalType).Select(x => new NewsLandingPageContentItem(x));
- }
-
- public static ContentPageContentItem AsContentPage(this IPublishedContent content)
- {
- return content.AsDocumentType("ContentPage", x => new ContentPageContentItem(x));
- }
-
- public static IEnumerable ContentPages(this IPublishedContent content, TraversalType traversalType = TraversalType.Children)
- {
- return content.GetEnumerable("ContentPage", traversalType).Select(x => new ContentPageContentItem(x));
- }
- }
-
- public class PublishedContentWrapper : IPublishedContent, IOwnerCollectionAware
- {
- protected IPublishedContent WrappedContent { get; private set; }
-
- public PublishedContentWrapper(IPublishedContent content)
- {
- WrappedContent = content;
- }
-
- public string Url
- {
- get { return WrappedContent.Url; }
- }
-
- public PublishedItemType ItemType
- {
- get { return WrappedContent.ItemType; }
- }
-
- public IPublishedContent Parent
- {
- get { return WrappedContent.Parent; }
- }
-
- public int Id
- {
- get { return WrappedContent.Id; }
- }
- public int TemplateId
- {
- get { return WrappedContent.TemplateId; }
- }
- public int SortOrder
- {
- get { return WrappedContent.SortOrder; }
- }
- public string Name
- {
- get { return WrappedContent.Name; }
- }
- public string UrlName
- {
- get { return WrappedContent.UrlName; }
- }
- public string DocumentTypeAlias
- {
- get { return WrappedContent.DocumentTypeAlias; }
- }
- public int DocumentTypeId
- {
- get { return WrappedContent.DocumentTypeId; }
- }
- public string WriterName
- {
- get { return WrappedContent.WriterName; }
- }
- public string CreatorName
- {
- get { return WrappedContent.CreatorName; }
- }
- public int WriterId
- {
- get { return WrappedContent.WriterId; }
- }
- public int CreatorId
- {
- get { return WrappedContent.CreatorId; }
- }
- public string Path
- {
- get { return WrappedContent.Path; }
- }
- public DateTime CreateDate
- {
- get { return WrappedContent.CreateDate; }
- }
- public DateTime UpdateDate
- {
- get { return WrappedContent.UpdateDate; }
- }
- public Guid Version
- {
- get { return WrappedContent.Version; }
- }
- public int Level
- {
- get { return WrappedContent.Level; }
- }
- public ICollection Properties
- {
- get { return WrappedContent.Properties; }
- }
-
- public object this[string propertyAlias]
- {
- get { return GetProperty(propertyAlias).Value; }
- }
-
- public IEnumerable Children
- {
- get { return WrappedContent.Children; }
- }
- public IPublishedContentProperty GetProperty(string alias)
- {
- return WrappedContent.GetProperty(alias);
- }
-
- private IEnumerable _ownersCollection;
-
- ///
- /// Need to get/set the owner collection when an item is returned from the result set of a query
- ///
- ///
- /// Based on this issue here: http://issues.umbraco.org/issue/U4-1797
- ///
- IEnumerable IOwnerCollectionAware.OwnersCollection
- {
- get
- {
- var publishedContentBase = WrappedContent as IOwnerCollectionAware;
- if (publishedContentBase != null)
- {
- return publishedContentBase.OwnersCollection;
- }
-
- //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
- {
- var publishedContentBase = WrappedContent as IOwnerCollectionAware;
- if (publishedContentBase != null)
- {
- publishedContentBase.OwnersCollection = value;
- }
- else
- {
- _ownersCollection = value;
- }
- }
- }
- }
-
- public partial class HomeContentItem : ContentPageContentItem
- {
- public HomeContentItem(IPublishedContent content)
- : base(content)
- {
- }
-
- public string SiteName
- {
- get { return WrappedContent.GetPropertyValue("siteName"); }
- }
- public string SiteDescription
- {
- get { return WrappedContent.GetPropertyValue("siteDescription"); }
- }
- }
-
- public partial class NewsLandingPageContentItem : ContentPageContentItem
- {
- public NewsLandingPageContentItem(IPublishedContent content)
- : base(content)
- {
- }
-
- public string PageTitle
- {
- get { return WrappedContent.GetPropertyValue("pageTitle"); }
- }
- }
-
- public partial class NewsArticleContentItem : PublishedContentWrapper
- {
- public NewsArticleContentItem(IPublishedContent content)
- : base(content)
- {
- }
-
- public string ArticleContent
- {
- get { return WrappedContent.GetPropertyValue("articleContent"); }
- }
- public DateTime ArticleDate
- {
- get { return WrappedContent.GetPropertyValue("articleDate"); }
- }
- public string ArticleAuthor
- {
- get { return WrappedContent.GetPropertyValue("articleAuthor"); }
- }
- }
-
- public partial class ContentPageContentItem : PublishedContentWrapper
- {
- public ContentPageContentItem(IPublishedContent content)
- : base(content)
- {
- }
-
- public string BodyContent
- {
- get { return WrappedContent.GetPropertyValue("bodyContent"); }
- }
- }
-
- #endregion
-}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs
index d1abe13d54..f8658734cc 100644
--- a/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs
+++ b/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs
@@ -4,6 +4,9 @@ using System.Xml;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Configuration;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.ObjectResolution;
+using Umbraco.Core.PropertyEditors;
using Umbraco.Tests.PublishedContent;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
@@ -15,11 +18,12 @@ using umbraco.BusinessLogic;
namespace Umbraco.Tests.PublishedCache
{
[TestFixture]
- public class PublishContentCacheTests
+ public class PublishContentCacheTests : BaseWebTest
{
private FakeHttpContextFactory _httpContextFactory;
private UmbracoContext _umbracoContext;
private ContextualPublishedContentCache _cache;
+ private XmlDocument _xml;
private string GetLegacyXml()
{
@@ -67,26 +71,20 @@ namespace Umbraco.Tests.PublishedCache
}
[SetUp]
- public void SetUp()
- {
- TestHelper.SetupLog4NetForTests();
+ public override void Initialize()
+ {
+ base.Initialize();
- //create the app context
- ApplicationContext.Current = new ApplicationContext(false);
-
- _httpContextFactory = new FakeHttpContextFactory("~/Home");
- //ensure the StateHelper is using our custom context
- StateHelper.HttpContext = _httpContextFactory.HttpContext;
+ _httpContextFactory = new FakeHttpContextFactory("~/Home");
+ //ensure the StateHelper is using our custom context
+ StateHelper.HttpContext = _httpContextFactory.HttpContext;
UmbracoSettings.UseLegacyXmlSchema = false;
+ _xml = new XmlDocument();
+ _xml.LoadXml(GetXml());
var cache = new PublishedContentCache
{
- GetXmlDelegate = (context, preview) =>
- {
- var doc = new XmlDocument();
- doc.LoadXml(GetXml());
- return doc;
- }
+ GetXmlDelegate = (context, preview) => _xml
};
_umbracoContext = new UmbracoContext(
@@ -95,30 +93,22 @@ namespace Umbraco.Tests.PublishedCache
new PublishedCaches(cache, new PublishedMediaCache()));
_cache = _umbracoContext.ContentCache;
- }
+ }
- private void SetupForLegacy()
+ private void SetupForLegacy()
{
- Umbraco.Core.Configuration.UmbracoSettings.UseLegacyXmlSchema = true;
-
- var cache = _umbracoContext.ContentCache.InnerCache as PublishedContentCache;
- if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported.");
-
- cache.GetXmlDelegate = (context, preview) =>
- {
- var doc = new XmlDocument();
- doc.LoadXml(GetLegacyXml());
- return doc;
- };
+ UmbracoSettings.UseLegacyXmlSchema = true;
+ _xml = new XmlDocument();
+ _xml.LoadXml(GetLegacyXml());
}
- [TearDown]
- public void TearDown()
- {
- UmbracoSettings.Reset();
- }
+ protected override void FreezeResolution()
+ {
+ PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver();
+ base.FreezeResolution();
+ }
- [Test]
+ [Test]
public void Has_Content_LegacySchema()
{
SetupForLegacy();
diff --git a/src/Umbraco.Tests/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedMediaCacheTests.cs
index a1d922e86d..b2ac31c91a 100644
--- a/src/Umbraco.Tests/PublishedCache/PublishedMediaCacheTests.cs
+++ b/src/Umbraco.Tests/PublishedCache/PublishedMediaCacheTests.cs
@@ -6,6 +6,7 @@ using Examine;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
using Umbraco.Tests.PublishedContent;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
@@ -16,22 +17,15 @@ using umbraco.BusinessLogic;
namespace Umbraco.Tests.PublishedCache
{
[TestFixture]
- public class PublishMediaCacheTests : PublishedContentTestBase
+ public class PublishMediaCacheTests : BaseWebTest
{
- public override void Initialize()
- {
- base.Initialize();
- }
-
-
-
-
- public override void TearDown()
- {
- base.TearDown();
- }
-
- [Test]
+ protected override void FreezeResolution()
+ {
+ PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver();
+ base.FreezeResolution();
+ }
+
+ [Test]
public void Get_Root_Docs()
{
var user = new User(0);
diff --git a/src/Umbraco.Tests/PublishedContent/DynamicDocumentTestsBase.cs b/src/Umbraco.Tests/PublishedContent/DynamicDocumentTestsBase.cs
index e0321e08d5..2b8f6dd958 100644
--- a/src/Umbraco.Tests/PublishedContent/DynamicDocumentTestsBase.cs
+++ b/src/Umbraco.Tests/PublishedContent/DynamicDocumentTestsBase.cs
@@ -6,6 +6,8 @@ using System.Web;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Dynamics;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.PropertyEditors;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.PublishedContent
@@ -18,7 +20,36 @@ namespace Umbraco.Tests.PublishedContent
get { return DatabaseBehavior.NoDatabasePerFixture; }
}
- protected override string GetXmlContent(int templateId)
+ public override void Initialize()
+ {
+ // required so we can access property.Value
+ //PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver();
+
+ base.Initialize();
+
+ // need to specify a custom callback for unit tests
+ // AutoPublishedContentTypes generates properties automatically
+ // when they are requested, but we must declare those that we
+ // explicitely want to be here...
+
+ var propertyTypes = new[]
+ {
+ // AutoPublishedContentType will auto-generate other properties
+ new PublishedPropertyType("umbracoNaviHide", Guid.Empty, 0, 0),
+ new PublishedPropertyType("selectedNodes", Guid.Empty, 0, 0),
+ new PublishedPropertyType("umbracoUrlAlias", Guid.Empty, 0, 0),
+ new PublishedPropertyType("content", Guid.Parse(Constants.PropertyEditors.TinyMCEv3), 0, 0),
+ new PublishedPropertyType("testRecursive", Guid.Empty, 0, 0),
+ new PublishedPropertyType("siteTitle", Guid.Empty, 0, 0),
+ new PublishedPropertyType("creatorName", Guid.Empty, 0, 0),
+ new PublishedPropertyType("blah", Guid.Empty, 0, 0), // ugly error when that one is missing...
+ };
+ var type = new AutoPublishedContentType(0, "anything", propertyTypes);
+ PublishedContentType.GetPublishedContentTypeCallback = (alias) => type;
+
+ }
+
+ protected override string GetXmlContent(int templateId)
{
return @"
+
+ // need to specify a custom callback for unit tests
+ // AutoPublishedContentTypes generates properties automatically
+ var type = new AutoPublishedContentType(0, "anything", new PublishedPropertyType[] {});
+ PublishedContentType.GetPublishedContentTypeCallback = (alias) => type;
+
+ // need to specify a different callback for testing
+ PublishedContentExtensions.GetPropertyAliasesAndNames = s =>
{
var userFields = new Dictionary()
{
@@ -118,12 +125,13 @@ namespace Umbraco.Tests.PublishedContent
private IPublishedContent GetContent(bool createChildren, int indexVals)
{
+ var contentTypeAlias = createChildren ? "Parent" : "Child";
var d = new TestPublishedContent
{
CreateDate = DateTime.Now,
CreatorId = 1,
CreatorName = "Shannon",
- DocumentTypeAlias = createChildren? "Parent" : "Child",
+ DocumentTypeAlias = contentTypeAlias,
DocumentTypeId = 2,
Id = 3,
SortOrder = 4,
@@ -137,8 +145,8 @@ namespace Umbraco.Tests.PublishedContent
WriterName = "Shannon",
Parent = null,
Level = 1,
- Properties = new Collection(
- new List()
+ Properties = new Collection(
+ new List()
{
new PropertyResult("property1", "value" + indexVals, Guid.NewGuid(), PropertyResultType.UserProperty),
new PropertyResult("property2", "value" + (indexVals + 1), Guid.NewGuid(), PropertyResultType.UserProperty)
@@ -166,50 +174,80 @@ namespace Umbraco.Tests.PublishedContent
return d;
}
+ // fixme - why can't we just use SolidPublishedContent here?
+ private class TestPublishedContent : IPublishedContent
+ {
+ public string Url { get; set; }
+ public PublishedItemType ItemType { get; set; }
- private class TestPublishedContent : IPublishedContent
- {
- public string Url { get; set; }
- public PublishedItemType ItemType { get; set; }
+ IPublishedContent IPublishedContent.Parent
+ {
+ get { return Parent; }
+ }
- IPublishedContent IPublishedContent.Parent
- {
- get { return Parent; }
- }
- IEnumerable IPublishedContent.Children
- {
- get { return Children; }
- }
- public IPublishedContent Parent { get; set; }
- public int Id { get; set; }
- public int TemplateId { get; set; }
- public int SortOrder { get; set; }
- public string Name { get; set; }
- public string UrlName { get; set; }
- public string DocumentTypeAlias { get; set; }
- public int DocumentTypeId { get; set; }
- public string WriterName { get; set; }
- public string CreatorName { get; set; }
- public int WriterId { get; set; }
- public int CreatorId { get; set; }
- public string Path { get; set; }
- public DateTime CreateDate { get; set; }
- public DateTime UpdateDate { get; set; }
- public Guid Version { get; set; }
- public int Level { get; set; }
- public ICollection Properties { get; set; }
+ IEnumerable IPublishedContent.Children
+ {
+ get { return Children; }
+ }
- public object this[string propertyAlias]
- {
- get { return GetProperty(propertyAlias).Value; }
- }
+ public IPublishedContent Parent { get; set; }
+ public int Id { get; set; }
+ public int TemplateId { get; set; }
+ public int SortOrder { get; set; }
+ public string Name { get; set; }
+ public string UrlName { get; set; }
+ public string DocumentTypeAlias { get; set; }
+ public int DocumentTypeId { get; set; }
+ public string WriterName { get; set; }
+ public string CreatorName { get; set; }
+ public int WriterId { get; set; }
+ public int CreatorId { get; set; }
+ public string Path { get; set; }
+ public DateTime CreateDate { get; set; }
+ public DateTime UpdateDate { get; set; }
+ public Guid Version { get; set; }
+ public int Level { get; set; }
+ public bool IsDraft { get; set; }
+ public int GetIndex() { throw new NotImplementedException();}
+
+ public ICollection Properties { get; set; }
- public IEnumerable Children { get; set; }
- public IPublishedContentProperty GetProperty(string alias)
- {
- return Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
- }
- }
+ public object this[string propertyAlias]
+ {
+ get { return GetProperty(propertyAlias).RawValue; } // fixme - why not just .Value?
+ }
+ public IEnumerable Children { get; set; }
+
+ public IPublishedProperty GetProperty(string alias)
+ {
+ return Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
+ }
+
+ public IPublishedProperty GetProperty(string alias, bool recurse)
+ {
+ var property = GetProperty(alias);
+ if (recurse == false) return property;
+
+ IPublishedContent content = this;
+ while (content != null && (property == null || property.HasValue == false))
+ {
+ content = content.Parent;
+ property = content == null ? null : content.GetProperty(alias);
+ }
+
+ return property;
+ }
+
+ public IEnumerable ContentSet
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public PublishedContentType ContentType
+ {
+ get { throw new NotImplementedException(); }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs
new file mode 100644
index 0000000000..4477410864
--- /dev/null
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs
@@ -0,0 +1,227 @@
+using System.Linq;
+using System.Collections.ObjectModel;
+using Lucene.Net.Documents;
+using NUnit.Framework;
+using Umbraco.Core;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.ObjectResolution;
+using Umbraco.Core.PropertyEditors;
+using Umbraco.Web;
+using Umbraco.Tests.TestHelpers;
+using umbraco.BusinessLogic;
+
+namespace Umbraco.Tests.PublishedContent
+{
+ [TestFixture]
+ public class PublishedContentMoreTests
+ {
+ // read http://stackoverflow.com/questions/7713326/extension-method-that-works-on-ienumerablet-and-iqueryablet
+ // and http://msmvps.com/blogs/jon_skeet/archive/2010/10/28/overloading-and-generic-constraints.aspx
+ // and http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx
+
+ private PluginManager _pluginManager;
+
+ [SetUp]
+ public void Setup()
+ {
+ // this is so the model factory looks into the test assembly
+ _pluginManager = PluginManager.Current;
+ PluginManager.Current = new PluginManager(false)
+ {
+ AssembliesToScan = _pluginManager.AssembliesToScan
+ .Union(new[] { typeof (PublishedContentMoreTests).Assembly})
+ };
+
+ PropertyValueConvertersResolver.Current =
+ new PropertyValueConvertersResolver();
+ PublishedContentModelFactoryResolver.Current =
+ new PublishedContentModelFactoryResolver();
+ Resolution.Freeze();
+
+ var caches = CreatePublishedContent();
+
+ ApplicationContext.Current = new ApplicationContext(false) { IsReady = true };
+ var factory = new FakeHttpContextFactory("http://umbraco.local/");
+ StateHelper.HttpContext = factory.HttpContext;
+ var context = new UmbracoContext(
+ factory.HttpContext,
+ ApplicationContext.Current,
+ caches);
+ UmbracoContext.Current = context;
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ PluginManager.Current = _pluginManager;
+ ApplicationContext.Current.DisposeIfDisposable();
+ ApplicationContext.Current = null;
+ }
+
+ [Test]
+ public void First()
+ {
+ var content = UmbracoContext.Current.ContentCache.GetAtRoot().First();
+ Assert.AreEqual("Content 1", content.Name);
+ }
+
+ [Test]
+ public void DefaultContentSetIsSiblings()
+ {
+ var content = UmbracoContext.Current.ContentCache.GetAtRoot().First();
+ Assert.AreEqual(0, content.Index());
+ Assert.IsTrue(content.IsFirst());
+ }
+
+ [Test]
+ public void RunOnLatestContentSet()
+ {
+ // get first content
+ var content = UmbracoContext.Current.ContentCache.GetAtRoot().First();
+ var id = content.Id;
+ Assert.IsTrue(content.IsFirst());
+
+ // reverse => should be last, but set has not changed => still first
+ content = UmbracoContext.Current.ContentCache.GetAtRoot().Reverse().First(x => x.Id == id);
+ Assert.IsTrue(content.IsFirst());
+ Assert.IsFalse(content.IsLast());
+
+ // reverse + new set => now it's last
+ content = UmbracoContext.Current.ContentCache.GetAtRoot().Reverse().ToContentSet().First(x => x.Id == id);
+ Assert.IsFalse(content.IsFirst());
+ Assert.IsTrue(content.IsLast());
+
+ // reverse that set => should be first, but no new set => still last
+ content = UmbracoContext.Current.ContentCache.GetAtRoot().Reverse().ToContentSet().Reverse().First(x => x.Id == id);
+ Assert.IsFalse(content.IsFirst());
+ Assert.IsTrue(content.IsLast());
+ }
+
+ [Test]
+ public void Distinct()
+ {
+ var content = UmbracoContext.Current.ContentCache.GetAtRoot()
+ .Distinct()
+ .Distinct()
+ .ToContentSet()
+ .First();
+
+ Assert.AreEqual("Content 1", content.Name);
+ Assert.IsTrue(content.IsFirst());
+ Assert.IsFalse(content.IsLast());
+
+ content = content.Next();
+ Assert.AreEqual("Content 2", content.Name);
+ Assert.IsFalse(content.IsFirst());
+ Assert.IsFalse(content.IsLast());
+
+ content = content.Next();
+ Assert.AreEqual("Content 2Sub", content.Name);
+ Assert.IsFalse(content.IsFirst());
+ Assert.IsTrue(content.IsLast());
+ }
+
+ [Test]
+ public void Position()
+ {
+ var content = UmbracoContext.Current.ContentCache.GetAtRoot()
+ .Where(x => x.GetPropertyValue("prop1") == 1234)
+ .ToContentSet()
+ .ToArray();
+
+ Assert.IsTrue(content.First().IsFirst());
+ Assert.IsFalse(content.First().IsLast());
+ Assert.IsFalse(content.First().Next().IsFirst());
+ Assert.IsFalse(content.First().Next().IsLast());
+ Assert.IsFalse(content.First().Next().Next().IsFirst());
+ Assert.IsTrue(content.First().Next().Next().IsLast());
+ }
+
+ static SolidPublishedCaches CreatePublishedContent()
+ {
+ var caches = new SolidPublishedCaches();
+ var cache = caches.ContentCache;
+
+ var props = new[]
+ {
+ new PublishedPropertyType("prop1", System.Guid.Empty, 1, 1),
+ };
+
+ var contentType1 = new PublishedContentType(1, "ContentType1", props);
+ var contentType2 = new PublishedContentType(2, "ContentType2", props);
+ var contentType2s = new PublishedContentType(3, "ContentType2Sub", props);
+
+ cache.Add(new SolidPublishedContent(contentType1)
+ {
+ Id = 1,
+ SortOrder = 0,
+ Name = "Content 1",
+ UrlName = "content-1",
+ Path = "/1",
+ Level = 1,
+ Url = "/content-1",
+ ParentId = -1,
+ ChildIds = new int[] {},
+ Properties = new Collection
+ {
+ new SolidPublishedProperty
+ {
+ Alias = "prop1",
+ HasValue = true,
+ Value = 1234,
+ RawValue = "1234"
+ }
+ }
+ });
+
+ cache.Add(new SolidPublishedContent(contentType2)
+ {
+ Id = 2,
+ SortOrder = 1,
+ Name = "Content 2",
+ UrlName = "content-2",
+ Path = "/2",
+ Level = 1,
+ Url = "/content-2",
+ ParentId = -1,
+ ChildIds = new int[] { },
+ Properties = new Collection
+ {
+ new SolidPublishedProperty
+ {
+ Alias = "prop1",
+ HasValue = true,
+ Value = 1234,
+ RawValue = "1234"
+ }
+ }
+ });
+
+ cache.Add(new SolidPublishedContent(contentType2s)
+ {
+ Id = 3,
+ SortOrder = 2,
+ Name = "Content 2Sub",
+ UrlName = "content-2sub",
+ Path = "/3",
+ Level = 1,
+ Url = "/content-2sub",
+ ParentId = -1,
+ ChildIds = new int[] { },
+ Properties = new Collection
+ {
+ new SolidPublishedProperty
+ {
+ Alias = "prop1",
+ HasValue = true,
+ Value = 1234,
+ RawValue = "1234"
+ }
+ }
+ });
+
+ return caches;
+ }
+ }
+}
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs
index b4e44dc216..de887c8fd7 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs
@@ -1,7 +1,10 @@
using System;
using System.IO;
+using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Configuration;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
@@ -19,16 +22,14 @@ namespace Umbraco.Tests.PublishedContent
{
base.Initialize();
- //need to specify a custom callback for unit tests
- PublishedContentHelper.GetDataTypeCallback = (docTypeAlias, propertyAlias) =>
+ // need to specify a custom callback for unit tests
+ var propertyTypes = new[]
{
- if (propertyAlias.InvariantEquals("content"))
- {
- //return the rte type id
- return Guid.Parse(Constants.PropertyEditors.TinyMCEv3);
- }
- return Guid.Empty;
+ // AutoPublishedContentType will auto-generate other properties
+ new PublishedPropertyType("content", Guid.Parse(Constants.PropertyEditors.TinyMCEv3), 0, 0),
};
+ var type = new AutoPublishedContentType(0, "anything", propertyTypes);
+ PublishedContentType.GetPublishedContentTypeCallback = (alias) => type;
var rCtx = GetRoutingContext("/test", 1234);
UmbracoContext.Current = rCtx.UmbracoContext;
@@ -37,17 +38,20 @@ namespace Umbraco.Tests.PublishedContent
protected override void FreezeResolution()
{
- PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver(
+ PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver(
new[]
{
- typeof(DatePickerPropertyEditorValueConverter),
- typeof(TinyMcePropertyEditorValueConverter),
- typeof(YesNoPropertyEditorValueConverter)
+ typeof(DatePickerValueConverter),
+ typeof(TinyMceValueConverter),
+ typeof(YesNoValueConverter)
});
PublishedCachesResolver.Current = new PublishedCachesResolver(new PublishedCaches(
new PublishedContentCache(), new PublishedMediaCache()));
+ if (PublishedContentModelFactoryResolver.HasCurrent == false)
+ PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver();
+
base.FreezeResolution();
}
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs
new file mode 100644
index 0000000000..a2cb4c5dac
--- /dev/null
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs
@@ -0,0 +1,271 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Umbraco.Core;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Web;
+using Umbraco.Web.PublishedCache;
+
+namespace Umbraco.Tests.PublishedContent
+{
+ class SolidPublishedCaches : IPublishedCaches
+ {
+ public readonly SolidPublishedContentCache ContentCache = new SolidPublishedContentCache();
+
+ public ContextualPublishedContentCache CreateContextualContentCache(UmbracoContext context)
+ {
+ return new ContextualPublishedContentCache(ContentCache, context);
+ }
+
+ public ContextualPublishedMediaCache CreateContextualMediaCache(UmbracoContext context)
+ {
+ return null;
+ }
+ }
+
+ class SolidPublishedContentCache : IPublishedContentCache
+ {
+ private readonly Dictionary